From e983ee4b8803e5659b8f3e418169e5d8c019d230 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Fri, 31 Oct 2008 19:49:12 -0400 Subject: cx18: Fix write retries for registers that always change - part 2. From: Andy Walls cx18: Fix write retries for registers that always change - part 2. Some registers, especially interrupt related ones, will never read back the value just written. Modified interrupt register readback checks to make sure the intended effect was achieved. Priority: high Signed-off-by: Andy Walls --- linux/drivers/media/video/cx18/cx18-io.c | 17 +++++++++++++++-- linux/drivers/media/video/cx18/cx18-io.h | 17 +++++++++++++++++ linux/drivers/media/video/cx18/cx18-irq.c | 19 +++++++++++-------- linux/drivers/media/video/cx18/cx18-mailbox.c | 4 ++-- 4 files changed, 45 insertions(+), 12 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/cx18/cx18-io.c b/linux/drivers/media/video/cx18/cx18-io.c index 31be5e868..220fae8d4 100644 --- a/linux/drivers/media/video/cx18/cx18-io.c +++ b/linux/drivers/media/video/cx18/cx18-io.c @@ -88,6 +88,19 @@ void cx18_writel_retry(struct cx18 *cx, u32 val, void __iomem *addr) cx18_log_write_retries(cx, i, addr); } +void _cx18_writel_expect(struct cx18 *cx, u32 val, void __iomem *addr, + u32 eval, u32 mask) +{ + int i; + eval &= mask; + for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) { + cx18_writel_noretry(cx, val, addr); + if (eval == (cx18_readl_noretry(cx, addr) & mask)) + break; + } + cx18_log_write_retries(cx, i, addr); +} + void cx18_writew_retry(struct cx18 *cx, u16 val, void __iomem *addr) { int i; @@ -218,7 +231,7 @@ 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_noretry(cx, val, SW1_INT_STATUS); + 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); } @@ -233,7 +246,7 @@ void cx18_sw1_irq_disable(struct cx18 *cx, u32 val) void cx18_sw2_irq_enable(struct cx18 *cx, u32 val) { u32 r; - cx18_write_reg_noretry(cx, val, SW2_INT_STATUS); + 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); } diff --git a/linux/drivers/media/video/cx18/cx18-io.h b/linux/drivers/media/video/cx18/cx18-io.h index 287a5e8bf..425244453 100644 --- a/linux/drivers/media/video/cx18/cx18-io.h +++ b/linux/drivers/media/video/cx18/cx18-io.h @@ -133,6 +133,8 @@ static inline void cx18_writel(struct cx18 *cx, u32 val, void __iomem *addr) cx18_writel_noretry(cx, val, 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) @@ -271,6 +273,21 @@ static inline void cx18_write_reg(struct cx18 *cx, u32 val, u32 reg) 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); +} + +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) { diff --git a/linux/drivers/media/video/cx18/cx18-irq.c b/linux/drivers/media/video/cx18/cx18-irq.c index 038f70323..ba99ed0ed 100644 --- a/linux/drivers/media/video/cx18/cx18-irq.c +++ b/linux/drivers/media/video/cx18/cx18-irq.c @@ -146,16 +146,19 @@ irqreturn_t cx18_irq_handler(int irq, void *dev_id) spin_lock(&cx->dma_reg_lock); - hw2_mask = cx18_read_reg(cx, HW2_INT_MASK5_PCI); - hw2 = cx18_read_reg(cx, HW2_INT_CLR_STATUS) & hw2_mask; - sw2_mask = cx18_read_reg(cx, SW2_INT_ENABLE_PCI) | IRQ_EPU_TO_HPU_ACK; - sw2 = cx18_read_reg(cx, SW2_INT_STATUS) & sw2_mask; sw1_mask = cx18_read_reg(cx, SW1_INT_ENABLE_PCI) | IRQ_EPU_TO_HPU; sw1 = cx18_read_reg(cx, SW1_INT_STATUS) & sw1_mask; + sw2_mask = cx18_read_reg(cx, SW2_INT_ENABLE_PCI) | IRQ_EPU_TO_HPU_ACK; + 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; - cx18_write_reg_noretry(cx, sw2&sw2_mask, SW2_INT_STATUS); - cx18_write_reg_noretry(cx, sw1&sw1_mask, SW1_INT_STATUS); - cx18_write_reg_noretry(cx, hw2&hw2_mask, HW2_INT_CLR_STATUS); + if (sw1) + cx18_write_reg_expect(cx, sw1, SW1_INT_STATUS, ~sw1, sw1); + if (sw2) + cx18_write_reg_expect(cx, sw2, SW2_INT_STATUS, ~sw2, sw2); + if (hw2) + 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); @@ -182,5 +185,5 @@ irqreturn_t cx18_irq_handler(int irq, void *dev_id) hpu_cmd(cx, sw1); spin_unlock(&cx->dma_reg_lock); - return (hw2 | sw1 | sw2) ? IRQ_HANDLED : IRQ_NONE; + return (sw1 || sw2 || hw2) ? IRQ_HANDLED : IRQ_NONE; } diff --git a/linux/drivers/media/video/cx18/cx18-mailbox.c b/linux/drivers/media/video/cx18/cx18-mailbox.c index 87f7c8e2c..851a90537 100644 --- a/linux/drivers/media/video/cx18/cx18-mailbox.c +++ b/linux/drivers/media/video/cx18/cx18-mailbox.c @@ -176,7 +176,7 @@ long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb) cx18_setup_page(cx, SCB_OFFSET); cx18_write_sync(cx, mb->request, &ack_mb->ack); - cx18_write_reg_noretry(cx, ack_irq, SW2_INT_SET); + cx18_write_reg_expect(cx, ack_irq, SW2_INT_SET, ack_irq, ack_irq); return 0; } @@ -225,7 +225,7 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) } if (info->flags & API_FAST) timeout /= 2; - cx18_write_reg_noretry(cx, irq, SW1_INT_SET); + 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) { -- cgit v1.2.3 From 20294c5bcf01a436fbdb78a6548ec74dad1afd77 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sat, 1 Nov 2008 00:07:36 -0400 Subject: cx18: Mark CX18_CPU_DE_RELEASE_MDL as a slow API call From: Andy Walls cx18: Mark CX18_CPU_DE_RELEASE_MDL as a slow API call. Give the encoder time to complete the MDL release before destroying the encoder internal task. This avoids an encoder lockup on the next digital capture and error messages about buffers being returned for an inactive encoder task handle. Priority: high Signed-off-by: Andy Walls --- linux/drivers/media/video/cx18/cx18-mailbox.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/video/cx18/cx18-mailbox.c b/linux/drivers/media/video/cx18/cx18-mailbox.c index 851a90537..acff7dfb6 100644 --- a/linux/drivers/media/video/cx18/cx18-mailbox.c +++ b/linux/drivers/media/video/cx18/cx18-mailbox.c @@ -83,7 +83,7 @@ static const struct cx18_api_info api_info[] = { 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, 0), + API_ENTRY(CPU, CX18_CPU_DE_RELEASE_MDL, API_SLOW), API_ENTRY(0, 0, 0), }; -- cgit v1.2.3 From 8d3c3456efe683debd81b7a17171bbb41019ec61 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sun, 2 Nov 2008 08:59:04 -0500 Subject: cx18: Fix write retries for registers that always change - part 3. From: Andy Walls 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. Priority: normal Signed-off-by: Andy Walls --- linux/drivers/media/video/cx18/cx18-av-audio.c | 41 +++++++--- linux/drivers/media/video/cx18/cx18-av-core.c | 95 ++++++++++++++++------- linux/drivers/media/video/cx18/cx18-av-core.h | 3 + linux/drivers/media/video/cx18/cx18-av-firmware.c | 18 +++-- linux/drivers/media/video/cx18/cx18-firmware.c | 60 +++++++++----- linux/drivers/media/video/cx18/cx18-gpio.c | 24 +++--- linux/drivers/media/video/cx18/cx18-i2c.c | 24 ++++-- 7 files changed, 185 insertions(+), 80 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/cx18/cx18-av-audio.c b/linux/drivers/media/video/cx18/cx18-av-audio.c index 0b5583788..486cad0c2 100644 --- a/linux/drivers/media/video/cx18/cx18-av-audio.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-av-core.c b/linux/drivers/media/video/cx18/cx18-av-core.c index 73f5141a4..5c079e35e 100644 --- a/linux/drivers/media/video/cx18/cx18-av-core.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-av-core.h b/linux/drivers/media/video/cx18/cx18-av-core.h index b67d8df20..a07988c6f 100644 --- a/linux/drivers/media/video/cx18/cx18-av-core.h +++ b/linux/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/linux/drivers/media/video/cx18/cx18-av-firmware.c b/linux/drivers/media/video/cx18/cx18-av-firmware.c index 522a035b2..caa748a1a 100644 --- a/linux/drivers/media/video/cx18/cx18-av-firmware.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-firmware.c b/linux/drivers/media/video/cx18/cx18-firmware.c index 51534428c..7a7c89032 100644 --- a/linux/drivers/media/video/cx18/cx18-firmware.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-gpio.c b/linux/drivers/media/video/cx18/cx18-gpio.c index 0e5604219..17b7a32fc 100644 --- a/linux/drivers/media/video/cx18/cx18-gpio.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-i2c.c b/linux/drivers/media/video/cx18/cx18-i2c.c index f28a1ff2e..1af9ea149 100644 --- a/linux/drivers/media/video/cx18/cx18-i2c.c +++ b/linux/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 @@ -424,22 +425,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 64df1078ed0a76647e88e4f091d29980982e9e8f Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sun, 2 Nov 2008 16:15:28 -0500 Subject: cx18: Reduce number of mmio read retries From: Andy Walls 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. Priority: normal Signed-off-by: Andy Walls --- linux/drivers/media/video/cx18/cx18-av-firmware.c | 4 ++-- linux/drivers/media/video/cx18/cx18-driver.h | 7 ++++--- linux/drivers/media/video/cx18/cx18-io.c | 24 +++++++++++------------ linux/drivers/media/video/cx18/cx18-io.h | 8 ++++---- 4 files changed, 22 insertions(+), 21 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/cx18/cx18-av-firmware.c b/linux/drivers/media/video/cx18/cx18-av-firmware.c index caa748a1a..924691dca 100644 --- a/linux/drivers/media/video/cx18/cx18-av-firmware.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-driver.h b/linux/drivers/media/video/cx18/cx18-driver.h index a4b1708fa..b822373ec 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.h +++ b/linux/drivers/media/video/cx18/cx18-driver.h @@ -350,11 +350,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/linux/drivers/media/video/cx18/cx18-io.c b/linux/drivers/media/video/cx18/cx18-io.c index 220fae8d4..0ad8dea3e 100644 --- a/linux/drivers/media/video/cx18/cx18-io.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-io.h b/linux/drivers/media/video/cx18/cx18-io.h index 425244453..cb695a596 100644 --- a/linux/drivers/media/video/cx18/cx18-io.h +++ b/linux/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 a8c7bca3ca525526b4a274562230ff233d698018 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sun, 2 Nov 2008 16:54:10 -0500 Subject: cx18: Fix PLL freq computation for debug display From: Andy Walls 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. Priority: normal Signed-off-by: Andy Walls --- linux/drivers/media/video/cx18/cx18-av-core.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/cx18/cx18-av-core.c b/linux/drivers/media/video/cx18/cx18-av-core.c index 5c079e35e..518bd701d 100644 --- a/linux/drivers/media/video/cx18/cx18-av-core.c +++ b/linux/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 817788e2c69eb1c7538e780de3acb5a95440b6c2 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Tue, 4 Nov 2008 20:02:23 -0500 Subject: cx18: Use correct Mailbox IRQ Ack values and misc IRQ handling cleanup From: Andy Walls cx18: Use correct Mailbox IRQ Ack values and misc IRQ handling cleanup. The SCB field definitions for Ack IRQ's for mailboxes were inconsistent with the bitmasks being loaded into those SCB fields and the SW2 Ack IRQ handling logic. Renamed fields in SCB to make things consistent and did misc IRQ handling cleanups: removing legacy ivtv dma_reg_lock, HPU IRQ flags, etc. Priority: high Signed-off-by: Andy Walls --- linux/drivers/media/video/cx18/cx18-driver.c | 1 - linux/drivers/media/video/cx18/cx18-driver.h | 2 -- linux/drivers/media/video/cx18/cx18-irq.c | 54 +++++++++++++++------------- linux/drivers/media/video/cx18/cx18-scb.h | 40 ++++++++++----------- 4 files changed, 49 insertions(+), 48 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/cx18/cx18-driver.c b/linux/drivers/media/video/cx18/cx18-driver.c index c95230999..888ae9409 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.c +++ b/linux/drivers/media/video/cx18/cx18-driver.c @@ -448,7 +448,6 @@ static int __devinit cx18_init_struct1(struct cx18 *cx) mutex_init(&cx->gpio_lock); spin_lock_init(&cx->lock); - spin_lock_init(&cx->dma_reg_lock); /* start counting open_id at 1 */ cx->open_id = 1; diff --git a/linux/drivers/media/video/cx18/cx18-driver.h b/linux/drivers/media/video/cx18/cx18-driver.h index b822373ec..9c0ba299b 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.h +++ b/linux/drivers/media/video/cx18/cx18-driver.h @@ -403,8 +403,6 @@ struct cx18 { spinlock_t lock; /* lock access to this struct */ int search_pack_header; - spinlock_t dma_reg_lock; /* lock access to DMA engine registers */ - int open_id; /* incremented each time an open occurs, used as unique ID. Starts at 1, so 0 can be used as uninitialized value in the stream->id. */ diff --git a/linux/drivers/media/video/cx18/cx18-irq.c b/linux/drivers/media/video/cx18/cx18-irq.c index ba99ed0ed..4c33d2a7f 100644 --- a/linux/drivers/media/video/cx18/cx18-irq.c +++ b/linux/drivers/media/video/cx18/cx18-irq.c @@ -30,8 +30,6 @@ #include "cx18-vbi.h" #include "cx18-scb.h" -#define DMA_MAGIC_COOKIE 0x000001fe - static void epu_dma_done(struct cx18 *cx, struct cx18_mailbox *mb) { u32 handle = mb->args[0]; @@ -109,7 +107,7 @@ static void epu_debug(struct cx18 *cx, struct cx18_mailbox *mb) CX18_INFO("FW version: %s\n", p - 1); } -static void hpu_cmd(struct cx18 *cx, u32 sw1) +static void epu_cmd(struct cx18 *cx, u32 sw1) { struct cx18_mailbox mb; @@ -125,12 +123,31 @@ static void hpu_cmd(struct cx18 *cx, u32 sw1) epu_debug(cx, &mb); break; default: - CX18_WARN("Unexpected mailbox command %08x\n", mb.cmd); + CX18_WARN("Unknown CPU_TO_EPU mailbox command %#08x\n", + mb.cmd); break; } } - if (sw1 & (IRQ_APU_TO_EPU | IRQ_HPU_TO_EPU)) - CX18_WARN("Unexpected interrupt %08x\n", sw1); + + if (sw1 & IRQ_APU_TO_EPU) { + cx18_memcpy_fromio(cx, &mb, &cx->scb->apu2epu_mb, sizeof(mb)); + CX18_WARN("Unknown APU_TO_EPU mailbox command %#08x\n", mb.cmd); + } + + if (sw1 & IRQ_HPU_TO_EPU) { + cx18_memcpy_fromio(cx, &mb, &cx->scb->hpu2epu_mb, sizeof(mb)); + CX18_WARN("Unknown HPU_TO_EPU mailbox command %#08x\n", mb.cmd); + } +} + +static void xpu_ack(struct cx18 *cx, u32 sw2) +{ + if (sw2 & IRQ_CPU_TO_EPU_ACK) + 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); } #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18) @@ -144,11 +161,9 @@ irqreturn_t cx18_irq_handler(int irq, void *dev_id) u32 sw2, sw2_mask; u32 hw2, hw2_mask; - spin_lock(&cx->dma_reg_lock); - - sw1_mask = cx18_read_reg(cx, SW1_INT_ENABLE_PCI) | IRQ_EPU_TO_HPU; + 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) | IRQ_EPU_TO_HPU_ACK; + 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; @@ -164,26 +179,15 @@ irqreturn_t cx18_irq_handler(int irq, void *dev_id) CX18_DEBUG_HI_IRQ("SW1: %x SW2: %x HW2: %x\n", sw1, sw2, hw2); /* To do: interrupt-based I2C handling - if (hw2 & 0x00c00000) { + if (hw2 & (HW2_I2C1_INT|HW2_I2C2_INT)) { } */ - if (sw2) { - if (sw2 & (cx18_readl(cx, &cx->scb->cpu2hpu_irq_ack) | - cx18_readl(cx, &cx->scb->cpu2epu_irq_ack))) - wake_up(&cx->mb_cpu_waitq); - if (sw2 & (cx18_readl(cx, &cx->scb->apu2hpu_irq_ack) | - cx18_readl(cx, &cx->scb->apu2epu_irq_ack))) - wake_up(&cx->mb_apu_waitq); - if (sw2 & cx18_readl(cx, &cx->scb->epu2hpu_irq_ack)) - wake_up(&cx->mb_epu_waitq); - if (sw2 & cx18_readl(cx, &cx->scb->hpu2epu_irq_ack)) - wake_up(&cx->mb_hpu_waitq); - } + if (sw2) + xpu_ack(cx, sw2); if (sw1) - hpu_cmd(cx, sw1); - spin_unlock(&cx->dma_reg_lock); + epu_cmd(cx, sw1); return (sw1 || sw2 || hw2) ? IRQ_HANDLED : IRQ_NONE; } diff --git a/linux/drivers/media/video/cx18/cx18-scb.h b/linux/drivers/media/video/cx18/cx18-scb.h index 86b4cb15d..594713bbe 100644 --- a/linux/drivers/media/video/cx18/cx18-scb.h +++ b/linux/drivers/media/video/cx18/cx18-scb.h @@ -128,22 +128,22 @@ struct cx18_scb { u32 apu2cpu_irq; /* Value to write to register SW2 register set (0xC7003140) after the command is cleared */ - u32 apu2cpu_irq_ack; + u32 cpu2apu_irq_ack; u32 reserved2[13]; u32 hpu2cpu_mb_offset; u32 hpu2cpu_irq; - u32 hpu2cpu_irq_ack; + u32 cpu2hpu_irq_ack; u32 reserved3[13]; u32 ppu2cpu_mb_offset; u32 ppu2cpu_irq; - u32 ppu2cpu_irq_ack; + u32 cpu2ppu_irq_ack; u32 reserved4[13]; u32 epu2cpu_mb_offset; u32 epu2cpu_irq; - u32 epu2cpu_irq_ack; + u32 cpu2epu_irq_ack; u32 reserved5[13]; u32 reserved6[8]; @@ -153,22 +153,22 @@ struct cx18_scb { u32 reserved11[7]; u32 cpu2apu_mb_offset; u32 cpu2apu_irq; - u32 cpu2apu_irq_ack; + u32 apu2cpu_irq_ack; u32 reserved12[13]; u32 hpu2apu_mb_offset; u32 hpu2apu_irq; - u32 hpu2apu_irq_ack; + u32 apu2hpu_irq_ack; u32 reserved13[13]; u32 ppu2apu_mb_offset; u32 ppu2apu_irq; - u32 ppu2apu_irq_ack; + u32 apu2ppu_irq_ack; u32 reserved14[13]; u32 epu2apu_mb_offset; u32 epu2apu_irq; - u32 epu2apu_irq_ack; + u32 apu2epu_irq_ack; u32 reserved15[13]; u32 reserved16[8]; @@ -178,22 +178,22 @@ struct cx18_scb { u32 reserved21[7]; u32 cpu2hpu_mb_offset; u32 cpu2hpu_irq; - u32 cpu2hpu_irq_ack; + u32 hpu2cpu_irq_ack; u32 reserved22[13]; u32 apu2hpu_mb_offset; u32 apu2hpu_irq; - u32 apu2hpu_irq_ack; + u32 hpu2apu_irq_ack; u32 reserved23[13]; u32 ppu2hpu_mb_offset; u32 ppu2hpu_irq; - u32 ppu2hpu_irq_ack; + u32 hpu2ppu_irq_ack; u32 reserved24[13]; u32 epu2hpu_mb_offset; u32 epu2hpu_irq; - u32 epu2hpu_irq_ack; + u32 hpu2epu_irq_ack; u32 reserved25[13]; u32 reserved26[8]; @@ -203,22 +203,22 @@ struct cx18_scb { u32 reserved31[7]; u32 cpu2ppu_mb_offset; u32 cpu2ppu_irq; - u32 cpu2ppu_irq_ack; + u32 ppu2cpu_irq_ack; u32 reserved32[13]; u32 apu2ppu_mb_offset; u32 apu2ppu_irq; - u32 apu2ppu_irq_ack; + u32 ppu2apu_irq_ack; u32 reserved33[13]; u32 hpu2ppu_mb_offset; u32 hpu2ppu_irq; - u32 hpu2ppu_irq_ack; + u32 ppu2hpu_irq_ack; u32 reserved34[13]; u32 epu2ppu_mb_offset; u32 epu2ppu_irq; - u32 epu2ppu_irq_ack; + u32 ppu2epu_irq_ack; u32 reserved35[13]; u32 reserved36[8]; @@ -228,22 +228,22 @@ struct cx18_scb { u32 reserved41[7]; u32 cpu2epu_mb_offset; u32 cpu2epu_irq; - u32 cpu2epu_irq_ack; + u32 epu2cpu_irq_ack; u32 reserved42[13]; u32 apu2epu_mb_offset; u32 apu2epu_irq; - u32 apu2epu_irq_ack; + u32 epu2apu_irq_ack; u32 reserved43[13]; u32 hpu2epu_mb_offset; u32 hpu2epu_irq; - u32 hpu2epu_irq_ack; + u32 epu2hpu_irq_ack; u32 reserved44[13]; u32 ppu2epu_mb_offset; u32 ppu2epu_irq; - u32 ppu2epu_irq_ack; + u32 epu2ppu_irq_ack; u32 reserved45[13]; u32 reserved46[8]; -- cgit v1.2.3 From 94703235501e112e7496efe5bce32671e121d98f Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Tue, 4 Nov 2008 22:49:14 -0500 Subject: cx18: Move DVB buffer transfer handling from irq handler to work_queue From: Andy Walls cx18: Move DVB buffer transfer handling from irq handler to work_queue thread. In order to properly lock the epu2cpu mailbox for driver to CX23418 commands, the DVB/TS buffer handling needs to be moved from the IRQ handler and IRQ context to a work queue. This work_queue implmentation is strikingly similar to the ivtv implementation - for better or worse. Priority: high Signed-off-by: Andy Walls --- linux/drivers/media/video/cx18/cx18-driver.c | 16 +++++++++++++ linux/drivers/media/video/cx18/cx18-driver.h | 18 +++++++++----- linux/drivers/media/video/cx18/cx18-dvb.c | 23 ++++++++++++++++++ linux/drivers/media/video/cx18/cx18-dvb.h | 1 + linux/drivers/media/video/cx18/cx18-irq.c | 35 +++++++++++++++++++++------- linux/drivers/media/video/cx18/cx18-irq.h | 8 ++++--- linux/drivers/media/video/cx18/cx18-queue.c | 14 +++++------ 7 files changed, 89 insertions(+), 26 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/cx18/cx18-driver.c b/linux/drivers/media/video/cx18/cx18-driver.c index 888ae9409..446a132df 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.c +++ b/linux/drivers/media/video/cx18/cx18-driver.c @@ -449,6 +449,18 @@ 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; + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) + INIT_WORK(&cx->work, cx18_work_handler); +#else + INIT_WORK(&cx->work, cx18_work_handler, cx); +#endif + /* start counting open_id at 1 */ cx->open_id = 1; @@ -829,6 +841,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; @@ -929,6 +942,9 @@ static void cx18_remove(struct pci_dev *pci_dev) cx18_halt_firmware(cx); + flush_workqueue(cx->work_queue); + destroy_workqueue(cx->work_queue); + cx18_streams_cleanup(cx, 1); exit_cx18_i2c(cx); diff --git a/linux/drivers/media/video/cx18/cx18-driver.h b/linux/drivers/media/video/cx18/cx18-driver.h index 9c0ba299b..fe9e5bb97 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.h +++ b/linux/drivers/media/video/cx18/cx18-driver.h @@ -199,12 +199,15 @@ struct cx18_options { #define CX18_F_S_APPL_IO 8 /* this stream is used read/written by an application */ /* per-cx18, i_flags */ -#define CX18_F_I_LOADED_FW 0 /* Loaded the firmware the first time */ -#define CX18_F_I_EOS 4 /* End of encoder stream reached */ -#define CX18_F_I_RADIO_USER 5 /* The radio tuner is selected */ -#define CX18_F_I_ENC_PAUSED 13 /* the encoder is paused */ -#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_LOADED_FW 0 /* Loaded firmware 1st time */ +#define CX18_F_I_EOS 4 /* End of encoder stream */ +#define CX18_F_I_RADIO_USER 5 /* radio tuner is selected */ +#define CX18_F_I_ENC_PAUSED 13 /* the encoder is paused */ +#define CX18_F_I_HAVE_WORK 15 /* there is work to be done */ +#define CX18_F_I_WORK_HANDLER_DVB 18 /* work to be done for DVB */ +#define CX18_F_I_INITED 21 /* set after first open */ +#define CX18_F_I_FAILED 22 /* set if first open failed */ +#define CX18_F_I_WORK_INITED 23 /* worker thread initialized */ /* These are the VBI types as they appear in the embedded VBI private packets. */ #define CX18_SLICED_TYPE_TELETEXT_B (1) @@ -432,6 +435,9 @@ 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 */ struct i2c_adapter i2c_adap[2]; struct i2c_algo_bit_data i2c_algo[2]; diff --git a/linux/drivers/media/video/cx18/cx18-dvb.c b/linux/drivers/media/video/cx18/cx18-dvb.c index afc694e7b..4542e2e5e 100644 --- a/linux/drivers/media/video/cx18/cx18-dvb.c +++ b/linux/drivers/media/video/cx18/cx18-dvb.c @@ -23,6 +23,8 @@ #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" @@ -300,3 +302,24 @@ static int dvb_register(struct cx18_stream *stream) return ret; } + +void cx18_dvb_work_handler(struct cx18 *cx) +{ + struct cx18_buffer *buf; + struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_TS]; + + while ((buf = cx18_dequeue(s, &s->q_full)) != NULL) { + if (s->dvb.enabled) + dvb_dmx_swfilter(&s->dvb.demux, buf->buf, + buf->bytesused); + + cx18_enqueue(s, buf, &s->q_free); + cx18_buf_sync_for_device(s, buf); + if (s->handle == CX18_INVALID_TASK_HANDLE) /* FIXME: improve */ + continue; + + cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle, + (void __iomem *)&cx->scb->cpu_mdl[buf->id] - cx->enc_mem, + 1, buf->id, s->buf_size); + } +} diff --git a/linux/drivers/media/video/cx18/cx18-dvb.h b/linux/drivers/media/video/cx18/cx18-dvb.h index bf8d8f6f5..bbdcefc87 100644 --- a/linux/drivers/media/video/cx18/cx18-dvb.h +++ b/linux/drivers/media/video/cx18/cx18-dvb.h @@ -23,3 +23,4 @@ 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/linux/drivers/media/video/cx18/cx18-irq.c b/linux/drivers/media/video/cx18/cx18-irq.c index 4c33d2a7f..aff5a3abd 100644 --- a/linux/drivers/media/video/cx18/cx18-irq.c +++ b/linux/drivers/media/video/cx18/cx18-irq.c @@ -29,6 +29,26 @@ #include "cx18-mailbox.h" #include "cx18-vbi.h" #include "cx18-scb.h" +#include "cx18-dvb.h" + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) +void cx18_work_handler(struct work_struct *work) +{ + struct cx18 *cx = container_of(work, struct cx18, work); +#else +void cx18_work_handler(void *arg) +{ + struct cx18 *cx = arg; +#endif + 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); +} static void epu_dma_done(struct cx18 *cx, struct cx18_mailbox *mb) { @@ -65,17 +85,11 @@ static void epu_dma_done(struct cx18 *cx, struct cx18_mailbox *mb) if (buf) { cx18_buf_sync_for_cpu(s, buf); if (s->type == CX18_ENC_STREAM_TYPE_TS && s->dvb.enabled) { - /* process the buffer here */ - CX18_DEBUG_HI_DMA("TS recv and sent bytesused=%d\n", + CX18_DEBUG_HI_DMA("TS recv bytesused = %d\n", buf->bytesused); - dvb_dmx_swfilter(&s->dvb.demux, buf->buf, - buf->bytesused); - - cx18_buf_sync_for_device(s, buf); - cx18_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); + 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 { @@ -189,5 +203,8 @@ irqreturn_t cx18_irq_handler(int irq, void *dev_id) if (sw1) epu_cmd(cx, sw1); + if (test_and_clear_bit(CX18_F_I_HAVE_WORK, &cx->i_flags)) + queue_work(cx->work_queue, &cx->work); + return (sw1 || sw2 || hw2) ? IRQ_HANDLED : IRQ_NONE; } diff --git a/linux/drivers/media/video/cx18/cx18-irq.h b/linux/drivers/media/video/cx18/cx18-irq.h index cbcd2022f..d1d9d1967 100644 --- a/linux/drivers/media/video/cx18/cx18-irq.h +++ b/linux/drivers/media/video/cx18/cx18-irq.h @@ -36,6 +36,8 @@ irqreturn_t cx18_irq_handler(int irq, void *dev_id, struct pt_regs *regs); irqreturn_t cx18_irq_handler(int irq, void *dev_id); #endif -void cx18_irq_work_handler(struct work_struct *work); -void cx18_dma_stream_dec_prepare(struct cx18_stream *s, u32 offset, int lock); -void cx18_unfinished_dma(unsigned long arg); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) +void cx18_work_handler(struct work_struct *work); +#else +void cx18_work_handler(void *arg); +#endif diff --git a/linux/drivers/media/video/cx18/cx18-queue.c b/linux/drivers/media/video/cx18/cx18-queue.c index a33ba04a2..174682c25 100644 --- a/linux/drivers/media/video/cx18/cx18-queue.c +++ b/linux/drivers/media/video/cx18/cx18-queue.c @@ -88,15 +88,13 @@ struct cx18_buffer *cx18_queue_get_buf_irq(struct cx18_stream *s, u32 id, if (buf->id != id) continue; + buf->bytesused = bytesused; - /* the transport buffers are handled differently, - they are not moved to the full queue */ - 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); - } + 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); return buf; } -- cgit v1.2.3 From 5c9a58b88f61db125e53c96ba49bada98fe466f0 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Wed, 5 Nov 2008 19:19:15 -0500 Subject: cx18: Use default kernel work queue; fix streaming flag for work handler From: Andy Walls 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. Priority: high Signed-off-by: Andy Walls --- linux/drivers/media/video/cx18/cx18-driver.c | 10 +------- linux/drivers/media/video/cx18/cx18-driver.h | 2 -- linux/drivers/media/video/cx18/cx18-dvb.c | 37 +++++++++++++++------------ linux/drivers/media/video/cx18/cx18-irq.c | 8 +----- linux/drivers/media/video/cx18/cx18-streams.c | 6 ++--- 5 files changed, 26 insertions(+), 37 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/cx18/cx18-driver.c b/linux/drivers/media/video/cx18/cx18-driver.c index 446a132df..62d88d284 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.c +++ b/linux/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; - } - #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) INIT_WORK(&cx->work, cx18_work_handler); #else @@ -841,7 +835,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; @@ -942,8 +935,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/linux/drivers/media/video/cx18/cx18-driver.h b/linux/drivers/media/video/cx18/cx18-driver.h index fe9e5bb97..04f1c624d 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.h +++ b/linux/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/linux/drivers/media/video/cx18/cx18-dvb.c b/linux/drivers/media/video/cx18/cx18-dvb.c index 4542e2e5e..4845f732e 100644 --- a/linux/drivers/media/video/cx18/cx18-dvb.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-irq.c b/linux/drivers/media/video/cx18/cx18-irq.c index aff5a3abd..0c1ee93b5 100644 --- a/linux/drivers/media/video/cx18/cx18-irq.c +++ b/linux/drivers/media/video/cx18/cx18-irq.c @@ -40,12 +40,6 @@ void cx18_work_handler(void *arg) { struct cx18 *cx = arg; #endif - 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); } @@ -204,7 +198,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/linux/drivers/media/video/cx18/cx18-streams.c b/linux/drivers/media/video/cx18/cx18-streams.c index 8ead4025e..d29a0b61b 100644 --- a/linux/drivers/media/video/cx18/cx18-streams.c +++ b/linux/drivers/media/video/cx18/cx18-streams.c @@ -587,9 +587,6 @@ int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end) #endif } - /* 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); @@ -597,6 +594,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 0d57e1fd0d539233bd7877668d3960b739b17fb7 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 9 Nov 2008 18:25:31 +0000 Subject: cx88-dvb: Add new cards. From: Igor M. Liplianin Add support for TurboSight TBS8910 DVB-S PCI card Add support for TurboSight TBS8910 DVB-S PCI card The card based on stv0299 or stv0288 demodulators. Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab --- linux/Documentation/video4linux/CARDLIST.cx88 | 2 ++ linux/drivers/media/video/cx88/cx88-cards.c | 34 +++++++++++++++++++++++++++ linux/drivers/media/video/cx88/cx88-dvb.c | 2 ++ linux/drivers/media/video/cx88/cx88.h | 2 ++ 4 files changed, 40 insertions(+) (limited to 'linux') diff --git a/linux/Documentation/video4linux/CARDLIST.cx88 b/linux/Documentation/video4linux/CARDLIST.cx88 index 12e600d74..dc8da9406 100644 --- a/linux/Documentation/video4linux/CARDLIST.cx88 +++ b/linux/Documentation/video4linux/CARDLIST.cx88 @@ -74,3 +74,5 @@ 73 -> TeVii S420 DVB-S [d420:9022] 74 -> Prolink Pixelview Global Extreme [1554:4976] 75 -> PROF 7300 DVB-S/S2 [B033:3033] + 76 -> SATTRADE ST4200 DVB-S/S2 [b200:4200] + 77 -> TBS 8910 DVB-S [8910:8888] diff --git a/linux/drivers/media/video/cx88/cx88-cards.c b/linux/drivers/media/video/cx88/cx88-cards.c index cd72ee5cb..929c2b03a 100644 --- a/linux/drivers/media/video/cx88/cx88-cards.c +++ b/linux/drivers/media/video/cx88/cx88-cards.c @@ -1880,6 +1880,18 @@ static const struct cx88_board cx88_boards[] = { } }, .mpeg = CX88_MPEG_DVB, }, + [CX88_BOARD_TBS_8910] = { + .name = "TBS 8910 DVB-S", + .tuner_type = UNSET, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .input = {{ + .type = CX88_VMUX_DVB, + .vmux = 0, + } }, + .mpeg = CX88_MPEG_DVB, + }, [CX88_BOARD_TBS_8920] = { .name = "TBS 8920 DVB-S/S2", .tuner_type = TUNER_ABSENT, @@ -1904,6 +1916,18 @@ static const struct cx88_board cx88_boards[] = { } }, .mpeg = CX88_MPEG_DVB, }, + [CX88_BOARD_SATTRADE_ST4200] = { + .name = "SATTRADE ST4200 DVB-S/S2", + .tuner_type = UNSET, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .input = {{ + .type = CX88_VMUX_DVB, + .vmux = 0, + } }, + .mpeg = CX88_MPEG_DVB, + }, }; /* ------------------------------------------------------------------ */ @@ -2293,6 +2317,10 @@ static const struct cx88_subid cx88_subids[] = { .subvendor = 0xA044, .subdevice = 0x2011, .card = CX88_BOARD_OMICOM_SS4_PCI, + }, { + .subvendor = 0x8910, + .subdevice = 0x8888, + .card = CX88_BOARD_TBS_8910, }, { .subvendor = 0x8920, .subdevice = 0x8888, @@ -2301,6 +2329,10 @@ static const struct cx88_subid cx88_subids[] = { .subvendor = 0xB033, .subdevice = 0x3033, .card = CX88_BOARD_PROF_7300, + }, { + .subvendor = 0xb200, + .subdevice = 0x4200, + .card = CX88_BOARD_SATTRADE_ST4200, }, }; @@ -2911,8 +2943,10 @@ static void cx88_card_setup(struct cx88_core *core) case CX88_BOARD_TEVII_S420: case CX88_BOARD_TEVII_S460: case CX88_BOARD_OMICOM_SS4_PCI: + case CX88_BOARD_TBS_8910: case CX88_BOARD_TBS_8920: case CX88_BOARD_PROF_7300: + case CX88_BOARD_SATTRADE_ST4200: cx_write(MO_SRST_IO, 0); msleep(100); cx_write(MO_SRST_IO, 1); diff --git a/linux/drivers/media/video/cx88/cx88-dvb.c b/linux/drivers/media/video/cx88/cx88-dvb.c index 2eb7cb117..f24f2250b 100644 --- a/linux/drivers/media/video/cx88/cx88-dvb.c +++ b/linux/drivers/media/video/cx88/cx88-dvb.c @@ -1049,6 +1049,7 @@ static int dvb_register(struct cx8802_dev *dev) 0x08, ISL6421_DCL, 0x00); } break; + case CX88_BOARD_TBS_8910: case CX88_BOARD_TEVII_S420: fe0->dvb.frontend = dvb_attach(stv0299_attach, &tevii_tuner_sharp_config, @@ -1085,6 +1086,7 @@ static int dvb_register(struct cx8802_dev *dev) case CX88_BOARD_OMICOM_SS4_PCI: case CX88_BOARD_TBS_8920: case CX88_BOARD_PROF_7300: + case CX88_BOARD_SATTRADE_ST4200: fe0->dvb.frontend = dvb_attach(cx24116_attach, &hauppauge_hvr4000_config, &core->i2c_adap); diff --git a/linux/drivers/media/video/cx88/cx88.h b/linux/drivers/media/video/cx88/cx88.h index b03bfca54..06c9f8dd3 100644 --- a/linux/drivers/media/video/cx88/cx88.h +++ b/linux/drivers/media/video/cx88/cx88.h @@ -229,6 +229,8 @@ extern struct sram_channel cx88_sram_channels[]; #define CX88_BOARD_TEVII_S420 73 #define CX88_BOARD_PROLINK_PV_GLOBAL_XTREME 74 #define CX88_BOARD_PROF_7300 75 +#define CX88_BOARD_SATTRADE_ST4200 76 +#define CX88_BOARD_TBS_8910 77 enum cx88_itype { CX88_VMUX_COMPOSITE1 = 1, -- cgit v1.2.3 From fd1f6bf81509dfa4affe1c001f05841a372fbbd8 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 9 Nov 2008 18:26:25 +0000 Subject: cx88-dvb: Add new cards. From: Igor M. Liplianin Add support for Prof 6200 DVB-S PCI card Add support for Prof 6200 DVB-S PCI card The card based on stv0299 or stv0288 demodulators. Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab --- linux/Documentation/video4linux/CARDLIST.cx88 | 1 + linux/drivers/media/video/cx88/cx88-cards.c | 17 +++++++++++++++++ linux/drivers/media/video/cx88/cx88-dvb.c | 1 + linux/drivers/media/video/cx88/cx88.h | 1 + 4 files changed, 20 insertions(+) (limited to 'linux') diff --git a/linux/Documentation/video4linux/CARDLIST.cx88 b/linux/Documentation/video4linux/CARDLIST.cx88 index dc8da9406..0d08f1edc 100644 --- a/linux/Documentation/video4linux/CARDLIST.cx88 +++ b/linux/Documentation/video4linux/CARDLIST.cx88 @@ -76,3 +76,4 @@ 75 -> PROF 7300 DVB-S/S2 [B033:3033] 76 -> SATTRADE ST4200 DVB-S/S2 [b200:4200] 77 -> TBS 8910 DVB-S [8910:8888] + 78 -> Prof 6200 DVB-S [b022:3022] diff --git a/linux/drivers/media/video/cx88/cx88-cards.c b/linux/drivers/media/video/cx88/cx88-cards.c index 929c2b03a..1d7d9ed75 100644 --- a/linux/drivers/media/video/cx88/cx88-cards.c +++ b/linux/drivers/media/video/cx88/cx88-cards.c @@ -1904,6 +1904,18 @@ static const struct cx88_board cx88_boards[] = { } }, .mpeg = CX88_MPEG_DVB, }, + [CX88_BOARD_PROF_6200] = { + .name = "Prof 6200 DVB-S", + .tuner_type = UNSET, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .input = {{ + .type = CX88_VMUX_DVB, + .vmux = 0, + } }, + .mpeg = CX88_MPEG_DVB, + }, [CX88_BOARD_PROF_7300] = { .name = "PROF 7300 DVB-S/S2", .tuner_type = UNSET, @@ -2325,6 +2337,10 @@ static const struct cx88_subid cx88_subids[] = { .subvendor = 0x8920, .subdevice = 0x8888, .card = CX88_BOARD_TBS_8920, + }, { + .subvendor = 0xb022, + .subdevice = 0x3022, + .card = CX88_BOARD_PROF_6200, }, { .subvendor = 0xB033, .subdevice = 0x3033, @@ -2945,6 +2961,7 @@ static void cx88_card_setup(struct cx88_core *core) case CX88_BOARD_OMICOM_SS4_PCI: case CX88_BOARD_TBS_8910: case CX88_BOARD_TBS_8920: + case CX88_BOARD_PROF_6200: case CX88_BOARD_PROF_7300: case CX88_BOARD_SATTRADE_ST4200: cx_write(MO_SRST_IO, 0); diff --git a/linux/drivers/media/video/cx88/cx88-dvb.c b/linux/drivers/media/video/cx88/cx88-dvb.c index f24f2250b..ae25a01be 100644 --- a/linux/drivers/media/video/cx88/cx88-dvb.c +++ b/linux/drivers/media/video/cx88/cx88-dvb.c @@ -1049,6 +1049,7 @@ static int dvb_register(struct cx8802_dev *dev) 0x08, ISL6421_DCL, 0x00); } break; + case CX88_BOARD_PROF_6200: case CX88_BOARD_TBS_8910: case CX88_BOARD_TEVII_S420: fe0->dvb.frontend = dvb_attach(stv0299_attach, diff --git a/linux/drivers/media/video/cx88/cx88.h b/linux/drivers/media/video/cx88/cx88.h index 06c9f8dd3..6f8e575a3 100644 --- a/linux/drivers/media/video/cx88/cx88.h +++ b/linux/drivers/media/video/cx88/cx88.h @@ -231,6 +231,7 @@ extern struct sram_channel cx88_sram_channels[]; #define CX88_BOARD_PROF_7300 75 #define CX88_BOARD_SATTRADE_ST4200 76 #define CX88_BOARD_TBS_8910 77 +#define CX88_BOARD_PROF_6200 78 enum cx88_itype { CX88_VMUX_COMPOSITE1 = 1, -- cgit v1.2.3 From 4e267afce9e50e64966383f58475ab6a82dce2f1 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 9 Nov 2008 17:59:33 +0000 Subject: cx88-dvb: Remove usage core->prev_set_voltage from cx24116 based cards. From: Igor M. Liplianin cx88-dvb: Remove usage core->prev_set_voltage from cx24116 based cards. Remove usage core->prev_set_voltage from cx24116 based cards, as it does not make sense for the demod. Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/cx88/cx88-dvb.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/cx88/cx88-dvb.c b/linux/drivers/media/video/cx88/cx88-dvb.c index ae25a01be..ce4e0b77c 100644 --- a/linux/drivers/media/video/cx88/cx88-dvb.c +++ b/linux/drivers/media/video/cx88/cx88-dvb.c @@ -1079,10 +1079,8 @@ static int dvb_register(struct cx8802_dev *dev) fe0->dvb.frontend = dvb_attach(cx24116_attach, &tevii_s460_config, &core->i2c_adap); - if (fe0->dvb.frontend != NULL) { - core->prev_set_voltage = fe0->dvb.frontend->ops.set_voltage; + if (fe0->dvb.frontend != NULL) fe0->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage; - } break; case CX88_BOARD_OMICOM_SS4_PCI: case CX88_BOARD_TBS_8920: @@ -1091,10 +1089,8 @@ static int dvb_register(struct cx8802_dev *dev) fe0->dvb.frontend = dvb_attach(cx24116_attach, &hauppauge_hvr4000_config, &core->i2c_adap); - if (fe0->dvb.frontend != NULL) { - core->prev_set_voltage = fe0->dvb.frontend->ops.set_voltage; + if (fe0->dvb.frontend != NULL) fe0->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage; - } break; default: printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card isn't supported yet\n", -- cgit v1.2.3 From 31dd068f4f1895c9cbd9c1ac7b289051cbada085 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 9 Nov 2008 16:50:07 +0000 Subject: WinFast DTV2000 H: add support for missing analog inputs From: Vincent Pelletier WinFast DTV2000 H: add support for missing analog inputs Add support for the following inputs: - radio tuner - composite 1 & 2 (only 1 is physically available, but composite 2 is also advertised by windows driver) - svideo Signed-off-by: Vincent Pelletier Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/cx88/cx88-cards.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/video/cx88/cx88-cards.c b/linux/drivers/media/video/cx88/cx88-cards.c index 1d7d9ed75..f9b5a9c09 100644 --- a/linux/drivers/media/video/cx88/cx88-cards.c +++ b/linux/drivers/media/video/cx88/cx88-cards.c @@ -1270,7 +1270,6 @@ static const struct cx88_board cx88_boards[] = { }, }, [CX88_BOARD_WINFAST_DTV2000H] = { - /* video inputs and radio still in testing */ .name = "WinFast DTV2000 H", .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3, .radio_type = UNSET, @@ -1284,7 +1283,35 @@ static const struct cx88_board cx88_boards[] = { .gpio1 = 0x00008203, .gpio2 = 0x00017304, .gpio3 = 0x02000000, + }, { + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + .gpio0 = 0x0001d701, + .gpio1 = 0x0000b207, + .gpio2 = 0x0001d701, + .gpio3 = 0x02000000, + }, { + .type = CX88_VMUX_COMPOSITE2, + .vmux = 2, + .gpio0 = 0x0001d503, + .gpio1 = 0x0000b207, + .gpio2 = 0x0001d503, + .gpio3 = 0x02000000, + }, { + .type = CX88_VMUX_SVIDEO, + .vmux = 3, + .gpio0 = 0x0001d701, + .gpio1 = 0x0000b207, + .gpio2 = 0x0001d701, + .gpio3 = 0x02000000, }}, + .radio = { + .type = CX88_RADIO, + .gpio0 = 0x00015702, + .gpio1 = 0x0000f207, + .gpio2 = 0x00015702, + .gpio3 = 0x02000000, + }, .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_GENIATECH_DVBS] = { -- cgit v1.2.3 From 2c0b6e88d3a926ef7a9e118642f1aedf091c29f8 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 6 Nov 2008 01:12:56 +0000 Subject: Add TerraTec Cinergy S USB support From: Igor M. Liplianin Add TerraTec Cinergy S USB support Signed-off-by: Igor M. Liplianin Acked-by: Thorsten Leupold Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/dvb/dvb-usb/dw2102.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/dvb/dvb-usb/dw2102.c b/linux/drivers/media/dvb/dvb-usb/dw2102.c index c9431713d..bc5e47a2b 100644 --- a/linux/drivers/media/dvb/dvb-usb/dw2102.c +++ b/linux/drivers/media/dvb/dvb-usb/dw2102.c @@ -26,6 +26,10 @@ #define USB_PID_DW2104 0x2104 #endif +#ifndef USB_PID_CINERGY_S +#define USB_PID_CINERGY_S 0x0064 +#endif + #define DW210X_READ_MSG 0 #define DW210X_WRITE_MSG 1 @@ -577,6 +581,7 @@ static struct usb_device_id dw2102_table[] = { {USB_DEVICE(USB_VID_CYPRESS, 0x2101)}, {USB_DEVICE(USB_VID_CYPRESS, 0x2104)}, {USB_DEVICE(0x9022, 0xd650)}, + {USB_DEVICE(USB_VID_TERRATEC, USB_PID_CINERGY_S)}, { } }; @@ -646,6 +651,7 @@ static int dw2102_load_firmware(struct usb_device *dev, dw210x_op_rw(dev, 0xbf, 0x0040, 0, &reset, 0, DW210X_WRITE_MSG); break; + case USB_PID_CINERGY_S: case USB_PID_DW2102: dw210x_op_rw(dev, 0xbf, 0x0040, 0, &reset, 0, DW210X_WRITE_MSG); @@ -725,7 +731,7 @@ static struct dvb_usb_device_properties dw2102_properties = { }, } }, - .num_device_descs = 2, + .num_device_descs = 3, .devices = { {"DVBWorld DVB-S 2102 USB2.0", {&dw2102_table[0], NULL}, @@ -735,6 +741,10 @@ static struct dvb_usb_device_properties dw2102_properties = { {&dw2102_table[1], NULL}, {NULL}, }, + {"TerraTec Cinergy S USB", + {&dw2102_table[4], NULL}, + {NULL}, + }, } }; -- cgit v1.2.3 From f632c32a0a44337780146df40146d6330b6aa9d8 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Wed, 5 Nov 2008 23:15:41 -0500 Subject: cx18: Add outgoing mailbox mutexes and check for ack via waitq vs poll From: Andy Walls 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. Priority: high Signed-off-by: Andy Walls --- linux/drivers/media/video/cx18/cx18-driver.c | 4 +- linux/drivers/media/video/cx18/cx18-driver.h | 7 ++- linux/drivers/media/video/cx18/cx18-irq.c | 21 +++---- linux/drivers/media/video/cx18/cx18-mailbox.c | 84 +++++++++++++-------------- linux/drivers/media/video/cx18/cx18-mailbox.h | 7 ++- 5 files changed, 60 insertions(+), 63 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/cx18/cx18-driver.c b/linux/drivers/media/video/cx18/cx18-driver.c index 62d88d284..470946fff 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.c +++ b/linux/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); @@ -470,8 +472,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/linux/drivers/media/video/cx18/cx18-driver.h b/linux/drivers/media/video/cx18/cx18-driver.h index 04f1c624d..e2ec15549 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.h +++ b/linux/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/linux/drivers/media/video/cx18/cx18-irq.c b/linux/drivers/media/video/cx18/cx18-irq.c index 0c1ee93b5..b222183b9 100644 --- a/linux/drivers/media/video/cx18/cx18-irq.c +++ b/linux/drivers/media/video/cx18/cx18-irq.c @@ -44,7 +44,7 @@ void cx18_work_handler(void *arg) 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; @@ -65,7 +65,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; } @@ -92,13 +92,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; @@ -108,7 +108,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) @@ -125,10 +125,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", @@ -141,11 +141,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) @@ -154,8 +149,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); } #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18) diff --git a/linux/drivers/media/video/cx18/cx18-mailbox.c b/linux/drivers/media/video/cx18/cx18-mailbox.c index acff7dfb6..d2975a2e6 100644 --- a/linux/drivers/media/video/cx18/cx18-mailbox.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-mailbox.h b/linux/drivers/media/video/cx18/cx18-mailbox.h index d99564153..54758f32d 100644 --- a/linux/drivers/media/video/cx18/cx18-mailbox.h +++ b/linux/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 10344d77478a9b43101a1bd3e5d427e68f50390f Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Wed, 5 Nov 2008 23:33:27 -0500 Subject: cx18: Roll driver version number due to significant changes From: Andy Walls Driver interrupt and mailbox handling has change significantly. Time for a roll to v1.0.2. Priority: normal Signed-off-by: Andy Walls --- linux/drivers/media/video/cx18/cx18-version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/video/cx18/cx18-version.h b/linux/drivers/media/video/cx18/cx18-version.h index 9f6be2d45..366cc1472 100644 --- a/linux/drivers/media/video/cx18/cx18-version.h +++ b/linux/drivers/media/video/cx18/cx18-version.h @@ -25,7 +25,7 @@ #define CX18_DRIVER_NAME "cx18" #define CX18_DRIVER_VERSION_MAJOR 1 #define CX18_DRIVER_VERSION_MINOR 0 -#define CX18_DRIVER_VERSION_PATCHLEVEL 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 94d05a47bd31702be24fbef3b4ee9448083295d6 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 7 Nov 2008 15:24:18 -0200 Subject: Properly handle error messages during alsa registering From: Mauro Carvalho Chehab Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-audio.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-audio.c b/linux/drivers/media/video/em28xx/em28xx-audio.c index d6856b331..bec42349e 100644 --- a/linux/drivers/media/video/em28xx/em28xx-audio.c +++ b/linux/drivers/media/video/em28xx/em28xx-audio.c @@ -500,7 +500,7 @@ static int em28xx_audio_init(struct em28xx *dev) if (dev->has_audio_class) { /* This device does not support the extension (in this case - the device is expecting the snd-usb-audio module */ + the device is expecting the snd-usb-audio module) */ return 0; } @@ -521,7 +521,12 @@ static int em28xx_audio_init(struct em28xx *dev) } spin_lock_init(&adev->slock); - ret = snd_pcm_new(card, "Em28xx Audio", 0, 0, 1, &pcm); + err = snd_pcm_new(card, "Em28xx Audio", 0, 0, 1, &pcm); + if (err < 0) { + snd_card_free(card); + return err; + } + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_em28xx_pcm_capture); pcm->info_flags = 0; pcm->private_data = dev; @@ -533,7 +538,7 @@ static int em28xx_audio_init(struct em28xx *dev) err = snd_card_register(card); if (err < 0) { snd_card_free(card); - return -ENOMEM; + return err; } adev->sndcard = card; adev->udev = dev->udev; -- cgit v1.2.3 From d2d260c5ae3e899c0f3d1482a4e58057c4031dbd Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Fri, 7 Nov 2008 21:57:46 -0500 Subject: cx18: Improve handling of outgoing mailboxes detected to be busy From: Andy Walls 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. Priority: high Signed-off-by: Andy Walls --- linux/drivers/media/video/cx18/cx18-irq.c | 3 +- linux/drivers/media/video/cx18/cx18-mailbox.c | 130 ++++++++++++-------------- 2 files changed, 64 insertions(+), 69 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/cx18/cx18-irq.c b/linux/drivers/media/video/cx18/cx18-irq.c index b222183b9..3e23fc350 100644 --- a/linux/drivers/media/video/cx18/cx18-irq.c +++ b/linux/drivers/media/video/cx18/cx18-irq.c @@ -177,7 +177,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/linux/drivers/media/video/cx18/cx18-mailbox.c b/linux/drivers/media/video/cx18/cx18-mailbox.c index d2975a2e6..0de4b9a7b 100644 --- a/linux/drivers/media/video/cx18/cx18-mailbox.c +++ b/linux/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 2e274dbbf9ab4c3f6cbc1aae4ae101dffed915de Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 8 Nov 2008 03:40:46 +0000 Subject: dsbr100: add suspend and resume From: Alexey Klimov This patch adds support for suspend and resume methods in driver. Without this kradio and gnomeradio crashes during resume. Also .supports_autosuspend in usb_driver struct set equal to 0 to avoid suspending of module if usb_autosuspend enabled. Signed-off-by: Alexey Klimov Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/radio/dsbr100.c | 47 +++++++++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 4 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/radio/dsbr100.c b/linux/drivers/media/radio/dsbr100.c index 752478fad..dc033a684 100644 --- a/linux/drivers/media/radio/dsbr100.c +++ b/linux/drivers/media/radio/dsbr100.c @@ -132,6 +132,9 @@ static int usb_dsbr100_probe(struct usb_interface *intf, static void usb_dsbr100_disconnect(struct usb_interface *intf); static int usb_dsbr100_open(struct inode *inode, struct file *file); static int usb_dsbr100_close(struct inode *inode, struct file *file); +static int usb_dsbr100_suspend(struct usb_interface *intf, + pm_message_t message); +static int usb_dsbr100_resume(struct usb_interface *intf); static int radio_nr = -1; module_param(radio_nr, int, 0); @@ -158,10 +161,16 @@ MODULE_DEVICE_TABLE (usb, usb_dsbr100_device_table); /* USB subsystem interface */ static struct usb_driver usb_dsbr100_driver = { - .name = "dsbr100", - .probe = usb_dsbr100_probe, - .disconnect = usb_dsbr100_disconnect, - .id_table = usb_dsbr100_device_table, + .name = "dsbr100", + .probe = usb_dsbr100_probe, + .disconnect = usb_dsbr100_disconnect, + .id_table = usb_dsbr100_device_table, + .suspend = usb_dsbr100_suspend, + .resume = usb_dsbr100_resume, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23) + .reset_resume = usb_dsbr100_resume, +#endif + .supports_autosuspend = 0, }; /* Low-level device interface begins here */ @@ -449,6 +458,36 @@ static int usb_dsbr100_close(struct inode *inode, struct file *file) return 0; } +/* Suspend device - stop device. */ +static int usb_dsbr100_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct dsbr100_device *radio = usb_get_intfdata(intf); + int retval; + + retval = dsbr100_stop(radio); + if (retval == -1) + dev_warn(&intf->dev, "dsbr100_stop failed\n"); + + dev_info(&intf->dev, "going into suspend..\n"); + + return 0; +} + +/* Resume device - start device. */ +static int usb_dsbr100_resume(struct usb_interface *intf) +{ + struct dsbr100_device *radio = usb_get_intfdata(intf); + int retval; + + retval = dsbr100_start(radio); + if (retval == -1) + dev_warn(&intf->dev, "dsbr100_start failed\n"); + + dev_info(&intf->dev, "coming out of suspend..\n"); + + return 0; +} + /* File system interface */ static const struct file_operations usb_dsbr100_fops = { .owner = THIS_MODULE, -- cgit v1.2.3 From 7b5da71eba5fbdf4fd23ed8efc93d4b942d8ba95 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 8 Nov 2008 03:31:50 +0000 Subject: dsbr100: add disabled controls and fix version From: Alexey Klimov Patch adds disabled controls in v4l2_queryctrl struct. Also version of driver corrected. -- Signed-off-by: Alexey Klimov Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/radio/dsbr100.c | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/radio/dsbr100.c b/linux/drivers/media/radio/dsbr100.c index dc033a684..d1865b164 100644 --- a/linux/drivers/media/radio/dsbr100.c +++ b/linux/drivers/media/radio/dsbr100.c @@ -94,8 +94,8 @@ */ #include /* for KERNEL_VERSION MACRO */ -#define DRIVER_VERSION "v0.41" -#define RADIO_VERSION KERNEL_VERSION(0,4,1) +#define DRIVER_VERSION "v0.43" +#define RADIO_VERSION KERNEL_VERSION(0, 4, 3) static struct v4l2_queryctrl radio_qctrl[] = { { @@ -105,7 +105,27 @@ static struct v4l2_queryctrl radio_qctrl[] = { .maximum = 1, .default_value = 1, .type = V4L2_CTRL_TYPE_BOOLEAN, - } + }, +/* HINT: the disabled controls are only here to satify kradio and such apps */ + { .id = V4L2_CID_AUDIO_VOLUME, + .flags = V4L2_CTRL_FLAG_DISABLED, + }, + { + .id = V4L2_CID_AUDIO_BALANCE, + .flags = V4L2_CTRL_FLAG_DISABLED, + }, + { + .id = V4L2_CID_AUDIO_BASS, + .flags = V4L2_CTRL_FLAG_DISABLED, + }, + { + .id = V4L2_CID_AUDIO_TREBLE, + .flags = V4L2_CTRL_FLAG_DISABLED, + }, + { + .id = V4L2_CID_AUDIO_LOUDNESS, + .flags = V4L2_CTRL_FLAG_DISABLED, + }, }; #define DRIVER_AUTHOR "Markus Demleitner " -- cgit v1.2.3 From 6df1bdecf27c9e9b049e97e0b157eaf49e4208ed Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sat, 8 Nov 2008 12:19:37 -0500 Subject: cx18: Further changes to improve mailbox protocol integrity & performnce From: Andy Walls 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. Priority: normal Signed-off-by: Andy Walls --- linux/drivers/media/video/cx18/cx18-driver.c | 2 +- linux/drivers/media/video/cx18/cx18-driver.h | 7 ++++ linux/drivers/media/video/cx18/cx18-io.c | 4 +++ linux/drivers/media/video/cx18/cx18-mailbox.c | 47 +++++++++++++++++---------- 4 files changed, 42 insertions(+), 18 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/cx18/cx18-driver.c b/linux/drivers/media/video/cx18/cx18-driver.c index 470946fff..11b693348 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-driver.h b/linux/drivers/media/video/cx18/cx18-driver.h index e2ec15549..ce7680675 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.h +++ b/linux/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/linux/drivers/media/video/cx18/cx18-io.c b/linux/drivers/media/video/cx18/cx18-io.c index 0ad8dea3e..48a8adc83 100644 --- a/linux/drivers/media/video/cx18/cx18-io.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-mailbox.c b/linux/drivers/media/video/cx18/cx18-mailbox.c index 0de4b9a7b..35f7188d4 100644 --- a/linux/drivers/media/video/cx18/cx18-mailbox.c +++ b/linux/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 359020f9d46c383a26678ad896a1b7911b6284f7 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sat, 8 Nov 2008 15:14:22 -0500 Subject: cx18: Minor fixes to APU firmware load process From: Andy Walls 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. Priority: normal Signed-off-by: Andy Walls --- linux/drivers/media/video/cx18/cx18-firmware.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/cx18/cx18-firmware.c b/linux/drivers/media/video/cx18/cx18-firmware.c index 7a7c89032..ab02da727 100644 --- a/linux/drivers/media/video/cx18/cx18-firmware.c +++ b/linux/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 6914ccb9c5fff818e5758ee7545adf3009101fa0 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 8 Nov 2008 22:47:28 +0100 Subject: uvcvideo: Add support for Samsung Q310 integrated webcam From: Laurent Pinchart Priority: normal Signed-off-by: Laurent Pinchart --- linux/drivers/media/video/uvc/uvc_driver.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'linux') diff --git a/linux/drivers/media/video/uvc/uvc_driver.c b/linux/drivers/media/video/uvc/uvc_driver.c index cbe9d0452..b400f7c89 100644 --- a/linux/drivers/media/video/uvc/uvc_driver.c +++ b/linux/drivers/media/video/uvc/uvc_driver.c @@ -1839,6 +1839,15 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_STREAM_NO_FID }, + /* Syntek (Samsung Q310) */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x174f, + .idProduct = 0x5931, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_STREAM_NO_FID }, /* Asus F9SG */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, -- cgit v1.2.3 From 5acb6f839802e05e6b49fcc2c212fc627d3253b4 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 8 Nov 2008 22:52:04 +0100 Subject: uvcvideo: Add support for Lenovo Thinkpad SL500 integrated webcam From: Laurent Pinchart Priority: normal Signed-off-by: Laurent Pinchart --- linux/drivers/media/video/uvc/uvc_driver.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'linux') diff --git a/linux/drivers/media/video/uvc/uvc_driver.c b/linux/drivers/media/video/uvc/uvc_driver.c index b400f7c89..34725a906 100644 --- a/linux/drivers/media/video/uvc/uvc_driver.c +++ b/linux/drivers/media/video/uvc/uvc_driver.c @@ -1866,6 +1866,15 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_STREAM_NO_FID }, + /* Lenovo Thinkpad SL500 */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x17ef, + .idProduct = 0x480b, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_STREAM_NO_FID }, /* Ecamm Pico iMage */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, -- cgit v1.2.3 From 42b332f9b4a1885c3204e5b3c6cfe6344dfc5243 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 8 Nov 2008 22:53:49 +0100 Subject: uvcvideo: Sort the frame descriptors during parsing From: Laurent Pinchart The UVC specification does not require frame descriptors to be sorted by bFrameIndex. At least one camera (145f:013e) is known not to sort the descriptors properly, so they need to be sorted during parsing as the driver then accesses them by index. Priority: normal Signed-off-by: Laurent Pinchart --- linux/drivers/media/video/uvc/uvc_driver.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/uvc/uvc_driver.c b/linux/drivers/media/video/uvc/uvc_driver.c index 34725a906..fd73c5336 100644 --- a/linux/drivers/media/video/uvc/uvc_driver.c +++ b/linux/drivers/media/video/uvc/uvc_driver.c @@ -288,8 +288,10 @@ static int uvc_parse_format(struct uvc_device *dev, struct uvc_format_desc *fmtdesc; struct uvc_frame *frame; const unsigned char *start = buffer; + unsigned char *_buffer; unsigned int interval; unsigned int i, n; + int _buflen; __u8 ftype; format->type = buffer[2]; @@ -410,12 +412,20 @@ static int uvc_parse_format(struct uvc_device *dev, buflen -= buffer[0]; buffer += buffer[0]; + /* Count the number of frame descriptors to test the bFrameIndex + * field when parsing the descriptors. We can't rely on the + * bNumFrameDescriptors field as some cameras don't initialize it + * properly. + */ + for (_buflen = buflen, _buffer = buffer; + _buflen > 2 && _buffer[2] == ftype; + _buflen -= _buffer[0], _buffer += _buffer[0]) + format->nframes++; + /* Parse the frame descriptors. Only uncompressed, MJPEG and frame * based formats have frame descriptors. */ while (buflen > 2 && buffer[2] == ftype) { - frame = &format->frame[format->nframes]; - if (ftype != VS_FRAME_FRAME_BASED) n = buflen > 25 ? buffer[25] : 0; else @@ -430,6 +440,16 @@ static int uvc_parse_format(struct uvc_device *dev, return -EINVAL; } + if (buffer[3] - 1 >= format->nframes) { + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming" + "interface %d frame index %u out of range\n", + dev->udev->devnum, alts->desc.bInterfaceNumber, + buffer[3]); + return -EINVAL; + } + + frame = &format->frame[buffer[3] - 1]; + frame->bFrameIndex = buffer[3]; frame->bmCapabilities = buffer[4]; frame->wWidth = le16_to_cpup((__le16 *)&buffer[5]); @@ -486,7 +506,6 @@ static int uvc_parse_format(struct uvc_device *dev, 10000000/frame->dwDefaultFrameInterval, (100000000/frame->dwDefaultFrameInterval)%10); - format->nframes++; buflen -= buffer[0]; buffer += buffer[0]; } -- cgit v1.2.3 From 6d66e543c6fd8adfc1495538a68b849999a8cc90 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 8 Nov 2008 23:14:50 +0100 Subject: uvcvideo: Handle failed video GET_{MIN|MAX|DEF} requests more gracefully From: Laurent Pinchart Failed requests will now generate a one-time warning message instead of the usual "Failed to query..." error, which should be more user-friendly. The driver will also recover automatically from failed GET_MIN/GET_MAX requests when the device is half-broken without requiring the MINMAX quirk (fully broken devices still need the quirk). Priority: normal Signed-off-by: Laurent Pinchart --- linux/drivers/media/video/uvc/uvc_driver.c | 126 ----------------------------- linux/drivers/media/video/uvc/uvc_v4l2.c | 4 +- linux/drivers/media/video/uvc/uvc_video.c | 63 ++++++++++++--- linux/drivers/media/video/uvc/uvcvideo.h | 14 +++- 4 files changed, 66 insertions(+), 141 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/uvc/uvc_driver.c b/linux/drivers/media/video/uvc/uvc_driver.c index fd73c5336..2760ddb96 100644 --- a/linux/drivers/media/video/uvc/uvc_driver.c +++ b/linux/drivers/media/video/uvc/uvc_driver.c @@ -1728,24 +1728,6 @@ static int uvc_reset_resume(struct usb_interface *intf) * though they are compliant. */ static struct usb_device_id uvc_ids[] = { - /* ALi M5606 (Clevo M540SR) */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x0402, - .idProduct = 0x5606, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, - /* Creative Live! Optia */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x041e, - .idProduct = 0x4057, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, /* Microsoft Lifecam NX-6000 */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, @@ -1831,15 +1813,6 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_STREAM_NO_FID }, - /* Silicon Motion SM371 */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x090c, - .idProduct = 0xb371, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, /* MT6227 */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, @@ -1924,105 +1897,6 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_PROBE_MINMAX | UVC_QUIRK_IGNORE_SELECTOR_UNIT}, - /* Acer OEM Webcam - Unknown vendor */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x5986, - .idProduct = 0x0100, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, - /* Packard Bell OEM Webcam - Bison Electronics */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x5986, - .idProduct = 0x0101, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, - /* Acer Crystal Eye webcam - Bison Electronics */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x5986, - .idProduct = 0x0102, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, - /* Compaq Presario B1200 - Bison Electronics */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x5986, - .idProduct = 0x0104, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, - /* Acer Travelmate 7720 - Bison Electronics */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x5986, - .idProduct = 0x0105, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, - /* Medion Akoya Mini E1210 - Bison Electronics */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x5986, - .idProduct = 0x0141, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, - /* Acer OrbiCam - Bison Electronics */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x5986, - .idProduct = 0x0200, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, - /* Fujitsu Amilo SI2636 - Bison Electronics */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x5986, - .idProduct = 0x0202, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, - /* Advent 4211 - Bison Electronics */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x5986, - .idProduct = 0x0203, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, - /* Bison Electronics */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x5986, - .idProduct = 0x0300, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, - /* Clevo M570TU - Bison Electronics */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x5986, - .idProduct = 0x0303, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, /* Generic USB Video Class */ { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) }, {} diff --git a/linux/drivers/media/video/uvc/uvc_v4l2.c b/linux/drivers/media/video/uvc/uvc_v4l2.c index 836136780..624bf74de 100644 --- a/linux/drivers/media/video/uvc/uvc_v4l2.c +++ b/linux/drivers/media/video/uvc/uvc_v4l2.c @@ -252,7 +252,7 @@ static int uvc_v4l2_set_format(struct uvc_video_device *video, if (ret < 0) return ret; - if ((ret = uvc_set_video_ctrl(video, &probe, 0)) < 0) + if ((ret = uvc_commit_video(video, &probe)) < 0) return ret; memcpy(&video->streaming->ctrl, &probe, sizeof probe); @@ -316,7 +316,7 @@ static int uvc_v4l2_set_streamparm(struct uvc_video_device *video, return ret; /* Commit the new settings. */ - if ((ret = uvc_set_video_ctrl(video, &probe, 0)) < 0) + if ((ret = uvc_commit_video(video, &probe)) < 0) return ret; memcpy(&video->streaming->ctrl, &probe, sizeof probe); diff --git a/linux/drivers/media/video/uvc/uvc_video.c b/linux/drivers/media/video/uvc/uvc_video.c index b7bb23820..4af94707e 100644 --- a/linux/drivers/media/video/uvc/uvc_video.c +++ b/linux/drivers/media/video/uvc/uvc_video.c @@ -36,15 +36,22 @@ static int __uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, { __u8 type = USB_TYPE_CLASS | USB_RECIP_INTERFACE; unsigned int pipe; - int ret; pipe = (query & 0x80) ? usb_rcvctrlpipe(dev->udev, 0) : usb_sndctrlpipe(dev->udev, 0); type |= (query & 0x80) ? USB_DIR_IN : USB_DIR_OUT; - ret = usb_control_msg(dev->udev, pipe, query, type, cs << 8, + return usb_control_msg(dev->udev, pipe, query, type, cs << 8, unit << 8 | intfnum, data, size, timeout); +} + +int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, + __u8 intfnum, __u8 cs, void *data, __u16 size) +{ + int ret; + ret = __uvc_query_ctrl(dev, query, unit, intfnum, cs, data, size, + UVC_CTRL_CONTROL_TIMEOUT); if (ret != size) { uvc_printk(KERN_ERR, "Failed to query (%u) UVC control %u " "(unit %u) : %d (exp. %u).\n", query, cs, unit, ret, @@ -55,13 +62,6 @@ static int __uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, return 0; } -int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, - __u8 intfnum, __u8 cs, void *data, __u16 size) -{ - return __uvc_query_ctrl(dev, query, unit, intfnum, cs, data, size, - UVC_CTRL_CONTROL_TIMEOUT); -} - static void uvc_fixup_buffer_size(struct uvc_video_device *video, struct uvc_streaming_control *ctrl) { @@ -102,8 +102,36 @@ static int uvc_get_video_ctrl(struct uvc_video_device *video, ret = __uvc_query_ctrl(video->dev, query, 0, video->streaming->intfnum, probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL, data, size, UVC_CTRL_STREAMING_TIMEOUT); - if (ret < 0) + + if ((query == GET_MIN || query == GET_MAX) && ret == 2) { + /* Some cameras, mostly based on Bison Electronics chipsets, + * answer a GET_MIN or GET_MAX request with the wCompQuality + * field only. + */ + uvc_warn_once(video->dev, UVC_WARN_MINMAX, "UVC non " + "compliance - GET_MIN/MAX(PROBE) incorrectly " + "supported. Enabling workaround.\n"); + memset(ctrl, 0, sizeof ctrl); + ctrl->wCompQuality = le16_to_cpup((__le16 *)data); + ret = 0; goto out; + } else if (query == GET_DEF && probe == 1) { + /* Many cameras don't support the GET_DEF request on their + * video probe control. Warn once and return, the caller will + * fall back to GET_CUR. + */ + uvc_warn_once(video->dev, UVC_WARN_PROBE_DEF, "UVC non " + "compliance - GET_DEF(PROBE) not supported. " + "Enabling workaround.\n"); + ret = -EIO; + goto out; + } else if (ret != size) { + uvc_printk(KERN_ERR, "Failed to query (%u) UVC %s control : " + "%d (exp. %u).\n", query, probe ? "probe" : "commit", + ret, size); + ret = -EIO; + goto out; + } ctrl->bmHint = le16_to_cpup((__le16 *)&data[0]); ctrl->bFormatIndex = data[2]; @@ -138,13 +166,14 @@ static int uvc_get_video_ctrl(struct uvc_video_device *video, * Try to get the value from the format and frame descriptor. */ uvc_fixup_buffer_size(video, ctrl); + ret = 0; out: kfree(data); return ret; } -int uvc_set_video_ctrl(struct uvc_video_device *video, +static int uvc_set_video_ctrl(struct uvc_video_device *video, struct uvc_streaming_control *ctrl, int probe) { __u8 *data; @@ -186,6 +215,12 @@ int uvc_set_video_ctrl(struct uvc_video_device *video, video->streaming->intfnum, probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL, data, size, UVC_CTRL_STREAMING_TIMEOUT); + if (ret != size) { + uvc_printk(KERN_ERR, "Failed to set UVC %s control : " + "%d (exp. %u).\n", probe ? "probe" : "commit", + ret, size); + ret = -EIO; + } kfree(data); return ret; @@ -252,6 +287,12 @@ done: return ret; } +int uvc_commit_video(struct uvc_video_device *video, + struct uvc_streaming_control *probe) +{ + return uvc_set_video_ctrl(video, probe, 0); +} + /* ------------------------------------------------------------------------ * Video codecs */ diff --git a/linux/drivers/media/video/uvc/uvcvideo.h b/linux/drivers/media/video/uvc/uvcvideo.h index 9a6bc1aaf..595b90ee6 100644 --- a/linux/drivers/media/video/uvc/uvcvideo.h +++ b/linux/drivers/media/video/uvc/uvcvideo.h @@ -617,6 +617,7 @@ enum uvc_device_state { struct uvc_device { struct usb_device *udev; struct usb_interface *intf; + unsigned long warnings; __u32 quirks; int intfnum; char name[32]; @@ -679,6 +680,9 @@ struct uvc_driver { #define UVC_TRACE_SUSPEND (1 << 8) #define UVC_TRACE_STATUS (1 << 9) +#define UVC_WARN_MINMAX 0 +#define UVC_WARN_PROBE_DEF 1 + extern unsigned int uvc_trace_param; #define uvc_trace(flag, msg...) \ @@ -687,6 +691,12 @@ extern unsigned int uvc_trace_param; printk(KERN_DEBUG "uvcvideo: " msg); \ } while (0) +#define uvc_warn_once(dev, warn, msg...) \ + do { \ + if (!test_and_set_bit(warn, &dev->warnings)) \ + printk(KERN_INFO "uvcvideo: " msg); \ + } while (0) + #define uvc_printk(level, msg...) \ printk(level "uvcvideo: " msg) @@ -740,10 +750,10 @@ extern int uvc_video_resume(struct uvc_video_device *video); extern int uvc_video_enable(struct uvc_video_device *video, int enable); extern int uvc_probe_video(struct uvc_video_device *video, struct uvc_streaming_control *probe); +extern int uvc_commit_video(struct uvc_video_device *video, + struct uvc_streaming_control *ctrl); extern int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, __u8 intfnum, __u8 cs, void *data, __u16 size); -extern int uvc_set_video_ctrl(struct uvc_video_device *video, - struct uvc_streaming_control *ctrl, int probe); /* Status */ extern int uvc_status_init(struct uvc_device *dev); -- cgit v1.2.3 From 4c6f942c4bda31271b632907d23021b22fa1d5cf Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sun, 9 Nov 2008 16:14:07 -0500 Subject: cx18: Prevent CX23418 from clearing it's outgoing ack interrupts to driver From: Andy Walls 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. Priority: high Signed-off-by: Andy Walls --- linux/drivers/media/video/cx18/cx18-firmware.c | 11 +++++++++++ linux/drivers/media/video/cx18/cx18-io.c | 7 +++++++ linux/drivers/media/video/cx18/cx18-io.h | 1 + linux/drivers/media/video/cx18/cx18-irq.h | 1 + 4 files changed, 20 insertions(+) (limited to 'linux') diff --git a/linux/drivers/media/video/cx18/cx18-firmware.c b/linux/drivers/media/video/cx18/cx18-firmware.c index ab02da727..06f5563d6 100644 --- a/linux/drivers/media/video/cx18/cx18-firmware.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-io.c b/linux/drivers/media/video/cx18/cx18-io.c index 48a8adc83..3c6485fce 100644 --- a/linux/drivers/media/video/cx18/cx18-io.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-io.h b/linux/drivers/media/video/cx18/cx18-io.h index cb695a596..4486b73fa 100644 --- a/linux/drivers/media/video/cx18/cx18-io.h +++ b/linux/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/linux/drivers/media/video/cx18/cx18-irq.h b/linux/drivers/media/video/cx18/cx18-irq.h index d1d9d1967..6472e42c6 100644 --- a/linux/drivers/media/video/cx18/cx18-irq.h +++ b/linux/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 #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18) -- cgit v1.2.3 From d2fdf36ebdc0970621958a5b6a7df809ed103bf8 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sun, 9 Nov 2008 17:51:44 -0500 Subject: cx18: Fix unitialized variable problem upon APU firmware file read failure From: Andy Walls 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. Priority: normal Signed-off-by: Andy Walls --- linux/drivers/media/video/cx18/cx18-firmware.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/cx18/cx18-firmware.c b/linux/drivers/media/video/cx18/cx18-firmware.c index 06f5563d6..d9c5f55ab 100644 --- a/linux/drivers/media/video/cx18/cx18-firmware.c +++ b/linux/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 01dc44fd923db93973cff119d441049abc1d669b Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Mon, 10 Nov 2008 08:45:51 +0100 Subject: gspca: Let gspca handle the webcam 0471:0328 instead of sn9c102. From: Jean-Francois Moine According to http://bugzilla.kernel.org/show_bug.cgi?id=11997, the driver sn9c102 does not work for this webcam. Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/sonixj.c | 2 +- linux/drivers/media/video/sn9c102/sn9c102_devtable.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/sonixj.c b/linux/drivers/media/video/gspca/sonixj.c index 94fa7f6d6..8b5eb91e1 100644 --- a/linux/drivers/media/video/gspca/sonixj.c +++ b/linux/drivers/media/video/gspca/sonixj.c @@ -1694,8 +1694,8 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x045e, 0x00f5), BSI(SN9C105, OV7660, 0x21)}, {USB_DEVICE(0x045e, 0x00f7), BSI(SN9C105, OV7660, 0x21)}, {USB_DEVICE(0x0471, 0x0327), BSI(SN9C105, MI0360, 0x5d)}, - {USB_DEVICE(0x0471, 0x0328), BSI(SN9C105, MI0360, 0x5d)}, #endif + {USB_DEVICE(0x0471, 0x0328), BSI(SN9C105, MI0360, 0x5d)}, {USB_DEVICE(0x0471, 0x0330), BSI(SN9C105, MI0360, 0x5d)}, {USB_DEVICE(0x0c45, 0x6040), BSI(SN9C102P, HV7131R, 0x11)}, /* bw600.inf: diff --git a/linux/drivers/media/video/sn9c102/sn9c102_devtable.h b/linux/drivers/media/video/sn9c102/sn9c102_devtable.h index 845587030..390722003 100644 --- a/linux/drivers/media/video/sn9c102/sn9c102_devtable.h +++ b/linux/drivers/media/video/sn9c102/sn9c102_devtable.h @@ -96,7 +96,9 @@ static const struct usb_device_id sn9c102_id_table[] = { { SN9C102_USB_DEVICE(0x045e, 0x00f5, BRIDGE_SN9C105), }, { SN9C102_USB_DEVICE(0x045e, 0x00f7, BRIDGE_SN9C105), }, { SN9C102_USB_DEVICE(0x0471, 0x0327, BRIDGE_SN9C105), }, +#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE { SN9C102_USB_DEVICE(0x0471, 0x0328, BRIDGE_SN9C105), }, +#endif { SN9C102_USB_DEVICE(0x0c45, 0x60c0, BRIDGE_SN9C105), }, { SN9C102_USB_DEVICE(0x0c45, 0x60c2, BRIDGE_SN9C105), }, { SN9C102_USB_DEVICE(0x0c45, 0x60c8, BRIDGE_SN9C105), }, -- cgit v1.2.3 From 80dcce8327ba20e39e4e16408d7099e99e25db5a Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Mon, 10 Nov 2008 09:56:55 +0100 Subject: gspca: Remove empty lines in traces. From: Jean-Francois Moine Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/gspca.c | 2 +- linux/drivers/media/video/gspca/sonixb.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/gspca.c b/linux/drivers/media/video/gspca/gspca.c index bdae0ce2c..7a2006c3e 100644 --- a/linux/drivers/media/video/gspca/gspca.c +++ b/linux/drivers/media/video/gspca/gspca.c @@ -2008,7 +2008,7 @@ int gspca_auto_gain_n_exposure(struct gspca_dev *gspca_dev, int avg_lum, desired lumination fast (with the risc of a slight overshoot) */ steps = abs(desired_avg_lum - avg_lum) / deadzone; - PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d\n", + PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d", avg_lum, desired_avg_lum, steps); for (i = 0; i < steps; i++) { diff --git a/linux/drivers/media/video/gspca/sonixb.c b/linux/drivers/media/video/gspca/sonixb.c index b167c9109..5ca8ab780 100644 --- a/linux/drivers/media/video/gspca/sonixb.c +++ b/linux/drivers/media/video/gspca/sonixb.c @@ -877,7 +877,7 @@ static void do_autogain(struct gspca_dev *gspca_dev) else if (gspca_auto_gain_n_exposure(gspca_dev, avg_lum, sd->brightness * desired_avg_lum / 127, deadzone, GAIN_KNEE, EXPOSURE_KNEE)) { - PDEBUG(D_FRAM, "autogain: gain changed: gain: %d expo: %d\n", + PDEBUG(D_FRAM, "autogain: gain changed: gain: %d expo: %d", (int)sd->gain, (int)sd->exposure); sd->autogain_ignore_frames = AUTOGAIN_IGNORE_FRAMES; } -- cgit v1.2.3 From 28615ed3424329580117e65a34106ca408bf564b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 10 Nov 2008 19:56:20 -0200 Subject: Whitespace cleanup From: Mauro Carvalho Chehab Some recent patches added a few bad whitespaces. Remove that. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/dvb/frontends/stb0899_algo.c | 2 +- linux/drivers/media/dvb/frontends/stb0899_cfg.h | 10 +++++----- linux/drivers/media/dvb/frontends/stb0899_priv.h | 8 ++++---- linux/drivers/media/dvb/frontends/tda8261.h | 2 +- linux/drivers/media/dvb/ttpci/budget-ci.c | 10 +++++----- 5 files changed, 16 insertions(+), 16 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/dvb/frontends/stb0899_algo.c b/linux/drivers/media/dvb/frontends/stb0899_algo.c index be4546751..156906f5d 100644 --- a/linux/drivers/media/dvb/frontends/stb0899_algo.c +++ b/linux/drivers/media/dvb/frontends/stb0899_algo.c @@ -91,7 +91,7 @@ static u32 stb0899_set_srate(struct stb0899_state *state, u32 master_clk, u32 sr * SFR = (srate << 21 + master_clk) / (2 * master_clk) * * stored as 20 bit number with an offset of 4 bit: - * sfr = SFR << 4; + * sfr = SFR << 4; */ tmp = stb0899_do_div((((u64)srate) << 21) + master_clk, 2 * master_clk); diff --git a/linux/drivers/media/dvb/frontends/stb0899_cfg.h b/linux/drivers/media/dvb/frontends/stb0899_cfg.h index 45934a110..1c860e469 100644 --- a/linux/drivers/media/dvb/frontends/stb0899_cfg.h +++ b/linux/drivers/media/dvb/frontends/stb0899_cfg.h @@ -210,12 +210,12 @@ static const struct stb0899_s1_reg stb0899_s1_init_3[] = { { STB0899_RCOMPC , 0xc9 }, { STB0899_AGC1CN , 0x01 }, { STB0899_AGC1REF , 0x10 }, - { STB0899_RTC , 0x23 }, + { STB0899_RTC , 0x23 }, { STB0899_TMGCFG , 0x4e }, { STB0899_AGC2REF , 0x34 }, { STB0899_TLSR , 0x84 }, { STB0899_CFD , 0xf7 }, - { STB0899_ACLC , 0x87 }, + { STB0899_ACLC , 0x87 }, { STB0899_BCLC , 0x94 }, { STB0899_EQON , 0x41 }, { STB0899_LDT , 0xf1 }, @@ -268,10 +268,10 @@ static const struct stb0899_s1_reg stb0899_s1_init_3[] = { { STB0899_ECNT3M , 0x0a }, { STB0899_ECNT3L , 0xad }, { STB0899_FECAUTO1 , 0x06 }, - { STB0899_FECM , 0x01 }, + { STB0899_FECM , 0x01 }, { STB0899_VTH12 , 0xb0 }, { STB0899_VTH23 , 0x7a }, - { STB0899_VTH34 , 0x58 }, + { STB0899_VTH34 , 0x58 }, { STB0899_VTH56 , 0x38 }, { STB0899_VTH67 , 0x34 }, { STB0899_VTH78 , 0x24 }, @@ -280,7 +280,7 @@ static const struct stb0899_s1_reg stb0899_s1_init_3[] = { { STB0899_RSULC , 0xb1 }, /* DVB = 0xb1, DSS = 0xa1 */ { STB0899_TSULC , 0x42 }, { STB0899_RSLLC , 0x41 }, - { STB0899_TSLPL , 0x12 }, + { STB0899_TSLPL , 0x12 }, { STB0899_TSCFGH , 0x0c }, { STB0899_TSCFGM , 0x00 }, { STB0899_TSCFGL , 0x00 }, diff --git a/linux/drivers/media/dvb/frontends/stb0899_priv.h b/linux/drivers/media/dvb/frontends/stb0899_priv.h index 3d3fa6584..e57ff227b 100644 --- a/linux/drivers/media/dvb/frontends/stb0899_priv.h +++ b/linux/drivers/media/dvb/frontends/stb0899_priv.h @@ -257,10 +257,10 @@ extern int stb0899_i2c_gate_ctrl(struct dvb_frontend *fe, int enable); #if 0 extern int _stb0899_write_s2reg(struct stb0899_state *state, - u32 stb0899_i2cdev, - u32 stb0899_base_addr, - u16 stb0899_reg_offset, - u32 stb0899_data); + u32 stb0899_i2cdev, + u32 stb0899_base_addr, + u16 stb0899_reg_offset, + u32 stb0899_data); #endif #define STB0899_READ_S2REG(DEVICE, REG) (_stb0899_read_s2reg(state, DEVICE, STB0899_BASE_##REG, STB0899_OFF0_##REG)) diff --git a/linux/drivers/media/dvb/frontends/tda8261.h b/linux/drivers/media/dvb/frontends/tda8261.h index 3a0545da3..006e45351 100644 --- a/linux/drivers/media/dvb/frontends/tda8261.h +++ b/linux/drivers/media/dvb/frontends/tda8261.h @@ -43,7 +43,7 @@ extern struct dvb_frontend *tda8261_attach(struct dvb_frontend *fe, #else static inline struct dvb_frontend *tda8261_attach(struct dvb_frontend *fe, - const struct tda8261_config *config, + const struct tda8261_config *config, struct i2c_adapter *i2c) { printk(KERN_WARNING "%s: Driver disabled by Kconfig\n", __func__); diff --git a/linux/drivers/media/dvb/ttpci/budget-ci.c b/linux/drivers/media/dvb/ttpci/budget-ci.c index cfaee802e..89797e1bd 100644 --- a/linux/drivers/media/dvb/ttpci/budget-ci.c +++ b/linux/drivers/media/dvb/ttpci/budget-ci.c @@ -1178,12 +1178,12 @@ static const struct stb0899_s1_reg tt3200_stb0899_s1_init_3[] = { { STB0899_RCOMPC , 0xc9 }, { STB0899_AGC1CN , 0x41 }, { STB0899_AGC1REF , 0x10 }, - { STB0899_RTC , 0x7a }, + { STB0899_RTC , 0x7a }, { STB0899_TMGCFG , 0x4e }, { STB0899_AGC2REF , 0x34 }, { STB0899_TLSR , 0x84 }, { STB0899_CFD , 0xc7 }, - { STB0899_ACLC , 0x87 }, + { STB0899_ACLC , 0x87 }, { STB0899_BCLC , 0x94 }, { STB0899_EQON , 0x41 }, { STB0899_LDT , 0xdd }, @@ -1236,10 +1236,10 @@ static const struct stb0899_s1_reg tt3200_stb0899_s1_init_3[] = { { STB0899_ECNT3M , 0x00 }, { STB0899_ECNT3L , 0x00 }, { STB0899_FECAUTO1 , 0x06 }, - { STB0899_FECM , 0x01 }, + { STB0899_FECM , 0x01 }, { STB0899_VTH12 , 0xf0 }, { STB0899_VTH23 , 0xa0 }, - { STB0899_VTH34 , 0x78 }, + { STB0899_VTH34 , 0x78 }, { STB0899_VTH56 , 0x4e }, { STB0899_VTH67 , 0x48 }, { STB0899_VTH78 , 0x38 }, @@ -1248,7 +1248,7 @@ static const struct stb0899_s1_reg tt3200_stb0899_s1_init_3[] = { { STB0899_RSULC , 0xb1 }, /* DVB = 0xb1, DSS = 0xa1 */ { STB0899_TSULC , 0x42 }, { STB0899_RSLLC , 0x40 }, - { STB0899_TSLPL , 0x12 }, + { STB0899_TSLPL , 0x12 }, { STB0899_TSCFGH , 0x0c }, { STB0899_TSCFGM , 0x00 }, { STB0899_TSCFGL , 0x0c }, -- cgit v1.2.3 From 44fb259c62c95f15516f65a961483476ad226ff6 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 11 Nov 2008 07:39:53 -0200 Subject: Backport a few minor changes from upstream From: Mauro Carvalho Chehab kernel-sync: Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/Documentation/video4linux/bttv/CONTRIBUTORS | 2 +- linux/drivers/media/dvb/frontends/Makefile | 1 - linux/sound/pci/bt87x.c | 3 +-- 3 files changed, 2 insertions(+), 4 deletions(-) (limited to 'linux') diff --git a/linux/Documentation/video4linux/bttv/CONTRIBUTORS b/linux/Documentation/video4linux/bttv/CONTRIBUTORS index 8aad6dd93..eb41b2650 100644 --- a/linux/Documentation/video4linux/bttv/CONTRIBUTORS +++ b/linux/Documentation/video4linux/bttv/CONTRIBUTORS @@ -3,7 +3,7 @@ Contributors to bttv: Michael Chu AverMedia fix and more flexible card recognition -Alan Cox +Alan Cox Video4Linux interface and 2.1.x kernel adaptation Chris Kleitsch diff --git a/linux/drivers/media/dvb/frontends/Makefile b/linux/drivers/media/dvb/frontends/Makefile index fd24de4ae..7a19c0c7b 100644 --- a/linux/drivers/media/dvb/frontends/Makefile +++ b/linux/drivers/media/dvb/frontends/Makefile @@ -6,7 +6,6 @@ EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ EXTRA_CFLAGS += -Idrivers/media/common/tuners/ s921-objs := s921_module.o s921_core.o - stb0899-objs = stb0899_drv.o stb0899_algo.o obj-$(CONFIG_DVB_PLL) += dvb-pll.o diff --git a/linux/sound/pci/bt87x.c b/linux/sound/pci/bt87x.c index 0dd872610..8b6868462 100644 --- a/linux/sound/pci/bt87x.c +++ b/linux/sound/pci/bt87x.c @@ -775,8 +775,7 @@ static int __devinit snd_bt87x_create(struct snd_card *card, pci_disable_device(pci); return err; } - chip->mmio = ioremap_nocache(pci_resource_start(pci, 0), - pci_resource_len(pci, 0)); + chip->mmio = pci_ioremap_bar(pci, 0); if (!chip->mmio) { snd_printk(KERN_ERR "cannot remap io memory\n"); err = -ENOMEM; -- cgit v1.2.3 From e3f3e1a33e43ca8f0f548b548be7d3ff5fbd3d5e Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Tue, 11 Nov 2008 11:10:11 +0100 Subject: gspca: Let gspca handle the webcam 0c45:613a instead of sn9c102. From: Jean-Francois Moine The driver sn9c102 has no code for the ov7648 sensor. Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/sonixj.c | 2 +- linux/drivers/media/video/sn9c102/sn9c102_devtable.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/sonixj.c b/linux/drivers/media/video/gspca/sonixj.c index 8b5eb91e1..886754ffd 100644 --- a/linux/drivers/media/video/gspca/sonixj.c +++ b/linux/drivers/media/video/gspca/sonixj.c @@ -1728,8 +1728,8 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x0c45, 0x6130), BSI(SN9C120, MI0360, 0x5d)}, #endif {USB_DEVICE(0x0c45, 0x6138), BSI(SN9C120, MO4000, 0x21)}, + {USB_DEVICE(0x0c45, 0x613a), BSI(SN9C120, OV7648, 0x21)}, #if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE -/* {USB_DEVICE(0x0c45, 0x613a), BSI(SN9C120, OV7648, 0x??)}, */ {USB_DEVICE(0x0c45, 0x613b), BSI(SN9C120, OV7660, 0x21)}, {USB_DEVICE(0x0c45, 0x613c), BSI(SN9C120, HV7131R, 0x11)}, /* {USB_DEVICE(0x0c45, 0x613e), BSI(SN9C120, OV7630, 0x??)}, */ diff --git a/linux/drivers/media/video/sn9c102/sn9c102_devtable.h b/linux/drivers/media/video/sn9c102/sn9c102_devtable.h index 390722003..b0d9d9589 100644 --- a/linux/drivers/media/video/sn9c102/sn9c102_devtable.h +++ b/linux/drivers/media/video/sn9c102/sn9c102_devtable.h @@ -117,7 +117,9 @@ static const struct usb_device_id sn9c102_id_table[] = { { SN9C102_USB_DEVICE(0x0c45, 0x610f, BRIDGE_SN9C120), }, { SN9C102_USB_DEVICE(0x0c45, 0x6130, BRIDGE_SN9C120), }, /* { SN9C102_USB_DEVICE(0x0c45, 0x6138, BRIDGE_SN9C120), }, MO8000 */ +#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE { SN9C102_USB_DEVICE(0x0c45, 0x613a, BRIDGE_SN9C120), }, +#endif { SN9C102_USB_DEVICE(0x0c45, 0x613b, BRIDGE_SN9C120), }, { SN9C102_USB_DEVICE(0x0c45, 0x613c, BRIDGE_SN9C120), }, { SN9C102_USB_DEVICE(0x0c45, 0x613e, BRIDGE_SN9C120), }, -- cgit v1.2.3 From 221e0e602ebce4471e92208e29081124c173af77 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Tue, 11 Nov 2008 12:42:56 +0100 Subject: gspca: Rewrite the exchanges with the sensor ov7648 of sonixj. From: Jean-Francois Moine The new exchanges are adapted from MS-win traces and the webcams work better. Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/sonixj.c | 142 ++++++++++++++++--------------- 1 file changed, 73 insertions(+), 69 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/sonixj.c b/linux/drivers/media/video/gspca/sonixj.c index 886754ffd..93cc65b5f 100644 --- a/linux/drivers/media/video/gspca/sonixj.c +++ b/linux/drivers/media/video/gspca/sonixj.c @@ -252,13 +252,13 @@ static const __u8 sn_ov7630[] = { static const __u8 sn_ov7648[] = { /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ - 0x00, 0x21, 0x62, 0x00, 0x1a, 0x20, 0x20, 0x20, + 0x00, 0x63, 0x40, 0x00, 0x1a, 0x20, 0x20, 0x20, /* reg8 reg9 rega regb regc regd rege regf */ - 0xa1, 0x6e, 0x18, 0x65, 0x00, 0x00, 0x00, 0x10, + 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ - 0x03, 0x00, 0x00, 0x06, 0x06, 0x28, 0x1e, 0x82, + 0x03, 0x00, 0x00, 0x01, 0x00, 0x28, 0x1e, 0x00, /* reg18 reg19 reg1a reg1b reg1c reg1d reg1e reg1f */ - 0x07, 0x00, 0x00, 0x00, 0x00, 0x00 + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00 }; static const __u8 sn_ov7660[] = { @@ -490,6 +490,53 @@ static const __u8 ov7630_sensor_init[][8] = { /* {0xb1, 0x21, 0x01, 0x88, 0x70, 0x00, 0x00, 0x10}, */ {} }; + +static const __u8 ov7648_sensor_init[][8] = { + {0xa1, 0x21, 0x76, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset */ + {0xa1, 0x21, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x21, 0x03, 0xa4, 0x30, 0x88, 0x00, 0x10}, + {0xb1, 0x21, 0x11, 0x80, 0x08, 0x00, 0x00, 0x10}, + {0xc1, 0x21, 0x13, 0xa0, 0x04, 0x84, 0x00, 0x10}, + {0xd1, 0x21, 0x17, 0x1a, 0x02, 0xba, 0xf4, 0x10}, + {0xa1, 0x21, 0x1b, 0x04, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x21, 0x1f, 0x41, 0xc0, 0x80, 0x80, 0x10}, + {0xd1, 0x21, 0x23, 0xde, 0xa0, 0x80, 0x32, 0x10}, + {0xd1, 0x21, 0x27, 0xfe, 0xa0, 0x00, 0x91, 0x10}, + {0xd1, 0x21, 0x2b, 0x00, 0x88, 0x85, 0x80, 0x10}, + {0xc1, 0x21, 0x2f, 0x9c, 0x00, 0xc4, 0x00, 0x10}, + {0xd1, 0x21, 0x60, 0xa6, 0x60, 0x88, 0x12, 0x10}, + {0xd1, 0x21, 0x64, 0x88, 0x00, 0x00, 0x94, 0x10}, + {0xd1, 0x21, 0x68, 0x7a, 0x0c, 0x00, 0x00, 0x10}, + {0xd1, 0x21, 0x6c, 0x11, 0x33, 0x22, 0x00, 0x10}, + {0xd1, 0x21, 0x70, 0x11, 0x00, 0x10, 0x50, 0x10}, + {0xd1, 0x21, 0x74, 0x20, 0x06, 0x00, 0xb5, 0x10}, + {0xd1, 0x21, 0x78, 0x8a, 0x00, 0x00, 0x00, 0x10}, + {0xb1, 0x21, 0x7c, 0x00, 0x43, 0x00, 0x00, 0x10}, + + {0xd1, 0x21, 0x21, 0x86, 0x00, 0xde, 0xa0, 0x10}, +/* {0xd1, 0x21, 0x25, 0x80, 0x32, 0xfe, 0xa0, 0x10}, jfm done */ +/* {0xd1, 0x21, 0x29, 0x00, 0x91, 0x00, 0x88, 0x10}, jfm done */ + {0xb1, 0x21, 0x2d, 0x85, 0x00, 0x00, 0x00, 0x10}, +/*...*/ +/* {0xa1, 0x21, 0x12, 0x08, 0x00, 0x00, 0x00, 0x10}, jfm done */ +/* {0xa1, 0x21, 0x75, 0x06, 0x00, 0x00, 0x00, 0x10}, jfm done */ + {0xa1, 0x21, 0x19, 0x02, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x10, 0x32, 0x00, 0x00, 0x00, 0x10}, +/* {0xa1, 0x21, 0x16, 0x00, 0x00, 0x00, 0x00, 0x10}, jfm done */ +/* {0xa1, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}, * GAIN - def */ +/* {0xb1, 0x21, 0x01, 0x6c, 0x6c, 0x00, 0x00, 0x10}, * B R - def: 80 */ +/*...*/ + {0xa1, 0x21, 0x11, 0x81, 0x00, 0x00, 0x00, 0x10}, /* CLKRC */ +/* {0xa1, 0x21, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x10}, jfm done */ +/* {0xa1, 0x21, 0x16, 0x00, 0x00, 0x00, 0x00, 0x10}, jfm done */ +/* {0xa1, 0x21, 0x2a, 0x91, 0x00, 0x00, 0x00, 0x10}, jfm done */ +/* {0xa1, 0x21, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10}, jfm done */ +/* {0xb1, 0x21, 0x01, 0x64, 0x84, 0x00, 0x00, 0x10}, * B R - def: 80 */ + + {} +}; + static const __u8 ov7660_sensor_init[][8] = { {0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset SCCB */ /* (delay 20ms) */ @@ -578,64 +625,6 @@ static const __u8 ov7660_sensor_init[][8] = { {0xa1, 0x21, 0x2b, 0xc3, 0x00, 0x00, 0x00, 0x10}, {} }; -/* reg 0x04 reg 0x07 reg 0x10 */ -/* expo = (COM1 & 0x02) | ((AECHH & 0x2f) << 10) | (AECh << 2) */ - -static const __u8 ov7648_sensor_init[][8] = { - {0xC1, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00}, - {0xC1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00}, - {0xC1, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00}, - {0xA1, 0x6E, 0x3F, 0x20, 0x00, 0x00, 0x00, 0x10}, - {0xA1, 0x6E, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xA1, 0x6E, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xD1, 0x6E, 0x04, 0x02, 0xB1, 0x02, 0x39, 0x10}, - {0xD1, 0x6E, 0x08, 0x00, 0x01, 0x00, 0x00, 0x10}, - {0xD1, 0x6E, 0x0C, 0x02, 0x7F, 0x01, 0xE0, 0x10}, - {0xD1, 0x6E, 0x12, 0x03, 0x02, 0x00, 0x03, 0x10}, - {0xD1, 0x6E, 0x16, 0x85, 0x40, 0x4A, 0x40, 0x10}, - {0xC1, 0x6E, 0x1A, 0x00, 0x80, 0x00, 0x00, 0x10}, - {0xD1, 0x6E, 0x1D, 0x08, 0x03, 0x00, 0x00, 0x10}, - {0xD1, 0x6E, 0x23, 0x00, 0xB0, 0x00, 0x94, 0x10}, - {0xD1, 0x6E, 0x27, 0x58, 0x00, 0x00, 0x00, 0x10}, - {0xD1, 0x6E, 0x2D, 0x14, 0x35, 0x61, 0x84, 0x10}, - {0xD1, 0x6E, 0x31, 0xA2, 0xBD, 0xD8, 0xFF, 0x10}, - {0xD1, 0x6E, 0x35, 0x06, 0x1E, 0x12, 0x02, 0x10}, - {0xD1, 0x6E, 0x39, 0xAA, 0x53, 0x37, 0xD5, 0x10}, - {0xA1, 0x6E, 0x3D, 0xF2, 0x00, 0x00, 0x00, 0x10}, - {0xD1, 0x6E, 0x3E, 0x00, 0x00, 0x80, 0x03, 0x10}, - {0xD1, 0x6E, 0x42, 0x03, 0x00, 0x00, 0x00, 0x10}, - {0xC1, 0x6E, 0x46, 0x00, 0x80, 0x80, 0x00, 0x10}, - {0xD1, 0x6E, 0x4B, 0x02, 0xEF, 0x08, 0xCD, 0x10}, - {0xD1, 0x6E, 0x4F, 0x00, 0xD0, 0x00, 0xA0, 0x10}, - {0xD1, 0x6E, 0x53, 0x01, 0xAA, 0x01, 0x40, 0x10}, - {0xD1, 0x6E, 0x5A, 0x50, 0x04, 0x30, 0x03, 0x10}, - {0xA1, 0x6E, 0x5E, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xD1, 0x6E, 0x5F, 0x10, 0x40, 0xFF, 0x00, 0x10}, - /* {0xD1, 0x6E, 0x63, 0x40, 0x40, 0x00, 0x00, 0x10}, - {0xD1, 0x6E, 0x67, 0x00, 0x00, 0x00, 0x00, 0x10}, - * This is currently setting a - * blue tint, and some things more , i leave it here for future test if - * somene is having problems with color on this sensor - {0xD1, 0x6E, 0x6B, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xD1, 0x6E, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xC1, 0x6E, 0x73, 0x10, 0x80, 0xEB, 0x00, 0x10}, - {0xA1, 0x6E, 0x1E, 0x03, 0x00, 0x00, 0x00, 0x10}, - {0xA1, 0x6E, 0x15, 0x01, 0x00, 0x00, 0x00, 0x10}, - {0xC1, 0x6E, 0x16, 0x40, 0x40, 0x40, 0x00, 0x10}, - {0xA1, 0x6E, 0x1D, 0x08, 0x00, 0x00, 0x00, 0x10}, - {0xA1, 0x6E, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10}, - {0xA1, 0x6E, 0x07, 0xB5, 0x00, 0x00, 0x00, 0x10}, - {0xA1, 0x6E, 0x18, 0x6B, 0x00, 0x00, 0x00, 0x10}, - {0xA1, 0x6E, 0x1D, 0x08, 0x00, 0x00, 0x00, 0x10}, - {0xA1, 0x6E, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10}, - {0xA1, 0x6E, 0x07, 0xB8, 0x00, 0x00, 0x00, 0x10}, */ - {0xC1, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00}, - {0xA1, 0x6E, 0x06, 0x03, 0x00, 0x00, 0x00, 0x10}, /* Bright... */ - {0xA1, 0x6E, 0x07, 0x66, 0x00, 0x00, 0x00, 0x10}, /* B.. */ - {0xC1, 0x6E, 0x1A, 0x03, 0x65, 0x90, 0x00, 0x10}, /* Bright/Witen....*/ -/* {0xC1, 0x6E, 0x16, 0x45, 0x40, 0x60, 0x00, 0x10}, * Bright/Witene */ - {} -}; static const __u8 qtable4[] = { 0x06, 0x04, 0x04, 0x06, 0x04, 0x04, 0x06, 0x06, 0x06, 0x06, 0x08, 0x06, @@ -854,8 +843,8 @@ static int configure_gpio(struct gspca_dev *gspca_dev, break; #endif case SENSOR_OV7648: - reg_w1(gspca_dev, 0x01, 0x43); - reg_w1(gspca_dev, 0x17, 0xae); + reg_w1(gspca_dev, 0x01, 0x63); + reg_w1(gspca_dev, 0x17, 0x20); reg_w1(gspca_dev, 0x01, 0x42); break; #if 1 @@ -951,6 +940,13 @@ static void ov7648_InitSensor(struct gspca_dev *gspca_dev) { int i = 0; + i2c_w8(gspca_dev, ov7648_sensor_init[i]); + i++; +/* win: dble reset */ + i2c_w8(gspca_dev, ov7648_sensor_init[i]); /* reset */ + i++; + msleep(20); +/* win: i2c reg read 00..7f */ while (ov7648_sensor_init[i][0]) { i2c_w8(gspca_dev, ov7648_sensor_init[i]); i++; @@ -1284,7 +1280,7 @@ static int sd_start(struct gspca_dev *gspca_dev) reg17 = 0xe2; break; case SENSOR_OV7648: - reg17 = 0xae; + reg17 = 0x20; break; #if 1 /*jfm: from win trace */ @@ -1305,6 +1301,10 @@ static int sd_start(struct gspca_dev *gspca_dev) for (i = 0; i < 8; i++) reg_w(gspca_dev, 0x84, reg84, sizeof reg84); switch (sd->sensor) { + case SENSOR_OV7648: + reg_w1(gspca_dev, 0x9a, 0x0a); + reg_w1(gspca_dev, 0x99, 0x60); + break; case SENSOR_OV7660: reg_w1(gspca_dev, 0x9a, 0x05); break; @@ -1349,8 +1349,8 @@ static int sd_start(struct gspca_dev *gspca_dev) break; case SENSOR_OV7648: ov7648_InitSensor(gspca_dev); - reg17 = 0xa2; - reg1 = 0x44; + reg17 = 0x21; +/* reg1 = 0x42; * 42 - 46? */ /* if (mode) ; * 320x2... else @@ -1422,6 +1422,8 @@ static void sd_stopN(struct gspca_dev *gspca_dev) { 0xa1, 0x11, 0x02, 0x09, 0x00, 0x00, 0x00, 0x10 }; static const __u8 stopmi0360[] = { 0xb1, 0x5d, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10 }; + static const __u8 stopov7648[] = + { 0xa1, 0x21, 0x76, 0x20, 0x00, 0x00, 0x00, 0x10 }; __u8 data; const __u8 *sn9c1xx; @@ -1435,8 +1437,10 @@ static void sd_stopN(struct gspca_dev *gspca_dev) i2c_w8(gspca_dev, stopmi0360); data = 0x29; break; - case SENSOR_OV7630: case SENSOR_OV7648: + i2c_w8(gspca_dev, stopov7648); + /* fall thru */ + case SENSOR_OV7630: data = 0x29; break; default: @@ -1720,7 +1724,7 @@ static const __devinitdata struct usb_device_id device_table[] = { /* {USB_DEVICE(0x0c45, 0x6123), BSI(SN9C110, SanyoCCD, 0x??)}, */ {USB_DEVICE(0x0c45, 0x6128), BSI(SN9C110, OM6802, 0x21)}, /*sn9c325?*/ /*bw600.inf:*/ - {USB_DEVICE(0x0c45, 0x612a), BSI(SN9C110, OV7648, 0x21)}, /*sn9c325?*/ + {USB_DEVICE(0x0c45, 0x612a), BSI(SN9C120, OV7648, 0x21)}, /*sn9c110?*/ {USB_DEVICE(0x0c45, 0x612c), BSI(SN9C110, MO4000, 0x21)}, {USB_DEVICE(0x0c45, 0x612e), BSI(SN9C110, OV7630, 0x21)}, /* {USB_DEVICE(0x0c45, 0x612f), BSI(SN9C110, ICM105C, 0x??)}, */ -- cgit v1.2.3 From 01f91062d013d4880c29186f61ff3572418e647d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 11 Nov 2008 09:48:27 -0200 Subject: cx88-dvb: MFE attachment clean-up for HVR-3000/4000 From: Darron Broad This cleans-up MFE attachment for these cards plus two cases of potential memory leak on attachment failure. Priority: normal Signed-off-by: Darron Broad Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/cx88/cx88-dvb.c | 118 +++++++++++++++--------------- 1 file changed, 57 insertions(+), 61 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/cx88/cx88-dvb.c b/linux/drivers/media/video/cx88/cx88-dvb.c index ce4e0b77c..5a9fafd25 100644 --- a/linux/drivers/media/video/cx88/cx88-dvb.c +++ b/linux/drivers/media/video/cx88/cx88-dvb.c @@ -613,7 +613,7 @@ static int dvb_register(struct cx8802_dev *dev) /* Get the first frontend */ fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1); if (!fe0) - return -EINVAL; + goto frontend_detach; /* multi-frontend gate control is undefined or defaults to fe0 */ dev->frontends.gate = 0; @@ -660,38 +660,35 @@ static int dvb_register(struct cx8802_dev *dev) } break; case CX88_BOARD_HAUPPAUGE_HVR3000: + /* MFE frontend 1 */ + mfe_shared = 1; + dev->frontends.gate = 2; /* DVB-S init */ fe0->dvb.frontend = dvb_attach(cx24123_attach, - &hauppauge_novas_config, - &dev->core->i2c_adap); + &hauppauge_novas_config, + &dev->core->i2c_adap); if (fe0->dvb.frontend) { - if (!dvb_attach(isl6421_attach, fe0->dvb.frontend, - &dev->core->i2c_adap, 0x08, ISL6421_DCL, 0x00)) { - dprintk( 1, "%s(): HVR3000 - DVB-S LNB Init: failed\n", __func__); - } - } else { - dprintk( 1, "%s(): HVR3000 - DVB-S Init: failed\n", __func__); + if (!dvb_attach(isl6421_attach, + fe0->dvb.frontend, + &dev->core->i2c_adap, + 0x08, ISL6421_DCL, 0x00)) + goto frontend_detach; } - /* DVB-T init */ + /* MFE frontend 2 */ fe1 = videobuf_dvb_get_frontend(&dev->frontends, 2); - if (fe1) { - dev->frontends.gate = 2; - mfe_shared = 1; - fe1->dvb.frontend = dvb_attach(cx22702_attach, - &hauppauge_hvr_config, - &dev->core->i2c_adap); - if (fe1->dvb.frontend) { - fe1->dvb.frontend->id = 1; - if(!dvb_attach(simple_tuner_attach, fe1->dvb.frontend, - &dev->core->i2c_adap, 0x61, - TUNER_PHILIPS_FMD1216ME_MK3)) { - dprintk( 1, "%s(): HVR3000 - DVB-T misc Init: failed\n", __func__); - } - } else { - dprintk( 1, "%s(): HVR3000 - DVB-T Init: failed\n", __func__); - } - } else { - dprintk( 1, "%s(): HVR3000 - DVB-T Init: can't find frontend 2.\n", __func__); + if (!fe1) + goto frontend_detach; + /* DVB-T init */ + fe1->dvb.frontend = dvb_attach(cx22702_attach, + &hauppauge_hvr_config, + &dev->core->i2c_adap); + if (fe1->dvb.frontend) { + fe1->dvb.frontend->id = 1; + if (!dvb_attach(simple_tuner_attach, + fe1->dvb.frontend, + &dev->core->i2c_adap, + 0x61, TUNER_PHILIPS_FMD1216ME_MK3)) + goto frontend_detach; } break; case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS: @@ -1005,48 +1002,47 @@ static int dvb_register(struct cx8802_dev *dev) } break; case CX88_BOARD_HAUPPAUGE_HVR4000: + /* MFE frontend 1 */ + mfe_shared = 1; + dev->frontends.gate = 2; /* DVB-S/S2 Init */ fe0->dvb.frontend = dvb_attach(cx24116_attach, - &hauppauge_hvr4000_config, - &dev->core->i2c_adap); + &hauppauge_hvr4000_config, + &dev->core->i2c_adap); if (fe0->dvb.frontend) { - if(!dvb_attach(isl6421_attach, fe0->dvb.frontend, - &dev->core->i2c_adap, 0x08, ISL6421_DCL, 0x00)) { - dprintk( 1, "%s(): HVR4000 - DVB-S LNB Init: failed\n", __func__); - } - } else { - dprintk( 1, "%s(): HVR4000 - DVB-S Init: failed\n", __func__); + if (!dvb_attach(isl6421_attach, + fe0->dvb.frontend, + &dev->core->i2c_adap, + 0x08, ISL6421_DCL, 0x00)) + goto frontend_detach; } - /* DVB-T Init */ + /* MFE frontend 2 */ fe1 = videobuf_dvb_get_frontend(&dev->frontends, 2); - if (fe1) { - dev->frontends.gate = 2; - mfe_shared = 1; - fe1->dvb.frontend = dvb_attach(cx22702_attach, - &hauppauge_hvr_config, - &dev->core->i2c_adap); - if (fe1->dvb.frontend) { - fe1->dvb.frontend->id = 1; - if(!dvb_attach(simple_tuner_attach, fe1->dvb.frontend, - &dev->core->i2c_adap, 0x61, - TUNER_PHILIPS_FMD1216ME_MK3)) { - dprintk( 1, "%s(): HVR4000 - DVB-T misc Init: failed\n", __func__); - } - } else { - dprintk( 1, "%s(): HVR4000 - DVB-T Init: failed\n", __func__); - } - } else { - dprintk( 1, "%s(): HVR4000 - DVB-T Init: can't find frontend 2.\n", __func__); + if (!fe1) + goto frontend_detach; + /* DVB-T Init */ + fe1->dvb.frontend = dvb_attach(cx22702_attach, + &hauppauge_hvr_config, + &dev->core->i2c_adap); + if (fe1->dvb.frontend) { + fe1->dvb.frontend->id = 1; + if (!dvb_attach(simple_tuner_attach, + fe1->dvb.frontend, + &dev->core->i2c_adap, + 0x61, TUNER_PHILIPS_FMD1216ME_MK3)) + goto frontend_detach; } break; case CX88_BOARD_HAUPPAUGE_HVR4000LITE: fe0->dvb.frontend = dvb_attach(cx24116_attach, - &hauppauge_hvr4000_config, - &dev->core->i2c_adap); + &hauppauge_hvr4000_config, + &dev->core->i2c_adap); if (fe0->dvb.frontend) { - dvb_attach(isl6421_attach, fe0->dvb.frontend, - &dev->core->i2c_adap, - 0x08, ISL6421_DCL, 0x00); + if (!dvb_attach(isl6421_attach, + fe0->dvb.frontend, + &dev->core->i2c_adap, + 0x08, ISL6421_DCL, 0x00)) + goto frontend_detach; } break; case CX88_BOARD_PROF_6200: @@ -1102,7 +1098,7 @@ static int dvb_register(struct cx8802_dev *dev) printk(KERN_ERR "%s/2: frontend initialization failed\n", core->name); - return -EINVAL; + goto frontend_detach; } /* define general-purpose callback pointer */ fe0->dvb.frontend->callback = cx88_tuner_callback; -- cgit v1.2.3 From f340cd6d7fd5375d619b1a902e8d005f79cd604a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 11 Nov 2008 09:50:02 -0200 Subject: saa7134-dvb: MFE attachment clean-up for saa-7134 dvb From: Darron Broad This cleans-up MFE attachment for saa-7134 dvb devices. note: MFE is not fully implemented here yet. Priority: normal Signed-off-by: Darron Broad Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/saa7134/saa7134-dvb.c | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/saa7134/saa7134-dvb.c b/linux/drivers/media/video/saa7134/saa7134-dvb.c index 099761046..704442f55 100644 --- a/linux/drivers/media/video/saa7134/saa7134-dvb.c +++ b/linux/drivers/media/video/saa7134/saa7134-dvb.c @@ -954,20 +954,14 @@ static int dvb_init(struct saa7134_dev *dev) /* FIXME: add support for multi-frontend */ mutex_init(&dev->frontends.lock); INIT_LIST_HEAD(&dev->frontends.felist); - dev->frontends.active_fe_id = 0; printk(KERN_INFO "%s() allocating 1 frontend\n", __func__); - - if (videobuf_dvb_alloc_frontend(&dev->frontends, 1) == NULL) { + fe0 = videobuf_dvb_alloc_frontend(&dev->frontends, 1); + if (!fe0) { printk(KERN_ERR "%s() failed to alloc\n", __func__); return -ENOMEM; } - /* Get the first frontend */ - fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1); - if (!fe0) - return -EINVAL; - /* init struct videobuf_dvb */ dev->ts.nr_bufs = 32; dev->ts.nr_packets = 32*4; @@ -1380,7 +1374,7 @@ static int dvb_init(struct saa7134_dev *dev) }; if (!fe0->dvb.frontend) - return -1; + goto dettach_frontend; fe = dvb_attach(xc2028_attach, fe0->dvb.frontend, &cfg); if (!fe) { @@ -1392,7 +1386,7 @@ static int dvb_init(struct saa7134_dev *dev) if (NULL == fe0->dvb.frontend) { printk(KERN_ERR "%s/dvb: frontend initialization failed\n", dev->name); - return -1; + goto dettach_frontend; } /* define general-purpose callback pointer */ fe0->dvb.frontend->callback = saa7134_tuner_callback; @@ -1415,11 +1409,8 @@ static int dvb_init(struct saa7134_dev *dev) return ret; dettach_frontend: - if (fe0->dvb.frontend) - dvb_frontend_detach(fe0->dvb.frontend); - fe0->dvb.frontend = NULL; - - return -1; + videobuf_dvb_dealloc_frontends(&dev->frontends); + return -EINVAL; } static int dvb_fini(struct saa7134_dev *dev) @@ -1458,8 +1449,7 @@ static int dvb_fini(struct saa7134_dev *dev) } } } - if (fe0->dvb.frontend) - videobuf_dvb_unregister_bus(&dev->frontends); + videobuf_dvb_unregister_bus(&dev->frontends); return 0; } -- cgit v1.2.3 From 2c2545da4d6f060cd6b64c71469a61cd455ba6d9 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 11 Nov 2008 17:43:40 +0100 Subject: uvcvideo: Enable compilation on kernels older than 2.6.22 From: Laurent Pinchart The uvcvideo driver makes use of the usb_endpoint_* functions as well as the list_first_entry and uninitialized_var macros. Add them to v4l/compat.h and update the driver with kernel version-based conditional compilation sections. Priority: normal Signed-off-by: Laurent Pinchart --- linux/drivers/media/video/uvc/uvc_ctrl.c | 2 ++ linux/drivers/media/video/uvc/uvc_driver.c | 2 ++ linux/drivers/media/video/uvc/uvc_status.c | 12 ++++++++++++ linux/drivers/media/video/uvc/uvc_v4l2.c | 6 ++++++ linux/drivers/media/video/uvc/uvc_video.c | 4 ++++ linux/drivers/media/video/uvc/uvcvideo.h | 1 + 6 files changed, 27 insertions(+) (limited to 'linux') diff --git a/linux/drivers/media/video/uvc/uvc_ctrl.c b/linux/drivers/media/video/uvc/uvc_ctrl.c index f16aafe9c..9de8a1db5 100644 --- a/linux/drivers/media/video/uvc/uvc_ctrl.c +++ b/linux/drivers/media/video/uvc/uvc_ctrl.c @@ -15,7 +15,9 @@ #include #include #include +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17) #include +#endif #include #include #include diff --git a/linux/drivers/media/video/uvc/uvc_driver.c b/linux/drivers/media/video/uvc/uvc_driver.c index 2760ddb96..046b98871 100644 --- a/linux/drivers/media/video/uvc/uvc_driver.c +++ b/linux/drivers/media/video/uvc/uvc_driver.c @@ -1915,7 +1915,9 @@ struct uvc_driver uvc_driver = { .reset_resume = uvc_reset_resume, #endif .id_table = uvc_ids, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) .supports_autosuspend = 1, +#endif }, }; diff --git a/linux/drivers/media/video/uvc/uvc_status.c b/linux/drivers/media/video/uvc/uvc_status.c index 5d60b264d..ab62d7bf9 100644 --- a/linux/drivers/media/video/uvc/uvc_status.c +++ b/linux/drivers/media/video/uvc/uvc_status.c @@ -15,7 +15,11 @@ #include #include #include +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18) +#include +#else #include +#endif #include "uvcvideo.h" @@ -45,7 +49,11 @@ static int uvc_input_init(struct uvc_device *dev) input->name = dev->name; input->phys = phys; usb_to_input_id(udev, &input->id); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) input->dev.parent = &dev->intf->dev; +#else + input->cdev.dev = &dev->intf->dev; +#endif set_bit(EV_KEY, input->evbit); set_bit(BTN_0, input->keybit); @@ -118,7 +126,11 @@ static void uvc_event_control(struct uvc_device *dev, __u8 *data, int len) data[1], data[3], attrs[data[4]], len); } +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) +static void uvc_status_complete(struct urb *urb, struct pt_regs *regs) +#else static void uvc_status_complete(struct urb *urb) +#endif { struct uvc_device *dev = urb->context; int len, ret; diff --git a/linux/drivers/media/video/uvc/uvc_v4l2.c b/linux/drivers/media/video/uvc/uvc_v4l2.c index 624bf74de..5588698e2 100644 --- a/linux/drivers/media/video/uvc/uvc_v4l2.c +++ b/linux/drivers/media/video/uvc/uvc_v4l2.c @@ -413,14 +413,18 @@ static int uvc_v4l2_open(struct inode *inode, struct file *file) goto done; } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) ret = usb_autopm_get_interface(video->dev->intf); if (ret < 0) goto done; +#endif /* Create the device handle. */ handle = kzalloc(sizeof *handle, GFP_KERNEL); if (handle == NULL) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) usb_autopm_put_interface(video->dev->intf); +#endif ret = -ENOMEM; goto done; } @@ -459,7 +463,9 @@ static int uvc_v4l2_release(struct inode *inode, struct file *file) kfree(handle); file->private_data = NULL; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) usb_autopm_put_interface(video->dev->intf); +#endif kref_put(&video->dev->kref, uvc_delete); return 0; } diff --git a/linux/drivers/media/video/uvc/uvc_video.c b/linux/drivers/media/video/uvc/uvc_video.c index 4af94707e..987e110fe 100644 --- a/linux/drivers/media/video/uvc/uvc_video.c +++ b/linux/drivers/media/video/uvc/uvc_video.c @@ -566,7 +566,11 @@ static void uvc_video_decode_bulk(struct urb *urb, } } +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) +static void uvc_video_complete(struct urb *urb, struct pt_regs *regs) +#else static void uvc_video_complete(struct urb *urb) +#endif { struct uvc_video_device *video = urb->context; struct uvc_video_queue *queue = &video->queue; diff --git a/linux/drivers/media/video/uvc/uvcvideo.h b/linux/drivers/media/video/uvc/uvcvideo.h index 595b90ee6..f95040380 100644 --- a/linux/drivers/media/video/uvc/uvcvideo.h +++ b/linux/drivers/media/video/uvc/uvcvideo.h @@ -4,6 +4,7 @@ #include #include +#include "compat.h" /* * Dynamic controls -- cgit v1.2.3 From 37e435002c62d00de4f01da3de1af44b3fd8773f Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 11 Nov 2008 18:04:30 +0100 Subject: uvcvideo: Use {get|set}_unaligned_le32 macros From: Laurent Pinchart Replace combination of endianess conversion and unaligned access macros with {get|set}_unaligned_*. Priority: normal Signed-off-by: Laurent Pinchart --- linux/drivers/media/video/uvc/uvc_video.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/uvc/uvc_video.c b/linux/drivers/media/video/uvc/uvc_video.c index 987e110fe..54bc53bea 100644 --- a/linux/drivers/media/video/uvc/uvc_video.c +++ b/linux/drivers/media/video/uvc/uvc_video.c @@ -142,14 +142,11 @@ static int uvc_get_video_ctrl(struct uvc_video_device *video, ctrl->wCompQuality = le16_to_cpup((__le16 *)&data[12]); ctrl->wCompWindowSize = le16_to_cpup((__le16 *)&data[14]); ctrl->wDelay = le16_to_cpup((__le16 *)&data[16]); - ctrl->dwMaxVideoFrameSize = - le32_to_cpu(get_unaligned((__le32 *)&data[18])); - ctrl->dwMaxPayloadTransferSize = - le32_to_cpu(get_unaligned((__le32 *)&data[22])); + ctrl->dwMaxVideoFrameSize = get_unaligned_le32(&data[18]); + ctrl->dwMaxPayloadTransferSize = get_unaligned_le32(&data[22]); if (size == 34) { - ctrl->dwClockFrequency = - le32_to_cpu(get_unaligned((__le32 *)&data[26])); + ctrl->dwClockFrequency = get_unaligned_le32(&data[26]); ctrl->bmFramingInfo = data[30]; ctrl->bPreferedVersion = data[31]; ctrl->bMinVersion = data[32]; @@ -197,14 +194,11 @@ static int uvc_set_video_ctrl(struct uvc_video_device *video, /* Note: Some of the fields below are not required for IN devices (see * UVC spec, 4.3.1.1), but we still copy them in case support for OUT * devices is added in the future. */ - put_unaligned(cpu_to_le32(ctrl->dwMaxVideoFrameSize), - (__le32 *)&data[18]); - put_unaligned(cpu_to_le32(ctrl->dwMaxPayloadTransferSize), - (__le32 *)&data[22]); + put_unaligned_le32(ctrl->dwMaxVideoFrameSize, &data[18]); + put_unaligned_le32(ctrl->dwMaxPayloadTransferSize, &data[22]); if (size == 34) { - put_unaligned(cpu_to_le32(ctrl->dwClockFrequency), - (__le32 *)&data[26]); + put_unaligned_le32(ctrl->dwClockFrequency, &data[26]); data[30] = ctrl->bmFramingInfo; data[31] = ctrl->bPreferedVersion; data[32] = ctrl->bMinVersion; -- cgit v1.2.3 From 347a206147fa5c4958c0881ef90d14362978d80a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 11 Nov 2008 18:11:28 +0100 Subject: uvcvideo: Fix unaligned memory access. From: Michael Hennerich Use the unaligned memory access macros when accessing unaligned memory. Priority: normal Signed-off-by: Michael Hennerich Signed-off-by: Bryan Wu Signed-off-by: Laurent Pinchart --- linux/drivers/media/video/uvc/uvc_driver.c | 39 +++++++++++++++--------------- 1 file changed, 19 insertions(+), 20 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/uvc/uvc_driver.c b/linux/drivers/media/video/uvc/uvc_driver.c index 046b98871..0a0ef6611 100644 --- a/linux/drivers/media/video/uvc/uvc_driver.c +++ b/linux/drivers/media/video/uvc/uvc_driver.c @@ -32,6 +32,7 @@ #include #include #include +#include #include @@ -452,20 +453,20 @@ static int uvc_parse_format(struct uvc_device *dev, frame->bFrameIndex = buffer[3]; frame->bmCapabilities = buffer[4]; - frame->wWidth = le16_to_cpup((__le16 *)&buffer[5]); - frame->wHeight = le16_to_cpup((__le16 *)&buffer[7]); - frame->dwMinBitRate = le32_to_cpup((__le32 *)&buffer[9]); - frame->dwMaxBitRate = le32_to_cpup((__le32 *)&buffer[13]); + frame->wWidth = get_unaligned_le16(&buffer[5]); + frame->wHeight = get_unaligned_le16(&buffer[7]); + frame->dwMinBitRate = get_unaligned_le32(&buffer[9]); + frame->dwMaxBitRate = get_unaligned_le32(&buffer[13]); if (ftype != VS_FRAME_FRAME_BASED) { frame->dwMaxVideoFrameBufferSize = - le32_to_cpup((__le32 *)&buffer[17]); + get_unaligned_le32(&buffer[17]); frame->dwDefaultFrameInterval = - le32_to_cpup((__le32 *)&buffer[21]); + get_unaligned_le32(&buffer[21]); frame->bFrameIntervalType = buffer[25]; } else { frame->dwMaxVideoFrameBufferSize = 0; frame->dwDefaultFrameInterval = - le32_to_cpup((__le32 *)&buffer[17]); + get_unaligned_le32(&buffer[17]); frame->bFrameIntervalType = buffer[21]; } frame->dwFrameInterval = *intervals; @@ -488,7 +489,7 @@ static int uvc_parse_format(struct uvc_device *dev, * some other divisions by zero which could happen. */ for (i = 0; i < n; ++i) { - interval = le32_to_cpup((__le32 *)&buffer[26+4*i]); + interval = get_unaligned_le32(&buffer[26+4*i]); *(*intervals)++ = interval ? interval : 1; } @@ -832,8 +833,7 @@ static int uvc_parse_vendor_control(struct uvc_device *dev, unit->type = VC_EXTENSION_UNIT; memcpy(unit->extension.guidExtensionCode, &buffer[4], 16); unit->extension.bNumControls = buffer[20]; - unit->extension.bNrInPins = - le16_to_cpup((__le16 *)&buffer[21]); + unit->extension.bNrInPins = get_unaligned_le16(&buffer[21]); unit->extension.baSourceID = (__u8 *)unit + sizeof *unit; memcpy(unit->extension.baSourceID, &buffer[22], p); unit->extension.bControlSize = buffer[22+p]; @@ -877,8 +877,8 @@ static int uvc_parse_standard_control(struct uvc_device *dev, return -EINVAL; } - dev->uvc_version = le16_to_cpup((__le16 *)&buffer[3]); - dev->clock_frequency = le32_to_cpup((__le32 *)&buffer[7]); + dev->uvc_version = get_unaligned_le16(&buffer[3]); + dev->clock_frequency = get_unaligned_le32(&buffer[7]); /* Parse all USB Video Streaming interfaces. */ for (i = 0; i < n; ++i) { @@ -905,7 +905,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev, /* Make sure the terminal type MSB is not null, otherwise it * could be confused with a unit. */ - type = le16_to_cpup((__le16 *)&buffer[4]); + type = get_unaligned_le16(&buffer[4]); if ((type & 0xff00) == 0) { uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " "interface %d INPUT_TERMINAL %d has invalid " @@ -947,11 +947,11 @@ static int uvc_parse_standard_control(struct uvc_device *dev, term->camera.bControlSize = n; term->camera.bmControls = (__u8 *)term + sizeof *term; term->camera.wObjectiveFocalLengthMin = - le16_to_cpup((__le16 *)&buffer[8]); + get_unaligned_le16(&buffer[8]); term->camera.wObjectiveFocalLengthMax = - le16_to_cpup((__le16 *)&buffer[10]); + get_unaligned_le16(&buffer[10]); term->camera.wOcularFocalLength = - le16_to_cpup((__le16 *)&buffer[12]); + get_unaligned_le16(&buffer[12]); memcpy(term->camera.bmControls, &buffer[15], n); } else if (UVC_ENTITY_TYPE(term) == ITT_MEDIA_TRANSPORT_INPUT) { term->media.bControlSize = n; @@ -987,7 +987,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev, /* Make sure the terminal type MSB is not null, otherwise it * could be confused with a unit. */ - type = le16_to_cpup((__le16 *)&buffer[4]); + type = get_unaligned_le16(&buffer[4]); if ((type & 0xff00) == 0) { uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " "interface %d OUTPUT_TERMINAL %d has invalid " @@ -1061,7 +1061,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev, unit->type = buffer[2]; unit->processing.bSourceID = buffer[4]; unit->processing.wMaxMultiplier = - le16_to_cpup((__le16 *)&buffer[5]); + get_unaligned_le16(&buffer[5]); unit->processing.bControlSize = buffer[7]; unit->processing.bmControls = (__u8 *)unit + sizeof *unit; memcpy(unit->processing.bmControls, &buffer[8], n); @@ -1096,8 +1096,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev, unit->type = buffer[2]; memcpy(unit->extension.guidExtensionCode, &buffer[4], 16); unit->extension.bNumControls = buffer[20]; - unit->extension.bNrInPins = - le16_to_cpup((__le16 *)&buffer[21]); + unit->extension.bNrInPins = get_unaligned_le16(&buffer[21]); unit->extension.baSourceID = (__u8 *)unit + sizeof *unit; memcpy(unit->extension.baSourceID, &buffer[22], p); unit->extension.bControlSize = buffer[22+p]; -- cgit v1.2.3 From cbc456f2703b542b2548726fee33cb664004047a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 11 Nov 2008 22:13:47 -0200 Subject: v4l core: add support for enumerating frame sizes and intervals From: Mauro Carvalho Chehab video_ioctl2 lacks implementation of those two ioctls: - VIDIOC_ENUM_FRAMESIZES and VIDIOC_ENUM_FRAMEINTERVALS Adds implementation for those. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/v4l2-ioctl.c | 67 ++++++++++++++++++++++++++++++++++ linux/include/media/v4l2-ioctl.h | 6 +++ 2 files changed, 73 insertions(+) (limited to 'linux') diff --git a/linux/drivers/media/video/v4l2-ioctl.c b/linux/drivers/media/video/v4l2-ioctl.c index b38d4954c..40b041402 100644 --- a/linux/drivers/media/video/v4l2-ioctl.c +++ b/linux/drivers/media/video/v4l2-ioctl.c @@ -1748,6 +1748,73 @@ static int __video_do_ioctl(struct file *file, ret = ops->vidioc_s_hw_freq_seek(file, fh, p); break; } + case VIDIOC_ENUM_FRAMESIZES: + { + struct v4l2_frmsizeenum *p = arg; + + if (!ops->vidioc_enum_framesizes) + break; + + memset(p, 0, sizeof(*p)); + + ret = ops->vidioc_enum_framesizes(file, fh, p); + dbgarg(cmd, + "index=%d, pixelformat=%d, type=%d ", + p->index, p->pixel_format, p->type); + switch (p->type) { + case V4L2_FRMSIZE_TYPE_DISCRETE: + dbgarg2("width = %d, height=%d\n", + p->discrete.width, p->discrete.height); + break; + case V4L2_FRMSIZE_TYPE_STEPWISE: + dbgarg2("min %dx%d, max %dx%d, step %dx%d\n", + p->stepwise.min_width, p->stepwise.min_height, + p->stepwise.step_width, p->stepwise.step_height, + p->stepwise.max_width, p->stepwise.max_height); + break; + case V4L2_FRMSIZE_TYPE_CONTINUOUS: + dbgarg2("continuous\n"); + break; + default: + dbgarg2("- Unknown type!\n"); + } + + break; + } + case VIDIOC_ENUM_FRAMEINTERVALS: + { + struct v4l2_frmivalenum *p = arg; + + if (!ops->vidioc_enum_frameintervals) + break; + + memset(p, 0, sizeof(*p)); + + ret = ops->vidioc_enum_frameintervals(file, fh, p); + dbgarg(cmd, + "index=%d, pixelformat=%d, width=%d, height=%d, type=%d ", + p->index, p->pixel_format, + p->width, p->height, p->type); + switch (p->type) { + case V4L2_FRMIVAL_TYPE_DISCRETE: + dbgarg2("fps=%d/%d\n", + p->discrete.numerator, + p->discrete.denominator); + break; + case V4L2_FRMIVAL_TYPE_STEPWISE: + dbgarg2("min=%d, max=%d, step=%d\n", + p->stepwise.min, p->stepwise.max, + p->stepwise.step); + break; + case V4L2_FRMIVAL_TYPE_CONTINUOUS: + dbgarg2("continuous\n"); + break; + default: + dbgarg2("- Unknown type!\n"); + } + break; + } + default: { if (!ops->vidioc_default) diff --git a/linux/include/media/v4l2-ioctl.h b/linux/include/media/v4l2-ioctl.h index f5985724c..c884432f9 100644 --- a/linux/include/media/v4l2-ioctl.h +++ b/linux/include/media/v4l2-ioctl.h @@ -232,6 +232,12 @@ struct v4l2_ioctl_ops { int (*vidioc_g_chip_ident) (struct file *file, void *fh, struct v4l2_chip_ident *chip); + int (*vidioc_enum_framesizes) (struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize); + + int (*vidioc_enum_frameintervals) (struct file *file, void *fh, + struct v4l2_frmivalenum *fival); + /* For other private ioctls */ int (*vidioc_default) (struct file *file, void *fh, int cmd, void *arg); -- cgit v1.2.3 From 1f0f6394be119a5916c8b5c6caa4910336544945 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 11 Nov 2008 22:15:03 -0200 Subject: v4l core: a few get ioctls were lacking memory clean From: Mauro Carvalho Chehab Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/v4l2-ioctl.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'linux') diff --git a/linux/drivers/media/video/v4l2-ioctl.c b/linux/drivers/media/video/v4l2-ioctl.c index 40b041402..bc4128d60 100644 --- a/linux/drivers/media/video/v4l2-ioctl.c +++ b/linux/drivers/media/video/v4l2-ioctl.c @@ -1480,9 +1480,15 @@ static int __video_do_ioctl(struct file *file, case VIDIOC_G_CROP: { struct v4l2_crop *p = arg; + __u32 type; if (!ops->vidioc_g_crop) break; + + type = p->type; + memset(p, 0, sizeof(*p)); + p->type = type; + dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names)); ret = ops->vidioc_g_crop(file, fh, p); if (!ret) @@ -1503,10 +1509,16 @@ static int __video_do_ioctl(struct file *file, case VIDIOC_CROPCAP: { struct v4l2_cropcap *p = arg; + __u32 type; /*FIXME: Should also show v4l2_fract pixelaspect */ if (!ops->vidioc_cropcap) break; + + type = p->type; + memset(p, 0, sizeof(*p)); + p->type = type; + dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names)); ret = ops->vidioc_cropcap(file, fh, p); if (!ret) { @@ -1521,6 +1533,9 @@ static int __video_do_ioctl(struct file *file, if (!ops->vidioc_g_jpegcomp) break; + + memset(p, 0, sizeof(*p)); + ret = ops->vidioc_g_jpegcomp(file, fh, p); if (!ret) dbgarg(cmd, "quality=%d, APPn=%d, " -- cgit v1.2.3 From bcabc8095d80312e5fcc817372cf97ae51b55cbd Mon Sep 17 00:00:00 2001 From: Douglas Schilling Landgraf Date: Wed, 12 Nov 2008 00:27:59 -0200 Subject: ttusb_dec: Add NULL pointer validation From: Douglas Schilling Landgraf Added validation for NULL pointer Priority: high Signed-off-by: Douglas Schilling Landgraf --- linux/drivers/media/dvb/ttusb-dec/ttusb_dec.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'linux') diff --git a/linux/drivers/media/dvb/ttusb-dec/ttusb_dec.c b/linux/drivers/media/dvb/ttusb-dec/ttusb_dec.c index 5c2885e17..9259cfe54 100644 --- a/linux/drivers/media/dvb/ttusb-dec/ttusb_dec.c +++ b/linux/drivers/media/dvb/ttusb-dec/ttusb_dec.c @@ -1166,6 +1166,12 @@ static int ttusb_dec_alloc_iso_urbs(struct ttusb_dec *dec) ISO_BUF_COUNT), &dec->iso_dma_handle); + if (!dec->iso_buffer) { + dprintk("%s: pci_alloc_consistent - not enough memory\n", + __func__); + return -ENOMEM; + } + memset(dec->iso_buffer, 0, ISO_FRAME_SIZE * (FRAMES_PER_ISO_BUF * ISO_BUF_COUNT)); -- cgit v1.2.3 From be08890b0323e39e6026b00048343971e10033a9 Mon Sep 17 00:00:00 2001 From: Douglas Schilling Landgraf Date: Wed, 12 Nov 2008 00:30:49 -0200 Subject: dvb-ttusb-budget: Add NULL pointer validation From: Douglas Schilling Landgraf Added validation for NULL pointer Priority: high Signed-off-by: Douglas Schilling Landgraf --- linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'linux') diff --git a/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c b/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c index 13085a8e2..9271e2366 100644 --- a/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c +++ b/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c @@ -828,6 +828,12 @@ static int ttusb_alloc_iso_urbs(struct ttusb *ttusb) ISO_BUF_COUNT, &ttusb->iso_dma_handle); + if (!ttusb->iso_buffer) { + dprintk("%s: pci_alloc_consistent - not enough memory\n", + __func__); + return -ENOMEM; + } + memset(ttusb->iso_buffer, 0, ISO_FRAME_SIZE * FRAMES_PER_ISO_BUF * ISO_BUF_COUNT); -- cgit v1.2.3 From 008d3cb607b312c44919e629541ff1e5cc12ae43 Mon Sep 17 00:00:00 2001 From: Douglas Schilling Landgraf Date: Wed, 12 Nov 2008 00:37:39 -0200 Subject: dvb-ttusb-budget: Add validation for ttusb_alloc_iso_urbs From: Douglas Schilling Landgraf Added validation for ttusb_alloc_iso_urbs Priority: high Signed-off-by: Douglas Schilling Landgraf --- linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c b/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c index 9271e2366..8775b78af 100644 --- a/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c +++ b/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c @@ -1685,7 +1685,14 @@ static int ttusb_probe(struct usb_interface *intf, const struct usb_device_id *i ttusb_setup_interfaces(ttusb); - ttusb_alloc_iso_urbs(ttusb); + result = ttusb_alloc_iso_urbs(ttusb); + if (result < 0) { + dprintk("%s: ttusb_alloc_iso_urbs - failed\n", __func__); + mutex_unlock(&ttusb->semi2c); + kfree(ttusb); + return result; + } + if (ttusb_init_controller(ttusb)) printk("ttusb_init_controller: error\n"); -- cgit v1.2.3 From cc6988d1d04471ba46cd740cca95c3fddfefc7bf Mon Sep 17 00:00:00 2001 From: Douglas Schilling Landgraf Date: Wed, 12 Nov 2008 00:47:57 -0200 Subject: ttusb_dec: fix memory leak From: Douglas Schilling Landgraf Free allocated memory Priority: high Signed-off-by: Douglas Schilling Landgraf --- linux/drivers/media/dvb/ttusb-dec/ttusb_dec.c | 1 + 1 file changed, 1 insertion(+) (limited to 'linux') diff --git a/linux/drivers/media/dvb/ttusb-dec/ttusb_dec.c b/linux/drivers/media/dvb/ttusb-dec/ttusb_dec.c index 9259cfe54..7cfc2e4d0 100644 --- a/linux/drivers/media/dvb/ttusb-dec/ttusb_dec.c +++ b/linux/drivers/media/dvb/ttusb-dec/ttusb_dec.c @@ -1269,6 +1269,7 @@ static int ttusb_dec_init_usb(struct ttusb_dec *dec) dec->irq_buffer = usb_buffer_alloc(dec->udev,IRQ_PACKET_SIZE, GFP_ATOMIC, &dec->irq_dma_handle); if(!dec->irq_buffer) { + usb_free_urb(dec->irq_urb); return -ENOMEM; } usb_fill_int_urb(dec->irq_urb, dec->udev,dec->irq_pipe, -- cgit v1.2.3 From 9690379dc06a080d7172bc3b2d21544f2bf3d8e1 Mon Sep 17 00:00:00 2001 From: Douglas Schilling Landgraf Date: Wed, 12 Nov 2008 00:56:56 -0200 Subject: usb-urb: fix memory leak From: Douglas Schilling Landgraf Free allocated memory Priority: high Signed-off-by: Douglas Schilling Landgraf --- linux/drivers/media/dvb/dvb-usb/usb-urb.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/dvb/dvb-usb/usb-urb.c b/linux/drivers/media/dvb/dvb-usb/usb-urb.c index 5caec2034..eb3aaaf7a 100644 --- a/linux/drivers/media/dvb/dvb-usb/usb-urb.c +++ b/linux/drivers/media/dvb/dvb-usb/usb-urb.c @@ -139,7 +139,7 @@ stream->buf_list[stream->buf_num], (long long)stream->dma_addr[stream->buf_num]) static int usb_bulk_urb_init(struct usb_data_stream *stream) { - int i; + int i, j; if ((i = usb_allocate_stream_buffers(stream,stream->props.count, stream->props.u.bulk.buffersize)) < 0) @@ -147,9 +147,13 @@ static int usb_bulk_urb_init(struct usb_data_stream *stream) /* allocate the URBs */ for (i = 0; i < stream->props.count; i++) { - if ((stream->urb_list[i] = usb_alloc_urb(0,GFP_ATOMIC)) == NULL) + stream->urb_list[i] = usb_alloc_urb(0, GFP_ATOMIC); + if (!stream->urb_list[i]) { + deb_mem("not enough memory for urb_alloc_urb!.\n"); + for (j = 0; j < i; j++) + usb_free_urb(stream->urb_list[i]); return -ENOMEM; - + } usb_fill_bulk_urb( stream->urb_list[i], stream->udev, usb_rcvbulkpipe(stream->udev,stream->props.endpoint), stream->buf_list[i], @@ -174,9 +178,14 @@ static int usb_isoc_urb_init(struct usb_data_stream *stream) for (i = 0; i < stream->props.count; i++) { struct urb *urb; int frame_offset = 0; - if ((stream->urb_list[i] = - usb_alloc_urb(stream->props.u.isoc.framesperurb,GFP_ATOMIC)) == NULL) + + stream->urb_list[i] = usb_alloc_urb(stream->props.u.isoc.framesperurb, GFP_ATOMIC); + if (!stream->urb_list[i]) { + deb_mem("not enough memory for urb_alloc_urb!\n"); + for (j = 0; j < i; j++) + usb_free_urb(stream->urb_list[i]); return -ENOMEM; + } urb = stream->urb_list[i]; -- cgit v1.2.3 From 01ea3eb2c9dbd7537a6cd1c915bf7a098d80715d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 12 Nov 2008 05:04:48 +0000 Subject: Add chip id for em2874 to list of known chips From: Devin Heitmueller Add em2874 chip id Add chip id for em2874 to list of known chips Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-cards.c | 4 ++++ linux/drivers/media/video/em28xx/em28xx-reg.h | 1 + 2 files changed, 5 insertions(+) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 4f078da31..072561e47 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -1328,6 +1328,10 @@ void em28xx_pre_card_setup(struct em28xx *dev) case CHIP_ID_EM2860: em28xx_info("chip ID is em2860\n"); break; + case CHIP_ID_EM2874: + em28xx_info("chip ID is em2874\n"); + dev->wait_after_write = 0; + break; case CHIP_ID_EM2883: em28xx_info("chip ID is em2882/em2883\n"); dev->wait_after_write = 0; diff --git a/linux/drivers/media/video/em28xx/em28xx-reg.h b/linux/drivers/media/video/em28xx/em28xx-reg.h index fac1ab23f..0892df17f 100644 --- a/linux/drivers/media/video/em28xx/em28xx-reg.h +++ b/linux/drivers/media/video/em28xx/em28xx-reg.h @@ -86,4 +86,5 @@ enum em28xx_chip_id { CHIP_ID_EM2860 = 34, CHIP_ID_EM2883 = 36, + CHIP_ID_EM2874 = 65, }; -- cgit v1.2.3 From 828680031c009faa7795dea3359744f38193eb57 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 12 Nov 2008 05:04:53 +0000 Subject: Remove unused variable from em28xx-audio.c From: Devin Heitmueller Remove unused variable from em28xx-audio.c Fix warning for unused "ret" variable Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-audio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-audio.c b/linux/drivers/media/video/em28xx/em28xx-audio.c index bec42349e..cabce87b7 100644 --- a/linux/drivers/media/video/em28xx/em28xx-audio.c +++ b/linux/drivers/media/video/em28xx/em28xx-audio.c @@ -496,7 +496,7 @@ static int em28xx_audio_init(struct em28xx *dev) struct snd_card *card; #endif static int devnr; - int ret, err; + int err; if (dev->has_audio_class) { /* This device does not support the extension (in this case -- cgit v1.2.3 From 9293ae2ae1a0de7312fc28fb3b77727d50248592 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 12 Nov 2008 05:04:57 +0000 Subject: Add a EM28XX_NODECODER option to the list of available decoders From: Devin Heitmueller Add a EM28XX_NODECODER option to the list of available decoders Add a EM28XX_NODECODER option to the list of available decoders. This option becomes important for devices that do not have analog support. Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx.h | 1 + 1 file changed, 1 insertion(+) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index d73b8c983..ce18c4e2b 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -269,6 +269,7 @@ struct em28xx_input { #define INPUT(nr) (&em28xx_boards[dev->model].input[nr]) enum em28xx_decoder { + EM28XX_NODECODER, EM28XX_TVP5150, EM28XX_SAA7113, EM28XX_SAA7114 -- cgit v1.2.3 From 5ac5a13ff2fd49780ebe8662765833c58a4bf669 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 12 Nov 2008 05:05:02 +0000 Subject: Remember chip id of devices at initialization From: Devin Heitmueller Remember chip id of devices at initialization When setting up the device, remember the chip id, so we can control behavior in the future without having to read the register continuously. Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-cards.c | 1 + linux/drivers/media/video/em28xx/em28xx.h | 1 + 2 files changed, 2 insertions(+) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 072561e47..adc323bef 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -1324,6 +1324,7 @@ void em28xx_pre_card_setup(struct em28xx *dev) dev->wait_after_write = 5; rc = em28xx_read_reg(dev, EM28XX_R0A_CHIPID); if (rc > 0) { + dev->chip_id = rc; switch (rc) { case CHIP_ID_EM2860: em28xx_info("chip ID is em2860\n"); diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index ce18c4e2b..5c1bdb295 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -376,6 +376,7 @@ struct em28xx { char name[30]; /* name (including minor) of the device */ int model; /* index in the device_data struct */ int devno; /* marks the number of this device */ + enum em28xx_chip_id chip_id; unsigned int is_em2800:1; unsigned int has_msp34xx:1; unsigned int has_tda9887:1; -- cgit v1.2.3 From 94a01f9a0f103d0960f2c005ed6c995982489054 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 12 Nov 2008 05:05:06 +0000 Subject: Support different GPIO/GPO registers for newer devices From: Devin Heitmueller Support different GPIO/GPO registers for newer devices Empia moved the location of the GPIO/GPO registers in newer devices. Add the ability to specify the relocated registers (including caching of register contents). Thanks for Ray Lu from Empia for providing the em2874 datasheet. Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-cards.c | 15 ++++++++++++--- linux/drivers/media/video/em28xx/em28xx-core.c | 8 ++++---- linux/drivers/media/video/em28xx/em28xx-reg.h | 3 +++ linux/drivers/media/video/em28xx/em28xx.h | 3 +++ 4 files changed, 22 insertions(+), 7 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index adc323bef..c9aacfcfc 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -1317,11 +1317,13 @@ void em28xx_pre_card_setup(struct em28xx *dev) { int rc; - rc = em28xx_read_reg(dev, EM2880_R04_GPO); - if (rc >= 0) - dev->reg_gpo = rc; + /* Set the default GPO/GPIO for legacy devices */ + dev->reg_gpo_num = EM2880_R04_GPO; + dev->reg_gpio_num = EM28XX_R08_GPIO; dev->wait_after_write = 5; + + /* Based on the Chip ID, set the device configuration */ rc = em28xx_read_reg(dev, EM28XX_R0A_CHIPID); if (rc > 0) { dev->chip_id = rc; @@ -1331,6 +1333,7 @@ void em28xx_pre_card_setup(struct em28xx *dev) break; case CHIP_ID_EM2874: em28xx_info("chip ID is em2874\n"); + dev->reg_gpio_num = EM2874_R80_GPIO; dev->wait_after_write = 0; break; case CHIP_ID_EM2883: @@ -1341,6 +1344,12 @@ void em28xx_pre_card_setup(struct em28xx *dev) em28xx_info("em28xx chip ID = %d\n", rc); } } + + /* Prepopulate cached GPO register content */ + rc = em28xx_read_reg(dev, dev->reg_gpo_num); + if (rc >= 0) + dev->reg_gpo = rc; + em28xx_set_model(dev); /* request some modules */ diff --git a/linux/drivers/media/video/em28xx/em28xx-core.c b/linux/drivers/media/video/em28xx/em28xx-core.c index 1a30e361c..27088f55a 100644 --- a/linux/drivers/media/video/em28xx/em28xx-core.c +++ b/linux/drivers/media/video/em28xx/em28xx-core.c @@ -175,9 +175,9 @@ int em28xx_write_regs(struct em28xx *dev, u16 reg, char *buf, int len) Not sure what happens on reading GPO register. */ if (rc >= 0) { - if (reg == EM2880_R04_GPO) + if (reg == dev->reg_gpo_num) dev->reg_gpo = buf[0]; - else if (reg == EM28XX_R08_GPIO) + else if (reg == dev->reg_gpio_num) dev->reg_gpio = buf[0]; } @@ -196,9 +196,9 @@ static int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val, u8 newval; /* Uses cache for gpo/gpio registers */ - if (reg == EM2880_R04_GPO) + if (reg == dev->reg_gpo_num) oldval = dev->reg_gpo; - else if (reg == EM28XX_R08_GPIO) + else if (reg == dev->reg_gpio_num) oldval = dev->reg_gpio; else oldval = em28xx_read_reg(dev, reg); diff --git a/linux/drivers/media/video/em28xx/em28xx-reg.h b/linux/drivers/media/video/em28xx/em28xx-reg.h index 0892df17f..1f0e8a34e 100644 --- a/linux/drivers/media/video/em28xx/em28xx-reg.h +++ b/linux/drivers/media/video/em28xx/em28xx-reg.h @@ -76,6 +76,9 @@ #define EM28XX_R10_LINE_IN_AC97 0x10 #define EM28XX_R14_VIDEO_AC97 0x14 +/* em2874 registers */ +#define EM2874_R80_GPIO 0x80 + /* register settings */ #define EM2800_AUDIO_SRC_TUNER 0x0d #define EM2800_AUDIO_SRC_LINE 0x0c diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index 5c1bdb295..d752d9cac 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -474,6 +474,9 @@ struct em28xx { enum em28xx_mode mode; + /* register numbers for GPO/GPIO registers */ + u16 reg_gpo_num, reg_gpio_num; + /* Caches GPO and GPIO registers */ unsigned char reg_gpo, reg_gpio; -- cgit v1.2.3 From 215aa89455a6032cdf605bdd4b5064b8c14b8067 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 12 Nov 2008 05:05:10 +0000 Subject: Skip reading eeprom in newer Empia devices From: Devin Heitmueller Skip reading eeprom in newer Empia devices Empia switched to a 16-bit addressable eeprom in newer devices. While we could certainly write a routine to read the eeprom, there is nothing of use in there that cannot be accessed through registers, and there is the risk that we could corrupt the eeprom (since a 16-bit read call is interpreted as a write call by 8-bit eeproms). So just be safe and bail out of the function. Thanks for Ray Lu from Empia for providing the em2874 datasheet. Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-i2c.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-i2c.c b/linux/drivers/media/video/em28xx/em28xx-i2c.c index 3bab56b99..51ecdcd34 100644 --- a/linux/drivers/media/video/em28xx/em28xx-i2c.c +++ b/linux/drivers/media/video/em28xx/em28xx-i2c.c @@ -332,6 +332,17 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char *eedata, int len) struct em28xx_eeprom *em_eeprom = (void *)eedata; int i, err, size = len, block; + if (dev->chip_id == CHIP_ID_EM2874) { + /* Empia switched to a 16-bit addressable eeprom in newer + devices. While we could certainly write a routine to read + the eeprom, there is nothing of use in there that cannot be + accessed through registers, and there is the risk that we + could corrupt the eeprom (since a 16-bit read call is + interpreted as a write call by 8-bit eeproms). + */ + return 0; + } + dev->i2c_client.addr = 0xa0 >> 1; /* Check if board has eeprom */ -- cgit v1.2.3 From 0b65108ee51fa8796b38304395fc9bd64321d98b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 12 Nov 2008 05:05:15 +0000 Subject: Fix possible null pointer dereference in info message From: Devin Heitmueller Fix possible null pointer dereference in info message Fix case where we could end up dereferencing a NULL pointer if dev->vdev or dev->vbi_dev were not set properly. Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-video.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index a4493da8a..040ea9ef3 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -1656,8 +1656,6 @@ static void em28xx_release_resources(struct em28xx *dev) /*FIXME: I2C IR should be disconnected */ - em28xx_info("V4L2 devices /dev/video%d and /dev/vbi%d deregistered\n", - dev->vdev->num, dev->vbi_dev->num); list_del(&dev->devlist); if (dev->sbutton_input_dev) em28xx_deregister_snapshot_button(dev); @@ -1669,6 +1667,8 @@ static void em28xx_release_resources(struct em28xx *dev) dev->radio_dev = NULL; } if (dev->vbi_dev) { + em28xx_info("V4L2 device /dev/vbi%d deregistered\n", + dev->vbi_dev->num); if (-1 != dev->vbi_dev->minor) video_unregister_device(dev->vbi_dev); else @@ -1676,6 +1676,8 @@ static void em28xx_release_resources(struct em28xx *dev) dev->vbi_dev = NULL; } if (dev->vdev) { + em28xx_info("V4L2 device /dev/video%d deregistered\n", + dev->vdev->num); if (-1 != dev->vdev->minor) video_unregister_device(dev->vdev); else -- cgit v1.2.3 From e3afb5932ce80a7a6bb8b796801e21b684b31611 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 12 Nov 2008 05:02:17 +0000 Subject: Handle changes to endpoint layout in em2874 From: Devin Heitmueller Handle changes to endpoint layout in em2874 Empia moved around their endpoint configuration in newer chips, so accommodate the changes Thanks for Ray Lu from Empia for providing the em2874 datasheet. Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-video.c | 36 ++++++++++++++++--------- 1 file changed, 23 insertions(+), 13 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index 040ea9ef3..6332eb07e 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -2206,7 +2206,7 @@ static int em28xx_usb_probe(struct usb_interface *interface, struct usb_interface *uif; struct em28xx *dev = NULL; int retval = -ENODEV; - int i, nr, ifnum; + int i, nr, ifnum, isoc_pipe; udev = usb_get_dev(interface_to_usbdev(interface)); ifnum = interface->altsetting[0].desc.bInterfaceNumber; @@ -2233,19 +2233,29 @@ static int em28xx_usb_probe(struct usb_interface *interface, ifnum, interface->altsetting[0].desc.bInterfaceClass); - endpoint = &interface->cur_altsetting->endpoint[1].desc; + endpoint = &interface->cur_altsetting->endpoint[0].desc; /* check if the device has the iso in endpoint at the correct place */ - if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != - USB_ENDPOINT_XFER_ISOC) { - em28xx_err(DRIVER_NAME " probing error: endpoint is non-ISO endpoint!\n"); - em28xx_devused &= ~(1<bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) { - em28xx_err(DRIVER_NAME " probing error: endpoint is ISO OUT endpoint!\n"); - em28xx_devused &= ~(1<bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == + USB_ENDPOINT_XFER_ISOC && + (interface->altsetting[1].endpoint[0].desc.wMaxPacketSize == 940)) + { + /* It's a newer em2874/em2875 device */ + isoc_pipe = 0; + } else { + isoc_pipe = 1; + endpoint = &interface->cur_altsetting->endpoint[1].desc; + if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != + USB_ENDPOINT_XFER_ISOC) { + em28xx_err(DRIVER_NAME " probing error: endpoint is non-ISO endpoint!\n"); + em28xx_devused &= ~(1<bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) { + em28xx_err(DRIVER_NAME " probing error: endpoint is ISO OUT endpoint!\n"); + em28xx_devused &= ~(1<= EM28XX_MAXBOARDS) { @@ -2296,7 +2306,7 @@ static int em28xx_usb_probe(struct usb_interface *interface, } for (i = 0; i < dev->num_alt ; i++) { - u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[1].desc. + u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].desc. wMaxPacketSize); dev->alt_max_pkt_size[i] = (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); -- cgit v1.2.3 From b4d980248bc67a682e8762f9471a3966503d0a42 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 12 Nov 2008 05:05:19 +0000 Subject: Don't load em28xx audio module for digital-only devices From: Devin Heitmueller Don't load em28xx audio module for digital-only devices Rework the logic so that the em28xx-alsa module does not get loaded for devices that don't support analog audio (such as the em2874) Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-audio.c | 10 +++++---- linux/drivers/media/video/em28xx/em28xx-video.c | 29 +++++++++++++++++++++---- linux/drivers/media/video/em28xx/em28xx.h | 1 + 3 files changed, 32 insertions(+), 8 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-audio.c b/linux/drivers/media/video/em28xx/em28xx-audio.c index cabce87b7..b86b0870d 100644 --- a/linux/drivers/media/video/em28xx/em28xx-audio.c +++ b/linux/drivers/media/video/em28xx/em28xx-audio.c @@ -498,9 +498,10 @@ static int em28xx_audio_init(struct em28xx *dev) static int devnr; int err; - if (dev->has_audio_class) { + if (dev->has_alsa_audio != 1) { /* This device does not support the extension (in this case - the device is expecting the snd-usb-audio module) */ + the device is expecting the snd-usb-audio module or + doesn't have analog audio support at all) */ return 0; } @@ -552,9 +553,10 @@ static int em28xx_audio_fini(struct em28xx *dev) if (dev == NULL) return 0; - if (dev->has_audio_class) { + if (dev->has_alsa_audio != 1) { /* This device does not support the extension (in this case - the device is expecting the snd-usb-audio module */ + the device is expecting the snd-usb-audio module or + doesn't have analog audio support at all) */ return 0; } diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index 6332eb07e..19684ad45 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -1984,6 +1984,19 @@ static struct video_device *em28xx_vdev_init(struct em28xx *dev, return vfd; } +int em28xx_supports_audio_extension(struct em28xx *dev) +{ + /* The chip dictates whether we support the Empia analog audio + extension */ + switch (dev->chip_id) { + case CHIP_ID_EM2874: + /* Either a digital-only device or provides AC97 audio */ + return 0; + case CHIP_ID_EM2883: + default: + return 1; + } +} /* * em28xx_init_dev() @@ -2173,7 +2186,7 @@ static void request_module_async(struct work_struct *work) if (dev->has_audio_class) request_module("snd-usb-audio"); - else + else if (dev->has_alsa_audio) request_module("em28xx-alsa"); if (dev->has_dvb) @@ -2287,9 +2300,6 @@ static int em28xx_usb_probe(struct usb_interface *interface, } } - printk(KERN_INFO DRIVER_NAME " %s usb audio class\n", - dev->has_audio_class ? "Has" : "Doesn't have"); - /* compute alternate max packet sizes */ uif = udev->actconfig->interface[0]; @@ -2324,6 +2334,17 @@ static int em28xx_usb_probe(struct usb_interface *interface, em28xx_info("Found %s\n", em28xx_boards[dev->model].name); + if (dev->has_audio_class == 0) { + /* We don't have a USB audio class, let's see if we support + ALSA Audio */ + dev->has_alsa_audio = em28xx_supports_audio_extension(dev); + if (dev->has_alsa_audio) + printk(KERN_INFO DRIVER_NAME " supports alsa audio\n"); + } else { + printk(KERN_INFO DRIVER_NAME " has usb audio class\n"); + } + + /* save our data pointer in this interface device */ usb_set_intfdata(interface, dev); diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index d752d9cac..12507422f 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -382,6 +382,7 @@ struct em28xx { unsigned int has_tda9887:1; unsigned int stream_on:1; /* Locks streams */ unsigned int has_audio_class:1; + unsigned int has_alsa_audio:1; unsigned int has_12mhz_i2s:1; unsigned int max_range_640_480:1; unsigned int has_dvb:1; -- cgit v1.2.3 From 2cb2731d0d8cc3f0abde2ae9929321a2bd032d59 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 12 Nov 2008 05:05:24 +0000 Subject: Properly support capture start on em2874 From: Devin Heitmueller Properly support capture start on em2874 The transport stream enable register moved in the em2874, so make it work properly. Thanks for Ray Lu from Empia for providing the em2874 datasheet. Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-core.c | 18 ++++++++++++++++++ linux/drivers/media/video/em28xx/em28xx-reg.h | 9 +++++++++ 2 files changed, 27 insertions(+) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-core.c b/linux/drivers/media/video/em28xx/em28xx-core.c index 27088f55a..48c2caabe 100644 --- a/linux/drivers/media/video/em28xx/em28xx-core.c +++ b/linux/drivers/media/video/em28xx/em28xx-core.c @@ -359,6 +359,24 @@ int em28xx_colorlevels_set_default(struct em28xx *dev) int em28xx_capture_start(struct em28xx *dev, int start) { int rc; + + if (dev->chip_id == CHIP_ID_EM2874) { + /* The Transport Stream Enable Register moved in em2874 */ + if (!start) { + rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE, + 0x00, + EM2874_TS1_CAPTURE_ENABLE); + return rc; + } + + /* Enable Transport Stream */ + rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE, + EM2874_TS1_CAPTURE_ENABLE, + EM2874_TS1_CAPTURE_ENABLE); + return rc; + } + + /* FIXME: which is the best order? */ /* video registers are sampled by VREF */ rc = em28xx_write_reg_bits(dev, EM28XX_R0C_USBSUSP, diff --git a/linux/drivers/media/video/em28xx/em28xx-reg.h b/linux/drivers/media/video/em28xx/em28xx-reg.h index 1f0e8a34e..50d1790d8 100644 --- a/linux/drivers/media/video/em28xx/em28xx-reg.h +++ b/linux/drivers/media/video/em28xx/em28xx-reg.h @@ -77,8 +77,17 @@ #define EM28XX_R14_VIDEO_AC97 0x14 /* em2874 registers */ +#define EM2874_R5F_TS_ENABLE 0x5f #define EM2874_R80_GPIO 0x80 +/* em2874 Transport Stream Enable Register (0x5f) */ +#define EM2874_TS1_CAPTURE_ENABLE (1 << 0) +#define EM2874_TS1_FILTER_ENABLE (1 << 1) +#define EM2874_TS1_NULL_DISCARD (1 << 2) +#define EM2874_TS2_CAPTURE_ENABLE (1 << 4) +#define EM2874_TS2_FILTER_ENABLE (1 << 5) +#define EM2874_TS2_NULL_DISCARD (1 << 6) + /* register settings */ #define EM2800_AUDIO_SRC_TUNER 0x0d #define EM2800_AUDIO_SRC_LINE 0x0c -- cgit v1.2.3 From 3fd0630b68e2110e8c61c0915242ba20231d4641 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 12 Nov 2008 05:05:28 +0000 Subject: Add registration for Pinnacle 80e ATSC tuner From: Devin Heitmueller Add registration for Pinnacle 80e ATSC tuner Register the em2874 based Pinnacle 80e device. Note that support for this device also requires the new drx-j driver (which is not available yet) Thanks for Ray Lu from Empia for providing the em2874 datasheet. Thanks to Joerg Schindler from Pinnacle for providing sample hardware. Thanks to Rainer Miethling from Pinnacle for providing engineering support. Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- linux/Documentation/video4linux/CARDLIST.em28xx | 1 + linux/drivers/media/video/em28xx/em28xx-cards.c | 34 +++++++++++++++++++++++++ linux/drivers/media/video/em28xx/em28xx.h | 1 + 3 files changed, 36 insertions(+) (limited to 'linux') diff --git a/linux/Documentation/video4linux/CARDLIST.em28xx b/linux/Documentation/video4linux/CARDLIST.em28xx index 187cc48d0..ea537f5ae 100644 --- a/linux/Documentation/video4linux/CARDLIST.em28xx +++ b/linux/Documentation/video4linux/CARDLIST.em28xx @@ -57,3 +57,4 @@ 56 -> Pinnacle Hybrid Pro (2) (em2882) [2304:0226] 57 -> Kworld PlusTV HD Hybrid 330 (em2883) [eb1a:a316] 58 -> Compro VideoMate ForYou/Stereo (em2820/em2840) [185b:2041] + 59 -> Pinnacle PCTV HD Mini (em2874) [2304:023f] diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index c9aacfcfc..75024a252 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -1088,6 +1088,20 @@ struct em28xx_board em28xx_boards[] = { .amux = EM28XX_AMUX_LINE_IN, } }, }, + [EM2874_BOARD_PINNACLE_PCTV_80E] = { + .name = "Pinnacle PCTV HD Mini", + .vchannels = 0, + .tuner_type = TUNER_ABSENT, + .has_dvb = 1, + .decoder = EM28XX_NODECODER, +#ifdef DJH_DEBUG + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = EM28XX_AMUX_LINE_IN, + } }, +#endif + }, }; const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards); @@ -1185,6 +1199,8 @@ struct usb_device_id em28xx_id_table [] = { .driver_info = EM2882_BOARD_PINNACLE_HYBRID_PRO }, { USB_DEVICE(0x2304, 0x0227), .driver_info = EM2880_BOARD_PINNACLE_PCTV_HD_PRO }, + { USB_DEVICE(0x2304, 0x023f), + .driver_info = EM2874_BOARD_PINNACLE_PCTV_80E }, { USB_DEVICE(0x0413, 0x6023), .driver_info = EM2800_BOARD_LEADTEK_WINFAST_USBII }, { USB_DEVICE(0x093b, 0xa005), @@ -1260,6 +1276,17 @@ static struct em28xx_reg_seq em2882_terratec_hybrid_xs_digital[] = { { -1, -1, -1, -1}, }; +/* Pinnacle PCTV HD Mini (80e) GPIOs + 0-5: not used + 6: demod reset, active low + 7: LED on, active high */ +static struct em28xx_reg_seq em2874_pinnacle_80e_digital[] = { + {EM28XX_R06_I2C_CLK, 0x45, 0xff, 10}, /*400 KHz*/ + {EM2874_R80_GPIO, 0x80, 0xff, 100},/*Demod reset*/ + {EM2874_R80_GPIO, 0xc0, 0xff, 10}, + { -1, -1, -1, -1}, +}; + /* * EEPROM hash table for devices with generic USB IDs */ @@ -1523,6 +1550,13 @@ void em28xx_pre_card_setup(struct em28xx *dev) /* enables audio for that device */ em28xx_write_regs_req(dev, 0x00, 0x08, "\xfd", 1); break; + + case EM2874_BOARD_PINNACLE_PCTV_80E: + /* Set 400 KHz clock */ + em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x45", 1); + + dev->digital_gpio = em2874_pinnacle_80e_digital; + break; } em28xx_gpio_set(dev, dev->tun_analog_gpio); diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index 12507422f..aa1588c22 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -98,6 +98,7 @@ #define EM2882_BOARD_PINNACLE_HYBRID_PRO 56 #define EM2883_BOARD_KWORLD_HYBRID_A316 57 #define EM2820_BOARD_COMPRO_VIDEOMATE_FORYOU 58 +#define EM2874_BOARD_PINNACLE_PCTV_80E 59 /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 -- cgit v1.2.3 From a3c3f7456fd59e54b7c0333ca9bb32b8be70044e Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 12 Nov 2008 02:03:02 -0200 Subject: v4l core: fix debug printk for enumberating framerates From: Mauro Carvalho Chehab Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/v4l2-ioctl.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/v4l2-ioctl.c b/linux/drivers/media/video/v4l2-ioctl.c index bc4128d60..86dd04026 100644 --- a/linux/drivers/media/video/v4l2-ioctl.c +++ b/linux/drivers/media/video/v4l2-ioctl.c @@ -1817,9 +1817,13 @@ static int __video_do_ioctl(struct file *file, p->discrete.denominator); break; case V4L2_FRMIVAL_TYPE_STEPWISE: - dbgarg2("min=%d, max=%d, step=%d\n", - p->stepwise.min, p->stepwise.max, - p->stepwise.step); + dbgarg2("min=%d/%d, max=%d/%d, step=%d/%d\n", + p->stepwise.min.numerator, + p->stepwise.min.denominator, + p->stepwise.max.numerator, + p->stepwise.max.denominator, + p->stepwise.step.numerator, + p->stepwise.step.denominator); break; case V4L2_FRMIVAL_TYPE_CONTINUOUS: dbgarg2("continuous\n"); -- cgit v1.2.3 From ae39f27bbdb9e06fee01813e98aef9499e409e59 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 12 Nov 2008 09:41:29 -0200 Subject: em28xx: Properly implement poll support for IR's From: Mauro Carvalho Chehab The first em28xx were based on i2c IR's. However, some newer designs are coming with a polling-based IR. Those are done by reading a register set at em28xx. This patch adds core polling support for those devices. Later patches will add support for some device-specific IR's. This patch adds the same basic IR polling code used by bttv, cx88 and saa7134, and shares the common getkey masks defined at ir-common. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-cards.c | 2 + linux/drivers/media/video/em28xx/em28xx-input.c | 260 ++++++++++++++++++++++-- linux/drivers/media/video/em28xx/em28xx-reg.h | 7 + linux/drivers/media/video/em28xx/em28xx-video.c | 4 + linux/drivers/media/video/em28xx/em28xx.h | 6 +- 5 files changed, 264 insertions(+), 15 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 75024a252..8d92cb05b 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -1848,4 +1848,6 @@ void em28xx_card_setup(struct em28xx *dev) #endif em28xx_config_tuner(dev); + + em28xx_ir_init(dev); } diff --git a/linux/drivers/media/video/em28xx/em28xx-input.c b/linux/drivers/media/video/em28xx/em28xx-input.c index 8d21eaad9..c3c9cba30 100644 --- a/linux/drivers/media/video/em28xx/em28xx-input.c +++ b/linux/drivers/media/video/em28xx/em28xx-input.c @@ -39,12 +39,42 @@ static unsigned int ir_debug; module_param(ir_debug, int, 0644); MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]"); -#define dprintk(fmt, arg...) \ +#define i2cdprintk(fmt, arg...) \ if (ir_debug) { \ printk(KERN_DEBUG "%s/ir: " fmt, ir->c.name , ## arg); \ } -/* ----------------------------------------------------------------------- */ +#define dprintk(fmt, arg...) \ + if (ir_debug) { \ + printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg); \ + } + +/********************************************************** + Polling structure used by em28xx IR's + **********************************************************/ + +struct em28xx_IR { + struct em28xx *dev; + struct input_dev *input; + struct ir_input_state ir; + char name[32]; + char phys[32]; + + /* poll external decoder */ + int polling; + struct work_struct work; + struct timer_list timer; + u32 last_gpio; + u32 mask_keycode; + u32 mask_keydown; + u32 mask_keyup; + + int (*get_key)(struct em28xx_IR *); +}; + +/********************************************************** + I2C IR based get keycodes - should be used with ir-kbd-i2c + **********************************************************/ int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) { @@ -52,7 +82,7 @@ int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) /* poll IR chip */ if (1 != i2c_master_recv(&ir->c, &b, 1)) { - dprintk("read error\n"); + i2cdprintk("read error\n"); return -EIO; } @@ -60,7 +90,7 @@ int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) down, while 0xff indicates that no button is hold down. 0xfe sequences are sometimes interrupted by 0xFF */ - dprintk("key %02x\n", b); + i2cdprintk("key %02x\n", b); if (b == 0xff) return 0; @@ -74,7 +104,6 @@ int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) return 1; } - int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) { unsigned char buf[2]; @@ -103,7 +132,7 @@ int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) ((buf[0]&0x10)>>3) | /* 0000 0010 */ ((buf[0]&0x20)>>5); /* 0000 0001 */ - dprintk("ir hauppauge (em2840): code=0x%02x (rcv=0x%02x)\n", + i2cdprintk("ir hauppauge (em2840): code=0x%02x (rcv=0x%02x)\n", code, buf[0]); /* return key */ @@ -120,11 +149,11 @@ int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, /* poll IR chip */ if (3 != i2c_master_recv(&ir->c, buf, 3)) { - dprintk("read error\n"); + i2cdprintk("read error\n"); return -EIO; } - dprintk("key %02x\n", buf[2]&0x3f); + i2cdprintk("key %02x\n", buf[2]&0x3f); if (buf[0] != 0x00) return 0; @@ -134,6 +163,215 @@ int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, return 1; } +/********************************************************** + Poll based get keycode functions + **********************************************************/ + +static int default_polling_getkey(struct em28xx_IR *ir) +{ + struct em28xx *dev = ir->dev; + int rc; + u32 msg; + + /* Read key toggle, brand, and key code */ + rc = dev->em28xx_read_reg_req_len(dev, 0, EM28XX_R45_IR, + (u8 *)&msg, sizeof(msg)); + if (rc < 0) + return rc; + + return (int)(msg & 0x7fffffffl); +} + +/********************************************************** + Polling code for em28xx + **********************************************************/ + +static void em28xx_ir_handle_key(struct em28xx_IR *ir) +{ + int gpio; + u32 data; + + /* read gpio value */ + gpio = ir->get_key(ir); + if (gpio < 0) + return; + + if (gpio == ir->last_gpio) + return; + ir->last_gpio = gpio; + + /* extract data */ + data = ir_extract_bits(gpio, ir->mask_keycode); + dprintk("irq gpio=0x%x code=%d | poll%s%s\n", + gpio, data, + (gpio & ir->mask_keydown) ? " down" : "", + (gpio & ir->mask_keyup) ? " up" : ""); + + /* Generate keyup/keydown events */ + if (ir->mask_keydown) { + /* bit set on keydown */ + if (gpio & ir->mask_keydown) + ir_input_keydown(ir->input, &ir->ir, data, data); + else + ir_input_nokey(ir->input, &ir->ir); + } else if (ir->mask_keyup) { + /* bit cleared on keydown */ + if (!(gpio & ir->mask_keyup)) + ir_input_keydown(ir->input, &ir->ir, data, data); + else + ir_input_nokey(ir->input, &ir->ir); + } else { + /* can't distinguish keydown/up :-/ */ + ir_input_keydown(ir->input, &ir->ir, data, data); + ir_input_nokey(ir->input, &ir->ir); + } +} + +static void ir_timer(unsigned long data) +{ + struct em28xx_IR *ir = (struct em28xx_IR *)data; + + schedule_work(&ir->work); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) +static void em28xx_ir_work(void *data) +#else +static void em28xx_ir_work(struct work_struct *work) +#endif +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) + struct em28xx_IR *ir = data; +#else + struct em28xx_IR *ir = container_of(work, struct em28xx_IR, work); +#endif + + em28xx_ir_handle_key(ir); + mod_timer(&ir->timer, jiffies + msecs_to_jiffies(ir->polling)); +} + +void em28xx_ir_start(struct em28xx_IR *ir) +{ + setup_timer(&ir->timer, ir_timer, (unsigned long)ir); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) + INIT_WORK(&ir->work, em28xx_ir_work, ir); +#else + INIT_WORK(&ir->work, em28xx_ir_work); +#endif + schedule_work(&ir->work); +} + +static void em28xx_ir_stop(struct em28xx_IR *ir) +{ + del_timer_sync(&ir->timer); + flush_scheduled_work(); +} + +int em28xx_ir_init(struct em28xx *dev) +{ + struct em28xx_IR *ir; + struct input_dev *input_dev; + IR_KEYTAB_TYPE *ir_codes = NULL; + int ir_type = IR_TYPE_OTHER; + int err = -ENOMEM; + + ir = kzalloc(sizeof(*ir), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!ir || !input_dev) + goto err_out_free; + + ir->input = input_dev; + + /* */ + ir->get_key = default_polling_getkey; + ir->polling = 50; /* ms */ + + /* detect & configure */ + switch (dev->model) { +#if 0 + /* dummy entry, just as a reference, while we don't add + other entries here + */ + case EM2820_BOARD_UNKNOWN: + ir_type = IR_TYPE_OTHER; + ir_codes = ir_codes_empty; + ir->mask_keycode = 0x007f0000; + break; + } +#endif + } + + if (NULL == ir_codes) { + err = -ENODEV; + goto err_out_free; + } + + /* Get the current key status, to avoid adding an + unexistent key code */ + ir->last_gpio = ir->get_key(ir); + + /* init input device */ + snprintf(ir->name, sizeof(ir->name), "em28xx IR (%s)", + dev->name); + + usb_make_path(dev->udev, ir->phys, sizeof(ir->phys)); + strlcat(ir->phys, "/input0", sizeof(ir->phys)); + + ir_input_init(input_dev, &ir->ir, ir_type, ir_codes); + input_dev->name = ir->name; + input_dev->phys = ir->phys; + input_dev->id.bustype = BUS_USB; + input_dev->id.version = 1; + input_dev->id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor); + input_dev->id.product = le16_to_cpu(dev->udev->descriptor.idProduct); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) + input_dev->dev.parent = &dev->udev->dev; +#else + input_dev->cdev.dev = &dev->udev->dev; +#endif + /* record handles to ourself */ + ir->dev = dev; + dev->ir = ir; + + em28xx_ir_start(ir); + + /* all done */ + err = input_register_device(ir->input); + if (err) + goto err_out_stop; + + return 0; + err_out_stop: + em28xx_ir_stop(ir); + dev->ir = NULL; + err_out_free: + input_free_device(input_dev); + kfree(ir); + return err; +} + +int em28xx_ir_fini(struct em28xx *dev) +{ + struct em28xx_IR *ir = dev->ir; + + /* skip detach on non attached boards */ + if (!ir) + return 0; + + em28xx_ir_stop(ir); + input_unregister_device(ir->input); + kfree(ir); + + /* done */ + dev->ir = NULL; + return 0; +} + +/********************************************************** + Handle Webcam snapshot button + **********************************************************/ + #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) static void em28xx_query_sbutton(void *data) #else @@ -232,9 +470,3 @@ void em28xx_deregister_snapshot_button(struct em28xx *dev) } return; } - -/* ---------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/linux/drivers/media/video/em28xx/em28xx-reg.h b/linux/drivers/media/video/em28xx/em28xx-reg.h index 50d1790d8..f67955a1b 100644 --- a/linux/drivers/media/video/em28xx/em28xx-reg.h +++ b/linux/drivers/media/video/em28xx/em28xx-reg.h @@ -71,6 +71,13 @@ #define EM28XX_R42_AC97ADDR 0x42 #define EM28XX_R43_AC97BUSY 0x43 +#define EM28XX_R45_IR 0x45 + /* 0x45 bit 7 - parity bit + bits 6-0 - count + 0x46 IR brand + 0x47 IR data + */ + /* em202 registers */ #define EM28XX_R02_MASTER_AC97 0x02 #define EM28XX_R10_LINE_IN_AC97 0x10 diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index 19684ad45..09886d567 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -1659,6 +1659,10 @@ static void em28xx_release_resources(struct em28xx *dev) list_del(&dev->devlist); if (dev->sbutton_input_dev) em28xx_deregister_snapshot_button(dev); + + if (dev->ir) + em28xx_ir_fini(dev); + if (dev->radio_dev) { if (-1 != dev->radio_dev->minor) video_unregister_device(dev->radio_dev); diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index aa1588c22..1bccb9ee4 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -390,6 +390,8 @@ struct em28xx { unsigned int has_snapshot_button:1; unsigned int valid:1; /* report for validated boards */ + struct em28xx_IR *ir; + /* Some older em28xx chips needs a waiting time after writing */ unsigned int wait_after_write; @@ -551,7 +553,6 @@ void em28xx_set_ir(struct em28xx *dev, struct IR_i2c *ir); int em28xx_tuner_callback(void *ptr, int component, int command, int arg); /* Provided by em28xx-input.c */ -/* TODO: Check if the standard get_key handlers on ir-common can be used */ int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw); int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw); int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, @@ -559,6 +560,9 @@ int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, void em28xx_register_snapshot_button(struct em28xx *dev); void em28xx_deregister_snapshot_button(struct em28xx *dev); +int em28xx_ir_init(struct em28xx *dev); +int em28xx_ir_fini(struct em28xx *dev); + /* printk macros */ #define em28xx_err(fmt, arg...) do {\ -- cgit v1.2.3 From 1664cd632369ed984a7ab5572fc0b84718f81205 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 11 Nov 2008 21:09:28 +0000 Subject: Fix section mismatch warning for dm1105 during make From: Igor M. Liplianin -- Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/dvb/dm1105/dm1105.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/dvb/dm1105/dm1105.c b/linux/drivers/media/dvb/dm1105/dm1105.c index f578cbdb6..100a7f661 100644 --- a/linux/drivers/media/dvb/dm1105/dm1105.c +++ b/linux/drivers/media/dvb/dm1105/dm1105.c @@ -375,7 +375,7 @@ static void dm1105dvb_dma_unmap(struct dm1105dvb *dm1105dvb) pci_free_consistent(dm1105dvb->pdev, 6*DM1105_DMA_BYTES, dm1105dvb->ts_buf, dm1105dvb->dma_addr); } -static void __devinit dm1105dvb_enable_irqs(struct dm1105dvb *dm1105dvb) +static void dm1105dvb_enable_irqs(struct dm1105dvb *dm1105dvb) { outb(INTMAK_ALLMASK, dm_io_mem(DM1105_INTMAK)); outb(1, dm_io_mem(DM1105_CR)); -- cgit v1.2.3 From 2a9e9cf9da405aca4f7303353c68de82e180fc2d Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 12 Nov 2008 15:46:43 +0100 Subject: uvcvideo: Commit streaming parameters when enabling the video stream. From: Laurent Pinchart Sonix-based cameras seem to require the host to commit video streaming parameters before selecting the alternate endpoint. While most applications issue a VIDIOC_S_FMT ioctl before starting streaming, this is not required by the V4L2 specification. The problem has been noticed on Ubuntu 8.10 with applications using libv4l. The library blocks VIDIOC_S_FMT calls when the requested format is identical to the currently selected format. As the driver commits video streaming parameters when initialising the device, only the first run of a libv4l-enabled application would succeed. For the sake of completeness, experiment showed that the camera keeps sending 12 bytes isochronous packets (header only, no data) without toggling the FID bit if video streaming parameters haven't been committed before selecting the alternate endpoint. Priority: normal Signed-off-by: Laurent Pinchart --- linux/drivers/media/video/uvc/uvc_v4l2.c | 7 ------- linux/drivers/media/video/uvc/uvc_video.c | 9 +++++---- 2 files changed, 5 insertions(+), 11 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/uvc/uvc_v4l2.c b/linux/drivers/media/video/uvc/uvc_v4l2.c index 5588698e2..b4191cb21 100644 --- a/linux/drivers/media/video/uvc/uvc_v4l2.c +++ b/linux/drivers/media/video/uvc/uvc_v4l2.c @@ -252,9 +252,6 @@ static int uvc_v4l2_set_format(struct uvc_video_device *video, if (ret < 0) return ret; - if ((ret = uvc_commit_video(video, &probe)) < 0) - return ret; - memcpy(&video->streaming->ctrl, &probe, sizeof probe); video->streaming->cur_format = format; video->streaming->cur_frame = frame; @@ -315,10 +312,6 @@ static int uvc_v4l2_set_streamparm(struct uvc_video_device *video, if ((ret = uvc_probe_video(video, &probe)) < 0) return ret; - /* Commit the new settings. */ - if ((ret = uvc_commit_video(video, &probe)) < 0) - return ret; - memcpy(&video->streaming->ctrl, &probe, sizeof probe); /* Return the actual frame period. */ diff --git a/linux/drivers/media/video/uvc/uvc_video.c b/linux/drivers/media/video/uvc/uvc_video.c index 54bc53bea..3be776a47 100644 --- a/linux/drivers/media/video/uvc/uvc_video.c +++ b/linux/drivers/media/video/uvc/uvc_video.c @@ -893,7 +893,7 @@ int uvc_video_resume(struct uvc_video_device *video) video->frozen = 0; - if ((ret = uvc_set_video_ctrl(video, &video->streaming->ctrl, 0)) < 0) { + if ((ret = uvc_commit_video(video, &video->streaming->ctrl)) < 0) { uvc_queue_enable(&video->queue, 0); return ret; } @@ -974,11 +974,8 @@ int uvc_video_init(struct uvc_video_device *video) break; } - /* Commit the default settings. */ probe->bFormatIndex = format->index; probe->bFrameIndex = frame->bFrameIndex; - if ((ret = uvc_set_video_ctrl(video, probe, 0)) < 0) - return ret; video->streaming->cur_format = format; video->streaming->cur_frame = frame; @@ -1018,6 +1015,10 @@ int uvc_video_enable(struct uvc_video_device *video, int enable) if ((ret = uvc_queue_enable(&video->queue, 1)) < 0) return ret; + /* Commit the streaming parameters. */ + if ((ret = uvc_commit_video(video, &video->streaming->ctrl)) < 0) + return ret; + return uvc_init_video(video, GFP_KERNEL); } -- cgit v1.2.3 From a96c9ec714a47d993bf53aaeb71a0de88f22277c Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 12 Nov 2008 15:05:46 -0200 Subject: em28xx: experimental support for HVR-950 IR From: Mauro Carvalho Chehab Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-input.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-input.c b/linux/drivers/media/video/em28xx/em28xx-input.c index c3c9cba30..e20e57185 100644 --- a/linux/drivers/media/video/em28xx/em28xx-input.c +++ b/linux/drivers/media/video/em28xx/em28xx-input.c @@ -288,28 +288,18 @@ int em28xx_ir_init(struct em28xx *dev) /* detect & configure */ switch (dev->model) { -#if 0 - /* dummy entry, just as a reference, while we don't add - other entries here - */ - case EM2820_BOARD_UNKNOWN: + case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950: ir_type = IR_TYPE_OTHER; - ir_codes = ir_codes_empty; + ir_codes = ir_codes_hauppauge_new; ir->mask_keycode = 0x007f0000; break; } -#endif - } if (NULL == ir_codes) { err = -ENODEV; goto err_out_free; } - /* Get the current key status, to avoid adding an - unexistent key code */ - ir->last_gpio = ir->get_key(ir); - /* init input device */ snprintf(ir->name, sizeof(ir->name), "em28xx IR (%s)", dev->name); @@ -334,6 +324,10 @@ int em28xx_ir_init(struct em28xx *dev) ir->dev = dev; dev->ir = ir; + /* Get the current key status, to avoid adding an + unexistent key code */ + ir->last_gpio = ir->get_key(ir); + em28xx_ir_start(ir); /* all done */ -- cgit v1.2.3 From 42290bd4d466901e792139032b1e0af60bd36cd7 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 12 Nov 2008 19:46:01 -0200 Subject: Fix key repetition with HVR-950 IR From: Mauro Carvalho Chehab Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-input.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-input.c b/linux/drivers/media/video/em28xx/em28xx-input.c index e20e57185..92114875f 100644 --- a/linux/drivers/media/video/em28xx/em28xx-input.c +++ b/linux/drivers/media/video/em28xx/em28xx-input.c @@ -68,6 +68,7 @@ struct em28xx_IR { u32 mask_keycode; u32 mask_keydown; u32 mask_keyup; + u32 mask_repeat; int (*get_key)(struct em28xx_IR *); }; @@ -171,15 +172,17 @@ static int default_polling_getkey(struct em28xx_IR *ir) { struct em28xx *dev = ir->dev; int rc; - u32 msg; + u8 msg[4] = { 0, 0, 0, 0 }; - /* Read key toggle, brand, and key code */ + /* Read key toggle, brand, and key code + on registers 0x45, 0x46 and 0x47 + */ rc = dev->em28xx_read_reg_req_len(dev, 0, EM28XX_R45_IR, - (u8 *)&msg, sizeof(msg)); + msg, sizeof(msg)); if (rc < 0) return rc; - return (int)(msg & 0x7fffffffl); + return (int)(le32_to_cpu(*(u32 *)msg)); } /********************************************************** @@ -198,6 +201,7 @@ static void em28xx_ir_handle_key(struct em28xx_IR *ir) if (gpio == ir->last_gpio) return; + ir->last_gpio = gpio; /* extract data */ @@ -220,6 +224,14 @@ static void em28xx_ir_handle_key(struct em28xx_IR *ir) ir_input_keydown(ir->input, &ir->ir, data, data); else ir_input_nokey(ir->input, &ir->ir); + } else if (ir->mask_repeat) { + int count = ir->mask_repeat & gpio; + + /* Avoid keyboard bouncing */ + if ((count == 1) || (count >= 5)) { + ir_input_keydown(ir->input, &ir->ir, data, data); + ir_input_nokey(ir->input, &ir->ir); + } } else { /* can't distinguish keydown/up :-/ */ ir_input_keydown(ir->input, &ir->ir, data, data); @@ -292,6 +304,7 @@ int em28xx_ir_init(struct em28xx *dev) ir_type = IR_TYPE_OTHER; ir_codes = ir_codes_hauppauge_new; ir->mask_keycode = 0x007f0000; + ir->mask_repeat = 0x0000007f; break; } -- cgit v1.2.3 From 24f47584d5c7fed9035c336c8201dff77a701227 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 16 Nov 2008 00:44:14 -0200 Subject: em28xx: Avoid i2c register error for boards without eeprom From: Mauro Carvalho Chehab Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-i2c.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-i2c.c b/linux/drivers/media/video/em28xx/em28xx-i2c.c index 51ecdcd34..ec3e3b157 100644 --- a/linux/drivers/media/video/em28xx/em28xx-i2c.c +++ b/linux/drivers/media/video/em28xx/em28xx-i2c.c @@ -348,9 +348,9 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char *eedata, int len) /* Check if board has eeprom */ err = i2c_master_recv(&dev->i2c_client, &buf, 0); if (err < 0) { - em28xx_errdev("%s: i2c_master_recv failed! err [%d]\n", - __func__, err); - return err; + em28xx_errdev("board has no eeprom\n"); + memset(eedata, 0, len); + return -ENODEV; } buf = 0; @@ -620,14 +620,16 @@ int em28xx_i2c_register(struct em28xx *dev) dev->i2c_client.adapter = &dev->i2c_adap; retval = em28xx_i2c_eeprom(dev, dev->eedata, sizeof(dev->eedata)); - if (retval < 0) { + if ((retval < 0) && (retval != -ENODEV)) { em28xx_errdev("%s: em28xx_i2_eeprom failed! retval [%d]\n", __func__, retval); + return retval; } if (i2c_scan) em28xx_do_i2c_scan(dev); + return 0; } -- cgit v1.2.3 From 5b255d384c4364c5d06495a91c15cd2f3f31e85c Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 13 Nov 2008 01:15:55 -0500 Subject: em28xx: refactor IR support From: Devin Heitmueller Refactor the em28xx IR support based on the em2860/em2880 and em2874 datasheets. Tested on the HVR-950 (em2883), Pinnacle 800e (em2883), Pinnacle 80e (em2874) using the remote controls that came with those products. Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-cards.c | 4 + linux/drivers/media/video/em28xx/em28xx-input.c | 202 +++++++++++++++--------- linux/drivers/media/video/em28xx/em28xx-reg.h | 8 + linux/drivers/media/video/em28xx/em28xx.h | 4 + 4 files changed, 147 insertions(+), 71 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 8d92cb05b..217d03c45 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -593,6 +593,7 @@ struct em28xx_board em28xx_boards[] = { .mts_firmware = 1, .has_12mhz_i2s = 1, .has_dvb = 1, + .ir_codes = ir_codes_hauppauge_new, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -616,6 +617,7 @@ struct em28xx_board em28xx_boards[] = { .mts_firmware = 1, .has_12mhz_i2s = 1, .has_dvb = 1, + .ir_codes = ir_codes_pinnacle_pctv_hd, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -1093,6 +1095,7 @@ struct em28xx_board em28xx_boards[] = { .vchannels = 0, .tuner_type = TUNER_ABSENT, .has_dvb = 1, + .ir_codes = ir_codes_pinnacle_pctv_hd, .decoder = EM28XX_NODECODER, #ifdef DJH_DEBUG .input = { { @@ -1334,6 +1337,7 @@ static void em28xx_set_model(struct em28xx *dev) dev->max_range_640_480 = em28xx_boards[dev->model].max_range_640_480; dev->has_dvb = em28xx_boards[dev->model].has_dvb; dev->has_snapshot_button = em28xx_boards[dev->model].has_snapshot_button; + dev->ir_codes = em28xx_boards[dev->model].ir_codes; dev->valid = em28xx_boards[dev->model].valid; } diff --git a/linux/drivers/media/video/em28xx/em28xx-input.c b/linux/drivers/media/video/em28xx/em28xx-input.c index 92114875f..29900920d 100644 --- a/linux/drivers/media/video/em28xx/em28xx-input.c +++ b/linux/drivers/media/video/em28xx/em28xx-input.c @@ -53,6 +53,13 @@ MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]"); Polling structure used by em28xx IR's **********************************************************/ +struct em28xx_ir_poll_result { + unsigned int toggle_bit:1; + unsigned int read_count:7; + u8 rc_address; + u8 rc_data[4]; /* 1 byte on em2860/2880, 4 on em2874 */ +}; + struct em28xx_IR { struct em28xx *dev; struct input_dev *input; @@ -64,13 +71,11 @@ struct em28xx_IR { int polling; struct work_struct work; struct timer_list timer; - u32 last_gpio; - u32 mask_keycode; - u32 mask_keydown; - u32 mask_keyup; - u32 mask_repeat; + unsigned int last_toggle:1; + unsigned int last_readcount; + unsigned int repeat_interval; - int (*get_key)(struct em28xx_IR *); + int (*get_key)(struct em28xx_IR *, struct em28xx_ir_poll_result *); }; /********************************************************** @@ -168,11 +173,13 @@ int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, Poll based get keycode functions **********************************************************/ -static int default_polling_getkey(struct em28xx_IR *ir) +/* This is for the em2860/em2880 */ +static int default_polling_getkey(struct em28xx_IR *ir, + struct em28xx_ir_poll_result *poll_result) { struct em28xx *dev = ir->dev; int rc; - u8 msg[4] = { 0, 0, 0, 0 }; + u8 msg[3] = { 0, 0, 0 }; /* Read key toggle, brand, and key code on registers 0x45, 0x46 and 0x47 @@ -182,7 +189,51 @@ static int default_polling_getkey(struct em28xx_IR *ir) if (rc < 0) return rc; - return (int)(le32_to_cpu(*(u32 *)msg)); + /* Infrared toggle (Reg 0x45[7]) */ + poll_result->toggle_bit = (msg[0] >> 7); + + /* Infrared read count (Reg 0x45[6:0] */ + poll_result->read_count = (msg[0] & 0x7f); + + /* Remote Control Address (Reg 0x46) */ + poll_result->rc_address = msg[1]; + + /* Remote Control Data (Reg 0x47) */ + poll_result->rc_data[0] = msg[2]; + + return 0; +} + +static int em2874_polling_getkey(struct em28xx_IR *ir, + struct em28xx_ir_poll_result *poll_result) +{ + struct em28xx *dev = ir->dev; + int rc; + u8 msg[5] = { 0, 0, 0, 0, 0 }; + + /* Read key toggle, brand, and key code + on registers 0x51-55 + */ + rc = dev->em28xx_read_reg_req_len(dev, 0, EM2874_R51_IR, + msg, sizeof(msg)); + if (rc < 0) + return rc; + + /* Infrared toggle (Reg 0x51[7]) */ + poll_result->toggle_bit = (msg[0] >> 7); + + /* Infrared read count (Reg 0x51[6:0] */ + poll_result->read_count = (msg[0] & 0x7f); + + /* Remote Control Address (Reg 0x52) */ + poll_result->rc_address = msg[1]; + + /* Remote Control Data (Reg 0x53-55) */ + poll_result->rc_data[0] = msg[2]; + poll_result->rc_data[1] = msg[3]; + poll_result->rc_data[2] = msg[4]; + + return 0; } /********************************************************** @@ -191,52 +242,60 @@ static int default_polling_getkey(struct em28xx_IR *ir) static void em28xx_ir_handle_key(struct em28xx_IR *ir) { - int gpio; - u32 data; - - /* read gpio value */ - gpio = ir->get_key(ir); - if (gpio < 0) + int result; + int do_sendkey = 0; + struct em28xx_ir_poll_result poll_result; + + /* read the registers containing the IR status */ + result = ir->get_key(ir, &poll_result); + if (result < 0) { + dprintk("ir->get_key() failed %d\n", result); return; + } - if (gpio == ir->last_gpio) - return; + dprintk("ir->get_key result tb=%02x rc=%02x lr=%02x data=%02x\n", + poll_result.toggle_bit, poll_result.read_count, + ir->last_readcount, poll_result.rc_data[0]); + + if (ir->dev->chip_id == CHIP_ID_EM2874) { + /* The em2874 clears the readcount field every time the + register is read. The em2860/2880 datasheet says that it + is supposed to clear the readcount, but it doesn't. So with + the em2874, we are looking for a non-zero read count as + opposed to a readcount that is incrementing */ + ir->last_readcount = 0; + } - ir->last_gpio = gpio; - - /* extract data */ - data = ir_extract_bits(gpio, ir->mask_keycode); - dprintk("irq gpio=0x%x code=%d | poll%s%s\n", - gpio, data, - (gpio & ir->mask_keydown) ? " down" : "", - (gpio & ir->mask_keyup) ? " up" : ""); - - /* Generate keyup/keydown events */ - if (ir->mask_keydown) { - /* bit set on keydown */ - if (gpio & ir->mask_keydown) - ir_input_keydown(ir->input, &ir->ir, data, data); - else - ir_input_nokey(ir->input, &ir->ir); - } else if (ir->mask_keyup) { - /* bit cleared on keydown */ - if (!(gpio & ir->mask_keyup)) - ir_input_keydown(ir->input, &ir->ir, data, data); - else - ir_input_nokey(ir->input, &ir->ir); - } else if (ir->mask_repeat) { - int count = ir->mask_repeat & gpio; - - /* Avoid keyboard bouncing */ - if ((count == 1) || (count >= 5)) { - ir_input_keydown(ir->input, &ir->ir, data, data); - ir_input_nokey(ir->input, &ir->ir); + if (poll_result.read_count == 0) { + /* The button has not been pressed since the last read */ + } else if (ir->last_toggle != poll_result.toggle_bit) { + /* A button has been pressed */ + dprintk("button has been pressed\n"); + ir->last_toggle = poll_result.toggle_bit; + ir->repeat_interval = 0; + do_sendkey = 1; + } else if (poll_result.toggle_bit == ir->last_toggle && + poll_result.read_count > 0 && + poll_result.read_count != ir->last_readcount) { + /* The button is still being held down */ + dprintk("button being held down\n"); + + /* Debouncer for first keypress */ + if (ir->repeat_interval++ > 9) { + /* Start repeating after 1 second */ + do_sendkey = 1; } - } else { - /* can't distinguish keydown/up :-/ */ - ir_input_keydown(ir->input, &ir->ir, data, data); + } + + if (do_sendkey) { + dprintk("sending keypress\n"); + ir_input_keydown(ir->input, &ir->ir, poll_result.rc_data[0], + poll_result.rc_data[0]); ir_input_nokey(ir->input, &ir->ir); } + + ir->last_readcount = poll_result.read_count; + return; } static void ir_timer(unsigned long data) @@ -283,10 +342,14 @@ int em28xx_ir_init(struct em28xx *dev) { struct em28xx_IR *ir; struct input_dev *input_dev; - IR_KEYTAB_TYPE *ir_codes = NULL; - int ir_type = IR_TYPE_OTHER; + u8 ir_config; int err = -ENOMEM; + if (dev->ir_codes == NULL) { + /* No remote control support */ + return 0; + } + ir = kzalloc(sizeof(*ir), GFP_KERNEL); input_dev = input_allocate_device(); if (!ir || !input_dev) @@ -294,25 +357,26 @@ int em28xx_ir_init(struct em28xx *dev) ir->input = input_dev; - /* */ - ir->get_key = default_polling_getkey; - ir->polling = 50; /* ms */ - - /* detect & configure */ - switch (dev->model) { - case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950: - ir_type = IR_TYPE_OTHER; - ir_codes = ir_codes_hauppauge_new; - ir->mask_keycode = 0x007f0000; - ir->mask_repeat = 0x0000007f; + /* Setup the proper handler based on the chip */ + switch (dev->chip_id) { + case CHIP_ID_EM2860: + case CHIP_ID_EM2883: + ir->get_key = default_polling_getkey; break; - } - - if (NULL == ir_codes) { - err = -ENODEV; + case CHIP_ID_EM2874: + ir->get_key = em2874_polling_getkey; + /* For now we only support RC5, so enable it */ + ir_config = EM2874_IR_RC5; + em28xx_write_regs(dev, EM2874_R50_IR_CONFIG, &ir_config, 1); + break; + default: + printk("Unrecognized em28xx chip id: IR not supported\n"); goto err_out_free; } + /* This is how often we ask the chip for IR information */ + ir->polling = 100; /* ms */ + /* init input device */ snprintf(ir->name, sizeof(ir->name), "em28xx IR (%s)", dev->name); @@ -320,7 +384,7 @@ int em28xx_ir_init(struct em28xx *dev) usb_make_path(dev->udev, ir->phys, sizeof(ir->phys)); strlcat(ir->phys, "/input0", sizeof(ir->phys)); - ir_input_init(input_dev, &ir->ir, ir_type, ir_codes); + ir_input_init(input_dev, &ir->ir, IR_TYPE_OTHER, dev->ir_codes); input_dev->name = ir->name; input_dev->phys = ir->phys; input_dev->id.bustype = BUS_USB; @@ -337,10 +401,6 @@ int em28xx_ir_init(struct em28xx *dev) ir->dev = dev; dev->ir = ir; - /* Get the current key status, to avoid adding an - unexistent key code */ - ir->last_gpio = ir->get_key(ir); - em28xx_ir_start(ir); /* all done */ diff --git a/linux/drivers/media/video/em28xx/em28xx-reg.h b/linux/drivers/media/video/em28xx/em28xx-reg.h index f67955a1b..b8e978363 100644 --- a/linux/drivers/media/video/em28xx/em28xx-reg.h +++ b/linux/drivers/media/video/em28xx/em28xx-reg.h @@ -84,9 +84,17 @@ #define EM28XX_R14_VIDEO_AC97 0x14 /* em2874 registers */ +#define EM2874_R50_IR_CONFIG 0x50 +#define EM2874_R51_IR 0x51 #define EM2874_R5F_TS_ENABLE 0x5f #define EM2874_R80_GPIO 0x80 +/* em2874 IR config register (0x50) */ +#define EM2874_IR_NEC 0x00 +#define EM2874_IR_RC5 0x04 +#define EM2874_IR_RC5_MODE_0 0x08 +#define EM2874_IR_RC5_MODE_6A 0x0b + /* em2874 Transport Stream Enable Register (0x5f) */ #define EM2874_TS1_CAPTURE_ENABLE (1 << 0) #define EM2874_TS1_FILTER_ENABLE (1 << 1) diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index 1bccb9ee4..b696dd429 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -303,6 +303,7 @@ struct em28xx_board { struct em28xx_input input[MAX_EM28XX_INPUT]; struct em28xx_input radio; + IR_KEYTAB_TYPE *ir_codes; }; struct em28xx_eeprom { @@ -484,6 +485,9 @@ struct em28xx { /* Caches GPO and GPIO registers */ unsigned char reg_gpo, reg_gpio; + /* Infrared remote control support */ + IR_KEYTAB_TYPE *ir_codes; + /* Snapshot button */ char snapshot_button_path[30]; /* path of the input dev */ struct input_dev *sbutton_input_dev; -- cgit v1.2.3 From 4de4f52a7dd66fb3db11f9647a3e0d87963605d8 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 16 Nov 2008 09:03:53 -0200 Subject: Add support for the ATI TV Wonder HD 600 USB Remote Control From: Devin Heitmueller Add support for the ATI TV Wonder HD 600 USB Remote Control (required a new keymap) Signed-off-by: Devin Heitmueller [mchehab@redhat.com: Fix CodingStyle] Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/common/ir-keymaps.c | 32 +++++++++++++++++++++++++ linux/drivers/media/video/em28xx/em28xx-cards.c | 1 + linux/include/media/ir-common.h | 1 + 3 files changed, 34 insertions(+) (limited to 'linux') diff --git a/linux/drivers/media/common/ir-keymaps.c b/linux/drivers/media/common/ir-keymaps.c index b58c8907f..6c0a80f45 100644 --- a/linux/drivers/media/common/ir-keymaps.c +++ b/linux/drivers/media/common/ir-keymaps.c @@ -2561,3 +2561,35 @@ IR_KEYTAB_TYPE ir_codes_real_audio_220_32_keys[IR_KEYTAB_SIZE] = { }; EXPORT_SYMBOL_GPL(ir_codes_real_audio_220_32_keys); + +/* ATI TV Wonder HD 600 USB + Devin Heitmueller + */ +IR_KEYTAB_TYPE ir_codes_ati_tv_wonder_hd_600[IR_KEYTAB_SIZE] = { + [0x00] = KEY_RECORD, /* Row 1 */ + [0x01] = KEY_PLAYPAUSE, + [0x02] = KEY_STOP, + [0x03] = KEY_POWER, + [0x04] = KEY_PREVIOUS, /* Row 2 */ + [0x05] = KEY_REWIND, + [0x06] = KEY_FORWARD, + [0x07] = KEY_NEXT, + [0x08] = KEY_EPG, /* Row 3 */ + [0x09] = KEY_HOME, + [0x0a] = KEY_MENU, + [0x0b] = KEY_CHANNELUP, + [0x0c] = KEY_BACK, /* Row 4 */ + [0x0d] = KEY_UP, + [0x0e] = KEY_INFO, + [0x0f] = KEY_CHANNELDOWN, + [0x10] = KEY_LEFT, /* Row 5 */ + [0x11] = KEY_SELECT, + [0x12] = KEY_RIGHT, + [0x13] = KEY_VOLUMEUP, + [0x14] = KEY_LAST, /* Row 6 */ + [0x15] = KEY_DOWN, + [0x16] = KEY_MUTE, + [0x17] = KEY_VOLUMEDOWN, +}; + +EXPORT_SYMBOL_GPL(ir_codes_ati_tv_wonder_hd_600); diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 217d03c45..5235410bd 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -641,6 +641,7 @@ struct em28xx_board em28xx_boards[] = { .mts_firmware = 1, .has_12mhz_i2s = 1, .has_dvb = 1, + .ir_codes = ir_codes_ati_tv_wonder_hd_600, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, diff --git a/linux/include/media/ir-common.h b/linux/include/media/ir-common.h index 38f2d93c3..3a88e13a2 100644 --- a/linux/include/media/ir-common.h +++ b/linux/include/media/ir-common.h @@ -157,6 +157,7 @@ extern IR_KEYTAB_TYPE ir_codes_avermedia_a16d[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_encore_enltv_fm53[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_real_audio_220_32_keys[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_msi_tvanywhere_plus[IR_KEYTAB_SIZE]; +extern IR_KEYTAB_TYPE ir_codes_ati_tv_wonder_hd_600[IR_KEYTAB_SIZE]; #endif /* -- cgit v1.2.3 From 7935f148ab5a647aa7a06b8cc0b4f509a124a961 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 16 Nov 2008 09:05:06 -0200 Subject: Some boards need to specify tuner address From: Mauro Carvalho Chehab Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-cards.c | 1 + linux/drivers/media/video/em28xx/em28xx.h | 1 + 2 files changed, 2 insertions(+) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 5235410bd..0d7862ec8 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -1770,6 +1770,7 @@ void em28xx_card_setup(struct em28xx *dev) em28xx_set_model(dev); dev->tuner_type = em28xx_boards[dev->model].tuner_type; + dev->tuner_addr = em28xx_boards[dev->model].tuner_addr; /* request some modules */ switch (dev->model) { diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index b696dd429..97afac4b6 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -286,6 +286,7 @@ struct em28xx_board { char *name; int vchannels; int tuner_type; + int tuner_addr; /* i2c flags */ unsigned int tda9887_conf; -- cgit v1.2.3 From d5c5bfeb8f2b68be7f6143e33d1f42395b1a9950 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 16 Nov 2008 03:33:32 +0000 Subject: Make s2api work for ATSC support From: Devin Heitmueller ATSC should be considered a legacy delivery system, or else fields such as p->u.vsb.modulation do not get populated (resulting in set_frontend failures) CC: Steven Toth Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/dvb/dvb-core/dvb_frontend.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c index 4d668e16d..3082ceb8b 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -1064,7 +1064,8 @@ void dtv_property_dump(struct dtv_property *tvp) int is_legacy_delivery_system(fe_delivery_system_t s) { if((s == SYS_UNDEFINED) || (s == SYS_DVBC_ANNEX_AC) || - (s == SYS_DVBC_ANNEX_B) || (s == SYS_DVBT) || (s == SYS_DVBS)) + (s == SYS_DVBC_ANNEX_B) || (s == SYS_DVBT) || (s == SYS_DVBS) || + (s == SYS_ATSC)) return 1; return 0; -- cgit v1.2.3 From 6a99ec7d1fca2090046e7671acb7df6b19707989 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 15 Nov 2008 20:16:11 +0000 Subject: make em28xx aux audio input work From: Devin Heitmueller The attached patch makes the em28xx auxillary audio input work. Tested with the HVR-950. em28xx: make auxillary audio input work The tuner audio input was working but the aux input wasn't. Tested with the HVR-950. Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-core.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-core.c b/linux/drivers/media/video/em28xx/em28xx-core.c index 48c2caabe..d3fc86b43 100644 --- a/linux/drivers/media/video/em28xx/em28xx-core.c +++ b/linux/drivers/media/video/em28xx/em28xx-core.c @@ -270,6 +270,8 @@ static int em28xx_set_audio_source(struct em28xx *dev) break; case EM28XX_AMUX_LINE_IN: input = EM28XX_AUDIO_SRC_LINE; + video = disable; + line = enable; break; case EM28XX_AMUX_AC97_VIDEO: input = EM28XX_AUDIO_SRC_LINE; -- cgit v1.2.3 From 3d34dfb02c8a0f924d78122ea83aa239a1063151 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 16 Nov 2008 06:39:50 +0000 Subject: Put s5h1411 into low power mode at end of attach() call From: Devin Heitmueller Place the s5h1411 into low power mode until first use (to handle the case where the user plugs in the device and then doesn't use it for a while). On the Pinnacle 801e, this brings the power usage from 126ma down to 82ma. Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/dvb/frontends/s5h1411.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'linux') diff --git a/linux/drivers/media/dvb/frontends/s5h1411.c b/linux/drivers/media/dvb/frontends/s5h1411.c index 40644aacf..66e2dd6d6 100644 --- a/linux/drivers/media/dvb/frontends/s5h1411.c +++ b/linux/drivers/media/dvb/frontends/s5h1411.c @@ -874,6 +874,9 @@ struct dvb_frontend *s5h1411_attach(const struct s5h1411_config *config, /* Note: Leaving the I2C gate open here. */ s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xf5, 1); + /* Put the device into low-power mode until first use */ + s5h1411_set_powerstate(&state->frontend, 1); + return &state->frontend; error: -- cgit v1.2.3 From 5f74e4c2e62639cfefc8edb708879cb187e6d8b5 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 16 Nov 2008 03:44:52 +0000 Subject: Make sure the i2c gate is open before powering down tuner From: Devin Heitmueller It is not safe to assume that the i2c gate will be open before issuing the command to power down the tuner. In fact, many demods only open the gate long enough to issue the tuning command. This fix allows power management to work properly for those tuners behind an i2c gate (in my case the problem was with the HVR-950Q) Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/dvb/dvb-core/dvb_frontend.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'linux') diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c index 3082ceb8b..e7adf9f47 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -656,6 +656,8 @@ restart: if (fe->ops.set_voltage) fe->ops.set_voltage(fe, SEC_VOLTAGE_OFF); if (fe->ops.tuner_ops.sleep) { + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); fe->ops.tuner_ops.sleep(fe); if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); -- cgit v1.2.3 From 08903c0e9f66d5b4c8339cec10cf6ddf0ec6bf8e Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 15 Nov 2008 04:10:14 +0000 Subject: v4l: s2255drv fix firmware test on big-endian From: Harvey Harrison Noticed by sparse: drivers/media/video/s2255drv.c:2531:6: warning: restricted __le32 degrades to integer Signed-off-by: Harvey Harrison Signed-off-by: Andrew Morton CC: Dean Anderson Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/s2255drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/video/s2255drv.c b/linux/drivers/media/video/s2255drv.c index 5a872787e..ab9814144 100644 --- a/linux/drivers/media/video/s2255drv.c +++ b/linux/drivers/media/video/s2255drv.c @@ -193,7 +193,7 @@ struct s2255_dmaqueue { #define S2255_FW_FAILED 3 #define S2255_FW_DISCONNECTING 4 -#define S2255_FW_MARKER 0x22552f2f +#define S2255_FW_MARKER cpu_to_le32(0x22552f2f) /* 2255 read states */ #define S2255_READ_IDLE 0 #define S2255_READ_FRAME 1 -- cgit v1.2.3 From 6c862246c352268e001c5dfb9f657f2b4832364e Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 15 Nov 2008 04:08:45 +0000 Subject: dvb: cinergyt2 annotate struct endianness, remove unused variable, add static From: Harvey Harrison All noticed by sparse: drivers/media/dvb/dvb-usb/cinergyT2-core.c:35:5: warning: symbol 'disable_remote' was not declared. Should it be static? drivers/media/dvb/dvb-usb/cinergyT2-core.c:48:23: warning: symbol 'cinergyt2_usb_device' was not declared. Should it be static? drivers/media/dvb/dvb-usb/cinergyT2-fe.c:160:15: warning: cast to restricted __le16 drivers/media/dvb/dvb-usb/cinergyT2-fe.c:190:9: warning: cast to restricted __le32 drivers/media/dvb/dvb-usb/cinergyT2-fe.c:208:9: warning: cast to restricted __le32 drivers/media/dvb/dvb-usb/cinergyT2-fe.c:227:24: warning: cast to restricted __le16 drivers/media/dvb/dvb-usb/cinergyT2-fe.c:275:12: warning: incorrect type in assignment (different base types) drivers/media/dvb/dvb-usb/cinergyT2-fe.c:275:12: expected unsigned short [unsigned] [assigned] [usertype] tps drivers/media/dvb/dvb-usb/cinergyT2-fe.c:275:12: got restricted __le16 [usertype] drivers/media/dvb/dvb-usb/cinergyT2-fe.c:276:13: warning: incorrect type in assignment (different base types) drivers/media/dvb/dvb-usb/cinergyT2-fe.c:276:13: expected unsigned int [unsigned] [assigned] [usertype] freq drivers/media/dvb/dvb-usb/cinergyT2-fe.c:276:13: got restricted __le32 [usertype] Signed-off-by: Harvey Harrison Signed-off-by: Andrew Morton CC: Thierry Merle Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/dvb/dvb-usb/cinergyT2-core.c | 3 +-- linux/drivers/media/dvb/dvb-usb/cinergyT2.h | 10 +++++----- 2 files changed, 6 insertions(+), 7 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/dvb/dvb-usb/cinergyT2-core.c b/linux/drivers/media/dvb/dvb-usb/cinergyT2-core.c index 3ac9f74e9..80e37a0d0 100644 --- a/linux/drivers/media/dvb/dvb-usb/cinergyT2-core.c +++ b/linux/drivers/media/dvb/dvb-usb/cinergyT2-core.c @@ -32,7 +32,6 @@ /* debug */ int dvb_usb_cinergyt2_debug; -int disable_remote; module_param_named(debug, dvb_usb_cinergyt2_debug, int, 0644); MODULE_PARM_DESC(debug, "set debugging level (1=info, xfer=2, rc=4 " @@ -45,7 +44,7 @@ struct cinergyt2_state { }; /* We are missing a release hook with usb_device data */ -struct dvb_usb_device *cinergyt2_usb_device; +static struct dvb_usb_device *cinergyt2_usb_device; static struct dvb_usb_device_properties cinergyt2_properties; diff --git a/linux/drivers/media/dvb/dvb-usb/cinergyT2.h b/linux/drivers/media/dvb/dvb-usb/cinergyT2.h index f008b41ad..4e3247d04 100644 --- a/linux/drivers/media/dvb/dvb-usb/cinergyT2.h +++ b/linux/drivers/media/dvb/dvb-usb/cinergyT2.h @@ -74,11 +74,11 @@ struct dvbt_get_status_msg { uint8_t bandwidth; uint16_t tps; uint8_t flags; - uint16_t gain; + __le16 gain; uint8_t snr; - uint32_t viterbi_error_rate; + __le32 viterbi_error_rate; uint32_t rs_error_rate; - uint32_t uncorrected_block_count; + __le32 uncorrected_block_count; uint8_t lock_bits; uint8_t prev_lock_bits; } __attribute__((packed)); @@ -86,9 +86,9 @@ struct dvbt_get_status_msg { struct dvbt_set_parameters_msg { uint8_t cmd; - uint32_t freq; + __le32 freq; uint8_t bandwidth; - uint16_t tps; + __le16 tps; uint8_t flags; } __attribute__((packed)); -- cgit v1.2.3 From 0dd5f609f5faa9eabdd86966e9bf7dd9d8d45b05 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 14 Nov 2008 21:19:56 +0000 Subject: usb vendor_ids/product_ids are __le16 From: Harvey Harrison Noticed by sparse: drivers/media/dvb/dvb-usb/af9015.c:756:25: warning: restricted __le16 degrades to integer drivers/media/dvb/dvb-usb/af9015.c:744:28: warning: restricted __le16 degrades to integer Signed-off-by: Harvey Harrison Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/dvb/dvb-usb/af9015.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/dvb/dvb-usb/af9015.c b/linux/drivers/media/dvb/dvb-usb/af9015.c index 6bc55614d..21e10fa23 100644 --- a/linux/drivers/media/dvb/dvb-usb/af9015.c +++ b/linux/drivers/media/dvb/dvb-usb/af9015.c @@ -742,7 +742,7 @@ static int af9015_read_config(struct usb_device *udev) } } else { switch (udev->descriptor.idVendor) { - case USB_VID_LEADTEK: + case cpu_to_le16(USB_VID_LEADTEK): af9015_properties[i].rc_key_map = af9015_rc_keys_leadtek; af9015_properties[i].rc_key_map_size = @@ -752,9 +752,9 @@ static int af9015_read_config(struct usb_device *udev) af9015_config.ir_table_size = ARRAY_SIZE(af9015_ir_table_leadtek); break; - case USB_VID_VISIONPLUS: + case cpu_to_le16(USB_VID_VISIONPLUS): if (udev->descriptor.idProduct == - USB_PID_AZUREWAVE_AD_TU700) { + cpu_to_le16(USB_PID_AZUREWAVE_AD_TU700)) { af9015_properties[i].rc_key_map = af9015_rc_keys_twinhan; af9015_properties[i].rc_key_map_size = @@ -765,7 +765,7 @@ static int af9015_read_config(struct usb_device *udev) ARRAY_SIZE(af9015_ir_table_twinhan); } break; - case USB_VID_KWORLD_2: + case cpu_to_le16(USB_VID_KWORLD_2): /* TODO: use correct rc keys */ af9015_properties[i].rc_key_map = af9015_rc_keys_twinhan; @@ -778,7 +778,7 @@ static int af9015_read_config(struct usb_device *udev) /* Check USB manufacturer and product strings and try to determine correct remote in case of chip vendor reference IDs are used. */ - case USB_VID_AFATECH: + case cpu_to_le16(USB_VID_AFATECH): memset(manufacturer, 0, sizeof(manufacturer)); usb_string(udev, udev->descriptor.iManufacturer, manufacturer, sizeof(manufacturer)); @@ -806,7 +806,7 @@ static int af9015_read_config(struct usb_device *udev) ARRAY_SIZE(af9015_ir_table_msi); } break; - case USB_VID_AVERMEDIA: + case cpu_to_le16(USB_VID_AVERMEDIA): af9015_properties[i].rc_key_map = af9015_rc_keys_avermedia; af9015_properties[i].rc_key_map_size = -- cgit v1.2.3 From f2dd6debc2a05925196c72e6341cbde336752caf Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 13 Nov 2008 02:18:21 +0000 Subject: drivers/media: use ARRAY_SIZE From: Julia Lawall ARRAY_SIZE is more concise to use when the size of an array is divided by the size of its type or the size of its first element. The semantic patch that makes this change is as follows: (http://www.emn.fr/x-info/coccinelle/) // @i@ @@ @depends on i using "paren.iso"@ type T; T[] E; @@ - (sizeof(E)/sizeof(E[...])) + ARRAY_SIZE(E) @depends on i using "paren.iso"@ type T; T[] E; @@ - (sizeof(E)/sizeof(T)) + ARRAY_SIZE(E) // Signed-off-by: Julia Lawall Signed-off-by: Andrew Morton Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/common/tuners/mxl5005s.c | 6 +++--- linux/drivers/media/video/gspca/gspca.c | 2 +- linux/drivers/media/video/gspca/mars.c | 2 +- linux/drivers/media/video/gspca/spca500.c | 4 ++-- linux/drivers/media/video/saa7134/saa7134-tvaudio.c | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/common/tuners/mxl5005s.c b/linux/drivers/media/common/tuners/mxl5005s.c index 3ed0d45cd..58418277a 100644 --- a/linux/drivers/media/common/tuners/mxl5005s.c +++ b/linux/drivers/media/common/tuners/mxl5005s.c @@ -3600,7 +3600,7 @@ static u16 MXL_GetInitRegister(struct dvb_frontend *fe, u8 *RegNum, 76, 77, 91, 134, 135, 137, 147, 156, 166, 167, 168, 25 }; - *count = sizeof(RegAddr) / sizeof(u8); + *count = ARRAY_SIZE(RegAddr); status += MXL_BlockInit(fe); @@ -3632,7 +3632,7 @@ static u16 MXL_GetCHRegister(struct dvb_frontend *fe, u8 *RegNum, u8 *RegVal, */ #endif - *count = sizeof(RegAddr) / sizeof(u8); + *count = ARRAY_SIZE(RegAddr); for (i = 0 ; i < *count; i++) { RegNum[i] = RegAddr[i]; @@ -3650,7 +3650,7 @@ static u16 MXL_GetCHRegister_ZeroIF(struct dvb_frontend *fe, u8 *RegNum, u8 RegAddr[] = {43, 136}; - *count = sizeof(RegAddr) / sizeof(u8); + *count = ARRAY_SIZE(RegAddr); for (i = 0; i < *count; i++) { RegNum[i] = RegAddr[i]; diff --git a/linux/drivers/media/video/gspca/gspca.c b/linux/drivers/media/video/gspca/gspca.c index 7a2006c3e..47e0179f7 100644 --- a/linux/drivers/media/video/gspca/gspca.c +++ b/linux/drivers/media/video/gspca/gspca.c @@ -747,7 +747,7 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, if (fmtdesc->index == index) break; /* new format */ index++; - if (index >= sizeof fmt_tb / sizeof fmt_tb[0]) + if (index >= ARRAY_SIZE(fmt_tb)) return -EINVAL; } } diff --git a/linux/drivers/media/video/gspca/mars.c b/linux/drivers/media/video/gspca/mars.c index 277ca34a8..492cdd3b5 100644 --- a/linux/drivers/media/video/gspca/mars.c +++ b/linux/drivers/media/video/gspca/mars.c @@ -123,7 +123,7 @@ static int sd_config(struct gspca_dev *gspca_dev, cam = &gspca_dev->cam; cam->epaddr = 0x01; cam->cam_mode = vga_mode; - cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; + cam->nmodes = ARRAY_SIZE(vga_mode); sd->qindex = 1; /* set the quantization table */ return 0; } diff --git a/linux/drivers/media/video/gspca/spca500.c b/linux/drivers/media/video/gspca/spca500.c index a3f616709..2e1c5e50e 100644 --- a/linux/drivers/media/video/gspca/spca500.c +++ b/linux/drivers/media/video/gspca/spca500.c @@ -650,10 +650,10 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->subtype = id->driver_info; if (sd->subtype != LogitechClickSmart310) { cam->cam_mode = vga_mode; - cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; + cam->nmodes = ARRAY_SIZE(vga_mode); } else { cam->cam_mode = sif_mode; - cam->nmodes = sizeof sif_mode / sizeof sif_mode[0]; + cam->nmodes = ARRAY_SIZE(sif_mode); } sd->qindex = 5; sd->brightness = BRIGHTNESS_DEF; diff --git a/linux/drivers/media/video/saa7134/saa7134-tvaudio.c b/linux/drivers/media/video/saa7134/saa7134-tvaudio.c index f7a3f1d0a..477d9e091 100644 --- a/linux/drivers/media/video/saa7134/saa7134-tvaudio.c +++ b/linux/drivers/media/video/saa7134/saa7134-tvaudio.c @@ -161,7 +161,7 @@ static struct saa7134_tvaudio tvaudio[] = { .mode = TVAUDIO_FM_MONO, } }; -#define TVAUDIO (sizeof(tvaudio)/sizeof(struct saa7134_tvaudio)) +#define TVAUDIO ARRAY_SIZE(tvaudio) /* ------------------------------------------------------------------ */ -- cgit v1.2.3 From ea9b10fde47b18f2fdf02022ac1d8243946a7397 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 13 Nov 2008 14:06:33 -0200 Subject: tvaudio: fix a memory leak From: Mauro Carvalho Chehab Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/tvaudio.c | 1 + 1 file changed, 1 insertion(+) (limited to 'linux') diff --git a/linux/drivers/media/video/tvaudio.c b/linux/drivers/media/video/tvaudio.c index 7e670f2bc..b8978efa4 100644 --- a/linux/drivers/media/video/tvaudio.c +++ b/linux/drivers/media/video/tvaudio.c @@ -1489,6 +1489,7 @@ static int chip_probe(struct i2c_client *client, const struct i2c_device_id *id) } if (desc->name == NULL) { v4l_dbg(1, debug, client, "no matching chip description found\n"); + kfree(chip); return -EIO; } v4l_info(client, "%s found @ 0x%x (%s)\n", desc->name, client->addr<<1, client->adapter->name); -- cgit v1.2.3 From ea41704cd7a8a8c5e40946ddc0f0f26981668177 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 13 Nov 2008 14:07:30 -0200 Subject: tvaudio: Make sure that we don't overflow name buffer From: Mauro Carvalho Chehab Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/tvaudio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/video/tvaudio.c b/linux/drivers/media/video/tvaudio.c index b8978efa4..a751cd40a 100644 --- a/linux/drivers/media/video/tvaudio.c +++ b/linux/drivers/media/video/tvaudio.c @@ -1502,7 +1502,7 @@ static int chip_probe(struct i2c_client *client, const struct i2c_device_id *id) /* fill required data structures */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) - strcpy(client->name, desc->name); + strlcpy(client->name, desc->name, I2C_NAME_SIZE); #else if (!id) strlcpy(client->name, desc->name, I2C_NAME_SIZE); -- cgit v1.2.3 From 9eac1f5563b1b7b5e1541397f92f81df36872554 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 13 Nov 2008 14:10:11 -0200 Subject: tvaudio: instead of using a magic number, use ARRAY_SIZE From: Mauro Carvalho Chehab Also, the default standard is the first one. So, fix the comment at the array. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/tvaudio.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/tvaudio.c b/linux/drivers/media/video/tvaudio.c index a751cd40a..329f92e71 100644 --- a/linux/drivers/media/video/tvaudio.c +++ b/linux/drivers/media/video/tvaudio.c @@ -780,7 +780,7 @@ static struct tda9874a_MODES { char *name; audiocmd cmd; } tda9874a_modelist[9] = { - { "A2, B/G", + { "A2, B/G", /* default */ { 9, { TDA9874A_C1FRA, 0x72,0x95,0x55, 0x77,0xA0,0x00, 0x00,0x00 }} }, { "A2, M (Korea)", { 9, { TDA9874A_C1FRA, 0x5D,0xC0,0x00, 0x62,0x6A,0xAA, 0x20,0x22 }} }, @@ -794,7 +794,7 @@ static struct tda9874a_MODES { { 9, { TDA9874A_C1FRA, 0x7D,0x00,0x00, 0x88,0x8A,0xAA, 0x08,0x33 }} }, { "NICAM, B/G", { 9, { TDA9874A_C1FRA, 0x72,0x95,0x55, 0x79,0xEA,0xAA, 0x08,0x33 }} }, - { "NICAM, D/K", /* default */ + { "NICAM, D/K", { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x79,0xEA,0xAA, 0x08,0x33 }} }, { "NICAM, L", { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x79,0xEA,0xAA, 0x09,0x33 }} } @@ -989,7 +989,7 @@ static int tda9874a_initialize(struct CHIPSTATE *chip) { if (tda9874a_SIF > 2) tda9874a_SIF = 1; - if (tda9874a_STD > 8) + if (tda9874a_STD >= ARRAY_SIZE(tda9874a_modelist)) tda9874a_STD = 0; if(tda9874a_AMSEL > 1) tda9874a_AMSEL = 0; -- cgit v1.2.3 From 89582c93e215270df62a6ef38208514fdfe0d85b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 13 Nov 2008 14:14:15 -0200 Subject: tvaudio: cleanup - group all callbacks together From: Mauro Carvalho Chehab Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/tvaudio.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/tvaudio.c b/linux/drivers/media/video/tvaudio.c index 329f92e71..90e262a98 100644 --- a/linux/drivers/media/video/tvaudio.c +++ b/linux/drivers/media/video/tvaudio.c @@ -1268,6 +1268,7 @@ static struct CHIPDESC chiplist[] = { .addr_hi = I2C_ADDR_TDA9840 >> 1, .registers = 5, + /* callbacks */ .checkit = tda9840_checkit, .getmode = tda9840_getmode, .setmode = tda9840_setmode, @@ -1278,13 +1279,14 @@ static struct CHIPDESC chiplist[] = { }, { .name = "tda9873h", - .checkit = tda9873_checkit, .insmodopt = &tda9873, .addr_lo = I2C_ADDR_TDA985x_L >> 1, .addr_hi = I2C_ADDR_TDA985x_H >> 1, .registers = 3, .flags = CHIP_HAS_INPUTSEL, + /* callbacks */ + .checkit = tda9873_checkit, .getmode = tda9873_getmode, .setmode = tda9873_setmode, .checkmode = generic_checkmode, @@ -1298,12 +1300,13 @@ static struct CHIPDESC chiplist[] = { }, { .name = "tda9874h/a", - .checkit = tda9874a_checkit, - .initialize = tda9874a_initialize, .insmodopt = &tda9874a, .addr_lo = I2C_ADDR_TDA9874 >> 1, .addr_hi = I2C_ADDR_TDA9874 >> 1, + /* callbacks */ + .initialize = tda9874a_initialize, + .checkit = tda9874a_checkit, .getmode = tda9874a_getmode, .setmode = tda9874a_setmode, .checkmode = generic_checkmode, @@ -1332,10 +1335,11 @@ static struct CHIPDESC chiplist[] = { .rightreg = TDA9855_VR, .bassreg = TDA9855_BA, .treblereg = TDA9855_TR, + + /* callbacks */ .volfunc = tda9855_volume, .bassfunc = tda9855_bass, .treblefunc = tda9855_treble, - .getmode = tda985x_getmode, .setmode = tda985x_setmode, @@ -1356,6 +1360,8 @@ static struct CHIPDESC chiplist[] = { .rightreg = TEA6300_VL, .bassreg = TEA6300_BA, .treblereg = TEA6300_TR, + + /* callbacks */ .volfunc = tea6300_shift10, .bassfunc = tea6300_shift12, .treblefunc = tea6300_shift12, @@ -1366,7 +1372,6 @@ static struct CHIPDESC chiplist[] = { }, { .name = "tea6320", - .initialize = tea6320_initialize, .insmodopt = &tea6320, .addr_lo = I2C_ADDR_TEA6300 >> 1, .addr_hi = I2C_ADDR_TEA6300 >> 1, @@ -1377,6 +1382,9 @@ static struct CHIPDESC chiplist[] = { .rightreg = TEA6320_V, .bassreg = TEA6320_BA, .treblereg = TEA6320_TR, + + /* callbacks */ + .initialize = tea6320_initialize, .volfunc = tea6320_volume, .bassfunc = tea6320_shift11, .treblefunc = tea6320_shift11, @@ -1409,16 +1417,18 @@ static struct CHIPDESC chiplist[] = { .rightreg = TDA8425_VR, .bassreg = TDA8425_BA, .treblereg = TDA8425_TR, + + /* callbacks */ + .initialize = tda8425_initialize, .volfunc = tda8425_shift10, .bassfunc = tda8425_shift12, .treblefunc = tda8425_shift12, + .setmode = tda8425_setmode, .inputreg = TDA8425_S1, .inputmap = { TDA8425_S1_CH1, TDA8425_S1_CH1, TDA8425_S1_CH1 }, .inputmute = TDA8425_S1_OFF, - .setmode = tda8425_setmode, - .initialize = tda8425_initialize, }, { .name = "pic16c54 (PV951)", @@ -1443,6 +1453,7 @@ static struct CHIPDESC chiplist[] = { .addr_hi = I2C_ADDR_TDA9840 >> 1, .registers = 2, + /* callbacks */ .getmode = ta8874z_getmode, .setmode = ta8874z_setmode, .checkmode = generic_checkmode, -- cgit v1.2.3 From c3168d5d415d0d378a76edcc3cf329b3f49b9f0c Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 13 Nov 2008 14:55:39 -0200 Subject: tvtime: avoid OOPS at generic_checkmode() From: Mauro Carvalho Chehab generic_checkmode() were called, via a callback, for some tvaudio chips. There's just one callback code used on all those boards. So, it makes no sense on keeping this as a callback. Since there were some OOPS reported on tvaudio on kerneloops.org, this patch removes this callback, adding the code at the only place were it is called: inside chip_tread. A flag were added to indicate the need for a kernel thread to set stereo mode on cards that needs it. Priority: high Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/tvaudio.c | 71 ++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 36 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/tvaudio.c b/linux/drivers/media/video/tvaudio.c index 90e262a98..7a7559138 100644 --- a/linux/drivers/media/video/tvaudio.c +++ b/linux/drivers/media/video/tvaudio.c @@ -61,7 +61,6 @@ typedef int (*checkit)(struct CHIPSTATE*); typedef int (*initialize)(struct CHIPSTATE*); typedef int (*getmode)(struct CHIPSTATE*); typedef void (*setmode)(struct CHIPSTATE*, int mode); -typedef void (*checkmode)(struct CHIPSTATE*); /* i2c command */ typedef struct AUDIOCMD { @@ -82,6 +81,7 @@ struct CHIPDESC { #define CHIP_HAS_VOLUME 1 #define CHIP_HAS_BASSTREBLE 2 #define CHIP_HAS_INPUTSEL 4 +#define CHIP_NEED_CHECKMODE 8 /* various i2c command sequences */ audiocmd init; @@ -99,9 +99,6 @@ struct CHIPDESC { getmode getmode; setmode setmode; - /* check / autoswitch audio after channel switches */ - checkmode checkmode; - /* input switch register + values for v4l inputs */ int inputreg; int inputmap[4]; @@ -267,6 +264,7 @@ static int chip_thread(void *data) { struct CHIPSTATE *chip = data; struct CHIPDESC *desc = chiplist + chip->type; + int mode; v4l_dbg(1, debug, chip->c, "%s: thread started\n", chip->c->name); set_freezable(); @@ -285,7 +283,26 @@ static int chip_thread(void *data) continue; /* have a look what's going on */ - desc->checkmode(chip); + mode = desc->getmode(chip); + if (mode == chip->prevmode) + continue; + + /* chip detected a new audio mode - set it */ + v4l_dbg(1, debug, chip->c, "%s: thread checkmode\n", + chip->c->name); + + chip->prevmode = mode; + + if (mode & V4L2_TUNER_MODE_STEREO) + desc->setmode(chip, V4L2_TUNER_MODE_STEREO); + if (mode & V4L2_TUNER_MODE_LANG1_LANG2) + desc->setmode(chip, V4L2_TUNER_MODE_STEREO); + else if (mode & V4L2_TUNER_MODE_LANG1) + desc->setmode(chip, V4L2_TUNER_MODE_LANG1); + else if (mode & V4L2_TUNER_MODE_LANG2) + desc->setmode(chip, V4L2_TUNER_MODE_LANG2); + else + desc->setmode(chip, V4L2_TUNER_MODE_MONO); /* schedule next check */ mod_timer(&chip->wt, jiffies+msecs_to_jiffies(2000)); @@ -295,29 +312,6 @@ static int chip_thread(void *data) return 0; } -static void generic_checkmode(struct CHIPSTATE *chip) -{ - struct CHIPDESC *desc = chiplist + chip->type; - int mode = desc->getmode(chip); - - if (mode == chip->prevmode) - return; - - v4l_dbg(1, debug, chip->c, "%s: thread checkmode\n", chip->c->name); - chip->prevmode = mode; - - if (mode & V4L2_TUNER_MODE_STEREO) - desc->setmode(chip,V4L2_TUNER_MODE_STEREO); - if (mode & V4L2_TUNER_MODE_LANG1_LANG2) - desc->setmode(chip,V4L2_TUNER_MODE_STEREO); - else if (mode & V4L2_TUNER_MODE_LANG1) - desc->setmode(chip,V4L2_TUNER_MODE_LANG1); - else if (mode & V4L2_TUNER_MODE_LANG2) - desc->setmode(chip,V4L2_TUNER_MODE_LANG2); - else - desc->setmode(chip,V4L2_TUNER_MODE_MONO); -} - /* ---------------------------------------------------------------------- */ /* audio chip descriptions - defines+functions for tda9840 */ @@ -1267,12 +1261,12 @@ static struct CHIPDESC chiplist[] = { .addr_lo = I2C_ADDR_TDA9840 >> 1, .addr_hi = I2C_ADDR_TDA9840 >> 1, .registers = 5, + .flags = CHIP_NEED_CHECKMODE, /* callbacks */ .checkit = tda9840_checkit, .getmode = tda9840_getmode, .setmode = tda9840_setmode, - .checkmode = generic_checkmode, .init = { 2, { TDA9840_TEST, TDA9840_TEST_INT1SN /* ,TDA9840_SW, TDA9840_MONO */} } @@ -1283,13 +1277,12 @@ static struct CHIPDESC chiplist[] = { .addr_lo = I2C_ADDR_TDA985x_L >> 1, .addr_hi = I2C_ADDR_TDA985x_H >> 1, .registers = 3, - .flags = CHIP_HAS_INPUTSEL, + .flags = CHIP_HAS_INPUTSEL | CHIP_NEED_CHECKMODE, /* callbacks */ .checkit = tda9873_checkit, .getmode = tda9873_getmode, .setmode = tda9873_setmode, - .checkmode = generic_checkmode, .init = { 4, { TDA9873_SW, 0xa4, 0x06, 0x03 } }, .inputreg = TDA9873_SW, @@ -1303,13 +1296,13 @@ static struct CHIPDESC chiplist[] = { .insmodopt = &tda9874a, .addr_lo = I2C_ADDR_TDA9874 >> 1, .addr_hi = I2C_ADDR_TDA9874 >> 1, + .flags = CHIP_NEED_CHECKMODE, /* callbacks */ .initialize = tda9874a_initialize, .checkit = tda9874a_checkit, .getmode = tda9874a_getmode, .setmode = tda9874a_setmode, - .checkmode = generic_checkmode, }, { .name = "tda9850", @@ -1452,11 +1445,11 @@ static struct CHIPDESC chiplist[] = { .addr_lo = I2C_ADDR_TDA9840 >> 1, .addr_hi = I2C_ADDR_TDA9840 >> 1, .registers = 2, + .flags = CHIP_NEED_CHECKMODE, /* callbacks */ .getmode = ta8874z_getmode, .setmode = ta8874z_setmode, - .checkmode = generic_checkmode, .init = {2, { TA8874Z_MONO_SET, TA8874Z_SEPARATION_DEFAULT}}, }, @@ -1543,7 +1536,7 @@ static int chip_probe(struct i2c_client *client, const struct i2c_device_id *id) } chip->thread = NULL; - if (desc->checkmode) { + if (desc->flags & CHIP_NEED_CHECKMODE) { /* start async thread */ init_timer(&chip->wt); chip->wt.function = chip_thread_wake; @@ -1816,12 +1809,18 @@ static int chip_command(struct i2c_client *client, break; case VIDIOC_S_FREQUENCY: chip->mode = 0; /* automatic */ - if (desc->checkmode && desc->setmode) { + + /* For chips that provide getmode, setmode and checkmode, + a kthread is created to automatically to set the audio + standard. In this case, start with MONO and wait 2 seconds + for the decoding to stablize. Then, run kthread to change + to stereo, if carrier detected. + */ + if (chip->thread) { desc->setmode(chip,V4L2_TUNER_MODE_MONO); if (chip->prevmode != V4L2_TUNER_MODE_MONO) chip->prevmode = -1; /* reset previous mode */ mod_timer(&chip->wt, jiffies+msecs_to_jiffies(2000)); - /* the thread will call checkmode() later */ } break; -- cgit v1.2.3 From 585e646822e5e6b349e06073319b26be6a29f81f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 13 Nov 2008 15:01:15 -0200 Subject: tvaudio: add additional logic to avoid OOPS From: Mauro Carvalho Chehab This patch checks for volume, bass, treble, set mode and get mode callbacks before actually enabling the code that would use them. Instead of aborting the driver for load, this patch will allow it to load with a reduced number of functionatities. This prevents OOPS if some board entry is missing a needed callback. Priority: high Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/tvaudio.c | 45 ++++++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 8 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/tvaudio.c b/linux/drivers/media/video/tvaudio.c index 7a7559138..b448856ab 100644 --- a/linux/drivers/media/video/tvaudio.c +++ b/linux/drivers/media/video/tvaudio.c @@ -1523,20 +1523,49 @@ static int chip_probe(struct i2c_client *client, const struct i2c_device_id *id) chip_cmd(chip,"init",&desc->init); if (desc->flags & CHIP_HAS_VOLUME) { - chip->left = desc->leftinit ? desc->leftinit : 65535; - chip->right = desc->rightinit ? desc->rightinit : 65535; - chip_write(chip,desc->leftreg,desc->volfunc(chip->left)); - chip_write(chip,desc->rightreg,desc->volfunc(chip->right)); + if (!desc->volfunc) { + /* This shouldn't be happen. Warn user, but keep working + without volume controls + */ + v4l_info(chip->c, "volume callback undefined!\n"); + desc->flags &= ~CHIP_HAS_VOLUME; + } else { + chip->left = desc->leftinit ? desc->leftinit : 65535; + chip->right = desc->rightinit ? desc->rightinit : 65535; + chip_write(chip, desc->leftreg, + desc->volfunc(chip->left)); + chip_write(chip, desc->rightreg, + desc->volfunc(chip->right)); + } } if (desc->flags & CHIP_HAS_BASSTREBLE) { - chip->treble = desc->trebleinit ? desc->trebleinit : 32768; - chip->bass = desc->bassinit ? desc->bassinit : 32768; - chip_write(chip,desc->bassreg,desc->bassfunc(chip->bass)); - chip_write(chip,desc->treblereg,desc->treblefunc(chip->treble)); + if (!desc->bassfunc || !desc->treblefunc) { + /* This shouldn't be happen. Warn user, but keep working + without bass/treble controls + */ + v4l_info(chip->c, "bass/treble callbacks undefined!\n"); + desc->flags &= ~CHIP_HAS_BASSTREBLE; + } else { + chip->treble = desc->trebleinit ? + desc->trebleinit : 32768; + chip->bass = desc->bassinit ? + desc->bassinit : 32768; + chip_write(chip, desc->bassreg, + desc->bassfunc(chip->bass)); + chip_write(chip, desc->treblereg, + desc->treblefunc(chip->treble)); + } } chip->thread = NULL; if (desc->flags & CHIP_NEED_CHECKMODE) { + if (!desc->getmode || !desc->setmode) { + /* This shouldn't be happen. Warn user, but keep working + without kthread + */ + v4l_info(chip->c, "set/get mode callbacks undefined!\n"); + return 0; + } /* start async thread */ init_timer(&chip->wt); chip->wt.function = chip_thread_wake; -- cgit v1.2.3 From b137313916dc95b292637c22d1ad517add1b5a9b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 13 Nov 2008 15:07:54 -0200 Subject: tvaudio: update initial comments From: Mauro Carvalho Chehab A driver used on several bttv boards since 2000 is not experimental anymore ;) Remove it from the comments. While there, update copyrights addind a quick note about the "recent" updates since 2005. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/tvaudio.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/video/tvaudio.c b/linux/drivers/media/video/tvaudio.c index b448856ab..9fc2dfe75 100644 --- a/linux/drivers/media/video/tvaudio.c +++ b/linux/drivers/media/video/tvaudio.c @@ -1,5 +1,5 @@ /* - * experimental driver for simple i2c audio chips. + * Driver for simple i2c audio chips. * * Copyright (c) 2000 Gerd Knorr * based on code by: @@ -7,6 +7,10 @@ * Steve VanDeBogart (vandebo@uclink.berkeley.edu) * Greg Alexander (galexand@acm.org) * + * Copyright(c) 2005-2008 Mauro Carvalho Chehab + * - Some cleanups, code fixes, etc + * - Convert it to V4L2 API + * * This code is placed under the terms of the GNU General Public License * * OPTIONS: -- cgit v1.2.3 From 63fddcde9b4824f1bd5d261dd0f91171d560a98a Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Thu, 13 Nov 2008 19:14:18 +0200 Subject: af9015: don't reconnect device in USB-bus From: Jose Alberto Reguero Don't reconnect device in the USB-bus. Reconnect command was not executed every time by device firmware and that causes harm. Reconnection is not needed so remove it. Priority: high Signed-off-by: Jose Alberto Reguero Signed-off-by: Antti Palosaari --- linux/drivers/media/dvb/dvb-usb/af9015.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/dvb/dvb-usb/af9015.c b/linux/drivers/media/dvb/dvb-usb/af9015.c index 6bc55614d..c52ebce9f 100644 --- a/linux/drivers/media/dvb/dvb-usb/af9015.c +++ b/linux/drivers/media/dvb/dvb-usb/af9015.c @@ -681,12 +681,6 @@ static int af9015_download_firmware(struct usb_device *udev, goto error; } - /* firmware is running, reconnect device in the usb bus */ - req.cmd = RECONNECT_USB; - ret = af9015_rw_udev(udev, &req); - if (ret) - err("reconnect failed: %d", ret); - error: return ret; } @@ -1219,6 +1213,7 @@ static struct dvb_usb_device_properties af9015_properties[] = { .usb_ctrl = DEVICE_SPECIFIC, .download_firmware = af9015_download_firmware, .firmware = "dvb-usb-af9015.fw", + .no_reconnect = 1, .size_of_priv = sizeof(struct af9015_state), \ @@ -1317,6 +1312,7 @@ static struct dvb_usb_device_properties af9015_properties[] = { .usb_ctrl = DEVICE_SPECIFIC, .download_firmware = af9015_download_firmware, .firmware = "dvb-usb-af9015.fw", + .no_reconnect = 1, .size_of_priv = sizeof(struct af9015_state), \ -- cgit v1.2.3 From a08c59762c341f4247d52bedd399077fcaac38d2 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 13 Nov 2008 17:22:53 -0200 Subject: tvaudio: use a direct reference for chip description From: Mauro Carvalho Chehab Instead of storing the pointer for the proper entry at chip description table, the driver were storing an indirect reference, by using an index. Better to reference directly the data. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/tvaudio.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/tvaudio.c b/linux/drivers/media/video/tvaudio.c index 9fc2dfe75..1575e6f9d 100644 --- a/linux/drivers/media/video/tvaudio.c +++ b/linux/drivers/media/video/tvaudio.c @@ -115,8 +115,9 @@ static struct CHIPDESC chiplist[]; struct CHIPSTATE { struct i2c_client *c; - /* index into CHIPDESC array */ - int type; + /* chip-specific description - should point to + an entry at CHIPDESC table */ + struct CHIPDESC *desc; /* shadow register set */ audiocmd shadow; @@ -267,7 +268,7 @@ static void chip_thread_wake(unsigned long data) static int chip_thread(void *data) { struct CHIPSTATE *chip = data; - struct CHIPDESC *desc = chiplist + chip->type; + struct CHIPDESC *desc = chip->desc; int mode; v4l_dbg(1, debug, chip->c, "%s: thread started\n", chip->c->name); @@ -1095,7 +1096,7 @@ static int tda8425_shift12(int val) { return (val >> 12) | 0xf0; } static int tda8425_initialize(struct CHIPSTATE *chip) { - struct CHIPDESC *desc = chiplist + chip->type; + struct CHIPDESC *desc = chip->desc; int inputmap[4] = { /* tuner */ TDA8425_S1_CH2, /* radio */ TDA8425_S1_CH1, /* extern */ TDA8425_S1_CH1, /* intern */ TDA8425_S1_OFF}; @@ -1515,7 +1516,7 @@ static int chip_probe(struct i2c_client *client, const struct i2c_device_id *id) if (!id) strlcpy(client->name, desc->name, I2C_NAME_SIZE); #endif - chip->type = desc-chiplist; + chip->desc = desc; chip->shadow.count = desc->registers+1; chip->prevmode = -1; chip->audmode = V4L2_TUNER_MODE_LANG1; @@ -1602,7 +1603,7 @@ static int chip_remove(struct i2c_client *client) static int tvaudio_get_ctrl(struct CHIPSTATE *chip, struct v4l2_control *ctrl) { - struct CHIPDESC *desc = chiplist + chip->type; + struct CHIPDESC *desc = chip->desc; switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: @@ -1642,7 +1643,7 @@ static int tvaudio_get_ctrl(struct CHIPSTATE *chip, static int tvaudio_set_ctrl(struct CHIPSTATE *chip, struct v4l2_control *ctrl) { - struct CHIPDESC *desc = chiplist + chip->type; + struct CHIPDESC *desc = chip->desc; switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: @@ -1718,7 +1719,7 @@ static int chip_command(struct i2c_client *client, unsigned int cmd, void *arg) { struct CHIPSTATE *chip = i2c_get_clientdata(client); - struct CHIPDESC *desc = chiplist + chip->type; + struct CHIPDESC *desc = chip->desc; v4l_dbg(1, debug, chip->c, "%s: chip_command 0x%x\n", chip->c->name, cmd); -- cgit v1.2.3 From ae85a80ddd128705ff86971d904a2a12e7cb48ad Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 13 Nov 2008 18:03:28 -0200 Subject: Avoid writing outside shadow.bytes[] array From: Mauro Carvalho Chehab There were no check about the limits of shadow.bytes array. This offers a risk of writing values outside the limits, overriding other data areas. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/tvaudio.c | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/tvaudio.c b/linux/drivers/media/video/tvaudio.c index 1575e6f9d..4a5c20ffa 100644 --- a/linux/drivers/media/video/tvaudio.c +++ b/linux/drivers/media/video/tvaudio.c @@ -157,7 +157,7 @@ static int chip_write(struct CHIPSTATE *chip, int subaddr, int val) { unsigned char buffer[2]; - if (-1 == subaddr) { + if (subaddr < 0) { v4l_dbg(1, debug, chip->c, "%s: chip_write: 0x%x\n", chip->c->name, val); chip->shadow.bytes[1] = val; @@ -168,6 +168,13 @@ static int chip_write(struct CHIPSTATE *chip, int subaddr, int val) return -1; } } else { + if (subaddr + 1 >= ARRAY_SIZE(chip->shadow.bytes)) { + v4l_info(chip->c, + "Tried to access a non-existent register: %d\n", + subaddr); + return -EINVAL; + } + v4l_dbg(1, debug, chip->c, "%s: chip_write: reg%d=0x%x\n", chip->c->name, subaddr, val); chip->shadow.bytes[subaddr+1] = val; @@ -182,12 +189,20 @@ static int chip_write(struct CHIPSTATE *chip, int subaddr, int val) return 0; } -static int chip_write_masked(struct CHIPSTATE *chip, int subaddr, int val, int mask) +static int chip_write_masked(struct CHIPSTATE *chip, + int subaddr, int val, int mask) { if (mask != 0) { - if (-1 == subaddr) { + if (subaddr < 0) { val = (chip->shadow.bytes[1] & ~mask) | (val & mask); } else { + if (subaddr + 1 >= ARRAY_SIZE(chip->shadow.bytes)) { + v4l_info(chip->c, + "Tried to access a non-existent register: %d\n", + subaddr); + return -EINVAL; + } + val = (chip->shadow.bytes[subaddr+1] & ~mask) | (val & mask); } } @@ -233,6 +248,15 @@ static int chip_cmd(struct CHIPSTATE *chip, char *name, audiocmd *cmd) if (0 == cmd->count) return 0; + if (cmd->count + cmd->bytes[0] - 1 >= ARRAY_SIZE(chip->shadow.bytes)) { + v4l_info(chip->c, + "Tried to access a non-existent register range: %d to %d\n", + cmd->bytes[0] + 1, cmd->bytes[0] + cmd->count - 1); + return -EINVAL; + } + + /* FIXME: it seems that the shadow bytes are wrong bellow !*/ + /* update our shadow register set; print bytes if (debug > 0) */ v4l_dbg(1, debug, chip->c, "%s: chip_cmd(%s): reg=%d, data:", chip->c->name, name,cmd->bytes[0]); -- cgit v1.2.3 From 7e22300bf63145f172fc8ea241447c828b564cf4 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 13 Nov 2008 18:25:04 -0200 Subject: tvaudio: Improve comments and remove a unneeded prototype From: Mauro Carvalho Chehab Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/tvaudio.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/tvaudio.c b/linux/drivers/media/video/tvaudio.c index 4a5c20ffa..fcf5a5314 100644 --- a/linux/drivers/media/video/tvaudio.c +++ b/linux/drivers/media/video/tvaudio.c @@ -109,7 +109,6 @@ struct CHIPDESC { int inputmute; int inputmask; }; -static struct CHIPDESC chiplist[]; /* current state of the chip */ struct CHIPSTATE { @@ -1868,11 +1867,13 @@ static int chip_command(struct i2c_client *client, case VIDIOC_S_FREQUENCY: chip->mode = 0; /* automatic */ - /* For chips that provide getmode, setmode and checkmode, - a kthread is created to automatically to set the audio - standard. In this case, start with MONO and wait 2 seconds - for the decoding to stablize. Then, run kthread to change - to stereo, if carrier detected. + /* For chips that provide getmode and setmode, and doesn't + automatically follows the stereo carrier, a kthread is + created to set the audio standard. In this case, when then + the video channel is changed, tvaudio starts on MONO mode. + After waiting for 2 seconds, the kernel thread is called, + to follow whatever audio standard is pointed by the + audio carrier. */ if (chip->thread) { desc->setmode(chip,V4L2_TUNER_MODE_MONO); @@ -1921,9 +1922,3 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = { .id_table = chip_id, #endif }; - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ -- cgit v1.2.3 From e9589d5e9a7ce0950ee6150ce06349dac22fd8e2 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 13 Nov 2008 19:12:43 -0200 Subject: tvaudio: Improve debug msg by printing something more human From: Mauro Carvalho Chehab Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/tvaudio.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/video/tvaudio.c b/linux/drivers/media/video/tvaudio.c index fcf5a5314..e6a52962e 100644 --- a/linux/drivers/media/video/tvaudio.c +++ b/linux/drivers/media/video/tvaudio.c @@ -37,6 +37,7 @@ #include #include +#include #include #include @@ -1744,7 +1745,10 @@ static int chip_command(struct i2c_client *client, struct CHIPSTATE *chip = i2c_get_clientdata(client); struct CHIPDESC *desc = chip->desc; - v4l_dbg(1, debug, chip->c, "%s: chip_command 0x%x\n", chip->c->name, cmd); + if (debug > 0) { + v4l_i2c_print_ioctl(chip->c, cmd); + printk("\n"); + } switch (cmd) { case AUDC_SET_RADIO: -- cgit v1.2.3 From e673ee96f4708b75bd25a9d4995dcf49667e239f Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Mon, 17 Nov 2008 22:20:38 +0200 Subject: af9015: Add support for the Digittrade DVB-T USB Stick remote From: Alain Kalker Adds support for the Digittrade DVB-T USB Stick remote. As the Digittrade USB stick identifies itself as a generic Afatech AF9015 device, the remote cannot be autodetected. To enable it, add the following to /etc/modprobe.d/dvb-usb-af9015 or /etc/modprobe.conf: options dvb-usb-af9015 remote=4 Priority: normal Signed-off-by: Alain Kalker Signed-off-by: Antti Palosaari --- linux/drivers/media/dvb/dvb-usb/af9015.c | 10 +++++ linux/drivers/media/dvb/dvb-usb/af9015.h | 64 ++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) (limited to 'linux') diff --git a/linux/drivers/media/dvb/dvb-usb/af9015.c b/linux/drivers/media/dvb/dvb-usb/af9015.c index c52ebce9f..77a29f7f9 100644 --- a/linux/drivers/media/dvb/dvb-usb/af9015.c +++ b/linux/drivers/media/dvb/dvb-usb/af9015.c @@ -733,6 +733,16 @@ static int af9015_read_config(struct usb_device *udev) af9015_config.ir_table_size = ARRAY_SIZE(af9015_ir_table_mygictv); break; + case AF9015_REMOTE_DIGITTRADE_DVB_T: + af9015_properties[i].rc_key_map = + af9015_rc_keys_digittrade; + af9015_properties[i].rc_key_map_size = + ARRAY_SIZE(af9015_rc_keys_digittrade); + af9015_config.ir_table = + af9015_ir_table_digittrade; + af9015_config.ir_table_size = + ARRAY_SIZE(af9015_ir_table_digittrade); + break; } } else { switch (udev->descriptor.idVendor) { diff --git a/linux/drivers/media/dvb/dvb-usb/af9015.h b/linux/drivers/media/dvb/dvb-usb/af9015.h index 1ab86cc6c..21c7782f4 100644 --- a/linux/drivers/media/dvb/dvb-usb/af9015.h +++ b/linux/drivers/media/dvb/dvb-usb/af9015.h @@ -123,6 +123,7 @@ enum af9015_remote { AF9015_REMOTE_A_LINK_DTU_M, AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3, AF9015_REMOTE_MYGICTV_U718, + AF9015_REMOTE_DIGITTRADE_DVB_T, }; /* Leadtek WinFast DTV Dongle Gold */ @@ -596,4 +597,67 @@ static u8 af9015_ir_table_avermedia[] = { 0x03, 0xfc, 0x03, 0xfc, 0x0e, 0x05, 0x00, }; +/* Digittrade DVB-T USB Stick */ +static struct dvb_usb_rc_key af9015_rc_keys_digittrade[] = { + { 0x01, 0x0f, KEY_LAST }, /* RETURN */ + { 0x05, 0x17, KEY_TEXT }, /* TELETEXT */ + { 0x01, 0x08, KEY_EPG }, /* EPG */ + { 0x05, 0x13, KEY_POWER }, /* POWER */ + { 0x01, 0x09, KEY_ZOOM }, /* FULLSCREEN */ + { 0x00, 0x40, KEY_AUDIO }, /* DUAL SOUND */ + { 0x00, 0x2c, KEY_PRINT }, /* SNAPSHOT */ + { 0x05, 0x16, KEY_SUBTITLE }, /* SUBTITLE */ + { 0x00, 0x52, KEY_CHANNELUP }, /* CH Up */ + { 0x00, 0x51, KEY_CHANNELDOWN },/* Ch Dn */ + { 0x00, 0x57, KEY_VOLUMEUP }, /* Vol Up */ + { 0x00, 0x56, KEY_VOLUMEDOWN }, /* Vol Dn */ + { 0x01, 0x10, KEY_MUTE }, /* MUTE */ + { 0x00, 0x27, KEY_0 }, + { 0x00, 0x1e, KEY_1 }, + { 0x00, 0x1f, KEY_2 }, + { 0x00, 0x20, KEY_3 }, + { 0x00, 0x21, KEY_4 }, + { 0x00, 0x22, KEY_5 }, + { 0x00, 0x23, KEY_6 }, + { 0x00, 0x24, KEY_7 }, + { 0x00, 0x25, KEY_8 }, + { 0x00, 0x26, KEY_9 }, + { 0x01, 0x17, KEY_PLAYPAUSE }, /* TIMESHIFT */ + { 0x01, 0x15, KEY_RECORD }, /* RECORD */ + { 0x03, 0x13, KEY_PLAY }, /* PLAY */ + { 0x01, 0x16, KEY_STOP }, /* STOP */ + { 0x01, 0x13, KEY_PAUSE }, /* PAUSE */ +}; + +static u8 af9015_ir_table_digittrade[] = { + 0x00, 0xff, 0x06, 0xf9, 0x13, 0x05, 0x00, + 0x00, 0xff, 0x4d, 0xb2, 0x17, 0x01, 0x00, + 0x00, 0xff, 0x1f, 0xe0, 0x2c, 0x00, 0x00, + 0x00, 0xff, 0x0a, 0xf5, 0x15, 0x01, 0x00, + 0x00, 0xff, 0x0e, 0xf1, 0x16, 0x01, 0x00, + 0x00, 0xff, 0x09, 0xf6, 0x09, 0x01, 0x00, + 0x00, 0xff, 0x01, 0xfe, 0x08, 0x01, 0x00, + 0x00, 0xff, 0x05, 0xfa, 0x10, 0x01, 0x00, + 0x00, 0xff, 0x02, 0xfd, 0x56, 0x00, 0x00, + 0x00, 0xff, 0x40, 0xbf, 0x57, 0x00, 0x00, + 0x00, 0xff, 0x19, 0xe6, 0x52, 0x00, 0x00, + 0x00, 0xff, 0x17, 0xe8, 0x51, 0x00, 0x00, + 0x00, 0xff, 0x10, 0xef, 0x0f, 0x01, 0x00, + 0x00, 0xff, 0x54, 0xab, 0x27, 0x00, 0x00, + 0x00, 0xff, 0x1b, 0xe4, 0x1e, 0x00, 0x00, + 0x00, 0xff, 0x11, 0xee, 0x1f, 0x00, 0x00, + 0x00, 0xff, 0x15, 0xea, 0x20, 0x00, 0x00, + 0x00, 0xff, 0x12, 0xed, 0x21, 0x00, 0x00, + 0x00, 0xff, 0x16, 0xe9, 0x22, 0x00, 0x00, + 0x00, 0xff, 0x4c, 0xb3, 0x23, 0x00, 0x00, + 0x00, 0xff, 0x48, 0xb7, 0x24, 0x00, 0x00, + 0x00, 0xff, 0x04, 0xfb, 0x25, 0x00, 0x00, + 0x00, 0xff, 0x00, 0xff, 0x26, 0x00, 0x00, + 0x00, 0xff, 0x1e, 0xe1, 0x13, 0x03, 0x00, + 0x00, 0xff, 0x1a, 0xe5, 0x13, 0x01, 0x00, + 0x00, 0xff, 0x03, 0xfc, 0x17, 0x05, 0x00, + 0x00, 0xff, 0x0d, 0xf2, 0x16, 0x05, 0x00, + 0x00, 0xff, 0x1d, 0xe2, 0x40, 0x00, 0x00, +}; + #endif -- cgit v1.2.3 From f7addb7035dedd9ba701c586fec16b234dde4a77 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 17 Nov 2008 22:32:11 +0100 Subject: uvcvideo: Fix printk badness when printing ioctl names From: Laurent Pinchart Add a trailing newline character after calling v4l_printk_ioctl() to avoid messing the kernel log up and merge the uvc_v4l2_ioctl message with the ioctl name. Priority: normal Signed-off-by: Laurent Pinchart --- linux/drivers/media/video/uvc/uvc_v4l2.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/uvc/uvc_v4l2.c b/linux/drivers/media/video/uvc/uvc_v4l2.c index b4191cb21..95eb6f3ad 100644 --- a/linux/drivers/media/video/uvc/uvc_v4l2.c +++ b/linux/drivers/media/video/uvc/uvc_v4l2.c @@ -470,9 +470,6 @@ static int uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) struct uvc_fh *handle = (struct uvc_fh *)file->private_data; int ret = 0; - if (uvc_trace_param & UVC_TRACE_IOCTL) - v4l_printk_ioctl(cmd); - switch (cmd) { /* Query capabilities */ case VIDIOC_QUERYCAP: @@ -989,7 +986,12 @@ static int uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) static int uvc_v4l2_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_ioctl\n"); + if (uvc_trace_param & UVC_TRACE_IOCTL) { + uvc_printk(KERN_DEBUG, "uvc_v4l2_ioctl("); + v4l_printk_ioctl(cmd); + printk(")\n"); + } + return video_usercopy(file, cmd, arg, uvc_v4l2_do_ioctl); } -- cgit v1.2.3 From 94af51fcec623e24c6a1e9fdf8130b1b70d7dd08 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 20 Nov 2008 08:07:45 -0200 Subject: new email address From: Jelle Foks Please accept this patch to refer to my new email address Priority: normal Signed-off-by: Jelle Foks Signed-off-by: Mauro Carvalho Chehab --- linux/Documentation/dvb/get_dvb_firmware | 0 linux/Documentation/video4linux/extract_xc3028.pl | 0 linux/drivers/media/video/cx23885/cx23885-417.c | 2 +- linux/drivers/media/video/cx88/cx88-blackbird.c | 4 ++-- linux/drivers/media/video/cx88/cx88-mpeg.c | 4 ++-- 5 files changed, 5 insertions(+), 5 deletions(-) mode change 100755 => 100644 linux/Documentation/dvb/get_dvb_firmware mode change 100755 => 100644 linux/Documentation/video4linux/extract_xc3028.pl (limited to 'linux') diff --git a/linux/Documentation/dvb/get_dvb_firmware b/linux/Documentation/dvb/get_dvb_firmware old mode 100755 new mode 100644 diff --git a/linux/Documentation/video4linux/extract_xc3028.pl b/linux/Documentation/video4linux/extract_xc3028.pl old mode 100755 new mode 100644 diff --git a/linux/drivers/media/video/cx23885/cx23885-417.c b/linux/drivers/media/video/cx23885/cx23885-417.c index d98e64545..dfe8ddbce 100644 --- a/linux/drivers/media/video/cx23885/cx23885-417.c +++ b/linux/drivers/media/video/cx23885/cx23885-417.c @@ -2,7 +2,7 @@ * * Support for a cx23417 mpeg encoder via cx23885 host port. * - * (c) 2004 Jelle Foks + * (c) 2004 Jelle Foks * (c) 2004 Gerd Knorr * (c) 2008 Steven Toth * - CX23885/7/8 support diff --git a/linux/drivers/media/video/cx88/cx88-blackbird.c b/linux/drivers/media/video/cx88/cx88-blackbird.c index 3d2afe195..b090c6620 100644 --- a/linux/drivers/media/video/cx88/cx88-blackbird.c +++ b/linux/drivers/media/video/cx88/cx88-blackbird.c @@ -3,7 +3,7 @@ * Support for a cx23416 mpeg encoder via cx2388x host port. * "blackbird" reference design. * - * (c) 2004 Jelle Foks + * (c) 2004 Jelle Foks * (c) 2004 Gerd Knorr * * (c) 2005-2006 Mauro Carvalho Chehab @@ -39,7 +39,7 @@ #include "cx88.h" MODULE_DESCRIPTION("driver for cx2388x/cx23416 based mpeg encoder cards"); -MODULE_AUTHOR("Jelle Foks , Gerd Knorr [SuSE Labs]"); +MODULE_AUTHOR("Jelle Foks , Gerd Knorr [SuSE Labs]"); MODULE_LICENSE("GPL"); static unsigned int mpegbufs = 32; diff --git a/linux/drivers/media/video/cx88/cx88-mpeg.c b/linux/drivers/media/video/cx88/cx88-mpeg.c index dbad45a35..9d407ac26 100644 --- a/linux/drivers/media/video/cx88/cx88-mpeg.c +++ b/linux/drivers/media/video/cx88/cx88-mpeg.c @@ -3,7 +3,7 @@ * Support for the mpeg transport stream transfers * PCI function #2 of the cx2388x. * - * (c) 2004 Jelle Foks + * (c) 2004 Jelle Foks * (c) 2004 Chris Pascoe * (c) 2004 Gerd Knorr * @@ -34,7 +34,7 @@ /* ------------------------------------------------------------------ */ MODULE_DESCRIPTION("mpeg driver for cx2388x based TV cards"); -MODULE_AUTHOR("Jelle Foks "); +MODULE_AUTHOR("Jelle Foks "); MODULE_AUTHOR("Chris Pascoe "); MODULE_AUTHOR("Gerd Knorr [SuSE Labs]"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 320830af384f2a322324e41bf4eeac3eeaf71b98 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 19 Nov 2008 03:36:29 +0000 Subject: radio-mr800: fix unplug From: Alexey Klimov This patch fixes problems(kernel oopses) with unplug of device while it's working. Patch adds disconnect_lock mutex, changes usb_amradio_close and usb_amradio_disconnect functions and adds a lot of safety checks. Signed-off-by: Alexey Klimov Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/radio/radio-mr800.c | 62 ++++++++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 8 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/radio/radio-mr800.c b/linux/drivers/media/radio/radio-mr800.c index 208ade5b0..dbce57c26 100644 --- a/linux/drivers/media/radio/radio-mr800.c +++ b/linux/drivers/media/radio/radio-mr800.c @@ -142,6 +142,7 @@ struct amradio_device { unsigned char *buffer; struct mutex lock; /* buffer locking */ + struct mutex disconnect_lock; int curfreq; int stereo; int users; @@ -210,6 +211,10 @@ static int amradio_stop(struct amradio_device *radio) int retval; int size; + /* safety check */ + if (radio->removed) + return -EIO; + mutex_lock(&radio->lock); radio->buffer[0] = 0x00; @@ -243,6 +248,10 @@ static int amradio_setfreq(struct amradio_device *radio, int freq) int size; unsigned short freq_send = 0x13 + (freq >> 3) / 25; + /* safety check */ + if (radio->removed) + return -EIO; + mutex_lock(&radio->lock); radio->buffer[0] = 0x00; @@ -296,18 +305,16 @@ static void usb_amradio_disconnect(struct usb_interface *intf) { struct amradio_device *radio = usb_get_intfdata(intf); + mutex_lock(&radio->disconnect_lock); + radio->removed = 1; usb_set_intfdata(intf, NULL); - if (radio) { + if (radio->users == 0) { video_unregister_device(radio->videodev); - radio->videodev = NULL; - if (radio->users) { - kfree(radio->buffer); - kfree(radio); - } else { - radio->removed = 1; - } + kfree(radio->buffer); + kfree(radio); } + mutex_unlock(&radio->disconnect_lock); } /* vidioc_querycap - query device capabilities */ @@ -328,6 +335,10 @@ static int vidioc_g_tuner(struct file *file, void *priv, { struct amradio_device *radio = video_get_drvdata(video_devdata(file)); + /* safety check */ + if (radio->removed) + return -EIO; + if (v->index > 0) return -EINVAL; @@ -354,6 +365,12 @@ static int vidioc_g_tuner(struct file *file, void *priv, static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *v) { + struct amradio_device *radio = video_get_drvdata(video_devdata(file)); + + /* safety check */ + if (radio->removed) + return -EIO; + if (v->index > 0) return -EINVAL; return 0; @@ -365,6 +382,10 @@ static int vidioc_s_frequency(struct file *file, void *priv, { struct amradio_device *radio = video_get_drvdata(video_devdata(file)); + /* safety check */ + if (radio->removed) + return -EIO; + radio->curfreq = f->frequency; if (amradio_setfreq(radio, radio->curfreq) < 0) amradio_dev_warn(&radio->videodev->dev, @@ -378,6 +399,10 @@ static int vidioc_g_frequency(struct file *file, void *priv, { struct amradio_device *radio = video_get_drvdata(video_devdata(file)); + /* safety check */ + if (radio->removed) + return -EIO; + f->type = V4L2_TUNER_RADIO; f->frequency = radio->curfreq; return 0; @@ -404,6 +429,10 @@ static int vidioc_g_ctrl(struct file *file, void *priv, { struct amradio_device *radio = video_get_drvdata(video_devdata(file)); + /* safety check */ + if (radio->removed) + return -EIO; + switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: ctrl->value = radio->muted; @@ -418,6 +447,10 @@ static int vidioc_s_ctrl(struct file *file, void *priv, { struct amradio_device *radio = video_get_drvdata(video_devdata(file)); + /* safety check */ + if (radio->removed) + return -EIO; + switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: if (ctrl->value) { @@ -503,14 +536,26 @@ static int usb_amradio_open(struct inode *inode, struct file *file) static int usb_amradio_close(struct inode *inode, struct file *file) { struct amradio_device *radio = video_get_drvdata(video_devdata(file)); + int retval; if (!radio) return -ENODEV; + + mutex_lock(&radio->disconnect_lock); radio->users = 0; if (radio->removed) { + video_unregister_device(radio->videodev); kfree(radio->buffer); kfree(radio); + + } else { + retval = amradio_stop(radio); + if (retval < 0) + amradio_dev_warn(&radio->videodev->dev, + "amradio_stop failed\n"); } + + mutex_unlock(&radio->disconnect_lock); return 0; } @@ -610,6 +655,7 @@ static int usb_amradio_probe(struct usb_interface *intf, radio->usbdev = interface_to_usbdev(intf); radio->curfreq = 95.16 * FREQ_MUL; + mutex_init(&radio->disconnect_lock); mutex_init(&radio->lock); video_set_drvdata(radio->videodev, radio); -- cgit v1.2.3 From 62b145c464b403e8ca5d1e17af30367298d5c6a0 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 19 Nov 2008 07:17:44 -0200 Subject: Avoid having two concurrent control URB's From: Mauro Carvalho Chehab Now that we have a polling task for IR, there's a race condition, since IR can be polling while other operations are being doing. Also, we are now sharing the same urb_buf for both read and write control urb operations. So, we need a mutex. Thanks to Davin Heitmueller for warning me. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-core.c | 12 ++++++++++-- linux/drivers/media/video/em28xx/em28xx-video.c | 1 + linux/drivers/media/video/em28xx/em28xx.h | 1 + 3 files changed, 12 insertions(+), 2 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-core.c b/linux/drivers/media/video/em28xx/em28xx-core.c index ef6830cd5..490afcba0 100644 --- a/linux/drivers/media/video/em28xx/em28xx-core.c +++ b/linux/drivers/media/video/em28xx/em28xx-core.c @@ -76,18 +76,22 @@ int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg, em28xx_regdbg("req=%02x, reg=%02x ", req, reg); + mutex_lock(&dev->ctrl_urb_lock); ret = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), req, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0x0000, reg, dev->urb_buf, len, HZ); if (ret < 0) { if (reg_debug) printk(" failed!\n"); + mutex_unlock(&dev->ctrl_urb_lock); return ret; } if (len) memcpy(buf, dev->urb_buf, len); + mutex_unlock(&dev->ctrl_urb_lock); + if (reg_debug) { printk("%02x values: ", ret); for (byte = 0; byte < len; byte++) @@ -112,16 +116,18 @@ int em28xx_read_reg_req(struct em28xx *dev, u8 req, u16 reg) em28xx_regdbg("req=%02x, reg=%02x:", req, reg); + mutex_lock(&dev->ctrl_urb_lock); ret = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), req, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0x0000, reg, dev->urb_buf, 1, HZ); + val = dev->urb_buf[0]; + mutex_unlock(&dev->ctrl_urb_lock); + if (ret < 0) { printk(" failed!\n"); return ret; } - val = dev->urb_buf[0]; - if (reg_debug) printk("%02x\n", (unsigned char) val); @@ -156,10 +162,12 @@ int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf, printk("\n"); } + mutex_lock(&dev->ctrl_urb_lock); memcpy(dev->urb_buf, buf, len); ret = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), req, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0x0000, reg, dev->urb_buf, len, HZ); + mutex_unlock(&dev->ctrl_urb_lock); if (dev->wait_after_write) msleep(dev->wait_after_write); diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index 625c9dea1..70d0bd086 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -2017,6 +2017,7 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, dev->udev = udev; mutex_init(&dev->lock); + mutex_init(&dev->ctrl_urb_lock); spin_lock_init(&dev->slock); init_waitqueue_head(&dev->open); init_waitqueue_head(&dev->wait_frame); diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index 2e108be19..2ef7d0224 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -450,6 +450,7 @@ struct em28xx { /* locks */ struct mutex lock; + struct mutex ctrl_urb_lock; /* protects urb_buf */ /* spinlock_t queue_lock; */ struct list_head inqueue, outqueue; wait_queue_head_t open, wait_frame, wait_stream; -- cgit v1.2.3 From 89163963726ca703e129cb9c2e38206d1f8a2cb4 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 19 Nov 2008 09:22:28 -0200 Subject: Make use of the em28xx chip configuration register to determine whether From: Devin Heitmueller we have AC97 audio, I2S audio, or no audio support at all. Thanks for Ray Lu from Empia for providing the em2860/em2880 datasheet. Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-reg.h | 10 +++++++ linux/drivers/media/video/em28xx/em28xx-video.c | 35 ++++++++++++++++++++++++- linux/drivers/media/video/em28xx/em28xx.h | 8 ++++++ 3 files changed, 52 insertions(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-reg.h b/linux/drivers/media/video/em28xx/em28xx-reg.h index 491d66abe..12c9132b0 100644 --- a/linux/drivers/media/video/em28xx/em28xx-reg.h +++ b/linux/drivers/media/video/em28xx/em28xx-reg.h @@ -17,6 +17,16 @@ /* em28xx registers */ +#define EM28XX_R00_CHIPCFG 0x00 + +/* em28xx Chip Configuration 0x00 */ +#define EM28XX_CHIPCFG_VENDOR_AUDIO 0x80 +#define EM28XX_CHIPCFG_I2S_VOLUME_CAPABLE 0x40 +#define EM28XX_CHIPCFG_I2S_3_SAMPRATES 0x30 +#define EM28XX_CHIPCFG_I2S_5_SAMPRATES 0x20 +#define EM28XX_CHIPCFG_AC97 0x10 +#define EM28XX_CHIPCFG_AUDIOMASK 0x30 + /* GPIO/GPO registers */ #define EM2880_R04_GPO 0x04 /* em2880-em2883 only */ #define EM28XX_R08_GPIO 0x08 /* em2820 or upper */ diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index 70d0bd086..d406fc224 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -1990,16 +1990,49 @@ static struct video_device *em28xx_vdev_init(struct em28xx *dev, int em28xx_supports_audio_extension(struct em28xx *dev) { + int rc; + /* The chip dictates whether we support the Empia analog audio extension */ switch (dev->chip_id) { case CHIP_ID_EM2874: - /* Either a digital-only device or provides AC97 audio */ + /* Digital only device - no analog support */ + dev->audio_mode = EM28XX_NO_AUDIO; return 0; + case CHIP_ID_EM2860: case CHIP_ID_EM2883: default: + /* See how this device is configured */ + rc = em28xx_read_reg(dev, EM28XX_R00_CHIPCFG); + if (rc & EM28XX_CHIPCFG_VENDOR_AUDIO) { + switch(rc & EM28XX_CHIPCFG_AUDIOMASK) { + case EM28XX_CHIPCFG_AC97: + em28xx_info("AC97 audio (5 sample rates)\n"); + dev->audio_mode = EM28XX_AC97; + break; + case EM28XX_CHIPCFG_I2S_3_SAMPRATES: + em28xx_info("I2S Audio (3 sample rates)\n"); + dev->audio_mode = EM28XX_I2S_3_SAMPLE_RATES; + break; + case EM28XX_CHIPCFG_I2S_5_SAMPRATES: + em28xx_info("I2S Audio (5 sample rates)\n"); + dev->audio_mode = EM28XX_I2S_5_SAMPLE_RATES; + break; + default: + em28xx_info("No audio support detected\n"); + dev->audio_mode = EM28XX_NO_AUDIO; + return 0; + } + } else { + em28xx_info("USB Audio class device\n"); + return 0; + } + /* The em28xx audio extension needs to be loaded */ return 1; } + + /* We should never reach this point */ + return 0; } /* diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index 2ef7d0224..d272598c9 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -257,6 +257,13 @@ enum enum28xx_itype { EM28XX_RADIO, }; +enum em28xx_audio_mode { + EM28XX_NO_AUDIO, + EM28XX_I2S_3_SAMPLE_RATES, + EM28XX_I2S_5_SAMPLE_RATES, + EM28XX_AC97, +}; + enum em28xx_amux { EM28XX_AMUX_VIDEO, EM28XX_AMUX_LINE_IN, @@ -412,6 +419,7 @@ struct em28xx { u32 i2s_speed; /* I2S speed for audio digital stream */ enum em28xx_decoder decoder; + enum em28xx_audio_mode audio_mode; int tuner_type; /* type of the tuner */ int tuner_addr; /* tuner address */ -- cgit v1.2.3 From d66eb05b55e0b7dee53b5abcdacd324c74289e84 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 19 Nov 2008 12:08:53 -0200 Subject: em28xx: remove two amux entries used only on one card From: Mauro Carvalho Chehab This patch parepares for an audio refactor patch that auto-detects ac97 chips. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-cards.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 57c91838e..fbbe5ab0c 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -946,11 +946,11 @@ struct em28xx_board em28xx_boards[] = { }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, - .amux = EM28XX_AMUX_AC97_LINE_IN, + .amux = EM28XX_AMUX_LINE_IN, }, { /* S-video has not been tested yet */ .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, - .amux = EM28XX_AMUX_AC97_LINE_IN, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2881_BOARD_DNT_DA2_HYBRID] = { -- cgit v1.2.3 From 460f427f45af6f9f477339f8b689d6576da41127 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 19 Nov 2008 12:10:50 -0200 Subject: em28xx: replace magic numbers to something more meaningful From: Mauro Carvalho Chehab audio mux entries were described by 0 and 1 magic numers. Use an enum alias for each entry type. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-cards.c | 252 ++++++++++++------------ 1 file changed, 126 insertions(+), 126 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index fbbe5ab0c..01765c33f 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -59,7 +59,7 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = 0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, } }, }, [EM2800_BOARD_UNKNOWN] = { @@ -71,11 +71,11 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = SAA7115_SVIDEO3, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2820_BOARD_UNKNOWN] = { @@ -91,7 +91,7 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = 0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, } }, }, [EM2820_BOARD_KWORLD_PVRTV2800RF] = { @@ -104,11 +104,11 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = SAA7115_SVIDEO3, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2820_BOARD_TERRATEC_CINERGY_250] = { @@ -120,15 +120,15 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = SAA7115_SVIDEO3, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2820_BOARD_PINNACLE_USB_2] = { @@ -140,15 +140,15 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = SAA7115_SVIDEO3, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2820_BOARD_HAUPPAUGE_WINTV_USB_2] = { @@ -183,15 +183,15 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = SAA7115_SVIDEO3, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2820_BOARD_HERCULES_SMART_TV_USB2] = { @@ -204,15 +204,15 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = SAA7115_SVIDEO3, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2820_BOARD_PINNACLE_USB_2_FM1216ME] = { @@ -226,15 +226,15 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = SAA7115_SVIDEO3, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2820_BOARD_GADMEI_UTV310] = { @@ -247,15 +247,15 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE1, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = SAA7115_SVIDEO3, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE] = { @@ -268,15 +268,15 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = 2, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = 0, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = 9, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2820_BOARD_PINNACLE_DVC_100] = { @@ -287,11 +287,11 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = SAA7115_SVIDEO3, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2820_BOARD_VIDEOLOGY_20K14XUSB] = { @@ -301,7 +301,7 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = 0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, } }, }, [EM2821_BOARD_PROLINK_PLAYTV_USB2] = { @@ -315,15 +315,15 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = SAA7115_SVIDEO3, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2821_BOARD_SUPERCOMP_USB_2] = { @@ -339,15 +339,15 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = SAA7115_SVIDEO3, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2821_BOARD_USBGEAR_VD204] = { @@ -358,11 +358,11 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = SAA7115_SVIDEO3, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2860_BOARD_NETGMBH_CAM] = { @@ -373,7 +373,7 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = 0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, } }, }, [EM2860_BOARD_TYPHOON_DVD_MAKER] = { @@ -384,11 +384,11 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = SAA7115_SVIDEO3, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2860_BOARD_GADMEI_UTV330] = { @@ -401,15 +401,15 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = SAA7115_SVIDEO3, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2860_BOARD_TERRATEC_HYBRID_XS] = { @@ -421,15 +421,15 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2861_BOARD_KWORLD_PVRTV_300U] = { @@ -441,15 +441,15 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2861_BOARD_YAKUMO_MOVIE_MIXER] = { @@ -460,15 +460,15 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2861_BOARD_PLEXTOR_PX_TV100U] = { @@ -481,15 +481,15 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2870_BOARD_TERRATEC_XS] = { @@ -531,15 +531,15 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900] = { @@ -553,15 +553,15 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2] = { @@ -574,7 +574,7 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, @@ -582,7 +582,7 @@ struct em28xx_board em28xx_boards[] = { }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950] = { @@ -598,15 +598,15 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2880_BOARD_PINNACLE_PCTV_HD_PRO] = { @@ -622,15 +622,15 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600] = { @@ -646,15 +646,15 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2880_BOARD_TERRATEC_HYBRID_XS] = { @@ -667,15 +667,15 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, /* maybe there's a reason behind it why Terratec sells the Hybrid XS @@ -690,15 +690,15 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2820_BOARD_MSI_VOX_USB_2] = { @@ -714,15 +714,15 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE4, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = SAA7115_SVIDEO3, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2800_BOARD_TERRATEC_CINERGY_200] = { @@ -735,15 +735,15 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = SAA7115_SVIDEO3, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2800_BOARD_GRABBEEX_USB2800] = { @@ -754,11 +754,11 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = SAA7115_SVIDEO3, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2800_BOARD_LEADTEK_WINFAST_USBII] = { @@ -771,15 +771,15 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = SAA7115_SVIDEO3, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2800_BOARD_KWORLD_USB2800] = { @@ -792,15 +792,15 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = SAA7115_SVIDEO3, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2820_BOARD_PINNACLE_DVC_90] = { @@ -811,11 +811,11 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = SAA7115_SVIDEO3, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2800_BOARD_VGEAR_POCKETTV] = { @@ -828,15 +828,15 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = SAA7115_SVIDEO3, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2820_BOARD_PROLINK_PLAYTV_USB2] = { @@ -869,7 +869,7 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_SVIDEO, .vmux = SAA7115_SVIDEO3, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, } }, }, [EM2880_BOARD_MSI_DIGIVOX_AD] = { @@ -881,15 +881,15 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2880_BOARD_MSI_DIGIVOX_AD_II] = { @@ -901,15 +901,15 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2880_BOARD_KWORLD_DVB_305U] = { @@ -921,15 +921,15 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2880_BOARD_KWORLD_DVB_310U] = { @@ -962,15 +962,15 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2881_BOARD_PINNACLE_HYBRID_PRO] = { @@ -982,15 +982,15 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2882_BOARD_PINNACLE_HYBRID_PRO] = { @@ -1003,15 +1003,15 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2882_BOARD_KWORLD_VS_DVBT] = { @@ -1023,15 +1023,15 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2882_BOARD_TERRATEC_HYBRID_XS] = { @@ -1043,15 +1043,15 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2883_BOARD_KWORLD_HYBRID_A316] = { @@ -1064,15 +1064,15 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, - .amux = 1, + .amux = EM28XX_AMUX_LINE_IN, } }, }, [EM2820_BOARD_COMPRO_VIDEOMATE_FORYOU] = { -- cgit v1.2.3 From 882f0f5cc3bf4bcd00923aad0cacf002d8009e06 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 19 Nov 2008 13:01:33 -0200 Subject: em28xx: Improve audio handling From: Mauro Carvalho Chehab This patch properly implements audio handling on em28xx. Before this patch, it was assumed that every device has an Empia 202 audio chip. However, this is not true. After this patch, specific AC97 chipset setup and configurations can be done. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-core.c | 253 +++++++++++++++++++----- linux/drivers/media/video/em28xx/em28xx-video.c | 63 +----- linux/drivers/media/video/em28xx/em28xx.h | 27 ++- 3 files changed, 226 insertions(+), 117 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-core.c b/linux/drivers/media/video/em28xx/em28xx-core.c index 490afcba0..3a6252a8b 100644 --- a/linux/drivers/media/video/em28xx/em28xx-core.c +++ b/linux/drivers/media/video/em28xx/em28xx-core.c @@ -223,16 +223,71 @@ static int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val, return em28xx_write_regs(dev, reg, &newval, 1); } +/* + * em28xx_is_ac97_ready() + * Checks if ac97 is ready + */ +static int em28xx_is_ac97_ready(struct em28xx *dev) +{ + int ret, i; + + /* Wait up to 50 ms for AC97 command to complete */ + for (i = 0; i < 10; i++, msleep(5)) { + ret = em28xx_read_reg(dev, EM28XX_R43_AC97BUSY); + if (ret < 0) + return ret; + + if (!(ret & 0x01)) + return 0; + } + + em28xx_warn("AC97 command still being executed: not handled properly!\n"); + return -EBUSY; +} + +/* + * em28xx_read_ac97() + * write a 16 bit value to the specified AC97 address (LSB first!) + */ +static int em28xx_read_ac97(struct em28xx *dev, u8 reg) +{ + int ret; + u8 addr = (reg & 0x7f) | 0x80; + u16 val; + + ret = em28xx_is_ac97_ready(dev); + if (ret < 0) + return ret; + + ret = em28xx_write_regs(dev, EM28XX_R42_AC97ADDR, &addr, 1); + if (ret < 0) + return ret; + + ret = dev->em28xx_read_reg_req_len(dev, 0, EM28XX_R40_AC97LSB, + (u8 *)&val, sizeof(val)); + + if (ret < 0) + return ret; + return le16_to_cpu(val); +} + /* * em28xx_write_ac97() * write a 16 bit value to the specified AC97 address (LSB first!) */ -static int em28xx_write_ac97(struct em28xx *dev, u8 reg, u8 *val) +static int em28xx_write_ac97(struct em28xx *dev, u8 reg, u16 val) { - int ret, i; + int ret; u8 addr = reg & 0x7f; + __le16 value; + + value = cpu_to_le16(val); + + ret = em28xx_is_ac97_ready(dev); + if (ret < 0) + return ret; - ret = em28xx_write_regs(dev, EM28XX_R40_AC97LSB, val, 2); + ret = em28xx_write_regs(dev, EM28XX_R40_AC97LSB, (u8 *) &value, 2); if (ret < 0) return ret; @@ -240,25 +295,36 @@ static int em28xx_write_ac97(struct em28xx *dev, u8 reg, u8 *val) if (ret < 0) return ret; - /* Wait up to 50 ms for AC97 command to complete */ - for (i = 0; i < 10; i++) { - ret = em28xx_read_reg(dev, EM28XX_R43_AC97BUSY); - if (ret < 0) - return ret; + return 0; +} - if (!(ret & 0x01)) - return 0; - msleep(5); +static int set_ac97_em202_input(struct em28xx *dev) +{ + int ret; + u16 enable = 0x0808; /* 12 dB attenuation Left/Right */ + u16 disable = 0x8808; /* bit 15 - mute volumme */ + u16 video, line; + + if (dev->ctl_ainput == EM28XX_AMUX_VIDEO) { + video = enable; + line = disable; + } else { + video = disable; + line = enable; } - em28xx_warn("AC97 command still being executed: not handled properly!\n"); - return 0; + + /* Sets em202 AC97 mixer registers */ + ret = em28xx_write_ac97(dev, AC97_VIDEO_VOL, video); + if (ret < 0) + return ret; + + ret = em28xx_write_ac97(dev, AC97_LINEIN_VOL, line); + + return ret; } static int em28xx_set_audio_source(struct em28xx *dev) { - static char *enable = "\x08\x08"; - static char *disable = "\x08\x88"; - char *video = enable, *line = disable; int ret; u8 input; @@ -280,18 +346,8 @@ static int em28xx_set_audio_source(struct em28xx *dev) case EM28XX_AMUX_VIDEO: input = EM28XX_AUDIO_SRC_TUNER; break; - case EM28XX_AMUX_LINE_IN: + default: input = EM28XX_AUDIO_SRC_LINE; - video = disable; - line = enable; - break; - case EM28XX_AMUX_AC97_VIDEO: - input = EM28XX_AUDIO_SRC_LINE; - break; - case EM28XX_AMUX_AC97_LINE_IN: - input = EM28XX_AUDIO_SRC_LINE; - video = disable; - line = enable; break; } } @@ -301,33 +357,36 @@ static int em28xx_set_audio_source(struct em28xx *dev) return ret; msleep(5); - /* Sets AC97 mixer registers - This is seems to be needed, even for non-ac97 configs - */ - ret = em28xx_write_ac97(dev, AC97_VIDEO_VOL, video); - if (ret < 0) - return ret; - - ret = em28xx_write_ac97(dev, AC97_LINEIN_VOL, line); + switch (dev->audio_mode.ac97) { + case EM28XX_NO_AC97: + break; + case EM28XX_AC97_OTHER: + /* We don't know how to handle this chip. + Let's hope it is close enough to em202 to work + */ + case EM28XX_AC97_EM202: + ret = set_ac97_em202_input(dev); + break; + } - return ret; + return 0; } int em28xx_audio_analog_set(struct em28xx *dev) { int ret; - char s[2] = { 0x00, 0x00 }; u8 xclk = 0x07; - s[0] |= 0x1f - dev->volume; - s[1] |= 0x1f - dev->volume; + if (!dev->audio_mode.has_audio) + return 0; - /* Mute */ - s[1] |= 0x80; - ret = em28xx_write_ac97(dev, AC97_MASTER_VOL, s); + if (dev->audio_mode.ac97 != EM28XX_NO_AC97) { + /* Mute */ + ret = em28xx_write_ac97(dev, AC97_MASTER_VOL, 0x8000); - if (ret < 0) - return ret; + if (ret < 0) + return ret; + } if (dev->has_12mhz_i2s) xclk |= 0x20; @@ -343,15 +402,113 @@ int em28xx_audio_analog_set(struct em28xx *dev) /* Selects the proper audio input */ ret = em28xx_set_audio_source(dev); - /* Unmute device */ - if (!dev->mute) - s[1] &= ~0x80; - ret = em28xx_write_ac97(dev, AC97_MASTER_VOL, s); + /* Sets volume */ + if (dev->audio_mode.ac97 != EM28XX_NO_AC97) { + int vol; + + /* LSB: left channel - both channels with the same level */ + vol = (0x1f - dev->volume) | ((0x1f - dev->volume) << 8); + + /* Mute device, if needed */ + if (dev->mute) + vol |= 0x8000; + + /* Sets volume */ + ret = em28xx_write_ac97(dev, AC97_MASTER_VOL, vol); + } return ret; } EXPORT_SYMBOL_GPL(em28xx_audio_analog_set); +int em28xx_audio_setup(struct em28xx *dev) +{ + int vid1, vid2, feat, cfg; + + if (dev->chip_id == CHIP_ID_EM2874) { + /* Digital only device - don't load any alsa module */ + dev->audio_mode.has_audio = 0; + dev->has_audio_class = 0; + dev->has_alsa_audio = 0; + return 0; + } + + /* If device doesn't support Usb Audio Class, use vendor class */ + if (!dev->has_audio_class) + dev->has_alsa_audio = 1; + + dev->audio_mode.has_audio = 1; + + /* See how this device is configured */ + cfg = em28xx_read_reg(dev, EM28XX_R00_CHIPCFG); + if (cfg < 0) + cfg = EM28XX_CHIPCFG_AC97; /* Be conservative */ + else + em28xx_info("Config register raw data: 0x%02x\n", cfg); + + if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) == + EM28XX_CHIPCFG_I2S_3_SAMPRATES) { + em28xx_info("I2S Audio (3 sample rates)\n"); + dev->audio_mode.i2s_3rates = 1; + } + if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) == + EM28XX_CHIPCFG_I2S_5_SAMPRATES) { + em28xx_info("I2S Audio (5 sample rates)\n"); + dev->audio_mode.i2s_5rates = 1; + } + + if (!(cfg & EM28XX_CHIPCFG_AC97)) { + dev->audio_mode.ac97 = EM28XX_NO_AC97; + goto init_audio; + } + + dev->audio_mode.ac97 = EM28XX_AC97_OTHER; + + vid1 = em28xx_read_ac97(dev, AC97_VENDOR_ID1); + if (vid1 < 0) { + /* Device likely doesn't support AC97 */ + em28xx_warn("AC97 chip type couldn't be determined\n"); + goto init_audio; + } + + vid2 = em28xx_read_ac97(dev, AC97_VENDOR_ID2); + if (vid2 < 0) + goto init_audio; + + dev->audio_mode.ac97_vendor_id1 = vid1; + dev->audio_mode.ac97_vendor_id2 = vid2; + em28xx_warn("AC97 vendor ID = %04x:%04x\n", vid1, vid2); + + feat = em28xx_read_ac97(dev, AC97_RESET); + if (feat < 0) + goto init_audio; + + dev->audio_mode.ac97_feat = feat; + em28xx_warn("AC97 features = 0x%04x\n", feat); + + if ((vid1 == 0xffff) && (vid2 == 0xffff) && (feat == 0x6a90)) + dev->audio_mode.ac97 = EM28XX_AC97_EM202; + +init_audio: + /* Reports detected AC97 processor */ + switch (dev->audio_mode.ac97) { + case EM28XX_NO_AC97: + em28xx_info("No AC97 audio processor\n"); + break; + case EM28XX_AC97_EM202: + em28xx_info("Empia 202 AC97 audio processor detected\n"); + break; + case EM28XX_AC97_OTHER: + em28xx_warn("Unknown AC97 audio processor detected!\n"); + break; + default: + break; + } + + return em28xx_audio_analog_set(dev); +} +EXPORT_SYMBOL_GPL(em28xx_audio_setup); + int em28xx_colorlevels_set_default(struct em28xx *dev) { em28xx_write_regs(dev, EM28XX_R20_YGAIN, "\x10", 1); /* contrast */ diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index d406fc224..88bff6bd7 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -1988,53 +1988,6 @@ static struct video_device *em28xx_vdev_init(struct em28xx *dev, return vfd; } -int em28xx_supports_audio_extension(struct em28xx *dev) -{ - int rc; - - /* The chip dictates whether we support the Empia analog audio - extension */ - switch (dev->chip_id) { - case CHIP_ID_EM2874: - /* Digital only device - no analog support */ - dev->audio_mode = EM28XX_NO_AUDIO; - return 0; - case CHIP_ID_EM2860: - case CHIP_ID_EM2883: - default: - /* See how this device is configured */ - rc = em28xx_read_reg(dev, EM28XX_R00_CHIPCFG); - if (rc & EM28XX_CHIPCFG_VENDOR_AUDIO) { - switch(rc & EM28XX_CHIPCFG_AUDIOMASK) { - case EM28XX_CHIPCFG_AC97: - em28xx_info("AC97 audio (5 sample rates)\n"); - dev->audio_mode = EM28XX_AC97; - break; - case EM28XX_CHIPCFG_I2S_3_SAMPRATES: - em28xx_info("I2S Audio (3 sample rates)\n"); - dev->audio_mode = EM28XX_I2S_3_SAMPLE_RATES; - break; - case EM28XX_CHIPCFG_I2S_5_SAMPRATES: - em28xx_info("I2S Audio (5 sample rates)\n"); - dev->audio_mode = EM28XX_I2S_5_SAMPLE_RATES; - break; - default: - em28xx_info("No audio support detected\n"); - dev->audio_mode = EM28XX_NO_AUDIO; - return 0; - } - } else { - em28xx_info("USB Audio class device\n"); - return 0; - } - /* The em28xx audio extension needs to be loaded */ - return 1; - } - - /* We should never reach this point */ - return 0; -} - /* * em28xx_init_dev() * allocates and inits the device structs, registers i2c bus and v4l device @@ -2083,11 +2036,10 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, em28xx_card_setup(dev); /* Configure audio */ - errCode = em28xx_audio_analog_set(dev); + errCode = em28xx_audio_setup(dev); if (errCode < 0) { - em28xx_errdev("%s: em28xx_audio_analog_set - errCode [%d]!\n", + em28xx_errdev("%s: Error while setting audio - errCode [%d]!\n", __func__, errCode); - return errCode; } /* configure the device */ @@ -2373,17 +2325,6 @@ static int em28xx_usb_probe(struct usb_interface *interface, em28xx_info("Found %s\n", em28xx_boards[dev->model].name); - if (dev->has_audio_class == 0) { - /* We don't have a USB audio class, let's see if we support - ALSA Audio */ - dev->has_alsa_audio = em28xx_supports_audio_extension(dev); - if (dev->has_alsa_audio) - printk(KERN_INFO DRIVER_NAME " supports alsa audio\n"); - } else { - printk(KERN_INFO DRIVER_NAME " has usb audio class\n"); - } - - /* save our data pointer in this interface device */ usb_set_intfdata(interface, dev); diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index d272598c9..06d5872f1 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -257,18 +257,28 @@ enum enum28xx_itype { EM28XX_RADIO, }; -enum em28xx_audio_mode { - EM28XX_NO_AUDIO, - EM28XX_I2S_3_SAMPLE_RATES, - EM28XX_I2S_5_SAMPLE_RATES, - EM28XX_AC97, +enum em28xx_ac97_mode { + EM28XX_NO_AC97 = 0, + EM28XX_AC97_EM202, + EM28XX_AC97_OTHER, +}; + +struct em28xx_audio_mode { + enum em28xx_ac97_mode ac97; + + u16 ac97_feat; + u16 ac97_vendor_id1; + u16 ac97_vendor_id2; + + unsigned int has_audio:1; + + unsigned int i2s_3rates:1; + unsigned int i2s_5rates:1; }; enum em28xx_amux { EM28XX_AMUX_VIDEO, EM28XX_AMUX_LINE_IN, - EM28XX_AMUX_AC97_VIDEO, - EM28XX_AMUX_AC97_LINE_IN, }; struct em28xx_input { @@ -419,7 +429,7 @@ struct em28xx { u32 i2s_speed; /* I2S speed for audio digital stream */ enum em28xx_decoder decoder; - enum em28xx_audio_mode audio_mode; + struct em28xx_audio_mode audio_mode; int tuner_type; /* type of the tuner */ int tuner_addr; /* tuner address */ @@ -544,6 +554,7 @@ int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf, int len); int em28xx_write_regs(struct em28xx *dev, u16 reg, char *buf, int len); int em28xx_audio_analog_set(struct em28xx *dev); +int em28xx_audio_setup(struct em28xx *dev); int em28xx_colorlevels_set_default(struct em28xx *dev); int em28xx_capture_start(struct em28xx *dev, int start); -- cgit v1.2.3 From 636008228786f51a4b2aec945cd47f1e12c60ea4 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 20 Nov 2008 09:56:19 -0200 Subject: em28xx: merge AC97 vendor id's into a single var From: Mauro Carvalho Chehab This makes easier to identify vendor ID, since AC97 vendors are generally identified by 3 bytes. The remaining byte is used by the vendor to identify its devices. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-core.c | 11 +++++++---- linux/drivers/media/video/em28xx/em28xx.h | 3 +-- 2 files changed, 8 insertions(+), 6 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-core.c b/linux/drivers/media/video/em28xx/em28xx-core.c index 3a6252a8b..f2f3f7900 100644 --- a/linux/drivers/media/video/em28xx/em28xx-core.c +++ b/linux/drivers/media/video/em28xx/em28xx-core.c @@ -424,6 +424,7 @@ EXPORT_SYMBOL_GPL(em28xx_audio_analog_set); int em28xx_audio_setup(struct em28xx *dev) { int vid1, vid2, feat, cfg; + u32 vid; if (dev->chip_id == CHIP_ID_EM2874) { /* Digital only device - don't load any alsa module */ @@ -475,9 +476,10 @@ int em28xx_audio_setup(struct em28xx *dev) if (vid2 < 0) goto init_audio; - dev->audio_mode.ac97_vendor_id1 = vid1; - dev->audio_mode.ac97_vendor_id2 = vid2; - em28xx_warn("AC97 vendor ID = %04x:%04x\n", vid1, vid2); + vid = vid1 << 16 | vid2; + + dev->audio_mode.ac97_vendor_id = vid; + em28xx_warn("AC97 vendor ID = 0x%08x\n", vid); feat = em28xx_read_ac97(dev, AC97_RESET); if (feat < 0) @@ -486,7 +488,8 @@ int em28xx_audio_setup(struct em28xx *dev) dev->audio_mode.ac97_feat = feat; em28xx_warn("AC97 features = 0x%04x\n", feat); - if ((vid1 == 0xffff) && (vid2 == 0xffff) && (feat == 0x6a90)) + /* Try to identify what audio processor we have */ + if ((vid == 0xffffffff) && (feat == 0x6a90)) dev->audio_mode.ac97 = EM28XX_AC97_EM202; init_audio: diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index 06d5872f1..c3c36d691 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -267,8 +267,7 @@ struct em28xx_audio_mode { enum em28xx_ac97_mode ac97; u16 ac97_feat; - u16 ac97_vendor_id1; - u16 ac97_vendor_id2; + u32 ac97_vendor_id; unsigned int has_audio:1; -- cgit v1.2.3 From 28a78dfe37a3cb9c3986be2b2d1e3e6a5fece43d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 20 Nov 2008 10:06:09 -0200 Subject: em28xx: improve AC97 handling From: Mauro Carvalho Chehab AC97 devices provide several input and outputs. However, before this patch, em28xx device weren't properly allowing the usage of ac97 possible combinations. Also, several input volumes were left untouched, instead of making sure that the volumes were set on mute state. This patch improves support for ac97 devices by allowing to use any inputs, and making sure that unused inputs are set on mute state. Yet, some work is still needed to select the AC97 output. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-core.c | 73 +++++++++++++++----------- linux/drivers/media/video/em28xx/em28xx-reg.h | 14 ++++- linux/drivers/media/video/em28xx/em28xx.h | 20 ++++++- 3 files changed, 74 insertions(+), 33 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-core.c b/linux/drivers/media/video/em28xx/em28xx-core.c index f2f3f7900..5292ca29d 100644 --- a/linux/drivers/media/video/em28xx/em28xx-core.c +++ b/linux/drivers/media/video/em28xx/em28xx-core.c @@ -298,29 +298,44 @@ static int em28xx_write_ac97(struct em28xx *dev, u8 reg, u16 val) return 0; } -static int set_ac97_em202_input(struct em28xx *dev) +struct em28xx_input_table { + enum em28xx_amux amux; + u8 reg; +}; + +static struct em28xx_input_table inputs[] = { + { EM28XX_AMUX_VIDEO, AC97_VIDEO_VOL }, + { EM28XX_AMUX_LINE_IN, AC97_LINEIN_VOL }, + { EM28XX_AMUX_PHONE, AC97_PHONE_VOL }, + { EM28XX_AMUX_MIC, AC97_MIC_VOL }, + { EM28XX_AMUX_CD, AC97_CD_VOL }, + { EM28XX_AMUX_AUX, AC97_AUX_VOL }, + { EM28XX_AMUX_PCM_OUT, AC97_PCM_OUT_VOL }, +}; + +static int set_ac97_input(struct em28xx *dev) { - int ret; - u16 enable = 0x0808; /* 12 dB attenuation Left/Right */ - u16 disable = 0x8808; /* bit 15 - mute volumme */ - u16 video, line; - - if (dev->ctl_ainput == EM28XX_AMUX_VIDEO) { - video = enable; - line = disable; - } else { - video = disable; - line = enable; - } + int ret, i; + enum em28xx_amux amux = dev->ctl_ainput; - /* Sets em202 AC97 mixer registers */ - ret = em28xx_write_ac97(dev, AC97_VIDEO_VOL, video); - if (ret < 0) - return ret; + /* EM28XX_AMUX_VIDEO2 is a special case used to indicate that + em28xx should point to LINE IN, while AC97 should use VIDEO + */ + if (amux == EM28XX_AMUX_VIDEO2) + amux = dev->ctl_ainput; - ret = em28xx_write_ac97(dev, AC97_LINEIN_VOL, line); + /* Mute all entres but the one that were selected */ + for (i = 0; i < ARRAY_SIZE(inputs); i++) { + if (amux == inputs[i].amux) + ret = em28xx_write_ac97(dev, inputs[i].reg, 0x0808); + else + ret = em28xx_write_ac97(dev, inputs[i].reg, 0x8000); - return ret; + if (ret < 0) + em28xx_warn("couldn't setup AC97 register %d\n", + inputs[i].reg); + } + return 0; } static int em28xx_set_audio_source(struct em28xx *dev) @@ -329,10 +344,10 @@ static int em28xx_set_audio_source(struct em28xx *dev) u8 input; if (dev->is_em2800) { - if (dev->ctl_ainput) - input = EM2800_AUDIO_SRC_LINE; - else + if (dev->ctl_ainput == EM28XX_AMUX_VIDEO) input = EM2800_AUDIO_SRC_TUNER; + else + input = EM2800_AUDIO_SRC_LINE; ret = em28xx_write_regs(dev, EM2800_R08_AUDIOSRC, &input, 1); if (ret < 0) @@ -360,16 +375,11 @@ static int em28xx_set_audio_source(struct em28xx *dev) switch (dev->audio_mode.ac97) { case EM28XX_NO_AC97: break; - case EM28XX_AC97_OTHER: - /* We don't know how to handle this chip. - Let's hope it is close enough to em202 to work - */ - case EM28XX_AC97_EM202: - ret = set_ac97_em202_input(dev); - break; + default: + ret = set_ac97_input(dev); } - return 0; + return ret; } int em28xx_audio_analog_set(struct em28xx *dev) @@ -380,6 +390,9 @@ int em28xx_audio_analog_set(struct em28xx *dev) if (!dev->audio_mode.has_audio) return 0; + /* It is assumed that all devices use master volume for output. + It would be possible to use also line output. + */ if (dev->audio_mode.ac97 != EM28XX_NO_AC97) { /* Mute */ ret = em28xx_write_ac97(dev, AC97_MASTER_VOL, 0x8000); diff --git a/linux/drivers/media/video/em28xx/em28xx-reg.h b/linux/drivers/media/video/em28xx/em28xx-reg.h index 12c9132b0..9727f3828 100644 --- a/linux/drivers/media/video/em28xx/em28xx-reg.h +++ b/linux/drivers/media/video/em28xx/em28xx-reg.h @@ -130,10 +130,13 @@ enum em28xx_chip_id { /* Standard AC97 registers */ #define AC97_RESET 0x00 + + /* Output volumes */ #define AC97_MASTER_VOL 0x02 -#define AC97_LINE_LEVEL_VOL 0x04 +#define AC97_LINE_LEVEL_VOL 0x04 /* Some devices use for headphones */ #define AC97_MASTER_MONO_VOL 0x06 + /* Input volumes */ #define AC97_PC_BEEP_VOL 0x0a #define AC97_PHONE_VOL 0x0c #define AC97_MIC_VOL 0x0e @@ -142,8 +145,12 @@ enum em28xx_chip_id { #define AC97_VIDEO_VOL 0x14 #define AC97_AUX_VOL 0x16 #define AC97_PCM_OUT_VOL 0x18 + + /* capture registers */ #define AC97_RECORD_SELECT 0x1a #define AC97_RECORD_GAIN 0x1c + + /* control registers */ #define AC97_GENERAL_PURPOSE 0x20 #define AC97_3D_CTRL 0x22 #define AC97_AUD_INT_AND_PAG 0x24 @@ -158,10 +165,15 @@ enum em28xx_chip_id { #define AC97_PCM_OUT_SURR_SRATE 0x2e #define AC97_PCM_OUT_LFE_SRATE 0x30 #define AC97_PCM_IN_SRATE 0x32 + + /* For devices with more than 2 channels, extra output volumes */ #define AC97_LFE_MASTER_VOL 0x36 #define AC97_SURR_MASTER_VOL 0x38 + + /* Digital SPDIF output control */ #define AC97_SPDIF_OUT_CTRL 0x3a + /* Vendor ID identifier */ #define AC97_VENDOR_ID1 0x7c #define AC97_VENDOR_ID2 0x7e diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index c3c36d691..fae80c42d 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -275,9 +275,25 @@ struct em28xx_audio_mode { unsigned int i2s_5rates:1; }; +/* em28xx has two audio inputs: tuner and line in. + However, on most devices, an auxiliary AC97 codec device is used. + The AC97 device may have several different inputs and outputs, + depending on their model. So, it is possible to use AC97 mixer to + address more than two different entries. + */ enum em28xx_amux { - EM28XX_AMUX_VIDEO, - EM28XX_AMUX_LINE_IN, + /* This is the only entry for em28xx tuner input */ + EM28XX_AMUX_VIDEO, /* em28xx tuner, AC97 mixer Video */ + + EM28XX_AMUX_LINE_IN, /* AC97 mixer Line In */ + + /* Some less-common mixer setups */ + EM28XX_AMUX_VIDEO2, /* em28xx Line in, AC97 mixer Video */ + EM28XX_AMUX_PHONE, + EM28XX_AMUX_MIC, + EM28XX_AMUX_CD, + EM28XX_AMUX_AUX, + EM28XX_AMUX_PCM_OUT, }; struct em28xx_input { -- cgit v1.2.3 From 0f71f2e1c83c02abd71844758e0d739b56bf4a2c Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 20 Nov 2008 10:52:20 -0200 Subject: Introduce a new function that writes to a single register. This is From: Devin Heitmueller useful because the vast majority of register writes are a single register, and this format permits or'ing register value bits together. Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-core.c | 6 ++++++ linux/drivers/media/video/em28xx/em28xx.h | 2 ++ 2 files changed, 8 insertions(+) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-core.c b/linux/drivers/media/video/em28xx/em28xx-core.c index 5292ca29d..dacb6cc5a 100644 --- a/linux/drivers/media/video/em28xx/em28xx-core.c +++ b/linux/drivers/media/video/em28xx/em28xx-core.c @@ -196,6 +196,12 @@ int em28xx_write_regs(struct em28xx *dev, u16 reg, char *buf, int len) return rc; } +/* Write a single register */ +int em28xx_write_reg(struct em28xx *dev, u16 reg, u8 val) +{ + return em28xx_write_regs(dev, reg, &val, 1); +} + /* * em28xx_write_reg_bits() * sets only some bits (specified by bitmask) of a register, by first reading diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index fae80c42d..9f96128df 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -568,6 +568,8 @@ int em28xx_read_reg(struct em28xx *dev, u16 reg); int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf, int len); int em28xx_write_regs(struct em28xx *dev, u16 reg, char *buf, int len); +int em28xx_write_reg(struct em28xx *dev, u16 reg, u8 val); + int em28xx_audio_analog_set(struct em28xx *dev); int em28xx_audio_setup(struct em28xx *dev); -- cgit v1.2.3 From c6f9ed52d0eb5d04c1c65914976d81315a872839 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 20 Nov 2008 10:53:05 -0200 Subject: Convert the calls that write the i2c clock register over to the new From: Devin Heitmueller em28xx_write_reg() function that allows for or'ing bits Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-cards.c | 60 ++++++++++++++++++------- linux/drivers/media/video/em28xx/em28xx-reg.h | 13 ++++++ 2 files changed, 58 insertions(+), 15 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 01765c33f..d2967c434 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -1405,7 +1405,9 @@ void em28xx_pre_card_setup(struct em28xx *dev) case EM2883_BOARD_KWORLD_HYBRID_A316: case EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600: em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1); - em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1); + em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, + EM28XX_I2C_CLK_WAIT_ENABLE | + EM28XX_I2C_FREQ_100_KHZ); msleep(50); /* Sets GPO/GPIO sequences for this device */ @@ -1417,7 +1419,9 @@ void em28xx_pre_card_setup(struct em28xx *dev) case EM2882_BOARD_TERRATEC_HYBRID_XS: em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1); - em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1); + em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, + EM28XX_I2C_CLK_WAIT_ENABLE | + EM28XX_I2C_FREQ_100_KHZ); msleep(50); /* should be added ir_codes here */ @@ -1437,7 +1441,9 @@ void em28xx_pre_card_setup(struct em28xx *dev) case EM2870_BOARD_KWORLD_350U: case EM2881_BOARD_DNT_DA2_HYBRID: em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1); - em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1); + em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, + EM28XX_I2C_CLK_WAIT_ENABLE | + EM28XX_I2C_FREQ_100_KHZ); msleep(50); /* NOTE: EM2881_DNT_DA2_HYBRID spend 140 msleep for digital @@ -1454,7 +1460,9 @@ void em28xx_pre_card_setup(struct em28xx *dev) case EM2880_BOARD_MSI_DIGIVOX_AD: case EM2880_BOARD_MSI_DIGIVOX_AD_II: em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1); - em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1); + em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, + EM28XX_I2C_CLK_WAIT_ENABLE | + EM28XX_I2C_FREQ_100_KHZ); msleep(50); /* Sets GPO/GPIO sequences for this device */ @@ -1471,7 +1479,9 @@ void em28xx_pre_card_setup(struct em28xx *dev) case EM2861_BOARD_PLEXTOR_PX_TV100U: em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1); - em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1); + em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, + EM28XX_I2C_CLK_WAIT_ENABLE | + EM28XX_I2C_FREQ_100_KHZ); /* FIXME guess */ /* Turn on analog audio output */ em28xx_write_regs_req(dev, 0x00, 0x08, "\xfd", 1); @@ -1480,7 +1490,9 @@ void em28xx_pre_card_setup(struct em28xx *dev) case EM2861_BOARD_KWORLD_PVRTV_300U: case EM2880_BOARD_KWORLD_DVB_305U: em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1); - em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x4c", 1); + em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, + EM28XX_I2C_CLK_WAIT_ENABLE | + EM28XX_I2C_FREQ_100_KHZ); msleep(10); em28xx_write_regs(dev, 0x08, "\x6d", 1); msleep(10); @@ -1490,7 +1502,9 @@ void em28xx_pre_card_setup(struct em28xx *dev) case EM2870_BOARD_KWORLD_355U: em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1); - em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1); + em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, + EM28XX_I2C_CLK_WAIT_ENABLE | + EM28XX_I2C_FREQ_100_KHZ); msleep(50); /* Sets GPO/GPIO sequences for this device */ @@ -1499,7 +1513,9 @@ void em28xx_pre_card_setup(struct em28xx *dev) case EM2870_BOARD_COMPRO_VIDEOMATE: em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1); - em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1); + em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, + EM28XX_I2C_CLK_WAIT_ENABLE | + EM28XX_I2C_FREQ_100_KHZ); /* TODO: someone can do some cleanup here... not everything's needed */ em28xx_write_regs(dev, 0x04, "\x00", 1); @@ -1518,7 +1534,9 @@ void em28xx_pre_card_setup(struct em28xx *dev) case EM2870_BOARD_TERRATEC_XS_MT2060: em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1); - em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1); + em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, + EM28XX_I2C_CLK_WAIT_ENABLE | + EM28XX_I2C_FREQ_100_KHZ); /* this device needs some gpio writes to get the DVB-T demod work */ em28xx_write_regs(dev, 0x08, "\xfe", 1); @@ -1530,7 +1548,10 @@ void em28xx_pre_card_setup(struct em28xx *dev) break; case EM2870_BOARD_PINNACLE_PCTV_DVB: - em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1); + em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, + EM28XX_I2C_CLK_WAIT_ENABLE | + EM28XX_I2C_FREQ_100_KHZ); + /* this device needs some gpio writes to get the DVB-T demod work */ em28xx_write_regs(dev, 0x08, "\xfe", 1); @@ -1546,7 +1567,9 @@ void em28xx_pre_card_setup(struct em28xx *dev) case EM2820_BOARD_GADMEI_UTV310: em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1); - em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1); + em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, + EM28XX_I2C_CLK_WAIT_ENABLE | + EM28XX_I2C_FREQ_100_KHZ); /* Turn on analog audio output */ em28xx_write_regs_req(dev, 0x00, 0x08, "\xfd", 1); break; @@ -1554,20 +1577,27 @@ void em28xx_pre_card_setup(struct em28xx *dev) case EM2860_BOARD_GADMEI_UTV330: /* Turn on IR */ em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x07", 1); - em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1); + em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, + EM28XX_I2C_CLK_WAIT_ENABLE | + EM28XX_I2C_FREQ_100_KHZ); /* should be added ir_codes here */ break; case EM2820_BOARD_MSI_VOX_USB_2: em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1); - em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1); + em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, + EM28XX_I2C_CLK_WAIT_ENABLE | + EM28XX_I2C_FREQ_100_KHZ); /* enables audio for that device */ em28xx_write_regs_req(dev, 0x00, 0x08, "\xfd", 1); break; case EM2874_BOARD_PINNACLE_PCTV_80E: - /* Set 400 KHz clock */ - em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x45", 1); + /* Set 400 KHz clock and select secondary i2c bus */ + em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, + EM28XX_I2C_CLK_WAIT_ENABLE | + EM2874_I2C_SECONDARY_BUS_SELECT | + EM28XX_I2C_FREQ_400_KHZ); dev->digital_gpio = em2874_pinnacle_80e_digital; break; diff --git a/linux/drivers/media/video/em28xx/em28xx-reg.h b/linux/drivers/media/video/em28xx/em28xx-reg.h index 9727f3828..98e95054e 100644 --- a/linux/drivers/media/video/em28xx/em28xx-reg.h +++ b/linux/drivers/media/video/em28xx/em28xx-reg.h @@ -32,6 +32,19 @@ #define EM28XX_R08_GPIO 0x08 /* em2820 or upper */ #define EM28XX_R06_I2C_CLK 0x06 + +/* em28xx I2C Clock Register (0x06) */ +#define EM28XX_I2C_CLK_ACK_LAST_READ 0x80 +#define EM28XX_I2C_CLK_WAIT_ENABLE 0x40 +#define EM28XX_I2C_EEPROM_ON_BOARD 0x08 +#define EM28XX_I2C_EEPROM_KEY_VALID 0x04 +#define EM2874_I2C_SECONDARY_BUS_SELECT 0x04 /* em2874 has two i2c busses */ +#define EM28XX_I2C_FREQ_1_5_MHZ 0x03 /* bus frequency (bits [1-0]) */ +#define EM28XX_I2C_FREQ_25_KHZ 0x02 +#define EM28XX_I2C_FREQ_400_KHZ 0x01 +#define EM28XX_I2C_FREQ_100_KHZ 0x00 + + #define EM28XX_R0A_CHIPID 0x0a #define EM28XX_R0C_USBSUSP 0x0c /* */ -- cgit v1.2.3 From 9881db1cdfab70b538cdd0ad234dcafbf867b62d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 14 Nov 2008 01:03:00 +0000 Subject: Fixed typo in sizeof() causing NULL pointer OOPS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From: Oldrich Jedlicka Signed-off-by: Oldřich Jedlička Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/saa7134/saa7134-cards.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/video/saa7134/saa7134-cards.c b/linux/drivers/media/video/saa7134/saa7134-cards.c index 65d3909d4..d4c432cb3 100644 --- a/linux/drivers/media/video/saa7134/saa7134-cards.c +++ b/linux/drivers/media/video/saa7134/saa7134-cards.c @@ -6151,7 +6151,7 @@ static void saa7134_tuner_setup(struct saa7134_dev *dev) struct v4l2_priv_tun_config xc2028_cfg; struct xc2028_ctrl ctl; - memset(&xc2028_cfg, 0, sizeof(ctl)); + memset(&xc2028_cfg, 0, sizeof(xc2028_cfg)); memset(&ctl, 0, sizeof(ctl)); ctl.fname = XC2028_DEFAULT_FIRMWARE; -- cgit v1.2.3 From a8e1bceccfb5d1b6d69c2bb36a4f6b9b74af14ba Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Fri, 14 Nov 2008 11:53:32 +0100 Subject: gspca: New subdriver parameter 'bulk_nurbs'. From: Antonio Ospite Some webcams with image transfer by bulk messages accept one or many permanent bulk read. Priority: normal Signed-off-by: Antonio Ospite Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/gspca.c | 19 +++++++++++++++---- linux/drivers/media/video/gspca/gspca.h | 4 ++++ 2 files changed, 19 insertions(+), 4 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/gspca.c b/linux/drivers/media/video/gspca/gspca.c index 7a2006c3e..709b63704 100644 --- a/linux/drivers/media/video/gspca/gspca.c +++ b/linux/drivers/media/video/gspca/gspca.c @@ -213,6 +213,7 @@ static void bulk_irq(struct urb *urb { struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context; struct gspca_frame *frame; + int st; PDEBUG(D_PACK, "bulk irq"); if (!gspca_dev->streaming) @@ -236,6 +237,13 @@ static void bulk_irq(struct urb *urb urb->transfer_buffer, urb->actual_length); } + + /* resubmit the URB */ + if (gspca_dev->cam.bulk_nurbs != 0) { + st = usb_submit_urb(urb, GFP_ATOMIC); + if (st < 0) + PDEBUG(D_ERR|D_PACK, "usb_submit_urb() ret %d", st); + } } /* @@ -533,11 +541,14 @@ static int create_urbs(struct gspca_dev *gspca_dev, nurbs = DEF_NURBS; } else { /* bulk */ npkt = 0; - bsize = gspca_dev->cam. bulk_size; + bsize = gspca_dev->cam.bulk_size; if (bsize == 0) bsize = psize; PDEBUG(D_STREAM, "bulk bsize:%d", bsize); - nurbs = 1; + if (gspca_dev->cam.bulk_nurbs != 0) + nurbs = gspca_dev->cam.bulk_nurbs; + else + nurbs = 1; } gspca_dev->nurbs = nurbs; @@ -625,8 +636,8 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) gspca_dev->streaming = 1; atomic_set(&gspca_dev->nevent, 0); - /* bulk transfers are started by the subdriver */ - if (gspca_dev->alt == 0) + /* some bulk transfers are started by the subdriver */ + if (gspca_dev->alt == 0 && gspca_dev->cam.bulk_nurbs == 0) break; /* submit the URBs */ diff --git a/linux/drivers/media/video/gspca/gspca.h b/linux/drivers/media/video/gspca/gspca.h index 6f097f4a1..d76eb9f3e 100644 --- a/linux/drivers/media/video/gspca/gspca.h +++ b/linux/drivers/media/video/gspca/gspca.h @@ -58,6 +58,10 @@ struct cam { int bulk_size; /* buffer size when image transfer by bulk */ struct v4l2_pix_format *cam_mode; /* size nmodes */ char nmodes; + __u8 bulk_nurbs; /* number of URBs in bulk mode + * - cannot be > MAX_NURBS + * - when 0 and bulk_size != 0 means + * 1 URB and submit done by subdriver */ __u8 epaddr; }; -- cgit v1.2.3 From cf18adb2c0dfce8e934fbf78c0be1e67a07c242f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 14 Nov 2008 11:46:59 -0200 Subject: tvaudio: fix OOPS when controlling bass/treble From: Mauro Carvalho Chehab Not all tvaudio chips allow controlling bass/treble. So, the driver has a table with a flag to indicate if the chip does support it. Unfortunately, the handling of this logic were broken for a very long time (probably since the first module version). Due to that, an OOPS were generated for devices that don't support bass/treble. This were the resulting OOPS message before the patch, with debug messages enabled: tvaudio' 1-005b: VIDIOC_S_CTRL BUG: unable to handle kernel NULL pointer dereference at 00000000 IP: [<00000000>] *pde = 22fda067 *pte = 00000000 Oops: 0000 [#1] SMP Modules linked in: snd_hda_intel snd_seq_dummy snd_seq_oss snd_seq_midi_event snd_seq snd_seq_device snd_pcm_oss snd_mixer_oss snd_pcm snd_timer snd_hwdep snd soundcore tuner_simple tuner_types tea5767 tuner tvaudio bttv bridgebnep rfcomm l2cap bluetooth it87 hwmon_vid hwmon fuse sunrpc ipt_REJECT nf_conntrack_ipv4 iptable_filter ip_tables ip6t_REJECT xt_tcpudp nf_conntrack_ipv6 xt_state nf_conntrack ip6table_filter ip6_tables x_tables ipv6 dm_mirrordm_multipath dm_mod configfs videodev v4l1_compat ir_common 8139cp compat_ioctl32 v4l2_common 8139too videobuf_dma_sg videobuf_core mii btcx_risc tveeprom i915 button snd_page_alloc serio_raw drm pcspkr i2c_algo_bit i2c_i801 i2c_core iTCO_wdt iTCO_vendor_support sr_mod cdrom sg ata_generic pata_acpi ata_piix libata sd_mod scsi_mod ext3 jbdmbcache uhci_hcd ohci_hcd ehci_hcd [last unloaded: soundcore] Pid: 15413, comm: qv4l2 Not tainted (2.6.25.14-108.fc9.i686 #1) EIP: 0060:[<00000000>] EFLAGS: 00210246 CPU: 0 EIP is at 0x0 EAX: 00008000 EBX: ebd21600 ECX: e2fd9ec4 EDX: 00200046 ESI: f8c0f0c4 EDI: f8c0f0c4 EBP: e2fd9d50 ESP: e2fd9d2c DS: 007b ES: 007b FS: 00d8 GS: 0033 SS: 0068 Process qv4l2 (pid: 15413, ti=e2fd9000 task=ebe44000 task.ti=e2fd9000) Stack: f8c0c6ae e2ff2a00 00000d00 e2fd9ec4 ebc4e000 e2fd9d5c f8c0c448 00000000 f899c12a e2fd9d5c f899c154 e2fd9d68 e2fd9d80 c0560185 e2fd9d88 f8f3e1d8 f8f3e1dc ebc4e034 f8f3e18c e2fd9ec4 00000000 e2fd9d90 f899c286 c008561c Call Trace: [] ? chip_command+0x266/0x4b6 [tvaudio] [] ? chip_command+0x0/0x4b6 [tvaudio] [] ? i2c_cmd+0x0/0x2f [i2c_core] [] ? i2c_cmd+0x2a/0x2f [i2c_core] [] ? device_for_each_child+0x21/0x49 [] ? i2c_clients_command+0x1c/0x1e [i2c_core] [] ? bttv_call_i2c_clients+0x14/0x16 [bttv] [] ? bttv_s_ctrl+0x1bc/0x313 [bttv] [] ? bttv_s_ctrl+0x0/0x313 [bttv] [] ? __video_do_ioctl+0x1f84/0x3726 [videodev] [] ? sock_aio_write+0x100/0x10d [] ? kmap_atomic_prot+0x1dd/0x1df [] ? enqueue_hrtimer+0xc2/0xcd [] ? copy_from_user+0x39/0x121 [] ? __video_ioctl2+0x1aa/0x24a [videodev] [] ? do_notify_resume+0x768/0x795 [] ? getnstimeofday+0x34/0xd1 [] ? autoremove_wake_function+0x0/0x33 [] ? video_ioctl2+0xf/0x13 [videodev] [] ? vfs_ioctl+0x50/0x69 [] ? do_vfs_ioctl+0x239/0x24c [] ? sys_ioctl+0x40/0x5b [] ? syscall_call+0x7/0xb [] ? cpuid4_cache_sysfs_exit+0x3d/0x69 ======================= Code: Bad EIP value. EIP: [<00000000>] 0x0 SS:ESP 0068:e2fd9d2c Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/tvaudio.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/tvaudio.c b/linux/drivers/media/video/tvaudio.c index e6a52962e..435af3455 100644 --- a/linux/drivers/media/video/tvaudio.c +++ b/linux/drivers/media/video/tvaudio.c @@ -1651,13 +1651,13 @@ static int tvaudio_get_ctrl(struct CHIPSTATE *chip, return 0; } case V4L2_CID_AUDIO_BASS: - if (desc->flags & CHIP_HAS_BASSTREBLE) + if (!(desc->flags & CHIP_HAS_BASSTREBLE)) break; ctrl->value = chip->bass; return 0; case V4L2_CID_AUDIO_TREBLE: - if (desc->flags & CHIP_HAS_BASSTREBLE) - return -EINVAL; + if (!(desc->flags & CHIP_HAS_BASSTREBLE)) + break; ctrl->value = chip->treble; return 0; } @@ -1717,16 +1717,15 @@ static int tvaudio_set_ctrl(struct CHIPSTATE *chip, return 0; } case V4L2_CID_AUDIO_BASS: - if (desc->flags & CHIP_HAS_BASSTREBLE) + if (!(desc->flags & CHIP_HAS_BASSTREBLE)) break; chip->bass = ctrl->value; chip_write(chip,desc->bassreg,desc->bassfunc(chip->bass)); return 0; case V4L2_CID_AUDIO_TREBLE: - if (desc->flags & CHIP_HAS_BASSTREBLE) - return -EINVAL; - + if (!(desc->flags & CHIP_HAS_BASSTREBLE)) + break; chip->treble = ctrl->value; chip_write(chip,desc->treblereg,desc->treblefunc(chip->treble)); @@ -1768,12 +1767,12 @@ static int chip_command(struct i2c_client *client, break; case V4L2_CID_AUDIO_VOLUME: case V4L2_CID_AUDIO_BALANCE: - if (!(desc->flags & CHIP_HAS_VOLUME)) + if ((desc->flags & CHIP_HAS_VOLUME)) return -EINVAL; break; case V4L2_CID_AUDIO_BASS: case V4L2_CID_AUDIO_TREBLE: - if (desc->flags & CHIP_HAS_BASSTREBLE) + if ((desc->flags & CHIP_HAS_BASSTREBLE)) return -EINVAL; break; default: -- cgit v1.2.3 From 60c3a3c2818fb9a088b9f2ea033fbc671fb3abc8 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 14 Nov 2008 14:38:46 -0200 Subject: Fix a small mistake on the previous patch From: Mauro Carvalho Chehab Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/tvaudio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/tvaudio.c b/linux/drivers/media/video/tvaudio.c index 435af3455..0a86c80a2 100644 --- a/linux/drivers/media/video/tvaudio.c +++ b/linux/drivers/media/video/tvaudio.c @@ -1767,12 +1767,12 @@ static int chip_command(struct i2c_client *client, break; case V4L2_CID_AUDIO_VOLUME: case V4L2_CID_AUDIO_BALANCE: - if ((desc->flags & CHIP_HAS_VOLUME)) + if (!(desc->flags & CHIP_HAS_VOLUME)) return -EINVAL; break; case V4L2_CID_AUDIO_BASS: case V4L2_CID_AUDIO_TREBLE: - if ((desc->flags & CHIP_HAS_BASSTREBLE)) + if (!(desc->flags & CHIP_HAS_BASSTREBLE)) return -EINVAL; break; default: -- cgit v1.2.3 From 0a07568bd0fb79c5dbd57d88a42e771cb6224f05 Mon Sep 17 00:00:00 2001 From: Patrick Boettcher Date: Sat, 15 Nov 2008 11:13:07 +0100 Subject: Make dib0700 remote control support work with firmware v1.20 From: Devin Heitmueller The format for reading the IR controller changed in firmware 1.20. It now provides the events on bulk endpoint 1 instead of using a control request. Support the new format, providing backward compatibility for users who might be using older firmware. Thanks to Patrick Boettcher for providing the required information on how the version 1.20 firmware works. Priority: high Signed-off-by: Devin Heitmueller Signed-off-by: Patrick Boettcher --- linux/drivers/media/dvb/dvb-usb/dib0700.h | 5 +- linux/drivers/media/dvb/dvb-usb/dib0700_core.c | 16 +++ linux/drivers/media/dvb/dvb-usb/dib0700_devices.c | 139 +++++++++++++++++++++- 3 files changed, 158 insertions(+), 2 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/dvb/dvb-usb/dib0700.h b/linux/drivers/media/dvb/dvb-usb/dib0700.h index 739193943..8b544fe79 100644 --- a/linux/drivers/media/dvb/dvb-usb/dib0700.h +++ b/linux/drivers/media/dvb/dvb-usb/dib0700.h @@ -22,7 +22,7 @@ extern int dvb_usb_dib0700_debug; #define REQUEST_I2C_READ 0x2 #define REQUEST_I2C_WRITE 0x3 -#define REQUEST_POLL_RC 0x4 +#define REQUEST_POLL_RC 0x4 /* deprecated in firmware v1.20 */ #define REQUEST_JUMPRAM 0x8 #define REQUEST_SET_CLOCK 0xB #define REQUEST_SET_GPIO 0xC @@ -40,11 +40,14 @@ struct dib0700_state { u16 mt2060_if1[2]; u8 rc_toggle; u8 rc_counter; + u8 rc_func_version; u8 is_dib7000pc; u8 fw_use_new_i2c_api; u8 disable_streaming_master_mode; }; +extern int dib0700_get_version(struct dvb_usb_device *d, u32 *hwversion, + u32 *romversion, u32 *ramversion, u32 *fwtype); extern int dib0700_set_gpio(struct dvb_usb_device *, enum dib07x0_gpios gpio, u8 gpio_dir, u8 gpio_val); extern int dib0700_ctrl_clock(struct dvb_usb_device *d, u32 clk_MHz, u8 clock_out_gp3); extern int dib0700_ctrl_rd(struct dvb_usb_device *d, u8 *tx, u8 txlen, u8 *rx, u8 rxlen); diff --git a/linux/drivers/media/dvb/dvb-usb/dib0700_core.c b/linux/drivers/media/dvb/dvb-usb/dib0700_core.c index 3c7bdef28..2fbb7049a 100644 --- a/linux/drivers/media/dvb/dvb-usb/dib0700_core.c +++ b/linux/drivers/media/dvb/dvb-usb/dib0700_core.c @@ -19,6 +19,22 @@ MODULE_PARM_DESC(dvb_usb_dib0700_ir_proto, "set ir protocol (0=NEC, 1=RC5 (defau DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +int dib0700_get_version(struct dvb_usb_device *d, u32 *hwversion, + u32 *romversion, u32 *ramversion, u32 *fwtype) +{ + u8 b[16]; + int ret = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), + REQUEST_GET_VERSION, + USB_TYPE_VENDOR | USB_DIR_IN, 0, 0, + b, sizeof(b), USB_CTRL_GET_TIMEOUT); + *hwversion = (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3]; + *romversion = (b[4] << 24) | (b[5] << 16) | (b[6] << 8) | b[7]; + *ramversion = (b[8] << 24) | (b[9] << 16) | (b[10] << 8) | b[11]; + *fwtype = (b[12] << 24) | (b[13] << 16) | (b[14] << 8) | b[15]; + return ret; +} + /* expecting rx buffer: request data[0] data[1] ... data[2] */ static int dib0700_ctrl_wr(struct dvb_usb_device *d, u8 *tx, u8 txlen) { diff --git a/linux/drivers/media/dvb/dvb-usb/dib0700_devices.c b/linux/drivers/media/dvb/dvb-usb/dib0700_devices.c index 0cfccc24b..f28d3ae59 100644 --- a/linux/drivers/media/dvb/dvb-usb/dib0700_devices.c +++ b/linux/drivers/media/dvb/dvb-usb/dib0700_devices.c @@ -38,6 +38,7 @@ static struct mt2060_config bristol_mt2060_config[2] = { } }; + static struct dibx000_agc_config bristol_dib3000p_mt2060_agc_config = { .band_caps = BAND_VHF | BAND_UHF, .setup = (1 << 8) | (5 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (2 << 0), @@ -451,8 +452,13 @@ static u8 rc_request[] = { REQUEST_POLL_RC, 0 }; /* Number of keypresses to ignore before start repeating */ #define RC_REPEAT_DELAY 2 +#define RC_REPEAT_DELAY_V1_20 5 -static int dib0700_rc_query(struct dvb_usb_device *d, u32 *event, int *state) + + +/* Used by firmware versions < 1.20 (deprecated) */ +static int dib0700_rc_query_legacy(struct dvb_usb_device *d, u32 *event, + int *state) { u8 key[4]; int i; @@ -529,6 +535,137 @@ static int dib0700_rc_query(struct dvb_usb_device *d, u32 *event, int *state) return 0; } +/* This is the structure of the RC response packet starting in firmware 1.20 */ +struct dib0700_rc_response { + u8 report_id; + u8 data_state; + u8 system_msb; + u8 system_lsb; + u8 data; + u8 not_data; +}; + +/* This supports the new IR response format for firmware v1.20 */ +static int dib0700_rc_query_v1_20(struct dvb_usb_device *d, u32 *event, + int *state) +{ + struct dvb_usb_rc_key *keymap = d->props.rc_key_map; + struct dib0700_state *st = d->priv; + struct dib0700_rc_response poll_reply; + u8 buf[6]; + int i; + int status; + int actlen; + int found = 0; + + /* Set initial results in case we exit the function early */ + *event = 0; + *state = REMOTE_NO_KEY_PRESSED; + + /* Firmware v1.20 provides RC data via bulk endpoint 1 */ + status = usb_bulk_msg(d->udev, usb_rcvbulkpipe(d->udev, 1), buf, + sizeof(buf), &actlen, 50); + if (status < 0) { + /* No data available (meaning no key press) */ + return 0; + } + + if (actlen != sizeof(buf)) { + /* We didn't get back the 6 byte message we expected */ + err("Unexpected RC response size [%d]", actlen); + return -1; + } + + poll_reply.report_id = buf[0]; + poll_reply.data_state = buf[1]; + poll_reply.system_msb = buf[2]; + poll_reply.system_lsb = buf[3]; + poll_reply.data = buf[4]; + poll_reply.not_data = buf[5]; + + /* + info("rid=%02x ds=%02x sm=%02x sl=%02x d=%02x nd=%02x\n", + poll_reply.report_id, poll_reply.data_state, + poll_reply.system_msb, poll_reply.system_lsb, + poll_reply.data, poll_reply.not_data); + */ + + if ((poll_reply.data + poll_reply.not_data) != 0xff) { + /* Key failed integrity check */ + err("key failed integrity check: %02x %02x %02x %02x", + poll_reply.system_msb, poll_reply.system_lsb, + poll_reply.data, poll_reply.not_data); + return -1; + } + + /* Find the key in the map */ + for (i = 0; i < d->props.rc_key_map_size; i++) { + if (keymap[i].custom == poll_reply.system_lsb && + keymap[i].data == poll_reply.data) { + *event = keymap[i].event; + found = 1; + break; + } + } + + if (found == 0) { + err("Unknown remote controller key: %02x %02x %02x %02x", + poll_reply.system_msb, poll_reply.system_lsb, + poll_reply.data, poll_reply.not_data); + d->last_event = 0; + return 0; + } + + if (poll_reply.data_state == 1) { + /* New key hit */ + st->rc_counter = 0; + *event = keymap[i].event; + *state = REMOTE_KEY_PRESSED; + d->last_event = keymap[i].event; + } else if (poll_reply.data_state == 2) { + /* Key repeated */ + st->rc_counter++; + + /* prevents unwanted double hits */ + if (st->rc_counter > RC_REPEAT_DELAY_V1_20) { + *event = d->last_event; + *state = REMOTE_KEY_PRESSED; + st->rc_counter = RC_REPEAT_DELAY_V1_20; + } + } else { + err("Unknown data state [%d]", poll_reply.data_state); + } + + return 0; +} + +static int dib0700_rc_query(struct dvb_usb_device *d, u32 *event, int *state) +{ + struct dib0700_state *st = d->priv; + + /* Because some people may have improperly named firmware files, + let's figure out whether to use the new firmware call or the legacy + call based on the firmware version embedded in the file */ + if (st->rc_func_version == 0) { + u32 hwver, romver, ramver, fwtype; + int ret = dib0700_get_version(d, &hwver, &romver, &ramver, + &fwtype); + if (ret < 0) { + err("Could not determine version info"); + return -1; + } + if (ramver < 0x10200) + st->rc_func_version = 1; + else + st->rc_func_version = 2; + } + + if (st->rc_func_version == 2) + return dib0700_rc_query_v1_20(d, event, state); + else + return dib0700_rc_query_legacy(d, event, state); +} + static struct dvb_usb_rc_key dib0700_rc_keys[] = { /* Key codes for the tiny Pinnacle remote*/ { 0x07, 0x00, KEY_MUTE }, -- cgit v1.2.3 From c2343663850d8931f75a7dfe4317c62732c6fce1 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sat, 15 Nov 2008 23:38:19 -0500 Subject: cx18: Major rewrite of interrupt handling for incoming mailbox processing From: Andy Walls 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. Priority: normal Signed-off-by: Andy Walls --- linux/drivers/media/video/cx18/cx18-driver.c | 11 +- linux/drivers/media/video/cx18/cx18-driver.h | 19 +- linux/drivers/media/video/cx18/cx18-dvb.c | 25 -- linux/drivers/media/video/cx18/cx18-dvb.h | 1 - linux/drivers/media/video/cx18/cx18-firmware.c | 5 + linux/drivers/media/video/cx18/cx18-io.c | 35 --- linux/drivers/media/video/cx18/cx18-io.h | 7 +- linux/drivers/media/video/cx18/cx18-irq.c | 139 ++--------- linux/drivers/media/video/cx18/cx18-irq.h | 6 - linux/drivers/media/video/cx18/cx18-mailbox.c | 324 ++++++++++++++++++++++++- linux/drivers/media/video/cx18/cx18-mailbox.h | 20 +- linux/drivers/media/video/cx18/cx18-queue.c | 25 +- linux/drivers/media/video/cx18/cx18-queue.h | 2 +- linux/drivers/media/video/cx18/cx18-scb.h | 8 +- linux/drivers/media/video/cx18/cx18-streams.c | 18 ++ linux/drivers/media/video/cx18/cx18-streams.h | 1 + 16 files changed, 420 insertions(+), 226 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/cx18/cx18-driver.c b/linux/drivers/media/video/cx18/cx18-driver.c index 54f0fde42..725ed2847 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.c +++ b/linux/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,11 +453,16 @@ static int __devinit cx18_init_struct1(struct cx18 *cx) spin_lock_init(&cx->lock); + 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; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) - INIT_WORK(&cx->work, cx18_work_handler); + INIT_WORK(&cx->epu_work_order[i].work, cx18_epu_work_handler); #else - INIT_WORK(&cx->work, cx18_work_handler, cx); + INIT_WORK(&cx->epu_work_order[i].work, cx18_epu_work_handler, + &cx->epu_work_order[i].work); #endif + } /* start counting open_id at 1 */ cx->open_id = 1; diff --git a/linux/drivers/media/video/cx18/cx18-driver.h b/linux/drivers/media/video/cx18/cx18-driver.h index ce7680675..5ebf84b78 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.h +++ b/linux/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/linux/drivers/media/video/cx18/cx18-dvb.c b/linux/drivers/media/video/cx18/cx18-dvb.c index 4845f732e..b74253b13 100644 --- a/linux/drivers/media/video/cx18/cx18-dvb.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-dvb.h b/linux/drivers/media/video/cx18/cx18-dvb.h index bbdcefc87..bf8d8f6f5 100644 --- a/linux/drivers/media/video/cx18/cx18-dvb.h +++ b/linux/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/linux/drivers/media/video/cx18/cx18-firmware.c b/linux/drivers/media/video/cx18/cx18-firmware.c index d9c5f55ab..fdea4b752 100644 --- a/linux/drivers/media/video/cx18/cx18-firmware.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-io.c b/linux/drivers/media/video/cx18/cx18-io.c index 3c6485fce..c67694f63 100644 --- a/linux/drivers/media/video/cx18/cx18-io.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-io.h b/linux/drivers/media/video/cx18/cx18-io.h index 4486b73fa..fdc2bcc92 100644 --- a/linux/drivers/media/video/cx18/cx18-io.h +++ b/linux/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/linux/drivers/media/video/cx18/cx18-irq.c b/linux/drivers/media/video/cx18/cx18-irq.c index 3e23fc350..52fc41521 100644 --- a/linux/drivers/media/video/cx18/cx18-irq.c +++ b/linux/drivers/media/video/cx18/cx18-irq.c @@ -21,127 +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" - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) -void cx18_work_handler(struct work_struct *work) -{ - struct cx18 *cx = container_of(work, struct cx18, work); -#else -void cx18_work_handler(void *arg) -{ - struct cx18 *cx = arg; -#endif - 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) { @@ -151,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); +} + #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18) irqreturn_t cx18_irq_handler(int irq, void *dev_id, struct pt_regs *regs) #else @@ -180,6 +70,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)) { } @@ -188,11 +85,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/linux/drivers/media/video/cx18/cx18-irq.h b/linux/drivers/media/video/cx18/cx18-irq.h index 6472e42c6..466cb0c25 100644 --- a/linux/drivers/media/video/cx18/cx18-irq.h +++ b/linux/drivers/media/video/cx18/cx18-irq.h @@ -36,9 +36,3 @@ irqreturn_t cx18_irq_handler(int irq, void *dev_id, struct pt_regs *regs); #else irqreturn_t cx18_irq_handler(int irq, void *dev_id); #endif - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) -void cx18_work_handler(struct work_struct *work); -#else -void cx18_work_handler(void *arg); -#endif diff --git a/linux/drivers/media/video/cx18/cx18-mailbox.c b/linux/drivers/media/video/cx18/cx18-mailbox.c index 35f7188d4..c13d02646 100644 --- a/linux/drivers/media/video/cx18/cx18-mailbox.c +++ b/linux/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,155 @@ 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); +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) +void cx18_epu_work_handler(struct work_struct *work) +{ + struct cx18_epu_work_order *order = + container_of(work, struct cx18_epu_work_order, work); +#else +void cx18_epu_work_handler(void *arg) +{ + struct cx18_epu_work_order *order = arg; +#endif + 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 +255,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 +473,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/linux/drivers/media/video/cx18/cx18-mailbox.h b/linux/drivers/media/video/cx18/cx18-mailbox.h index 54758f32d..39da39d47 100644 --- a/linux/drivers/media/video/cx18/cx18-mailbox.h +++ b/linux/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,13 @@ 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); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) +void cx18_epu_work_handler(struct work_struct *work); +#else +void cx18_epu_work_handler(void *arg); +#endif #endif diff --git a/linux/drivers/media/video/cx18/cx18-queue.c b/linux/drivers/media/video/cx18/cx18-queue.c index 174682c25..c5a81aed6 100644 --- a/linux/drivers/media/video/cx18/cx18-queue.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-queue.h b/linux/drivers/media/video/cx18/cx18-queue.h index 7f93bb13c..92edb5e3a 100644 --- a/linux/drivers/media/video/cx18/cx18-queue.h +++ b/linux/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/linux/drivers/media/video/cx18/cx18-scb.h b/linux/drivers/media/video/cx18/cx18-scb.h index 594713bbe..29866f02f 100644 --- a/linux/drivers/media/video/cx18/cx18-scb.h +++ b/linux/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/linux/drivers/media/video/cx18/cx18-streams.c b/linux/drivers/media/video/cx18/cx18-streams.c index d29a0b61b..823e6d655 100644 --- a/linux/drivers/media/video/cx18/cx18-streams.c +++ b/linux/drivers/media/video/cx18/cx18-streams.c @@ -622,3 +622,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/linux/drivers/media/video/cx18/cx18-streams.h b/linux/drivers/media/video/cx18/cx18-streams.h index f327e947b..3fd578d0e 100644 --- a/linux/drivers/media/video/cx18/cx18-streams.h +++ b/linux/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 50766f4c30d4894525df9b6abbb0c6899a7718ee Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 16 Nov 2008 11:40:21 -0200 Subject: Add chip ID's for em2820 and em2840 From: Mauro Carvalho Chehab Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-cards.c | 6 ++++++ linux/drivers/media/video/em28xx/em28xx-reg.h | 2 ++ 2 files changed, 8 insertions(+) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 0d7862ec8..fcba8944c 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -1360,6 +1360,12 @@ void em28xx_pre_card_setup(struct em28xx *dev) if (rc > 0) { dev->chip_id = rc; switch (rc) { + case CHIP_ID_EM2820: + em28xx_info("chip ID is em2820\n"); + break; + case CHIP_ID_EM2840: + em28xx_info("chip ID is em2840\n"); + break; case CHIP_ID_EM2860: em28xx_info("chip ID is em2860\n"); break; diff --git a/linux/drivers/media/video/em28xx/em28xx-reg.h b/linux/drivers/media/video/em28xx/em28xx-reg.h index b8e978363..6e5757caa 100644 --- a/linux/drivers/media/video/em28xx/em28xx-reg.h +++ b/linux/drivers/media/video/em28xx/em28xx-reg.h @@ -111,6 +111,8 @@ /* FIXME: Need to be populated with the other chip ID's */ enum em28xx_chip_id { + CHIP_ID_EM2820 = 18, + CHIP_ID_EM2840 = 20, CHIP_ID_EM2860 = 34, CHIP_ID_EM2883 = 36, CHIP_ID_EM2874 = 65, -- cgit v1.2.3 From 24ecbd3b24f466029402175e8bfd52a9044a0f3d Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sun, 16 Nov 2008 15:15:01 -0500 Subject: cx18: Change to singlethreaded global work queue thread for deferable work From: Andy Walls 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. Priority: normal Signed-off-by: Andy Walls --- linux/drivers/media/video/cx18/cx18-driver.c | 25 ++++++++++++++++++++++++- linux/drivers/media/video/cx18/cx18-driver.h | 1 + linux/drivers/media/video/cx18/cx18-mailbox.c | 2 +- 3 files changed, 26 insertions(+), 2 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/cx18/cx18-driver.c b/linux/drivers/media/video/cx18/cx18-driver.c index 725ed2847..61f0ea9d3 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.c +++ b/linux/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, @@ -925,6 +928,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); @@ -942,7 +952,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); @@ -986,8 +996,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"); @@ -1000,11 +1019,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/linux/drivers/media/video/cx18/cx18-driver.h b/linux/drivers/media/video/cx18/cx18-driver.h index 5ebf84b78..f8929eb72 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.h +++ b/linux/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/linux/drivers/media/video/cx18/cx18-mailbox.c b/linux/drivers/media/video/cx18/cx18-mailbox.c index c13d02646..703aaab69 100644 --- a/linux/drivers/media/video/cx18/cx18-mailbox.c +++ b/linux/drivers/media/video/cx18/cx18-mailbox.c @@ -415,7 +415,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 46dede1a2dfcab4d4e89df531be1b43e1a0d3eb6 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Fri, 21 Nov 2008 15:14:37 -0500 Subject: sms1xxx: use new firmware for Hauppauge WinTV MiniStick From: Michael Krufky New firmware image brings enhanced tuning performance. Firmware is available for download at the following location: http://www.steventoth.net/linux/sms1xxx Priority: high Signed-off-by: Michael Krufky --- linux/drivers/media/dvb/siano/sms-cards.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/dvb/siano/sms-cards.c b/linux/drivers/media/dvb/siano/sms-cards.c index 6f9b77360..e98d6caf2 100644 --- a/linux/drivers/media/dvb/siano/sms-cards.c +++ b/linux/drivers/media/dvb/siano/sms-cards.c @@ -95,7 +95,7 @@ static struct sms_board sms_boards[] = { [SMS1XXX_BOARD_HAUPPAUGE_WINDHAM] = { .name = "Hauppauge WinTV MiniStick", .type = SMS_NOVA_B0, - .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-hcw-55xxx-dvbt-01.fw", + .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-hcw-55xxx-dvbt-02.fw", }, }; -- cgit v1.2.3 From 2f2626b0267721b6b523fd0e5a1b8e895f359646 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sat, 22 Nov 2008 09:21:02 -0500 Subject: sms1xxx: add autodetection support for Hauppauge WinTV MiniCard From: Michael Krufky add USB IDs for Hauppauge WinTV MiniCard DVB-T Priority: high Signed-off-by: Michael Krufky --- linux/drivers/media/dvb/siano/sms-cards.c | 15 +++++++++++++++ linux/drivers/media/dvb/siano/sms-cards.h | 1 + 2 files changed, 16 insertions(+) (limited to 'linux') diff --git a/linux/drivers/media/dvb/siano/sms-cards.c b/linux/drivers/media/dvb/siano/sms-cards.c index e98d6caf2..4344efb3e 100644 --- a/linux/drivers/media/dvb/siano/sms-cards.c +++ b/linux/drivers/media/dvb/siano/sms-cards.c @@ -38,6 +38,16 @@ struct usb_device_id smsusb_id_table[] = { .driver_info = SMS1XXX_BOARD_HAUPPAUGE_OKEMO_A }, { USB_DEVICE(0x2040, 0x1801), .driver_info = SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B }, + { USB_DEVICE(0x2040, 0x2000), + .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD }, + { USB_DEVICE(0x2040, 0x2009), + .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD }, + { USB_DEVICE(0x2040, 0x200a), + .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD }, + { USB_DEVICE(0x2040, 0x2010), + .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD }, + { USB_DEVICE(0x2040, 0x2019), + .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD }, { USB_DEVICE(0x2040, 0x5500), .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, { USB_DEVICE(0x2040, 0x5510), @@ -97,6 +107,11 @@ static struct sms_board sms_boards[] = { .type = SMS_NOVA_B0, .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-hcw-55xxx-dvbt-02.fw", }, + [SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD] = { + .name = "Hauppauge WinTV MiniCard", + .type = SMS_NOVA_B0, + .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-hcw-55xxx-dvbt-02.fw", + }, }; struct sms_board *sms_get_board(int id) diff --git a/linux/drivers/media/dvb/siano/sms-cards.h b/linux/drivers/media/dvb/siano/sms-cards.h index c8f3da6f9..240d8b359 100644 --- a/linux/drivers/media/dvb/siano/sms-cards.h +++ b/linux/drivers/media/dvb/siano/sms-cards.h @@ -32,6 +32,7 @@ #define SMS1XXX_BOARD_HAUPPAUGE_OKEMO_A 6 #define SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B 7 #define SMS1XXX_BOARD_HAUPPAUGE_WINDHAM 8 +#define SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD 9 struct sms_board { enum sms_device_type_st type; -- cgit v1.2.3 From 733be4b5b9ad94b8d0683954a464aafb7276d3fe Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sun, 16 Nov 2008 16:01:58 -0500 Subject: sms1xxx: add functions to configure and set gpio From: Michael Krufky Priority: normal Signed-off-by: Michael Krufky --- linux/drivers/media/dvb/siano/smscoreapi.c | 70 ++++++++++++++++++++++++++++++ linux/drivers/media/dvb/siano/smscoreapi.h | 44 ++++++++++++++----- 2 files changed, 102 insertions(+), 12 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/dvb/siano/smscoreapi.c b/linux/drivers/media/dvb/siano/smscoreapi.c index 700a0a6a7..2ce099883 100644 --- a/linux/drivers/media/dvb/siano/smscoreapi.c +++ b/linux/drivers/media/dvb/siano/smscoreapi.c @@ -1248,6 +1248,76 @@ static int smscore_map_common_buffer(struct smscore_device_t *coredev, } #endif +int smscore_configure_gpio(struct smscore_device_t *coredev, u32 pin, + struct smscore_gpio_config *pinconfig) +{ + struct { + struct SmsMsgHdr_ST hdr; + u32 data[6]; + } msg; + + if (coredev->device_flags & SMS_DEVICE_FAMILY2) { + msg.hdr.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; + msg.hdr.msgDstId = HIF_TASK; + msg.hdr.msgFlags = 0; + msg.hdr.msgType = MSG_SMS_GPIO_CONFIG_EX_REQ; + msg.hdr.msgLength = sizeof(msg); + + msg.data[0] = pin; + msg.data[1] = pinconfig->pullupdown; + + /* Convert slew rate for Nova: Fast(0) = 3 / Slow(1) = 0; */ + msg.data[2] = pinconfig->outputslewrate == 0 ? 3 : 0; + + switch (pinconfig->outputdriving) { + case SMS_GPIO_OUTPUTDRIVING_16mA: + msg.data[3] = 7; /* Nova - 16mA */ + break; + case SMS_GPIO_OUTPUTDRIVING_12mA: + msg.data[3] = 5; /* Nova - 11mA */ + break; + case SMS_GPIO_OUTPUTDRIVING_8mA: + msg.data[3] = 3; /* Nova - 7mA */ + break; + case SMS_GPIO_OUTPUTDRIVING_4mA: + default: + msg.data[3] = 2; /* Nova - 4mA */ + break; + } + + msg.data[4] = pinconfig->direction; + msg.data[5] = 0; + } else /* TODO: SMS_DEVICE_FAMILY1 */ + return -EINVAL; + + return coredev->sendrequest_handler(coredev->context, + &msg, sizeof(msg)); +} + +int smscore_set_gpio(struct smscore_device_t *coredev, u32 pin, int level) +{ + struct { + struct SmsMsgHdr_ST hdr; + u32 data[3]; + } msg; + + if (pin > MAX_GPIO_PIN_NUMBER) + return -EINVAL; + + msg.hdr.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; + msg.hdr.msgDstId = HIF_TASK; + msg.hdr.msgFlags = 0; + msg.hdr.msgType = MSG_SMS_GPIO_SET_LEVEL_REQ; + msg.hdr.msgLength = sizeof(msg); + + msg.data[0] = pin; + msg.data[1] = level ? 1 : 0; + msg.data[2] = 0; + + return coredev->sendrequest_handler(coredev->context, + &msg, sizeof(msg)); +} + static int __init smscore_module_init(void) { int rc = 0; diff --git a/linux/drivers/media/dvb/siano/smscoreapi.h b/linux/drivers/media/dvb/siano/smscoreapi.h index 3bba565d7..3a98e031b 100644 --- a/linux/drivers/media/dvb/siano/smscoreapi.h +++ b/linux/drivers/media/dvb/siano/smscoreapi.h @@ -187,6 +187,8 @@ struct smsclient_params_t { #define MSG_SW_RELOAD_EXEC_REQ 704 #define MSG_SW_RELOAD_EXEC_RES 705 #define MSG_SMS_SPI_INT_LINE_SET_REQ 710 +#define MSG_SMS_GPIO_CONFIG_EX_REQ 712 +#define MSG_SMS_GPIO_CONFIG_EX_RES 713 #define MSG_SMS_ISDBT_TUNE_REQ 776 #define MSG_SMS_ISDBT_TUNE_RES 777 @@ -481,18 +483,6 @@ struct SMSHOSTLIB_STATISTICS_DVB_ST { }; -struct SMSHOSTLIB_GPIO_CONFIG_ST { - u8 Direction; /* GPIO direction: Input - 0, Output - 1 */ - u8 PullUpDown; /* PullUp/PullDown: None - 0, - * PullDown - 1, PullUp - 2, Keeper - 3 */ - u8 InputCharacteristics; /* Input Characteristics: Normal - 0, - * Schmitt trigger - 1 */ - u8 OutputSlewRate; /* Output Slew Rate: - * Fast slew rate - 0, Slow slew rate - 1 */ - u8 OutputDriving; /* Output driving capability: - * 4mA - 0, 8mA - 1, 12mA - 2, 16mA - 3 */ -}; - struct SMSHOSTLIB_I2C_REQ_ST { u32 DeviceAddress; /* I2c device address */ u32 WriteCount; /* number of bytes to write */ @@ -507,6 +497,32 @@ struct SMSHOSTLIB_I2C_RES_ST { }; #endif +struct smscore_gpio_config { +#define SMS_GPIO_DIRECTION_INPUT 0 +#define SMS_GPIO_DIRECTION_OUTPUT 1 + u8 direction; + +#define SMS_GPIO_PULLUPDOWN_NONE 0 +#define SMS_GPIO_PULLUPDOWN_PULLDOWN 1 +#define SMS_GPIO_PULLUPDOWN_PULLUP 2 +#define SMS_GPIO_PULLUPDOWN_KEEPER 3 + u8 pullupdown; + +#define SMS_GPIO_INPUTCHARACTERISTICS_NORMAL 0 +#define SMS_GPIO_INPUTCHARACTERISTICS_SCHMITT 1 + u8 inputcharacteristics; + +#define SMS_GPIO_OUTPUTSLEWRATE_FAST 0 +#define SMS_GPIO_OUTPUTSLEWRATE_SLOW 1 + u8 outputslewrate; + +#define SMS_GPIO_OUTPUTDRIVING_4mA 0 +#define SMS_GPIO_OUTPUTDRIVING_8mA 1 +#define SMS_GPIO_OUTPUTDRIVING_12mA 2 +#define SMS_GPIO_OUTPUTDRIVING_16mA 3 + u8 outputdriving; +}; + struct smsdvb_client_t { struct list_head entry; @@ -567,6 +583,10 @@ struct smscore_buffer_t *smscore_getbuffer(struct smscore_device_t *coredev); extern void smscore_putbuffer(struct smscore_device_t *coredev, struct smscore_buffer_t *cb); +int smscore_configure_gpio(struct smscore_device_t *coredev, u32 pin, + struct smscore_gpio_config *pinconfig); +int smscore_set_gpio(struct smscore_device_t *coredev, u32 pin, int level); + void smscore_set_board_id(struct smscore_device_t *core, int id); int smscore_get_board_id(struct smscore_device_t *core); -- cgit v1.2.3 From 89619141828f0acac90a00d469d4458672624cd8 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sun, 16 Nov 2008 18:18:05 -0500 Subject: cx18: Convert per stream queue spinlocks into mutexes From: Andy Walls 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. Priority: normal Signed-off-by: Andy Walls --- linux/drivers/media/video/cx18/cx18-driver.h | 2 +- linux/drivers/media/video/cx18/cx18-queue.c | 23 +++++++++-------------- linux/drivers/media/video/cx18/cx18-streams.c | 2 +- 3 files changed, 11 insertions(+), 16 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/cx18/cx18-driver.h b/linux/drivers/media/video/cx18/cx18-driver.h index f8929eb72..041fa660a 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.h +++ b/linux/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/linux/drivers/media/video/cx18/cx18-queue.c b/linux/drivers/media/video/cx18/cx18-queue.c index c5a81aed6..5a3839403 100644 --- a/linux/drivers/media/video/cx18/cx18-queue.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-streams.c b/linux/drivers/media/video/cx18/cx18-streams.c index 823e6d655..21434df1a 100644 --- a/linux/drivers/media/video/cx18/cx18-streams.c +++ b/linux/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 c01ba77a68ca36f759d9ff6f85edadd4de41cca1 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sun, 16 Nov 2008 19:18:00 -0500 Subject: cx18: Propagate staleness of mailbox and mdl ack data to work handler From: Andy Walls 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. Priority: normal Signed-off-by: Andy Walls --- linux/drivers/media/video/cx18/cx18-driver.h | 7 ++++++ linux/drivers/media/video/cx18/cx18-mailbox.c | 34 ++++++++++++++++----------- 2 files changed, 27 insertions(+), 14 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/cx18/cx18-driver.h b/linux/drivers/media/video/cx18/cx18-driver.h index 041fa660a..749bbb60a 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.h +++ b/linux/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/linux/drivers/media/video/cx18/cx18-mailbox.c b/linux/drivers/media/video/cx18/cx18-mailbox.c index 703aaab69..c7fcd6507 100644 --- a/linux/drivers/media/video/cx18/cx18-mailbox.c +++ b/linux/drivers/media/video/cx18/cx18-mailbox.c @@ -239,7 +239,7 @@ void cx18_epu_work_handler(void *arg) * 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; @@ -262,15 +262,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; @@ -282,20 +287,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; @@ -309,14 +315,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; @@ -325,10 +331,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", @@ -376,7 +382,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) { @@ -397,6 +402,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)); @@ -406,14 +412,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 1c63a34167b585f61c9fe7c9f998ee98bb5da5d9 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sun, 16 Nov 2008 20:45:42 -0500 Subject: sms1xxx: turn off LEDs after initialization of Hauppauge WinTV MiniStick From: Michael Krufky Priority: normal Signed-off-by: Michael Krufky --- linux/drivers/media/dvb/siano/sms-cards.c | 40 +++++++++++++++++++++++++++++++ linux/drivers/media/dvb/siano/sms-cards.h | 5 ++++ linux/drivers/media/dvb/siano/smsdvb.c | 2 ++ 3 files changed, 47 insertions(+) (limited to 'linux') diff --git a/linux/drivers/media/dvb/siano/sms-cards.c b/linux/drivers/media/dvb/siano/sms-cards.c index 4344efb3e..eb4bb03b2 100644 --- a/linux/drivers/media/dvb/siano/sms-cards.c +++ b/linux/drivers/media/dvb/siano/sms-cards.c @@ -106,6 +106,9 @@ static struct sms_board sms_boards[] = { .name = "Hauppauge WinTV MiniStick", .type = SMS_NOVA_B0, .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-hcw-55xxx-dvbt-02.fw", + .led_power = 26, + .led_lo = 27, + .led_hi = 28, }, [SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD] = { .name = "Hauppauge WinTV MiniCard", @@ -121,3 +124,40 @@ struct sms_board *sms_get_board(int id) return &sms_boards[id]; } +static int sms_set_gpio(struct smscore_device_t *coredev, u32 pin, int enable) +{ + int ret; + struct smscore_gpio_config gpioconfig = { + .direction = SMS_GPIO_DIRECTION_OUTPUT, + .pullupdown = SMS_GPIO_PULLUPDOWN_NONE, + .inputcharacteristics = SMS_GPIO_INPUTCHARACTERISTICS_NORMAL, + .outputslewrate = SMS_GPIO_OUTPUTSLEWRATE_FAST, + .outputdriving = SMS_GPIO_OUTPUTDRIVING_4mA, + }; + + if (pin == 0) + return -EINVAL; + + ret = smscore_configure_gpio(coredev, pin, &gpioconfig); + + if (ret < 0) + return ret; + + return smscore_set_gpio(coredev, pin, enable); +} + +int sms_board_setup(struct smscore_device_t *coredev) +{ + int board_id = smscore_get_board_id(coredev); + struct sms_board *board = sms_get_board(board_id); + + switch (board_id) { + case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM: + /* turn off all LEDs */ + sms_set_gpio(coredev, board->led_power, 0); + sms_set_gpio(coredev, board->led_hi, 0); + sms_set_gpio(coredev, board->led_lo, 0); + break; + } + return 0; +} diff --git a/linux/drivers/media/dvb/siano/sms-cards.h b/linux/drivers/media/dvb/siano/sms-cards.h index 240d8b359..25820909b 100644 --- a/linux/drivers/media/dvb/siano/sms-cards.h +++ b/linux/drivers/media/dvb/siano/sms-cards.h @@ -37,10 +37,15 @@ struct sms_board { enum sms_device_type_st type; char *name, *fw[DEVICE_MODE_MAX]; + + /* gpios */ + int led_power, led_hi, led_lo; }; struct sms_board *sms_get_board(int id); +int sms_board_setup(struct smscore_device_t *coredev); + extern struct usb_device_id smsusb_id_table[]; #endif /* __SMS_CARDS_H__ */ diff --git a/linux/drivers/media/dvb/siano/smsdvb.c b/linux/drivers/media/dvb/siano/smsdvb.c index f2a6c6f9e..28bce9f85 100644 --- a/linux/drivers/media/dvb/siano/smsdvb.c +++ b/linux/drivers/media/dvb/siano/smsdvb.c @@ -411,6 +411,8 @@ static int smsdvb_hotplug(struct smscore_device_t *coredev, sms_info("success"); + sms_board_setup(coredev); + return 0; client_error: -- cgit v1.2.3 From 41442c233ecb6016a8d6c73ff23002138488e42b Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sun, 16 Nov 2008 21:33:41 -0500 Subject: cx18: Streamline cx18-io[ch] wrappers and enforce MMIO retry strategy From: Andy Walls 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. Priority: normal Signed-off-by: Andy Walls --- linux/drivers/media/video/cx18/cx18-av-core.c | 5 - linux/drivers/media/video/cx18/cx18-av-core.h | 1 - linux/drivers/media/video/cx18/cx18-av-firmware.c | 5 +- linux/drivers/media/video/cx18/cx18-driver.c | 28 +- linux/drivers/media/video/cx18/cx18-driver.h | 13 - linux/drivers/media/video/cx18/cx18-gpio.c | 2 - linux/drivers/media/video/cx18/cx18-i2c.c | 18 +- linux/drivers/media/video/cx18/cx18-io.c | 128 --------- linux/drivers/media/video/cx18/cx18-io.h | 320 ++++------------------ 9 files changed, 71 insertions(+), 449 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/cx18/cx18-av-core.c b/linux/drivers/media/video/cx18/cx18-av-core.c index 518bd701d..13a4adaa1 100644 --- a/linux/drivers/media/video/cx18/cx18-av-core.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-av-core.h b/linux/drivers/media/video/cx18/cx18-av-core.h index a07988c6f..455761fce 100644 --- a/linux/drivers/media/video/cx18/cx18-av-core.h +++ b/linux/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/linux/drivers/media/video/cx18/cx18-av-firmware.c b/linux/drivers/media/video/cx18/cx18-av-firmware.c index 924691dca..cf52e08c2 100644 --- a/linux/drivers/media/video/cx18/cx18-av-firmware.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-driver.c b/linux/drivers/media/video/cx18/cx18-driver.c index 61f0ea9d3..15c1fb9b2 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-driver.h b/linux/drivers/media/video/cx18/cx18-driver.h index 749bbb60a..02a82c3b7 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.h +++ b/linux/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/linux/drivers/media/video/cx18/cx18-gpio.c b/linux/drivers/media/video/cx18/cx18-gpio.c index 17b7a32fc..a1625c8e0 100644 --- a/linux/drivers/media/video/cx18/cx18-gpio.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-i2c.c b/linux/drivers/media/video/cx18/cx18-i2c.c index 1af9ea149..222d5b83a 100644 --- a/linux/drivers/media/video/cx18/cx18-i2c.c +++ b/linux/drivers/media/video/cx18/cx18-i2c.c @@ -184,9 +184,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) @@ -197,9 +197,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) @@ -433,16 +433,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. */ @@ -452,12 +446,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/linux/drivers/media/video/cx18/cx18-io.c b/linux/drivers/media/video/cx18/cx18-io.c index c67694f63..a2b5e807f 100644 --- a/linux/drivers/media/video/cx18/cx18-io.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-io.h b/linux/drivers/media/video/cx18/cx18-io.h index fdc2bcc92..73321fb4c 100644 --- a/linux/drivers/media/video/cx18/cx18-io.h +++ b/linux/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 c965174067222d803288076b540514a554cb14e8 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Mon, 17 Nov 2008 18:52:56 +0100 Subject: gspca: Correct restart of webcams in spca501. From: Hans de Goede Currently the spca501 driver only works the first time used, then the cam needs to be unplugged or the driver reloaded, this is because currently the data which should be send each time the stream is started is send only once. Likewise currently the data to initialize the cam is send from the probe function, and thus will not get send after a suspend/resume. This patch fixes this by sending both at the right time. Priority: normal Signed-off-by: Hans de Goede Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/spca501.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/spca501.c b/linux/drivers/media/video/gspca/spca501.c index 979344907..3af5f9d60 100644 --- a/linux/drivers/media/video/gspca/spca501.c +++ b/linux/drivers/media/video/gspca/spca501.c @@ -1941,6 +1941,14 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->contrast = sd_ctrls[MY_CONTRAST].qctrl.default_value; sd->colors = sd_ctrls[MY_COLOR].qctrl.default_value; + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + switch (sd->subtype) { case Arowana300KCMOSCamera: case SmileIntlCamera: @@ -1959,15 +1967,17 @@ static int sd_config(struct gspca_dev *gspca_dev, goto error; break; } + PDEBUG(D_STREAM, "Initializing SPCA501 finished"); return 0; error: return -EINVAL; } -/* this function is called at probe and resume time */ -static int sd_init(struct gspca_dev *gspca_dev) +static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + int mode; switch (sd->subtype) { case ThreeComHomeConnectLite: @@ -1987,14 +1997,6 @@ static int sd_init(struct gspca_dev *gspca_dev) /* Generic 501 open data */ write_vector(gspca_dev, spca501_open_data); } - PDEBUG(D_STREAM, "Initializing SPCA501 finished"); - return 0; -} - -static int sd_start(struct gspca_dev *gspca_dev) -{ - struct usb_device *dev = gspca_dev->dev; - int mode; /* memorize the wanted pixel format */ mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; -- cgit v1.2.3 From ff8138511038450f4fd212669615835c98822d17 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Mon, 17 Nov 2008 18:56:41 +0100 Subject: gspca: Don't return the control values from the webcams in spca501. From: Hans de Goede The bridge takes some time to update after a write, so apps doing a quick write/read can get the old value back. Priority: normal Signed-off-by: Hans de Goede Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/spca501.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/spca501.c b/linux/drivers/media/video/gspca/spca501.c index 3af5f9d60..29277bdda 100644 --- a/linux/drivers/media/video/gspca/spca501.c +++ b/linux/drivers/media/video/gspca/spca501.c @@ -1822,6 +1822,7 @@ static int reg_write(struct usb_device *dev, return ret; } +#if 0 /* returns: negative is error, pos or zero is data */ static int reg_read(struct gspca_dev *gspca_dev, __u16 req, /* bRequest */ @@ -1845,6 +1846,7 @@ static int reg_read(struct gspca_dev *gspca_dev, } return (gspca_dev->usb_buf[1] << 8) + gspca_dev->usb_buf[0]; } +#endif static int write_vector(struct gspca_dev *gspca_dev, const __u16 data[][3]) @@ -1876,11 +1878,13 @@ static void setbrightness(struct gspca_dev *gspca_dev) static void getbrightness(struct gspca_dev *gspca_dev) { +#if 0 struct sd *sd = (struct sd *) gspca_dev; __u16 brightness; brightness = reg_read(gspca_dev, SPCA501_REG_CCDSP, 0x11, 2); - sd->brightness = brightness << 1; + sd->brightness = brightness; +#endif } static void setcontrast(struct gspca_dev *gspca_dev) @@ -1906,7 +1910,6 @@ static void getcontrast(struct gspca_dev *gspca_dev) 0x01, 1) & 0xff); #endif -/* spca50x->contrast = 0xaa01; */ } static void setcolors(struct gspca_dev *gspca_dev) @@ -1918,11 +1921,13 @@ static void setcolors(struct gspca_dev *gspca_dev) static void getcolors(struct gspca_dev *gspca_dev) { +#if 0 struct sd *sd = (struct sd *) gspca_dev; sd->colors = reg_read(gspca_dev, SPCA501_REG_CCDSP, 0x0c, 2); /* sd->hue = (reg_read(gspca_dev, SPCA501_REG_CCDSP, 0x13, */ /* 2) & 0xFF) << 8; */ +#endif } /* this function is called at probe time */ -- cgit v1.2.3 From 950febeb0e11d8b6ff554e5c671a6c0d906731e8 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Mon, 17 Nov 2008 19:03:03 +0100 Subject: gspca: Split brightness and red and blue balance in spca501. From: Hans de Goede Priority: normal Signed-off-by: Hans de Goede Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/spca501.c | 98 ++++++++++++++++++++++++++++--- 1 file changed, 89 insertions(+), 9 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/spca501.c b/linux/drivers/media/video/gspca/spca501.c index 29277bdda..12e7124bf 100644 --- a/linux/drivers/media/video/gspca/spca501.c +++ b/linux/drivers/media/video/gspca/spca501.c @@ -34,6 +34,8 @@ struct sd { unsigned short contrast; __u8 brightness; __u8 colors; + __u8 blue_balance; + __u8 red_balance; char subtype; #define Arowana300KCMOSCamera 0 @@ -52,6 +54,10 @@ static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val); static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setblue_balance(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getblue_balance(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setred_balance(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getred_balance(struct gspca_dev *gspca_dev, __s32 *val); static struct ctrl sd_ctrls[] = { #define MY_BRIGHTNESS 0 @@ -63,7 +69,7 @@ static struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 127, .step = 1, - .default_value = 63, + .default_value = 0, }, .set = sd_setbrightness, .get = sd_getbrightness, @@ -75,9 +81,9 @@ static struct ctrl sd_ctrls[] = { .type = V4L2_CTRL_TYPE_INTEGER, .name = "Contrast", .minimum = 0, - .maximum = 0xffff, + .maximum = 64725, .step = 1, - .default_value = 0xaa00, + .default_value = 64725, }, .set = sd_setcontrast, .get = sd_getcontrast, @@ -91,11 +97,39 @@ static struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 63, .step = 1, - .default_value = 31, + .default_value = 20, }, .set = sd_setcolors, .get = sd_getcolors, }, +#define MY_BLUE_BALANCE 3 + { + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Blue Balance", + .minimum = 0, + .maximum = 127, + .step = 1, + .default_value = 0, + }, + .set = sd_setblue_balance, + .get = sd_getblue_balance, + }, +#define MY_RED_BALANCE 4 + { + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Red Balance", + .minimum = 0, + .maximum = 127, + .step = 1, + .default_value = 0, + }, + .set = sd_setred_balance, + .get = sd_getred_balance, + }, }; static struct v4l2_pix_format vga_mode[] = { @@ -1871,9 +1905,7 @@ static void setbrightness(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x11, sd->brightness); reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x12, sd->brightness); - reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x13, sd->brightness); } static void getbrightness(struct gspca_dev *gspca_dev) @@ -1882,7 +1914,7 @@ static void getbrightness(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; __u16 brightness; - brightness = reg_read(gspca_dev, SPCA501_REG_CCDSP, 0x11, 2); + brightness = reg_read(gspca_dev, SPCA501_REG_CCDSP, 0x12, 2); sd->brightness = brightness; #endif } @@ -1925,11 +1957,23 @@ static void getcolors(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; sd->colors = reg_read(gspca_dev, SPCA501_REG_CCDSP, 0x0c, 2); -/* sd->hue = (reg_read(gspca_dev, SPCA501_REG_CCDSP, 0x13, */ -/* 2) & 0xFF) << 8; */ #endif } +static void setblue_balance(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x11, sd->blue_balance); +} + +static void setred_balance(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x13, sd->red_balance); +} + /* this function is called at probe time */ static int sd_config(struct gspca_dev *gspca_dev, const struct usb_device_id *id) @@ -2128,6 +2172,42 @@ static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) return 0; } +static int sd_setblue_balance(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->blue_balance = val; + if (gspca_dev->streaming) + setblue_balance(gspca_dev); + return 0; +} + +static int sd_getblue_balance(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->blue_balance; + return 0; +} + +static int sd_setred_balance(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->red_balance = val; + if (gspca_dev->streaming) + setred_balance(gspca_dev); + return 0; +} + +static int sd_getred_balance(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->red_balance; + return 0; +} + /* sub-driver description */ static const struct sd_desc sd_desc = { .name = MODULE_NAME, -- cgit v1.2.3 From 2ad2e741fe5f88e50ab91f2c5bf2d1a4710f2ebb Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 17 Nov 2008 23:30:09 -0200 Subject: Add AC97 registers found on em28xx devices From: Mauro Carvalho Chehab Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-reg.h | 52 +++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-reg.h b/linux/drivers/media/video/em28xx/em28xx-reg.h index 6e5757caa..763e57900 100644 --- a/linux/drivers/media/video/em28xx/em28xx-reg.h +++ b/linux/drivers/media/video/em28xx/em28xx-reg.h @@ -117,3 +117,55 @@ enum em28xx_chip_id { CHIP_ID_EM2883 = 36, CHIP_ID_EM2874 = 65, }; + +/* + * Registers used by em202 and other AC97 chips + */ + +/* Standard AC97 registers */ +#define AC97_RESET 0x00 +#define AC97_MASTER_VOL 0x02 +#define AC97_LINE_LEVEL_VOL 0x04 +#define AC97_MASTER_MONO_VOL 0x06 + +#define AC97_PC_BEEP_VOL 0x0a +#define AC97_PHONE_VOL 0x0c +#define AC97_MIC_VOL 0x0e +#define AC97_LINEIN_VOL 0x10 +#define AC97_CD_VOL 0x12 +#define AC97_VIDEO_VOL 0x14 +#define AC97_AUX_VOL 0x16 +#define AC97_PCM_OUT_VOL 0x18 +#define AC97_RECORD_SELECT 0x1a +#define AC97_RECORD_GAIN 0x1c +#define AC97_GENERAL_PURPOSE 0x20 +#define AC97_3D_CTRL 0x22 +#define AC97_AUD_INT_AND_PAG 0x24 +#define AC97_POWER_DOWN_CTRL 0x26 +#define AC97_EXT_AUD_ID 0x28 +#define AC97_EXT_AUD_CTRL 0x2a + +/* Supported rate varies for each AC97 device + if write an unsupported value, it will return the closest one + */ +#define AC97_PCM_OUT_FRONT_SRATE 0x2c +#define AC97_PCM_OUT_SURR_SRATE 0x2e +#define AC97_PCM_OUT_LFE_SRATE 0x30 +#define AC97_PCM_IN_SRATE 0x32 +#define AC97_LFE_MASTER_VOL 0x36 +#define AC97_SURR_MASTER_VOL 0x38 +#define AC97_SPDIF_OUT_CTRL 0x3a + +#define AC97_VENDOR_ID1 0x7c +#define AC97_VENDOR_ID2 0x7e + +/* EMP202 vendor registers */ +#define EM202_EXT_MODEM_CTRL 0x3e +#define EM202_GPIO_CONF 0x4c +#define EM202_GPIO_POLARITY 0x4e +#define EM202_GPIO_STICKY 0x50 +#define EM202_GPIO_MASK 0x52 +#define EM202_GPIO_STATUS 0x54 +#define EM202_SPDIF_OUT_SEL 0x6a +#define EM202_ANTIPOP 0x72 +#define EM202_EAPD_GPIO_ACCESS 0x74 -- cgit v1.2.3 From 3333dbe217df35d692d56a27cce4735cb9d6b5fb Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 17 Nov 2008 23:40:36 -0200 Subject: Remove the previous register names From: Mauro Carvalho Chehab Previously, AC97 registers were named as if they were part of em28xx device, generating some confusion. Replace such names for a more general naming convention. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-core.c | 8 ++++---- linux/drivers/media/video/em28xx/em28xx-reg.h | 5 ----- 2 files changed, 4 insertions(+), 9 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-core.c b/linux/drivers/media/video/em28xx/em28xx-core.c index d3fc86b43..8794bfe36 100644 --- a/linux/drivers/media/video/em28xx/em28xx-core.c +++ b/linux/drivers/media/video/em28xx/em28xx-core.c @@ -292,11 +292,11 @@ static int em28xx_set_audio_source(struct em28xx *dev) /* Sets AC97 mixer registers This is seems to be needed, even for non-ac97 configs */ - ret = em28xx_write_ac97(dev, EM28XX_R14_VIDEO_AC97, video); + ret = em28xx_write_ac97(dev, AC97_VIDEO_VOL, video); if (ret < 0) return ret; - ret = em28xx_write_ac97(dev, EM28XX_R10_LINE_IN_AC97, line); + ret = em28xx_write_ac97(dev, AC97_LINEIN_VOL, line); return ret; } @@ -312,7 +312,7 @@ int em28xx_audio_analog_set(struct em28xx *dev) /* Mute */ s[1] |= 0x80; - ret = em28xx_write_ac97(dev, EM28XX_R02_MASTER_AC97, s); + ret = em28xx_write_ac97(dev, AC97_MASTER_VOL, s); if (ret < 0) return ret; @@ -334,7 +334,7 @@ int em28xx_audio_analog_set(struct em28xx *dev) /* Unmute device */ if (!dev->mute) s[1] &= ~0x80; - ret = em28xx_write_ac97(dev, EM28XX_R02_MASTER_AC97, s); + ret = em28xx_write_ac97(dev, AC97_MASTER_VOL, s); return ret; } diff --git a/linux/drivers/media/video/em28xx/em28xx-reg.h b/linux/drivers/media/video/em28xx/em28xx-reg.h index 763e57900..fcab09912 100644 --- a/linux/drivers/media/video/em28xx/em28xx-reg.h +++ b/linux/drivers/media/video/em28xx/em28xx-reg.h @@ -78,11 +78,6 @@ 0x47 IR data */ -/* em202 registers */ -#define EM28XX_R02_MASTER_AC97 0x02 -#define EM28XX_R10_LINE_IN_AC97 0x10 -#define EM28XX_R14_VIDEO_AC97 0x14 - /* em2874 registers */ #define EM2874_R50_IR_CONFIG 0x50 #define EM2874_R51_IR 0x51 -- cgit v1.2.3 From 79ee5103332e36280ca8ff8426dce1d8cb37df1c Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Mon, 17 Nov 2008 20:48:46 -0500 Subject: cx18: Remove unnecessary MMIO accesses in time critical irq handling path From: Andy Walls 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. Priority: normal Signed-off-by: Andy Walls --- linux/drivers/media/video/cx18/cx18-driver.h | 4 ++++ linux/drivers/media/video/cx18/cx18-io.c | 20 ++++++++------------ linux/drivers/media/video/cx18/cx18-irq.c | 13 ++++--------- linux/drivers/media/video/cx18/cx18-mailbox.c | 7 ++++++- 4 files changed, 22 insertions(+), 22 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/cx18/cx18-driver.h b/linux/drivers/media/video/cx18/cx18-driver.h index 02a82c3b7..cad352aeb 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.h +++ b/linux/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/linux/drivers/media/video/cx18/cx18-io.c b/linux/drivers/media/video/cx18/cx18-io.c index a2b5e807f..c6f1d0d7f 100644 --- a/linux/drivers/media/video/cx18/cx18-io.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-irq.c b/linux/drivers/media/video/cx18/cx18-irq.c index 52fc41521..34e25373e 100644 --- a/linux/drivers/media/video/cx18/cx18-irq.c +++ b/linux/drivers/media/video/cx18/cx18-irq.c @@ -48,16 +48,11 @@ irqreturn_t cx18_irq_handler(int irq, void *dev_id) #endif { 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/linux/drivers/media/video/cx18/cx18-mailbox.c b/linux/drivers/media/video/cx18/cx18-mailbox.c index c7fcd6507..6ad35d0f9 100644 --- a/linux/drivers/media/video/cx18/cx18-mailbox.c +++ b/linux/drivers/media/video/cx18/cx18-mailbox.c @@ -405,7 +405,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 47b954968656ce2ddac212e3750a135a4b1aa581 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Tue, 18 Nov 2008 08:19:52 +0100 Subject: gspca: Reset the bulk URB status before resubmitting at irq level. From: Jean-Francois Moine Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/gspca.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/gspca.c b/linux/drivers/media/video/gspca/gspca.c index 1d54a7dce..5197ccadd 100644 --- a/linux/drivers/media/video/gspca/gspca.c +++ b/linux/drivers/media/video/gspca/gspca.c @@ -177,7 +177,6 @@ static void fill_frame(struct gspca_dev *gspca_dev, } /* resubmit the URB */ - urb->status = 0; st = usb_submit_urb(urb, GFP_ATOMIC); if (st < 0) PDEBUG(D_ERR|D_PACK, "usb_submit_urb() ret %d", st); @@ -218,7 +217,13 @@ static void bulk_irq(struct urb *urb PDEBUG(D_PACK, "bulk irq"); if (!gspca_dev->streaming) return; - if (urb->status != 0 && urb->status != -ECONNRESET) { + switch (urb->status) { + case 0: + break; + case -ECONNRESET: + urb->status = 0; + break; + default: #ifdef CONFIG_PM if (!gspca_dev->frozen) #endif -- cgit v1.2.3 From ff8653be50368f7997308c7834fbb41d771132cc Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 18 Nov 2008 06:05:46 -0200 Subject: Add em2750 to the list of known em28xx chip ids. From: Devin Heitmueller Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-cards.c | 3 +++ linux/drivers/media/video/em28xx/em28xx-reg.h | 1 + 2 files changed, 4 insertions(+) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index fcba8944c..57c91838e 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -1360,6 +1360,9 @@ void em28xx_pre_card_setup(struct em28xx *dev) if (rc > 0) { dev->chip_id = rc; switch (rc) { + case CHIP_ID_EM2750: + em28xx_info("chip ID is em2750\n"); + break; case CHIP_ID_EM2820: em28xx_info("chip ID is em2820\n"); break; diff --git a/linux/drivers/media/video/em28xx/em28xx-reg.h b/linux/drivers/media/video/em28xx/em28xx-reg.h index fcab09912..491d66abe 100644 --- a/linux/drivers/media/video/em28xx/em28xx-reg.h +++ b/linux/drivers/media/video/em28xx/em28xx-reg.h @@ -108,6 +108,7 @@ enum em28xx_chip_id { CHIP_ID_EM2820 = 18, CHIP_ID_EM2840 = 20, + CHIP_ID_EM2750 = 33, CHIP_ID_EM2860 = 34, CHIP_ID_EM2883 = 36, CHIP_ID_EM2874 = 65, -- cgit v1.2.3 From 7830a441c0d4f9753dea8a72466091346ded739b Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Tue, 18 Nov 2008 10:33:08 +0100 Subject: gspca: Memory leak when disconnect while streaming. From: Jean-Francois Moine As a side effect, the sd routine stop0 is called on disconnect. This permits the subdriver to free its resources. Priority: high Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/conex.c | 3 +++ linux/drivers/media/video/gspca/finepix.c | 8 ++++++++ linux/drivers/media/video/gspca/gspca.c | 17 ++++++++--------- linux/drivers/media/video/gspca/gspca.h | 2 +- linux/drivers/media/video/gspca/pac7311.c | 3 +++ linux/drivers/media/video/gspca/spca501.c | 3 +++ linux/drivers/media/video/gspca/spca505.c | 4 ++++ linux/drivers/media/video/gspca/spca561.c | 3 +++ linux/drivers/media/video/gspca/vc032x.c | 3 +++ linux/drivers/media/video/gspca/zc3xx.c | 3 +++ 10 files changed, 39 insertions(+), 10 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/conex.c b/linux/drivers/media/video/gspca/conex.c index a9d51ba7c..de28354ea 100644 --- a/linux/drivers/media/video/gspca/conex.c +++ b/linux/drivers/media/video/gspca/conex.c @@ -846,10 +846,13 @@ static int sd_start(struct gspca_dev *gspca_dev) return 0; } +/* called on streamoff with alt 0 and on disconnect */ static void sd_stop0(struct gspca_dev *gspca_dev) { int retry = 50; + if (!gspca_dev->present) + return; reg_w_val(gspca_dev, 0x0000, 0x00); reg_r(gspca_dev, 0x0002, 1); reg_w_val(gspca_dev, 0x0053, 0x00); diff --git a/linux/drivers/media/video/gspca/finepix.c b/linux/drivers/media/video/gspca/finepix.c index d3e3f085b..03cb94466 100644 --- a/linux/drivers/media/video/gspca/finepix.c +++ b/linux/drivers/media/video/gspca/finepix.c @@ -276,6 +276,12 @@ static void sd_stopN(struct gspca_dev *gspca_dev) /* Stop the state machine */ if (dev->state != FPIX_NOP) wait_for_completion(&dev->can_close); +} + +/* called on streamoff with alt 0 and disconnect */ +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + struct usb_fpix *dev = (struct usb_fpix *) gspca_dev; usb_free_urb(dev->control_urb); dev->control_urb = NULL; @@ -382,6 +388,7 @@ static int sd_start(struct gspca_dev *gspca_dev) error: /* Free the ressources */ sd_stopN(gspca_dev); + sd_stop0(gspca_dev); return ret; } @@ -422,6 +429,7 @@ static const struct sd_desc sd_desc = { .init = sd_init, .start = sd_start, .stopN = sd_stopN, + .stop0 = sd_stop0, }; /* -- device connect -- */ diff --git a/linux/drivers/media/video/gspca/gspca.c b/linux/drivers/media/video/gspca/gspca.c index 5197ccadd..d78835584 100644 --- a/linux/drivers/media/video/gspca/gspca.c +++ b/linux/drivers/media/video/gspca/gspca.c @@ -681,15 +681,14 @@ static void gspca_stream_off(struct gspca_dev *gspca_dev) { gspca_dev->streaming = 0; atomic_set(&gspca_dev->nevent, 0); - if (gspca_dev->present) { - if (gspca_dev->sd_desc->stopN) - gspca_dev->sd_desc->stopN(gspca_dev); - destroy_urbs(gspca_dev); - gspca_set_alt0(gspca_dev); - if (gspca_dev->sd_desc->stop0) - gspca_dev->sd_desc->stop0(gspca_dev); - PDEBUG(D_STREAM, "stream off OK"); - } + if (gspca_dev->present + && gspca_dev->sd_desc->stopN) + gspca_dev->sd_desc->stopN(gspca_dev); + destroy_urbs(gspca_dev); + gspca_set_alt0(gspca_dev); + if (gspca_dev->sd_desc->stop0) + gspca_dev->sd_desc->stop0(gspca_dev); + PDEBUG(D_STREAM, "stream off OK"); } static void gspca_set_default_mode(struct gspca_dev *gspca_dev) diff --git a/linux/drivers/media/video/gspca/gspca.h b/linux/drivers/media/video/gspca/gspca.h index d76eb9f3e..35f463c95 100644 --- a/linux/drivers/media/video/gspca/gspca.h +++ b/linux/drivers/media/video/gspca/gspca.h @@ -101,7 +101,7 @@ struct sd_desc { cam_pkt_op pkt_scan; /* optional operations */ cam_v_op stopN; /* called on stream off - main alt */ - cam_v_op stop0; /* called on stream off - alt 0 */ + cam_v_op stop0; /* called on stream off & disconnect - alt 0 */ cam_v_op dq_callback; /* called when a frame has been dequeued */ cam_jpg_op get_jcomp; cam_jpg_op set_jcomp; diff --git a/linux/drivers/media/video/gspca/pac7311.c b/linux/drivers/media/video/gspca/pac7311.c index a122634e0..f443df77e 100644 --- a/linux/drivers/media/video/gspca/pac7311.c +++ b/linux/drivers/media/video/gspca/pac7311.c @@ -763,10 +763,13 @@ static void sd_stopN(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */ } +/* called on streamoff with alt 0 and on disconnect */ static void sd_stop0(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + if (!gspca_dev->present) + return; if (sd->sensor == SENSOR_PAC7302) { reg_w(gspca_dev, 0xff, 0x01); reg_w(gspca_dev, 0x78, 0x40); diff --git a/linux/drivers/media/video/gspca/spca501.c b/linux/drivers/media/video/gspca/spca501.c index 12e7124bf..17dab9859 100644 --- a/linux/drivers/media/video/gspca/spca501.c +++ b/linux/drivers/media/video/gspca/spca501.c @@ -2084,8 +2084,11 @@ static void sd_stopN(struct gspca_dev *gspca_dev) reg_write(gspca_dev->dev, SPCA501_REG_CTLRL, 0x01, 0x00); } +/* called on streamoff with alt 0 and on disconnect */ static void sd_stop0(struct gspca_dev *gspca_dev) { + if (!gspca_dev->present) + return; reg_write(gspca_dev->dev, SPCA501_REG_CTLRL, 0x05, 0x00); } diff --git a/linux/drivers/media/video/gspca/spca505.c b/linux/drivers/media/video/gspca/spca505.c index 62bab3cc1..c52598e94 100644 --- a/linux/drivers/media/video/gspca/spca505.c +++ b/linux/drivers/media/video/gspca/spca505.c @@ -811,8 +811,12 @@ static void sd_stopN(struct gspca_dev *gspca_dev) reg_write(gspca_dev->dev, 0x02, 0x00, 0x00); } +/* called on streamoff with alt 0 and on disconnect */ static void sd_stop0(struct gspca_dev *gspca_dev) { + if (!gspca_dev->present) + return; + /* This maybe reset or power control */ reg_write(gspca_dev->dev, 0x03, 0x03, 0x20); reg_write(gspca_dev->dev, 0x03, 0x01, 0x0); diff --git a/linux/drivers/media/video/gspca/spca561.c b/linux/drivers/media/video/gspca/spca561.c index 5b65dd66b..4c0046cc7 100644 --- a/linux/drivers/media/video/gspca/spca561.c +++ b/linux/drivers/media/video/gspca/spca561.c @@ -780,10 +780,13 @@ static void sd_stopN(struct gspca_dev *gspca_dev) } } +/* called on streamoff with alt 0 and on disconnect */ static void sd_stop0(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + if (!gspca_dev->present) + return; if (sd->chip_revision == Rev012A) { reg_w_val(gspca_dev->dev, 0x8118, 0x29); reg_w_val(gspca_dev->dev, 0x8114, 0x08); diff --git a/linux/drivers/media/video/gspca/vc032x.c b/linux/drivers/media/video/gspca/vc032x.c index 95531138e..f6fae60ae 100644 --- a/linux/drivers/media/video/gspca/vc032x.c +++ b/linux/drivers/media/video/gspca/vc032x.c @@ -1830,10 +1830,13 @@ static void sd_stopN(struct gspca_dev *gspca_dev) reg_w(dev, 0xa0, 0x09, 0xb003); } +/* called on streamoff with alt 0 and on disconnect */ static void sd_stop0(struct gspca_dev *gspca_dev) { struct usb_device *dev = gspca_dev->dev; + if (!gspca_dev->present) + return; reg_w(dev, 0x89, 0xffff, 0xffff); } diff --git a/linux/drivers/media/video/gspca/zc3xx.c b/linux/drivers/media/video/gspca/zc3xx.c index b74ce0ef1..adbca6a9d 100644 --- a/linux/drivers/media/video/gspca/zc3xx.c +++ b/linux/drivers/media/video/gspca/zc3xx.c @@ -7371,10 +7371,13 @@ static int sd_start(struct gspca_dev *gspca_dev) return 0; } +/* called on streamoff with alt 0 and on disconnect */ static void sd_stop0(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + if (!gspca_dev->present) + return; send_unknown(gspca_dev->dev, sd->sensor); } -- cgit v1.2.3 From a370946d785e65e0ded57703e7e4981631ad9012 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 18 Nov 2008 11:23:19 -0200 Subject: em28xx: Avoid memory leaks if registration fails From: Mauro Carvalho Chehab em28xx_init_dev() has some error conditions that are not properly de-allocating dev var, nor freeing the device number for a future usage. Priority: high Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-video.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index 09886d567..625c9dea1 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -2034,8 +2034,6 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, errCode = em28xx_config(dev); if (errCode) { em28xx_errdev("error configuring device\n"); - em28xx_devused &= ~(1<devno); - kfree(dev); return -ENOMEM; } @@ -2172,7 +2170,6 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, fail_unreg: em28xx_release_resources(dev); mutex_unlock(&dev->lock); - kfree(dev); return retval; } @@ -2333,8 +2330,12 @@ static int em28xx_usb_probe(struct usb_interface *interface, /* allocate device struct */ retval = em28xx_init_dev(&dev, udev, nr); - if (retval) + if (retval) { + em28xx_devused &= ~(1<devno); + kfree(dev); + return retval; + } em28xx_info("Found %s\n", em28xx_boards[dev->model].name); -- cgit v1.2.3 From ae01a3498c4c64115313788ab68a8a99c7e8de6e Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 18 Nov 2008 15:51:08 -0200 Subject: em28xx: avoid allocating/dealocating memory on every control urb From: Mauro Carvalho Chehab Before this patch, every register setup on em28xx were dynamically allocating a temporary buffer for control URB's to be handled. To avoid this ping-pong, use, instead a pre-allocated buffer. Also, be sure that read control URB's also use the buffer, instead of relying on a stack buffer. Priority: high Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-core.c | 48 ++++++++++++++------------ linux/drivers/media/video/em28xx/em28xx.h | 5 +++ 2 files changed, 31 insertions(+), 22 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-core.c b/linux/drivers/media/video/em28xx/em28xx-core.c index 8794bfe36..ef6830cd5 100644 --- a/linux/drivers/media/video/em28xx/em28xx-core.c +++ b/linux/drivers/media/video/em28xx/em28xx-core.c @@ -69,19 +69,29 @@ int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg, int ret, byte; if (dev->state & DEV_DISCONNECTED) - return(-ENODEV); + return -ENODEV; + + if (len > URB_MAX_CTRL_SIZE) + return -EINVAL; em28xx_regdbg("req=%02x, reg=%02x ", req, reg); ret = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), req, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0x0000, reg, buf, len, HZ); + 0x0000, reg, dev->urb_buf, len, HZ); + if (ret < 0) { + if (reg_debug) + printk(" failed!\n"); + return ret; + } + + if (len) + memcpy(buf, dev->urb_buf, len); if (reg_debug) { - printk(ret < 0 ? " failed!\n" : "%02x values: ", ret); + printk("%02x values: ", ret); for (byte = 0; byte < len; byte++) printk(" %02x", (unsigned char)buf[byte]); - printk("\n"); } @@ -104,14 +114,16 @@ int em28xx_read_reg_req(struct em28xx *dev, u8 req, u16 reg) ret = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), req, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0x0000, reg, &val, 1, HZ); + 0x0000, reg, dev->urb_buf, 1, HZ); + if (ret < 0) { + printk(" failed!\n"); + return ret; + } - if (reg_debug) - printk(ret < 0 ? " failed!\n" : - "%02x\n", (unsigned char) val); + val = dev->urb_buf[0]; - if (ret < 0) - return ret; + if (reg_debug) + printk("%02x\n", (unsigned char) val); return val; } @@ -130,19 +142,13 @@ int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf, { int ret; - /*usb_control_msg seems to expect a kmalloced buffer */ - unsigned char *bufs; - if (dev->state & DEV_DISCONNECTED) return -ENODEV; - if (len < 1) + if ((len < 1) || (len > URB_MAX_CTRL_SIZE)) return -EINVAL; - bufs = kmalloc(len, GFP_KERNEL); - em28xx_regdbg("req=%02x reg=%02x:", req, reg); - if (reg_debug) { int i; for (i = 0; i < len; ++i) @@ -150,16 +156,14 @@ int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf, printk("\n"); } - if (!bufs) - return -ENOMEM; - memcpy(bufs, buf, len); + memcpy(dev->urb_buf, buf, len); ret = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), req, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0x0000, reg, bufs, len, HZ); + 0x0000, reg, dev->urb_buf, len, HZ); + if (dev->wait_after_write) msleep(dev->wait_after_write); - kfree(bufs); return ret; } diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index 97afac4b6..2e108be19 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -104,6 +104,9 @@ #define EM28XX_MIN_BUF 4 #define EM28XX_DEF_BUF 8 +/*Limits the max URB message size */ +#define URB_MAX_CTRL_SIZE 80 + /* Params for validated field */ #define EM28XX_BOARD_NOT_VALIDATED 1 #define EM28XX_BOARD_VALIDATED 0 @@ -468,6 +471,8 @@ struct em28xx { unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */ struct urb *urb[EM28XX_NUM_BUFS]; /* urb for isoc transfers */ char *transfer_buffer[EM28XX_NUM_BUFS]; /* transfer buffers for isoc transfer */ + char urb_buf[URB_MAX_CTRL_SIZE]; /* urb control msg buffer */ + /* helper funcs that call usb_control_msg */ int (*em28xx_write_regs) (struct em28xx *dev, u16 reg, char *buf, int len); -- cgit v1.2.3 From ffa812c1a2fa46e3b894155e598c0e6e0d51b2f1 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Tue, 18 Nov 2008 19:52:31 +0100 Subject: gspca: Lock the subdrivers via module_get/put. From: Jean-Francois Moine The previous subdriver protection against rmmod was done via the file operations table in the device descriptor. On device disconnection while streaming, the device structure was freed at close time, and the module_put still used the module name in the freed area. Now, explicit module get/put are done on open and close. Priority: high Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/gspca.c | 13 ++++++++++--- linux/drivers/media/video/gspca/gspca.h | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/gspca.c b/linux/drivers/media/video/gspca/gspca.c index d78835584..4e3833a00 100644 --- a/linux/drivers/media/video/gspca/gspca.c +++ b/linux/drivers/media/video/gspca/gspca.c @@ -909,6 +909,13 @@ static int dev_open(struct inode *inode, struct file *file) ret = -EBUSY; goto out; } + + /* protect the subdriver against rmmod */ + if (!try_module_get(gspca_dev->module)) { + ret = -ENODEV; + goto out; + } + gspca_dev->users++; /* one more user */ @@ -955,6 +962,7 @@ static int dev_close(struct inode *inode, struct file *file) gspca_dev->memory = GSPCA_MEMORY_NO; } file->private_data = NULL; + module_put(gspca_dev->module); mutex_unlock(&gspca_dev->queue_lock); PDEBUG(D_STREAM, "close done"); @@ -1906,9 +1914,8 @@ int gspca_dev_probe(struct usb_interface *intf, /* init video stuff */ memcpy(&gspca_dev->vdev, &gspca_template, sizeof gspca_template); gspca_dev->vdev.parent = &dev->dev; - memcpy(&gspca_dev->fops, &dev_fops, sizeof gspca_dev->fops); - gspca_dev->vdev.fops = &gspca_dev->fops; - gspca_dev->fops.owner = module; /* module protection */ + gspca_dev->vdev.fops = &dev_fops; + gspca_dev->module = module; gspca_dev->present = 1; ret = video_register_device(&gspca_dev->vdev, VFL_TYPE_GRABBER, diff --git a/linux/drivers/media/video/gspca/gspca.h b/linux/drivers/media/video/gspca/gspca.h index 35f463c95..4ff9b8105 100644 --- a/linux/drivers/media/video/gspca/gspca.h +++ b/linux/drivers/media/video/gspca/gspca.h @@ -125,7 +125,7 @@ struct gspca_frame { struct gspca_dev { struct video_device vdev; /* !! must be the first item */ - struct file_operations fops; + struct module *module; /* subdriver handling the device */ struct usb_device *dev; struct kref kref; struct file *capt_file; /* file doing video capture */ -- cgit v1.2.3 From ab836bf8e5b5007ce7e1e2e6513637e99fea6a83 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Tue, 18 Nov 2008 23:24:33 -0500 Subject: cx18: Restore buffers that have fallen out of the transfer rotation From: Andy Walls Restore buffers that have fallen out of the transfer rotation, and check for coherent mailbox data when processing a stale mailbox. Priority: normal Signed-off-by: Andy Walls --- linux/drivers/media/video/cx18/cx18-driver.h | 1 + linux/drivers/media/video/cx18/cx18-mailbox.c | 59 +++++++++++++++++++++------ linux/drivers/media/video/cx18/cx18-queue.c | 59 ++++++++++++++++++++------- 3 files changed, 92 insertions(+), 27 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/cx18/cx18-driver.h b/linux/drivers/media/video/cx18/cx18-driver.h index cad352aeb..f06290d32 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.h +++ b/linux/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/linux/drivers/media/video/cx18/cx18-mailbox.c b/linux/drivers/media/video/cx18/cx18-mailbox.c index 6ad35d0f9..a1060880a 100644 --- a/linux/drivers/media/video/cx18/cx18-mailbox.c +++ b/linux/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)) @@ -263,10 +295,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; } @@ -413,9 +445,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/linux/drivers/media/video/cx18/cx18-queue.c b/linux/drivers/media/video/cx18/cx18-queue.c index 5a3839403..ff6df3632 100644 --- a/linux/drivers/media/video/cx18/cx18-queue.c +++ b/linux/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 6978eda341c7f776839e356af1ac424d65bec4b9 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Wed, 19 Nov 2008 10:37:53 +0100 Subject: gspca: Move the video device to a separate area. From: Jean-Francois Moine The video device was part of the gspca device. On device disconnection while streaming, the device structure is freed at close time. In this case, the remaining close job on the video device run out of allocated memory. Priority: high Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/gspca.c | 28 +++++++++++++--------------- linux/drivers/media/video/gspca/gspca.h | 2 +- 2 files changed, 14 insertions(+), 16 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/gspca.c b/linux/drivers/media/video/gspca/gspca.c index 4e3833a00..35f504440 100644 --- a/linux/drivers/media/video/gspca/gspca.c +++ b/linux/drivers/media/video/gspca/gspca.c @@ -897,7 +897,7 @@ static int dev_open(struct inode *inode, struct file *file) int ret; PDEBUG(D_STREAM, "%s open", current->comm); - gspca_dev = (struct gspca_dev *) video_devdata(file); + gspca_dev = video_drvdata(file); if (mutex_lock_interruptible(&gspca_dev->queue_lock)) return -ERESTARTSYS; if (!gspca_dev->present) { @@ -925,10 +925,10 @@ static int dev_open(struct inode *inode, struct file *file) #ifdef GSPCA_DEBUG /* activate the v4l2 debug */ if (gspca_debug & D_V4L2) - gspca_dev->vdev.debug |= V4L2_DEBUG_IOCTL + gspca_dev->vdev->debug |= V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG; else - gspca_dev->vdev.debug &= ~(V4L2_DEBUG_IOCTL + gspca_dev->vdev->debug &= ~(V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG); #endif ret = 0; @@ -1790,11 +1790,6 @@ out: return ret; } -static void dev_release(struct video_device *vfd) -{ - /* nothing */ -} - static struct file_operations dev_fops = { .owner = THIS_MODULE, .open = dev_open, @@ -1842,7 +1837,7 @@ static struct video_device gspca_template = { .name = "gspca main driver", .fops = &dev_fops, .ioctl_ops = &dev_ioctl_ops, - .release = dev_release, /* mandatory */ + .release = video_device_release, .minor = -1, }; @@ -1912,16 +1907,18 @@ int gspca_dev_probe(struct usb_interface *intf, init_waitqueue_head(&gspca_dev->wq); /* init video stuff */ - memcpy(&gspca_dev->vdev, &gspca_template, sizeof gspca_template); - gspca_dev->vdev.parent = &dev->dev; - gspca_dev->vdev.fops = &dev_fops; + gspca_dev->vdev = video_device_alloc(); + memcpy(gspca_dev->vdev, &gspca_template, sizeof gspca_template); + gspca_dev->vdev->parent = &dev->dev; gspca_dev->module = module; gspca_dev->present = 1; - ret = video_register_device(&gspca_dev->vdev, + video_set_drvdata(gspca_dev->vdev, gspca_dev); + ret = video_register_device(gspca_dev->vdev, VFL_TYPE_GRABBER, video_nr); if (ret < 0) { err("video_register_device err %d", ret); + video_device_release(gspca_dev->vdev); goto out; } @@ -1929,7 +1926,8 @@ int gspca_dev_probe(struct usb_interface *intf, PDEBUG(D_PROBE, "probe ok"); return 0; out: - kref_put(&gspca_dev->kref, gspca_delete); + kfree(gspca_dev->usb_buf); + kfree(gspca_dev); return ret; } EXPORT_SYMBOL(gspca_dev_probe); @@ -1947,7 +1945,7 @@ void gspca_disconnect(struct usb_interface *intf) usb_set_intfdata(intf, NULL); /* We don't want people trying to open up the device */ - video_unregister_device(&gspca_dev->vdev); + video_unregister_device(gspca_dev->vdev); gspca_dev->present = 0; gspca_dev->streaming = 0; diff --git a/linux/drivers/media/video/gspca/gspca.h b/linux/drivers/media/video/gspca/gspca.h index 4ff9b8105..a557738e8 100644 --- a/linux/drivers/media/video/gspca/gspca.h +++ b/linux/drivers/media/video/gspca/gspca.h @@ -124,7 +124,7 @@ struct gspca_frame { }; struct gspca_dev { - struct video_device vdev; /* !! must be the first item */ + struct video_device *vdev; struct module *module; /* subdriver handling the device */ struct usb_device *dev; struct kref kref; -- cgit v1.2.3 From 7b1618ce2f951b1d38a9d687ddc0c48b47ed41db Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Wed, 19 Nov 2008 20:59:51 +0100 Subject: gspca: Revert the previous change. From: Jean-Francois Moine Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/gspca.c | 28 +++++++++++++++------------- linux/drivers/media/video/gspca/gspca.h | 2 +- 2 files changed, 16 insertions(+), 14 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/gspca.c b/linux/drivers/media/video/gspca/gspca.c index 35f504440..4e3833a00 100644 --- a/linux/drivers/media/video/gspca/gspca.c +++ b/linux/drivers/media/video/gspca/gspca.c @@ -897,7 +897,7 @@ static int dev_open(struct inode *inode, struct file *file) int ret; PDEBUG(D_STREAM, "%s open", current->comm); - gspca_dev = video_drvdata(file); + gspca_dev = (struct gspca_dev *) video_devdata(file); if (mutex_lock_interruptible(&gspca_dev->queue_lock)) return -ERESTARTSYS; if (!gspca_dev->present) { @@ -925,10 +925,10 @@ static int dev_open(struct inode *inode, struct file *file) #ifdef GSPCA_DEBUG /* activate the v4l2 debug */ if (gspca_debug & D_V4L2) - gspca_dev->vdev->debug |= V4L2_DEBUG_IOCTL + gspca_dev->vdev.debug |= V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG; else - gspca_dev->vdev->debug &= ~(V4L2_DEBUG_IOCTL + gspca_dev->vdev.debug &= ~(V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG); #endif ret = 0; @@ -1790,6 +1790,11 @@ out: return ret; } +static void dev_release(struct video_device *vfd) +{ + /* nothing */ +} + static struct file_operations dev_fops = { .owner = THIS_MODULE, .open = dev_open, @@ -1837,7 +1842,7 @@ static struct video_device gspca_template = { .name = "gspca main driver", .fops = &dev_fops, .ioctl_ops = &dev_ioctl_ops, - .release = video_device_release, + .release = dev_release, /* mandatory */ .minor = -1, }; @@ -1907,18 +1912,16 @@ int gspca_dev_probe(struct usb_interface *intf, init_waitqueue_head(&gspca_dev->wq); /* init video stuff */ - gspca_dev->vdev = video_device_alloc(); - memcpy(gspca_dev->vdev, &gspca_template, sizeof gspca_template); - gspca_dev->vdev->parent = &dev->dev; + memcpy(&gspca_dev->vdev, &gspca_template, sizeof gspca_template); + gspca_dev->vdev.parent = &dev->dev; + gspca_dev->vdev.fops = &dev_fops; gspca_dev->module = module; gspca_dev->present = 1; - video_set_drvdata(gspca_dev->vdev, gspca_dev); - ret = video_register_device(gspca_dev->vdev, + ret = video_register_device(&gspca_dev->vdev, VFL_TYPE_GRABBER, video_nr); if (ret < 0) { err("video_register_device err %d", ret); - video_device_release(gspca_dev->vdev); goto out; } @@ -1926,8 +1929,7 @@ int gspca_dev_probe(struct usb_interface *intf, PDEBUG(D_PROBE, "probe ok"); return 0; out: - kfree(gspca_dev->usb_buf); - kfree(gspca_dev); + kref_put(&gspca_dev->kref, gspca_delete); return ret; } EXPORT_SYMBOL(gspca_dev_probe); @@ -1945,7 +1947,7 @@ void gspca_disconnect(struct usb_interface *intf) usb_set_intfdata(intf, NULL); /* We don't want people trying to open up the device */ - video_unregister_device(gspca_dev->vdev); + video_unregister_device(&gspca_dev->vdev); gspca_dev->present = 0; gspca_dev->streaming = 0; diff --git a/linux/drivers/media/video/gspca/gspca.h b/linux/drivers/media/video/gspca/gspca.h index a557738e8..4ff9b8105 100644 --- a/linux/drivers/media/video/gspca/gspca.h +++ b/linux/drivers/media/video/gspca/gspca.h @@ -124,7 +124,7 @@ struct gspca_frame { }; struct gspca_dev { - struct video_device *vdev; + struct video_device vdev; /* !! must be the first item */ struct module *module; /* subdriver handling the device */ struct usb_device *dev; struct kref kref; -- cgit v1.2.3 From ec61b66eb8aaf86b73140a52ba756f40a9121af4 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Wed, 19 Nov 2008 21:10:47 +0100 Subject: gspca: Use the ref counting of v4l2 for disconnection. From: Hans de Goede Priority: normal Signed-off-by: Hans de Goede Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/gspca.c | 25 ++++++------------------- linux/drivers/media/video/gspca/gspca.h | 1 - 2 files changed, 6 insertions(+), 20 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/gspca.c b/linux/drivers/media/video/gspca/gspca.c index 4e3833a00..c08a31f7b 100644 --- a/linux/drivers/media/video/gspca/gspca.c +++ b/linux/drivers/media/video/gspca/gspca.c @@ -30,7 +30,6 @@ #include #include #include -#include #include #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) #include @@ -881,11 +880,11 @@ out: return ret; } -static void gspca_delete(struct kref *kref) +static void gspca_release(struct video_device *vfd) { - struct gspca_dev *gspca_dev = container_of(kref, struct gspca_dev, kref); + struct gspca_dev *gspca_dev = container_of(vfd, struct gspca_dev, vdev); - PDEBUG(D_STREAM, "device deleted"); + PDEBUG(D_STREAM, "device released"); kfree(gspca_dev->usb_buf); kfree(gspca_dev); @@ -918,9 +917,6 @@ static int dev_open(struct inode *inode, struct file *file) gspca_dev->users++; - /* one more user */ - kref_get(&gspca_dev->kref); - file->private_data = gspca_dev; #ifdef GSPCA_DEBUG /* activate the v4l2 debug */ @@ -967,8 +963,6 @@ static int dev_close(struct inode *inode, struct file *file) PDEBUG(D_STREAM, "close done"); - kref_put(&gspca_dev->kref, gspca_delete); - return 0; } @@ -1790,11 +1784,6 @@ out: return ret; } -static void dev_release(struct video_device *vfd) -{ - /* nothing */ -} - static struct file_operations dev_fops = { .owner = THIS_MODULE, .open = dev_open, @@ -1842,7 +1831,7 @@ static struct video_device gspca_template = { .name = "gspca main driver", .fops = &dev_fops, .ioctl_ops = &dev_ioctl_ops, - .release = dev_release, /* mandatory */ + .release = gspca_release, .minor = -1, }; @@ -1880,7 +1869,6 @@ int gspca_dev_probe(struct usb_interface *intf, err("couldn't kzalloc gspca struct"); return -ENOMEM; } - kref_init(&gspca_dev->kref); gspca_dev->usb_buf = kmalloc(USB_BUF_SZ, GFP_KERNEL); if (!gspca_dev->usb_buf) { err("out of memory"); @@ -1929,7 +1917,8 @@ int gspca_dev_probe(struct usb_interface *intf, PDEBUG(D_PROBE, "probe ok"); return 0; out: - kref_put(&gspca_dev->kref, gspca_delete); + kfree(gspca_dev->usb_buf); + kfree(gspca_dev); return ret; } EXPORT_SYMBOL(gspca_dev_probe); @@ -1952,8 +1941,6 @@ void gspca_disconnect(struct usb_interface *intf) gspca_dev->present = 0; gspca_dev->streaming = 0; - kref_put(&gspca_dev->kref, gspca_delete); - PDEBUG(D_PROBE, "disconnect complete"); } EXPORT_SYMBOL(gspca_disconnect); diff --git a/linux/drivers/media/video/gspca/gspca.h b/linux/drivers/media/video/gspca/gspca.h index 4ff9b8105..dcfc6fd2a 100644 --- a/linux/drivers/media/video/gspca/gspca.h +++ b/linux/drivers/media/video/gspca/gspca.h @@ -127,7 +127,6 @@ struct gspca_dev { struct video_device vdev; /* !! must be the first item */ struct module *module; /* subdriver handling the device */ struct usb_device *dev; - struct kref kref; struct file *capt_file; /* file doing video capture */ struct cam cam; /* device information */ -- cgit v1.2.3 From cd230a7b1b0e074e4dcf674043ffc64bee3bdab2 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Wed, 19 Nov 2008 21:16:26 +0100 Subject: gspca: Remove the event counter and simplify the frame wait. From: Hans de Goede Priority: normal Signed-off-by: Hans de Goede Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/gspca.c | 40 ++++++++++----------------------- linux/drivers/media/video/gspca/gspca.h | 1 - 2 files changed, 12 insertions(+), 29 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/gspca.c b/linux/drivers/media/video/gspca/gspca.c index c08a31f7b..8e6af151e 100644 --- a/linux/drivers/media/video/gspca/gspca.c +++ b/linux/drivers/media/video/gspca/gspca.c @@ -310,7 +310,6 @@ struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev, frame->v4l2_buf.bytesused = frame->data_end - frame->data; frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_QUEUED; frame->v4l2_buf.flags |= V4L2_BUF_FLAG_DONE; - atomic_inc(&gspca_dev->nevent); wake_up_interruptible(&gspca_dev->wq); /* event = new frame */ i = (gspca_dev->fr_i + 1) % gspca_dev->nframes; gspca_dev->fr_i = i; @@ -404,7 +403,6 @@ static int frame_alloc(struct gspca_dev *gspca_dev, gspca_dev->fr_i = gspca_dev->fr_o = gspca_dev->fr_q = 0; gspca_dev->last_packet_type = DISCARD_PACKET; gspca_dev->sequence = 0; - atomic_set(&gspca_dev->nevent, 0); return 0; } @@ -638,7 +636,6 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) goto out; } gspca_dev->streaming = 1; - atomic_set(&gspca_dev->nevent, 0); /* some bulk transfers are started by the subdriver */ if (gspca_dev->alt == 0 && gspca_dev->cam.bulk_nurbs == 0) @@ -679,7 +676,6 @@ static int gspca_set_alt0(struct gspca_dev *gspca_dev) static void gspca_stream_off(struct gspca_dev *gspca_dev) { gspca_dev->streaming = 0; - atomic_set(&gspca_dev->nevent, 0); if (gspca_dev->present && gspca_dev->sd_desc->stopN) gspca_dev->sd_desc->stopN(gspca_dev); @@ -1265,7 +1261,6 @@ static int vidioc_streamoff(struct file *file, void *priv, gspca_dev->fr_i = gspca_dev->fr_o = gspca_dev->fr_q = 0; gspca_dev->last_packet_type = DISCARD_PACKET; gspca_dev->sequence = 0; - atomic_set(&gspca_dev->nevent, 0); ret = 0; out: mutex_unlock(&gspca_dev->queue_lock); @@ -1469,33 +1464,22 @@ static int frame_wait(struct gspca_dev *gspca_dev, i = gspca_dev->fr_o; j = gspca_dev->fr_queue[i]; frame = &gspca_dev->frame[j]; - if (frame->v4l2_buf.flags & V4L2_BUF_FLAG_DONE) { - atomic_dec(&gspca_dev->nevent); - goto ok; - } - if (nonblock_ing) /* no frame yet */ - return -EAGAIN; - /* wait till a frame is ready */ - for (;;) { + if (!(frame->v4l2_buf.flags & V4L2_BUF_FLAG_DONE)) { + if (nonblock_ing) + return -EAGAIN; + + /* wait till a frame is ready */ ret = wait_event_interruptible_timeout(gspca_dev->wq, - atomic_read(&gspca_dev->nevent) > 0, - msecs_to_jiffies(3000)); - if (ret <= 0) { - if (ret < 0) - return ret; /* interrupt */ - return -EIO; /* timeout */ - } - atomic_dec(&gspca_dev->nevent); - if (!gspca_dev->streaming || !gspca_dev->present) + (frame->v4l2_buf.flags & V4L2_BUF_FLAG_DONE) || + !gspca_dev->streaming || !gspca_dev->present, + msecs_to_jiffies(3000)); + if (ret < 0) + return ret; + if (ret == 0 || !gspca_dev->streaming || !gspca_dev->present) return -EIO; - i = gspca_dev->fr_o; - j = gspca_dev->fr_queue[i]; - frame = &gspca_dev->frame[j]; - if (frame->v4l2_buf.flags & V4L2_BUF_FLAG_DONE) - break; } -ok: + gspca_dev->fr_o = (i + 1) % gspca_dev->nframes; PDEBUG(D_FRAM, "frame wait q:%d i:%d o:%d", gspca_dev->fr_q, diff --git a/linux/drivers/media/video/gspca/gspca.h b/linux/drivers/media/video/gspca/gspca.h index dcfc6fd2a..15db13296 100644 --- a/linux/drivers/media/video/gspca/gspca.h +++ b/linux/drivers/media/video/gspca/gspca.h @@ -155,7 +155,6 @@ struct gspca_dev { __u16 height; __u32 sequence; /* frame sequence number */ - atomic_t nevent; /* number of frames done */ wait_queue_head_t wq; /* wait queue */ struct mutex usb_lock; /* usb exchange protection */ struct mutex read_lock; /* read protection */ -- cgit v1.2.3 From 85c98bb35cedc8a0d7ca7dc03ac20dfedab23922 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 20 Nov 2008 11:30:26 -0200 Subject: em28xx: fix a race condition with hald From: Mauro Carvalho Chehab Newer versions of hald tries to open it to call QUERYCAP. Due to the lack of a proper locking, it is possible to open the device before it finishes initialization. This patch adds a lock to avoid this risk, and to protect the list of em28xx devices. While here, remove the uneeded BKL lock. Priority: high Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-video.c | 129 ++++++++++++++---------- 1 file changed, 76 insertions(+), 53 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index 88bff6bd7..8cef89ad8 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -73,6 +73,7 @@ MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); static LIST_HEAD(em28xx_devlist); +static DEFINE_MUTEX(em28xx_devlist_mutex); static unsigned int card[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; static unsigned int video_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; @@ -1562,7 +1563,7 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp) struct em28xx_fh *fh; enum v4l2_buf_type fh_type = 0; - lock_kernel(); + mutex_lock(&em28xx_devlist_mutex); list_for_each_entry(h, &em28xx_devlist, devlist) { if (h->vdev->minor == minor) { dev = h; @@ -1578,10 +1579,11 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp) dev = h; } } - if (NULL == dev) { - unlock_kernel(); + mutex_unlock(&em28xx_devlist_mutex); + if (NULL == dev) return -ENODEV; - } + + mutex_lock(&dev->lock); em28xx_videodbg("open minor=%d type=%s users=%d\n", minor, v4l2_type_names[fh_type], dev->users); @@ -1590,6 +1592,7 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp) errCode = em28xx_set_mode(dev, EM28XX_ANALOG_MODE); if (errCode < 0) { em28xx_errdev("Device locked on digital mode. Can't open analog\n"); + mutex_unlock(&dev->lock); return -EBUSY; } #endif @@ -1597,10 +1600,9 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp) fh = kzalloc(sizeof(struct em28xx_fh), GFP_KERNEL); if (!fh) { em28xx_errdev("em28xx-video.c: Out of memory?!\n"); - unlock_kernel(); + mutex_unlock(&dev->lock); return -ENOMEM; } - mutex_lock(&dev->lock); fh->dev = dev; fh->radio = radio; fh->type = fh_type; @@ -1641,7 +1643,6 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp) sizeof(struct em28xx_buffer), fh); mutex_unlock(&dev->lock); - unlock_kernel(); return errCode; } @@ -1939,6 +1940,7 @@ int em28xx_register_extension(struct em28xx_ops *ops) { struct em28xx *dev = NULL; + mutex_lock(&em28xx_devlist_mutex); mutex_lock(&em28xx_extension_devlist_lock); list_add_tail(&ops->next, &em28xx_extension_devlist); list_for_each_entry(dev, &em28xx_devlist, devlist) { @@ -1947,6 +1949,7 @@ int em28xx_register_extension(struct em28xx_ops *ops) } printk(KERN_INFO "Em28xx: Initialized (%s) extension\n", ops->name); mutex_unlock(&em28xx_extension_devlist_lock); + mutex_unlock(&em28xx_devlist_mutex); return 0; } EXPORT_SYMBOL(em28xx_register_extension); @@ -1955,6 +1958,7 @@ void em28xx_unregister_extension(struct em28xx_ops *ops) { struct em28xx *dev = NULL; + mutex_lock(&em28xx_devlist_mutex); list_for_each_entry(dev, &em28xx_devlist, devlist) { if (dev) ops->fini(dev); @@ -1964,6 +1968,7 @@ void em28xx_unregister_extension(struct em28xx_ops *ops) printk(KERN_INFO "Em28xx: Removed (%s) extension\n", ops->name); list_del(&ops->next); mutex_unlock(&em28xx_extension_devlist_lock); + mutex_unlock(&em28xx_devlist_mutex); } EXPORT_SYMBOL(em28xx_unregister_extension); @@ -1988,6 +1993,60 @@ static struct video_device *em28xx_vdev_init(struct em28xx *dev, return vfd; } +static int register_analog_devices(struct em28xx *dev) +{ + int ret; + + /* allocate and fill video video_device struct */ + dev->vdev = em28xx_vdev_init(dev, &em28xx_video_template, "video"); + if (!dev->vdev) { + em28xx_errdev("cannot allocate video_device.\n"); + return -ENODEV; + } + + /* register v4l2 video video_device */ + ret = video_register_device(dev->vdev, VFL_TYPE_GRABBER, + video_nr[dev->devno]); + if (ret) { + em28xx_errdev("unable to register video device (error=%i).\n", + ret); + return ret; + } + + /* Allocate and fill vbi video_device struct */ + dev->vbi_dev = em28xx_vdev_init(dev, &em28xx_video_template, "vbi"); + + /* register v4l2 vbi video_device */ + ret = video_register_device(dev->vbi_dev, VFL_TYPE_VBI, + vbi_nr[dev->devno]); + if (ret < 0) { + em28xx_errdev("unable to register vbi device\n"); + return ret; + } + + if (em28xx_boards[dev->model].radio.type == EM28XX_RADIO) { + dev->radio_dev = em28xx_vdev_init(dev, &em28xx_radio_template, "radio"); + if (!dev->radio_dev) { + em28xx_errdev("cannot allocate video_device.\n"); + return -ENODEV; + } + ret = video_register_device(dev->radio_dev, VFL_TYPE_RADIO, + radio_nr[dev->devno]); + if (ret < 0) { + em28xx_errdev("can't register radio device\n"); + return ret; + } + em28xx_info("Registered radio device as /dev/radio%d\n", + dev->radio_dev->num); + } + + em28xx_info("V4L2 device registered as /dev/video%d and /dev/vbi%d\n", + dev->vdev->num, dev->vbi_dev->num); + + return 0; +} + + /* * em28xx_init_dev() * allocates and inits the device structs, registers i2c bus and v4l device @@ -2066,50 +2125,6 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, return errCode; } - list_add_tail(&dev->devlist, &em28xx_devlist); - - /* allocate and fill video video_device struct */ - dev->vdev = em28xx_vdev_init(dev, &em28xx_video_template, "video"); - if (NULL == dev->vdev) { - em28xx_errdev("cannot allocate video_device.\n"); - goto fail_unreg; - } - - /* register v4l2 video video_device */ - retval = video_register_device(dev->vdev, VFL_TYPE_GRABBER, - video_nr[dev->devno]); - if (retval) { - em28xx_errdev("unable to register video device (error=%i).\n", - retval); - goto fail_unreg; - } - - /* Allocate and fill vbi video_device struct */ - dev->vbi_dev = em28xx_vdev_init(dev, &em28xx_video_template, "vbi"); - /* register v4l2 vbi video_device */ - if (video_register_device(dev->vbi_dev, VFL_TYPE_VBI, - vbi_nr[dev->devno]) < 0) { - em28xx_errdev("unable to register vbi device\n"); - retval = -ENODEV; - goto fail_unreg; - } - - if (em28xx_boards[dev->model].radio.type == EM28XX_RADIO) { - dev->radio_dev = em28xx_vdev_init(dev, &em28xx_radio_template, "radio"); - if (NULL == dev->radio_dev) { - em28xx_errdev("cannot allocate video_device.\n"); - goto fail_unreg; - } - retval = video_register_device(dev->radio_dev, VFL_TYPE_RADIO, - radio_nr[dev->devno]); - if (retval < 0) { - em28xx_errdev("can't register radio device\n"); - goto fail_unreg; - } - em28xx_info("Registered radio device as /dev/radio%d\n", - dev->radio_dev->num); - } - /* init video dma queues */ INIT_LIST_HEAD(&dev->vidq.active); INIT_LIST_HEAD(&dev->vidq.queued); @@ -2139,8 +2154,14 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, video_mux(dev, 0); - em28xx_info("V4L2 device registered as /dev/video%d and /dev/vbi%d\n", - dev->vdev->num, dev->vbi_dev->num); + mutex_lock(&em28xx_devlist_mutex); + list_add_tail(&dev->devlist, &em28xx_devlist); + retval = register_analog_devices(dev); + if (retval < 0) { + em28xx_release_resources(dev); + mutex_unlock(&em28xx_devlist_mutex); + goto fail_reg_devices; + } mutex_lock(&em28xx_extension_devlist_lock); if (!list_empty(&em28xx_extension_devlist)) { @@ -2150,11 +2171,13 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, } } mutex_unlock(&em28xx_extension_devlist_lock); + mutex_unlock(&em28xx_devlist_mutex); return 0; fail_unreg: em28xx_release_resources(dev); +fail_reg_devices: mutex_unlock(&dev->lock); return retval; } -- cgit v1.2.3 From 6247d126d87bc803082fe3023f08c4704e430537 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 20 Nov 2008 13:10:44 -0200 Subject: em28xx: Fix a stupid cut-and-paste error From: Mauro Carvalho Chehab Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-core.c b/linux/drivers/media/video/em28xx/em28xx-core.c index dacb6cc5a..f3e3acefb 100644 --- a/linux/drivers/media/video/em28xx/em28xx-core.c +++ b/linux/drivers/media/video/em28xx/em28xx-core.c @@ -328,7 +328,7 @@ static int set_ac97_input(struct em28xx *dev) em28xx should point to LINE IN, while AC97 should use VIDEO */ if (amux == EM28XX_AMUX_VIDEO2) - amux = dev->ctl_ainput; + amux = EM28XX_AMUX_VIDEO; /* Mute all entres but the one that were selected */ for (i = 0; i < ARRAY_SIZE(inputs); i++) { -- cgit v1.2.3 From 0ca43157210b3c9c92e6dbaa545f69f4e1ebbc9e Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 20 Nov 2008 13:40:51 -0200 Subject: em28xx: allow specifying audio output From: Mauro Carvalho Chehab Some boards use different AC97 setups for output. This patch adds the capability of specifying the output to be used. Currently, only one output is selected, but the better is to allow user to select it via a mixer, on alsa driver. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-core.c | 24 ++++++++++---- linux/drivers/media/video/em28xx/em28xx-video.c | 44 +++++++++++++++++++------ linux/drivers/media/video/em28xx/em28xx.h | 10 ++++++ 3 files changed, 61 insertions(+), 17 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-core.c b/linux/drivers/media/video/em28xx/em28xx-core.c index f3e3acefb..bc40f2e33 100644 --- a/linux/drivers/media/video/em28xx/em28xx-core.c +++ b/linux/drivers/media/video/em28xx/em28xx-core.c @@ -388,9 +388,17 @@ static int em28xx_set_audio_source(struct em28xx *dev) return ret; } +static int outputs[] = { + [EM28XX_AOUT_MASTER] = AC97_MASTER_VOL, + [EM28XX_AOUT_LINE] = AC97_LINE_LEVEL_VOL, + [EM28XX_AOUT_MONO] = AC97_MASTER_MONO_VOL, + [EM28XX_AOUT_LFE] = AC97_LFE_MASTER_VOL, + [EM28XX_AOUT_SURR] = AC97_SURR_MASTER_VOL, +}; + int em28xx_audio_analog_set(struct em28xx *dev) { - int ret; + int ret, i; u8 xclk = 0x07; if (!dev->audio_mode.has_audio) @@ -400,11 +408,13 @@ int em28xx_audio_analog_set(struct em28xx *dev) It would be possible to use also line output. */ if (dev->audio_mode.ac97 != EM28XX_NO_AC97) { - /* Mute */ - ret = em28xx_write_ac97(dev, AC97_MASTER_VOL, 0x8000); - - if (ret < 0) - return ret; + /* Mute all outputs */ + for (i = 0; i < ARRAY_SIZE(outputs); i++) { + ret = em28xx_write_ac97(dev, outputs[i], 0x8000); + if (ret < 0) + em28xx_warn("couldn't setup AC97 register %d\n", + outputs[i]); + } } if (dev->has_12mhz_i2s) @@ -433,7 +443,7 @@ int em28xx_audio_analog_set(struct em28xx *dev) vol |= 0x8000; /* Sets volume */ - ret = em28xx_write_ac97(dev, AC97_MASTER_VOL, vol); + ret = em28xx_write_ac97(dev, outputs[dev->ctl_aoutput], vol); } return ret; diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index 8cef89ad8..70a43e13e 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -571,6 +571,7 @@ static void video_mux(struct em28xx *dev, int index) route.output = 0; dev->ctl_input = index; dev->ctl_ainput = INPUT(index)->amux; + dev->ctl_aoutput = INPUT(index)->aout; em28xx_i2c_call_clients(dev, VIDIOC_INT_S_VIDEO_ROUTING, &route); @@ -933,20 +934,38 @@ static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) { struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; - unsigned int index = a->index; - if (a->index > 1) - return -EINVAL; - - index = dev->ctl_ainput; - - if (index == 0) + switch (a->index) { + case EM28XX_AMUX_VIDEO: strcpy(a->name, "Television"); - else + break; + case EM28XX_AMUX_LINE_IN: strcpy(a->name, "Line In"); + break; + case EM28XX_AMUX_VIDEO2: + strcpy(a->name, "Television alt"); + break; + case EM28XX_AMUX_PHONE: + strcpy(a->name, "Phone"); + break; + case EM28XX_AMUX_MIC: + strcpy(a->name, "Mic"); + break; + case EM28XX_AMUX_CD: + strcpy(a->name, "CD"); + break; + case EM28XX_AMUX_AUX: + strcpy(a->name, "Aux"); + break; + case EM28XX_AMUX_PCM_OUT: + strcpy(a->name, "PCM"); + break; + default: + return -EINVAL; + } + a->index = dev->ctl_ainput; a->capability = V4L2_AUDCAP_STEREO; - a->index = index; return 0; } @@ -956,9 +975,14 @@ static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a) struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; +#if 0 + /* Doesn't allow manual routing */ if (a->index != dev->ctl_ainput) return -EINVAL; - +#else + dev->ctl_ainput = INPUT(a->index)->amux; + dev->ctl_aoutput = INPUT(a->index)->aout; +#endif return 0; } diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index 9f96128df..2d74b5c5c 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -296,10 +296,19 @@ enum em28xx_amux { EM28XX_AMUX_PCM_OUT, }; +enum em28xx_aout { + EM28XX_AOUT_MASTER = 0, /* should be the default */ + EM28XX_AOUT_LINE, + EM28XX_AOUT_MONO, + EM28XX_AOUT_LFE, + EM28XX_AOUT_SURR, +}; + struct em28xx_input { enum enum28xx_itype type; unsigned int vmux; enum em28xx_amux amux; + enum em28xx_aout aout; }; #define INPUT(nr) (&em28xx_boards[dev->model].input[nr]) @@ -459,6 +468,7 @@ struct em28xx { int ctl_freq; /* selected frequency */ unsigned int ctl_input; /* selected input */ unsigned int ctl_ainput;/* selected audio input */ + unsigned int ctl_aoutput;/* selected audio output */ int mute; int volume; /* frame properties */ -- cgit v1.2.3 From 9c67fb052ee5c718868e6b9b422cdaf4e001dec4 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 20 Nov 2008 13:49:33 -0200 Subject: em28xx: Add detection of Sigmatel Stac97xx series of AC97 devices From: Mauro Carvalho Chehab Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-core.c | 6 ++++++ linux/drivers/media/video/em28xx/em28xx.h | 1 + 2 files changed, 7 insertions(+) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-core.c b/linux/drivers/media/video/em28xx/em28xx-core.c index bc40f2e33..711584515 100644 --- a/linux/drivers/media/video/em28xx/em28xx-core.c +++ b/linux/drivers/media/video/em28xx/em28xx-core.c @@ -520,6 +520,8 @@ int em28xx_audio_setup(struct em28xx *dev) /* Try to identify what audio processor we have */ if ((vid == 0xffffffff) && (feat == 0x6a90)) dev->audio_mode.ac97 = EM28XX_AC97_EM202; + else if ((vid >> 8) == 0x838476) + dev->audio_mode.ac97 = EM28XX_AC97_SIGMATEL; init_audio: /* Reports detected AC97 processor */ @@ -530,6 +532,10 @@ init_audio: case EM28XX_AC97_EM202: em28xx_info("Empia 202 AC97 audio processor detected\n"); break; + case EM28XX_AC97_SIGMATEL: + em28xx_info("Sigmatel audio processor detected(stac 97%02x)\n", + dev->audio_mode.ac97_vendor_id & 0xff); + break; case EM28XX_AC97_OTHER: em28xx_warn("Unknown AC97 audio processor detected!\n"); break; diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index 2d74b5c5c..7de9afb0e 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -260,6 +260,7 @@ enum enum28xx_itype { enum em28xx_ac97_mode { EM28XX_NO_AC97 = 0, EM28XX_AC97_EM202, + EM28XX_AC97_SIGMATEL, EM28XX_AC97_OTHER, }; -- cgit v1.2.3 From 28e0ca25a6ca00f5568a1e5dcb884ab090595bc0 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 20 Nov 2008 14:39:39 -0200 Subject: Allow opening more than one output at the same time From: Mauro Carvalho Chehab Some devices use more than one AC97 outputs. This patch allows such devices to properly work. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-core.c | 33 +++++++++++++++---------- linux/drivers/media/video/em28xx/em28xx-video.c | 6 +++++ linux/drivers/media/video/em28xx/em28xx.h | 10 ++++---- 3 files changed, 31 insertions(+), 18 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-core.c b/linux/drivers/media/video/em28xx/em28xx-core.c index 711584515..eb8515895 100644 --- a/linux/drivers/media/video/em28xx/em28xx-core.c +++ b/linux/drivers/media/video/em28xx/em28xx-core.c @@ -304,12 +304,12 @@ static int em28xx_write_ac97(struct em28xx *dev, u8 reg, u16 val) return 0; } -struct em28xx_input_table { - enum em28xx_amux amux; +struct em28xx_vol_table { + enum em28xx_amux mux; u8 reg; }; -static struct em28xx_input_table inputs[] = { +static struct em28xx_vol_table inputs[] = { { EM28XX_AMUX_VIDEO, AC97_VIDEO_VOL }, { EM28XX_AMUX_LINE_IN, AC97_LINEIN_VOL }, { EM28XX_AMUX_PHONE, AC97_PHONE_VOL }, @@ -332,7 +332,7 @@ static int set_ac97_input(struct em28xx *dev) /* Mute all entres but the one that were selected */ for (i = 0; i < ARRAY_SIZE(inputs); i++) { - if (amux == inputs[i].amux) + if (amux == inputs[i].mux) ret = em28xx_write_ac97(dev, inputs[i].reg, 0x0808); else ret = em28xx_write_ac97(dev, inputs[i].reg, 0x8000); @@ -388,12 +388,12 @@ static int em28xx_set_audio_source(struct em28xx *dev) return ret; } -static int outputs[] = { - [EM28XX_AOUT_MASTER] = AC97_MASTER_VOL, - [EM28XX_AOUT_LINE] = AC97_LINE_LEVEL_VOL, - [EM28XX_AOUT_MONO] = AC97_MASTER_MONO_VOL, - [EM28XX_AOUT_LFE] = AC97_LFE_MASTER_VOL, - [EM28XX_AOUT_SURR] = AC97_SURR_MASTER_VOL, +struct em28xx_vol_table outputs[] = { + { EM28XX_AOUT_MASTER, AC97_MASTER_VOL }, + { EM28XX_AOUT_LINE, AC97_LINE_LEVEL_VOL }, + { EM28XX_AOUT_MONO, AC97_MASTER_MONO_VOL }, + { EM28XX_AOUT_LFE, AC97_LFE_MASTER_VOL }, + { EM28XX_AOUT_SURR, AC97_SURR_MASTER_VOL }, }; int em28xx_audio_analog_set(struct em28xx *dev) @@ -410,10 +410,10 @@ int em28xx_audio_analog_set(struct em28xx *dev) if (dev->audio_mode.ac97 != EM28XX_NO_AC97) { /* Mute all outputs */ for (i = 0; i < ARRAY_SIZE(outputs); i++) { - ret = em28xx_write_ac97(dev, outputs[i], 0x8000); + ret = em28xx_write_ac97(dev, outputs[i].reg, 0x8000); if (ret < 0) em28xx_warn("couldn't setup AC97 register %d\n", - outputs[i]); + outputs[i].reg); } } @@ -443,7 +443,14 @@ int em28xx_audio_analog_set(struct em28xx *dev) vol |= 0x8000; /* Sets volume */ - ret = em28xx_write_ac97(dev, outputs[dev->ctl_aoutput], vol); + for (i = 0; i < ARRAY_SIZE(outputs); i++) { + if (dev->ctl_aoutput & outputs[i].mux) + ret = em28xx_write_ac97(dev, outputs[i].reg, + vol); + if (ret < 0) + em28xx_warn("couldn't setup AC97 register %d\n", + outputs[i].reg); + } } return ret; diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index 70a43e13e..e585780c8 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -573,6 +573,9 @@ static void video_mux(struct em28xx *dev, int index) dev->ctl_ainput = INPUT(index)->amux; dev->ctl_aoutput = INPUT(index)->aout; + if (!dev->ctl_aoutput) + dev->ctl_aoutput = EM28XX_AOUT_MASTER; + em28xx_i2c_call_clients(dev, VIDIOC_INT_S_VIDEO_ROUTING, &route); if (dev->has_msp34xx) { @@ -982,6 +985,9 @@ static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a) #else dev->ctl_ainput = INPUT(a->index)->amux; dev->ctl_aoutput = INPUT(a->index)->aout; + + if (!dev->ctl_aoutput) + dev->ctl_aoutput = EM28XX_AOUT_MASTER; #endif return 0; } diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index 7de9afb0e..151668c60 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -298,11 +298,11 @@ enum em28xx_amux { }; enum em28xx_aout { - EM28XX_AOUT_MASTER = 0, /* should be the default */ - EM28XX_AOUT_LINE, - EM28XX_AOUT_MONO, - EM28XX_AOUT_LFE, - EM28XX_AOUT_SURR, + EM28XX_AOUT_MASTER = 1 << 0, + EM28XX_AOUT_LINE = 1 << 1, + EM28XX_AOUT_MONO = 1 << 2, + EM28XX_AOUT_LFE = 1 << 3, + EM28XX_AOUT_SURR = 1 << 4, }; struct em28xx_input { -- cgit v1.2.3 From 5b08829d068b1cfdf62e0920618a5290101c3d45 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 20 Nov 2008 14:43:05 -0200 Subject: em28xx: fix Pixelview PlayTV board entry From: Mauro Carvalho Chehab Pixelview uses a Sigmatel stac codec. It has an external line out pin, connected to AC97_MASTER_VOL. It also provides I2S output, but using a different volume for this (AC97_MONO_VOL). Now that AC97 setup is finally fixed, such boards will work better. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-cards.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index d2967c434..1a1dd2085 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -848,7 +848,9 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, - .amux = EM28XX_AMUX_LINE_IN, + .amux = EM28XX_AMUX_VIDEO, + .aout = EM28XX_AOUT_MONO | /* I2S */ + EM28XX_AOUT_MASTER, /* Line out pin */ }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, -- cgit v1.2.3 From cf4b37dddc28c17056179e3b7ecb15660952db7e Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 20 Nov 2008 19:38:29 -0200 Subject: em28xx: devices with xc2028/3028 don't have tda9887 From: Mauro Carvalho Chehab This patch cleans up the entries of xc2028/3028, since those devices don't need or use a tda9887. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-cards.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 1a1dd2085..a9ae6ea6e 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -588,7 +588,6 @@ struct em28xx_board em28xx_boards[] = { [EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950] = { .name = "Hauppauge WinTV HVR 950", .vchannels = 3, - .tda9887_conf = TDA9887_PRESENT, .tuner_type = TUNER_XC2028, .mts_firmware = 1, .has_12mhz_i2s = 1, @@ -612,7 +611,6 @@ struct em28xx_board em28xx_boards[] = { [EM2880_BOARD_PINNACLE_PCTV_HD_PRO] = { .name = "Pinnacle PCTV HD Pro Stick", .vchannels = 3, - .tda9887_conf = TDA9887_PRESENT, .tuner_type = TUNER_XC2028, .mts_firmware = 1, .has_12mhz_i2s = 1, @@ -636,7 +634,6 @@ struct em28xx_board em28xx_boards[] = { [EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600] = { .name = "AMD ATI TV Wonder HD 600", .vchannels = 3, - .tda9887_conf = TDA9887_PRESENT, .tuner_type = TUNER_XC2028, .mts_firmware = 1, .has_12mhz_i2s = 1, @@ -660,7 +657,6 @@ struct em28xx_board em28xx_boards[] = { [EM2880_BOARD_TERRATEC_HYBRID_XS] = { .name = "Terratec Hybrid XS", .vchannels = 3, - .tda9887_conf = TDA9887_PRESENT, .tuner_type = TUNER_XC2028, .decoder = EM28XX_TVP5150, .has_dvb = 1, @@ -684,7 +680,6 @@ struct em28xx_board em28xx_boards[] = { [EM2880_BOARD_TERRATEC_PRODIGY_XS] = { .name = "Terratec Prodigy XS", .vchannels = 3, - .tda9887_conf = TDA9887_PRESENT, .tuner_type = TUNER_XC2028, .decoder = EM28XX_TVP5150, .input = { { -- cgit v1.2.3 From 629cb47b4af820e76773eac137150742487afbfb Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 20 Nov 2008 19:47:37 -0200 Subject: em28xx: fix a regression caused by changeset 9630 From: Mauro Carvalho Chehab If removing and reiserting the driver on some devices, tuner type will be unset at the second time. This patch fixes this issue. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-cards.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index a9ae6ea6e..9177ea54b 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -1806,7 +1806,8 @@ void em28xx_card_setup(struct em28xx *dev) em28xx_set_model(dev); dev->tuner_type = em28xx_boards[dev->model].tuner_type; - dev->tuner_addr = em28xx_boards[dev->model].tuner_addr; + if (em28xx_boards[dev->model].tuner_addr) + dev->tuner_addr = em28xx_boards[dev->model].tuner_addr; /* request some modules */ switch (dev->model) { -- cgit v1.2.3 From 520e23e06771404dd24313d5debf22dca73746ba Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 21 Nov 2008 20:00:55 -0200 Subject: bttv: fix some entries on Phytec boards and add missing ones From: Dirk Heer This Patch does modify the bttv-cards.c and bttc.h so that the driver supports VD-011, VD-012, VD-012-X1 and VD-012-X2 Framegrabber from Phytec Messtechnik GmbH. Priority: normal Signed-off-by: Dirk Heer Signed-off-by: Mauro Carvalho Chehab --- linux/Documentation/video4linux/CARDLIST.bttv | 7 +++- linux/drivers/media/video/bt8xx/bttv-cards.c | 60 ++++++++++++++++++++++++--- linux/drivers/media/video/bt8xx/bttv.h | 8 +++- 3 files changed, 65 insertions(+), 10 deletions(-) (limited to 'linux') diff --git a/linux/Documentation/video4linux/CARDLIST.bttv b/linux/Documentation/video4linux/CARDLIST.bttv index 60ba66836..0d93fa1ac 100644 --- a/linux/Documentation/video4linux/CARDLIST.bttv +++ b/linux/Documentation/video4linux/CARDLIST.bttv @@ -104,8 +104,8 @@ 103 -> Grand X-Guard / Trust 814PCI [0304:0102] 104 -> Nebula Electronics DigiTV [0071:0101] 105 -> ProVideo PV143 [aa00:1430,aa00:1431,aa00:1432,aa00:1433,aa03:1433] -106 -> PHYTEC VD-009-X1 MiniDIN (bt878) -107 -> PHYTEC VD-009-X1 Combi (bt878) +106 -> PHYTEC VD-009-X1 VD-011 MiniDIN (bt878) +107 -> PHYTEC VD-009-X1 VD-011 Combi (bt878) 108 -> PHYTEC VD-009 MiniDIN (bt878) 109 -> PHYTEC VD-009 Combi (bt878) 110 -> IVC-100 [ff00:a132] @@ -151,3 +151,6 @@ 150 -> Geovision GV-600 [008a:763c] 151 -> Kozumi KTV-01C 152 -> Encore ENL TV-FM-2 [1000:1801] +153 -> PHYTEC VD-012 (bt878) +154 -> PHYTEC VD-012-X1 (bt878) +155 -> PHYTEC VD-012-X2 (bt878) diff --git a/linux/drivers/media/video/bt8xx/bttv-cards.c b/linux/drivers/media/video/bt8xx/bttv-cards.c index d67e127f1..a00228893 100644 --- a/linux/drivers/media/video/bt8xx/bttv-cards.c +++ b/linux/drivers/media/video/bt8xx/bttv-cards.c @@ -2240,9 +2240,9 @@ struct tvcard bttv_tvcards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, - [BTTV_BOARD_VD009X1_MINIDIN] = { + [BTTV_BOARD_VD009X1_VD011_MINIDIN] = { /* M.Klahr@phytec.de */ - .name = "PHYTEC VD-009-X1 MiniDIN (bt878)", + .name = "PHYTEC VD-009-X1 VD-011 MiniDIN (bt878)", .video_inputs = 4, .audio_inputs = 0, .tuner = UNSET, /* card has no tuner */ @@ -2250,14 +2250,14 @@ struct tvcard bttv_tvcards[] = { .gpiomask = 0x00, .muxsel = { 2, 3, 1, 0 }, .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */ - .needs_tvaudio = 1, + .needs_tvaudio = 0, .pll = PLL_28, .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, - [BTTV_BOARD_VD009X1_COMBI] = { - .name = "PHYTEC VD-009-X1 Combi (bt878)", + [BTTV_BOARD_VD009X1_VD011_COMBI] = { + .name = "PHYTEC VD-009-X1 VD-011 Combi (bt878)", .video_inputs = 4, .audio_inputs = 0, .tuner = UNSET, /* card has no tuner */ @@ -2265,7 +2265,7 @@ struct tvcard bttv_tvcards[] = { .gpiomask = 0x00, .muxsel = { 2, 3, 1, 1 }, .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */ - .needs_tvaudio = 1, + .needs_tvaudio = 0, .pll = PLL_28, .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, @@ -3093,6 +3093,54 @@ struct tvcard bttv_tvcards[] = { .pll = PLL_28, .has_radio = 1, .has_remote = 1, + }, + [BTTV_BOARD_VD012] = { + /* D.Heer@Phytec.de */ + .name = "PHYTEC VD-012 (bt878)", + .video_inputs = 4, + .audio_inputs = 0, + .tuner = UNSET, /* card has no tuner */ + .svhs = UNSET, /* card has no s-video */ + .gpiomask = 0x00, + .muxsel = { 0, 2, 3, 1 }, + .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */ + .needs_tvaudio = 0, + .pll = PLL_28, + .tuner_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + }, + [BTTV_BOARD_VD012_X1] = { + /* D.Heer@Phytec.de */ + .name = "PHYTEC VD-012-X1 (bt878)", + .video_inputs = 4, + .audio_inputs = 0, + .tuner = UNSET, /* card has no tuner */ + .svhs = 3, + .gpiomask = 0x00, + .muxsel = { 2, 3, 1 }, + .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */ + .needs_tvaudio = 0, + .pll = PLL_28, + .tuner_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + }, + [BTTV_BOARD_VD012_X2] = { + /* D.Heer@Phytec.de */ + .name = "PHYTEC VD-012-X2 (bt878)", + .video_inputs = 4, + .audio_inputs = 0, + .tuner = UNSET, /* card has no tuner */ + .svhs = 3, + .gpiomask = 0x00, + .muxsel = { 3, 2, 1 }, + .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */ + .needs_tvaudio = 0, + .pll = PLL_28, + .tuner_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, } }; diff --git a/linux/drivers/media/video/bt8xx/bttv.h b/linux/drivers/media/video/bt8xx/bttv.h index 20f027ee4..ed0446a20 100644 --- a/linux/drivers/media/video/bt8xx/bttv.h +++ b/linux/drivers/media/video/bt8xx/bttv.h @@ -131,8 +131,8 @@ #define BTTV_BOARD_XGUARD 0x67 #define BTTV_BOARD_NEBULA_DIGITV 0x68 #define BTTV_BOARD_PV143 0x69 -#define BTTV_BOARD_VD009X1_MINIDIN 0x6a -#define BTTV_BOARD_VD009X1_COMBI 0x6b +#define BTTV_BOARD_VD009X1_VD011_MINIDIN 0x6a +#define BTTV_BOARD_VD009X1_VD011_COMBI 0x6b #define BTTV_BOARD_VD009_MINIDIN 0x6c #define BTTV_BOARD_VD009_COMBI 0x6d #define BTTV_BOARD_IVC100 0x6e @@ -178,6 +178,10 @@ #define BTTV_BOARD_GEOVISION_GV600 0x96 #define BTTV_BOARD_KOZUMI_KTV_01C 0x97 #define BTTV_BOARD_ENLTV_FM_2 0x98 +#define BTTV_BOARD_VD012 0x99 +#define BTTV_BOARD_VD012_X1 0x9a +#define BTTV_BOARD_VD012_X2 0x9b + /* more card-specific defines */ #define PT2254_L_CHANNEL 0x10 -- cgit v1.2.3 From ad1f215cdb537422f9af722be3eb8523720355c4 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 20 Nov 2008 09:37:05 +0100 Subject: af9015: fix compilation error From: Hans Verkuil It is much more readable to swap the byteorder in the switch rather than in each case statement. In addition, this fixes a compile error when compiled for 2.6.16. Priority: normal Signed-off-by: Hans Verkuil --- linux/drivers/media/dvb/dvb-usb/af9015.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/dvb/dvb-usb/af9015.c b/linux/drivers/media/dvb/dvb-usb/af9015.c index b2f724fb5..e6e534e04 100644 --- a/linux/drivers/media/dvb/dvb-usb/af9015.c +++ b/linux/drivers/media/dvb/dvb-usb/af9015.c @@ -745,8 +745,8 @@ static int af9015_read_config(struct usb_device *udev) break; } } else { - switch (udev->descriptor.idVendor) { - case cpu_to_le16(USB_VID_LEADTEK): + switch (le16_to_cpu(udev->descriptor.idVendor)) { + case USB_VID_LEADTEK: af9015_properties[i].rc_key_map = af9015_rc_keys_leadtek; af9015_properties[i].rc_key_map_size = @@ -756,7 +756,7 @@ static int af9015_read_config(struct usb_device *udev) af9015_config.ir_table_size = ARRAY_SIZE(af9015_ir_table_leadtek); break; - case cpu_to_le16(USB_VID_VISIONPLUS): + case USB_VID_VISIONPLUS: if (udev->descriptor.idProduct == cpu_to_le16(USB_PID_AZUREWAVE_AD_TU700)) { af9015_properties[i].rc_key_map = @@ -769,7 +769,7 @@ static int af9015_read_config(struct usb_device *udev) ARRAY_SIZE(af9015_ir_table_twinhan); } break; - case cpu_to_le16(USB_VID_KWORLD_2): + case USB_VID_KWORLD_2: /* TODO: use correct rc keys */ af9015_properties[i].rc_key_map = af9015_rc_keys_twinhan; @@ -782,7 +782,7 @@ static int af9015_read_config(struct usb_device *udev) /* Check USB manufacturer and product strings and try to determine correct remote in case of chip vendor reference IDs are used. */ - case cpu_to_le16(USB_VID_AFATECH): + case USB_VID_AFATECH: memset(manufacturer, 0, sizeof(manufacturer)); usb_string(udev, udev->descriptor.iManufacturer, manufacturer, sizeof(manufacturer)); @@ -810,7 +810,7 @@ static int af9015_read_config(struct usb_device *udev) ARRAY_SIZE(af9015_ir_table_msi); } break; - case cpu_to_le16(USB_VID_AVERMEDIA): + case USB_VID_AVERMEDIA: af9015_properties[i].rc_key_map = af9015_rc_keys_avermedia; af9015_properties[i].rc_key_map_size = -- cgit v1.2.3 From ab89c89c05b4b66ddf9f7335cf33132473dd8479 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Fri, 21 Nov 2008 08:34:15 +0100 Subject: gspca: Do the sn9c105 - ov7660 work again in sonixj. From: Jean-Francois Moine The sn9c105 and sn9c120 bridges must be differently programmed for the ov7660. Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/sonixj.c | 48 +++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 16 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/sonixj.c b/linux/drivers/media/video/gspca/sonixj.c index 93cc65b5f..d2892218f 100644 --- a/linux/drivers/media/video/gspca/sonixj.c +++ b/linux/drivers/media/video/gspca/sonixj.c @@ -850,11 +850,14 @@ static int configure_gpio(struct gspca_dev *gspca_dev, #if 1 /*jfm: from win trace */ case SENSOR_OV7660: - reg_w1(gspca_dev, 0x01, 0x61); - reg_w1(gspca_dev, 0x17, 0x20); - reg_w1(gspca_dev, 0x01, 0x60); - reg_w1(gspca_dev, 0x01, 0x40); - break; + if (sd->bridge == BRIDGE_SN9C120) { + reg_w1(gspca_dev, 0x01, 0x61); + reg_w1(gspca_dev, 0x17, 0x20); + reg_w1(gspca_dev, 0x01, 0x60); + reg_w1(gspca_dev, 0x01, 0x40); + break; + } + /* fall thru */ #endif default: reg_w1(gspca_dev, 0x01, 0x43); @@ -1285,14 +1288,18 @@ static int sd_start(struct gspca_dev *gspca_dev) #if 1 /*jfm: from win trace */ case SENSOR_OV7660: - reg17 = 0xa0; - break; + if (sd->bridge == BRIDGE_SN9C120) { + reg17 = 0xa0; + break; + } + /* fall thru */ #endif default: reg17 = 0x60; break; } reg_w1(gspca_dev, 0x17, reg17); +/* set reg1 was here */ reg_w1(gspca_dev, 0x05, sn9c1xx[5]); reg_w1(gspca_dev, 0x07, sn9c1xx[7]); reg_w1(gspca_dev, 0x06, sn9c1xx[6]); @@ -1306,8 +1313,11 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_w1(gspca_dev, 0x99, 0x60); break; case SENSOR_OV7660: - reg_w1(gspca_dev, 0x9a, 0x05); - break; + if (sd->bridge == BRIDGE_SN9C120) { + reg_w1(gspca_dev, 0x9a, 0x05); + break; + } + /* fall thru */ default: reg_w1(gspca_dev, 0x9a, 0x08); reg_w1(gspca_dev, 0x99, 0x59); @@ -1316,10 +1326,10 @@ static int sd_start(struct gspca_dev *gspca_dev) mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; if (mode) - reg1 = 0x46; /* 320 clk 48Mhz */ + reg1 = 0x46; /* 320x240: clk 48Mhz, video trf enable */ else - reg1 = 0x06; /* 640 clk 24Mz */ - reg17 = 0x61; + reg1 = 0x06; /* 640x480: clk 24Mhz, video trf enable */ + reg17 = 0x61; /* 0x:20: enable sensor clock */ switch (sd->sensor) { case SENSOR_HV7131R: hv7131R_InitSensor(gspca_dev); @@ -1363,9 +1373,15 @@ static int sd_start(struct gspca_dev *gspca_dev) /* reg17 = 0x21; * 320 */ /* reg1 = 0x44; */ /* reg1 = 0x46; (done) */ - } else { - reg17 = 0xa2; /* 640 */ - reg1 = 0x44; + } else { /* 640 */ + if (sd->bridge == BRIDGE_SN9C120) { + reg17 = 0xa2; + reg1 = 0x44; /* 48 Mhz, video trf eneble */ + } else { + reg17 = 0x22; + reg1 = 0x06; /* 24 Mhz, video trf eneble + * inverse power down */ + } } break; } @@ -1393,6 +1409,7 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_w1(gspca_dev, 0x18, reg18); reg_w1(gspca_dev, 0x17, reg17); + reg_w1(gspca_dev, 0x01, reg1); switch (sd->sensor) { case SENSOR_MI0360: setinfrared(sd); @@ -1411,7 +1428,6 @@ static int sd_start(struct gspca_dev *gspca_dev) break; } setautogain(gspca_dev); - reg_w1(gspca_dev, 0x01, reg1); return 0; } -- cgit v1.2.3 From cbd9b1715c3df1c998013c4606b32512a4ac1beb Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Fri, 21 Nov 2008 23:23:22 -0500 Subject: cx18: Adjust outgoing mailbox timeouts and remove statistics logging From: Andy Walls 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. Priority: normal Signed-off-by: Andy Walls --- linux/drivers/media/video/cx18/cx18-driver.c | 2 -- linux/drivers/media/video/cx18/cx18-driver.h | 8 ------- linux/drivers/media/video/cx18/cx18-io.c | 14 ------------ linux/drivers/media/video/cx18/cx18-io.h | 4 ---- linux/drivers/media/video/cx18/cx18-ioctl.c | 1 - linux/drivers/media/video/cx18/cx18-mailbox.c | 33 +++++++-------------------- 6 files changed, 8 insertions(+), 54 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/cx18/cx18-driver.c b/linux/drivers/media/video/cx18/cx18-driver.c index 15c1fb9b2..1fa9a670b 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.c +++ b/linux/drivers/media/video/cx18/cx18-driver.c @@ -839,7 +839,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); @@ -956,7 +955,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/linux/drivers/media/video/cx18/cx18-driver.h b/linux/drivers/media/video/cx18/cx18-driver.h index f06290d32..6e4c90e6c 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.h +++ b/linux/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/linux/drivers/media/video/cx18/cx18-io.c b/linux/drivers/media/video/cx18/cx18-io.c index c6f1d0d7f..ec5b3d7bc 100644 --- a/linux/drivers/media/video/cx18/cx18-io.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-io.h b/linux/drivers/media/video/cx18/cx18-io.h index 73321fb4c..e6716dcb1 100644 --- a/linux/drivers/media/video/cx18/cx18-io.h +++ b/linux/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/linux/drivers/media/video/cx18/cx18-ioctl.c b/linux/drivers/media/video/cx18/cx18-ioctl.c index 20c7650d7..cc3774012 100644 --- a/linux/drivers/media/video/cx18/cx18-ioctl.c +++ b/linux/drivers/media/video/cx18/cx18-ioctl.c @@ -857,7 +857,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/linux/drivers/media/video/cx18/cx18-mailbox.c b/linux/drivers/media/video/cx18/cx18-mailbox.c index a1060880a..322402185 100644 --- a/linux/drivers/media/video/cx18/cx18-mailbox.c +++ b/linux/drivers/media/video/cx18/cx18-mailbox.c @@ -468,13 +468,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); @@ -529,7 +522,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); @@ -539,8 +532,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; @@ -554,10 +547,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); @@ -567,27 +558,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 7dacf773190ee5feda4783ac5106b4c7ad031ffb Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Fri, 21 Nov 2008 23:37:34 -0500 Subject: cx18: Copyright attribution update for files modified by awalls From: Andy Walls Add copyright attribution for files modified by awalls in 2008 Priority: normal Signed-off-by: Andy Walls --- linux/drivers/media/video/cx18/cx18-av-audio.c | 1 + linux/drivers/media/video/cx18/cx18-av-core.c | 1 + linux/drivers/media/video/cx18/cx18-av-core.h | 1 + linux/drivers/media/video/cx18/cx18-av-firmware.c | 1 + linux/drivers/media/video/cx18/cx18-cards.c | 1 + linux/drivers/media/video/cx18/cx18-cards.h | 1 + linux/drivers/media/video/cx18/cx18-driver.h | 1 + linux/drivers/media/video/cx18/cx18-dvb.c | 1 + linux/drivers/media/video/cx18/cx18-fileops.c | 1 + linux/drivers/media/video/cx18/cx18-firmware.c | 1 + linux/drivers/media/video/cx18/cx18-gpio.c | 1 + linux/drivers/media/video/cx18/cx18-gpio.h | 1 + linux/drivers/media/video/cx18/cx18-i2c.c | 1 + linux/drivers/media/video/cx18/cx18-ioctl.c | 1 + linux/drivers/media/video/cx18/cx18-ioctl.h | 1 + linux/drivers/media/video/cx18/cx18-irq.c | 1 + linux/drivers/media/video/cx18/cx18-irq.h | 1 + linux/drivers/media/video/cx18/cx18-mailbox.c | 1 + linux/drivers/media/video/cx18/cx18-mailbox.h | 1 + linux/drivers/media/video/cx18/cx18-queue.c | 1 + linux/drivers/media/video/cx18/cx18-queue.h | 1 + linux/drivers/media/video/cx18/cx18-scb.c | 1 + linux/drivers/media/video/cx18/cx18-scb.h | 1 + linux/drivers/media/video/cx18/cx18-streams.c | 1 + linux/drivers/media/video/cx18/cx18-streams.h | 1 + 25 files changed, 25 insertions(+) (limited to 'linux') diff --git a/linux/drivers/media/video/cx18/cx18-av-audio.c b/linux/drivers/media/video/cx18/cx18-av-audio.c index 486cad0c2..fd85b9b2d 100644 --- a/linux/drivers/media/video/cx18/cx18-av-audio.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-av-core.c b/linux/drivers/media/video/cx18/cx18-av-core.c index 13a4adaa1..40ea6fde6 100644 --- a/linux/drivers/media/video/cx18/cx18-av-core.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-av-core.h b/linux/drivers/media/video/cx18/cx18-av-core.h index 455761fce..cf68a6039 100644 --- a/linux/drivers/media/video/cx18/cx18-av-core.h +++ b/linux/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/linux/drivers/media/video/cx18/cx18-av-firmware.c b/linux/drivers/media/video/cx18/cx18-av-firmware.c index cf52e08c2..c64fd0a05 100644 --- a/linux/drivers/media/video/cx18/cx18-av-firmware.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-cards.c b/linux/drivers/media/video/cx18/cx18-cards.c index 7345fc09d..23bd871b7 100644 --- a/linux/drivers/media/video/cx18/cx18-cards.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-cards.h b/linux/drivers/media/video/cx18/cx18-cards.h index 32155f6e6..a54aae9ed 100644 --- a/linux/drivers/media/video/cx18/cx18-cards.h +++ b/linux/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/linux/drivers/media/video/cx18/cx18-driver.h b/linux/drivers/media/video/cx18/cx18-driver.h index 6e4c90e6c..ca1f43781 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.h +++ b/linux/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/linux/drivers/media/video/cx18/cx18-dvb.c b/linux/drivers/media/video/cx18/cx18-dvb.c index b74253b13..034e09a37 100644 --- a/linux/drivers/media/video/cx18/cx18-dvb.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-fileops.c b/linux/drivers/media/video/cx18/cx18-fileops.c index e884271b8..45b5f402e 100644 --- a/linux/drivers/media/video/cx18/cx18-fileops.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-firmware.c b/linux/drivers/media/video/cx18/cx18-firmware.c index fdea4b752..8eac84314 100644 --- a/linux/drivers/media/video/cx18/cx18-firmware.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-gpio.c b/linux/drivers/media/video/cx18/cx18-gpio.c index a1625c8e0..1a99329f3 100644 --- a/linux/drivers/media/video/cx18/cx18-gpio.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-gpio.h b/linux/drivers/media/video/cx18/cx18-gpio.h index beb7424b9..39ffccc19 100644 --- a/linux/drivers/media/video/cx18/cx18-gpio.h +++ b/linux/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/linux/drivers/media/video/cx18/cx18-i2c.c b/linux/drivers/media/video/cx18/cx18-i2c.c index 222d5b83a..c0a79ecd4 100644 --- a/linux/drivers/media/video/cx18/cx18-i2c.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-ioctl.c b/linux/drivers/media/video/cx18/cx18-ioctl.c index cc3774012..5d9c1146e 100644 --- a/linux/drivers/media/video/cx18/cx18-ioctl.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-ioctl.h b/linux/drivers/media/video/cx18/cx18-ioctl.h index 2222f679d..08fe24e95 100644 --- a/linux/drivers/media/video/cx18/cx18-ioctl.h +++ b/linux/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/linux/drivers/media/video/cx18/cx18-irq.c b/linux/drivers/media/video/cx18/cx18-irq.c index 34e25373e..8c22637f4 100644 --- a/linux/drivers/media/video/cx18/cx18-irq.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-irq.h b/linux/drivers/media/video/cx18/cx18-irq.h index 466cb0c25..b94ca6019 100644 --- a/linux/drivers/media/video/cx18/cx18-irq.h +++ b/linux/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/linux/drivers/media/video/cx18/cx18-mailbox.c b/linux/drivers/media/video/cx18/cx18-mailbox.c index 322402185..420325be4 100644 --- a/linux/drivers/media/video/cx18/cx18-mailbox.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-mailbox.h b/linux/drivers/media/video/cx18/cx18-mailbox.h index 39da39d47..35104458e 100644 --- a/linux/drivers/media/video/cx18/cx18-mailbox.h +++ b/linux/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/linux/drivers/media/video/cx18/cx18-queue.c b/linux/drivers/media/video/cx18/cx18-queue.c index ff6df3632..7b09c9a3e 100644 --- a/linux/drivers/media/video/cx18/cx18-queue.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-queue.h b/linux/drivers/media/video/cx18/cx18-queue.h index 92edb5e3a..ff50a2b7e 100644 --- a/linux/drivers/media/video/cx18/cx18-queue.h +++ b/linux/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/linux/drivers/media/video/cx18/cx18-scb.c b/linux/drivers/media/video/cx18/cx18-scb.c index f56d3772a..ac18bd932 100644 --- a/linux/drivers/media/video/cx18/cx18-scb.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-scb.h b/linux/drivers/media/video/cx18/cx18-scb.h index 29866f02f..1dc1c431f 100644 --- a/linux/drivers/media/video/cx18/cx18-scb.h +++ b/linux/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/linux/drivers/media/video/cx18/cx18-streams.c b/linux/drivers/media/video/cx18/cx18-streams.c index 21434df1a..f7a7f38d8 100644 --- a/linux/drivers/media/video/cx18/cx18-streams.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-streams.h b/linux/drivers/media/video/cx18/cx18-streams.h index 3fd578d0e..7218b1504 100644 --- a/linux/drivers/media/video/cx18/cx18-streams.h +++ b/linux/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 87a6503adb7cc52716e97cede3c58b6be46ea993 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Fri, 21 Nov 2008 23:40:11 -0500 Subject: cx18: Update version due to significant irq handling changes From: Andy Walls Update the version number due to significant changes in the way the mailboxes and interrupts are handled. Priority: normal Signed-off-by: Andy Walls --- linux/drivers/media/video/cx18/cx18-version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/video/cx18/cx18-version.h b/linux/drivers/media/video/cx18/cx18-version.h index 366cc1472..eb043d599 100644 --- a/linux/drivers/media/video/cx18/cx18-version.h +++ b/linux/drivers/media/video/cx18/cx18-version.h @@ -25,7 +25,7 @@ #define CX18_DRIVER_NAME "cx18" #define CX18_DRIVER_VERSION_MAJOR 1 #define CX18_DRIVER_VERSION_MINOR 0 -#define CX18_DRIVER_VERSION_PATCHLEVEL 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 07a364e7c3c25915bc7d1200f6f5ae3c68dc6f9c Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Sat, 22 Nov 2008 08:17:06 +0100 Subject: gspca: Fix typos and name errors in Kconfig. From: Jean-Francois Moine Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/Kconfig | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/Kconfig b/linux/drivers/media/video/gspca/Kconfig index 6b557c057..fdd4f6537 100644 --- a/linux/drivers/media/video/gspca/Kconfig +++ b/linux/drivers/media/video/gspca/Kconfig @@ -12,7 +12,7 @@ menuconfig USB_GSPCA "Video For Linux" to use this driver. To compile this driver as modules, choose M here: the - modules will be called gspca_main. + module will be called gspca_main. if USB_GSPCA && VIDEO_V4L2 @@ -83,10 +83,11 @@ config USB_GSPCA_PAC7311 module will be called gspca_pac7311. config USB_GSPCA_SONIXB - tristate "SN9C102 USB Camera Driver" + tristate "SONIX Bayer USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA help - Say Y here if you want support for cameras based on the SONIXB chip. + Say Y here if you want support for cameras based on the Sonix + chips with Bayer format (SN9C101, SN9C102 and SN9C103). To compile this driver as a module, choose M here: the module will be called gspca_sonixb. @@ -95,7 +96,8 @@ config USB_GSPCA_SONIXJ tristate "SONIX JPEG USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA help - Say Y here if you want support for cameras based on the SONIXJ chip. + Say Y here if you want support for cameras based on the Sonix + chips with JPEG format (SN9C102P, SN9C105 and >= SN9C110). To compile this driver as a module, choose M here: the module will be called gspca_sonixj @@ -171,7 +173,7 @@ config USB_GSPCA_SUNPLUS SPCA504(abc) SPCA533 SPCA536 chips. To compile this driver as a module, choose M here: the - module will be called gspca_spca5xx. + module will be called gspca_sunplus. config USB_GSPCA_T613 tristate "T613 (JPEG Compliance) USB Camera Driver" -- cgit v1.2.3 From 76a743d6289354b95a4d27c4a376d257203ebfd7 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Sat, 22 Nov 2008 08:27:34 +0100 Subject: gspca: Remove some unuseful core in main. From: Jean-Francois Moine Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/gspca.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/gspca.c b/linux/drivers/media/video/gspca/gspca.c index 8e6af151e..404461b8a 100644 --- a/linux/drivers/media/video/gspca/gspca.c +++ b/linux/drivers/media/video/gspca/gspca.c @@ -1867,10 +1867,10 @@ int gspca_dev_probe(struct usb_interface *intf, gspca_dev->empty_packet = -1; /* don't check the empty packets */ /* configure the subdriver and initialize the USB device */ - ret = gspca_dev->sd_desc->config(gspca_dev, id); + ret = sd_desc->config(gspca_dev, id); if (ret < 0) goto out; - ret = gspca_dev->sd_desc->init(gspca_dev); + ret = sd_desc->init(gspca_dev); if (ret < 0) goto out; ret = gspca_set_alt0(gspca_dev); @@ -1886,7 +1886,6 @@ int gspca_dev_probe(struct usb_interface *intf, /* init video stuff */ memcpy(&gspca_dev->vdev, &gspca_template, sizeof gspca_template); gspca_dev->vdev.parent = &dev->dev; - gspca_dev->vdev.fops = &dev_fops; gspca_dev->module = module; gspca_dev->present = 1; ret = video_register_device(&gspca_dev->vdev, -- cgit v1.2.3 From 45bad1b774b960e44eaf6b907d207ddb7645db09 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Sat, 22 Nov 2008 08:53:31 +0100 Subject: gspca: Let gspca handle the webcams 045e:00f5 & 00f7 instead of sn9c102. From: Jean-Francois Moine Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/sonixj.c | 2 ++ linux/drivers/media/video/sn9c102/sn9c102_devtable.h | 2 ++ 2 files changed, 4 insertions(+) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/sonixj.c b/linux/drivers/media/video/gspca/sonixj.c index d2892218f..182b84f1c 100644 --- a/linux/drivers/media/video/gspca/sonixj.c +++ b/linux/drivers/media/video/gspca/sonixj.c @@ -1711,8 +1711,10 @@ static const __devinitdata struct usb_device_id device_table[] = { #if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE {USB_DEVICE(0x0458, 0x7025), BSI(SN9C120, MI0360, 0x5d)}, {USB_DEVICE(0x0458, 0x702e), BSI(SN9C120, OV7660, 0x21)}, +#endif {USB_DEVICE(0x045e, 0x00f5), BSI(SN9C105, OV7660, 0x21)}, {USB_DEVICE(0x045e, 0x00f7), BSI(SN9C105, OV7660, 0x21)}, +#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE {USB_DEVICE(0x0471, 0x0327), BSI(SN9C105, MI0360, 0x5d)}, #endif {USB_DEVICE(0x0471, 0x0328), BSI(SN9C105, MI0360, 0x5d)}, diff --git a/linux/drivers/media/video/sn9c102/sn9c102_devtable.h b/linux/drivers/media/video/sn9c102/sn9c102_devtable.h index b0d9d9589..8cb3457e7 100644 --- a/linux/drivers/media/video/sn9c102/sn9c102_devtable.h +++ b/linux/drivers/media/video/sn9c102/sn9c102_devtable.h @@ -93,8 +93,10 @@ static const struct usb_device_id sn9c102_id_table[] = { { SN9C102_USB_DEVICE(0x0c45, 0x60bc, BRIDGE_SN9C103), }, { SN9C102_USB_DEVICE(0x0c45, 0x60be, BRIDGE_SN9C103), }, /* SN9C105 */ +#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE { SN9C102_USB_DEVICE(0x045e, 0x00f5, BRIDGE_SN9C105), }, { SN9C102_USB_DEVICE(0x045e, 0x00f7, BRIDGE_SN9C105), }, +#endif { SN9C102_USB_DEVICE(0x0471, 0x0327, BRIDGE_SN9C105), }, #if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE { SN9C102_USB_DEVICE(0x0471, 0x0328, BRIDGE_SN9C105), }, -- cgit v1.2.3 From af3657eb719b76f16b5be6f6b6a76db32aa479dc Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Sat, 22 Nov 2008 09:23:39 +0100 Subject: gspca:Subdriver ov534 added. From: Antonio Ospite The OmniVision OV534 is the USB bridge chip used in Sony Playstation EYE, it is found also in other webcams like Hercules Blog Webcam and Hercules Dualpix HD. This driver is the port to gspca of a prototype driver by Mark Ferrell based on vivi. The original code to initialize the camera and start the capture is from Jim Paris on ps2dev.org, here integrated with the analysis of the USB communications taken by the windows driver. Priority: normal Signed-off-by: Antonio Ospite Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/Kconfig | 10 + linux/drivers/media/video/gspca/Makefile | 2 + linux/drivers/media/video/gspca/ov534.c | 505 +++++++++++++++++++++++++++++++ 3 files changed, 517 insertions(+) create mode 100644 linux/drivers/media/video/gspca/ov534.c (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/Kconfig b/linux/drivers/media/video/gspca/Kconfig index fdd4f6537..770fb699d 100644 --- a/linux/drivers/media/video/gspca/Kconfig +++ b/linux/drivers/media/video/gspca/Kconfig @@ -64,6 +64,16 @@ config USB_GSPCA_OV519 To compile this driver as a module, choose M here: the module will be called gspca_ov519. +config USB_GSPCA_OV534 + tristate "OV534 USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the OV534 chip. + (e.g. Sony Playstation EYE) + + To compile this driver as a module, choose M here: the + module will be called gspca_ov534. + config USB_GSPCA_PAC207 tristate "Pixart PAC207 USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA diff --git a/linux/drivers/media/video/gspca/Makefile b/linux/drivers/media/video/gspca/Makefile index 22734f5a6..6c8046e23 100644 --- a/linux/drivers/media/video/gspca/Makefile +++ b/linux/drivers/media/video/gspca/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_USB_GSPCA_ETOMS) += gspca_etoms.o obj-$(CONFIG_USB_GSPCA_FINEPIX) += gspca_finepix.o obj-$(CONFIG_USB_GSPCA_MARS) += gspca_mars.o obj-$(CONFIG_USB_GSPCA_OV519) += gspca_ov519.o +obj-$(CONFIG_USB_GSPCA_OV534) += gspca_ov534.o obj-$(CONFIG_USB_GSPCA_PAC207) += gspca_pac207.o obj-$(CONFIG_USB_GSPCA_PAC7311) += gspca_pac7311.o obj-$(CONFIG_USB_GSPCA_SONIXB) += gspca_sonixb.o @@ -27,6 +28,7 @@ gspca_etoms-objs := etoms.o gspca_finepix-objs := finepix.o gspca_mars-objs := mars.o gspca_ov519-objs := ov519.o +gspca_ov534-objs := ov534.o gspca_pac207-objs := pac207.o gspca_pac7311-objs := pac7311.o gspca_sonixb-objs := sonixb.o diff --git a/linux/drivers/media/video/gspca/ov534.c b/linux/drivers/media/video/gspca/ov534.c new file mode 100644 index 000000000..a574be09b --- /dev/null +++ b/linux/drivers/media/video/gspca/ov534.c @@ -0,0 +1,505 @@ +/* + * ov534/ov772x gspca driver + * Copyright (C) 2008 Antonio Ospite + * + * Based on a prototype written by Mark Ferrell + * USB protocol reverse engineered by Jim Paris + * https://jim.sh/svn/jim/devl/playstation/ps3/eye/test/ + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MODULE_NAME "ov534" + +#include "gspca.h" + +#define OV534_REG_ADDRESS 0xf1 /* ? */ +#define OV534_REG_SUBADDR 0xf2 +#define OV534_REG_WRITE 0xf3 +#define OV534_REG_READ 0xf4 +#define OV534_REG_OPERATION 0xf5 +#define OV534_REG_STATUS 0xf6 + +#define OV534_OP_WRITE_3 0x37 +#define OV534_OP_WRITE_2 0x33 +#define OV534_OP_READ_2 0xf9 + +#define CTRL_TIMEOUT 500 + +MODULE_AUTHOR("Antonio Ospite "); +MODULE_DESCRIPTION("GSPCA/OV534 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* global parameters */ +static int frame_rate; + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + __u8 frame_rate; +}; + +/* V4L2 controls supported by the driver */ +static struct ctrl sd_ctrls[] = { +}; + +static struct v4l2_pix_format vga_mode[] = { + {640, 480, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, + .bytesperline = 640 * 2, + .sizeimage = 640 * 480 * 2, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, +}; + +static void ov534_reg_write(struct usb_device *udev, u16 reg, u16 val) +{ + u16 data = val; + int ret; + + PDEBUG(D_USBO, "reg=0x%04x, val=0%04x", reg, val); + ret = usb_control_msg(udev, + usb_sndctrlpipe(udev, 0), + 0x1, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x0, reg, &data, 1, CTRL_TIMEOUT); + if (ret < 0) + PDEBUG(D_ERR, "write failed"); +} + +static u16 ov534_reg_read(struct usb_device *udev, u16 reg) +{ + u16 data; + int ret; + + ret = usb_control_msg(udev, + usb_rcvctrlpipe(udev, 0), + 0x1, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x0, reg, &data, 1, CTRL_TIMEOUT); + PDEBUG(D_USBI, "reg=0x%04x, data=0x%04x", reg, data); + if (ret < 0) + PDEBUG(D_ERR, "read failed"); + return data; +} + +static void ov534_reg_verify_write(struct usb_device *udev, u16 reg, u16 val) +{ + u16 data; + + ov534_reg_write(udev, reg, val); + data = ov534_reg_read(udev, reg); + if (data != val) { + PDEBUG(D_ERR | D_USBO, + "unexpected result from read: 0x%04x != 0x%04x", val, + data); + } +} + +/* Two bits control LED: 0x21 bit 7 and 0x23 bit 7. + * (direction and output)? */ +static void ov534_set_led(struct usb_device *udev, int status) +{ + u16 data; + + PDEBUG(D_CONF, "led status: %d", status); + + data = ov534_reg_read(udev, 0x21); + data |= 0x80; + ov534_reg_write(udev, 0x21, data); + + data = ov534_reg_read(udev, 0x23); + if (status) + data |= 0x80; + else + data &= ~(0x80); + + ov534_reg_write(udev, 0x23, data); +} + +static int sccb_check_status(struct usb_device *udev) +{ + u16 data; + int i; + + for (i = 0; i < 5; i++) { + data = ov534_reg_read(udev, OV534_REG_STATUS); + + switch (data & 0xFF) { + case 0x00: + return 1; + case 0x04: + return 0; + case 0x03: + break; + default: + PDEBUG(D_ERR, "sccb status 0x%02x, attempt %d/5\n", + data, i + 1); + } + } + return 0; +} + +static void sccb_reg_write(struct usb_device *udev, u16 reg, u16 val) +{ + PDEBUG(D_USBO, "reg: 0x%04x, val: 0x%04x", reg, val); + ov534_reg_write(udev, OV534_REG_SUBADDR, reg); + ov534_reg_write(udev, OV534_REG_WRITE, val); + ov534_reg_write(udev, OV534_REG_OPERATION, OV534_OP_WRITE_3); + + if (!sccb_check_status(udev)) + PDEBUG(D_ERR, "sccb_reg_write failed"); +} + +/* setup method */ +static void ov534_setup(struct usb_device *udev) +{ + ov534_reg_verify_write(udev, 0xe7, 0x3a); + + ov534_reg_write(udev, OV534_REG_ADDRESS, 0x60); + ov534_reg_write(udev, OV534_REG_ADDRESS, 0x60); + ov534_reg_write(udev, OV534_REG_ADDRESS, 0x60); + ov534_reg_write(udev, OV534_REG_ADDRESS, 0x42); + + ov534_reg_verify_write(udev, 0xc2, 0x0c); + ov534_reg_verify_write(udev, 0x88, 0xf8); + ov534_reg_verify_write(udev, 0xc3, 0x69); + ov534_reg_verify_write(udev, 0x89, 0xff); + ov534_reg_verify_write(udev, 0x76, 0x03); + ov534_reg_verify_write(udev, 0x92, 0x01); + ov534_reg_verify_write(udev, 0x93, 0x18); + ov534_reg_verify_write(udev, 0x94, 0x10); + ov534_reg_verify_write(udev, 0x95, 0x10); + ov534_reg_verify_write(udev, 0xe2, 0x00); + ov534_reg_verify_write(udev, 0xe7, 0x3e); + + ov534_reg_write(udev, 0x1c, 0x0a); + ov534_reg_write(udev, 0x1d, 0x22); + ov534_reg_write(udev, 0x1d, 0x06); + + ov534_reg_verify_write(udev, 0x96, 0x00); + + ov534_reg_write(udev, 0x97, 0x20); + ov534_reg_write(udev, 0x97, 0x20); + ov534_reg_write(udev, 0x97, 0x20); + ov534_reg_write(udev, 0x97, 0x0a); + ov534_reg_write(udev, 0x97, 0x3f); + ov534_reg_write(udev, 0x97, 0x4a); + ov534_reg_write(udev, 0x97, 0x20); + ov534_reg_write(udev, 0x97, 0x15); + ov534_reg_write(udev, 0x97, 0x0b); + + ov534_reg_verify_write(udev, 0x8e, 0x40); + ov534_reg_verify_write(udev, 0x1f, 0x81); + ov534_reg_verify_write(udev, 0x34, 0x05); + ov534_reg_verify_write(udev, 0xe3, 0x04); + ov534_reg_verify_write(udev, 0x88, 0x00); + ov534_reg_verify_write(udev, 0x89, 0x00); + ov534_reg_verify_write(udev, 0x76, 0x00); + ov534_reg_verify_write(udev, 0xe7, 0x2e); + ov534_reg_verify_write(udev, 0x31, 0xf9); + ov534_reg_verify_write(udev, 0x25, 0x42); + ov534_reg_verify_write(udev, 0x21, 0xf0); + + ov534_reg_write(udev, 0x1c, 0x00); + ov534_reg_write(udev, 0x1d, 0x40); + ov534_reg_write(udev, 0x1d, 0x02); + ov534_reg_write(udev, 0x1d, 0x00); + ov534_reg_write(udev, 0x1d, 0x02); + ov534_reg_write(udev, 0x1d, 0x57); + ov534_reg_write(udev, 0x1d, 0xff); + + ov534_reg_verify_write(udev, 0x8d, 0x1c); + ov534_reg_verify_write(udev, 0x8e, 0x80); + ov534_reg_verify_write(udev, 0xe5, 0x04); + + ov534_set_led(udev, 1); + + sccb_reg_write(udev, 0x12, 0x80); + sccb_reg_write(udev, 0x11, 0x01); + sccb_reg_write(udev, 0x11, 0x01); + sccb_reg_write(udev, 0x11, 0x01); + sccb_reg_write(udev, 0x11, 0x01); + sccb_reg_write(udev, 0x11, 0x01); + sccb_reg_write(udev, 0x11, 0x01); + sccb_reg_write(udev, 0x11, 0x01); + sccb_reg_write(udev, 0x11, 0x01); + sccb_reg_write(udev, 0x11, 0x01); + sccb_reg_write(udev, 0x11, 0x01); + sccb_reg_write(udev, 0x11, 0x01); + + ov534_set_led(udev, 0); + + sccb_reg_write(udev, 0x3d, 0x03); + sccb_reg_write(udev, 0x17, 0x26); + sccb_reg_write(udev, 0x18, 0xa0); + sccb_reg_write(udev, 0x19, 0x07); + sccb_reg_write(udev, 0x1a, 0xf0); + sccb_reg_write(udev, 0x32, 0x00); + sccb_reg_write(udev, 0x29, 0xa0); + sccb_reg_write(udev, 0x2c, 0xf0); + sccb_reg_write(udev, 0x65, 0x20); + sccb_reg_write(udev, 0x11, 0x01); + sccb_reg_write(udev, 0x42, 0x7f); + sccb_reg_write(udev, 0x63, 0xe0); + sccb_reg_write(udev, 0x64, 0xff); + sccb_reg_write(udev, 0x66, 0x00); + sccb_reg_write(udev, 0x13, 0xf0); + sccb_reg_write(udev, 0x0d, 0x41); + sccb_reg_write(udev, 0x0f, 0xc5); + sccb_reg_write(udev, 0x14, 0x11); + + ov534_set_led(udev, 1); + + sccb_reg_write(udev, 0x22, 0x7f); + sccb_reg_write(udev, 0x23, 0x03); + sccb_reg_write(udev, 0x24, 0x40); + sccb_reg_write(udev, 0x25, 0x30); + sccb_reg_write(udev, 0x26, 0xa1); + sccb_reg_write(udev, 0x2a, 0x00); + sccb_reg_write(udev, 0x2b, 0x00); + sccb_reg_write(udev, 0x6b, 0xaa); + sccb_reg_write(udev, 0x13, 0xff); + + ov534_set_led(udev, 0); + + sccb_reg_write(udev, 0x90, 0x05); + sccb_reg_write(udev, 0x91, 0x01); + sccb_reg_write(udev, 0x92, 0x03); + sccb_reg_write(udev, 0x93, 0x00); + sccb_reg_write(udev, 0x94, 0x60); + sccb_reg_write(udev, 0x95, 0x3c); + sccb_reg_write(udev, 0x96, 0x24); + sccb_reg_write(udev, 0x97, 0x1e); + sccb_reg_write(udev, 0x98, 0x62); + sccb_reg_write(udev, 0x99, 0x80); + sccb_reg_write(udev, 0x9a, 0x1e); + sccb_reg_write(udev, 0x9b, 0x08); + sccb_reg_write(udev, 0x9c, 0x20); + sccb_reg_write(udev, 0x9e, 0x81); + + ov534_set_led(udev, 1); + + sccb_reg_write(udev, 0xa6, 0x04); + sccb_reg_write(udev, 0x7e, 0x0c); + sccb_reg_write(udev, 0x7f, 0x16); + sccb_reg_write(udev, 0x80, 0x2a); + sccb_reg_write(udev, 0x81, 0x4e); + sccb_reg_write(udev, 0x82, 0x61); + sccb_reg_write(udev, 0x83, 0x6f); + sccb_reg_write(udev, 0x84, 0x7b); + sccb_reg_write(udev, 0x85, 0x86); + sccb_reg_write(udev, 0x86, 0x8e); + sccb_reg_write(udev, 0x87, 0x97); + sccb_reg_write(udev, 0x88, 0xa4); + sccb_reg_write(udev, 0x89, 0xaf); + sccb_reg_write(udev, 0x8a, 0xc5); + sccb_reg_write(udev, 0x8b, 0xd7); + sccb_reg_write(udev, 0x8c, 0xe8); + sccb_reg_write(udev, 0x8d, 0x20); + + sccb_reg_write(udev, 0x0c, 0x90); + + ov534_reg_verify_write(udev, 0xc0, 0x50); + ov534_reg_verify_write(udev, 0xc1, 0x3c); + ov534_reg_verify_write(udev, 0xc2, 0x0c); + + ov534_set_led(udev, 1); + + sccb_reg_write(udev, 0x2b, 0x00); + sccb_reg_write(udev, 0x22, 0x7f); + sccb_reg_write(udev, 0x23, 0x03); + sccb_reg_write(udev, 0x11, 0x01); + sccb_reg_write(udev, 0x0c, 0xd0); + sccb_reg_write(udev, 0x64, 0xff); + sccb_reg_write(udev, 0x0d, 0x41); + + sccb_reg_write(udev, 0x14, 0x41); + sccb_reg_write(udev, 0x0e, 0xcd); + sccb_reg_write(udev, 0xac, 0xbf); + sccb_reg_write(udev, 0x8e, 0x00); + sccb_reg_write(udev, 0x0c, 0xd0); + + ov534_reg_write(udev, 0xe0, 0x09); + ov534_set_led(udev, 0); +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct cam *cam; + + cam = &gspca_dev->cam; + + cam->epaddr = 0x01; + cam->cam_mode = vga_mode; + cam->nmodes = ARRAY_SIZE(vga_mode); + + cam->bulk_size = vga_mode[0].sizeimage; + cam->bulk_nurbs = 2; + + PDEBUG(D_PROBE, "bulk_size = %d", cam->bulk_size); + + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *)gspca_dev; + ov534_setup(gspca_dev->dev); + + if (frame_rate > 0) + sd->frame_rate = frame_rate; + + PDEBUG(D_PROBE, "frame_rate = %d", sd->frame_rate); + + switch (sd->frame_rate) { + case 50: + sccb_reg_write(gspca_dev->dev, 0x11, 0x01); + sccb_check_status(gspca_dev->dev); + sccb_reg_write(gspca_dev->dev, 0x0d, 0x41); + sccb_check_status(gspca_dev->dev); + ov534_reg_verify_write(gspca_dev->dev, 0xe5, 0x02); + break; + case 40: + sccb_reg_write(gspca_dev->dev, 0x11, 0x02); + sccb_check_status(gspca_dev->dev); + sccb_reg_write(gspca_dev->dev, 0x0d, 0xc1); + sccb_check_status(gspca_dev->dev); + ov534_reg_verify_write(gspca_dev->dev, 0xe5, 0x04); + break; + case 30: + default: + sccb_reg_write(gspca_dev->dev, 0x11, 0x04); + sccb_check_status(gspca_dev->dev); + sccb_reg_write(gspca_dev->dev, 0x0d, 0x81); + sccb_check_status(gspca_dev->dev); + ov534_reg_verify_write(gspca_dev->dev, 0xe5, 0x02); + break; + case 15: + sccb_reg_write(gspca_dev->dev, 0x11, 0x03); + sccb_check_status(gspca_dev->dev); + sccb_reg_write(gspca_dev->dev, 0x0d, 0x41); + sccb_check_status(gspca_dev->dev); + ov534_reg_verify_write(gspca_dev->dev, 0xe5, 0x04); + break; + }; + + return 0; +} + +static int sd_start(struct gspca_dev *gspca_dev) +{ + PDEBUG(D_PROBE, "width = %d, height = %d", + gspca_dev->width, gspca_dev->height); + + gspca_dev->cam.bulk_size = gspca_dev->width * gspca_dev->height * 2; + + /* start streaming data */ + ov534_set_led(gspca_dev->dev, 1); + ov534_reg_write(gspca_dev->dev, 0xe0, 0x00); + + return 0; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + /* stop streaming data */ + ov534_reg_write(gspca_dev->dev, 0xe0, 0x09); + ov534_set_led(gspca_dev->dev, 0); +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, + __u8 *data, int len) +{ + /* + * The current camera setup doesn't stream the last pixel, so we set it + * to a dummy value + */ + __u8 last_pixel[4] = { 0, 0, 0, 0 }; + int framesize = gspca_dev->cam.bulk_size; + + if (len == framesize - 4) { + frame = + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, len); + frame = + gspca_frame_add(gspca_dev, LAST_PACKET, frame, last_pixel, + 4); + } else + PDEBUG(D_PACK, "packet len = %d, framesize = %d", len, + framesize); +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .init = sd_init, + .start = sd_start, + .stopN = sd_stopN, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +static const __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x06f8, 0x3002)}, /* Hercules Blog Webcam */ + {USB_DEVICE(0x06f8, 0x3003)}, /* Hercules Dualpix HD Weblog */ + {USB_DEVICE(0x1415, 0x2000)}, /* Sony HD Eye for PS3 (SLEH 00201) */ + {} +}; + +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "registered"); + return 0; +} + +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); + +module_param(frame_rate, int, 0644); +MODULE_PARM_DESC(frame_rate, "Frame rate (15, 30, 40, 50)"); -- cgit v1.2.3 From 0649e35124be51fb4e39b6c4f4235aa299422174 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Sat, 22 Nov 2008 09:35:49 +0100 Subject: gspca: Add the ov534 webcams in the gspca documentation. From: Jean-Francois Moine Priority: normal Signed-off-by: Jean-Francois Moine --- linux/Documentation/video4linux/gspca.txt | 3 +++ 1 file changed, 3 insertions(+) (limited to 'linux') diff --git a/linux/Documentation/video4linux/gspca.txt b/linux/Documentation/video4linux/gspca.txt index 2a23fe31e..4c96adc0b 100644 --- a/linux/Documentation/video4linux/gspca.txt +++ b/linux/Documentation/video4linux/gspca.txt @@ -169,6 +169,8 @@ spca500 06bd:0404 Agfa CL20 spca500 06be:0800 Optimedia sunplus 06d6:0031 Trust 610 LCD PowerC@m Zoom spca506 06e1:a190 ADS Instant VCD +ov534 06f8:3002 Hercules Blog Webcam +ov534 06f8:3003 Hercules Dualpix HD Weblog spca508 0733:0110 ViewQuest VQ110 spca508 0130:0130 Clone Digital Webcam 11043 spca501 0733:0401 Intel Create and Share @@ -263,6 +265,7 @@ etoms 102c:6251 Qcam xxxxxx VGA zc3xx 10fd:0128 Typhoon Webshot II USB 300k 0x0128 spca561 10fd:7e50 FlyCam Usb 100 zc3xx 10fd:8050 Typhoon Webshot II USB 300k +ov534 1415:2000 Sony HD Eye for PS3 (SLEH 00201) pac207 145f:013a Trust WB-1300N spca501 1776:501c Arowana 300K CMOS Camera t613 17a1:0128 TASCORP JPEG Webcam, NGS Cyclops -- cgit v1.2.3 From 53de899b707730eb70b95b5a90cd1519a795c88a Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Sat, 22 Nov 2008 12:26:26 +0100 Subject: gspca: Fix an access to freed memory at disconnect time. From: leandro Costantino On disconnection (USB or rmmod subdriver), the device is freed by video_unregister_device(). Setting the 'present' and 'streaming' flags must be done before the call to this function. Priority: high Signed-off-by: leandro Costantino Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/gspca.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/gspca.c b/linux/drivers/media/video/gspca/gspca.c index 404461b8a..d725663fc 100644 --- a/linux/drivers/media/video/gspca/gspca.c +++ b/linux/drivers/media/video/gspca/gspca.c @@ -1916,14 +1916,15 @@ void gspca_disconnect(struct usb_interface *intf) { struct gspca_dev *gspca_dev = usb_get_intfdata(intf); + gspca_dev->present = 0; + gspca_dev->streaming = 0; + usb_set_intfdata(intf, NULL); -/* We don't want people trying to open up the device */ + /* release the device */ + /* (this will call gspca_release() immediatly or on last close) */ video_unregister_device(&gspca_dev->vdev); - gspca_dev->present = 0; - gspca_dev->streaming = 0; - PDEBUG(D_PROBE, "disconnect complete"); } EXPORT_SYMBOL(gspca_disconnect); -- cgit v1.2.3 From cebe140b38fdc174fd1234f6d69345d0b54c84fe Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 22 Nov 2008 11:50:38 -0200 Subject: em28xx: improve message probing logs From: Mauro Carvalho Chehab On some em28xx devices, there's an unused interface. This is printed on the logs as an error. We can just ignore that interface. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-video.c | 38 ++++++++++++++++--------- 1 file changed, 25 insertions(+), 13 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index e585780c8..5da519bfa 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -2280,12 +2280,6 @@ static int em28xx_usb_probe(struct usb_interface *interface, return -ENODEV; } - em28xx_err(DRIVER_NAME " new video device (%04x:%04x): interface %i, class %i\n", - udev->descriptor.idVendor, - udev->descriptor.idProduct, - ifnum, - interface->altsetting[0].desc.bInterfaceClass); - endpoint = &interface->cur_altsetting->endpoint[0].desc; /* check if the device has the iso in endpoint at the correct place */ @@ -2296,21 +2290,39 @@ static int em28xx_usb_probe(struct usb_interface *interface, /* It's a newer em2874/em2875 device */ isoc_pipe = 0; } else { + int check_interface = 1; isoc_pipe = 1; endpoint = &interface->cur_altsetting->endpoint[1].desc; if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != - USB_ENDPOINT_XFER_ISOC) { - em28xx_err(DRIVER_NAME " probing error: endpoint is non-ISO endpoint!\n"); - em28xx_devused &= ~(1<bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) { - em28xx_err(DRIVER_NAME " probing error: endpoint is ISO OUT endpoint!\n"); + USB_ENDPOINT_XFER_ISOC) + check_interface = 0; + + if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) + check_interface = 0; + + if (!check_interface) { + em28xx_err(DRIVER_NAME " video device (%04x:%04x): " + "interface %i, class %i found.\n", + udev->descriptor.idVendor, + udev->descriptor.idProduct, + ifnum, + interface->altsetting[0].desc.bInterfaceClass); + + em28xx_err(DRIVER_NAME " This is an anciliary " + "interface not used by the driver\n"); + em28xx_devused &= ~(1<descriptor.idVendor, + udev->descriptor.idProduct, + ifnum, + interface->altsetting[0].desc.bInterfaceClass); + if (nr >= EM28XX_MAXBOARDS) { printk(DRIVER_NAME ": Supports only %i em28xx boards.\n", EM28XX_MAXBOARDS); -- cgit v1.2.3 From 4a2ed0660fbb4d6312b79d4f822f9fc53e3696ea Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sat, 22 Nov 2008 12:26:37 -0500 Subject: sms1xxx: enable power LED on Hauppauge WinTV MiniStick From: Michael Krufky Enable power LED while the frontend is in use. Priority: normal Signed-off-by: Michael Krufky --- linux/drivers/media/dvb/siano/sms-cards.c | 15 +++++++++++++++ linux/drivers/media/dvb/siano/sms-cards.h | 2 ++ linux/drivers/media/dvb/siano/smsdvb.c | 24 ++++++++++++++++++++++++ 3 files changed, 41 insertions(+) (limited to 'linux') diff --git a/linux/drivers/media/dvb/siano/sms-cards.c b/linux/drivers/media/dvb/siano/sms-cards.c index eb4bb03b2..f6b12da67 100644 --- a/linux/drivers/media/dvb/siano/sms-cards.c +++ b/linux/drivers/media/dvb/siano/sms-cards.c @@ -161,3 +161,18 @@ int sms_board_setup(struct smscore_device_t *coredev) } return 0; } + +int sms_board_power(struct smscore_device_t *coredev, int onoff) +{ + int board_id = smscore_get_board_id(coredev); + struct sms_board *board = sms_get_board(board_id); + + switch (board_id) { + case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM: + /* power LED */ + sms_set_gpio(coredev, + board->led_power, onoff ? 1 : 0); + break; + } + return 0; +} diff --git a/linux/drivers/media/dvb/siano/sms-cards.h b/linux/drivers/media/dvb/siano/sms-cards.h index 25820909b..f68da5d77 100644 --- a/linux/drivers/media/dvb/siano/sms-cards.h +++ b/linux/drivers/media/dvb/siano/sms-cards.h @@ -46,6 +46,8 @@ struct sms_board *sms_get_board(int id); int sms_board_setup(struct smscore_device_t *coredev); +int sms_board_power(struct smscore_device_t *coredev, int onoff); + extern struct usb_device_id smsusb_id_table[]; #endif /* __SMS_CARDS_H__ */ diff --git a/linux/drivers/media/dvb/siano/smsdvb.c b/linux/drivers/media/dvb/siano/smsdvb.c index 28bce9f85..890a586b2 100644 --- a/linux/drivers/media/dvb/siano/smsdvb.c +++ b/linux/drivers/media/dvb/siano/smsdvb.c @@ -276,6 +276,27 @@ static int smsdvb_get_frontend(struct dvb_frontend *fe, /* todo: */ memcpy(fep, &client->fe_params, sizeof(struct dvb_frontend_parameters)); + + return 0; +} + +static int smsdvb_init(struct dvb_frontend *fe) +{ + struct smsdvb_client_t *client = + container_of(fe, struct smsdvb_client_t, frontend); + + sms_board_power(client->coredev, 1); + + return 0; +} + +static int smsdvb_sleep(struct dvb_frontend *fe) +{ + struct smsdvb_client_t *client = + container_of(fe, struct smsdvb_client_t, frontend); + + sms_board_power(client->coredev, 0); + return 0; } @@ -311,6 +332,9 @@ static struct dvb_frontend_ops smsdvb_fe_ops = { .read_ber = smsdvb_read_ber, .read_signal_strength = smsdvb_read_signal_strength, .read_snr = smsdvb_read_snr, + + .init = smsdvb_init, + .sleep = smsdvb_sleep, }; static int smsdvb_hotplug(struct smscore_device_t *coredev, -- cgit v1.2.3 From ecb3fc0c32c92f54d8e0560752cbecd4190d84f0 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sat, 22 Nov 2008 12:33:00 -0500 Subject: sms1xxx: enable LNA control on Hauppauge WinTV MiniCard From: Michael Krufky Power the LNA while the frontend is in use. Priority: normal Signed-off-by: Michael Krufky --- linux/drivers/media/dvb/siano/sms-cards.c | 20 +++++++++++++++++++- linux/drivers/media/dvb/siano/sms-cards.h | 3 ++- 2 files changed, 21 insertions(+), 2 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/dvb/siano/sms-cards.c b/linux/drivers/media/dvb/siano/sms-cards.c index f6b12da67..0341d53f5 100644 --- a/linux/drivers/media/dvb/siano/sms-cards.c +++ b/linux/drivers/media/dvb/siano/sms-cards.c @@ -41,7 +41,7 @@ struct usb_device_id smsusb_id_table[] = { { USB_DEVICE(0x2040, 0x2000), .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD }, { USB_DEVICE(0x2040, 0x2009), - .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD }, + .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2 }, { USB_DEVICE(0x2040, 0x200a), .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD }, { USB_DEVICE(0x2040, 0x2010), @@ -114,6 +114,13 @@ static struct sms_board sms_boards[] = { .name = "Hauppauge WinTV MiniCard", .type = SMS_NOVA_B0, .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-hcw-55xxx-dvbt-02.fw", + .lna_ctrl = 29, + }, + [SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2] = { + .name = "Hauppauge WinTV MiniCard", + .type = SMS_NOVA_B0, + .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-hcw-55xxx-dvbt-02.fw", + .lna_ctrl = 1, }, }; @@ -158,6 +165,11 @@ int sms_board_setup(struct smscore_device_t *coredev) sms_set_gpio(coredev, board->led_hi, 0); sms_set_gpio(coredev, board->led_lo, 0); break; + case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2: + case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD: + /* turn off LNA */ + sms_set_gpio(coredev, board->lna_ctrl, 0); + break; } return 0; } @@ -173,6 +185,12 @@ int sms_board_power(struct smscore_device_t *coredev, int onoff) sms_set_gpio(coredev, board->led_power, onoff ? 1 : 0); break; + case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2: + case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD: + /* LNA */ + sms_set_gpio(coredev, + board->lna_ctrl, onoff ? 1 : 0); + break; } return 0; } diff --git a/linux/drivers/media/dvb/siano/sms-cards.h b/linux/drivers/media/dvb/siano/sms-cards.h index f68da5d77..6384d8997 100644 --- a/linux/drivers/media/dvb/siano/sms-cards.h +++ b/linux/drivers/media/dvb/siano/sms-cards.h @@ -33,13 +33,14 @@ #define SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B 7 #define SMS1XXX_BOARD_HAUPPAUGE_WINDHAM 8 #define SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD 9 +#define SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2 10 struct sms_board { enum sms_device_type_st type; char *name, *fw[DEVICE_MODE_MAX]; /* gpios */ - int led_power, led_hi, led_lo; + int led_power, led_hi, led_lo, lna_ctrl; }; struct sms_board *sms_get_board(int id); -- cgit v1.2.3 From 050475e9486c0de3e3720f2e4a7dfdbe6796d980 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sat, 22 Nov 2008 12:56:37 -0500 Subject: sms1xxx: fix invalid unc readings From: Michael Krufky Add function smsdvb_read_ucblocks to report uncorrectable error counts. Priority: normal Signed-off-by: Michael Krufky --- linux/drivers/media/dvb/siano/smscoreapi.h | 2 +- linux/drivers/media/dvb/siano/smsdvb.c | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/dvb/siano/smscoreapi.h b/linux/drivers/media/dvb/siano/smscoreapi.h index 3a98e031b..f8999e8fb 100644 --- a/linux/drivers/media/dvb/siano/smscoreapi.h +++ b/linux/drivers/media/dvb/siano/smscoreapi.h @@ -535,7 +535,7 @@ struct smsdvb_client_t { struct dvb_frontend frontend; fe_status_t fe_status; - int fe_ber, fe_snr, fe_signal_strength; + int fe_ber, fe_snr, fe_unc, fe_signal_strength; struct completion tune_done, stat_done; diff --git a/linux/drivers/media/dvb/siano/smsdvb.c b/linux/drivers/media/dvb/siano/smsdvb.c index 890a586b2..0c48d2c3d 100644 --- a/linux/drivers/media/dvb/siano/smsdvb.c +++ b/linux/drivers/media/dvb/siano/smsdvb.c @@ -60,6 +60,7 @@ static int smsdvb_onresponse(void *context, struct smscore_buffer_t *cb) client->fe_snr = p->Stat.SNR; client->fe_ber = p->Stat.BER; + client->fe_unc = p->Stat.BERErrorCount; if (p->Stat.InBandPwr < -95) client->fe_signal_strength = 0; @@ -72,6 +73,7 @@ static int smsdvb_onresponse(void *context, struct smscore_buffer_t *cb) client->fe_status = 0; client->fe_snr = client->fe_ber = + client->fe_unc = client->fe_signal_strength = 0; } @@ -217,6 +219,18 @@ static int smsdvb_read_snr(struct dvb_frontend *fe, u16 *snr) return rc; } +static int smsdvb_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct smsdvb_client_t *client = + container_of(fe, struct smsdvb_client_t, frontend); + int rc = smsdvb_send_statistics_request(client); + + if (!rc) + *ucblocks = client->fe_unc; + + return rc; +} + static int smsdvb_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *tune) { @@ -332,6 +346,7 @@ static struct dvb_frontend_ops smsdvb_fe_ops = { .read_ber = smsdvb_read_ber, .read_signal_strength = smsdvb_read_signal_strength, .read_snr = smsdvb_read_snr, + .read_ucblocks = smsdvb_read_ucblocks, .init = smsdvb_init, .sleep = smsdvb_sleep, -- cgit v1.2.3 From 7026808ec63b0f4aa67bc7d067510957924dcbb7 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sat, 22 Nov 2008 13:32:19 -0500 Subject: sms1xxx: enable signal quality indicator LEDs on Hauppauge WinTV MiniStick From: Michael Krufky Priority: normal Signed-off-by: Michael Krufky --- linux/drivers/media/dvb/siano/sms-cards.c | 22 ++++++++++++++++++++++ linux/drivers/media/dvb/siano/sms-cards.h | 4 ++++ linux/drivers/media/dvb/siano/smscoreapi.c | 8 ++++++++ linux/drivers/media/dvb/siano/smscoreapi.h | 2 ++ linux/drivers/media/dvb/siano/smsdvb.c | 15 +++++++++++++-- 5 files changed, 49 insertions(+), 2 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/dvb/siano/sms-cards.c b/linux/drivers/media/dvb/siano/sms-cards.c index 0341d53f5..fd62e0b85 100644 --- a/linux/drivers/media/dvb/siano/sms-cards.c +++ b/linux/drivers/media/dvb/siano/sms-cards.c @@ -194,3 +194,25 @@ int sms_board_power(struct smscore_device_t *coredev, int onoff) } return 0; } + +int sms_board_led_feedback(struct smscore_device_t *coredev, int led) +{ + int board_id = smscore_get_board_id(coredev); + struct sms_board *board = sms_get_board(board_id); + + /* dont touch GPIO if LEDs are already set */ + if (smscore_led_state(coredev, -1) == led) + return 0; + + switch (board_id) { + case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM: + sms_set_gpio(coredev, + board->led_lo, (led & SMS_LED_LO) ? 1 : 0); + sms_set_gpio(coredev, + board->led_hi, (led & SMS_LED_HI) ? 1 : 0); + + smscore_led_state(coredev, led); + break; + } + return 0; +} diff --git a/linux/drivers/media/dvb/siano/sms-cards.h b/linux/drivers/media/dvb/siano/sms-cards.h index 6384d8997..8e0fe9fd2 100644 --- a/linux/drivers/media/dvb/siano/sms-cards.h +++ b/linux/drivers/media/dvb/siano/sms-cards.h @@ -47,6 +47,10 @@ struct sms_board *sms_get_board(int id); int sms_board_setup(struct smscore_device_t *coredev); +#define SMS_LED_OFF 0 +#define SMS_LED_LO 1 +#define SMS_LED_HI 2 +int sms_board_led_feedback(struct smscore_device_t *coredev, int led); int sms_board_power(struct smscore_device_t *coredev, int onoff); extern struct usb_device_id smsusb_id_table[]; diff --git a/linux/drivers/media/dvb/siano/smscoreapi.c b/linux/drivers/media/dvb/siano/smscoreapi.c index 2ce099883..b19dd9c7d 100644 --- a/linux/drivers/media/dvb/siano/smscoreapi.c +++ b/linux/drivers/media/dvb/siano/smscoreapi.c @@ -91,6 +91,7 @@ struct smscore_device_t { struct completion init_device_done, reload_start_done, resume_done; int board_id; + int led_state; }; void smscore_set_board_id(struct smscore_device_t *core, int id) @@ -98,6 +99,13 @@ void smscore_set_board_id(struct smscore_device_t *core, int id) core->board_id = id; } +int smscore_led_state(struct smscore_device_t *core, int led) +{ + if (led >= 0) + core->led_state = led; + return core->led_state; +} + int smscore_get_board_id(struct smscore_device_t *core) { return core->board_id; diff --git a/linux/drivers/media/dvb/siano/smscoreapi.h b/linux/drivers/media/dvb/siano/smscoreapi.h index f8999e8fb..1ab277767 100644 --- a/linux/drivers/media/dvb/siano/smscoreapi.h +++ b/linux/drivers/media/dvb/siano/smscoreapi.h @@ -590,6 +590,8 @@ int smscore_set_gpio(struct smscore_device_t *coredev, u32 pin, int level); void smscore_set_board_id(struct smscore_device_t *core, int id); int smscore_get_board_id(struct smscore_device_t *core); +int smscore_led_state(struct smscore_device_t *core, int led); + /* smsdvb.c */ int smsdvb_register(void); void smsdvb_unregister(void); diff --git a/linux/drivers/media/dvb/siano/smsdvb.c b/linux/drivers/media/dvb/siano/smsdvb.c index 0c48d2c3d..bbc87fe2e 100644 --- a/linux/drivers/media/dvb/siano/smsdvb.c +++ b/linux/drivers/media/dvb/siano/smsdvb.c @@ -167,8 +167,18 @@ static int smsdvb_send_statistics_request(struct smsdvb_client_t *client) struct SmsMsgHdr_ST Msg = { MSG_SMS_GET_STATISTICS_REQ, DVBT_BDA_CONTROL_MSG_ID, HIF_TASK, sizeof(struct SmsMsgHdr_ST), 0 }; - return smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg), - &client->stat_done); + int ret = smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg), + &client->stat_done); + if (ret < 0) + return ret; + + if (client->fe_status & FE_HAS_LOCK) + sms_board_led_feedback(client->coredev, + (client->fe_unc == 0) ? + SMS_LED_HI : SMS_LED_LO); + else + sms_board_led_feedback(client->coredev, SMS_LED_OFF); + return ret; } static int smsdvb_read_status(struct dvb_frontend *fe, fe_status_t *stat) @@ -309,6 +319,7 @@ static int smsdvb_sleep(struct dvb_frontend *fe) struct smsdvb_client_t *client = container_of(fe, struct smsdvb_client_t, frontend); + sms_board_led_feedback(client->coredev, SMS_LED_OFF); sms_board_power(client->coredev, 0); return 0; -- cgit v1.2.3 From 31674e74f8cbf0314f12262b769aa8254e49805f Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sat, 22 Nov 2008 12:08:46 -0500 Subject: sms1xxx: add USB suspend and hibernation support From: Uri Shkolnik This patch provides USB suspend and hibernation support for Siano's SMS chipset based USB device Priority: normal Signed-off-by: Uri Shkolnik Signed-off-by: Michael Krufky --- linux/drivers/media/dvb/siano/smsusb.c | 45 ++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) (limited to 'linux') diff --git a/linux/drivers/media/dvb/siano/smsusb.c b/linux/drivers/media/dvb/siano/smsusb.c index a50ea8259..fd191b0ef 100644 --- a/linux/drivers/media/dvb/siano/smsusb.c +++ b/linux/drivers/media/dvb/siano/smsusb.c @@ -436,11 +436,56 @@ static void smsusb_disconnect(struct usb_interface *intf) smsusb_term_device(intf); } +static int smsusb_suspend(struct usb_interface *intf, pm_message_t msg) +{ + struct smsusb_device_t *dev = + (struct smsusb_device_t *)usb_get_intfdata(intf); + printk(KERN_INFO "%s Entering status %d.\n", __func__, msg.event); + smsusb_stop_streaming(dev); + return 0; +} + +static int smsusb_resume(struct usb_interface *intf) +{ + int rc, i; + struct smsusb_device_t *dev = + (struct smsusb_device_t *)usb_get_intfdata(intf); + struct usb_device *udev = interface_to_usbdev(intf); + + printk(KERN_INFO "%s Entering.\n", __func__); + usb_clear_halt(udev, usb_rcvbulkpipe(udev, 0x81)); + usb_clear_halt(udev, usb_rcvbulkpipe(udev, 0x02)); + + for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) + printk(KERN_INFO "endpoint %d %02x %02x %d\n", i, + intf->cur_altsetting->endpoint[i].desc.bEndpointAddress, + intf->cur_altsetting->endpoint[i].desc.bmAttributes, + intf->cur_altsetting->endpoint[i].desc.wMaxPacketSize); + + if (intf->num_altsetting > 0) { + rc = usb_set_interface(udev, + intf->cur_altsetting->desc. + bInterfaceNumber, 0); + if (rc < 0) { + printk(KERN_INFO + "%s usb_set_interface failed, rc %d\n", + __func__, rc); + return rc; + } + } + + smsusb_start_streaming(dev); + return 0; +} + static struct usb_driver smsusb_driver = { .name = "sms1xxx", .probe = smsusb_probe, .disconnect = smsusb_disconnect, .id_table = smsusb_id_table, + + .suspend = smsusb_suspend, + .resume = smsusb_resume, }; int smsusb_register(void) -- cgit v1.2.3 From b0d6553d68b8a1ffded1620af3daf811d387fa31 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 22 Nov 2008 21:37:57 -0200 Subject: Remove warnings when compiling with kernel 2.6.18 From: Mauro Carvalho Chehab Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/bt8xx/bttv-driver.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/video/bt8xx/bttv-driver.c b/linux/drivers/media/video/bt8xx/bttv-driver.c index 09188617b..e398de6c1 100644 --- a/linux/drivers/media/video/bt8xx/bttv-driver.c +++ b/linux/drivers/media/video/bt8xx/bttv-driver.c @@ -163,14 +163,22 @@ MODULE_LICENSE("GPL"); /* ----------------------------------------------------------------------- */ /* sysfs */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) static ssize_t show_card(struct device *cd, struct device_attribute *attr, char *buf) +#else +static ssize_t show_card(struct class_device *cd, char *buf) +#endif { struct video_device *vfd = container_of(cd, struct video_device, dev); struct bttv *btv = dev_get_drvdata(vfd->parent); return sprintf(buf, "%d\n", btv ? btv->c.type : UNSET); } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) static DEVICE_ATTR(card, S_IRUGO, show_card, NULL); +#else +static CLASS_DEVICE_ATTR(card, S_IRUGO, show_card, NULL); +#endif /* ----------------------------------------------------------------------- */ /* dvb auto-load setup */ @@ -4089,7 +4097,7 @@ bttv_irq_switch_vbi(struct bttv *btv) spin_unlock(&btv->s_lock); } -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) static irqreturn_t bttv_irq(int irq, void *dev_id, struct pt_regs * regs) #else static irqreturn_t bttv_irq(int irq, void *dev_id) @@ -4271,8 +4279,13 @@ static int __devinit bttv_register_video(struct bttv *btv) goto err; printk(KERN_INFO "bttv%d: registered device video%d\n", btv->c.nr, btv->video_dev->num); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) + if (class_device_create_file(&btv->video_dev->dev, + &class_device_attr_card)<0) { +#else if (device_create_file(&btv->video_dev->dev, &dev_attr_card)<0) { +#endif printk(KERN_ERR "bttv%d: device_create_file 'card' " "failed\n", btv->c.nr); goto err; -- cgit v1.2.3 From d1d4c7f36c5e80f2f6c427b8dd3455f36ae33c6d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 30 Nov 2008 01:36:58 +0100 Subject: v4l2: add v4l2_device and v4l2_subdev structs to the v4l2 framework. From: Hans Verkuil Start implementing a proper v4l2 framework as discussed during the Linux Plumbers Conference 2008. Introduces v4l2_device (for device instances) and v4l2_subdev (representing sub-device instances). Priority: normal Signed-off-by: Hans Verkuil Reviewed-by: Laurent Pinchart Reviewed-by: Guennadi Liakhovetski Reviewed-by: Andy Walls Reviewed-by: David Brownell --- linux/Documentation/video4linux/v4l2-framework.txt | 362 +++++++++++++++++++++ linux/drivers/media/video/Makefile | 2 +- linux/drivers/media/video/v4l2-device.c | 86 +++++ linux/drivers/media/video/v4l2-subdev.c | 108 ++++++ linux/include/media/v4l2-device.h | 109 +++++++ linux/include/media/v4l2-subdev.h | 188 +++++++++++ 6 files changed, 854 insertions(+), 1 deletion(-) create mode 100644 linux/Documentation/video4linux/v4l2-framework.txt create mode 100644 linux/drivers/media/video/v4l2-device.c create mode 100644 linux/drivers/media/video/v4l2-subdev.c create mode 100644 linux/include/media/v4l2-device.h create mode 100644 linux/include/media/v4l2-subdev.h (limited to 'linux') diff --git a/linux/Documentation/video4linux/v4l2-framework.txt b/linux/Documentation/video4linux/v4l2-framework.txt new file mode 100644 index 000000000..60eaf54e7 --- /dev/null +++ b/linux/Documentation/video4linux/v4l2-framework.txt @@ -0,0 +1,362 @@ +Overview of the V4L2 driver framework +===================================== + +This text documents the various structures provided by the V4L2 framework and +their relationships. + + +Introduction +------------ + +The V4L2 drivers tend to be very complex due to the complexity of the +hardware: most devices have multiple ICs, export multiple device nodes in +/dev, and create also non-V4L2 devices such as DVB, ALSA, FB, I2C and input +(IR) devices. + +Especially the fact that V4L2 drivers have to setup supporting ICs to +do audio/video muxing/encoding/decoding makes it more complex than most. +Usually these ICs are connected to the main bridge driver through one or +more I2C busses, but other busses can also be used. Such devices are +called 'sub-devices'. + +For a long time the framework was limited to the video_device struct for +creating V4L device nodes and video_buf for handling the video buffers +(note that this document does not discuss the video_buf framework). + +This meant that all drivers had to do the setup of device instances and +connecting to sub-devices themselves. Some of this is quite complicated +to do right and many drivers never did do it correctly. + +There is also a lot of common code that could never be refactored due to +the lack of a framework. + +So this framework sets up the basic building blocks that all drivers +need and this same framework should make it much easier to refactor +common code into utility functions shared by all drivers. + + +Structure of a driver +--------------------- + +All drivers have the following structure: + +1) A struct for each device instance containing the device state. + +2) A way of initializing and commanding sub-devices (if any). + +3) Creating V4L2 device nodes (/dev/videoX, /dev/vbiX, /dev/radioX and + /dev/vtxX) and keeping track of device-node specific data. + +4) Filehandle-specific structs containing per-filehandle data. + +This is a rough schematic of how it all relates: + + device instances + | + +-sub-device instances + | + \-V4L2 device nodes + | + \-filehandle instances + + +Structure of the framework +-------------------------- + +The framework closely resembles the driver structure: it has a v4l2_device +struct for the device instance data, a v4l2_subdev struct to refer to +sub-device instances, the video_device struct stores V4L2 device node data +and in the future a v4l2_fh struct will keep track of filehandle instances +(this is not yet implemented). + + +struct v4l2_device +------------------ + +Each device instance is represented by a struct v4l2_device (v4l2-device.h). +Very simple devices can just allocate this struct, but most of the time you +would embed this struct inside a larger struct. + +You must register the device instance: + + v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev); + +Registration will initialize the v4l2_device struct and link dev->driver_data +to v4l2_dev. Registration will also set v4l2_dev->name to a value derived from +dev (driver name followed by the bus_id, to be precise). You may change the +name after registration if you want. + +You unregister with: + + v4l2_device_unregister(struct v4l2_device *v4l2_dev); + +Unregistering will also automatically unregister all subdevs from the device. + +Sometimes you need to iterate over all devices registered by a specific +driver. This is usually the case if multiple device drivers use the same +hardware. E.g. the ivtvfb driver is a framebuffer driver that uses the ivtv +hardware. The same is true for alsa drivers for example. + +You can iterate over all registered devices as follows: + +static int callback(struct device *dev, void *p) +{ + struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); + + /* test if this device was inited */ + if (v4l2_dev == NULL) + return 0; + ... + return 0; +} + +int iterate(void *p) +{ + struct device_driver *drv; + int err; + + /* Find driver 'ivtv' on the PCI bus. + pci_bus_type is a global. For USB busses use usb_bus_type. */ + drv = driver_find("ivtv", &pci_bus_type); + /* iterate over all ivtv device instances */ + err = driver_for_each_device(drv, NULL, p, callback); + put_driver(drv); + return err; +} + +Sometimes you need to keep a running counter of the device instance. This is +commonly used to map a device instance to an index of a module option array. + +The recommended approach is as follows: + +static atomic_t drv_instance = ATOMIC_INIT(0); + +static int __devinit drv_probe(struct pci_dev *dev, + const struct pci_device_id *pci_id) +{ + ... + state->instance = atomic_inc_return(&drv_instance) - 1; +} + + +struct v4l2_subdev +------------------ + +Many drivers need to communicate with sub-devices. These devices can do all +sort of tasks, but most commonly they handle audio and/or video muxing, +encoding or decoding. For webcams common sub-devices are sensors and camera +controllers. + +Usually these are I2C devices, but not necessarily. In order to provide the +driver with a consistent interface to these sub-devices the v4l2_subdev struct +(v4l2-subdev.h) was created. + +Each sub-device driver must have a v4l2_subdev struct. This struct can be +stand-alone for simple sub-devices or it might be embedded in a larger struct +if more state information needs to be stored. Usually there is a low-level +device struct (e.g. i2c_client) that contains the device data as setup +by the kernel. It is recommended to store that pointer in the private +data of v4l2_subdev using v4l2_set_subdevdata(). That makes it easy to go +from a v4l2_subdev to the actual low-level bus-specific device data. + +You also need a way to go from the low-level struct to v4l2_subdev. For the +common i2c_client struct the i2c_set_clientdata() call is used to store a +v4l2_subdev pointer, for other busses you may have to use other methods. + +From the bridge driver perspective you load the sub-device module and somehow +obtain the v4l2_subdev pointer. For i2c devices this is easy: you call +i2c_get_clientdata(). For other busses something similar needs to be done. +Helper functions exists for sub-devices on an I2C bus that do most of this +tricky work for you. + +Each v4l2_subdev contains function pointers that sub-device drivers can +implement (or leave NULL if it is not applicable). Since sub-devices can do +so many different things and you do not want to end up with a huge ops struct +of which only a handful of ops are commonly implemented, the function pointers +are sorted according to category and each category has its own ops struct. + +The top-level ops struct contains pointers to the category ops structs, which +may be NULL if the subdev driver does not support anything from that category. + +It looks like this: + +struct v4l2_subdev_core_ops { + int (*g_chip_ident)(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip); + int (*log_status)(struct v4l2_subdev *sd); + int (*init)(struct v4l2_subdev *sd, u32 val); + ... +}; + +struct v4l2_subdev_tuner_ops { + ... +}; + +struct v4l2_subdev_audio_ops { + ... +}; + +struct v4l2_subdev_video_ops { + ... +}; + +struct v4l2_subdev_ops { + const struct v4l2_subdev_core_ops *core; + const struct v4l2_subdev_tuner_ops *tuner; + const struct v4l2_subdev_audio_ops *audio; + const struct v4l2_subdev_video_ops *video; +}; + +The core ops are common to all subdevs, the other categories are implemented +depending on the sub-device. E.g. a video device is unlikely to support the +audio ops and vice versa. + +This setup limits the number of function pointers while still making it easy +to add new ops and categories. + +A sub-device driver initializes the v4l2_subdev struct using: + + v4l2_subdev_init(subdev, &ops); + +Afterwards you need to initialize subdev->name with a unique name and set the +module owner. This is done for you if you use the i2c helper functions. + +A device (bridge) driver needs to register the v4l2_subdev with the +v4l2_device: + + int err = v4l2_device_register_subdev(device, subdev); + +This can fail if the subdev module disappeared before it could be registered. +After this function was called successfully the subdev->dev field points to +the v4l2_device. + +You can unregister a sub-device using: + + v4l2_device_unregister_subdev(subdev); + +Afterwards the subdev module can be unloaded and subdev->dev == NULL. + +You can call an ops function either directly: + + err = subdev->ops->core->g_chip_ident(subdev, &chip); + +but it is better and easier to use this macro: + + err = v4l2_subdev_call(subdev, core, g_chip_ident, &chip); + +The macro will to the right NULL pointer checks and returns -ENODEV if subdev +is NULL, -ENOIOCTLCMD if either subdev->core or subdev->core->g_chip_ident is +NULL, or the actual result of the subdev->ops->core->g_chip_ident ops. + +It is also possible to call all or a subset of the sub-devices: + + v4l2_device_call_all(dev, 0, core, g_chip_ident, &chip); + +Any subdev that does not support this ops is skipped and error results are +ignored. If you want to check for errors use this: + + err = v4l2_device_call_until_err(dev, 0, core, g_chip_ident, &chip); + +Any error except -ENOIOCTLCMD will exit the loop with that error. If no +errors (except -ENOIOCTLCMD) occured, then 0 is returned. + +The second argument to both calls is a group ID. If 0, then all subdevs are +called. If non-zero, then only those whose group ID match that value will +be called. Before a bridge driver registers a subdev it can set subdev->grp_id +to whatever value it wants (it's 0 by default). This value is owned by the +bridge driver and the sub-device driver will never modify or use it. + +The group ID gives the bridge driver more control how callbacks are called. +For example, there may be multiple audio chips on a board, each capable of +changing the volume. But usually only one will actually be used when the +user want to change the volume. You can set the group ID for that subdev to +e.g. AUDIO_CONTROLLER and specify that as the group ID value when calling +v4l2_device_call_all(). That ensures that it will only go to the subdev +that needs it. + +The advantage of using v4l2_subdev is that it is a generic struct and does +not contain any knowledge about the underlying hardware. So a driver might +contain several subdevs that use an I2C bus, but also a subdev that is +controlled through GPIO pins. This distinction is only relevant when setting +up the device, but once the subdev is registered it is completely transparent. + + +I2C sub-device drivers +---------------------- + +Since these drivers are so common, special helper functions are available to +ease the use of these drivers (v4l2-common.h). + +The recommended method of adding v4l2_subdev support to an I2C driver is to +embed the v4l2_subdev struct into the state struct that is created for each +I2C device instance. Very simple devices have no state struct and in that case +you can just create a v4l2_subdev directly. + +A typical state struct would look like this (where 'chipname' is replaced by +the name of the chip): + +struct chipname_state { + struct v4l2_subdev sd; + ... /* additional state fields */ +}; + +Initialize the v4l2_subdev struct as follows: + + v4l2_i2c_subdev_init(&state->sd, client, subdev_ops); + +This function will fill in all the fields of v4l2_subdev and ensure that the +v4l2_subdev and i2c_client both point to one another. + +You should also add a helper inline function to go from a v4l2_subdev pointer +to a chipname_state struct: + +static inline struct chipname_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct chipname_state, sd); +} + +Use this to go from the v4l2_subdev struct to the i2c_client struct: + + struct i2c_client *client = v4l2_get_subdevdata(sd); + +And this to go from an i2c_client to a v4l2_subdev struct: + + struct v4l2_subdev *sd = i2c_get_clientdata(client); + +Finally you need to make a command function to make driver->command() +call the right subdev_ops functions: + +static int subdev_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + +If driver->command is never used then you can leave this out. Eventually the +driver->command usage should be removed from v4l. + +Make sure to call v4l2_device_unregister_subdev(sd) when the remove() callback +is called. This will unregister the sub-device from the bridge driver. It is +safe to call this even if the sub-device was never registered. + + +The bridge driver also has some helper functions it can use: + +struct v4l2_subdev *sd = v4l2_i2c_new_subdev(adapter, "module_foo", "chipid", 0x36); + +This loads the given module (can be NULL if no module needs to be loaded) and +calls i2c_new_device() with the given i2c_adapter and chip/address arguments. +If all goes well, then it registers the subdev with the v4l2_device. It gets +the v4l2_device by calling i2c_get_adapdata(adapter), so you should make sure +that adapdata is set to v4l2_device when you setup the i2c_adapter in your +driver. + +You can also use v4l2_i2c_new_probed_subdev() which is very similar to +v4l2_i2c_new_subdev(), except that it has an array of possible I2C addresses +that it should probe. Internally it calls i2c_new_probed_device(). + +Both functions return NULL if something went wrong. + + +struct video_device +------------------- + +Not yet documented. diff --git a/linux/drivers/media/video/Makefile b/linux/drivers/media/video/Makefile index 53d43f9e0..d16356ebe 100644 --- a/linux/drivers/media/video/Makefile +++ b/linux/drivers/media/video/Makefile @@ -8,7 +8,7 @@ msp3400-objs := msp3400-driver.o msp3400-kthreads.o stkwebcam-objs := stk-webcam.o stk-sensor.o -videodev-objs := v4l2-dev.o v4l2-ioctl.o +videodev-objs := v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-subdev.o obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-compat-ioctl32.o v4l2-int-device.o diff --git a/linux/drivers/media/video/v4l2-device.c b/linux/drivers/media/video/v4l2-device.c new file mode 100644 index 000000000..9eefde031 --- /dev/null +++ b/linux/drivers/media/video/v4l2-device.c @@ -0,0 +1,86 @@ +/* + V4L2 device support. + + Copyright (C) 2008 Hans Verkuil + + 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 + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + +int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev) +{ + if (dev == NULL || v4l2_dev == NULL) + return -EINVAL; + /* Warn if we apparently re-register a device */ + WARN_ON(dev_get_drvdata(dev)); + INIT_LIST_HEAD(&v4l2_dev->subdevs); + spin_lock_init(&v4l2_dev->lock); + v4l2_dev->dev = dev; + snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s %s", + dev->driver->name, dev->bus_id); + dev_set_drvdata(dev, v4l2_dev); + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_device_register); + +void v4l2_device_unregister(struct v4l2_device *v4l2_dev) +{ + struct v4l2_subdev *sd, *next; + + if (v4l2_dev == NULL || v4l2_dev->dev == NULL) + return; + dev_set_drvdata(v4l2_dev->dev, NULL); + /* unregister subdevs */ + list_for_each_entry_safe(sd, next, &v4l2_dev->subdevs, list) + v4l2_device_unregister_subdev(sd); + + v4l2_dev->dev = NULL; +} +EXPORT_SYMBOL_GPL(v4l2_device_unregister); + +int v4l2_device_register_subdev(struct v4l2_device *dev, struct v4l2_subdev *sd) +{ + /* Check for valid input */ + if (dev == NULL || sd == NULL || !sd->name[0]) + return -EINVAL; + /* Warn if we apparently re-register a subdev */ + WARN_ON(sd->dev); + if (!try_module_get(sd->owner)) + return -ENODEV; + sd->dev = dev; + spin_lock(&dev->lock); + list_add_tail(&sd->list, &dev->subdevs); + spin_unlock(&dev->lock); + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_device_register_subdev); + +void v4l2_device_unregister_subdev(struct v4l2_subdev *sd) +{ + /* return if it isn't registered */ + if (sd == NULL || sd->dev == NULL) + return; + spin_lock(&sd->dev->lock); + list_del(&sd->list); + spin_unlock(&sd->dev->lock); + sd->dev = NULL; + module_put(sd->owner); +} +EXPORT_SYMBOL_GPL(v4l2_device_unregister_subdev); diff --git a/linux/drivers/media/video/v4l2-subdev.c b/linux/drivers/media/video/v4l2-subdev.c new file mode 100644 index 000000000..fe1f01c97 --- /dev/null +++ b/linux/drivers/media/video/v4l2-subdev.c @@ -0,0 +1,108 @@ +/* + V4L2 sub-device support. + + Copyright (C) 2008 Hans Verkuil + + 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 + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + +int v4l2_subdev_command(struct v4l2_subdev *sd, unsigned cmd, void *arg) +{ + switch (cmd) { + case VIDIOC_QUERYCTRL: + return v4l2_subdev_call(sd, core, querymenu, arg); + case VIDIOC_G_CTRL: + return v4l2_subdev_call(sd, core, g_ctrl, arg); + case VIDIOC_S_CTRL: + return v4l2_subdev_call(sd, core, s_ctrl, arg); + case VIDIOC_QUERYMENU: + return v4l2_subdev_call(sd, core, queryctrl, arg); + case VIDIOC_LOG_STATUS: + return v4l2_subdev_call(sd, core, log_status); + case VIDIOC_G_CHIP_IDENT: + return v4l2_subdev_call(sd, core, g_chip_ident, arg); + case VIDIOC_INT_S_STANDBY: + return v4l2_subdev_call(sd, core, s_standby, *(u32 *)arg); + case VIDIOC_INT_RESET: + return v4l2_subdev_call(sd, core, reset, *(u32 *)arg); + case VIDIOC_INT_S_GPIO: + return v4l2_subdev_call(sd, core, s_gpio, *(u32 *)arg); + case VIDIOC_INT_INIT: + return v4l2_subdev_call(sd, core, init, *(u32 *)arg); +#ifdef CONFIG_VIDEO_ADV_DEBUG + case VIDIOC_DBG_G_REGISTER: + return v4l2_subdev_call(sd, core, g_register, arg); + case VIDIOC_DBG_S_REGISTER: + return v4l2_subdev_call(sd, core, s_register, arg); +#endif + + case VIDIOC_INT_S_TUNER_MODE: + return v4l2_subdev_call(sd, tuner, s_mode, *(enum v4l2_tuner_type *)arg); + case AUDC_SET_RADIO: + return v4l2_subdev_call(sd, tuner, s_radio); + case VIDIOC_S_TUNER: + return v4l2_subdev_call(sd, tuner, s_tuner, arg); + case VIDIOC_G_TUNER: + return v4l2_subdev_call(sd, tuner, g_tuner, arg); + case VIDIOC_S_STD: + return v4l2_subdev_call(sd, tuner, s_std, *(v4l2_std_id *)arg); + case VIDIOC_S_FREQUENCY: + return v4l2_subdev_call(sd, tuner, s_frequency, arg); + case VIDIOC_G_FREQUENCY: + return v4l2_subdev_call(sd, tuner, g_frequency, arg); + case TUNER_SET_TYPE_ADDR: + return v4l2_subdev_call(sd, tuner, s_type_addr, arg); + case TUNER_SET_CONFIG: + return v4l2_subdev_call(sd, tuner, s_config, arg); + + case VIDIOC_INT_AUDIO_CLOCK_FREQ: + return v4l2_subdev_call(sd, audio, s_clock_freq, *(u32 *)arg); + case VIDIOC_INT_S_AUDIO_ROUTING: + return v4l2_subdev_call(sd, audio, s_routing, arg); + case VIDIOC_INT_I2S_CLOCK_FREQ: + return v4l2_subdev_call(sd, audio, s_i2s_clock_freq, *(u32 *)arg); + + case VIDIOC_INT_S_VIDEO_ROUTING: + return v4l2_subdev_call(sd, video, s_routing, arg); + case VIDIOC_INT_S_CRYSTAL_FREQ: + return v4l2_subdev_call(sd, video, s_crystal_freq, arg); + case VIDIOC_INT_DECODE_VBI_LINE: + return v4l2_subdev_call(sd, video, decode_vbi_line, arg); + case VIDIOC_INT_S_VBI_DATA: + return v4l2_subdev_call(sd, video, s_vbi_data, arg); + case VIDIOC_INT_G_VBI_DATA: + return v4l2_subdev_call(sd, video, g_vbi_data, arg); + case VIDIOC_S_FMT: + return v4l2_subdev_call(sd, video, s_fmt, arg); + case VIDIOC_G_FMT: + return v4l2_subdev_call(sd, video, g_fmt, arg); + case VIDIOC_INT_S_STD_OUTPUT: + return v4l2_subdev_call(sd, video, s_std_output, *(v4l2_std_id *)arg); + case VIDIOC_STREAMON: + return v4l2_subdev_call(sd, video, s_stream, 1); + case VIDIOC_STREAMOFF: + return v4l2_subdev_call(sd, video, s_stream, 0); + + default: + return v4l2_subdev_call(sd, core, ioctl, cmd, arg); + } +} +EXPORT_SYMBOL_GPL(v4l2_subdev_command); diff --git a/linux/include/media/v4l2-device.h b/linux/include/media/v4l2-device.h new file mode 100644 index 000000000..97b283a04 --- /dev/null +++ b/linux/include/media/v4l2-device.h @@ -0,0 +1,109 @@ +/* + V4L2 device support header. + + Copyright (C) 2008 Hans Verkuil + + 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 + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _V4L2_DEVICE_H +#define _V4L2_DEVICE_H + +#include + +/* Each instance of a V4L2 device should create the v4l2_device struct, + either stand-alone or embedded in a larger struct. + + It allows easy access to sub-devices (see v4l2-subdev.h) and provides + basic V4L2 device-level support. + */ + +#define V4L2_DEVICE_NAME_SIZE (BUS_ID_SIZE + 16) + +struct v4l2_device { + /* dev->driver_data points to this struct */ + struct device *dev; + /* used to keep track of the registered subdevs */ + struct list_head subdevs; + /* lock this struct; can be used by the driver as well if this + struct is embedded into a larger struct. */ + spinlock_t lock; + /* unique device name, by default the driver name + bus ID */ + char name[V4L2_DEVICE_NAME_SIZE]; +}; + +/* Initialize v4l2_dev and make dev->driver_data point to v4l2_dev */ +int __must_check v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev); +/* Set v4l2_dev->dev->driver_data to NULL and unregister all sub-devices */ +void v4l2_device_unregister(struct v4l2_device *v4l2_dev); + +/* Register a subdev with a v4l2 device. While registered the subdev module + is marked as in-use. An error is returned if the module is no longer + loaded when you attempt to register it. */ +int __must_check v4l2_device_register_subdev(struct v4l2_device *dev, struct v4l2_subdev *sd); +/* Unregister a subdev with a v4l2 device. Can also be called if the subdev + wasn't registered. In that case it will do nothing. */ +void v4l2_device_unregister_subdev(struct v4l2_subdev *sd); + +/* Iterate over all subdevs. */ +#define v4l2_device_for_each_subdev(sd, dev) \ + list_for_each_entry(sd, &(dev)->subdevs, list) + +/* Call the specified callback for all subdevs matching the condition. + Ignore any errors. Note that you cannot add or delete a subdev + while walking the subdevs list. */ +#define __v4l2_device_call_subdevs(dev, cond, o, f, args...) \ + do { \ + struct v4l2_subdev *sd; \ + \ + list_for_each_entry(sd, &(dev)->subdevs, list) \ + if ((cond) && sd->ops->o && sd->ops->o->f) \ + sd->ops->o->f(sd , ##args); \ + } while (0) + +/* Call the specified callback for all subdevs matching the condition. + If the callback returns an error other than 0 or -ENOIOCTLCMD, then + return with that error code. Note that you cannot add or delete a + subdev while walking the subdevs list. */ +#define __v4l2_device_call_subdevs_until_err(dev, cond, o, f, args...) \ +({ \ + struct v4l2_subdev *sd; \ + int err = 0; \ + \ + list_for_each_entry(sd, &(dev)->subdevs, list) { \ + if ((cond) && sd->ops->o && sd->ops->o->f) \ + err = sd->ops->o->f(sd , ##args); \ + if (err && err != -ENOIOCTLCMD) \ + break; \ + } \ + (err == -ENOIOCTLCMD) ? 0 : err; \ +}) + +/* Call the specified callback for all subdevs matching grp_id (if 0, then + match them all). Ignore any errors. Note that you cannot add or delete + a subdev while walking the subdevs list. */ +#define v4l2_device_call_all(dev, grp_id, o, f, args...) \ + __v4l2_device_call_subdevs(dev, \ + !(grp_id) || sd->grp_id == (grp_id), o, f , ##args) + +/* Call the specified callback for all subdevs matching grp_id (if 0, then + match them all). If the callback returns an error other than 0 or + -ENOIOCTLCMD, then return with that error code. Note that you cannot + add or delete a subdev while walking the subdevs list. */ +#define v4l2_device_call_until_err(dev, grp_id, o, f, args...) \ + __v4l2_device_call_subdevs_until_err(dev, \ + !(grp_id) || sd->grp_id == (grp_id), o, f , ##args) + +#endif diff --git a/linux/include/media/v4l2-subdev.h b/linux/include/media/v4l2-subdev.h new file mode 100644 index 000000000..bc9e0fbf2 --- /dev/null +++ b/linux/include/media/v4l2-subdev.h @@ -0,0 +1,188 @@ +/* + V4L2 sub-device support header. + + Copyright (C) 2008 Hans Verkuil + + 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 + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _V4L2_SUBDEV_H +#define _V4L2_SUBDEV_H + +#include + +struct v4l2_device; +struct v4l2_subdev; +struct tuner_setup; + +/* Sub-devices are devices that are connected somehow to the main bridge + device. These devices are usually audio/video muxers/encoders/decoders or + sensors and webcam controllers. + + Usually these devices are controlled through an i2c bus, but other busses + may also be used. + + The v4l2_subdev struct provides a way of accessing these devices in a + generic manner. Most operations that these sub-devices support fall in + a few categories: core ops, audio ops, video ops and tuner ops. + + More categories can be added if needed, although this should remain a + limited set (no more than approx. 8 categories). + + Each category has its own set of ops that subdev drivers can implement. + + A subdev driver can leave the pointer to the category ops NULL if + it does not implement them (e.g. an audio subdev will generally not + implement the video category ops). The exception is the core category: + this must always be present. + + These ops are all used internally so it is no problem to change, remove + or add ops or move ops from one to another category. Currently these + ops are based on the original ioctls, but since ops are not limited to + one argument there is room for improvement here once all i2c subdev + drivers are converted to use these ops. + */ + +/* Core ops: it is highly recommended to implement at least these ops: + + g_chip_ident + log_status + g_register + s_register + + This provides basic debugging support. + + The ioctl ops is meant for generic ioctl-like commands. Depending on + the use-case it might be better to use subdev-specific ops (currently + not yet implemented) since ops provide proper type-checking. + */ +struct v4l2_subdev_core_ops { + int (*g_chip_ident)(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip); + int (*log_status)(struct v4l2_subdev *sd); + int (*init)(struct v4l2_subdev *sd, u32 val); + int (*s_standby)(struct v4l2_subdev *sd, u32 standby); + int (*reset)(struct v4l2_subdev *sd, u32 val); + int (*s_gpio)(struct v4l2_subdev *sd, u32 val); + int (*queryctrl)(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc); + int (*g_ctrl)(struct v4l2_subdev *sd, struct v4l2_control *ctrl); + int (*s_ctrl)(struct v4l2_subdev *sd, struct v4l2_control *ctrl); + int (*querymenu)(struct v4l2_subdev *sd, struct v4l2_querymenu *qm); + int (*ioctl)(struct v4l2_subdev *sd, int cmd, void *arg); +#ifdef CONFIG_VIDEO_ADV_DEBUG + int (*g_register)(struct v4l2_subdev *sd, struct v4l2_register *reg); + int (*s_register)(struct v4l2_subdev *sd, struct v4l2_register *reg); +#endif +}; + +struct v4l2_subdev_tuner_ops { + int (*s_mode)(struct v4l2_subdev *sd, enum v4l2_tuner_type); + int (*s_radio)(struct v4l2_subdev *sd); + int (*s_frequency)(struct v4l2_subdev *sd, struct v4l2_frequency *freq); + int (*g_frequency)(struct v4l2_subdev *sd, struct v4l2_frequency *freq); + int (*g_tuner)(struct v4l2_subdev *sd, struct v4l2_tuner *vt); + int (*s_tuner)(struct v4l2_subdev *sd, struct v4l2_tuner *vt); + int (*s_std)(struct v4l2_subdev *sd, v4l2_std_id norm); + int (*s_type_addr)(struct v4l2_subdev *sd, struct tuner_setup *type); + int (*s_config)(struct v4l2_subdev *sd, const struct v4l2_priv_tun_config *config); +}; + +struct v4l2_subdev_audio_ops { + int (*s_clock_freq)(struct v4l2_subdev *sd, u32 freq); + int (*s_i2s_clock_freq)(struct v4l2_subdev *sd, u32 freq); + int (*s_routing)(struct v4l2_subdev *sd, const struct v4l2_routing *route); +}; + +struct v4l2_subdev_video_ops { + int (*s_routing)(struct v4l2_subdev *sd, const struct v4l2_routing *route); + int (*s_crystal_freq)(struct v4l2_subdev *sd, struct v4l2_crystal_freq *freq); + int (*decode_vbi_line)(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi_line); + int (*s_vbi_data)(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *vbi_data); + int (*g_vbi_data)(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_data *vbi_data); + int (*s_std_output)(struct v4l2_subdev *sd, v4l2_std_id std); + int (*s_stream)(struct v4l2_subdev *sd, int enable); + int (*s_fmt)(struct v4l2_subdev *sd, struct v4l2_format *fmt); + int (*g_fmt)(struct v4l2_subdev *sd, struct v4l2_format *fmt); +}; + +struct v4l2_subdev_ops { + const struct v4l2_subdev_core_ops *core; + const struct v4l2_subdev_tuner_ops *tuner; + const struct v4l2_subdev_audio_ops *audio; + const struct v4l2_subdev_video_ops *video; +}; + +#define V4L2_SUBDEV_NAME_SIZE 32 + +/* Each instance of a subdev driver should create this struct, either + stand-alone or embedded in a larger struct. + */ +struct v4l2_subdev { + struct list_head list; + struct module *owner; + struct v4l2_device *dev; + const struct v4l2_subdev_ops *ops; + /* name must be unique */ + char name[V4L2_SUBDEV_NAME_SIZE]; + /* can be used to group similar subdevs, value is driver-specific */ + u32 grp_id; + /* pointer to private data */ + void *priv; +}; + +static inline void v4l2_set_subdevdata(struct v4l2_subdev *sd, void *p) +{ + sd->priv = p; +} + +static inline void *v4l2_get_subdevdata(const struct v4l2_subdev *sd) +{ + return sd->priv; +} + +/* Convert an ioctl-type command to the proper v4l2_subdev_ops function call. + This is used by subdev modules that can be called by both old-style ioctl + commands and through the v4l2_subdev_ops. + + The ioctl API of the subdev driver can call this function to call the + right ops based on the ioctl cmd and arg. + + Once all subdev drivers have been converted and all drivers no longer + use the ioctl interface, then this function can be removed. + */ +int v4l2_subdev_command(struct v4l2_subdev *sd, unsigned cmd, void *arg); + +static inline void v4l2_subdev_init(struct v4l2_subdev *sd, + const struct v4l2_subdev_ops *ops) +{ + INIT_LIST_HEAD(&sd->list); + /* ops->core MUST be set */ + BUG_ON(!ops || !ops->core); + sd->ops = ops; + sd->dev = NULL; + sd->name[0] = '\0'; + sd->grp_id = 0; + sd->priv = NULL; +} + +/* Call an ops of a v4l2_subdev, doing the right checks against + NULL pointers. + + Example: err = v4l2_subdev_call(sd, core, g_chip_ident, &chip); + */ +#define v4l2_subdev_call(sd, o, f, args...) \ + (!(sd) ? -ENODEV : (((sd) && (sd)->ops->o && (sd)->ops->o->f) ? \ + (sd)->ops->o->f((sd) , ##args) : -ENOIOCTLCMD)) + +#endif -- cgit v1.2.3 From d3e57a388a0702b9c964e85ee5015d4ab5945ed4 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 23 Nov 2008 16:19:45 +0100 Subject: v4l2-common: add i2c helper functions From: Hans Verkuil Add helper functions to load i2c sub-devices, integrating them into the v4l2-framework. Priority: normal Signed-off-by: Hans Verkuil --- linux/drivers/media/video/v4l2-common.c | 170 ++++++++++++++++++++++++++++++++ linux/include/media/v4l2-common.h | 41 ++++++++ 2 files changed, 211 insertions(+) (limited to 'linux') diff --git a/linux/drivers/media/video/v4l2-common.c b/linux/drivers/media/video/v4l2-common.c index e87a04a60..6d5e41ecc 100644 --- a/linux/drivers/media/video/v4l2-common.c +++ b/linux/drivers/media/video/v4l2-common.c @@ -58,6 +58,7 @@ #include #define __OLD_VIDIOC_ /* To allow fixing old calls*/ #include +#include #include #include @@ -802,4 +803,173 @@ int v4l2_i2c_attach(struct i2c_adapter *adapter, int address, struct i2c_driver return err != -ENOMEM ? 0 : err; } EXPORT_SYMBOL(v4l2_i2c_attach); + +void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client, + const struct v4l2_subdev_ops *ops) +{ + v4l2_subdev_init(sd, ops); + /* the owner is the same as the i2c_client's driver owner */ + sd->owner = client->driver->driver.owner; + /* i2c_client and v4l2_subdev point to one another */ + v4l2_set_subdevdata(sd, client); + i2c_set_clientdata(client, sd); + /* initialize name */ + snprintf(sd->name, sizeof(sd->name), "%s %d-%04x", + client->driver->driver.name, i2c_adapter_id(client->adapter), + client->addr); +} +EXPORT_SYMBOL_GPL(v4l2_i2c_subdev_init); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) +/* Supporting function to find a client on a specific address on the + given adapter. Used for legacy i2c drivers. */ +static struct i2c_client *v4l2_i2c_legacy_find_client(struct i2c_adapter *adap, u8 addr) +{ + struct i2c_client *result = NULL; + struct i2c_client *client; + struct list_head *item; + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) + down(&adap->clist_lock); +#else + mutex_lock(&adap->clist_lock); +#endif + list_for_each(item, &adap->clients) { + client = list_entry(item, struct i2c_client, list); + if (client->addr == addr) { + result = client; + break; + } + } +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) + up(&adap->clist_lock); +#else + mutex_unlock(&adap->clist_lock); +#endif + return result; +} +#endif + + +/* Load an i2c sub-device. It assumes that i2c_get_adapdata(adapter) + returns the v4l2_device and that i2c_get_clientdata(client) + returns the v4l2_subdev. */ +struct v4l2_subdev *v4l2_i2c_new_subdev(struct i2c_adapter *adapter, + const char *module_name, const char *client_type, u8 addr) +{ + struct v4l2_device *dev = i2c_get_adapdata(adapter); + struct v4l2_subdev *sd = NULL; + struct i2c_client *client; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) + struct i2c_board_info info; +#endif + + BUG_ON(!dev); +#ifdef MODULE + if (module_name) + request_module(module_name); +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) + /* Setup the i2c board info with the device type and + the device address. */ + memset(&info, 0, sizeof(info)); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) + strlcpy(info.driver_name, client_type, sizeof(info.driver_name)); +#else + strlcpy(info.type, client_type, sizeof(info.type)); +#endif + info.addr = addr; + + /* Create the i2c client */ + client = i2c_new_device(adapter, &info); +#else + /* Legacy code: loading the module automatically + probes and creates the i2c_client on the adapter. + Try to find the client by walking the adapter's client list. */ + client = v4l2_i2c_legacy_find_client(adapter, addr); +#endif + /* Note: it is possible in the future that + c->driver is NULL if the driver is still being loaded. + We need better support from the kernel so that we + can easily wait for the load to finish. */ + if (client == NULL || client->driver == NULL) + return NULL; + + /* Lock the module so we can safely get the v4l2_subdev pointer */ + if (!try_module_get(client->driver->driver.owner)) + return NULL; + sd = i2c_get_clientdata(client); + + /* Register with the v4l2_device which increases the module's + use count as well. */ + if (v4l2_device_register_subdev(dev, sd)) + sd = NULL; + /* Decrease the module use count to match the first try_module_get. */ + module_put(client->driver->driver.owner); + return sd; + +} +EXPORT_SYMBOL_GPL(v4l2_i2c_new_subdev); + +/* Probe and load an i2c sub-device. It assumes that i2c_get_adapdata(adapter) + returns the v4l2_device and that i2c_get_clientdata(client) + returns the v4l2_subdev. */ +struct v4l2_subdev *v4l2_i2c_new_probed_subdev(struct i2c_adapter *adapter, + const char *module_name, const char *client_type, + const unsigned short *addrs) +{ + struct v4l2_device *dev = i2c_get_adapdata(adapter); + struct v4l2_subdev *sd = NULL; + struct i2c_client *client = NULL; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) + struct i2c_board_info info; +#endif + + BUG_ON(!dev); +#ifdef MODULE + if (module_name) + request_module(module_name); +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) + /* Setup the i2c board info with the device type and + the device address. */ + memset(&info, 0, sizeof(info)); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) + strlcpy(info.driver_name, client_type, sizeof(info.driver_name)); +#else + strlcpy(info.type, client_type, sizeof(info.type)); +#endif + + /* Probe and create the i2c client */ + client = i2c_new_probed_device(adapter, &info, addrs); +#else + /* Legacy code: loading the module should automatically + probe and create the i2c_client on the adapter. + Try to find the client by walking the adapter's client list + for each of the possible addresses. */ + while (!client && *addrs != I2C_CLIENT_END) + client = v4l2_i2c_legacy_find_client(adapter, *addrs++); +#endif + /* Note: it is possible in the future that + c->driver is NULL if the driver is still being loaded. + We need better support from the kernel so that we + can easily wait for the load to finish. */ + if (client == NULL || client->driver == NULL) + return NULL; + + /* Lock the module so we can safely get the v4l2_subdev pointer */ + if (!try_module_get(client->driver->driver.owner)) + return NULL; + sd = i2c_get_clientdata(client); + + /* Register with the v4l2_device which increases the module's + use count as well. */ + if (v4l2_device_register_subdev(dev, sd)) + sd = NULL; + /* Decrease the module use count to match the first try_module_get. */ + module_put(client->driver->driver.owner); + return sd; +} +EXPORT_SYMBOL_GPL(v4l2_i2c_new_probed_subdev); + #endif diff --git a/linux/include/media/v4l2-common.h b/linux/include/media/v4l2-common.h index 2f8719abf..f99c866d8 100644 --- a/linux/include/media/v4l2-common.h +++ b/linux/include/media/v4l2-common.h @@ -57,6 +57,29 @@ /* ------------------------------------------------------------------------- */ +/* These printk constructs can be used with v4l2_device and v4l2_subdev */ +#define v4l2_printk(level, dev, fmt, arg...) \ + printk(level "%s: " fmt, (dev)->name , ## arg) + +#define v4l2_err(dev, fmt, arg...) \ + v4l2_printk(KERN_ERR, dev, fmt , ## arg) + +#define v4l2_warn(dev, fmt, arg...) \ + v4l2_printk(KERN_WARNING, dev, fmt , ## arg) + +#define v4l2_info(dev, fmt, arg...) \ + v4l2_printk(KERN_INFO, dev, fmt , ## arg) + +/* These three macros assume that the debug level is set with a module + parameter called 'debug'. */ +#define v4l2_dbg(level, debug, dev, fmt, arg...) \ + do { \ + if (debug >= (level)) \ + v4l2_printk(KERN_DEBUG, dev, fmt , ## arg); \ + } while (0) + +/* ------------------------------------------------------------------------- */ + /* Priority helper functions */ struct v4l2_prio_state { @@ -104,11 +127,29 @@ struct i2c_driver; struct i2c_adapter; struct i2c_client; struct i2c_device_id; +struct v4l2_device; +struct v4l2_subdev; +struct v4l2_subdev_ops; int v4l2_i2c_attach(struct i2c_adapter *adapter, int address, struct i2c_driver *driver, const char *name, int (*probe)(struct i2c_client *, const struct i2c_device_id *)); +/* Load an i2c module and return an initialized v4l2_subdev struct. + Only call request_module if module_name != NULL. + The client_type argument is the name of the chip that's on the adapter. */ +struct v4l2_subdev *v4l2_i2c_new_subdev(struct i2c_adapter *adapter, + const char *module_name, const char *client_type, u8 addr); +/* Probe and load an i2c module and return an initialized v4l2_subdev struct. + Only call request_module if module_name != NULL. + The client_type argument is the name of the chip that's on the adapter. */ +struct v4l2_subdev *v4l2_i2c_new_probed_subdev(struct i2c_adapter *adapter, + const char *module_name, const char *client_type, + const unsigned short *addrs); +/* Initialize an v4l2_subdev with data from an i2c_client struct */ +void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client, + const struct v4l2_subdev_ops *ops); + /* ------------------------------------------------------------------------- */ /* Internal ioctls */ -- cgit v1.2.3 From 127fb1f48bed6f38a5a9ad79344a821673bcad7a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 23 Nov 2008 17:11:16 +0000 Subject: Add Compro VideoMate E650F (DVB-T part only). From: Igor M. Liplianin Add Compro VideoMate E650F (DVB-T part only). The card based on cx23885 PCI-Express chip, xc3028 tuner and ce6353 demodulator. CC: Steven Toth Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab --- linux/Documentation/video4linux/CARDLIST.cx23885 | 1 + linux/drivers/media/video/cx23885/cx23885-cards.c | 12 ++++++++++++ linux/drivers/media/video/cx23885/cx23885-dvb.c | 1 + linux/drivers/media/video/cx23885/cx23885.h | 1 + 4 files changed, 15 insertions(+) (limited to 'linux') diff --git a/linux/Documentation/video4linux/CARDLIST.cx23885 b/linux/Documentation/video4linux/CARDLIST.cx23885 index 64823ccac..35ea130e9 100644 --- a/linux/Documentation/video4linux/CARDLIST.cx23885 +++ b/linux/Documentation/video4linux/CARDLIST.cx23885 @@ -11,3 +11,4 @@ 10 -> DViCO FusionHDTV7 Dual Express [18ac:d618] 11 -> DViCO FusionHDTV DVB-T Dual Express [18ac:db78] 12 -> Leadtek Winfast PxDVR3200 H [107d:6681] + 13 -> Compro VideoMate E650F [185b:e800] diff --git a/linux/drivers/media/video/cx23885/cx23885-cards.c b/linux/drivers/media/video/cx23885/cx23885-cards.c index dc3f79638..db1e8bdab 100644 --- a/linux/drivers/media/video/cx23885/cx23885-cards.c +++ b/linux/drivers/media/video/cx23885/cx23885-cards.c @@ -159,6 +159,10 @@ struct cx23885_board cx23885_boards[] = { .name = "Leadtek Winfast PxDVR3200 H", .portc = CX23885_MPEG_DVB, }, + [CX23885_BOARD_COMPRO_VIDEOMATE_E650F] = { + .name = "Compro VideoMate E650F", + .portc = CX23885_MPEG_DVB, + }, }; const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards); @@ -238,6 +242,10 @@ struct cx23885_subid cx23885_subids[] = { .subvendor = 0x107d, .subdevice = 0x6681, .card = CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H, + }, { + .subvendor = 0x185b, + .subdevice = 0xe800, + .card = CX23885_BOARD_COMPRO_VIDEOMATE_E650F, }, }; const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids); @@ -391,6 +399,7 @@ int cx23885_tuner_callback(void *priv, int component, int command, int arg) case CX23885_BOARD_HAUPPAUGE_HVR1500: case CX23885_BOARD_HAUPPAUGE_HVR1500Q: case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H: + case CX23885_BOARD_COMPRO_VIDEOMATE_E650F: /* Tuner Reset Command */ bitmask = 0x04; break; @@ -531,6 +540,7 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) cx_set(GP0_IO, 0x000f000f); break; case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H: + case CX23885_BOARD_COMPRO_VIDEOMATE_E650F: /* GPIO-2 xc3028 tuner reset */ /* The following GPIO's are on the internal AVCore (cx25840) */ @@ -631,6 +641,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1700: case CX23885_BOARD_HAUPPAUGE_HVR1400: case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H: + case CX23885_BOARD_COMPRO_VIDEOMATE_E650F: default: ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ @@ -645,6 +656,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1800lp: case CX23885_BOARD_HAUPPAUGE_HVR1700: case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H: + case CX23885_BOARD_COMPRO_VIDEOMATE_E650F: request_module("cx25840"); break; } diff --git a/linux/drivers/media/video/cx23885/cx23885-dvb.c b/linux/drivers/media/video/cx23885/cx23885-dvb.c index a11fdbfb5..990fd217b 100644 --- a/linux/drivers/media/video/cx23885/cx23885-dvb.c +++ b/linux/drivers/media/video/cx23885/cx23885-dvb.c @@ -503,6 +503,7 @@ static int dvb_register(struct cx23885_tsport *port) break; } case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H: + case CX23885_BOARD_COMPRO_VIDEOMATE_E650F: i2c_bus = &dev->i2c_bus[0]; fe0->dvb.frontend = dvb_attach(zl10353_attach, diff --git a/linux/drivers/media/video/cx23885/cx23885.h b/linux/drivers/media/video/cx23885/cx23885.h index 26f755354..ad93e0e88 100644 --- a/linux/drivers/media/video/cx23885/cx23885.h +++ b/linux/drivers/media/video/cx23885/cx23885.h @@ -67,6 +67,7 @@ #define CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP 10 #define CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP 11 #define CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H 12 +#define CX23885_BOARD_COMPRO_VIDEOMATE_E650F 13 /* Currently unsupported by the driver: PAL/H, NTSC/Kr, SECAM B/G/H/LC */ #define CX23885_NORMS (\ -- cgit v1.2.3 From b2c41052eb2e8da66d3ca3d44fcda96ef9be2ee7 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Sun, 23 Nov 2008 18:47:50 +0100 Subject: gspca: Simplify the brightness/contrast for ov76xx sensors in sonixj. From: Jean-Francois Moine Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/sonixj.c | 73 ++++++-------------------------- 1 file changed, 12 insertions(+), 61 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/sonixj.c b/linux/drivers/media/video/gspca/sonixj.c index 182b84f1c..bfd256716 100644 --- a/linux/drivers/media/video/gspca/sonixj.c +++ b/linux/drivers/media/video/gspca/sonixj.c @@ -1136,25 +1136,6 @@ static unsigned int setexposure(struct gspca_dev *gspca_dev, return expo; } -/* this function is used for sensors o76xx only */ -static void setbrightcont(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int val; - __u8 reg84_full[0x15]; - - memcpy(reg84_full, reg84, sizeof reg84_full); - val = sd->contrast * 0x30 / CONTRAST_MAX + 0x10; /* 10..40 */ - reg84_full[0] = (val + 1) / 2; /* red */ - reg84_full[2] = val; /* green */ - reg84_full[4] = (val + 1) / 5; /* blue */ - val = (sd->brightness - BRIGHTNESS_DEF) * 0x10 - / BRIGHTNESS_MAX; - reg84_full[0x12] = val & 0x1f; /* 5:0 signed value */ - reg_w(gspca_dev, 0x84, reg84_full, sizeof reg84_full); -} - -/* sensor != ov76xx */ static void setbrightness(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -1179,24 +1160,23 @@ static void setbrightness(struct gspca_dev *gspca_dev) case SENSOR_OM6802: expo = sd->brightness >> 6; sd->exposure = setexposure(gspca_dev, expo); - k2 = sd->brightness >> 11; + k2 >>= 1; break; } - reg_w1(gspca_dev, 0x96, k2); + reg_w1(gspca_dev, 0x96, k2); /* color matrix Y offset */ } -/* sensor != ov76xx */ static void setcontrast(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; __u8 k2; __u8 contrast[] = { 0x00, 0x00, 0x28, 0x00, 0x07, 0x00 }; - k2 = sd->contrast; - contrast[2] = k2; - contrast[0] = (k2 + 1) >> 1; - contrast[4] = (k2 + 1) / 5; + k2 = sd->contrast * 0x30 / (CONTRAST_MAX + 1) + 0x10; /* 10..40 */ + contrast[0] = (k2 + 1) / 2; /* red */ + contrast[2] = k2; /* green */ + contrast[4] = (k2 + 1) / 5; /* blue */ reg_w(gspca_dev, 0x84, contrast, 6); } @@ -1413,20 +1393,13 @@ static int sd_start(struct gspca_dev *gspca_dev) switch (sd->sensor) { case SENSOR_MI0360: setinfrared(sd); - /* fall thru */ - case SENSOR_HV7131R: - case SENSOR_MO4000: - case SENSOR_OM6802: - setbrightness(gspca_dev); - setcontrast(gspca_dev); break; case SENSOR_OV7630: setvflip(sd); - /* fall thru */ - default: /* OV76xx */ - setbrightcont(gspca_dev); break; } + setbrightness(gspca_dev); + setcontrast(gspca_dev); setautogain(gspca_dev); return 0; } @@ -1564,19 +1537,8 @@ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) struct sd *sd = (struct sd *) gspca_dev; sd->brightness = val; - if (gspca_dev->streaming) { - switch (sd->sensor) { - case SENSOR_HV7131R: - case SENSOR_MI0360: - case SENSOR_MO4000: - case SENSOR_OM6802: - setbrightness(gspca_dev); - break; - default: /* OV76xx */ - setbrightcont(gspca_dev); - break; - } - } + if (gspca_dev->streaming) + setbrightness(gspca_dev); return 0; } @@ -1593,19 +1555,8 @@ static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) struct sd *sd = (struct sd *) gspca_dev; sd->contrast = val; - if (gspca_dev->streaming) { - switch (sd->sensor) { - case SENSOR_HV7131R: - case SENSOR_MI0360: - case SENSOR_MO4000: - case SENSOR_OM6802: - setcontrast(gspca_dev); - break; - default: /* OV76xx */ - setbrightcont(gspca_dev); - break; - } - } + if (gspca_dev->streaming) + setcontrast(gspca_dev); return 0; } -- cgit v1.2.3 From 97d6559961971da20d7ea7ac672231e2b2033dc3 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Sun, 23 Nov 2008 19:53:28 +0100 Subject: gspca: Delay when trying an other altsetting on streaming start. From: Jean-Francois Moine Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/gspca.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/gspca.c b/linux/drivers/media/video/gspca/gspca.c index d725663fc..79be1e0e4 100644 --- a/linux/drivers/media/video/gspca/gspca.c +++ b/linux/drivers/media/video/gspca/gspca.c @@ -649,8 +649,11 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) "usb_submit_urb [%d] err %d", n, ret); gspca_dev->streaming = 0; destroy_urbs(gspca_dev); - if (ret == -ENOSPC) + if (ret == -ENOSPC) { + mdelay(20); /* wait for kill + * complete */ break; /* try the previous alt */ + } goto out; } } -- cgit v1.2.3 From f60183b96cdd33d9d00bde6f769048c15bb27c4c Mon Sep 17 00:00:00 2001 From: Patrick Boettcher Date: Sun, 23 Nov 2008 20:26:54 +0100 Subject: Add support for the CX24113 DVB-S tuner driver From: Patrick Boettcher This commit adds support for the CX24113 DVB-S tuner driver and thus support for the Technisat Skystar2 revision 2.8. The driver was created with the help of Technisat. Thank you very much. Priority: normal Signed-off-by: Patrick Boettcher --- linux/drivers/media/dvb/b2c2/Kconfig | 1 + linux/drivers/media/dvb/frontends/Kconfig | 8 + linux/drivers/media/dvb/frontends/Makefile | 1 + linux/drivers/media/dvb/frontends/cx24113.c | 608 ++++++++++++++++++++++++++++ linux/drivers/media/dvb/frontends/cx24113.h | 8 +- 5 files changed, 621 insertions(+), 5 deletions(-) create mode 100644 linux/drivers/media/dvb/frontends/cx24113.c (limited to 'linux') diff --git a/linux/drivers/media/dvb/b2c2/Kconfig b/linux/drivers/media/dvb/b2c2/Kconfig index 73dc2ee9b..e5c27e355 100644 --- a/linux/drivers/media/dvb/b2c2/Kconfig +++ b/linux/drivers/media/dvb/b2c2/Kconfig @@ -14,6 +14,7 @@ config DVB_B2C2_FLEXCOP select DVB_TUNER_ITD1000 if !DVB_FE_CUSTOMISE select DVB_ISL6421 if !DVB_FE_CUSTOMISE select DVB_CX24123 if !DVB_FE_CUSTOMISE + select DVB_TUNER_CX24113 if !DVB_FE_CUSTOMISE help Support for the digital TV receiver chip made by B2C2 Inc. included in Technisats PCI cards and USB boxes. diff --git a/linux/drivers/media/dvb/frontends/Kconfig b/linux/drivers/media/dvb/frontends/Kconfig index 2470d88e9..002695607 100644 --- a/linux/drivers/media/dvb/frontends/Kconfig +++ b/linux/drivers/media/dvb/frontends/Kconfig @@ -118,6 +118,14 @@ config DVB_TUNER_ITD1000 help A DVB-S tuner module. Say Y when you want to support this frontend. +config DVB_TUNER_CX24113 + tristate "Conexant CX24113/CX24128 tuner for DVB-S/DSS" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + A DVB-S tuner module. Say Y when you want to support this frontend. + + config DVB_TDA826X tristate "Philips TDA826X silicon tuner" depends on DVB_CORE && I2C diff --git a/linux/drivers/media/dvb/frontends/Makefile b/linux/drivers/media/dvb/frontends/Makefile index 7a19c0c7b..af7bdf0ad 100644 --- a/linux/drivers/media/dvb/frontends/Makefile +++ b/linux/drivers/media/dvb/frontends/Makefile @@ -54,6 +54,7 @@ obj-$(CONFIG_DVB_S5H1409) += s5h1409.o obj-$(CONFIG_DVB_TUNER_ITD1000) += itd1000.o obj-$(CONFIG_DVB_AU8522) += au8522.o obj-$(CONFIG_DVB_TDA10048) += tda10048.o +obj-$(CONFIG_DVB_TUNER_CX24113) += cx24113.o obj-$(CONFIG_DVB_S5H1411) += s5h1411.o obj-$(CONFIG_DVB_LGS8GL5) += lgs8gl5.o obj-$(CONFIG_DVB_DUMMY_FE) += dvb_dummy_fe.o diff --git a/linux/drivers/media/dvb/frontends/cx24113.c b/linux/drivers/media/dvb/frontends/cx24113.c new file mode 100644 index 000000000..e2e5df9de --- /dev/null +++ b/linux/drivers/media/dvb/frontends/cx24113.c @@ -0,0 +1,608 @@ +/* + * Driver for Conexant CX24113/CX24128 Tuner (Satelite) + * + * Copyright (C) 2007-8 Patrick Boettcher + * + * Developed for BBTI / Technisat + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include + +#include "dvb_frontend.h" +#include "cx24113.h" + +static int debug; + +#define info(args...) do { printk(KERN_INFO "CX24113: " args); } while (0) +#define err(args...) do { printk(KERN_ERR "CX24113: " args); } while (0) + +#define dprintk(args...) \ + do { \ + if (debug) { \ + printk(KERN_DEBUG "CX24113: %s: ", __func__); \ + printk(args); \ + } \ + } while (0) + +struct cx24113_state { + struct i2c_adapter *i2c; + const struct cx24113_config *config; + +#define REV_CX24113 0x23 + u8 rev; + u8 ver; + + u8 icp_mode:1; + +#define ICP_LEVEL1 0 +#define ICP_LEVEL2 1 +#define ICP_LEVEL3 2 +#define ICP_LEVEL4 3 + u8 icp_man:2; + u8 icp_auto_low:2; + u8 icp_auto_mlow:2; + u8 icp_auto_mhi:2; + u8 icp_auto_hi:2; + u8 icp_dig; + +#define LNA_MIN_GAIN 0 +#define LNA_MID_GAIN 1 +#define LNA_MAX_GAIN 2 + u8 lna_gain:2; + + u8 acp_on:1; + + u8 vco_mode:2; + u8 vco_shift:1; +#define VCOBANDSEL_6 0x80 +#define VCOBANDSEL_5 0x01 +#define VCOBANDSEL_4 0x02 +#define VCOBANDSEL_3 0x04 +#define VCOBANDSEL_2 0x08 +#define VCOBANDSEL_1 0x10 + u8 vco_band; + +#define VCODIV4 4 +#define VCODIV2 2 + u8 vcodiv; + + u8 bs_delay:4; + u16 bs_freqcnt:13; + u16 bs_rdiv; + u8 prescaler_mode:1; + + u8 rfvga_bias_ctrl; + + s16 tuner_gain_thres; + u8 gain_level; + + u32 frequency; + + u8 refdiv; + + u8 Fwindow_enabled; +}; + +static int cx24113_writereg(struct cx24113_state *state, int reg, int data) +{ + u8 buf[] = { reg, data }; + struct i2c_msg msg = { .addr = state->config->i2c_addr, + .flags = 0, .buf = buf, .len = 2 }; + int err = i2c_transfer(state->i2c, &msg, 1); + if (err != 1) { + printk(KERN_DEBUG "%s: writereg error(err == %i, reg == 0x%02x," + " data == 0x%02x)\n", __func__, err, reg, data); + return err; + } + + return 0; +} + +static int cx24113_readreg(struct cx24113_state *state, u8 reg) +{ + int ret; + u8 b; + struct i2c_msg msg[] = { + { .addr = state->config->i2c_addr, + .flags = 0, .buf = ®, .len = 1 }, + { .addr = state->config->i2c_addr, + .flags = I2C_M_RD, .buf = &b, .len = 1 } + }; + + ret = i2c_transfer(state->i2c, msg, 2); + + if (ret != 2) { + printk(KERN_DEBUG "%s: reg=0x%x (error=%d)\n", + __func__, reg, ret); + return ret; + } + + return b; +} + +static void cx24113_set_parameters(struct cx24113_state *state) +{ + u8 r; + + r = cx24113_readreg(state, 0x10) & 0x82; + r |= state->icp_mode; + r |= state->icp_man << 4; + r |= state->icp_dig << 2; + r |= state->prescaler_mode << 5; + cx24113_writereg(state, 0x10, r); + + r = (state->icp_auto_low << 0) | (state->icp_auto_mlow << 2) + | (state->icp_auto_mhi << 4) | (state->icp_auto_hi << 6); + cx24113_writereg(state, 0x11, r); + + if (state->rev == REV_CX24113) { + r = cx24113_readreg(state, 0x20) & 0xec; + r |= state->lna_gain; + r |= state->rfvga_bias_ctrl << 4; + cx24113_writereg(state, 0x20, r); + } + + r = cx24113_readreg(state, 0x12) & 0x03; + r |= state->acp_on << 2; + r |= state->bs_delay << 4; + cx24113_writereg(state, 0x12, r); + + r = cx24113_readreg(state, 0x18) & 0x40; + r |= state->vco_shift; + if (state->vco_band == VCOBANDSEL_6) + r |= (1 << 7); + else + r |= (state->vco_band << 1); + cx24113_writereg(state, 0x18, r); + + r = cx24113_readreg(state, 0x14) & 0x20; + r |= (state->vco_mode << 6) | ((state->bs_freqcnt >> 8) & 0x1f); + cx24113_writereg(state, 0x14, r); + cx24113_writereg(state, 0x15, (state->bs_freqcnt & 0xff)); + + cx24113_writereg(state, 0x16, (state->bs_rdiv >> 4) & 0xff); + r = (cx24113_readreg(state, 0x17) & 0x0f) | + ((state->bs_rdiv & 0x0f) << 4); + cx24113_writereg(state, 0x17, r); +} + +#define VGA_0 0x00 +#define VGA_1 0x04 +#define VGA_2 0x02 +#define VGA_3 0x06 +#define VGA_4 0x01 +#define VGA_5 0x05 +#define VGA_6 0x03 +#define VGA_7 0x07 + +#define RFVGA_0 0x00 +#define RFVGA_1 0x01 +#define RFVGA_2 0x02 +#define RFVGA_3 0x03 + +static int cx24113_set_gain_settings(struct cx24113_state *state, + s16 power_estimation) +{ + u8 ampout = cx24113_readreg(state, 0x1d) & 0xf0, + vga = cx24113_readreg(state, 0x1f) & 0x3f, + rfvga = cx24113_readreg(state, 0x20) & 0xf3; + u8 gain_level = power_estimation >= state->tuner_gain_thres; + + dprintk("power estimation: %d, thres: %d, gain_level: %d/%d\n", + power_estimation, state->tuner_gain_thres, + state->gain_level, gain_level); + + if (gain_level == state->gain_level) + return 0; /* nothing to be done */ + + ampout |= 0xf; + + if (gain_level) { + rfvga |= RFVGA_0 << 2; + vga |= (VGA_7 << 3) | VGA_7; + } else { + rfvga |= RFVGA_2 << 2; + vga |= (VGA_6 << 3) | VGA_2; + } + state->gain_level = gain_level; + + cx24113_writereg(state, 0x1d, ampout); + cx24113_writereg(state, 0x1f, vga); + cx24113_writereg(state, 0x20, rfvga); + + return 1; /* did something */ +} + +static int cx24113_set_Fref(struct cx24113_state *state, u8 high) +{ + u8 xtal = cx24113_readreg(state, 0x02); + if (state->rev == 0x43 && state->vcodiv == VCODIV4) + high = 1; + + xtal &= ~0x2; + if (high) + xtal |= high << 1; + return cx24113_writereg(state, 0x02, xtal); +} + +static int cx24113_enable(struct cx24113_state *state, u8 enable) +{ + u8 r21 = (cx24113_readreg(state, 0x21) & 0xc0) | enable; + if (state->rev == REV_CX24113) + r21 |= (1 << 1); + return cx24113_writereg(state, 0x21, r21); +} + +static int cx24113_set_bandwidth(struct cx24113_state *state, u32 bandwidth_khz) +{ + u8 r; + + if (bandwidth_khz <= 19000) + r = 0x03 << 6; + else if (bandwidth_khz <= 25000) + r = 0x02 << 6; + else + r = 0x01 << 6; + + dprintk("bandwidth to be set: %d\n", bandwidth_khz); + bandwidth_khz *= 10; + bandwidth_khz -= 10000; + bandwidth_khz /= 1000; + bandwidth_khz += 5; + bandwidth_khz /= 10; + + dprintk("bandwidth: %d %d\n", r >> 6, bandwidth_khz); + + r |= bandwidth_khz & 0x3f; + + return cx24113_writereg(state, 0x1e, r); +} + +static int cx24113_set_clk_inversion(struct cx24113_state *state, u8 on) +{ + u8 r = (cx24113_readreg(state, 0x10) & 0x7f) | ((on & 0x1) << 7); + return cx24113_writereg(state, 0x10, r); +} + +static int cx24113_get_status(struct dvb_frontend *fe, u32 *status) +{ + struct cx24113_state *state = fe->tuner_priv; + u8 r = (cx24113_readreg(state, 0x10) & 0x02) >> 1; + if (r) + *status |= TUNER_STATUS_LOCKED; + dprintk("PLL locked: %d\n", r); + return 0; +} + +static u8 cx24113_set_ref_div(struct cx24113_state *state, u8 refdiv) +{ + if (state->rev == 0x43 && state->vcodiv == VCODIV4) + refdiv = 2; + return state->refdiv = refdiv; +} + +static void cx24113_calc_pll_nf(struct cx24113_state *state, u16 *n, s32 *f) +{ + s32 N; + s64 F; + u8 R, r; + u8 vcodiv; + u8 factor; + s32 freq_hz = state->frequency * 1000; + + if (state->config->xtal_khz < 20000) + factor = 1; + else + factor = 2; + + if (state->rev == REV_CX24113) { + if (state->frequency >= 1100000) + vcodiv = VCODIV2; + else + vcodiv = VCODIV4; + } else { + if (state->frequency >= 1165000) + vcodiv = VCODIV2; + else + vcodiv = VCODIV4; + } + state->vcodiv = vcodiv; + + dprintk("calculating N/F for %dHz with vcodiv %d\n", freq_hz, vcodiv); + R = 0; + do { + R = cx24113_set_ref_div(state, R + 1); + + /* calculate tuner PLL settings: */ + N = (freq_hz / 100 * vcodiv) * R; + N /= (state->config->xtal_khz) * factor * 2; + N += 5; /* For round up. */ + N /= 10; + N -= 32; + } while (N < 6 && R < 3); + + if (N < 6) { + err("strange frequency: N < 6\n"); + return; + } + F = freq_hz; + F *= (u64) (R * vcodiv * 262144); + dprintk("1 N: %d, F: %lld, R: %d\n", N, F, R); + do_div(F, state->config->xtal_khz*1000 * factor * 2); + dprintk("2 N: %d, F: %lld, R: %d\n", N, F, R); + F -= (N + 32) * 262144; + + dprintk("3 N: %d, F: %lld, R: %d\n", N, F, R); + + if (state->Fwindow_enabled) { + if (F > (262144 / 2 - 1638)) + F = 262144 / 2 - 1638; + if (F < (-262144 / 2 + 1638)) + F = -262144 / 2 + 1638; + if ((F < 3277 && F > 0) || (F > -3277 && F < 0)) { + F = 0; + r = cx24113_readreg(state, 0x10); + cx24113_writereg(state, 0x10, r | (1 << 6)); + } + } + dprintk("4 N: %d, F: %lld, R: %d\n", N, F, R); + + *n = (u16) N; + *f = (s32) F; +} + + +static void cx24113_set_nfr(struct cx24113_state *state, u16 n, s32 f, u8 r) +{ + u8 reg; + cx24113_writereg(state, 0x19, (n >> 1) & 0xff); + + reg = ((n & 0x1) << 7) | ((f >> 11) & 0x7f); + cx24113_writereg(state, 0x1a, reg); + + cx24113_writereg(state, 0x1b, (f >> 3) & 0xff); + + reg = cx24113_readreg(state, 0x1c) & 0x1f; + cx24113_writereg(state, 0x1c, reg | ((f & 0x7) << 5)); + + cx24113_set_Fref(state, r - 1); +} + +static int cx24113_set_frequency(struct cx24113_state *state, u32 frequency) +{ + u8 r = 1; /* or 2 */ + u16 n = 6; + s32 f = 0; + + r = cx24113_readreg(state, 0x14); + cx24113_writereg(state, 0x14, r & 0x3f); + + r = cx24113_readreg(state, 0x10); + cx24113_writereg(state, 0x10, r & 0xbf); + + state->frequency = frequency; + + dprintk("tuning to frequency: %d\n", frequency); + + cx24113_calc_pll_nf(state, &n, &f); + cx24113_set_nfr(state, n, f, state->refdiv); + + r = cx24113_readreg(state, 0x18) & 0xbf; + if (state->vcodiv != VCODIV2) + r |= 1 << 6; + cx24113_writereg(state, 0x18, r); + + /* The need for this sleep is not clear. But helps in some cases */ + msleep(5); + + r = cx24113_readreg(state, 0x1c) & 0xef; + cx24113_writereg(state, 0x1c, r | (1 << 4)); + return 0; +} + +static int cx24113_init(struct dvb_frontend *fe) +{ + struct cx24113_state *state = fe->tuner_priv; + int ret; + + state->tuner_gain_thres = -50; + state->gain_level = 255; /* to force a gain-setting initialization */ + state->icp_mode = 0; + + if (state->config->xtal_khz < 11000) { + state->icp_auto_hi = ICP_LEVEL4; + state->icp_auto_mhi = ICP_LEVEL4; + state->icp_auto_mlow = ICP_LEVEL3; + state->icp_auto_low = ICP_LEVEL3; + } else { + state->icp_auto_hi = ICP_LEVEL4; + state->icp_auto_mhi = ICP_LEVEL4; + state->icp_auto_mlow = ICP_LEVEL3; + state->icp_auto_low = ICP_LEVEL2; + } + + state->icp_dig = ICP_LEVEL3; + state->icp_man = ICP_LEVEL1; + state->acp_on = 1; + state->vco_mode = 0; + state->vco_shift = 0; + state->vco_band = VCOBANDSEL_1; + state->bs_delay = 8; + state->bs_freqcnt = 0x0fff; + state->bs_rdiv = 0x0fff; + state->prescaler_mode = 0; + state->lna_gain = LNA_MAX_GAIN; + state->rfvga_bias_ctrl = 1; + state->Fwindow_enabled = 1; + + cx24113_set_Fref(state, 0); + cx24113_enable(state, 0x3d); + cx24113_set_parameters(state); + + cx24113_set_gain_settings(state, -30); + + cx24113_set_bandwidth(state, 18025); + cx24113_set_clk_inversion(state, 1); + + if (state->config->xtal_khz >= 40000) + ret = cx24113_writereg(state, 0x02, + (cx24113_readreg(state, 0x02) & 0xfb) | (1 << 2)); + else + ret = cx24113_writereg(state, 0x02, + (cx24113_readreg(state, 0x02) & 0xfb) | (0 << 2)); + + return ret; +} + +static int cx24113_set_params(struct dvb_frontend *fe, + struct dvb_frontend_parameters *p) +{ + struct cx24113_state *state = fe->tuner_priv; + /* for a ROLL-OFF factor of 0.35, 0.2: 600, 0.25: 625 */ + u32 roll_off = 675; + u32 bw; + + bw = ((p->u.qpsk.symbol_rate/100) * roll_off) / 1000; + bw += (10000000/100) + 5; + bw /= 10; + bw += 1000; + cx24113_set_bandwidth(state, bw); + + cx24113_set_frequency(state, p->frequency); + msleep(5); + return cx24113_get_status(fe, &bw); +} + +static s8 cx24113_agc_table[2][10] = { + {-54, -41, -35, -30, -25, -21, -16, -10, -6, -2}, + {-39, -35, -30, -25, -19, -15, -11, -5, 1, 9}, +}; + +void cx24113_agc_callback(struct dvb_frontend *fe) +{ + struct cx24113_state *state = fe->tuner_priv; + s16 s, i; + if (!fe->ops.read_signal_strength) + return; + + do { + /* this only works with the current CX24123 implementation */ + fe->ops.read_signal_strength(fe, (u16 *) &s); + s >>= 8; + dprintk("signal strength: %d\n", s); + for (i = 0; i < sizeof(cx24113_agc_table[0]); i++) + if (cx24113_agc_table[state->gain_level][i] > s) + break; + s = -25 - i*5; + } while (cx24113_set_gain_settings(state, s)); +} +EXPORT_SYMBOL(cx24113_agc_callback); + +static int cx24113_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct cx24113_state *state = fe->tuner_priv; + *frequency = state->frequency; + return 0; +} + +static int cx24113_release(struct dvb_frontend *fe) +{ + struct cx24113_state *state = fe->tuner_priv; + dprintk("\n"); + fe->tuner_priv = NULL; + kfree(state); + return 0; +} + +static const struct dvb_tuner_ops cx24113_tuner_ops = { + .info = { + .name = "Conexant CX24113", + .frequency_min = 950000, + .frequency_max = 2150000, + .frequency_step = 125, + }, + + .release = cx24113_release, + + .init = cx24113_init, + .sleep = NULL, + + .set_params = cx24113_set_params, + .get_frequency = cx24113_get_frequency, + .get_bandwidth = NULL, + .get_status = cx24113_get_status, +}; + +struct dvb_frontend *cx24113_attach(struct dvb_frontend *fe, + const struct cx24113_config *config, struct i2c_adapter *i2c) +{ + /* allocate memory for the internal state */ + struct cx24113_state *state = + kzalloc(sizeof(struct cx24113_state), GFP_KERNEL); + if (state == NULL) { + err("Unable to kmalloc\n"); + goto error; + } + + /* setup the state */ + state->config = config; + state->i2c = i2c; + + info("trying to detect myself\n"); + + /* making a dummy read, because of some expected troubles + * after power on */ + cx24113_readreg(state, 0x00); + + switch (state->rev = cx24113_readreg(state, 0x00)) { + case 0x43: + info("unknown device\n"); + break; + case REV_CX24113: + info("CX24113\n"); + break; + default: + err("unsupported revision: %x\n", state->rev); + goto error; + } + state->ver = cx24113_readreg(state, 0x01); + info("version: %x\n", state->ver); + + /* create dvb_frontend */ + memcpy(&fe->ops.tuner_ops, &cx24113_tuner_ops, + sizeof(struct dvb_tuner_ops)); + fe->tuner_priv = state; + return fe; + +error: + kfree(state); + + return NULL; +} +EXPORT_SYMBOL(cx24113_attach); + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)"); + +MODULE_AUTHOR("Patrick Boettcher "); +MODULE_DESCRIPTION("DVB Frontend module for Conexant CX24113/CX24128hardware"); +MODULE_LICENSE("GPL"); + diff --git a/linux/drivers/media/dvb/frontends/cx24113.h b/linux/drivers/media/dvb/frontends/cx24113.h index d549041b2..5de0f7ffd 100644 --- a/linux/drivers/media/dvb/frontends/cx24113.h +++ b/linux/drivers/media/dvb/frontends/cx24113.h @@ -16,7 +16,7 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.= + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef CX24113_H @@ -30,10 +30,8 @@ struct cx24113_config { u32 xtal_khz; }; -/* TODO: #if defined(CONFIG_DVB_TUNER_CX24113) || \ - * (defined(CONFIG_DVB_TUNER_CX24113_MODULE) && defined(MODULE)) */ - -#if 0 +#if defined(CONFIG_DVB_TUNER_CX24113) || \ + (defined(CONFIG_DVB_TUNER_CX24113_MODULE) && defined(MODULE)) extern struct dvb_frontend *cx24113_attach(struct dvb_frontend *, const struct cx24113_config *config, struct i2c_adapter *i2c); -- cgit v1.2.3 From 752b7bb4231bff537eda1576e55e26ed7a363e38 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sun, 23 Nov 2008 14:27:57 -0500 Subject: cx18: Quiet a sometimes common warning that often has benign consequences From: Andy Walls 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. Priority: normal Signed-off-by: Andy Walls --- linux/drivers/media/video/cx18/cx18-mailbox.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/cx18/cx18-mailbox.c b/linux/drivers/media/video/cx18/cx18-mailbox.c index 420325be4..8415b9683 100644 --- a/linux/drivers/media/video/cx18/cx18-mailbox.c +++ b/linux/drivers/media/video/cx18/cx18-mailbox.c @@ -563,9 +563,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 d06ed8475f6edc68d8aee44a9e8c9cf0e87e29ef Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sun, 14 Dec 2008 16:52:12 -0500 Subject: cx18 Replace magic number 63 with CX18_MAX_FW_MDLS_PER_STREAM From: Andy Walls Removed magic number that referred to firmware limit on the number of MDLs the firmware can maintain for any stream at any one time. Priority: normal Signed-off-by: Andy Walls --- linux/drivers/media/video/cx18/cx18-driver.h | 5 +++-- linux/drivers/media/video/cx18/cx18-queue.c | 5 +++-- linux/drivers/media/video/cx18/cx18-streams.c | 5 +++-- 3 files changed, 9 insertions(+), 6 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/cx18/cx18-driver.h b/linux/drivers/media/video/cx18/cx18-driver.h index 018d98f94..0d2edebc3 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.h +++ b/linux/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/linux/drivers/media/video/cx18/cx18-queue.c b/linux/drivers/media/video/cx18/cx18-queue.c index a6b0666f0..2c58b0c47 100644 --- a/linux/drivers/media/video/cx18/cx18-queue.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-streams.c b/linux/drivers/media/video/cx18/cx18-streams.c index 9ed542624..acaf774d8 100644 --- a/linux/drivers/media/video/cx18/cx18-streams.c +++ b/linux/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 33d2dd93cb9c5d52dfee61dcd037b3dc290ada81 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sun, 14 Dec 2008 17:21:35 -0500 Subject: cx18: VBI comment corrections and comments about VBI issues From: Andy Walls VBI comment corrections to avoid future confusion about standards. Comments on cx18 VBI implementation shortcomings that need resolution. Priority: normal Signed-off-by: Andy Walls --- linux/drivers/media/video/cx18/cx18-driver.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/cx18/cx18-driver.c b/linux/drivers/media/video/cx18/cx18-driver.c index cea4735b6..f2a5b2c4d 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.c +++ b/linux/drivers/media/video/cx18/cx18-driver.c @@ -614,18 +614,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 */ @@ -929,6 +932,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 75db59d39a9a9df4b32e034d483c126aed873c1a Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sun, 14 Dec 2008 19:05:36 -0500 Subject: cx18: Convert some list manipulations to emphasize entries not lists From: Andy Walls Priority: normal Signed-off-by: Andy Walls --- linux/drivers/media/video/cx18/cx18-queue.c | 14 ++++++-------- linux/drivers/media/video/cx18/cx18-streams.c | 4 +--- 2 files changed, 7 insertions(+), 11 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/cx18/cx18-queue.c b/linux/drivers/media/video/cx18/cx18-queue.c index 2c58b0c47..8d9441e88 100644 --- a/linux/drivers/media/video/cx18/cx18-queue.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-streams.c b/linux/drivers/media/video/cx18/cx18-streams.c index acaf774d8..5acefd059 100644 --- a/linux/drivers/media/video/cx18/cx18-streams.c +++ b/linux/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 4a31b6de97b5e45eaff001a1e7a813964660f436 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sun, 23 Nov 2008 17:16:44 -0500 Subject: cx18: Use a known open task handle when setting stream CX2341x parameters From: Andy Walls 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. Priority: normal Signed-off-by: Andy Walls --- linux/drivers/media/video/cx18/cx18-controls.c | 5 ++++- linux/drivers/media/video/cx18/cx18-mailbox.c | 5 +++-- linux/drivers/media/video/cx18/cx18-mailbox.h | 7 +++++++ linux/drivers/media/video/cx18/cx18-streams.c | 6 +++++- 4 files changed, 19 insertions(+), 4 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/cx18/cx18-controls.c b/linux/drivers/media/video/cx18/cx18-controls.c index f46c7e5ed..17edf305d 100644 --- a/linux/drivers/media/video/cx18/cx18-controls.c +++ b/linux/drivers/media/video/cx18/cx18-controls.c @@ -259,6 +259,7 @@ int cx18_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c) return err; } if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) { + struct cx18_api_func_private priv; struct cx2341x_mpeg_params p = cx->params; int err = cx2341x_ext_ctrls(&p, atomic_read(&cx->ana_capturing), c, VIDIOC_S_EXT_CTRLS); @@ -278,7 +279,9 @@ int cx18_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c) fmt.fmt.pix.height = cx->params.height; cx18_av_cmd(cx, VIDIOC_S_FMT, &fmt); } - err = cx2341x_update(cx, cx18_api_func, &cx->params, &p); + priv.cx = cx; + priv.s = &cx->streams[id->type]; + err = cx2341x_update(&priv, cx18_api_func, &cx->params, &p); if (!err && cx->params.stream_vbi_fmt != p.stream_vbi_fmt) err = cx18_setup_vbi_fmt(cx, p.stream_vbi_fmt); cx->params = p; diff --git a/linux/drivers/media/video/cx18/cx18-mailbox.c b/linux/drivers/media/video/cx18/cx18-mailbox.c index eb5c31af6..f20fbf091 100644 --- a/linux/drivers/media/video/cx18/cx18-mailbox.c +++ b/linux/drivers/media/video/cx18/cx18-mailbox.c @@ -612,8 +612,9 @@ static int cx18_set_filter_param(struct cx18_stream *s) int cx18_api_func(void *priv, u32 cmd, int in, int out, u32 data[CX2341X_MBOX_MAX_DATA]) { - struct cx18 *cx = priv; - struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_MPG]; + struct cx18_api_func_private *api_priv = priv; + struct cx18 *cx = api_priv->cx; + struct cx18_stream *s = api_priv->s; switch (cmd) { case CX2341X_ENC_SET_OUTPUT_PORT: diff --git a/linux/drivers/media/video/cx18/cx18-mailbox.h b/linux/drivers/media/video/cx18/cx18-mailbox.h index 35104458e..a667f1ae4 100644 --- a/linux/drivers/media/video/cx18/cx18-mailbox.h +++ b/linux/drivers/media/video/cx18/cx18-mailbox.h @@ -79,6 +79,13 @@ struct cx18_mailbox { u32 error; }; +struct cx18_stream; + +struct cx18_api_func_private { + struct cx18 *cx; + struct cx18_stream *s; +}; + int cx18_api(struct cx18 *cx, u32 cmd, int args, u32 data[]); int cx18_vapi_result(struct cx18 *cx, u32 data[MAX_MB_ARGUMENTS], u32 cmd, int args, ...); diff --git a/linux/drivers/media/video/cx18/cx18-streams.c b/linux/drivers/media/video/cx18/cx18-streams.c index 5acefd059..f9e44c0cf 100644 --- a/linux/drivers/media/video/cx18/cx18-streams.c +++ b/linux/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 1b1d9fda3678e6dc40cc9670e03f18fd85f6942d Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Mon, 24 Nov 2008 10:28:58 +0100 Subject: gspca: Simplify the pkt_scan of stk014. From: Jean-Francois Moine Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/stk014.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/stk014.c b/linux/drivers/media/video/gspca/stk014.c index d9d64911f..74f57db53 100644 --- a/linux/drivers/media/video/gspca/stk014.c +++ b/linux/drivers/media/video/gspca/stk014.c @@ -424,10 +424,8 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, /* beginning of the frame */ #define STKHDRSZ 12 - gspca_frame_add(gspca_dev, INTER_PACKET, frame, - data + STKHDRSZ, len - STKHDRSZ); -#undef STKHDRSZ - return; + data += STKHDRSZ; + len -= STKHDRSZ; } gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); } -- cgit v1.2.3 From d83c8412dcfbd95cb0794db8f623f0a697097064 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Mon, 24 Nov 2008 10:38:21 +0100 Subject: gspca: Use msleep instead of mdelay. From: Jean-Francois Moine Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/gspca.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/gspca.c b/linux/drivers/media/video/gspca/gspca.c index 79be1e0e4..baf4aa524 100644 --- a/linux/drivers/media/video/gspca/gspca.c +++ b/linux/drivers/media/video/gspca/gspca.c @@ -650,7 +650,7 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) gspca_dev->streaming = 0; destroy_urbs(gspca_dev); if (ret == -ENOSPC) { - mdelay(20); /* wait for kill + msleep(20); /* wait for kill * complete */ break; /* try the previous alt */ } -- cgit v1.2.3 From 7dddcd8c1610cacae33a0ee67971d597c4bdaffb Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 24 Nov 2008 09:45:57 -0200 Subject: em28xx-alsa: implement another locking schema From: Mauro Carvalho Chehab Instead of using a spinlock, it is better to call the proper pcm stream locking schema. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-audio.c | 37 ++++++++++++++++--------- 1 file changed, 24 insertions(+), 13 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-audio.c b/linux/drivers/media/video/em28xx/em28xx-audio.c index b86b0870d..7bdbbf717 100644 --- a/linux/drivers/media/video/em28xx/em28xx-audio.c +++ b/linux/drivers/media/video/em28xx/em28xx-audio.c @@ -80,7 +80,9 @@ static void em28xx_audio_isocirq(struct urb *urb) struct em28xx *dev = urb->context; int i; unsigned int oldptr; +#ifdef NO_PCM_LOCK unsigned long flags; +#endif int period_elapsed = 0; int status; unsigned char *cp; @@ -106,9 +108,26 @@ static void em28xx_audio_isocirq(struct urb *urb) if (!length) continue; +#ifdef NO_PCM_LOCK spin_lock_irqsave(&dev->adev->slock, flags); - +#endif oldptr = dev->adev->hwptr_done_capture; + if (oldptr + length >= runtime->buffer_size) { + unsigned int cnt = + runtime->buffer_size - oldptr; + memcpy(runtime->dma_area + oldptr * stride, cp, + cnt * stride); + memcpy(runtime->dma_area, cp + cnt * stride, + length * stride - cnt * stride); + } else { + memcpy(runtime->dma_area + oldptr * stride, cp, + length * stride); + } + +#ifndef NO_PCM_LOCK + snd_pcm_stream_lock(substream); +#endif + dev->adev->hwptr_done_capture += length; if (dev->adev->hwptr_done_capture >= runtime->buffer_size) @@ -123,19 +142,11 @@ static void em28xx_audio_isocirq(struct urb *urb) period_elapsed = 1; } +#ifdef NO_PCM_LOCK spin_unlock_irqrestore(&dev->adev->slock, flags); - - if (oldptr + length >= runtime->buffer_size) { - unsigned int cnt = - runtime->buffer_size - oldptr; - memcpy(runtime->dma_area + oldptr * stride, cp, - cnt * stride); - memcpy(runtime->dma_area, cp + cnt * stride, - length * stride - cnt * stride); - } else { - memcpy(runtime->dma_area + oldptr * stride, cp, - length * stride); - } +#else + snd_pcm_stream_unlock(substream); +#endif } if (period_elapsed) snd_pcm_period_elapsed(substream); -- cgit v1.2.3 From a7992964c85bcd6fd2140245b904c9a54bc93238 Mon Sep 17 00:00:00 2001 From: Douglas Schilling Landgraf Date: Mon, 24 Nov 2008 10:51:20 -0200 Subject: em28xx: fix oops audio From: Douglas Schilling Landgraf Replaced usb_kill_usb for usb_unlink_usb (wait until urb to fully stop require USB core to put the calling process to sleep). Oops: http://www.kerneloops.org/raw.php?rawid=71799&msgid= Priority: high Signed-off-by: Douglas Schilling Landgraf --- linux/drivers/media/video/em28xx/em28xx-audio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-audio.c b/linux/drivers/media/video/em28xx/em28xx-audio.c index 7bdbbf717..c62455202 100644 --- a/linux/drivers/media/video/em28xx/em28xx-audio.c +++ b/linux/drivers/media/video/em28xx/em28xx-audio.c @@ -63,7 +63,7 @@ static int em28xx_isoc_audio_deinit(struct em28xx *dev) dprintk("Stopping isoc\n"); for (i = 0; i < EM28XX_AUDIO_BUFS; i++) { - usb_kill_urb(dev->adev->urb[i]); + usb_unlink_urb(dev->adev->urb[i]); usb_free_urb(dev->adev->urb[i]); dev->adev->urb[i] = NULL; } -- cgit v1.2.3 From e01ddb9b92a5b1f69f1a5a88bdb1ffb7bb984ef5 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 25 Nov 2008 07:03:31 -0200 Subject: em28xx: cleanup XCLK register usage From: Devin Heitmueller Convert over to setting the XCLK register usage with the new em28xx_write_reg() function. Thanks to Ray Lu from Empia for providing the em2860/2880 datasheet. Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-cards.c | 57 ++++++++++++++++++------- linux/drivers/media/video/em28xx/em28xx-reg.h | 18 ++++++++ 2 files changed, 60 insertions(+), 15 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 9177ea54b..9ba6eadb5 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -1401,7 +1401,9 @@ void em28xx_pre_card_setup(struct em28xx *dev) case EM2882_BOARD_PINNACLE_HYBRID_PRO: case EM2883_BOARD_KWORLD_HYBRID_A316: case EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600: - em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1); + em28xx_write_reg(dev, EM28XX_R0F_XCLK, + EM28XX_XCLK_IR_RC5_MODE | + EM28XX_XCLK_FREQUENCY_12MHZ); em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_100_KHZ); @@ -1415,7 +1417,9 @@ void em28xx_pre_card_setup(struct em28xx *dev) break; case EM2882_BOARD_TERRATEC_HYBRID_XS: - em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1); + em28xx_write_reg(dev, EM28XX_R0F_XCLK, + EM28XX_XCLK_IR_RC5_MODE | + EM28XX_XCLK_FREQUENCY_12MHZ); em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_100_KHZ); @@ -1437,7 +1441,9 @@ void em28xx_pre_card_setup(struct em28xx *dev) case EM2880_BOARD_KWORLD_DVB_310U: case EM2870_BOARD_KWORLD_350U: case EM2881_BOARD_DNT_DA2_HYBRID: - em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1); + em28xx_write_reg(dev, EM28XX_R0F_XCLK, + EM28XX_XCLK_IR_RC5_MODE | + EM28XX_XCLK_FREQUENCY_12MHZ); em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_100_KHZ); @@ -1456,7 +1462,9 @@ void em28xx_pre_card_setup(struct em28xx *dev) case EM2880_BOARD_MSI_DIGIVOX_AD: case EM2880_BOARD_MSI_DIGIVOX_AD_II: - em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1); + em28xx_write_reg(dev, EM28XX_R0F_XCLK, + EM28XX_XCLK_IR_RC5_MODE | + EM28XX_XCLK_FREQUENCY_12MHZ); em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_100_KHZ); @@ -1471,11 +1479,14 @@ void em28xx_pre_card_setup(struct em28xx *dev) case EM2750_BOARD_UNKNOWN: case EM2750_BOARD_DLCW_130: - em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x0a", 1); + em28xx_write_reg(dev, EM28XX_R0F_XCLK, + EM28XX_XCLK_FREQUENCY_48MHZ); break; case EM2861_BOARD_PLEXTOR_PX_TV100U: - em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1); + em28xx_write_reg(dev, EM28XX_R0F_XCLK, + EM28XX_XCLK_IR_RC5_MODE | + EM28XX_XCLK_FREQUENCY_12MHZ); em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_100_KHZ); @@ -1486,7 +1497,9 @@ void em28xx_pre_card_setup(struct em28xx *dev) case EM2861_BOARD_KWORLD_PVRTV_300U: case EM2880_BOARD_KWORLD_DVB_305U: - em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1); + em28xx_write_reg(dev, EM28XX_R0F_XCLK, + EM28XX_XCLK_IR_RC5_MODE | + EM28XX_XCLK_FREQUENCY_12MHZ); em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_100_KHZ); @@ -1498,7 +1511,9 @@ void em28xx_pre_card_setup(struct em28xx *dev) break; case EM2870_BOARD_KWORLD_355U: - em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1); + em28xx_write_reg(dev, EM28XX_R0F_XCLK, + EM28XX_XCLK_IR_RC5_MODE | + EM28XX_XCLK_FREQUENCY_12MHZ); em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_100_KHZ); @@ -1509,7 +1524,9 @@ void em28xx_pre_card_setup(struct em28xx *dev) break; case EM2870_BOARD_COMPRO_VIDEOMATE: - em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1); + em28xx_write_reg(dev, EM28XX_R0F_XCLK, + EM28XX_XCLK_IR_RC5_MODE | + EM28XX_XCLK_FREQUENCY_12MHZ); em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_100_KHZ); @@ -1530,7 +1547,9 @@ void em28xx_pre_card_setup(struct em28xx *dev) break; case EM2870_BOARD_TERRATEC_XS_MT2060: - em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1); + em28xx_write_reg(dev, EM28XX_R0F_XCLK, + EM28XX_XCLK_IR_RC5_MODE | + EM28XX_XCLK_FREQUENCY_12MHZ); em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_100_KHZ); @@ -1558,12 +1577,17 @@ void em28xx_pre_card_setup(struct em28xx *dev) em28xx_write_regs(dev, 0x08, "\xfe", 1); mdelay(70); /* switch em2880 rc protocol */ - em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x22", 1); + /* djh - I have serious doubts this is right... */ + em28xx_write_reg(dev, EM28XX_R0F_XCLK, + EM28XX_XCLK_IR_RC5_MODE | + EM28XX_XCLK_FREQUENCY_10MHZ); /* should be added ir_codes here */ break; case EM2820_BOARD_GADMEI_UTV310: - em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1); + em28xx_write_reg(dev, EM28XX_R0F_XCLK, + EM28XX_XCLK_IR_RC5_MODE | + EM28XX_XCLK_FREQUENCY_12MHZ); em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_100_KHZ); @@ -1572,8 +1596,9 @@ void em28xx_pre_card_setup(struct em28xx *dev) break; case EM2860_BOARD_GADMEI_UTV330: - /* Turn on IR */ - em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x07", 1); + em28xx_write_reg(dev, EM28XX_R0F_XCLK, + EM28XX_XCLK_IR_RC5_MODE | + EM28XX_XCLK_FREQUENCY_12MHZ); em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_100_KHZ); @@ -1581,7 +1606,9 @@ void em28xx_pre_card_setup(struct em28xx *dev) break; case EM2820_BOARD_MSI_VOX_USB_2: - em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1); + em28xx_write_reg(dev, EM28XX_R0F_XCLK, + EM28XX_XCLK_IR_RC5_MODE | + EM28XX_XCLK_FREQUENCY_12MHZ); em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_100_KHZ); diff --git a/linux/drivers/media/video/em28xx/em28xx-reg.h b/linux/drivers/media/video/em28xx/em28xx-reg.h index 98e95054e..45d588c3a 100644 --- a/linux/drivers/media/video/em28xx/em28xx-reg.h +++ b/linux/drivers/media/video/em28xx/em28xx-reg.h @@ -51,6 +51,24 @@ #define EM28XX_R0E_AUDIOSRC 0x0e #define EM28XX_R0F_XCLK 0x0f +/* em28xx XCLK Register (0x0f) */ +#define EM28XX_XCLK_AUDIO_UNMUTE 0x80 /* otherwise audio muted */ +#define EM28XX_XCLK_I2S_MSB_TIMING 0x40 /* otherwise standard timing */ +#define EM28XX_XCLK_IR_RC5_MODE 0x20 /* otherwise NEC mode */ +#define EM28XX_XCLK_IR_NEC_CHK_PARITY 0x10 +#define EM28XX_XCLK_FREQUENCY_30MHZ 0x00 /* Freq. select (bits [3-0]) */ +#define EM28XX_XCLK_FREQUENCY_15MHZ 0x01 +#define EM28XX_XCLK_FREQUENCY_10MHZ 0x02 +#define EM28XX_XCLK_FREQUENCY_7_5MHZ 0x03 +#define EM28XX_XCLK_FREQUENCY_6MHZ 0x04 +#define EM28XX_XCLK_FREQUENCY_5MHZ 0x05 +#define EM28XX_XCLK_FREQUENCY_4_3MHZ 0x06 +#define EM28XX_XCLK_FREQUENCY_12MHZ 0x07 +#define EM28XX_XCLK_FREQUENCY_20MHZ 0x08 +#define EM28XX_XCLK_FREQUENCY_20MHZ_2 0x09 +#define EM28XX_XCLK_FREQUENCY_48MHZ 0x0a +#define EM28XX_XCLK_FREQUENCY_24MHZ 0x0b + #define EM28XX_R10_VINMODE 0x10 #define EM28XX_R11_VINCTRL 0x11 #define EM28XX_R12_VINENABLE 0x12 /* */ -- cgit v1.2.3 From 88682d2d8d89ab9c29ec079a2e849724e3b4b77d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 25 Nov 2008 07:04:02 -0200 Subject: em28xx: Cleanup GPIO/GPO setup code From: Devin Heitmueller Cleanup the calls to set the GPIOs and GPOs for various devices, replacing the register number with the #define from em28xx-reg.h and converting over to using em28xx_write_reg() Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-cards.c | 36 ++++++++++++------------- 1 file changed, 18 insertions(+), 18 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 9ba6eadb5..7a304d8fe 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -1492,7 +1492,7 @@ void em28xx_pre_card_setup(struct em28xx *dev) EM28XX_I2C_FREQ_100_KHZ); /* FIXME guess */ /* Turn on analog audio output */ - em28xx_write_regs_req(dev, 0x00, 0x08, "\xfd", 1); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd); break; case EM2861_BOARD_KWORLD_PVRTV_300U: @@ -1504,9 +1504,9 @@ void em28xx_pre_card_setup(struct em28xx *dev) EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_100_KHZ); msleep(10); - em28xx_write_regs(dev, 0x08, "\x6d", 1); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0x6d); msleep(10); - em28xx_write_regs(dev, 0x08, "\x7d", 1); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0x7d); msleep(10); break; @@ -1532,17 +1532,17 @@ void em28xx_pre_card_setup(struct em28xx *dev) EM28XX_I2C_FREQ_100_KHZ); /* TODO: someone can do some cleanup here... not everything's needed */ - em28xx_write_regs(dev, 0x04, "\x00", 1); + em28xx_write_reg(dev, EM2880_R04_GPO, 0x00); msleep(10); - em28xx_write_regs(dev, 0x04, "\x01", 1); + em28xx_write_reg(dev, EM2880_R04_GPO, 0x01); msleep(10); - em28xx_write_regs(dev, 0x08, "\xfd", 1); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd); mdelay(70); - em28xx_write_regs(dev, 0x08, "\xfc", 1); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfc); mdelay(70); - em28xx_write_regs(dev, 0x08, "\xdc", 1); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xdc); mdelay(70); - em28xx_write_regs(dev, 0x08, "\xfc", 1); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfc); mdelay(70); break; @@ -1555,11 +1555,11 @@ void em28xx_pre_card_setup(struct em28xx *dev) EM28XX_I2C_FREQ_100_KHZ); /* this device needs some gpio writes to get the DVB-T demod work */ - em28xx_write_regs(dev, 0x08, "\xfe", 1); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe); mdelay(70); - em28xx_write_regs(dev, 0x08, "\xde", 1); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xde); mdelay(70); - dev->em28xx_write_regs(dev, 0x08, "\xfe", 1); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe); mdelay(70); break; @@ -1570,11 +1570,11 @@ void em28xx_pre_card_setup(struct em28xx *dev) /* this device needs some gpio writes to get the DVB-T demod work */ - em28xx_write_regs(dev, 0x08, "\xfe", 1); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe); mdelay(70); - em28xx_write_regs(dev, 0x08, "\xde", 1); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xde); mdelay(70); - em28xx_write_regs(dev, 0x08, "\xfe", 1); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe); mdelay(70); /* switch em2880 rc protocol */ /* djh - I have serious doubts this is right... */ @@ -1592,7 +1592,7 @@ void em28xx_pre_card_setup(struct em28xx *dev) EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_100_KHZ); /* Turn on analog audio output */ - em28xx_write_regs_req(dev, 0x00, 0x08, "\xfd", 1); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd); break; case EM2860_BOARD_GADMEI_UTV330: @@ -1613,7 +1613,7 @@ void em28xx_pre_card_setup(struct em28xx *dev) EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_100_KHZ); /* enables audio for that device */ - em28xx_write_regs_req(dev, 0x00, 0x08, "\xfd", 1); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd); break; case EM2874_BOARD_PINNACLE_PCTV_80E: @@ -1866,7 +1866,7 @@ void em28xx_card_setup(struct em28xx *dev) } case EM2820_BOARD_KWORLD_PVRTV2800RF: /* GPIO enables sound on KWORLD PVR TV 2800RF */ - em28xx_write_regs_req(dev, 0x00, 0x08, "\xf9", 1); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xf9); break; case EM2820_BOARD_UNKNOWN: case EM2800_BOARD_UNKNOWN: -- cgit v1.2.3 From c8686bdb02ff7ea276b796993fee13626e52ccea Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 25 Nov 2008 08:05:06 -0200 Subject: em28xx: Properly handles XCLK and I2C speed From: Mauro Carvalho Chehab The previous patches removed XCLK and I2C magic. Now, we finally know what those registers do. Also, only a very few cards need different setups for those. Instead of keeping the setups for those values inside the per-device hack magic switch, move the uncommon values to the board-specific struct, and have a common setup for all other boards. So, almost 100 lines of hacking magic were removed. A co-lateral effect of this patch is that it also fixes a bug at em28xx-core, where xclk were set, without taking any care about not overriding a previous xclk setup. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-cards.c | 158 +++++------------------- linux/drivers/media/video/em28xx/em28xx-core.c | 8 +- linux/drivers/media/video/em28xx/em28xx.h | 6 +- 3 files changed, 39 insertions(+), 133 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 7a304d8fe..f6815ae85 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -56,6 +56,7 @@ struct em28xx_board em28xx_boards[] = { [EM2750_BOARD_UNKNOWN] = { .name = "Unknown EM2750/EM2751 webcam grabber", .vchannels = 1, + .xclk = EM28XX_XCLK_FREQUENCY_48MHZ, .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = 0, @@ -79,15 +80,15 @@ struct em28xx_board em28xx_boards[] = { } }, }, [EM2820_BOARD_UNKNOWN] = { - .name = "Unknown EM2750/28xx video grabber", - .is_em2800 = 0, - .tuner_type = TUNER_ABSENT, + .name = "Unknown EM2750/28xx video grabber", + .tuner_type = TUNER_ABSENT, }, [EM2750_BOARD_DLCW_130] = { /* Beijing Huaqi Information Digital Technology Co., Ltd */ .name = "Huaqi DLCW-130", .valid = EM28XX_BOARD_NOT_VALIDATED, .vchannels = 1, + .xclk = EM28XX_XCLK_FREQUENCY_48MHZ, .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = 0, @@ -515,6 +516,10 @@ struct em28xx_board em28xx_boards[] = { .name = "Pinnacle PCTV DVB-T", .valid = EM28XX_BOARD_NOT_VALIDATED, .tuner_type = TUNER_ABSENT, /* MT2060 */ + + /* djh - I have serious doubts this is right... */ + .xclk = EM28XX_XCLK_IR_RC5_MODE | + EM28XX_XCLK_FREQUENCY_10MHZ, }, [EM2870_BOARD_COMPRO_VIDEOMATE] = { .name = "Compro, VideoMate U3", @@ -548,7 +553,7 @@ struct em28xx_board em28xx_boards[] = { .tda9887_conf = TDA9887_PRESENT, .tuner_type = TUNER_XC2028, .mts_firmware = 1, - .has_dvb = 1, + .has_dvb = 1, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -590,7 +595,6 @@ struct em28xx_board em28xx_boards[] = { .vchannels = 3, .tuner_type = TUNER_XC2028, .mts_firmware = 1, - .has_12mhz_i2s = 1, .has_dvb = 1, .ir_codes = ir_codes_hauppauge_new, .decoder = EM28XX_TVP5150, @@ -613,7 +617,6 @@ struct em28xx_board em28xx_boards[] = { .vchannels = 3, .tuner_type = TUNER_XC2028, .mts_firmware = 1, - .has_12mhz_i2s = 1, .has_dvb = 1, .ir_codes = ir_codes_pinnacle_pctv_hd, .decoder = EM28XX_TVP5150, @@ -636,7 +639,6 @@ struct em28xx_board em28xx_boards[] = { .vchannels = 3, .tuner_type = TUNER_XC2028, .mts_firmware = 1, - .has_12mhz_i2s = 1, .has_dvb = 1, .ir_codes = ir_codes_ati_tv_wonder_hd_600, .decoder = EM28XX_TVP5150, @@ -1092,9 +1094,12 @@ struct em28xx_board em28xx_boards[] = { .name = "Pinnacle PCTV HD Mini", .vchannels = 0, .tuner_type = TUNER_ABSENT, - .has_dvb = 1, - .ir_codes = ir_codes_pinnacle_pctv_hd, + .has_dvb = 1, + .ir_codes = ir_codes_pinnacle_pctv_hd, .decoder = EM28XX_NODECODER, + .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | + EM2874_I2C_SECONDARY_BUS_SELECT | + EM28XX_I2C_FREQ_400_KHZ, #ifdef DJH_DEBUG .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -1331,7 +1336,8 @@ static void em28xx_set_model(struct em28xx *dev) dev->tda9887_conf = em28xx_boards[dev->model].tda9887_conf; dev->decoder = em28xx_boards[dev->model].decoder; dev->video_inputs = em28xx_boards[dev->model].vchannels; - dev->has_12mhz_i2s = em28xx_boards[dev->model].has_12mhz_i2s; + dev->xclk = em28xx_boards[dev->model].xclk; + dev->i2c_speed = em28xx_boards[dev->model].i2c_speed; dev->max_range_640_480 = em28xx_boards[dev->model].max_range_640_480; dev->has_dvb = em28xx_boards[dev->model].has_dvb; dev->has_snapshot_button = em28xx_boards[dev->model].has_snapshot_button; @@ -1390,6 +1396,21 @@ void em28xx_pre_card_setup(struct em28xx *dev) em28xx_set_model(dev); + /* Those are the default values for the majority of boards + Use those values if not specified otherwise at boards entry + */ + if (!dev->xclk) + dev->xclk = EM28XX_XCLK_IR_RC5_MODE | + EM28XX_XCLK_FREQUENCY_12MHZ; + + if (!dev->i2c_speed) + dev->i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | + EM28XX_I2C_FREQ_100_KHZ; + + em28xx_write_reg(dev, EM28XX_R0F_XCLK, dev->xclk & 0x7f); + em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, dev->i2c_speed); + msleep(50); + /* request some modules */ switch (dev->model) { case EM2880_BOARD_TERRATEC_PRODIGY_XS: @@ -1401,14 +1422,6 @@ void em28xx_pre_card_setup(struct em28xx *dev) case EM2882_BOARD_PINNACLE_HYBRID_PRO: case EM2883_BOARD_KWORLD_HYBRID_A316: case EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600: - em28xx_write_reg(dev, EM28XX_R0F_XCLK, - EM28XX_XCLK_IR_RC5_MODE | - EM28XX_XCLK_FREQUENCY_12MHZ); - em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, - EM28XX_I2C_CLK_WAIT_ENABLE | - EM28XX_I2C_FREQ_100_KHZ); - msleep(50); - /* Sets GPO/GPIO sequences for this device */ dev->analog_gpio = hauppauge_wintv_hvr_900_analog; dev->digital_gpio = hauppauge_wintv_hvr_900_digital; @@ -1417,16 +1430,6 @@ void em28xx_pre_card_setup(struct em28xx *dev) break; case EM2882_BOARD_TERRATEC_HYBRID_XS: - em28xx_write_reg(dev, EM28XX_R0F_XCLK, - EM28XX_XCLK_IR_RC5_MODE | - EM28XX_XCLK_FREQUENCY_12MHZ); - em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, - EM28XX_I2C_CLK_WAIT_ENABLE | - EM28XX_I2C_FREQ_100_KHZ); - msleep(50); - - /* should be added ir_codes here */ - /* Sets GPO/GPIO sequences for this device */ dev->analog_gpio = hauppauge_wintv_hvr_900_analog; dev->digital_gpio = hauppauge_wintv_hvr_900_digital; @@ -1441,14 +1444,6 @@ void em28xx_pre_card_setup(struct em28xx *dev) case EM2880_BOARD_KWORLD_DVB_310U: case EM2870_BOARD_KWORLD_350U: case EM2881_BOARD_DNT_DA2_HYBRID: - em28xx_write_reg(dev, EM28XX_R0F_XCLK, - EM28XX_XCLK_IR_RC5_MODE | - EM28XX_XCLK_FREQUENCY_12MHZ); - em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, - EM28XX_I2C_CLK_WAIT_ENABLE | - EM28XX_I2C_FREQ_100_KHZ); - msleep(50); - /* NOTE: EM2881_DNT_DA2_HYBRID spend 140 msleep for digital and analog commands. If this commands doesn't work, add this timer. */ @@ -1462,14 +1457,6 @@ void em28xx_pre_card_setup(struct em28xx *dev) case EM2880_BOARD_MSI_DIGIVOX_AD: case EM2880_BOARD_MSI_DIGIVOX_AD_II: - em28xx_write_reg(dev, EM28XX_R0F_XCLK, - EM28XX_XCLK_IR_RC5_MODE | - EM28XX_XCLK_FREQUENCY_12MHZ); - em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, - EM28XX_I2C_CLK_WAIT_ENABLE | - EM28XX_I2C_FREQ_100_KHZ); - msleep(50); - /* Sets GPO/GPIO sequences for this device */ dev->analog_gpio = em2880_msi_digivox_ad_analog; dev->digital_gpio = em2880_msi_digivox_ad_digital; @@ -1477,19 +1464,7 @@ void em28xx_pre_card_setup(struct em28xx *dev) dev->tun_digital_gpio = default_callback; break; - case EM2750_BOARD_UNKNOWN: - case EM2750_BOARD_DLCW_130: - em28xx_write_reg(dev, EM28XX_R0F_XCLK, - EM28XX_XCLK_FREQUENCY_48MHZ); - break; - case EM2861_BOARD_PLEXTOR_PX_TV100U: - em28xx_write_reg(dev, EM28XX_R0F_XCLK, - EM28XX_XCLK_IR_RC5_MODE | - EM28XX_XCLK_FREQUENCY_12MHZ); - em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, - EM28XX_I2C_CLK_WAIT_ENABLE | - EM28XX_I2C_FREQ_100_KHZ); /* FIXME guess */ /* Turn on analog audio output */ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd); @@ -1497,13 +1472,6 @@ void em28xx_pre_card_setup(struct em28xx *dev) case EM2861_BOARD_KWORLD_PVRTV_300U: case EM2880_BOARD_KWORLD_DVB_305U: - em28xx_write_reg(dev, EM28XX_R0F_XCLK, - EM28XX_XCLK_IR_RC5_MODE | - EM28XX_XCLK_FREQUENCY_12MHZ); - em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, - EM28XX_I2C_CLK_WAIT_ENABLE | - EM28XX_I2C_FREQ_100_KHZ); - msleep(10); em28xx_write_reg(dev, EM28XX_R08_GPIO, 0x6d); msleep(10); em28xx_write_reg(dev, EM28XX_R08_GPIO, 0x7d); @@ -1511,25 +1479,11 @@ void em28xx_pre_card_setup(struct em28xx *dev) break; case EM2870_BOARD_KWORLD_355U: - em28xx_write_reg(dev, EM28XX_R0F_XCLK, - EM28XX_XCLK_IR_RC5_MODE | - EM28XX_XCLK_FREQUENCY_12MHZ); - em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, - EM28XX_I2C_CLK_WAIT_ENABLE | - EM28XX_I2C_FREQ_100_KHZ); - msleep(50); - /* Sets GPO/GPIO sequences for this device */ dev->digital_gpio = em2870_kworld_355u_digital; break; case EM2870_BOARD_COMPRO_VIDEOMATE: - em28xx_write_reg(dev, EM28XX_R0F_XCLK, - EM28XX_XCLK_IR_RC5_MODE | - EM28XX_XCLK_FREQUENCY_12MHZ); - em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, - EM28XX_I2C_CLK_WAIT_ENABLE | - EM28XX_I2C_FREQ_100_KHZ); /* TODO: someone can do some cleanup here... not everything's needed */ em28xx_write_reg(dev, EM2880_R04_GPO, 0x00); @@ -1547,12 +1501,6 @@ void em28xx_pre_card_setup(struct em28xx *dev) break; case EM2870_BOARD_TERRATEC_XS_MT2060: - em28xx_write_reg(dev, EM28XX_R0F_XCLK, - EM28XX_XCLK_IR_RC5_MODE | - EM28XX_XCLK_FREQUENCY_12MHZ); - em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, - EM28XX_I2C_CLK_WAIT_ENABLE | - EM28XX_I2C_FREQ_100_KHZ); /* this device needs some gpio writes to get the DVB-T demod work */ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe); @@ -1564,10 +1512,6 @@ void em28xx_pre_card_setup(struct em28xx *dev) break; case EM2870_BOARD_PINNACLE_PCTV_DVB: - em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, - EM28XX_I2C_CLK_WAIT_ENABLE | - EM28XX_I2C_FREQ_100_KHZ); - /* this device needs some gpio writes to get the DVB-T demod work */ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe); @@ -1576,53 +1520,15 @@ void em28xx_pre_card_setup(struct em28xx *dev) mdelay(70); em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe); mdelay(70); - /* switch em2880 rc protocol */ - /* djh - I have serious doubts this is right... */ - em28xx_write_reg(dev, EM28XX_R0F_XCLK, - EM28XX_XCLK_IR_RC5_MODE | - EM28XX_XCLK_FREQUENCY_10MHZ); - /* should be added ir_codes here */ break; case EM2820_BOARD_GADMEI_UTV310: - em28xx_write_reg(dev, EM28XX_R0F_XCLK, - EM28XX_XCLK_IR_RC5_MODE | - EM28XX_XCLK_FREQUENCY_12MHZ); - em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, - EM28XX_I2C_CLK_WAIT_ENABLE | - EM28XX_I2C_FREQ_100_KHZ); - /* Turn on analog audio output */ - em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd); - break; - - case EM2860_BOARD_GADMEI_UTV330: - em28xx_write_reg(dev, EM28XX_R0F_XCLK, - EM28XX_XCLK_IR_RC5_MODE | - EM28XX_XCLK_FREQUENCY_12MHZ); - em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, - EM28XX_I2C_CLK_WAIT_ENABLE | - EM28XX_I2C_FREQ_100_KHZ); - /* should be added ir_codes here */ - break; - case EM2820_BOARD_MSI_VOX_USB_2: - em28xx_write_reg(dev, EM28XX_R0F_XCLK, - EM28XX_XCLK_IR_RC5_MODE | - EM28XX_XCLK_FREQUENCY_12MHZ); - em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, - EM28XX_I2C_CLK_WAIT_ENABLE | - EM28XX_I2C_FREQ_100_KHZ); - /* enables audio for that device */ + /* enables audio for that devices */ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd); break; case EM2874_BOARD_PINNACLE_PCTV_80E: - /* Set 400 KHz clock and select secondary i2c bus */ - em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, - EM28XX_I2C_CLK_WAIT_ENABLE | - EM2874_I2C_SECONDARY_BUS_SELECT | - EM28XX_I2C_FREQ_400_KHZ); - dev->digital_gpio = em2874_pinnacle_80e_digital; break; } diff --git a/linux/drivers/media/video/em28xx/em28xx-core.c b/linux/drivers/media/video/em28xx/em28xx-core.c index eb8515895..64503c04a 100644 --- a/linux/drivers/media/video/em28xx/em28xx-core.c +++ b/linux/drivers/media/video/em28xx/em28xx-core.c @@ -399,7 +399,7 @@ struct em28xx_vol_table outputs[] = { int em28xx_audio_analog_set(struct em28xx *dev) { int ret, i; - u8 xclk = 0x07; + u8 xclk; if (!dev->audio_mode.has_audio) return 0; @@ -417,13 +417,11 @@ int em28xx_audio_analog_set(struct em28xx *dev) } } - if (dev->has_12mhz_i2s) - xclk |= 0x20; - + xclk = dev->xclk & 0x7f; if (!dev->mute) xclk |= 0x80; - ret = em28xx_write_reg_bits(dev, EM28XX_R0F_XCLK, xclk, 0xa7); + ret = em28xx_write_reg(dev, EM28XX_R0F_XCLK, xclk); if (ret < 0) return ret; msleep(10); diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index 151668c60..c6f7870b7 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -339,12 +339,13 @@ struct em28xx_board { unsigned int is_em2800:1; unsigned int has_msp34xx:1; unsigned int mts_firmware:1; - unsigned int has_12mhz_i2s:1; unsigned int max_range_640_480:1; unsigned int has_dvb:1; unsigned int has_snapshot_button:1; unsigned int valid:1; + unsigned char xclk, i2c_speed; + enum em28xx_decoder decoder; struct em28xx_input input[MAX_EM28XX_INPUT]; @@ -431,12 +432,13 @@ struct em28xx { unsigned int stream_on:1; /* Locks streams */ unsigned int has_audio_class:1; unsigned int has_alsa_audio:1; - unsigned int has_12mhz_i2s:1; unsigned int max_range_640_480:1; unsigned int has_dvb:1; unsigned int has_snapshot_button:1; unsigned int valid:1; /* report for validated boards */ + unsigned char xclk, i2c_speed; + struct em28xx_IR *ir; /* Some older em28xx chips needs a waiting time after writing */ -- cgit v1.2.3 From aadce2c5e2229edf817d779c20b5bcf74e12ac1a Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 24 Nov 2008 13:53:22 +0100 Subject: em28xx: fix compile warning From: Hans Verkuil Label fail_unreg is no longer used. Priority: normal Signed-off-by: Hans Verkuil --- linux/drivers/media/video/em28xx/em28xx-video.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index 5da519bfa..634b82f4b 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -2205,8 +2205,6 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, return 0; -fail_unreg: - em28xx_release_resources(dev); fail_reg_devices: mutex_unlock(&dev->lock); return retval; -- cgit v1.2.3 From 3738acdddc0414a41b7256e8a40a19f27106dccb Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 29 Nov 2008 16:45:59 +0100 Subject: cs53l32a: convert to v4l2_subdev. From: Hans Verkuil Priority: normal Signed-off-by: Hans Verkuil --- linux/drivers/media/video/cs53l32a.c | 188 +++++++++++++++++++++-------------- 1 file changed, 112 insertions(+), 76 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/cs53l32a.c b/linux/drivers/media/video/cs53l32a.c index 68f31270c..0620c4af4 100644 --- a/linux/drivers/media/video/cs53l32a.c +++ b/linux/drivers/media/video/cs53l32a.c @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include #include #include "compat.h" @@ -48,84 +48,104 @@ I2C_CLIENT_INSMOD; /* ----------------------------------------------------------------------- */ -static int cs53l32a_write(struct i2c_client *client, u8 reg, u8 value) +static int cs53l32a_write(struct v4l2_subdev *sd, u8 reg, u8 value) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + return i2c_smbus_write_byte_data(client, reg, value); } -static int cs53l32a_read(struct i2c_client *client, u8 reg) +static int cs53l32a_read(struct v4l2_subdev *sd, u8 reg) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + return i2c_smbus_read_byte_data(client, reg); } -static int cs53l32a_command(struct i2c_client *client, unsigned cmd, void *arg) +static int cs53l32a_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) { - struct v4l2_routing *route = arg; - struct v4l2_control *ctrl = arg; - - switch (cmd) { - case VIDIOC_INT_G_AUDIO_ROUTING: - route->input = (cs53l32a_read(client, 0x01) >> 4) & 3; - route->output = 0; - break; - - case VIDIOC_INT_S_AUDIO_ROUTING: - /* There are 2 physical inputs, but the second input can be - placed in two modes, the first mode bypasses the PGA (gain), - the second goes through the PGA. Hence there are three - possible inputs to choose from. */ - if (route->input > 2) { - v4l_err(client, "Invalid input %d.\n", route->input); - return -EINVAL; - } - cs53l32a_write(client, 0x01, 0x01 + (route->input << 4)); - break; - - case VIDIOC_G_CTRL: - if (ctrl->id == V4L2_CID_AUDIO_MUTE) { - ctrl->value = (cs53l32a_read(client, 0x03) & 0xc0) != 0; - break; - } - if (ctrl->id != V4L2_CID_AUDIO_VOLUME) - return -EINVAL; - ctrl->value = (s8)cs53l32a_read(client, 0x04); - break; - - case VIDIOC_S_CTRL: - if (ctrl->id == V4L2_CID_AUDIO_MUTE) { - cs53l32a_write(client, 0x03, ctrl->value ? 0xf0 : 0x30); - break; - } - if (ctrl->id != V4L2_CID_AUDIO_VOLUME) - return -EINVAL; - if (ctrl->value > 12 || ctrl->value < -96) - return -EINVAL; - cs53l32a_write(client, 0x04, (u8) ctrl->value); - cs53l32a_write(client, 0x05, (u8) ctrl->value); - break; - - case VIDIOC_G_CHIP_IDENT: - return v4l2_chip_ident_i2c_client(client, - arg, V4L2_IDENT_CS53l32A, 0); - - case VIDIOC_LOG_STATUS: - { - u8 v = cs53l32a_read(client, 0x01); - u8 m = cs53l32a_read(client, 0x03); - s8 vol = cs53l32a_read(client, 0x04); - - v4l_info(client, "Input: %d%s\n", (v >> 4) & 3, - (m & 0xC0) ? " (muted)" : ""); - v4l_info(client, "Volume: %d dB\n", vol); - break; - } - - default: + /* There are 2 physical inputs, but the second input can be + placed in two modes, the first mode bypasses the PGA (gain), + the second goes through the PGA. Hence there are three + possible inputs to choose from. */ + if (route->input > 2) { + v4l2_err(sd, "Invalid input %d.\n", route->input); return -EINVAL; } + cs53l32a_write(sd, 0x01, 0x01 + (route->input << 4)); + return 0; +} + +static int cs53l32a_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + if (ctrl->id == V4L2_CID_AUDIO_MUTE) { + ctrl->value = (cs53l32a_read(sd, 0x03) & 0xc0) != 0; + return 0; + } + if (ctrl->id != V4L2_CID_AUDIO_VOLUME) + return -EINVAL; + ctrl->value = (s8)cs53l32a_read(sd, 0x04); + return 0; +} + +static int cs53l32a_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + if (ctrl->id == V4L2_CID_AUDIO_MUTE) { + cs53l32a_write(sd, 0x03, ctrl->value ? 0xf0 : 0x30); + return 0; + } + if (ctrl->id != V4L2_CID_AUDIO_VOLUME) + return -EINVAL; + if (ctrl->value > 12 || ctrl->value < -96) + return -EINVAL; + cs53l32a_write(sd, 0x04, (u8) ctrl->value); + cs53l32a_write(sd, 0x05, (u8) ctrl->value); return 0; } +static int cs53l32a_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, + chip, V4L2_IDENT_CS53l32A, 0); +} + +static int cs53l32a_log_status(struct v4l2_subdev *sd) +{ + u8 v = cs53l32a_read(sd, 0x01); + u8 m = cs53l32a_read(sd, 0x03); + s8 vol = cs53l32a_read(sd, 0x04); + + v4l2_info(sd, "Input: %d%s\n", (v >> 4) & 3, + (m & 0xC0) ? " (muted)" : ""); + v4l2_info(sd, "Volume: %d dB\n", vol); + return 0; +} + +static int cs53l32a_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops cs53l32a_core_ops = { + .log_status = cs53l32a_log_status, + .g_chip_ident = cs53l32a_g_chip_ident, + .g_ctrl = cs53l32a_g_ctrl, + .s_ctrl = cs53l32a_s_ctrl, +}; + +static const struct v4l2_subdev_audio_ops cs53l32a_audio_ops = { + .s_routing = cs53l32a_s_routing, +}; + +static const struct v4l2_subdev_ops cs53l32a_ops = { + .core = &cs53l32a_core_ops, + .audio = &cs53l32a_audio_ops, +}; + /* ----------------------------------------------------------------------- */ /* i2c implementation */ @@ -138,6 +158,7 @@ static int cs53l32a_command(struct i2c_client *client, unsigned cmd, void *arg) static int cs53l32a_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct v4l2_subdev *sd; int i; /* Check if the adapter supports the needed features */ @@ -154,32 +175,46 @@ static int cs53l32a_probe(struct i2c_client *client, v4l_info(client, "chip found @ 0x%x (%s)\n", client->addr << 1, client->adapter->name); + sd = kmalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); + if (sd == NULL) + return -ENOMEM; + v4l2_i2c_subdev_init(sd, client, &cs53l32a_ops); + for (i = 1; i <= 7; i++) { - u8 v = cs53l32a_read(client, i); + u8 v = cs53l32a_read(sd, i); - v4l_dbg(1, debug, client, "Read Reg %d %02x\n", i, v); + v4l2_dbg(1, debug, sd, "Read Reg %d %02x\n", i, v); } /* Set cs53l32a internal register for Adaptec 2010/2410 setup */ - cs53l32a_write(client, 0x01, (u8) 0x21); - cs53l32a_write(client, 0x02, (u8) 0x29); - cs53l32a_write(client, 0x03, (u8) 0x30); - cs53l32a_write(client, 0x04, (u8) 0x00); - cs53l32a_write(client, 0x05, (u8) 0x00); - cs53l32a_write(client, 0x06, (u8) 0x00); - cs53l32a_write(client, 0x07, (u8) 0x00); + cs53l32a_write(sd, 0x01, (u8) 0x21); + cs53l32a_write(sd, 0x02, (u8) 0x29); + cs53l32a_write(sd, 0x03, (u8) 0x30); + cs53l32a_write(sd, 0x04, (u8) 0x00); + cs53l32a_write(sd, 0x05, (u8) 0x00); + cs53l32a_write(sd, 0x06, (u8) 0x00); + cs53l32a_write(sd, 0x07, (u8) 0x00); /* Display results, should be 0x21,0x29,0x30,0x00,0x00,0x00,0x00 */ for (i = 1; i <= 7; i++) { - u8 v = cs53l32a_read(client, i); + u8 v = cs53l32a_read(sd, i); - v4l_dbg(1, debug, client, "Read Reg %d %02x\n", i, v); + v4l2_dbg(1, debug, sd, "Read Reg %d %02x\n", i, v); } return 0; } +static int cs53l32a_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(sd); + return 0; +} + #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) static const struct i2c_device_id cs53l32a_id[] = { { "cs53l32a", 0 }, @@ -192,6 +227,7 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "cs53l32a", .driverid = I2C_DRIVERID_CS53L32A, .command = cs53l32a_command, + .remove = cs53l32a_remove, .probe = cs53l32a_probe, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) .id_table = cs53l32a_id, -- cgit v1.2.3 From 91617d3e59f1badd43dd28ea7f3157c7781d08dc Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 29 Nov 2008 16:50:06 +0100 Subject: cx25840: convert to v4l2_subdev. From: Hans Verkuil Priority: normal Signed-off-by: Hans Verkuil --- linux/drivers/media/video/cx25840/cx25840-audio.c | 14 +- linux/drivers/media/video/cx25840/cx25840-core.c | 447 ++++++++++++--------- linux/drivers/media/video/cx25840/cx25840-core.h | 7 + .../drivers/media/video/cx25840/cx25840-firmware.c | 2 +- linux/drivers/media/video/cx25840/cx25840-vbi.c | 2 +- 5 files changed, 279 insertions(+), 193 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/cx25840/cx25840-audio.c b/linux/drivers/media/video/cx25840/cx25840-audio.c index 8e134312c..3ee7d7a7d 100644 --- a/linux/drivers/media/video/cx25840/cx25840-audio.c +++ b/linux/drivers/media/video/cx25840/cx25840-audio.c @@ -26,7 +26,7 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); if (freq != 32000 && freq != 44100 && freq != 48000) return -EINVAL; @@ -194,7 +194,7 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq) void cx25840_audio_set_path(struct i2c_client *client) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); /* assert soft reset */ cx25840_and_or(client, 0x810, ~0x1, 0x01); @@ -236,7 +236,7 @@ void cx25840_audio_set_path(struct i2c_client *client) static int get_volume(struct i2c_client *client) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); int vol; if (state->unmute_volume >= 0) @@ -253,7 +253,7 @@ static int get_volume(struct i2c_client *client) static void set_volume(struct i2c_client *client, int volume) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); int vol; if (state->unmute_volume >= 0) { @@ -341,14 +341,14 @@ static void set_balance(struct i2c_client *client, int balance) static int get_mute(struct i2c_client *client) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); return state->unmute_volume >= 0; } static void set_mute(struct i2c_client *client, int mute) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); if (mute && state->unmute_volume == -1) { int vol = get_volume(client); @@ -366,7 +366,7 @@ static void set_mute(struct i2c_client *client, int mute) int cx25840_audio(struct i2c_client *client, unsigned int cmd, void *arg) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); struct v4l2_control *ctrl = arg; int retval; diff --git a/linux/drivers/media/video/cx25840/cx25840-core.c b/linux/drivers/media/video/cx25840/cx25840-core.c index cb4a9c64e..52a2a5799 100644 --- a/linux/drivers/media/video/cx25840/cx25840-core.c +++ b/linux/drivers/media/video/cx25840/cx25840-core.c @@ -198,7 +198,7 @@ void cx25840_work_handler(void *arg) static void cx25840_initialize(struct i2c_client *client) { DEFINE_WAIT(wait); - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); struct workqueue_struct *q; /* datasheet startup in numbered steps, refer to page 3-77 */ @@ -270,7 +270,7 @@ static void cx25840_initialize(struct i2c_client *client) static void cx23885_initialize(struct i2c_client *client) { DEFINE_WAIT(wait); - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); struct workqueue_struct *q; /* Internal Reset */ @@ -369,7 +369,7 @@ static void cx23885_initialize(struct i2c_client *client) void cx25840_std_setup(struct i2c_client *client) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); v4l2_std_id std = state->std; int hblank, hactive, burst, vblank, vactive, sc; int vblank656, src_decimation; @@ -516,7 +516,7 @@ void cx25840_std_setup(struct i2c_client *client) static void input_change(struct i2c_client *client) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); v4l2_std_id std = state->std; /* Follow step 8c and 8d of section 3.16 in the cx25840 datasheet */ @@ -570,7 +570,7 @@ static void input_change(struct i2c_client *client) static int set_input(struct i2c_client *client, enum cx25840_video_input vid_input, enum cx25840_audio_input aud_input) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); u8 is_composite = (vid_input >= CX25840_COMPOSITE1 && vid_input <= CX25840_COMPOSITE8); u8 reg; @@ -690,7 +690,7 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp static int set_v4lstd(struct i2c_client *client) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); u8 fmt = 0; /* zero is autodetect */ u8 pal_m = 0; @@ -739,9 +739,10 @@ static int set_v4lstd(struct i2c_client *client) /* ----------------------------------------------------------------------- */ -static int set_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl) +static int cx25840_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); switch (ctrl->id) { case CX25840_CID_ENABLE_PVR150_WORKAROUND: @@ -805,9 +806,10 @@ static int set_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl) return 0; } -static int get_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl) +static int cx25840_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); switch (ctrl->id) { case CX25840_CID_ENABLE_PVR150_WORKAROUND: @@ -842,21 +844,23 @@ static int get_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl) /* ----------------------------------------------------------------------- */ -static int get_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt) +static int cx25840_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + switch (fmt->type) { case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: return cx25840_vbi(client, VIDIOC_G_FMT, fmt); default: return -EINVAL; } - return 0; } -static int set_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt) +static int cx25840_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); struct v4l2_pix_format *pix; int HSC, VSC, Vsrc, Hsrc, filter, Vlines; int is_50Hz = !(state->std & V4L2_STD_525_60); @@ -933,7 +937,7 @@ static void log_video_status(struct i2c_client *client) "0xD", "0xE", "0xF" }; - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); u8 vidfmt_sel = cx25840_read(client, 0x400) & 0xf; u8 gen_stat1 = cx25840_read(client, 0x40d); u8 gen_stat2 = cx25840_read(client, 0x40e); @@ -963,7 +967,7 @@ static void log_video_status(struct i2c_client *client) static void log_audio_status(struct i2c_client *client) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); u8 download_ctl = cx25840_read(client, 0x803); u8 mod_det_stat0 = cx25840_read(client, 0x804); u8 mod_det_stat1 = cx25840_read(client, 0x805); @@ -1116,21 +1120,12 @@ static void log_audio_status(struct i2c_client *client) /* ----------------------------------------------------------------------- */ -static int cx25840_command(struct i2c_client *client, unsigned int cmd, - void *arg) +static int cx25840_init(struct v4l2_subdev *sd, u32 val) { - struct cx25840_state *state = i2c_get_clientdata(client); - struct v4l2_tuner *vt = arg; - struct v4l2_routing *route = arg; - - /* ignore these commands */ - switch (cmd) { - case TUNER_SET_TYPE_ADDR: - return 0; - } + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); if (!state->is_initialized) { - v4l_dbg(1, cx25840_debug, client, "cmd %08x triggered fw load\n", cmd); /* initialize on first use */ state->is_initialized = 1; if (state->is_cx25836) @@ -1140,50 +1135,69 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd, else cx25840_initialize(client); } + return 0; +} - switch (cmd) { #ifdef CONFIG_VIDEO_ADV_DEBUG - /* ioctls to allow direct access to the - * cx25840 registers for testing */ - case VIDIOC_DBG_G_REGISTER: - case VIDIOC_DBG_S_REGISTER: - { - struct v4l2_register *reg = arg; - - if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; +static int cx25840_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); - if (cmd == VIDIOC_DBG_G_REGISTER) - reg->val = cx25840_read(client, reg->reg & 0x0fff); - else - cx25840_write(client, reg->reg & 0x0fff, reg->val & 0xff); - break; - } + if (!v4l2_chip_match_i2c_client(client, + reg->match_type, reg->match_chip)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reg->val = cx25840_read(client, reg->reg & 0x0fff); + return 0; +} + +static int cx25840_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, + reg->match_type, reg->match_chip)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + cx25840_write(client, reg->reg & 0x0fff, reg->val & 0xff); + return 0; +} #endif - case VIDIOC_INT_DECODE_VBI_LINE: - return cx25840_vbi(client, cmd, arg); +static int cx25840_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); - case VIDIOC_INT_AUDIO_CLOCK_FREQ: - return cx25840_audio(client, cmd, arg); + return cx25840_vbi(client, VIDIOC_INT_DECODE_VBI_LINE, vbi); +} + +static int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return cx25840_audio(client, VIDIOC_INT_AUDIO_CLOCK_FREQ, &freq); +} + +static int cx25840_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - case VIDIOC_STREAMON: - v4l_dbg(1, cx25840_debug, client, "enable output\n"); + v4l_dbg(1, cx25840_debug, client, "%s output\n", + enable ? "enable" : "disable"); + if (enable) { if (state->is_cx23885) { u8 v = (cx25840_read(client, 0x421) | 0x0b); cx25840_write(client, 0x421, v); } else { cx25840_write(client, 0x115, - state->is_cx25836 ? 0x0c : 0x8c); + state->is_cx25836 ? 0x0c : 0x8c); cx25840_write(client, 0x116, - state->is_cx25836 ? 0x04 : 0x07); + state->is_cx25836 ? 0x04 : 0x07); } - break; - - case VIDIOC_STREAMOFF: - v4l_dbg(1, cx25840_debug, client, "disable output\n"); + } else { if (state->is_cx23885) { u8 v = cx25840_read(client, 0x421) & ~(0x0b); cx25840_write(client, 0x421, v); @@ -1191,133 +1205,136 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd, cx25840_write(client, 0x115, 0x00); cx25840_write(client, 0x116, 0x00); } - break; + } + return 0; +} - case VIDIOC_LOG_STATUS: - log_video_status(client); - if (!state->is_cx25836) - log_audio_status(client); - break; +static int cx25840_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) +{ + struct cx25840_state *state = to_state(sd); - case VIDIOC_G_CTRL: - return get_v4lctrl(client, (struct v4l2_control *)arg); + switch (qc->id) { + case V4L2_CID_BRIGHTNESS: + case V4L2_CID_CONTRAST: + case V4L2_CID_SATURATION: + case V4L2_CID_HUE: + return v4l2_ctrl_query_fill_std(qc); + default: + break; + } + if (state->is_cx25836) + return -EINVAL; - case VIDIOC_S_CTRL: - return set_v4lctrl(client, (struct v4l2_control *)arg); + switch (qc->id) { + case V4L2_CID_AUDIO_VOLUME: + return v4l2_ctrl_query_fill(qc, 0, 65535, + 65535 / 100, state->default_volume); + case V4L2_CID_AUDIO_MUTE: + case V4L2_CID_AUDIO_BALANCE: + case V4L2_CID_AUDIO_BASS: + case V4L2_CID_AUDIO_TREBLE: + return v4l2_ctrl_query_fill_std(qc); + default: + return -EINVAL; + } + return -EINVAL; +} - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; +static int cx25840_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - switch (qc->id) { - case V4L2_CID_BRIGHTNESS: - case V4L2_CID_CONTRAST: - case V4L2_CID_SATURATION: - case V4L2_CID_HUE: - return v4l2_ctrl_query_fill_std(qc); - default: - break; - } - if (state->is_cx25836) - return -EINVAL; + if (state->radio == 0 && state->std == std) + return 0; + state->radio = 0; + state->std = std; + return set_v4lstd(client); +} - switch (qc->id) { - case V4L2_CID_AUDIO_VOLUME: - return v4l2_ctrl_query_fill(qc, 0, 65535, - 65535 / 100, state->default_volume); - case V4L2_CID_AUDIO_MUTE: - case V4L2_CID_AUDIO_BALANCE: - case V4L2_CID_AUDIO_BASS: - case V4L2_CID_AUDIO_TREBLE: - return v4l2_ctrl_query_fill_std(qc); - default: - return -EINVAL; - } - return -EINVAL; - } +static int cx25840_s_radio(struct v4l2_subdev *sd) +{ + struct cx25840_state *state = to_state(sd); - case VIDIOC_G_STD: - *(v4l2_std_id *)arg = state->std; - break; + state->radio = 1; + return 0; +} - case VIDIOC_S_STD: - if (state->radio == 0 && state->std == *(v4l2_std_id *)arg) - return 0; - state->radio = 0; - state->std = *(v4l2_std_id *)arg; - return set_v4lstd(client); +static int cx25840_s_video_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - case AUDC_SET_RADIO: - state->radio = 1; - break; + return set_input(client, route->input, state->aud_input); +} - case VIDIOC_INT_G_VIDEO_ROUTING: - route->input = state->vid_input; - route->output = 0; - break; +static int cx25840_s_audio_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - case VIDIOC_INT_S_VIDEO_ROUTING: - return set_input(client, route->input, state->aud_input); + if (state->is_cx25836) + return -EINVAL; + return set_input(client, state->vid_input, route->input); +} - case VIDIOC_INT_G_AUDIO_ROUTING: - if (state->is_cx25836) - return -EINVAL; - route->input = state->aud_input; - route->output = 0; - break; +static int cx25840_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - case VIDIOC_INT_S_AUDIO_ROUTING: - if (state->is_cx25836) - return -EINVAL; - return set_input(client, state->vid_input, route->input); + if (!state->is_cx25836) + input_change(client); + return 0; +} - case VIDIOC_S_FREQUENCY: - if (!state->is_cx25836) { - input_change(client); - } - break; +static int cx25840_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 vpres = cx25840_read(client, 0x40e) & 0x20; + u8 mode; + int val = 0; - case VIDIOC_G_TUNER: - { - u8 vpres = cx25840_read(client, 0x40e) & 0x20; - u8 mode; - int val = 0; + if (state->radio) + return 0; - if (state->radio) - break; + vt->signal = vpres ? 0xffff : 0x0; + if (state->is_cx25836) + return 0; - vt->signal = vpres ? 0xffff : 0x0; - if (state->is_cx25836) - break; + vt->capability |= + V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | + V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; - vt->capability |= - V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | - V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; + mode = cx25840_read(client, 0x804); - mode = cx25840_read(client, 0x804); + /* get rxsubchans and audmode */ + if ((mode & 0xf) == 1) + val |= V4L2_TUNER_SUB_STEREO; + else + val |= V4L2_TUNER_SUB_MONO; - /* get rxsubchans and audmode */ - if ((mode & 0xf) == 1) - val |= V4L2_TUNER_SUB_STEREO; - else - val |= V4L2_TUNER_SUB_MONO; + if (mode == 2 || mode == 4) + val = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; - if (mode == 2 || mode == 4) - val = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; + if (mode & 0x10) + val |= V4L2_TUNER_SUB_SAP; - if (mode & 0x10) - val |= V4L2_TUNER_SUB_SAP; + vt->rxsubchans = val; + vt->audmode = state->audmode; + return 0; +} - vt->rxsubchans = val; - vt->audmode = state->audmode; - break; - } +static int cx25840_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - case VIDIOC_S_TUNER: - if (state->radio || state->is_cx25836) - break; + if (state->radio || state->is_cx25836) + return 0; - switch (vt->audmode) { + switch (vt->audmode) { case V4L2_TUNER_MODE_MONO: /* mono -> mono stereo -> mono @@ -1345,41 +1362,100 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd, break; default: return -EINVAL; - } - state->audmode = vt->audmode; - break; + } + state->audmode = vt->audmode; + return 0; +} - case VIDIOC_G_FMT: - return get_v4lfmt(client, (struct v4l2_format *)arg); +static int cx25840_reset(struct v4l2_subdev *sd, u32 val) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - case VIDIOC_S_FMT: - return set_v4lfmt(client, (struct v4l2_format *)arg); + if (state->is_cx25836) + cx25836_initialize(client); + else if (state->is_cx23885) + cx23885_initialize(client); + else + cx25840_initialize(client); + return 0; +} - case VIDIOC_INT_RESET: - if (state->is_cx25836) - cx25836_initialize(client); - else if (state->is_cx23885) - cx23885_initialize(client); - else - cx25840_initialize(client); - break; +static int cx25840_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - case VIDIOC_G_CHIP_IDENT: - return v4l2_chip_ident_i2c_client(client, arg, state->id, state->rev); + return v4l2_chip_ident_i2c_client(client, chip, state->id, state->rev); +} - default: - return -EINVAL; - } +static int cx25840_log_status(struct v4l2_subdev *sd) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + log_video_status(client); + if (!state->is_cx25836) + log_audio_status(client); return 0; } +static int cx25840_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops cx25840_core_ops = { + .log_status = cx25840_log_status, + .g_chip_ident = cx25840_g_chip_ident, + .g_ctrl = cx25840_g_ctrl, + .s_ctrl = cx25840_s_ctrl, + .queryctrl = cx25840_queryctrl, + .reset = cx25840_reset, + .init = cx25840_init, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = cx25840_g_register, + .s_register = cx25840_s_register, +#endif +}; + +static const struct v4l2_subdev_tuner_ops cx25840_tuner_ops = { + .s_frequency = cx25840_s_frequency, + .s_std = cx25840_s_std, + .s_radio = cx25840_s_radio, + .g_tuner = cx25840_g_tuner, + .s_tuner = cx25840_s_tuner, +}; + +static const struct v4l2_subdev_audio_ops cx25840_audio_ops = { + .s_clock_freq = cx25840_s_clock_freq, + .s_routing = cx25840_s_audio_routing, +}; + +static const struct v4l2_subdev_video_ops cx25840_video_ops = { + .s_routing = cx25840_s_video_routing, + .g_fmt = cx25840_g_fmt, + .s_fmt = cx25840_s_fmt, + .decode_vbi_line = cx25840_decode_vbi_line, + .s_stream = cx25840_s_stream, +}; + +static const struct v4l2_subdev_ops cx25840_ops = { + .core = &cx25840_core_ops, + .tuner = &cx25840_tuner_ops, + .audio = &cx25840_audio_ops, + .video = &cx25840_video_ops, +}; + /* ----------------------------------------------------------------------- */ static int cx25840_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct cx25840_state *state; + struct v4l2_subdev *sd; u32 id; u16 device_id; @@ -1411,10 +1487,11 @@ static int cx25840_probe(struct i2c_client *client, } state = kzalloc(sizeof(struct cx25840_state), GFP_KERNEL); - if (state == NULL) { + if (state == NULL) return -ENOMEM; - } + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &cx25840_ops); /* Note: revision '(device_id & 0x0f) == 2' was never built. The marking skips from 0x1 == 22 to 0x3 == 23. */ v4l_info(client, "cx25%3x-2%x found @ 0x%x (%s)\n", @@ -1422,7 +1499,6 @@ static int cx25840_probe(struct i2c_client *client, (device_id & 0x0f) < 3 ? (device_id & 0x0f) + 1 : (device_id & 0x0f), client->addr << 1, client->adapter->name); - i2c_set_clientdata(client, state); state->c = client; state->is_cx25836 = ((device_id & 0xff00) == 0x8300); state->is_cx23885 = (device_id == 0x0000) || (device_id == 0x1313); @@ -1449,7 +1525,10 @@ static int cx25840_probe(struct i2c_client *client, static int cx25840_remove(struct i2c_client *client) { - kfree(i2c_get_clientdata(client)); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); return 0; } diff --git a/linux/drivers/media/video/cx25840/cx25840-core.h b/linux/drivers/media/video/cx25840/cx25840-core.h index cd549b119..0a99c19df 100644 --- a/linux/drivers/media/video/cx25840/cx25840-core.h +++ b/linux/drivers/media/video/cx25840/cx25840-core.h @@ -23,6 +23,7 @@ #include "compat.h" #include +#include #include /* ENABLE_PVR150_WORKAROUND activates a workaround for a hardware bug that is @@ -35,6 +36,7 @@ struct cx25840_state { struct i2c_client *c; + struct v4l2_subdev sd; int pvr150_workaround; int radio; v4l2_std_id std; @@ -54,6 +56,11 @@ struct cx25840_state { struct work_struct fw_work; /* work entry for fw load */ }; +static inline struct cx25840_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct cx25840_state, sd); +} + /* ----------------------------------------------------------------------- */ /* cx25850-core.c */ int cx25840_write(struct i2c_client *client, u16 addr, u8 value); diff --git a/linux/drivers/media/video/cx25840/cx25840-firmware.c b/linux/drivers/media/video/cx25840/cx25840-firmware.c index 04a5dbb65..01fbe174e 100644 --- a/linux/drivers/media/video/cx25840/cx25840-firmware.c +++ b/linux/drivers/media/video/cx25840/cx25840-firmware.c @@ -92,7 +92,7 @@ static int fw_write(struct i2c_client *client, const u8 *data, int size) int cx25840_loadfw(struct i2c_client *client) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); const struct firmware *fw = NULL; u8 buffer[FWSEND]; const u8 *ptr; diff --git a/linux/drivers/media/video/cx25840/cx25840-vbi.c b/linux/drivers/media/video/cx25840/cx25840-vbi.c index e45c39625..7790afd5b 100644 --- a/linux/drivers/media/video/cx25840/cx25840-vbi.c +++ b/linux/drivers/media/video/cx25840/cx25840-vbi.c @@ -85,7 +85,7 @@ static int decode_vps(u8 * dst, u8 * p) int cx25840_vbi(struct i2c_client *client, unsigned int cmd, void *arg) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); struct v4l2_format *fmt; struct v4l2_sliced_vbi_format *svbi; -- cgit v1.2.3 From 59736da8febbd58e35798210cb51e2e9e4dbe3a9 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 29 Nov 2008 16:51:32 +0100 Subject: m52790: convert to v4l2_subdev. From: Hans Verkuil Priority: normal Signed-off-by: Hans Verkuil --- linux/drivers/media/video/m52790.c | 176 +++++++++++++++++++++++-------------- 1 file changed, 111 insertions(+), 65 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/m52790.c b/linux/drivers/media/video/m52790.c index f39da4a36..1976451e0 100644 --- a/linux/drivers/media/video/m52790.c +++ b/linux/drivers/media/video/m52790.c @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include #include #include "compat.h" @@ -44,89 +44,130 @@ I2C_CLIENT_INSMOD; #endif struct m52790_state { + struct v4l2_subdev sd; u16 input; u16 output; }; +static inline struct m52790_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct m52790_state, sd); +} + /* ----------------------------------------------------------------------- */ -static int m52790_write(struct i2c_client *client) +static int m52790_write(struct v4l2_subdev *sd) { - struct m52790_state *state = i2c_get_clientdata(client); + struct m52790_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 sw1 = (state->input | state->output) & 0xff; u8 sw2 = (state->input | state->output) >> 8; return i2c_smbus_write_byte_data(client, sw1, sw2); } -static int m52790_command(struct i2c_client *client, unsigned int cmd, - void *arg) +/* Note: audio and video are linked and cannot be switched separately. + So audio and video routing commands are identical for this chip. + In theory the video amplifier and audio modes could be handled + separately for the output, but that seems to be overkill right now. + The same holds for implementing an audio mute control, this is now + part of the audio output routing. The normal case is that another + chip takes care of the actual muting so making it part of the + output routing seems to be the right thing to do for now. */ +static int m52790_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) { - struct m52790_state *state = i2c_get_clientdata(client); - struct v4l2_routing *route = arg; - - /* Note: audio and video are linked and cannot be switched separately. - So audio and video routing commands are identical for this chip. - In theory the video amplifier and audio modes could be handled - separately for the output, but that seems to be overkill right now. - The same holds for implementing an audio mute control, this is now - part of the audio output routing. The normal case is that another - chip takes care of the actual muting so making it part of the - output routing seems to be the right thing to do for now. */ - switch (cmd) { - case VIDIOC_INT_G_AUDIO_ROUTING: - case VIDIOC_INT_G_VIDEO_ROUTING: - route->input = state->input; - route->output = state->output; - break; - - case VIDIOC_INT_S_AUDIO_ROUTING: - case VIDIOC_INT_S_VIDEO_ROUTING: - state->input = route->input; - state->output = route->output; - m52790_write(client); - break; + struct m52790_state *state = to_state(sd); + + state->input = route->input; + state->output = route->output; + m52790_write(sd); + return 0; +} #ifdef CONFIG_VIDEO_ADV_DEBUG - case VIDIOC_DBG_G_REGISTER: - case VIDIOC_DBG_S_REGISTER: - { - struct v4l2_register *reg = arg; - - if (!v4l2_chip_match_i2c_client(client, - reg->match_type, reg->match_chip)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - if (reg->reg != 0) - return -EINVAL; - if (cmd == VIDIOC_DBG_G_REGISTER) - reg->val = state->input | state->output; - else { - state->input = reg->val & 0x0303; - state->output = reg->val & ~0x0303; - m52790_write(client); - } - break; - } -#endif +static int m52790_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +{ + struct m52790_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - case VIDIOC_G_CHIP_IDENT: - return v4l2_chip_ident_i2c_client(client, arg, - V4L2_IDENT_M52790, 0); + if (!v4l2_chip_match_i2c_client(client, + reg->match_type, reg->match_chip)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (reg->reg != 0) + return -EINVAL; + reg->val = state->input | state->output; + return 0; +} - case VIDIOC_LOG_STATUS: - v4l_info(client, "Switch 1: %02x\n", - (state->input | state->output) & 0xff); - v4l_info(client, "Switch 2: %02x\n", - (state->input | state->output) >> 8); - break; +static int m52790_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +{ + struct m52790_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - default: + if (!v4l2_chip_match_i2c_client(client, + reg->match_type, reg->match_chip)) return -EINVAL; - } + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (reg->reg != 0) + return -EINVAL; + state->input = reg->val & 0x0303; + state->output = reg->val & ~0x0303; + m52790_write(sd); return 0; } +#endif + +static int m52790_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_M52790, 0); +} + +static int m52790_log_status(struct v4l2_subdev *sd) +{ + struct m52790_state *state = to_state(sd); + + v4l2_info(sd, "Switch 1: %02x\n", + (state->input | state->output) & 0xff); + v4l2_info(sd, "Switch 2: %02x\n", + (state->input | state->output) >> 8); + return 0; +} + +static int m52790_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops m52790_core_ops = { + .log_status = m52790_log_status, + .g_chip_ident = m52790_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = m52790_g_register, + .s_register = m52790_s_register, +#endif +}; + +static const struct v4l2_subdev_audio_ops m52790_audio_ops = { + .s_routing = m52790_s_routing, +}; + +static const struct v4l2_subdev_video_ops m52790_video_ops = { + .s_routing = m52790_s_routing, +}; + +static const struct v4l2_subdev_ops m52790_ops = { + .core = &m52790_core_ops, + .audio = &m52790_audio_ops, + .video = &m52790_video_ops, +}; /* ----------------------------------------------------------------------- */ @@ -136,6 +177,7 @@ static int m52790_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct m52790_state *state; + struct v4l2_subdev *sd; /* Check if the adapter supports the needed features */ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) @@ -152,16 +194,20 @@ static int m52790_probe(struct i2c_client *client, if (state == NULL) return -ENOMEM; + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &m52790_ops); state->input = M52790_IN_TUNER; state->output = M52790_OUT_STEREO; - i2c_set_clientdata(client, state); - m52790_write(client); + m52790_write(sd); return 0; } static int m52790_remove(struct i2c_client *client) { - kfree(i2c_get_clientdata(client)); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); return 0; } -- cgit v1.2.3 From 24dc2f33e9336e9e01da8321352fb93173454e88 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 24 Nov 2008 22:16:46 +0100 Subject: msp3400: convert to v4l2_subdev. From: Hans Verkuil Priority: normal Signed-off-by: Hans Verkuil --- linux/drivers/media/video/msp3400-driver.c | 406 +++++++++++++++------------ linux/drivers/media/video/msp3400-driver.h | 7 + linux/drivers/media/video/msp3400-kthreads.c | 34 +-- 3 files changed, 249 insertions(+), 198 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/msp3400-driver.c b/linux/drivers/media/video/msp3400-driver.c index c6c0a584c..def15e381 100644 --- a/linux/drivers/media/video/msp3400-driver.c +++ b/linux/drivers/media/video/msp3400-driver.c @@ -51,18 +51,18 @@ #include #include #include -#include -#include -#include -#include -#include -#include #include #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) #include #else #include #endif +#include +#include +#include +#include +#include +#include #include "compat.h" #include "msp3400-driver.h" @@ -270,7 +270,7 @@ static char *scart_names[] = { void msp_set_scart(struct i2c_client *client, int in, int out) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); state->in_scart = in; @@ -294,7 +294,7 @@ void msp_set_scart(struct i2c_client *client, int in, int out) void msp_set_audio(struct i2c_client *client) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); int bal = 0, bass, treble, loudness; int val = 0; int reallymuted = state->muted | state->scan_in_progress; @@ -341,7 +341,7 @@ void msp_set_audio(struct i2c_client *client) static void msp_wake_thread(struct i2c_client *client) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); if (NULL == state->kthread) return; @@ -395,9 +395,9 @@ static int msp_mode_v4l1_to_v4l2(int mode) } #endif -static int msp_get_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) +static int msp_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(sd); switch (ctrl->id) { case V4L2_CID_AUDIO_VOLUME: @@ -438,9 +438,10 @@ static int msp_get_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) return 0; } -static int msp_set_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) +static int msp_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); switch (ctrl->id) { case V4L2_CID_AUDIO_VOLUME: @@ -486,40 +487,16 @@ static int msp_set_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) return 0; } -static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) +#ifdef CONFIG_VIDEO_ALLOW_V4L1 +static int msp_ioctl(struct v4l2_subdev *sd, int cmd, void *arg) { - struct msp_state *state = i2c_get_clientdata(client); - - if (msp_debug >= 2) - v4l_i2c_print_ioctl(client, cmd); + struct msp_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); switch (cmd) { - case AUDC_SET_RADIO: - if (state->radio) - return 0; - state->radio = 1; - v4l_dbg(1, msp_debug, client, "switching to radio mode\n"); - state->watch_stereo = 0; - switch (state->opmode) { - case OPMODE_MANUAL: - /* set msp3400 to FM radio mode */ - msp3400c_set_mode(client, MSP_MODE_FM_RADIO); - msp3400c_set_carrier(client, MSP_CARRIER(10.7), - MSP_CARRIER(10.7)); - msp_set_audio(client); - break; - case OPMODE_AUTODETECT: - case OPMODE_AUTOSELECT: - /* the thread will do for us */ - msp_wake_thread(client); - break; - } - break; - /* --- v4l ioctls --- */ /* take care: bttv does userspace copying, we'll get a kernel pointer here... */ -#ifdef CONFIG_VIDEO_ALLOW_V4L1 case VIDIOCGAUDIO: { struct video_audio *va = arg; @@ -593,105 +570,137 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) msp_wake_thread(client); break; } -#endif - case VIDIOC_S_FREQUENCY: - { - /* new channel -- kick audio carrier scan */ - msp_wake_thread(client); - break; + default: + return -ENOIOCTLCMD; } + return 0; +} +#endif - /* --- v4l2 ioctls --- */ - case VIDIOC_S_STD: - { - v4l2_std_id *id = arg; - int update = state->radio || state->v4l2_std != *id; +/* --- v4l2 ioctls --- */ +static int msp_s_radio(struct v4l2_subdev *sd) +{ + struct msp_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - state->v4l2_std = *id; - state->radio = 0; - if (update) - msp_wake_thread(client); + if (state->radio) return 0; + state->radio = 1; + v4l_dbg(1, msp_debug, client, "switching to radio mode\n"); + state->watch_stereo = 0; + switch (state->opmode) { + case OPMODE_MANUAL: + /* set msp3400 to FM radio mode */ + msp3400c_set_mode(client, MSP_MODE_FM_RADIO); + msp3400c_set_carrier(client, MSP_CARRIER(10.7), + MSP_CARRIER(10.7)); + msp_set_audio(client); + break; + case OPMODE_AUTODETECT: + case OPMODE_AUTOSELECT: + /* the thread will do for us */ + msp_wake_thread(client); + break; } + return 0; +} - case VIDIOC_INT_G_AUDIO_ROUTING: - { - struct v4l2_routing *rt = arg; +static int msp_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); - *rt = state->routing; - break; - } + /* new channel -- kick audio carrier scan */ + msp_wake_thread(client); + return 0; +} - case VIDIOC_INT_S_AUDIO_ROUTING: - { - struct v4l2_routing *rt = arg; - int tuner = (rt->input >> 3) & 1; - int sc_in = rt->input & 0x7; - int sc1_out = rt->output & 0xf; - int sc2_out = (rt->output >> 4) & 0xf; - u16 val, reg; - int i; - int extern_input = 1; - - if (state->routing.input == rt->input && - state->routing.output == rt->output) - break; - state->routing = *rt; - /* check if the tuner input is used */ - for (i = 0; i < 5; i++) { - if (((rt->input >> (4 + i * 4)) & 0xf) == 0) - extern_input = 0; - } - state->mode = extern_input ? MSP_MODE_EXTERN : MSP_MODE_AM_DETECT; - state->rxsubchans = V4L2_TUNER_SUB_STEREO; - msp_set_scart(client, sc_in, 0); - msp_set_scart(client, sc1_out, 1); - msp_set_scart(client, sc2_out, 2); - msp_set_audmode(client); - reg = (state->opmode == OPMODE_AUTOSELECT) ? 0x30 : 0xbb; - val = msp_read_dem(client, reg); - msp_write_dem(client, reg, (val & ~0x100) | (tuner << 8)); - /* wake thread when a new input is chosen */ +static int msp_s_std(struct v4l2_subdev *sd, v4l2_std_id id) +{ + struct msp_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int update = state->radio || state->v4l2_std != id; + + state->v4l2_std = id; + state->radio = 0; + if (update) msp_wake_thread(client); - break; + return 0; +} + +static int msp_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *rt) +{ + struct msp_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int tuner = (rt->input >> 3) & 1; + int sc_in = rt->input & 0x7; + int sc1_out = rt->output & 0xf; + int sc2_out = (rt->output >> 4) & 0xf; + u16 val, reg; + int i; + int extern_input = 1; + + if (state->routing.input == rt->input && + state->routing.output == rt->output) + return 0; + state->routing = *rt; + /* check if the tuner input is used */ + for (i = 0; i < 5; i++) { + if (((rt->input >> (4 + i * 4)) & 0xf) == 0) + extern_input = 0; } + state->mode = extern_input ? MSP_MODE_EXTERN : MSP_MODE_AM_DETECT; + state->rxsubchans = V4L2_TUNER_SUB_STEREO; + msp_set_scart(client, sc_in, 0); + msp_set_scart(client, sc1_out, 1); + msp_set_scart(client, sc2_out, 2); + msp_set_audmode(client); + reg = (state->opmode == OPMODE_AUTOSELECT) ? 0x30 : 0xbb; + val = msp_read_dem(client, reg); + msp_write_dem(client, reg, (val & ~0x100) | (tuner << 8)); + /* wake thread when a new input is chosen */ + msp_wake_thread(client); + return 0; +} - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *vt = arg; +static int msp_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct msp_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - if (state->radio) - break; - if (state->opmode == OPMODE_AUTOSELECT) - msp_detect_stereo(client); - vt->audmode = state->audmode; - vt->rxsubchans = state->rxsubchans; - vt->capability |= V4L2_TUNER_CAP_STEREO | - V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; - break; - } + if (state->radio) + return 0; + if (state->opmode == OPMODE_AUTOSELECT) + msp_detect_stereo(client); + vt->audmode = state->audmode; + vt->rxsubchans = state->rxsubchans; + vt->capability |= V4L2_TUNER_CAP_STEREO | + V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; + return 0; +} - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *vt = (struct v4l2_tuner *)arg; +static int msp_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct msp_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - if (state->radio) /* TODO: add mono/stereo support for radio */ - break; - if (state->audmode == vt->audmode) - break; - state->audmode = vt->audmode; - /* only set audmode */ - msp_set_audmode(client); - break; - } + if (state->radio) /* TODO: add mono/stereo support for radio */ + return 0; + if (state->audmode == vt->audmode) + return 0; + state->audmode = vt->audmode; + /* only set audmode */ + msp_set_audmode(client); + return 0; +} - case VIDIOC_INT_I2S_CLOCK_FREQ: - { - u32 *a = (u32 *)arg; +static int msp_s_i2s_clock_freq(struct v4l2_subdev *sd, u32 freq) +{ + struct msp_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - v4l_dbg(1, msp_debug, client, "Setting I2S speed to %d\n", *a); + v4l_dbg(1, msp_debug, client, "Setting I2S speed to %d\n", freq); - switch (*a) { + switch (freq) { case 1024000: state->i2s_mode = 0; break; @@ -700,24 +709,24 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) break; default: return -EINVAL; - } - break; } + return 0; +} - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; +static int msp_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) +{ + struct msp_state *state = to_state(sd); - switch (qc->id) { + switch (qc->id) { case V4L2_CID_AUDIO_VOLUME: case V4L2_CID_AUDIO_MUTE: return v4l2_ctrl_query_fill_std(qc); default: break; - } - if (!state->has_sound_processing) - return -EINVAL; - switch (qc->id) { + } + if (!state->has_sound_processing) + return -EINVAL; + switch (qc->id) { case V4L2_CID_AUDIO_LOUDNESS: case V4L2_CID_AUDIO_BALANCE: case V4L2_CID_AUDIO_BASS: @@ -725,32 +734,38 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) return v4l2_ctrl_query_fill_std(qc); default: return -EINVAL; - } } + return 0; +} - case VIDIOC_G_CTRL: - return msp_get_ctrl(client, arg); - - case VIDIOC_S_CTRL: - return msp_set_ctrl(client, arg); +static int msp_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) +{ + struct msp_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - case VIDIOC_LOG_STATUS: - { - const char *p; + return v4l2_chip_ident_i2c_client(client, chip, state->ident, + (state->rev1 << 16) | state->rev2); +} - if (state->opmode == OPMODE_AUTOSELECT) - msp_detect_stereo(client); - v4l_info(client, "%s rev1 = 0x%04x rev2 = 0x%04x\n", - client->name, state->rev1, state->rev2); - v4l_info(client, "Audio: volume %d%s\n", - state->volume, state->muted ? " (muted)" : ""); - if (state->has_sound_processing) { - v4l_info(client, "Audio: balance %d bass %d treble %d loudness %s\n", - state->balance, state->bass, - state->treble, - state->loudness ? "on" : "off"); - } - switch (state->mode) { +static int msp_log_status(struct v4l2_subdev *sd) +{ + struct msp_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + const char *p; + + if (state->opmode == OPMODE_AUTOSELECT) + msp_detect_stereo(client); + v4l_info(client, "%s rev1 = 0x%04x rev2 = 0x%04x\n", + client->name, state->rev1, state->rev2); + v4l_info(client, "Audio: volume %d%s\n", + state->volume, state->muted ? " (muted)" : ""); + if (state->has_sound_processing) { + v4l_info(client, "Audio: balance %d bass %d treble %d loudness %s\n", + state->balance, state->bass, + state->treble, + state->loudness ? "on" : "off"); + } + switch (state->mode) { case MSP_MODE_AM_DETECT: p = "AM (for carrier detect)"; break; case MSP_MODE_FM_RADIO: p = "FM Radio"; break; case MSP_MODE_FM_TERRA: p = "Terrestial FM-mono/stereo"; break; @@ -761,36 +776,25 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) case MSP_MODE_BTSC: p = "BTSC"; break; case MSP_MODE_EXTERN: p = "External input"; break; default: p = "unknown"; break; - } - if (state->mode == MSP_MODE_EXTERN) { - v4l_info(client, "Mode: %s\n", p); - } else if (state->opmode == OPMODE_MANUAL) { - v4l_info(client, "Mode: %s (%s%s)\n", p, + } + if (state->mode == MSP_MODE_EXTERN) { + v4l_info(client, "Mode: %s\n", p); + } else if (state->opmode == OPMODE_MANUAL) { + v4l_info(client, "Mode: %s (%s%s)\n", p, (state->rxsubchans & V4L2_TUNER_SUB_STEREO) ? "stereo" : "mono", (state->rxsubchans & V4L2_TUNER_SUB_LANG2) ? ", dual" : ""); - } else { - if (state->opmode == OPMODE_AUTODETECT) - v4l_info(client, "Mode: %s\n", p); - v4l_info(client, "Standard: %s (%s%s)\n", + } else { + if (state->opmode == OPMODE_AUTODETECT) + v4l_info(client, "Mode: %s\n", p); + v4l_info(client, "Standard: %s (%s%s)\n", msp_standard_std_name(state->std), (state->rxsubchans & V4L2_TUNER_SUB_STEREO) ? "stereo" : "mono", (state->rxsubchans & V4L2_TUNER_SUB_LANG2) ? ", dual" : ""); - } - v4l_info(client, "Audmode: 0x%04x\n", state->audmode); - v4l_info(client, "Routing: 0x%08x (input) 0x%08x (output)\n", - state->routing.input, state->routing.output); - v4l_info(client, "ACB: 0x%04x\n", state->acb); - break; - } - - case VIDIOC_G_CHIP_IDENT: - return v4l2_chip_ident_i2c_client(client, arg, state->ident, - (state->rev1 << 16) | state->rev2); - - default: - /* unknown */ - return -EINVAL; } + v4l_info(client, "Audmode: 0x%04x\n", state->audmode); + v4l_info(client, "Routing: 0x%08x (input) 0x%08x (output)\n", + state->routing.input, state->routing.output); + v4l_info(client, "ACB: 0x%04x\n", state->acb); return 0; } @@ -808,11 +812,49 @@ static int msp_resume(struct i2c_client *client) return 0; } +static int msp_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops msp_core_ops = { + .log_status = msp_log_status, + .g_chip_ident = msp_g_chip_ident, + .g_ctrl = msp_g_ctrl, + .s_ctrl = msp_s_ctrl, + .queryctrl = msp_queryctrl, +#ifdef CONFIG_VIDEO_ALLOW_V4L1 + .ioctl = msp_ioctl, +#endif +}; + +static const struct v4l2_subdev_tuner_ops msp_tuner_ops = { + .s_frequency = msp_s_frequency, + .g_tuner = msp_g_tuner, + .s_tuner = msp_s_tuner, + .s_radio = msp_s_radio, + .s_std = msp_s_std, +}; + +static const struct v4l2_subdev_audio_ops msp_audio_ops = { + .s_routing = msp_s_routing, + .s_i2s_clock_freq = msp_s_i2s_clock_freq, +}; + +static const struct v4l2_subdev_ops msp_ops = { + .core = &msp_core_ops, + .tuner = &msp_tuner_ops, + .audio = &msp_audio_ops, +}; + /* ----------------------------------------------------------------------- */ static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct msp_state *state; + struct v4l2_subdev *sd; int (*thread_func)(void *data) = NULL; int msp_hard; int msp_family; @@ -836,7 +878,8 @@ static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id) if (!state) return -ENOMEM; - i2c_set_clientdata(client, state); + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &msp_ops); state->v4l2_std = V4L2_STD_NTSC; state->audmode = V4L2_TUNER_MODE_STEREO; @@ -990,8 +1033,9 @@ static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id) static int msp_remove(struct i2c_client *client) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); + v4l2_device_unregister_subdev(&state->sd); /* shutdown control thread */ if (state->kthread) { state->restart = 1; diff --git a/linux/drivers/media/video/msp3400-driver.h b/linux/drivers/media/video/msp3400-driver.h index ab69a290e..3fe1c1b10 100644 --- a/linux/drivers/media/video/msp3400-driver.h +++ b/linux/drivers/media/video/msp3400-driver.h @@ -5,6 +5,7 @@ #define MSP3400_DRIVER_H #include +#include /* ---------------------------------------------------------------------- */ @@ -49,6 +50,7 @@ extern int msp_dolby; extern int msp_stereo_thresh; struct msp_state { + struct v4l2_subdev sd; int rev1, rev2; int ident; u8 has_nicam; @@ -96,6 +98,11 @@ struct msp_state { unsigned int watch_stereo:1; }; +static inline struct msp_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct msp_state, sd); +} + /* msp3400-driver.c */ int msp_write_dem(struct i2c_client *client, int addr, int val); int msp_write_dsp(struct i2c_client *client, int addr, int val); diff --git a/linux/drivers/media/video/msp3400-kthreads.c b/linux/drivers/media/video/msp3400-kthreads.c index 29c0d5cf6..ccba71d9f 100644 --- a/linux/drivers/media/video/msp3400-kthreads.c +++ b/linux/drivers/media/video/msp3400-kthreads.c @@ -162,7 +162,7 @@ const char *msp_standard_std_name(int std) static void msp_set_source(struct i2c_client *client, u16 src) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); if (msp_dolby) { msp_write_dsp(client, 0x0008, 0x0520); /* I2S1 */ @@ -189,7 +189,7 @@ void msp3400c_set_carrier(struct i2c_client *client, int cdo1, int cdo2) void msp3400c_set_mode(struct i2c_client *client, int mode) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); struct msp3400c_init_data_dem *data = &msp3400c_init_data[mode]; int tuner = (state->routing.input >> 3) & 1; int i; @@ -230,7 +230,7 @@ static void msp3400c_set_audmode(struct i2c_client *client) static char *strmode[] = { "mono", "stereo", "lang2", "lang1", "lang1+lang2" }; - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); char *modestr = (state->audmode >= 0 && state->audmode < 5) ? strmode[state->audmode] : "unknown"; int src = 0; /* channel source: FM/AM, nicam or SCART */ @@ -359,7 +359,7 @@ static void msp3400c_set_audmode(struct i2c_client *client) static void msp3400c_print_mode(struct i2c_client *client) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); if (state->main == state->second) v4l_dbg(1, msp_debug, client, @@ -388,7 +388,7 @@ static void msp3400c_print_mode(struct i2c_client *client) static int msp3400c_detect_stereo(struct i2c_client *client) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); int val; int rxsubchans = state->rxsubchans; int newnicam = state->nicam_on; @@ -466,7 +466,7 @@ static int msp3400c_detect_stereo(struct i2c_client *client) /* stereo/multilang monitoring */ static void watch_stereo(struct i2c_client *client) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); if (msp_detect_stereo(client)) msp_set_audmode(client); @@ -478,7 +478,7 @@ static void watch_stereo(struct i2c_client *client) int msp3400c_thread(void *data) { struct i2c_client *client = data; - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); struct msp3400c_carrier_detect *cd; int count, max1, max2, val1, val2, val, i; @@ -662,7 +662,7 @@ no_second: int msp3410d_thread(void *data) { struct i2c_client *client = data; - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); int val, i, std, count; v4l_dbg(1, msp_debug, client, "msp3410 daemon started\n"); @@ -828,7 +828,7 @@ restart: static int msp34xxg_modus(struct i2c_client *client) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); if (state->radio) { v4l_dbg(1, msp_debug, client, "selected radio modus\n"); @@ -855,7 +855,7 @@ static int msp34xxg_modus(struct i2c_client *client) static void msp34xxg_set_source(struct i2c_client *client, u16 reg, int in) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); int source, matrix; switch (state->audmode) { @@ -898,7 +898,7 @@ static void msp34xxg_set_source(struct i2c_client *client, u16 reg, int in) static void msp34xxg_set_sources(struct i2c_client *client) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); u32 in = state->routing.input; msp34xxg_set_source(client, 0x0008, (in >> 4) & 0xf); @@ -914,7 +914,7 @@ static void msp34xxg_set_sources(struct i2c_client *client) /* (re-)initialize the msp34xxg */ static void msp34xxg_reset(struct i2c_client *client) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); int tuner = (state->routing.input >> 3) & 1; int modus; @@ -957,7 +957,7 @@ static void msp34xxg_reset(struct i2c_client *client) int msp34xxg_thread(void *data) { struct i2c_client *client = data; - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); int val, i; v4l_dbg(1, msp_debug, client, "msp34xxg daemon started\n"); @@ -1052,7 +1052,7 @@ unmute: static int msp34xxg_detect_stereo(struct i2c_client *client) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); int status = msp_read_dem(client, 0x0200); int is_bilingual = status & 0x100; int is_stereo = status & 0x40; @@ -1081,7 +1081,7 @@ static int msp34xxg_detect_stereo(struct i2c_client *client) static void msp34xxg_set_audmode(struct i2c_client *client) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); if (state->std == 0x20) { if ((state->rxsubchans & V4L2_TUNER_SUB_SAP) && @@ -1098,7 +1098,7 @@ static void msp34xxg_set_audmode(struct i2c_client *client) void msp_set_audmode(struct i2c_client *client) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); switch (state->opmode) { case OPMODE_MANUAL: @@ -1113,7 +1113,7 @@ void msp_set_audmode(struct i2c_client *client) int msp_detect_stereo(struct i2c_client *client) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); switch (state->opmode) { case OPMODE_MANUAL: -- cgit v1.2.3 From 9db11dc4fccfa3ed7eea0e3ab3623990aea228f8 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 29 Nov 2008 16:55:19 +0100 Subject: saa7115: convert to v4l2_subdev. From: Hans Verkuil Priority: normal Signed-off-by: Hans Verkuil --- linux/drivers/media/video/saa7115.c | 763 +++++++++++++++++++----------------- 1 file changed, 399 insertions(+), 364 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/saa7115.c b/linux/drivers/media/video/saa7115.c index 02386e403..e9edd4d37 100644 --- a/linux/drivers/media/video/saa7115.c +++ b/linux/drivers/media/video/saa7115.c @@ -44,7 +44,7 @@ #include #include #include -#include +#include #include #include #include @@ -71,6 +71,7 @@ static unsigned short normal_i2c[] = { I2C_CLIENT_INSMOD; struct saa711x_state { + struct v4l2_subdev sd; v4l2_std_id std; int input; int output; @@ -90,10 +91,17 @@ struct saa711x_state { u8 apll; }; +static inline struct saa711x_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct saa711x_state, sd); +} + /* ----------------------------------------------------------------------- */ -static inline int saa711x_write(struct i2c_client *client, u8 reg, u8 value) +static inline int saa711x_write(struct v4l2_subdev *sd, u8 reg, u8 value) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + return i2c_smbus_write_byte_data(client, reg, value); } @@ -129,9 +137,9 @@ static int saa711x_has_reg(const int id, const u8 reg) return 1; } -static int saa711x_writeregs(struct i2c_client *client, const unsigned char *regs) +static int saa711x_writeregs(struct v4l2_subdev *sd, const unsigned char *regs) { - struct saa711x_state *state = i2c_get_clientdata(client); + struct saa711x_state *state = to_state(sd); unsigned char reg, data; while (*regs != 0x00) { @@ -140,18 +148,20 @@ static int saa711x_writeregs(struct i2c_client *client, const unsigned char *reg /* According with datasheets, reserved regs should be filled with 0 - seems better not to touch on they */ - if (saa711x_has_reg(state->ident,reg)) { - if (saa711x_write(client, reg, data) < 0) + if (saa711x_has_reg(state->ident, reg)) { + if (saa711x_write(sd, reg, data) < 0) return -1; } else { - v4l_dbg(1, debug, client, "tried to access reserved reg 0x%02x\n", reg); + v4l2_dbg(1, debug, sd, "tried to access reserved reg 0x%02x\n", reg); } } return 0; } -static inline int saa711x_read(struct i2c_client *client, u8 reg) +static inline int saa711x_read(struct v4l2_subdev *sd, u8 reg) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + return i2c_smbus_read_byte_data(client, reg); } @@ -602,7 +612,7 @@ static int saa711x_odd_parity(u8 c) return c & 1; } -static int saa711x_decode_vps(u8 * dst, u8 * p) +static int saa711x_decode_vps(u8 *dst, u8 *p) { static const u8 biphase_tbl[] = { 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, @@ -649,7 +659,7 @@ static int saa711x_decode_vps(u8 * dst, u8 * p) return err & 0xf0; } -static int saa711x_decode_wss(u8 * p) +static int saa711x_decode_wss(u8 *p) { static const int wss_bits[8] = { 0, 0, 0, 1, 0, 1, 1, 1 @@ -676,9 +686,9 @@ static int saa711x_decode_wss(u8 * p) return wss; } -static int saa711x_set_audio_clock_freq(struct i2c_client *client, u32 freq) +static int saa711x_s_clock_freq(struct v4l2_subdev *sd, u32 freq) { - struct saa711x_state *state = i2c_get_clientdata(client); + struct saa711x_state *state = to_state(sd); u32 acpf; u32 acni; u32 hz; @@ -686,10 +696,10 @@ static int saa711x_set_audio_clock_freq(struct i2c_client *client, u32 freq) u8 acc = 0; /* reg 0x3a, audio clock control */ /* Checks for chips that don't have audio clock (saa7111, saa7113) */ - if (!saa711x_has_reg(state->ident,R_30_AUD_MAST_CLK_CYCLES_PER_FIELD)) + if (!saa711x_has_reg(state->ident, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD)) return 0; - v4l_dbg(1, debug, client, "set audio clock freq: %d\n", freq); + v4l2_dbg(1, debug, sd, "set audio clock freq: %d\n", freq); /* sanity check */ if (freq < 32000 || freq > 48000) @@ -716,66 +726,66 @@ static int saa711x_set_audio_clock_freq(struct i2c_client *client, u32 freq) if (state->apll) acc |= 0x08; - saa711x_write(client, R_38_CLK_RATIO_AMXCLK_TO_ASCLK, 0x03); - saa711x_write(client, R_39_CLK_RATIO_ASCLK_TO_ALRCLK, 0x10); - saa711x_write(client, R_3A_AUD_CLK_GEN_BASIC_SETUP, acc); + saa711x_write(sd, R_38_CLK_RATIO_AMXCLK_TO_ASCLK, 0x03); + saa711x_write(sd, R_39_CLK_RATIO_ASCLK_TO_ALRCLK, 0x10); + saa711x_write(sd, R_3A_AUD_CLK_GEN_BASIC_SETUP, acc); - saa711x_write(client, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD, acpf & 0xff); - saa711x_write(client, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+1, + saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD, acpf & 0xff); + saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+1, (acpf >> 8) & 0xff); - saa711x_write(client, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+2, + saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+2, (acpf >> 16) & 0x03); - saa711x_write(client, R_34_AUD_MAST_CLK_NOMINAL_INC, acni & 0xff); - saa711x_write(client, R_34_AUD_MAST_CLK_NOMINAL_INC+1, (acni >> 8) & 0xff); - saa711x_write(client, R_34_AUD_MAST_CLK_NOMINAL_INC+2, (acni >> 16) & 0x3f); + saa711x_write(sd, R_34_AUD_MAST_CLK_NOMINAL_INC, acni & 0xff); + saa711x_write(sd, R_34_AUD_MAST_CLK_NOMINAL_INC+1, (acni >> 8) & 0xff); + saa711x_write(sd, R_34_AUD_MAST_CLK_NOMINAL_INC+2, (acni >> 16) & 0x3f); state->audclk_freq = freq; return 0; } -static int saa711x_set_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl) +static int saa711x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct saa711x_state *state = i2c_get_clientdata(client); + struct saa711x_state *state = to_state(sd); switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: if (ctrl->value < 0 || ctrl->value > 255) { - v4l_err(client, "invalid brightness setting %d\n", ctrl->value); + v4l2_err(sd, "invalid brightness setting %d\n", ctrl->value); return -ERANGE; } state->bright = ctrl->value; - saa711x_write(client, R_0A_LUMA_BRIGHT_CNTL, state->bright); + saa711x_write(sd, R_0A_LUMA_BRIGHT_CNTL, state->bright); break; case V4L2_CID_CONTRAST: if (ctrl->value < 0 || ctrl->value > 127) { - v4l_err(client, "invalid contrast setting %d\n", ctrl->value); + v4l2_err(sd, "invalid contrast setting %d\n", ctrl->value); return -ERANGE; } state->contrast = ctrl->value; - saa711x_write(client, R_0B_LUMA_CONTRAST_CNTL, state->contrast); + saa711x_write(sd, R_0B_LUMA_CONTRAST_CNTL, state->contrast); break; case V4L2_CID_SATURATION: if (ctrl->value < 0 || ctrl->value > 127) { - v4l_err(client, "invalid saturation setting %d\n", ctrl->value); + v4l2_err(sd, "invalid saturation setting %d\n", ctrl->value); return -ERANGE; } state->sat = ctrl->value; - saa711x_write(client, R_0C_CHROMA_SAT_CNTL, state->sat); + saa711x_write(sd, R_0C_CHROMA_SAT_CNTL, state->sat); break; case V4L2_CID_HUE: if (ctrl->value < -127 || ctrl->value > 127) { - v4l_err(client, "invalid hue setting %d\n", ctrl->value); + v4l2_err(sd, "invalid hue setting %d\n", ctrl->value); return -ERANGE; } state->hue = ctrl->value; - saa711x_write(client, R_0D_CHROMA_HUE_CNTL, state->hue); + saa711x_write(sd, R_0D_CHROMA_HUE_CNTL, state->hue); break; default: @@ -785,9 +795,9 @@ static int saa711x_set_v4lctrl(struct i2c_client *client, struct v4l2_control *c return 0; } -static int saa711x_get_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl) +static int saa711x_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct saa711x_state *state = i2c_get_clientdata(client); + struct saa711x_state *state = to_state(sd); switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: @@ -809,16 +819,16 @@ static int saa711x_get_v4lctrl(struct i2c_client *client, struct v4l2_control *c return 0; } -static int saa711x_set_size(struct i2c_client *client, int width, int height) +static int saa711x_set_size(struct v4l2_subdev *sd, int width, int height) { - struct saa711x_state *state = i2c_get_clientdata(client); + struct saa711x_state *state = to_state(sd); int HPSC, HFSC; int VSCY; int res; int is_50hz = state->std & V4L2_STD_625_50; int Vsrc = is_50hz ? 576 : 480; - v4l_dbg(1, debug, client, "decoder set size to %ix%i\n",width,height); + v4l2_dbg(1, debug, sd, "decoder set size to %ix%i\n", width, height); /* FIXME need better bounds checking here */ if ((width < 1) || (width > 1440)) @@ -826,7 +836,7 @@ static int saa711x_set_size(struct i2c_client *client, int width, int height) if ((height < 1) || (height > Vsrc)) return -EINVAL; - if (!saa711x_has_reg(state->ident,R_D0_B_HORIZ_PRESCALING)) { + if (!saa711x_has_reg(state->ident, R_D0_B_HORIZ_PRESCALING)) { /* Decoder only supports 720 columns and 480 or 576 lines */ if (width != 720) return -EINVAL; @@ -844,22 +854,22 @@ static int saa711x_set_size(struct i2c_client *client, int width, int height) /* Set output width/height */ /* width */ - saa711x_write(client, R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH, + saa711x_write(sd, R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH, (u8) (width & 0xff)); - saa711x_write(client, R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, + saa711x_write(sd, R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, (u8) ((width >> 8) & 0xff)); /* Vertical Scaling uses height/2 */ - res=height/2; + res = height / 2; /* On 60Hz, it is using a higher Vertical Output Size */ if (!is_50hz) res += (VRES_60HZ - 480) >> 1; /* height */ - saa711x_write(client, R_CE_B_VERT_OUTPUT_WINDOW_LENGTH, + saa711x_write(sd, R_CE_B_VERT_OUTPUT_WINDOW_LENGTH, (u8) (res & 0xff)); - saa711x_write(client, R_CF_B_VERT_OUTPUT_WINDOW_LENGTH_MSB, + saa711x_write(sd, R_CF_B_VERT_OUTPUT_WINDOW_LENGTH_MSB, (u8) ((res >> 8) & 0xff)); /* Scaling settings */ @@ -870,54 +880,54 @@ static int saa711x_set_size(struct i2c_client *client, int width, int height) HFSC = (int)((1024 * 720) / (HPSC * width)); /* FIXME hardcodes to "Task B" * write H prescaler integer */ - saa711x_write(client, R_D0_B_HORIZ_PRESCALING, + saa711x_write(sd, R_D0_B_HORIZ_PRESCALING, (u8) (HPSC & 0x3f)); - v4l_dbg(1, debug, client, "Hpsc: 0x%05x, Hfsc: 0x%05x\n", HPSC, HFSC); + v4l2_dbg(1, debug, sd, "Hpsc: 0x%05x, Hfsc: 0x%05x\n", HPSC, HFSC); /* write H fine-scaling (luminance) */ - saa711x_write(client, R_D8_B_HORIZ_LUMA_SCALING_INC, + saa711x_write(sd, R_D8_B_HORIZ_LUMA_SCALING_INC, (u8) (HFSC & 0xff)); - saa711x_write(client, R_D9_B_HORIZ_LUMA_SCALING_INC_MSB, + saa711x_write(sd, R_D9_B_HORIZ_LUMA_SCALING_INC_MSB, (u8) ((HFSC >> 8) & 0xff)); /* write H fine-scaling (chrominance) * must be lum/2, so i'll just bitshift :) */ - saa711x_write(client, R_DC_B_HORIZ_CHROMA_SCALING, + saa711x_write(sd, R_DC_B_HORIZ_CHROMA_SCALING, (u8) ((HFSC >> 1) & 0xff)); - saa711x_write(client, R_DD_B_HORIZ_CHROMA_SCALING_MSB, + saa711x_write(sd, R_DD_B_HORIZ_CHROMA_SCALING_MSB, (u8) ((HFSC >> 9) & 0xff)); VSCY = (int)((1024 * Vsrc) / height); - v4l_dbg(1, debug, client, "Vsrc: %d, Vscy: 0x%05x\n", Vsrc, VSCY); + v4l2_dbg(1, debug, sd, "Vsrc: %d, Vscy: 0x%05x\n", Vsrc, VSCY); /* Correct Contrast and Luminance */ - saa711x_write(client, R_D5_B_LUMA_CONTRAST_CNTL, + saa711x_write(sd, R_D5_B_LUMA_CONTRAST_CNTL, (u8) (64 * 1024 / VSCY)); - saa711x_write(client, R_D6_B_CHROMA_SATURATION_CNTL, + saa711x_write(sd, R_D6_B_CHROMA_SATURATION_CNTL, (u8) (64 * 1024 / VSCY)); /* write V fine-scaling (luminance) */ - saa711x_write(client, R_E0_B_VERT_LUMA_SCALING_INC, + saa711x_write(sd, R_E0_B_VERT_LUMA_SCALING_INC, (u8) (VSCY & 0xff)); - saa711x_write(client, R_E1_B_VERT_LUMA_SCALING_INC_MSB, + saa711x_write(sd, R_E1_B_VERT_LUMA_SCALING_INC_MSB, (u8) ((VSCY >> 8) & 0xff)); /* write V fine-scaling (chrominance) */ - saa711x_write(client, R_E2_B_VERT_CHROMA_SCALING_INC, + saa711x_write(sd, R_E2_B_VERT_CHROMA_SCALING_INC, (u8) (VSCY & 0xff)); - saa711x_write(client, R_E3_B_VERT_CHROMA_SCALING_INC_MSB, + saa711x_write(sd, R_E3_B_VERT_CHROMA_SCALING_INC_MSB, (u8) ((VSCY >> 8) & 0xff)); - saa711x_writeregs(client, saa7115_cfg_reset_scaler); + saa711x_writeregs(sd, saa7115_cfg_reset_scaler); /* Activates task "B" */ - saa711x_write(client, R_80_GLOBAL_CNTL_1, - saa711x_read(client,R_80_GLOBAL_CNTL_1) | 0x20); + saa711x_write(sd, R_80_GLOBAL_CNTL_1, + saa711x_read(sd, R_80_GLOBAL_CNTL_1) | 0x20); return 0; } -static void saa711x_set_v4lstd(struct i2c_client *client, v4l2_std_id std) +static void saa711x_set_v4lstd(struct v4l2_subdev *sd, v4l2_std_id std) { - struct saa711x_state *state = i2c_get_clientdata(client); + struct saa711x_state *state = to_state(sd); /* Prevent unnecessary standard changes. During a standard change the I-Port is temporarily disabled. Any devices @@ -933,13 +943,13 @@ static void saa711x_set_v4lstd(struct i2c_client *client, v4l2_std_id std) // This works for NTSC-M, SECAM-L and the 50Hz PAL variants. if (std & V4L2_STD_525_60) { - v4l_dbg(1, debug, client, "decoder set standard 60 Hz\n"); - saa711x_writeregs(client, saa7115_cfg_60hz_video); - saa711x_set_size(client, 720, 480); + v4l2_dbg(1, debug, sd, "decoder set standard 60 Hz\n"); + saa711x_writeregs(sd, saa7115_cfg_60hz_video); + saa711x_set_size(sd, 720, 480); } else { - v4l_dbg(1, debug, client, "decoder set standard 50 Hz\n"); - saa711x_writeregs(client, saa7115_cfg_50hz_video); - saa711x_set_size(client, 720, 576); + v4l2_dbg(1, debug, sd, "decoder set standard 50 Hz\n"); + saa711x_writeregs(sd, saa7115_cfg_50hz_video); + saa711x_set_size(sd, 720, 576); } /* Register 0E - Bits D6-D4 on NO-AUTO mode @@ -953,7 +963,7 @@ static void saa711x_set_v4lstd(struct i2c_client *client, v4l2_std_id std) */ if (state->ident == V4L2_IDENT_SAA7111 || state->ident == V4L2_IDENT_SAA7113) { - u8 reg = saa711x_read(client, R_0E_CHROMA_CNTL_1) & 0x8f; + u8 reg = saa711x_read(sd, R_0E_CHROMA_CNTL_1) & 0x8f; if (std == V4L2_STD_PAL_M) { reg |= 0x30; @@ -966,87 +976,31 @@ static void saa711x_set_v4lstd(struct i2c_client *client, v4l2_std_id std) } else if (std & V4L2_STD_SECAM) { reg |= 0x50; } - saa711x_write(client, R_0E_CHROMA_CNTL_1, reg); + saa711x_write(sd, R_0E_CHROMA_CNTL_1, reg); } else { /* restart task B if needed */ - int taskb = saa711x_read(client, R_80_GLOBAL_CNTL_1) & 0x10; + int taskb = saa711x_read(sd, R_80_GLOBAL_CNTL_1) & 0x10; if (taskb && state->ident == V4L2_IDENT_SAA7114) { - saa711x_writeregs(client, saa7115_cfg_vbi_on); + saa711x_writeregs(sd, saa7115_cfg_vbi_on); } /* switch audio mode too! */ - saa711x_set_audio_clock_freq(client, state->audclk_freq); - } -} - -static v4l2_std_id saa711x_get_v4lstd(struct i2c_client *client) -{ - struct saa711x_state *state = i2c_get_clientdata(client); - - return state->std; -} - -static void saa711x_log_status(struct i2c_client *client) -{ - struct saa711x_state *state = i2c_get_clientdata(client); - int reg1e, reg1f; - int signalOk; - int vcr; - - v4l_info(client, "Audio frequency: %d Hz\n", state->audclk_freq); - if (state->ident != V4L2_IDENT_SAA7115) { - /* status for the saa7114 */ - reg1f = saa711x_read(client, R_1F_STATUS_BYTE_2_VD_DEC); - signalOk = (reg1f & 0xc1) == 0x81; - v4l_info(client, "Video signal: %s\n", signalOk ? "ok" : "bad"); - v4l_info(client, "Frequency: %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz"); - return; - } - - /* status for the saa7115 */ - reg1e = saa711x_read(client, R_1E_STATUS_BYTE_1_VD_DEC); - reg1f = saa711x_read(client, R_1F_STATUS_BYTE_2_VD_DEC); - - signalOk = (reg1f & 0xc1) == 0x81 && (reg1e & 0xc0) == 0x80; - vcr = !(reg1f & 0x10); - - if (state->input >= 6) { - v4l_info(client, "Input: S-Video %d\n", state->input - 6); - } else { - v4l_info(client, "Input: Composite %d\n", state->input); + saa711x_s_clock_freq(sd, state->audclk_freq); } - v4l_info(client, "Video signal: %s\n", signalOk ? (vcr ? "VCR" : "broadcast/DVD") : "bad"); - v4l_info(client, "Frequency: %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz"); - - switch (reg1e & 0x03) { - case 1: - v4l_info(client, "Detected format: NTSC\n"); - break; - case 2: - v4l_info(client, "Detected format: PAL\n"); - break; - case 3: - v4l_info(client, "Detected format: SECAM\n"); - break; - default: - v4l_info(client, "Detected format: BW/No color\n"); - break; - } - v4l_info(client, "Width, Height: %d, %d\n", state->width, state->height); } /* setup the sliced VBI lcr registers according to the sliced VBI format */ -static void saa711x_set_lcr(struct i2c_client *client, struct v4l2_sliced_vbi_format *fmt) +static void saa711x_set_lcr(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt) { - struct saa711x_state *state = i2c_get_clientdata(client); + struct saa711x_state *state = to_state(sd); int is_50hz = (state->std & V4L2_STD_625_50); u8 lcr[24]; int i, x; #if 1 /* keep */ /* saa7113/7114/7118 VBI support are experimental */ - if (!saa711x_has_reg(state->ident,R_41_LCR_BASE)) + if (!saa711x_has_reg(state->ident, R_41_LCR_BASE)) return; #else @@ -1110,16 +1064,16 @@ static void saa711x_set_lcr(struct i2c_client *client, struct v4l2_sliced_vbi_fo /* write the lcr registers */ for (i = 2; i <= 23; i++) { - saa711x_write(client, i - 2 + R_41_LCR_BASE, lcr[i]); + saa711x_write(sd, i - 2 + R_41_LCR_BASE, lcr[i]); } /* enable/disable raw VBI capturing */ - saa711x_writeregs(client, fmt == NULL ? + saa711x_writeregs(sd, fmt == NULL ? saa7115_cfg_vbi_on : saa7115_cfg_vbi_off); } -static int saa711x_get_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt) +static int saa711x_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) { static u16 lcr2vbi[] = { 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */ @@ -1135,10 +1089,10 @@ static int saa711x_get_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt return -EINVAL; memset(sliced, 0, sizeof(*sliced)); /* done if using raw VBI */ - if (saa711x_read(client, R_80_GLOBAL_CNTL_1) & 0x10) + if (saa711x_read(sd, R_80_GLOBAL_CNTL_1) & 0x10) return 0; for (i = 2; i <= 23; i++) { - u8 v = saa711x_read(client, i - 2 + R_41_LCR_BASE); + u8 v = saa711x_read(sd, i - 2 + R_41_LCR_BASE); sliced->service_lines[0][i] = lcr2vbi[v >> 4]; sliced->service_lines[1][i] = lcr2vbi[v & 0xf]; @@ -1148,20 +1102,20 @@ static int saa711x_get_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt return 0; } -static int saa711x_set_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt) +static int saa711x_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) { if (fmt->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) { - saa711x_set_lcr(client, &fmt->fmt.sliced); + saa711x_set_lcr(sd, &fmt->fmt.sliced); return 0; } if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - saa711x_set_lcr(client, NULL); + saa711x_set_lcr(sd, NULL); return 0; } if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - return saa711x_set_size(client,fmt->fmt.pix.width,fmt->fmt.pix.height); + return saa711x_set_size(sd, fmt->fmt.pix.width, fmt->fmt.pix.height); } /* Decode the sliced VBI data stream as created by the saa7115. @@ -1170,13 +1124,12 @@ static int saa711x_set_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt The current implementation uses SAV/EAV codes and not the ancillary data headers. The vbi->p pointer points to the R_5E_SDID byte right after the SAV code. */ -static void saa711x_decode_vbi_line(struct i2c_client *client, - struct v4l2_decode_vbi_line *vbi) +static int saa711x_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi) { + struct saa711x_state *state = to_state(sd); static const char vbi_no_data_pattern[] = { 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0 }; - struct saa711x_state *state = i2c_get_clientdata(client); u8 *p = vbi->p; u32 wss; int id1, id2; /* the ID1 and ID2 bytes from the internal header */ @@ -1203,7 +1156,7 @@ static void saa711x_decode_vbi_line(struct i2c_client *client, /* If the VBI slicer does not detect any signal it will fill up the payload buffer with 0xa0 bytes. */ if (!memcmp(p, vbi_no_data_pattern, sizeof(vbi_no_data_pattern))) - return; + return 0; /* decode payloads */ switch (id2) { @@ -1212,275 +1165,352 @@ static void saa711x_decode_vbi_line(struct i2c_client *client, break; case 4: if (!saa711x_odd_parity(p[0]) || !saa711x_odd_parity(p[1])) - return; + return 0; vbi->type = V4L2_SLICED_CAPTION_525; break; case 5: wss = saa711x_decode_wss(p); if (wss == -1) - return; + return 0; p[0] = wss & 0xff; p[1] = wss >> 8; vbi->type = V4L2_SLICED_WSS_625; break; case 7: if (saa711x_decode_vps(p, p) != 0) - return; + return 0; vbi->type = V4L2_SLICED_VPS; break; default: - return; + break; } + return 0; } /* ============ SAA7115 AUDIO settings (end) ============= */ -static int saa7115_command(struct i2c_client *client, unsigned int cmd, void *arg) +static int saa711x_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) { - struct saa711x_state *state = i2c_get_clientdata(client); + struct saa711x_state *state = to_state(sd); + int status; - /* ioctls to allow direct access to the saa7115 registers for testing */ - switch (cmd) { - case VIDIOC_S_FMT: - return saa711x_set_v4lfmt(client, (struct v4l2_format *)arg); + if (state->radio) + return 0; + status = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC); - case VIDIOC_G_FMT: - return saa711x_get_v4lfmt(client, (struct v4l2_format *)arg); + v4l2_dbg(1, debug, sd, "status: 0x%02x\n", status); + vt->signal = ((status & (1 << 6)) == 0) ? 0xffff : 0x0; + return 0; +} - case VIDIOC_INT_AUDIO_CLOCK_FREQ: - return saa711x_set_audio_clock_freq(client, *(u32 *)arg); +static int saa711x_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) +{ + switch (qc->id) { + case V4L2_CID_BRIGHTNESS: + case V4L2_CID_CONTRAST: + case V4L2_CID_SATURATION: + case V4L2_CID_HUE: + return v4l2_ctrl_query_fill_std(qc); + default: + return -EINVAL; + } +} - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *vt = arg; - int status; +static int saa711x_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct saa711x_state *state = to_state(sd); - if (state->radio) - break; - status = saa711x_read(client, R_1F_STATUS_BYTE_2_VD_DEC); + state->radio = 0; + saa711x_set_v4lstd(sd, std); + return 0; +} - v4l_dbg(1, debug, client, "status: 0x%02x\n", status); - vt->signal = ((status & (1 << 6)) == 0) ? 0xffff : 0x0; - break; - } +static int saa711x_s_radio(struct v4l2_subdev *sd) +{ + struct saa711x_state *state = to_state(sd); - case VIDIOC_LOG_STATUS: - saa711x_log_status(client); - break; + state->radio = 1; + return 0; +} - case VIDIOC_G_CTRL: - return saa711x_get_v4lctrl(client, (struct v4l2_control *)arg); +static int saa711x_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +{ + struct saa711x_state *state = to_state(sd); + u32 input = route->input; + u8 mask = (state->ident == V4L2_IDENT_SAA7111) ? 0xf8 : 0xf0; + + v4l2_dbg(1, debug, sd, "decoder set input %d output %d\n", route->input, route->output); + /* saa7111/3 does not have these inputs */ + if ((state->ident == V4L2_IDENT_SAA7113 || + state->ident == V4L2_IDENT_SAA7111) && + (route->input == SAA7115_COMPOSITE4 || + route->input == SAA7115_COMPOSITE5)) { + return -EINVAL; + } + if (route->input > SAA7115_SVIDEO3) + return -EINVAL; + if (route->output > SAA7115_IPORT_ON) + return -EINVAL; + if (state->input == route->input && state->output == route->output) + return 0; + v4l2_dbg(1, debug, sd, "now setting %s input %s output\n", + (route->input >= SAA7115_SVIDEO0) ? "S-Video" : "Composite", + (route->output == SAA7115_IPORT_ON) ? "iport on" : "iport off"); + state->input = route->input; + + /* saa7111 has slightly different input numbering */ + if (state->ident == V4L2_IDENT_SAA7111) { + if (input >= SAA7115_COMPOSITE4) + input -= 2; + /* saa7111 specific */ + saa711x_write(sd, R_10_CHROMA_CNTL_2, + (saa711x_read(sd, R_10_CHROMA_CNTL_2) & 0x3f) | + ((route->output & 0xc0) ^ 0x40)); + saa711x_write(sd, R_13_RT_X_PORT_OUT_CNTL, + (saa711x_read(sd, R_13_RT_X_PORT_OUT_CNTL) & 0xf0) | + ((route->output & 2) ? 0x0a : 0)); + } - case VIDIOC_S_CTRL: - return saa711x_set_v4lctrl(client, (struct v4l2_control *)arg); + /* select mode */ + saa711x_write(sd, R_02_INPUT_CNTL_1, + (saa711x_read(sd, R_02_INPUT_CNTL_1) & mask) | + input); - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; + /* bypass chrominance trap for S-Video modes */ + saa711x_write(sd, R_09_LUMA_CNTL, + (saa711x_read(sd, R_09_LUMA_CNTL) & 0x7f) | + (state->input >= SAA7115_SVIDEO0 ? 0x80 : 0x0)); - switch (qc->id) { - case V4L2_CID_BRIGHTNESS: - case V4L2_CID_CONTRAST: - case V4L2_CID_SATURATION: - case V4L2_CID_HUE: - return v4l2_ctrl_query_fill_std(qc); - default: - return -EINVAL; - } + state->output = route->output; + if (state->ident == V4L2_IDENT_SAA7114 || + state->ident == V4L2_IDENT_SAA7115) { + saa711x_write(sd, R_83_X_PORT_I_O_ENA_AND_OUT_CLK, + (saa711x_read(sd, R_83_X_PORT_I_O_ENA_AND_OUT_CLK) & 0xfe) | + (state->output & 0x01)); } + return 0; +} - case VIDIOC_G_STD: - *(v4l2_std_id *)arg = saa711x_get_v4lstd(client); - break; +static int saa711x_s_gpio(struct v4l2_subdev *sd, u32 val) +{ + struct saa711x_state *state = to_state(sd); - case VIDIOC_S_STD: - state->radio = 0; - saa711x_set_v4lstd(client, *(v4l2_std_id *)arg); - break; + if (state->ident != V4L2_IDENT_SAA7111) + return -EINVAL; + saa711x_write(sd, 0x11, (saa711x_read(sd, 0x11) & 0x7f) | + (val ? 0x80 : 0)); + return 0; +} - case AUDC_SET_RADIO: - state->radio = 1; - break; +static int saa711x_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct saa711x_state *state = to_state(sd); - case VIDIOC_INT_G_VIDEO_ROUTING: - { - struct v4l2_routing *route = arg; + v4l2_dbg(1, debug, sd, "%s output\n", + enable ? "enable" : "disable"); - route->input = state->input; - route->output = state->output; - break; + if (state->enable != enable) { + state->enable = enable; + saa711x_write(sd, R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, + state->enable); } + return 0; +} - case VIDIOC_INT_S_VIDEO_ROUTING: - { - struct v4l2_routing *route = arg; - u32 input = route->input; - u8 mask = (state->ident == V4L2_IDENT_SAA7111) ? 0xf8 : 0xf0; - - v4l_dbg(1, debug, client, "decoder set input %d output %d\n", route->input, route->output); - /* saa7111/3 does not have these inputs */ - if ((state->ident == V4L2_IDENT_SAA7113 || - state->ident == V4L2_IDENT_SAA7111) && - (route->input == SAA7115_COMPOSITE4 || - route->input == SAA7115_COMPOSITE5)) { - return -EINVAL; - } - if (route->input > SAA7115_SVIDEO3) - return -EINVAL; - if (route->output > SAA7115_IPORT_ON) - return -EINVAL; - if (state->input == route->input && state->output == route->output) - break; - v4l_dbg(1, debug, client, "now setting %s input %s output\n", - (route->input >= SAA7115_SVIDEO0) ? "S-Video" : "Composite", (route->output == SAA7115_IPORT_ON) ? "iport on" : "iport off"); - state->input = route->input; - - /* saa7111 has slightly different input numbering */ - if (state->ident == V4L2_IDENT_SAA7111) { - if (input >= SAA7115_COMPOSITE4) - input -= 2; - /* saa7111 specific */ - saa711x_write(client, R_10_CHROMA_CNTL_2, - (saa711x_read(client, R_10_CHROMA_CNTL_2) & 0x3f) | - ((route->output & 0xc0) ^ 0x40)); - saa711x_write(client, R_13_RT_X_PORT_OUT_CNTL, - (saa711x_read(client, R_13_RT_X_PORT_OUT_CNTL) & 0xf0) | - ((route->output & 2) ? 0x0a : 0)); - } +static int saa711x_s_crystal_freq(struct v4l2_subdev *sd, struct v4l2_crystal_freq *freq) +{ + struct saa711x_state *state = to_state(sd); - /* select mode */ - saa711x_write(client, R_02_INPUT_CNTL_1, - (saa711x_read(client, R_02_INPUT_CNTL_1) & mask) | - input); + if (freq->freq != SAA7115_FREQ_32_11_MHZ && + freq->freq != SAA7115_FREQ_24_576_MHZ) + return -EINVAL; + state->crystal_freq = freq->freq; + state->cgcdiv = (freq->flags & SAA7115_FREQ_FL_CGCDIV) ? 3 : 4; + state->ucgc = (freq->flags & SAA7115_FREQ_FL_UCGC) ? 1 : 0; + state->apll = (freq->flags & SAA7115_FREQ_FL_APLL) ? 1 : 0; + saa711x_s_clock_freq(sd, state->audclk_freq); + return 0; +} - /* bypass chrominance trap for S-Video modes */ - saa711x_write(client, R_09_LUMA_CNTL, - (saa711x_read(client, R_09_LUMA_CNTL) & 0x7f) | - (state->input >= SAA7115_SVIDEO0 ? 0x80 : 0x0)); +static int saa711x_reset(struct v4l2_subdev *sd, u32 val) +{ + v4l2_dbg(1, debug, sd, "decoder RESET\n"); + saa711x_writeregs(sd, saa7115_cfg_reset_scaler); + return 0; +} - state->output = route->output; - if (state->ident == V4L2_IDENT_SAA7114 || - state->ident == V4L2_IDENT_SAA7115) { - saa711x_write(client, R_83_X_PORT_I_O_ENA_AND_OUT_CLK, - (saa711x_read(client, R_83_X_PORT_I_O_ENA_AND_OUT_CLK) & 0xfe) | - (state->output & 0x01)); +static int saa711x_g_vbi_data(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_data *data) +{ + /* Note: the internal field ID is inverted for NTSC, + so data->field 0 maps to the saa7115 even field, + whereas for PAL it maps to the saa7115 odd field. */ + switch (data->id) { + case V4L2_SLICED_WSS_625: + if (saa711x_read(sd, 0x6b) & 0xc0) + return -EIO; + data->data[0] = saa711x_read(sd, 0x6c); + data->data[1] = saa711x_read(sd, 0x6d); + return 0; + case V4L2_SLICED_CAPTION_525: + if (data->field == 0) { + /* CC */ + if (saa711x_read(sd, 0x66) & 0x30) + return -EIO; + data->data[0] = saa711x_read(sd, 0x69); + data->data[1] = saa711x_read(sd, 0x6a); + return 0; } - break; + /* XDS */ + if (saa711x_read(sd, 0x66) & 0xc0) + return -EIO; + data->data[0] = saa711x_read(sd, 0x67); + data->data[1] = saa711x_read(sd, 0x68); + return 0; + default: + return -EINVAL; } +} - case VIDIOC_STREAMON: - case VIDIOC_STREAMOFF: - v4l_dbg(1, debug, client, "%s output\n", - (cmd == VIDIOC_STREAMON) ? "enable" : "disable"); - - if (state->enable != (cmd == VIDIOC_STREAMON)) { - state->enable = (cmd == VIDIOC_STREAMON); - saa711x_write(client, - R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, - state->enable); - } - break; +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int saa711x_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); - case VIDIOC_INT_S_CRYSTAL_FREQ: - { - struct v4l2_crystal_freq *freq = arg; + if (!v4l2_chip_match_i2c_client(client, + reg->match_type, reg->match_chip)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reg->val = saa711x_read(sd, reg->reg & 0xff); + return 0; +} - if (freq->freq != SAA7115_FREQ_32_11_MHZ && - freq->freq != SAA7115_FREQ_24_576_MHZ) - return -EINVAL; - state->crystal_freq = freq->freq; - state->cgcdiv = (freq->flags & SAA7115_FREQ_FL_CGCDIV) ? 3 : 4; - state->ucgc = (freq->flags & SAA7115_FREQ_FL_UCGC) ? 1 : 0; - state->apll = (freq->flags & SAA7115_FREQ_FL_APLL) ? 1 : 0; - saa711x_set_audio_clock_freq(client, state->audclk_freq); - break; - } +static int saa711x_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); - case VIDIOC_INT_DECODE_VBI_LINE: - saa711x_decode_vbi_line(client, arg); - break; + if (!v4l2_chip_match_i2c_client(client, + reg->match_type, reg->match_chip)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + saa711x_write(sd, reg->reg & 0xff, reg->val & 0xff); + return 0; +} +#endif - case VIDIOC_INT_RESET: - v4l_dbg(1, debug, client, "decoder RESET\n"); - saa711x_writeregs(client, saa7115_cfg_reset_scaler); - break; +static int saa711x_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) +{ + struct saa711x_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - case VIDIOC_INT_S_GPIO: - if (state->ident != V4L2_IDENT_SAA7111) - return -EINVAL; - saa711x_write(client, 0x11, (saa711x_read(client, 0x11) & 0x7f) | - (*(u32 *)arg ? 0x80 : 0)); - break; + return v4l2_chip_ident_i2c_client(client, chip, state->ident, 0); +} - case VIDIOC_INT_G_VBI_DATA: - { - struct v4l2_sliced_vbi_data *data = arg; +static int saa711x_log_status(struct v4l2_subdev *sd) +{ + struct saa711x_state *state = to_state(sd); + int reg1e, reg1f; + int signalOk; + int vcr; - /* Note: the internal field ID is inverted for NTSC, - so data->field 0 maps to the saa7115 even field, - whereas for PAL it maps to the saa7115 odd field. */ - switch (data->id) { - case V4L2_SLICED_WSS_625: - if (saa711x_read(client, 0x6b) & 0xc0) - return -EIO; - data->data[0] = saa711x_read(client, 0x6c); - data->data[1] = saa711x_read(client, 0x6d); - return 0; - case V4L2_SLICED_CAPTION_525: - if (data->field == 0) { - /* CC */ - if (saa711x_read(client, 0x66) & 0x30) - return -EIO; - data->data[0] = saa711x_read(client, 0x69); - data->data[1] = saa711x_read(client, 0x6a); - return 0; - } - /* XDS */ - if (saa711x_read(client, 0x66) & 0xc0) - return -EIO; - data->data[0] = saa711x_read(client, 0x67); - data->data[1] = saa711x_read(client, 0x68); - return 0; - default: - return -EINVAL; - } - break; + v4l2_info(sd, "Audio frequency: %d Hz\n", state->audclk_freq); + if (state->ident != V4L2_IDENT_SAA7115) { + /* status for the saa7114 */ + reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC); + signalOk = (reg1f & 0xc1) == 0x81; + v4l2_info(sd, "Video signal: %s\n", signalOk ? "ok" : "bad"); + v4l2_info(sd, "Frequency: %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz"); + return 0; } -#ifdef CONFIG_VIDEO_ADV_DEBUG - case VIDIOC_DBG_G_REGISTER: - case VIDIOC_DBG_S_REGISTER: - { - struct v4l2_register *reg = arg; + /* status for the saa7115 */ + reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC); + reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC); - if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - if (cmd == VIDIOC_DBG_G_REGISTER) - reg->val = saa711x_read(client, reg->reg & 0xff); - else - saa711x_write(client, reg->reg & 0xff, reg->val & 0xff); - break; - } -#endif + signalOk = (reg1f & 0xc1) == 0x81 && (reg1e & 0xc0) == 0x80; + vcr = !(reg1f & 0x10); - case VIDIOC_G_CHIP_IDENT: - return v4l2_chip_ident_i2c_client(client, arg, state->ident, 0); + if (state->input >= 6) + v4l2_info(sd, "Input: S-Video %d\n", state->input - 6); + else + v4l2_info(sd, "Input: Composite %d\n", state->input); + v4l2_info(sd, "Video signal: %s\n", signalOk ? (vcr ? "VCR" : "broadcast/DVD") : "bad"); + v4l2_info(sd, "Frequency: %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz"); + switch (reg1e & 0x03) { + case 1: + v4l2_info(sd, "Detected format: NTSC\n"); + break; + case 2: + v4l2_info(sd, "Detected format: PAL\n"); + break; + case 3: + v4l2_info(sd, "Detected format: SECAM\n"); + break; default: - return -EINVAL; + v4l2_info(sd, "Detected format: BW/No color\n"); + break; } - + v4l2_info(sd, "Width, Height: %d, %d\n", state->width, state->height); return 0; } +static int saa711x_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops saa711x_core_ops = { + .log_status = saa711x_log_status, + .g_chip_ident = saa711x_g_chip_ident, + .g_ctrl = saa711x_g_ctrl, + .s_ctrl = saa711x_s_ctrl, + .queryctrl = saa711x_queryctrl, + .reset = saa711x_reset, + .s_gpio = saa711x_s_gpio, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = saa711x_g_register, + .s_register = saa711x_s_register, +#endif +}; + +static const struct v4l2_subdev_tuner_ops saa711x_tuner_ops = { + .s_std = saa711x_s_std, + .s_radio = saa711x_s_radio, + .g_tuner = saa711x_g_tuner, +}; + +static const struct v4l2_subdev_audio_ops saa711x_audio_ops = { + .s_clock_freq = saa711x_s_clock_freq, +}; + +static const struct v4l2_subdev_video_ops saa711x_video_ops = { + .s_routing = saa711x_s_routing, + .s_crystal_freq = saa711x_s_crystal_freq, + .g_fmt = saa711x_g_fmt, + .s_fmt = saa711x_s_fmt, + .g_vbi_data = saa711x_g_vbi_data, + .decode_vbi_line = saa711x_decode_vbi_line, + .s_stream = saa711x_s_stream, +}; + +static const struct v4l2_subdev_ops saa711x_ops = { + .core = &saa711x_core_ops, + .tuner = &saa711x_tuner_ops, + .audio = &saa711x_audio_ops, + .video = &saa711x_video_ops, +}; + /* ----------------------------------------------------------------------- */ -static int saa7115_probe(struct i2c_client *client, +static int saa711x_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct saa711x_state *state; + struct v4l2_subdev *sd; int i; char name[17]; char chip_id; @@ -1497,8 +1527,8 @@ static int saa7115_probe(struct i2c_client *client, #endif for (i = 0; i < 0x0f; i++) { - saa711x_write(client, 0, i); - name[i] = (saa711x_read(client, 0) & 0x0f) + '0'; + i2c_smbus_write_byte_data(client, 0, i); + name[i] = (i2c_smbus_read_byte_data(client, 0) & 0x0f) + '0'; if (name[i] > '9') name[i] += 'a' - '9' - 1; } @@ -1527,7 +1557,8 @@ static int saa7115_probe(struct i2c_client *client, state = kzalloc(sizeof(struct saa711x_state), GFP_KERNEL); if (state == NULL) return -ENOMEM; - i2c_set_clientdata(client, state); + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &saa711x_ops); state->input = -1; state->output = SAA7115_IPORT_ON; state->enable = 1; @@ -1554,41 +1585,45 @@ static int saa7115_probe(struct i2c_client *client, break; default: state->ident = V4L2_IDENT_SAA7111; - v4l_info(client, "WARNING: Chip is not known - Falling back to saa7111\n"); + v4l2_info(sd, "WARNING: Chip is not known - Falling back to saa7111\n"); } state->audclk_freq = 48000; - v4l_dbg(1, debug, client, "writing init values\n"); + v4l2_dbg(1, debug, sd, "writing init values\n"); /* init to 60hz/48khz */ state->crystal_freq = SAA7115_FREQ_24_576_MHZ; switch (state->ident) { case V4L2_IDENT_SAA7111: - saa711x_writeregs(client, saa7111_init); + saa711x_writeregs(sd, saa7111_init); break; case V4L2_IDENT_SAA7113: - saa711x_writeregs(client, saa7113_init); + saa711x_writeregs(sd, saa7113_init); break; default: state->crystal_freq = SAA7115_FREQ_32_11_MHZ; - saa711x_writeregs(client, saa7115_init_auto_input); + saa711x_writeregs(sd, saa7115_init_auto_input); } if (state->ident != V4L2_IDENT_SAA7111) - saa711x_writeregs(client, saa7115_init_misc); - saa711x_set_v4lstd(client, V4L2_STD_NTSC); + saa711x_writeregs(sd, saa7115_init_misc); + saa711x_set_v4lstd(sd, V4L2_STD_NTSC); - v4l_dbg(1, debug, client, "status: (1E) 0x%02x, (1F) 0x%02x\n", - saa711x_read(client, R_1E_STATUS_BYTE_1_VD_DEC), saa711x_read(client, R_1F_STATUS_BYTE_2_VD_DEC)); + v4l2_dbg(1, debug, sd, "status: (1E) 0x%02x, (1F) 0x%02x\n", + saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC), + saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC)); return 0; } /* ----------------------------------------------------------------------- */ -static int saa7115_remove(struct i2c_client *client) +static int saa711x_remove(struct i2c_client *client) { - kfree(i2c_get_clientdata(client)); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); return 0; } @@ -1608,9 +1643,9 @@ MODULE_DEVICE_TABLE(i2c, saa7115_id); static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "saa7115", .driverid = I2C_DRIVERID_SAA711X, - .command = saa7115_command, - .probe = saa7115_probe, - .remove = saa7115_remove, + .command = saa711x_command, + .probe = saa711x_probe, + .remove = saa711x_remove, .legacy_class = I2C_CLASS_TV_ANALOG | I2C_CLASS_TV_DIGITAL, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) .id_table = saa7115_id, -- cgit v1.2.3 From e9df8e267dfb3180b0e396e7cf94effa8f55d21c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 29 Nov 2008 16:56:38 +0100 Subject: saa7127: convert to v4l2_subdev. From: Hans Verkuil Priority: normal Signed-off-by: Hans Verkuil --- linux/drivers/media/video/saa7127.c | 421 ++++++++++++++++++++---------------- 1 file changed, 232 insertions(+), 189 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/saa7127.c b/linux/drivers/media/video/saa7127.c index fd7d15305..5414b027f 100644 --- a/linux/drivers/media/video/saa7127.c +++ b/linux/drivers/media/video/saa7127.c @@ -53,7 +53,7 @@ #include #include #include -#include +#include #include #include #include @@ -237,6 +237,7 @@ static struct i2c_reg_value saa7127_init_config_50hz[] = { */ struct saa7127_state { + struct v4l2_subdev sd; v4l2_std_id std; u32 ident; enum saa7127_input_type input_type; @@ -256,6 +257,11 @@ struct saa7127_state { u8 reg_61; }; +static inline struct saa7127_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct saa7127_state, sd); +} + static const char * const output_strs[] = { "S-Video + Composite", @@ -287,32 +293,35 @@ static const char * const wss_strs[] = { /* ----------------------------------------------------------------------- */ -static int saa7127_read(struct i2c_client *client, u8 reg) +static int saa7127_read(struct v4l2_subdev *sd, u8 reg) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + return i2c_smbus_read_byte_data(client, reg); } /* ----------------------------------------------------------------------- */ -static int saa7127_write(struct i2c_client *client, u8 reg, u8 val) +static int saa7127_write(struct v4l2_subdev *sd, u8 reg, u8 val) { + struct i2c_client *client = v4l2_get_subdevdata(sd); int i; for (i = 0; i < 3; i++) { if (i2c_smbus_write_byte_data(client, reg, val) == 0) return 0; } - v4l_err(client, "I2C Write Problem\n"); + v4l2_err(sd, "I2C Write Problem\n"); return -1; } /* ----------------------------------------------------------------------- */ -static int saa7127_write_inittab(struct i2c_client *client, +static int saa7127_write_inittab(struct v4l2_subdev *sd, const struct i2c_reg_value *regs) { while (regs->reg != 0) { - saa7127_write(client, regs->reg, regs->value); + saa7127_write(sd, regs->reg, regs->value); regs++; } return 0; @@ -320,16 +329,16 @@ static int saa7127_write_inittab(struct i2c_client *client, /* ----------------------------------------------------------------------- */ -static int saa7127_set_vps(struct i2c_client *client, struct v4l2_sliced_vbi_data *data) +static int saa7127_set_vps(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data) { - struct saa7127_state *state = i2c_get_clientdata(client); + struct saa7127_state *state = to_state(sd); int enable = (data->line != 0); if (enable && (data->field != 0 || data->line != 16)) return -EINVAL; if (state->vps_enable != enable) { - v4l_dbg(1, debug, client, "Turn VPS Signal %s\n", enable ? "on" : "off"); - saa7127_write(client, 0x54, enable << 7); + v4l2_dbg(1, debug, sd, "Turn VPS Signal %s\n", enable ? "on" : "off"); + saa7127_write(sd, 0x54, enable << 7); state->vps_enable = enable; } if (!enable) @@ -340,91 +349,91 @@ static int saa7127_set_vps(struct i2c_client *client, struct v4l2_sliced_vbi_dat state->vps_data[2] = data->data[9]; state->vps_data[3] = data->data[10]; state->vps_data[4] = data->data[11]; - v4l_dbg(1, debug, client, "Set VPS data %02x %02x %02x %02x %02x\n", + v4l2_dbg(1, debug, sd, "Set VPS data %02x %02x %02x %02x %02x\n", state->vps_data[0], state->vps_data[1], state->vps_data[2], state->vps_data[3], state->vps_data[4]); - saa7127_write(client, 0x55, state->vps_data[0]); - saa7127_write(client, 0x56, state->vps_data[1]); - saa7127_write(client, 0x57, state->vps_data[2]); - saa7127_write(client, 0x58, state->vps_data[3]); - saa7127_write(client, 0x59, state->vps_data[4]); + saa7127_write(sd, 0x55, state->vps_data[0]); + saa7127_write(sd, 0x56, state->vps_data[1]); + saa7127_write(sd, 0x57, state->vps_data[2]); + saa7127_write(sd, 0x58, state->vps_data[3]); + saa7127_write(sd, 0x59, state->vps_data[4]); return 0; } /* ----------------------------------------------------------------------- */ -static int saa7127_set_cc(struct i2c_client *client, struct v4l2_sliced_vbi_data *data) +static int saa7127_set_cc(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data) { - struct saa7127_state *state = i2c_get_clientdata(client); + struct saa7127_state *state = to_state(sd); u16 cc = data->data[1] << 8 | data->data[0]; int enable = (data->line != 0); if (enable && (data->field != 0 || data->line != 21)) return -EINVAL; if (state->cc_enable != enable) { - v4l_dbg(1, debug, client, + v4l2_dbg(1, debug, sd, "Turn CC %s\n", enable ? "on" : "off"); - saa7127_write(client, SAA7127_REG_CLOSED_CAPTION, + saa7127_write(sd, SAA7127_REG_CLOSED_CAPTION, (state->xds_enable << 7) | (enable << 6) | 0x11); state->cc_enable = enable; } if (!enable) return 0; - v4l_dbg(2, debug, client, "CC data: %04x\n", cc); - saa7127_write(client, SAA7127_REG_LINE_21_ODD_0, cc & 0xff); - saa7127_write(client, SAA7127_REG_LINE_21_ODD_1, cc >> 8); + v4l2_dbg(2, debug, sd, "CC data: %04x\n", cc); + saa7127_write(sd, SAA7127_REG_LINE_21_ODD_0, cc & 0xff); + saa7127_write(sd, SAA7127_REG_LINE_21_ODD_1, cc >> 8); state->cc_data = cc; return 0; } /* ----------------------------------------------------------------------- */ -static int saa7127_set_xds(struct i2c_client *client, struct v4l2_sliced_vbi_data *data) +static int saa7127_set_xds(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data) { - struct saa7127_state *state = i2c_get_clientdata(client); + struct saa7127_state *state = to_state(sd); u16 xds = data->data[1] << 8 | data->data[0]; int enable = (data->line != 0); if (enable && (data->field != 1 || data->line != 21)) return -EINVAL; if (state->xds_enable != enable) { - v4l_dbg(1, debug, client, "Turn XDS %s\n", enable ? "on" : "off"); - saa7127_write(client, SAA7127_REG_CLOSED_CAPTION, + v4l2_dbg(1, debug, sd, "Turn XDS %s\n", enable ? "on" : "off"); + saa7127_write(sd, SAA7127_REG_CLOSED_CAPTION, (enable << 7) | (state->cc_enable << 6) | 0x11); state->xds_enable = enable; } if (!enable) return 0; - v4l_dbg(2, debug, client, "XDS data: %04x\n", xds); - saa7127_write(client, SAA7127_REG_LINE_21_EVEN_0, xds & 0xff); - saa7127_write(client, SAA7127_REG_LINE_21_EVEN_1, xds >> 8); + v4l2_dbg(2, debug, sd, "XDS data: %04x\n", xds); + saa7127_write(sd, SAA7127_REG_LINE_21_EVEN_0, xds & 0xff); + saa7127_write(sd, SAA7127_REG_LINE_21_EVEN_1, xds >> 8); state->xds_data = xds; return 0; } /* ----------------------------------------------------------------------- */ -static int saa7127_set_wss(struct i2c_client *client, struct v4l2_sliced_vbi_data *data) +static int saa7127_set_wss(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data) { - struct saa7127_state *state = i2c_get_clientdata(client); + struct saa7127_state *state = to_state(sd); int enable = (data->line != 0); if (enable && (data->field != 0 || data->line != 23)) return -EINVAL; if (state->wss_enable != enable) { - v4l_dbg(1, debug, client, "Turn WSS %s\n", enable ? "on" : "off"); - saa7127_write(client, 0x27, enable << 7); + v4l2_dbg(1, debug, sd, "Turn WSS %s\n", enable ? "on" : "off"); + saa7127_write(sd, 0x27, enable << 7); state->wss_enable = enable; } if (!enable) return 0; - saa7127_write(client, 0x26, data->data[0]); - saa7127_write(client, 0x27, 0x80 | (data->data[1] & 0x3f)); - v4l_dbg(1, debug, client, + saa7127_write(sd, 0x26, data->data[0]); + saa7127_write(sd, 0x27, 0x80 | (data->data[1] & 0x3f)); + v4l2_dbg(1, debug, sd, "WSS mode: %s\n", wss_strs[data->data[0] & 0xf]); state->wss_mode = (data->data[1] & 0x3f) << 8 | data->data[0]; return 0; @@ -432,18 +441,18 @@ static int saa7127_set_wss(struct i2c_client *client, struct v4l2_sliced_vbi_dat /* ----------------------------------------------------------------------- */ -static int saa7127_set_video_enable(struct i2c_client *client, int enable) +static int saa7127_set_video_enable(struct v4l2_subdev *sd, int enable) { - struct saa7127_state *state = i2c_get_clientdata(client); + struct saa7127_state *state = to_state(sd); if (enable) { - v4l_dbg(1, debug, client, "Enable Video Output\n"); - saa7127_write(client, 0x2d, state->reg_2d); - saa7127_write(client, 0x61, state->reg_61); + v4l2_dbg(1, debug, sd, "Enable Video Output\n"); + saa7127_write(sd, 0x2d, state->reg_2d); + saa7127_write(sd, 0x61, state->reg_61); } else { - v4l_dbg(1, debug, client, "Disable Video Output\n"); - saa7127_write(client, 0x2d, (state->reg_2d & 0xf0)); - saa7127_write(client, 0x61, (state->reg_61 | 0xc0)); + v4l2_dbg(1, debug, sd, "Disable Video Output\n"); + saa7127_write(sd, 0x2d, (state->reg_2d & 0xf0)); + saa7127_write(sd, 0x61, (state->reg_61 | 0xc0)); } state->video_enable = enable; return 0; @@ -451,32 +460,32 @@ static int saa7127_set_video_enable(struct i2c_client *client, int enable) /* ----------------------------------------------------------------------- */ -static int saa7127_set_std(struct i2c_client *client, v4l2_std_id std) +static int saa7127_set_std(struct v4l2_subdev *sd, v4l2_std_id std) { - struct saa7127_state *state = i2c_get_clientdata(client); + struct saa7127_state *state = to_state(sd); const struct i2c_reg_value *inittab; if (std & V4L2_STD_525_60) { - v4l_dbg(1, debug, client, "Selecting 60 Hz video Standard\n"); + v4l2_dbg(1, debug, sd, "Selecting 60 Hz video Standard\n"); inittab = saa7127_init_config_60hz; state->reg_61 = SAA7127_60HZ_DAC_CONTROL; } else { - v4l_dbg(1, debug, client, "Selecting 50 Hz video Standard\n"); + v4l2_dbg(1, debug, sd, "Selecting 50 Hz video Standard\n"); inittab = saa7127_init_config_50hz; state->reg_61 = SAA7127_50HZ_DAC_CONTROL; } /* Write Table */ - saa7127_write_inittab(client, inittab); + saa7127_write_inittab(sd, inittab); state->std = std; return 0; } /* ----------------------------------------------------------------------- */ -static int saa7127_set_output_type(struct i2c_client *client, int output) +static int saa7127_set_output_type(struct v4l2_subdev *sd, int output) { - struct saa7127_state *state = i2c_get_clientdata(client); + struct saa7127_state *state = to_state(sd); switch (output) { case SAA7127_OUTPUT_TYPE_RGB: @@ -512,165 +521,195 @@ static int saa7127_set_output_type(struct i2c_client *client, int output) default: return -EINVAL; } - v4l_dbg(1, debug, client, + v4l2_dbg(1, debug, sd, "Selecting %s output type\n", output_strs[output]); /* Configure Encoder */ - saa7127_write(client, 0x2d, state->reg_2d); - saa7127_write(client, 0x3a, state->reg_3a | state->reg_3a_cb); + saa7127_write(sd, 0x2d, state->reg_2d); + saa7127_write(sd, 0x3a, state->reg_3a | state->reg_3a_cb); state->output_type = output; return 0; } /* ----------------------------------------------------------------------- */ -static int saa7127_set_input_type(struct i2c_client *client, int input) +static int saa7127_set_input_type(struct v4l2_subdev *sd, int input) { - struct saa7127_state *state = i2c_get_clientdata(client); + struct saa7127_state *state = to_state(sd); switch (input) { case SAA7127_INPUT_TYPE_NORMAL: /* avia */ - v4l_dbg(1, debug, client, "Selecting Normal Encoder Input\n"); + v4l2_dbg(1, debug, sd, "Selecting Normal Encoder Input\n"); state->reg_3a_cb = 0; break; case SAA7127_INPUT_TYPE_TEST_IMAGE: /* color bar */ - v4l_dbg(1, debug, client, "Selecting Color Bar generator\n"); + v4l2_dbg(1, debug, sd, "Selecting Color Bar generator\n"); state->reg_3a_cb = 0x80; break; default: return -EINVAL; } - saa7127_write(client, 0x3a, state->reg_3a | state->reg_3a_cb); + saa7127_write(sd, 0x3a, state->reg_3a | state->reg_3a_cb); state->input_type = input; return 0; } /* ----------------------------------------------------------------------- */ -static int saa7127_command(struct i2c_client *client, - unsigned int cmd, void *arg) +static int saa7127_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) { - struct saa7127_state *state = i2c_get_clientdata(client); - struct v4l2_format *fmt = arg; - struct v4l2_routing *route = arg; - - switch (cmd) { - case VIDIOC_INT_S_STD_OUTPUT: - if (state->std == *(v4l2_std_id *)arg) - break; - return saa7127_set_std(client, *(v4l2_std_id *)arg); - - case VIDIOC_INT_G_STD_OUTPUT: - *(v4l2_std_id *)arg = state->std; - break; + struct saa7127_state *state = to_state(sd); - case VIDIOC_INT_G_VIDEO_ROUTING: - route->input = state->input_type; - route->output = state->output_type; - break; + if (state->std == std) + return 0; + return saa7127_set_std(sd, std); +} - case VIDIOC_INT_S_VIDEO_ROUTING: - { - int rc = 0; +static int saa7127_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +{ + struct saa7127_state *state = to_state(sd); + int rc = 0; + + if (state->input_type != route->input) + rc = saa7127_set_input_type(sd, route->input); + if (rc == 0 && state->output_type != route->output) + rc = saa7127_set_output_type(sd, route->output); + return rc; +} - if (state->input_type != route->input) - rc = saa7127_set_input_type(client, route->input); - if (rc == 0 && state->output_type != route->output) - rc = saa7127_set_output_type(client, route->output); - return rc; - } +static int saa7127_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct saa7127_state *state = to_state(sd); - case VIDIOC_STREAMON: - case VIDIOC_STREAMOFF: - if (state->video_enable == (cmd == VIDIOC_STREAMON)) - break; - return saa7127_set_video_enable(client, cmd == VIDIOC_STREAMON); - - case VIDIOC_G_FMT: - if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) - return -EINVAL; - - memset(&fmt->fmt.sliced, 0, sizeof(fmt->fmt.sliced)); - if (state->vps_enable) - fmt->fmt.sliced.service_lines[0][16] = V4L2_SLICED_VPS; - if (state->wss_enable) - fmt->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625; - if (state->cc_enable) { - fmt->fmt.sliced.service_lines[0][21] = V4L2_SLICED_CAPTION_525; - fmt->fmt.sliced.service_lines[1][21] = V4L2_SLICED_CAPTION_525; - } - fmt->fmt.sliced.service_set = - (state->vps_enable ? V4L2_SLICED_VPS : 0) | - (state->wss_enable ? V4L2_SLICED_WSS_625 : 0) | - (state->cc_enable ? V4L2_SLICED_CAPTION_525 : 0); - break; + if (state->video_enable == enable) + return 0; + return saa7127_set_video_enable(sd, enable); +} - case VIDIOC_LOG_STATUS: - v4l_info(client, "Standard: %s\n", (state->std & V4L2_STD_525_60) ? "60 Hz" : "50 Hz"); - v4l_info(client, "Input: %s\n", state->input_type ? "color bars" : "normal"); - v4l_info(client, "Output: %s\n", state->video_enable ? - output_strs[state->output_type] : "disabled"); - v4l_info(client, "WSS: %s\n", state->wss_enable ? - wss_strs[state->wss_mode] : "disabled"); - v4l_info(client, "VPS: %s\n", state->vps_enable ? "enabled" : "disabled"); - v4l_info(client, "CC: %s\n", state->cc_enable ? "enabled" : "disabled"); - break; +static int saa7127_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) +{ + struct saa7127_state *state = to_state(sd); -#ifdef CONFIG_VIDEO_ADV_DEBUG - case VIDIOC_DBG_G_REGISTER: - case VIDIOC_DBG_S_REGISTER: - { - struct v4l2_register *reg = arg; - - if (!v4l2_chip_match_i2c_client(client, - reg->match_type, reg->match_chip)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - if (cmd == VIDIOC_DBG_G_REGISTER) - reg->val = saa7127_read(client, reg->reg & 0xff); - else - saa7127_write(client, reg->reg & 0xff, reg->val & 0xff); - break; - } -#endif + if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) + return -EINVAL; - case VIDIOC_INT_S_VBI_DATA: - { - struct v4l2_sliced_vbi_data *data = arg; - - switch (data->id) { - case V4L2_SLICED_WSS_625: - return saa7127_set_wss(client, data); - case V4L2_SLICED_VPS: - return saa7127_set_vps(client, data); - case V4L2_SLICED_CAPTION_525: - if (data->field == 0) - return saa7127_set_cc(client, data); - return saa7127_set_xds(client, data); - default: - return -EINVAL; - } - break; + memset(&fmt->fmt.sliced, 0, sizeof(fmt->fmt.sliced)); + if (state->vps_enable) + fmt->fmt.sliced.service_lines[0][16] = V4L2_SLICED_VPS; + if (state->wss_enable) + fmt->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625; + if (state->cc_enable) { + fmt->fmt.sliced.service_lines[0][21] = V4L2_SLICED_CAPTION_525; + fmt->fmt.sliced.service_lines[1][21] = V4L2_SLICED_CAPTION_525; } + fmt->fmt.sliced.service_set = + (state->vps_enable ? V4L2_SLICED_VPS : 0) | + (state->wss_enable ? V4L2_SLICED_WSS_625 : 0) | + (state->cc_enable ? V4L2_SLICED_CAPTION_525 : 0); + return 0; +} - case VIDIOC_G_CHIP_IDENT: - return v4l2_chip_ident_i2c_client(client, arg, state->ident, 0); - +static int saa7127_s_vbi_data(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data) +{ + switch (data->id) { + case V4L2_SLICED_WSS_625: + return saa7127_set_wss(sd, data); + case V4L2_SLICED_VPS: + return saa7127_set_vps(sd, data); + case V4L2_SLICED_CAPTION_525: + if (data->field == 0) + return saa7127_set_cc(sd, data); + return saa7127_set_xds(sd, data); default: return -EINVAL; } return 0; } +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int saa7127_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, + reg->match_type, reg->match_chip)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reg->val = saa7127_read(sd, reg->reg & 0xff); + return 0; +} + +static int saa7127_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, + reg->match_type, reg->match_chip)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + saa7127_write(sd, reg->reg & 0xff, reg->val & 0xff); + return 0; +} +#endif + +static int saa7127_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) +{ + struct saa7127_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, state->ident, 0); +} + +static int saa7127_log_status(struct v4l2_subdev *sd) +{ + struct saa7127_state *state = to_state(sd); + + v4l2_info(sd, "Standard: %s\n", (state->std & V4L2_STD_525_60) ? "60 Hz" : "50 Hz"); + v4l2_info(sd, "Input: %s\n", state->input_type ? "color bars" : "normal"); + v4l2_info(sd, "Output: %s\n", state->video_enable ? + output_strs[state->output_type] : "disabled"); + v4l2_info(sd, "WSS: %s\n", state->wss_enable ? + wss_strs[state->wss_mode] : "disabled"); + v4l2_info(sd, "VPS: %s\n", state->vps_enable ? "enabled" : "disabled"); + v4l2_info(sd, "CC: %s\n", state->cc_enable ? "enabled" : "disabled"); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops saa7127_core_ops = { + .log_status = saa7127_log_status, + .g_chip_ident = saa7127_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = saa7127_g_register, + .s_register = saa7127_s_register, +#endif +}; + +static const struct v4l2_subdev_video_ops saa7127_video_ops = { + .s_vbi_data = saa7127_s_vbi_data, + .g_fmt = saa7127_g_fmt, + .s_std_output = saa7127_s_std_output, + .s_routing = saa7127_s_routing, + .s_stream = saa7127_s_stream, +}; + +static const struct v4l2_subdev_ops saa7127_ops = { + .core = &saa7127_core_ops, + .video = &saa7127_video_ops, +}; + /* ----------------------------------------------------------------------- */ static int saa7127_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct saa7127_state *state; + struct v4l2_subdev *sd; struct v4l2_sliced_vbi_data vbi = { 0, 0, 0, 0 }; /* set to disabled */ /* Check if the adapter supports the needed features */ @@ -684,40 +723,42 @@ static int saa7127_probe(struct i2c_client *client, v4l_dbg(1, debug, client, "detecting saa7127 client on address 0x%x\n", client->addr << 1); + state = kzalloc(sizeof(struct saa7127_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &saa7127_ops); + /* First test register 0: Bits 5-7 are a version ID (should be 0), and bit 2 should also be 0. This is rather general, so the second test is more specific and looks at the 'ending point of burst in clock cycles' which is 0x1d after a reset and not expected to ever change. */ - if ((saa7127_read(client, 0) & 0xe4) != 0 || - (saa7127_read(client, 0x29) & 0x3f) != 0x1d) { - v4l_dbg(1, debug, client, "saa7127 not found\n"); + if ((saa7127_read(sd, 0) & 0xe4) != 0 || + (saa7127_read(sd, 0x29) & 0x3f) != 0x1d) { + v4l2_dbg(1, debug, sd, "saa7127 not found\n"); + kfree(state); return -ENODEV; } - state = kzalloc(sizeof(struct saa7127_state), GFP_KERNEL); - - if (state == NULL) - return -ENOMEM; - - i2c_set_clientdata(client, state); /* Configure Encoder */ - v4l_dbg(1, debug, client, "Configuring encoder\n"); - saa7127_write_inittab(client, saa7127_init_config_common); - saa7127_set_std(client, V4L2_STD_NTSC); - saa7127_set_output_type(client, SAA7127_OUTPUT_TYPE_BOTH); - saa7127_set_vps(client, &vbi); - saa7127_set_wss(client, &vbi); - saa7127_set_cc(client, &vbi); - saa7127_set_xds(client, &vbi); + v4l2_dbg(1, debug, sd, "Configuring encoder\n"); + saa7127_write_inittab(sd, saa7127_init_config_common); + saa7127_set_std(sd, V4L2_STD_NTSC); + saa7127_set_output_type(sd, SAA7127_OUTPUT_TYPE_BOTH); + saa7127_set_vps(sd, &vbi); + saa7127_set_wss(sd, &vbi); + saa7127_set_cc(sd, &vbi); + saa7127_set_xds(sd, &vbi); if (test_image == 1) /* The Encoder has an internal Colorbar generator */ /* This can be used for debugging */ - saa7127_set_input_type(client, SAA7127_INPUT_TYPE_TEST_IMAGE); + saa7127_set_input_type(sd, SAA7127_INPUT_TYPE_TEST_IMAGE); else - saa7127_set_input_type(client, SAA7127_INPUT_TYPE_NORMAL); - saa7127_set_video_enable(client, 1); + saa7127_set_input_type(sd, SAA7127_INPUT_TYPE_NORMAL); + saa7127_set_video_enable(sd, 1); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) if (id->driver_data) { /* Chip type is already known */ @@ -729,10 +770,10 @@ static int saa7127_probe(struct i2c_client *client, int read_result; /* Detect if it's an saa7129 */ - read_result = saa7127_read(client, SAA7129_REG_FADE_KEY_COL2); - saa7127_write(client, SAA7129_REG_FADE_KEY_COL2, 0xaa); - if (saa7127_read(client, SAA7129_REG_FADE_KEY_COL2) == 0xaa) { - saa7127_write(client, SAA7129_REG_FADE_KEY_COL2, + read_result = saa7127_read(sd, SAA7129_REG_FADE_KEY_COL2); + saa7127_write(sd, SAA7129_REG_FADE_KEY_COL2, 0xaa); + if (saa7127_read(sd, SAA7129_REG_FADE_KEY_COL2) == 0xaa) { + saa7127_write(sd, SAA7129_REG_FADE_KEY_COL2, read_result); state->ident = V4L2_IDENT_SAA7129; strlcpy(client->name, "saa7129", I2C_NAME_SIZE); @@ -742,10 +783,10 @@ static int saa7127_probe(struct i2c_client *client, } } - v4l_info(client, "%s found @ 0x%x (%s)\n", client->name, + v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name, client->addr << 1, client->adapter->name); if (state->ident == V4L2_IDENT_SAA7129) - saa7127_write_inittab(client, saa7129_init_config_extra); + saa7127_write_inittab(sd, saa7129_init_config_extra); return 0; } @@ -753,9 +794,12 @@ static int saa7127_probe(struct i2c_client *client, static int saa7127_remove(struct i2c_client *client) { + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); /* Turn off TV output */ - saa7127_set_video_enable(client, 0); - kfree(i2c_get_clientdata(client)); + saa7127_set_video_enable(sd, 0); + kfree(to_state(sd)); return 0; } @@ -776,7 +820,6 @@ MODULE_DEVICE_TABLE(i2c, saa7127_id); static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "saa7127", .driverid = I2C_DRIVERID_SAA7127, - .command = saa7127_command, .probe = saa7127_probe, .remove = saa7127_remove, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) -- cgit v1.2.3 From a9b4aaf1a121a8ed7f9fd39e8ae69595e252f669 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 29 Nov 2008 16:57:44 +0100 Subject: saa717x: convert to v4l2_subdev. From: Hans Verkuil Priority: normal Signed-off-by: Hans Verkuil --- linux/drivers/media/video/saa717x.c | 610 ++++++++++++++++++------------------ 1 file changed, 313 insertions(+), 297 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/saa717x.c b/linux/drivers/media/video/saa717x.c index b0751c195..2d667e66a 100644 --- a/linux/drivers/media/video/saa717x.c +++ b/linux/drivers/media/video/saa717x.c @@ -37,7 +37,7 @@ #include #include -#include +#include #include #include "compat.h" @@ -60,6 +60,7 @@ I2C_CLIENT_INSMOD; #endif struct saa717x_state { + struct v4l2_subdev sd; v4l2_std_id std; int input; int enable; @@ -81,6 +82,11 @@ struct saa717x_state { int audio_input; }; +static inline struct saa717x_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct saa717x_state, sd); +} + /* ----------------------------------------------------------------------- */ /* for audio mode */ @@ -94,8 +100,9 @@ struct saa717x_state { /* ----------------------------------------------------------------------- */ -static int saa717x_write(struct i2c_client *client, u32 reg, u32 value) +static int saa717x_write(struct v4l2_subdev *sd, u32 reg, u32 value) { + struct i2c_client *client = v4l2_get_subdevdata(sd); struct i2c_adapter *adap = client->adapter; int fw_addr = reg == 0x454 || (reg >= 0x464 && reg <= 0x478) || reg == 0x480 || reg == 0x488; unsigned char mm1[6]; @@ -115,20 +122,21 @@ static int saa717x_write(struct i2c_client *client, u32 reg, u32 value) } msg.len = fw_addr ? 5 : 3; /* Long Registers have *only* three bytes! */ msg.buf = mm1; - v4l_dbg(2, debug, client, "wrote: reg 0x%03x=%08x\n", reg, value); + v4l2_dbg(2, debug, sd, "wrote: reg 0x%03x=%08x\n", reg, value); return i2c_transfer(adap, &msg, 1) == 1; } -static void saa717x_write_regs(struct i2c_client *client, u32 *data) +static void saa717x_write_regs(struct v4l2_subdev *sd, u32 *data) { while (data[0] || data[1]) { - saa717x_write(client, data[0], data[1]); + saa717x_write(sd, data[0], data[1]); data += 2; } } -static u32 saa717x_read(struct i2c_client *client, u32 reg) +static u32 saa717x_read(struct v4l2_subdev *sd, u32 reg) { + struct i2c_client *client = v4l2_get_subdevdata(sd); struct i2c_adapter *adap = client->adapter; int fw_addr = (reg >= 0x404 && reg <= 0x4b8) || reg == 0x528; unsigned char mm1[2]; @@ -152,7 +160,7 @@ static u32 saa717x_read(struct i2c_client *client, u32 reg) else value = mm2[0] & 0xff; - v4l_dbg(2, debug, client, "read: reg 0x%03x=0x%08x\n", reg, value); + v4l2_dbg(2, debug, sd, "read: reg 0x%03x=0x%08x\n", reg, value); return value; } @@ -686,7 +694,7 @@ static u32 reg_set_audio_template[4][2] = /* Get detected audio flags (from saa7134 driver) */ -static void get_inf_dev_status(struct i2c_client *client, +static void get_inf_dev_status(struct v4l2_subdev *sd, int *dual_flag, int *stereo_flag) { u32 reg_data3; @@ -725,13 +733,13 @@ static void get_inf_dev_status(struct i2c_client *client, /* (demdec status: 0x528) */ /* read current status */ - reg_data3 = saa717x_read(client, 0x0528); + reg_data3 = saa717x_read(sd, 0x0528); - v4l_dbg(1, debug, client, "tvaudio thread status: 0x%x [%s%s%s]\n", + v4l2_dbg(1, debug, sd, "tvaudio thread status: 0x%x [%s%s%s]\n", reg_data3, stdres[reg_data3 & 0x1f], (reg_data3 & 0x000020) ? ",stereo" : "", (reg_data3 & 0x000040) ? ",dual" : ""); - v4l_dbg(1, debug, client, "detailed status: " + v4l2_dbg(1, debug, sd, "detailed status: " "%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s\n", (reg_data3 & 0x000080) ? " A2/EIAJ pilot tone " : "", (reg_data3 & 0x000100) ? " A2/EIAJ dual " : "", @@ -752,51 +760,51 @@ static void get_inf_dev_status(struct i2c_client *client, (reg_data3 & 0x100000) ? " init done " : ""); if (reg_data3 & 0x000220) { - v4l_dbg(1, debug, client, "ST!!!\n"); + v4l2_dbg(1, debug, sd, "ST!!!\n"); *stereo_flag = 1; } if (reg_data3 & 0x000140) { - v4l_dbg(1, debug, client, "DUAL!!!\n"); + v4l2_dbg(1, debug, sd, "DUAL!!!\n"); *dual_flag = 1; } } /* regs write to set audio mode */ -static void set_audio_mode(struct i2c_client *client, int audio_mode) +static void set_audio_mode(struct v4l2_subdev *sd, int audio_mode) { - v4l_dbg(1, debug, client, "writing registers to set audio mode by set %d\n", + v4l2_dbg(1, debug, sd, "writing registers to set audio mode by set %d\n", audio_mode); - saa717x_write(client, 0x46c, reg_set_audio_template[audio_mode][0]); - saa717x_write(client, 0x470, reg_set_audio_template[audio_mode][1]); + saa717x_write(sd, 0x46c, reg_set_audio_template[audio_mode][0]); + saa717x_write(sd, 0x470, reg_set_audio_template[audio_mode][1]); } /* write regs to video output level (bright,contrast,hue,sat) */ -static void set_video_output_level_regs(struct i2c_client *client, +static void set_video_output_level_regs(struct v4l2_subdev *sd, struct saa717x_state *decoder) { /* brightness ffh (bright) - 80h (ITU level) - 00h (dark) */ - saa717x_write(client, 0x10a, decoder->bright); + saa717x_write(sd, 0x10a, decoder->bright); /* contrast 7fh (max: 1.984) - 44h (ITU) - 40h (1.0) - 0h (luminance off) 40: i2c dump c0h (-1.0 inverse chrominance) 80h (-2.0 inverse chrominance) */ - saa717x_write(client, 0x10b, decoder->contrast); + saa717x_write(sd, 0x10b, decoder->contrast); /* saturation? 7fh(max)-40h(ITU)-0h(color off) c0h (-1.0 inverse chrominance) 80h (-2.0 inverse chrominance) */ - saa717x_write(client, 0x10c, decoder->sat); + saa717x_write(sd, 0x10c, decoder->sat); /* color hue (phase) control 7fh (+178.6) - 0h (0 normal) - 80h (-180.0) */ - saa717x_write(client, 0x10d, decoder->hue); + saa717x_write(sd, 0x10d, decoder->hue); } /* write regs to set audio volume, bass and treble */ -static int set_audio_regs(struct i2c_client *client, +static int set_audio_regs(struct v4l2_subdev *sd, struct saa717x_state *decoder) { u8 mute = 0xac; /* -84 dB */ @@ -804,8 +812,8 @@ static int set_audio_regs(struct i2c_client *client, unsigned int work_l, work_r; /* set SIF analog I/O select */ - saa717x_write(client, 0x0594, decoder->audio_input); - v4l_dbg(1, debug, client, "set audio input %d\n", + saa717x_write(sd, 0x0594, decoder->audio_input); + v4l2_dbg(1, debug, sd, "set audio input %d\n", decoder->audio_input); /* normalize ( 65535 to 0 -> 24 to -40 (not -84)) */ @@ -825,17 +833,17 @@ static int set_audio_regs(struct i2c_client *client, ((u8)decoder->audio_main_vol_r << 8); } - saa717x_write(client, 0x480, val); + saa717x_write(sd, 0x480, val); /* bass and treble; go to another function */ /* set bass and treble */ val = decoder->audio_main_bass | (decoder->audio_main_treble << 8); - saa717x_write(client, 0x488, val); + saa717x_write(sd, 0x488, val); return 0; } /********** scaling staff ***********/ -static void set_h_prescale(struct i2c_client *client, +static void set_h_prescale(struct v4l2_subdev *sd, int task, int prescale) { static const struct { @@ -868,107 +876,101 @@ static void set_h_prescale(struct i2c_client *client, return; /* horizonal prescaling */ - saa717x_write(client, 0x60 + task_shift, vals[i].xpsc); + saa717x_write(sd, 0x60 + task_shift, vals[i].xpsc); /* accumulation length */ - saa717x_write(client, 0x61 + task_shift, vals[i].xacl); + saa717x_write(sd, 0x61 + task_shift, vals[i].xacl); /* level control */ - saa717x_write(client, 0x62 + task_shift, + saa717x_write(sd, 0x62 + task_shift, (vals[i].xc2_1 << 3) | vals[i].xdcg); /*FIR prefilter control */ - saa717x_write(client, 0x63 + task_shift, + saa717x_write(sd, 0x63 + task_shift, (vals[i].vpfy << 2) | vals[i].vpfy); } /********** scaling staff ***********/ -static void set_v_scale(struct i2c_client *client, int task, int yscale) +static void set_v_scale(struct v4l2_subdev *sd, int task, int yscale) { int task_shift; task_shift = task * 0x40; /* Vertical scaling ratio (LOW) */ - saa717x_write(client, 0x70 + task_shift, yscale & 0xff); + saa717x_write(sd, 0x70 + task_shift, yscale & 0xff); /* Vertical scaling ratio (HI) */ - saa717x_write(client, 0x71 + task_shift, yscale >> 8); -} - -static int saa717x_set_audio_clock_freq(struct i2c_client *client, u32 freq) -{ - /* not yet implament, so saa717x_cfg_??hz_??_audio is not defined. */ - return 0; + saa717x_write(sd, 0x71 + task_shift, yscale >> 8); } -static int saa717x_set_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl) +static int saa717x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct saa717x_state *state = i2c_get_clientdata(client); + struct saa717x_state *state = to_state(sd); switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: if (ctrl->value < 0 || ctrl->value > 255) { - v4l_err(client, "invalid brightness setting %d\n", ctrl->value); + v4l2_err(sd, "invalid brightness setting %d\n", ctrl->value); return -ERANGE; } state->bright = ctrl->value; - v4l_dbg(1, debug, client, "bright:%d\n", state->bright); - saa717x_write(client, 0x10a, state->bright); + v4l2_dbg(1, debug, sd, "bright:%d\n", state->bright); + saa717x_write(sd, 0x10a, state->bright); break; case V4L2_CID_CONTRAST: if (ctrl->value < 0 || ctrl->value > 127) { - v4l_err(client, "invalid contrast setting %d\n", ctrl->value); + v4l2_err(sd, "invalid contrast setting %d\n", ctrl->value); return -ERANGE; } state->contrast = ctrl->value; - v4l_dbg(1, debug, client, "contrast:%d\n", state->contrast); - saa717x_write(client, 0x10b, state->contrast); + v4l2_dbg(1, debug, sd, "contrast:%d\n", state->contrast); + saa717x_write(sd, 0x10b, state->contrast); break; case V4L2_CID_SATURATION: if (ctrl->value < 0 || ctrl->value > 127) { - v4l_err(client, "invalid saturation setting %d\n", ctrl->value); + v4l2_err(sd, "invalid saturation setting %d\n", ctrl->value); return -ERANGE; } state->sat = ctrl->value; - v4l_dbg(1, debug, client, "sat:%d\n", state->sat); - saa717x_write(client, 0x10c, state->sat); + v4l2_dbg(1, debug, sd, "sat:%d\n", state->sat); + saa717x_write(sd, 0x10c, state->sat); break; case V4L2_CID_HUE: if (ctrl->value < -127 || ctrl->value > 127) { - v4l_err(client, "invalid hue setting %d\n", ctrl->value); + v4l2_err(sd, "invalid hue setting %d\n", ctrl->value); return -ERANGE; } state->hue = ctrl->value; - v4l_dbg(1, debug, client, "hue:%d\n", state->hue); - saa717x_write(client, 0x10d, state->hue); + v4l2_dbg(1, debug, sd, "hue:%d\n", state->hue); + saa717x_write(sd, 0x10d, state->hue); break; case V4L2_CID_AUDIO_MUTE: state->audio_main_mute = ctrl->value; - set_audio_regs(client, state); + set_audio_regs(sd, state); break; case V4L2_CID_AUDIO_VOLUME: state->audio_main_volume = ctrl->value; - set_audio_regs(client, state); + set_audio_regs(sd, state); break; case V4L2_CID_AUDIO_BALANCE: state->audio_main_balance = ctrl->value; - set_audio_regs(client, state); + set_audio_regs(sd, state); break; case V4L2_CID_AUDIO_TREBLE: state->audio_main_treble = ctrl->value; - set_audio_regs(client, state); + set_audio_regs(sd, state); break; case V4L2_CID_AUDIO_BASS: state->audio_main_bass = ctrl->value; - set_audio_regs(client, state); + set_audio_regs(sd, state); break; default: @@ -978,9 +980,9 @@ static int saa717x_set_v4lctrl(struct i2c_client *client, struct v4l2_control *c return 0; } -static int saa717x_get_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl) +static int saa717x_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct saa717x_state *state = i2c_get_clientdata(client); + struct saa717x_state *state = to_state(sd); switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: @@ -1109,13 +1111,15 @@ static struct v4l2_queryctrl saa717x_qctrl[] = { }, }; -static int saa717x_set_video_input(struct i2c_client *client, struct saa717x_state *decoder, int inp) +static int saa717x_s_video_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) { + struct saa717x_state *decoder = to_state(sd); + int inp = route->input; int is_tuner = inp & 0x80; /* tuner input flag */ inp &= 0x7f; - v4l_dbg(1, debug, client, "decoder set input (%d)\n", inp); + v4l2_dbg(1, debug, sd, "decoder set input (%d)\n", inp); /* inputs from 0-9 are available*/ /* saa717x have mode0-mode9 but mode5 is reserved. */ if (inp < 0 || inp > 9 || inp == 5) @@ -1125,222 +1129,197 @@ static int saa717x_set_video_input(struct i2c_client *client, struct saa717x_sta int input_line = inp; decoder->input = input_line; - v4l_dbg(1, debug, client, "now setting %s input %d\n", + v4l2_dbg(1, debug, sd, "now setting %s input %d\n", input_line >= 6 ? "S-Video" : "Composite", input_line); /* select mode */ - saa717x_write(client, 0x102, - (saa717x_read(client, 0x102) & 0xf0) | + saa717x_write(sd, 0x102, + (saa717x_read(sd, 0x102) & 0xf0) | input_line); /* bypass chrominance trap for modes 6..9 */ - saa717x_write(client, 0x109, - (saa717x_read(client, 0x109) & 0x7f) | + saa717x_write(sd, 0x109, + (saa717x_read(sd, 0x109) & 0x7f) | (input_line < 6 ? 0x0 : 0x80)); /* change audio_mode */ if (is_tuner) { /* tuner */ - set_audio_mode(client, decoder->tuner_audio_mode); + set_audio_mode(sd, decoder->tuner_audio_mode); } else { /* Force to STEREO mode if Composite or * S-Video were chosen */ - set_audio_mode(client, TUNER_AUDIO_STEREO); + set_audio_mode(sd, TUNER_AUDIO_STEREO); } /* change initialize procedure (Composite/S-Video) */ if (is_tuner) - saa717x_write_regs(client, reg_init_tuner_input); + saa717x_write_regs(sd, reg_init_tuner_input); else if (input_line >= 6) - saa717x_write_regs(client, reg_init_svideo_input); + saa717x_write_regs(sd, reg_init_svideo_input); else - saa717x_write_regs(client, reg_init_composite_input); + saa717x_write_regs(sd, reg_init_composite_input); } return 0; } -static int saa717x_command(struct i2c_client *client, unsigned cmd, void *arg) +static int saa717x_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) { - struct saa717x_state *decoder = i2c_get_clientdata(client); - - v4l_dbg(1, debug, client, "IOCTL: %08x\n", cmd); + int i; - switch (cmd) { - case VIDIOC_INT_AUDIO_CLOCK_FREQ: - return saa717x_set_audio_clock_freq(client, *(u32 *)arg); + for (i = 0; i < ARRAY_SIZE(saa717x_qctrl); i++) + if (qc->id && qc->id == saa717x_qctrl[i].id) { + memcpy(qc, &saa717x_qctrl[i], sizeof(*qc)); + return 0; + } + return -EINVAL; +} - case VIDIOC_G_CTRL: - return saa717x_get_v4lctrl(client, (struct v4l2_control *)arg); +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int saa717x_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); - case VIDIOC_S_CTRL: - return saa717x_set_v4lctrl(client, (struct v4l2_control *)arg); + if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reg->val = saa717x_read(sd, reg->reg); + return 0; +} - case VIDIOC_QUERYCTRL: { - struct v4l2_queryctrl *qc = arg; - int i; +static int saa717x_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u16 addr = reg->reg & 0xffff; + u8 val = reg->val & 0xff; - for (i = 0; i < ARRAY_SIZE(saa717x_qctrl); i++) - if (qc->id && qc->id == saa717x_qctrl[i].id) { - memcpy(qc, &saa717x_qctrl[i], sizeof(*qc)); - return 0; - } + if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip)) return -EINVAL; - } - -#ifdef CONFIG_VIDEO_ADV_DEBUG - case VIDIOC_DBG_G_REGISTER: { - struct v4l2_register *reg = arg; - - if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - reg->val = saa717x_read(client, reg->reg); - break; - } + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + saa717x_write(sd, addr, val); + return 0; +} +#endif - case VIDIOC_DBG_S_REGISTER: { - struct v4l2_register *reg = arg; - u16 addr = reg->reg & 0xffff; - u8 val = reg->val & 0xff; +static int saa717x_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) +{ + struct v4l2_pix_format *pix; + int prescale, h_scale, v_scale; - if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - saa717x_write(client, addr, val); - break; - } -#endif + pix = &fmt->fmt.pix; + v4l2_dbg(1, debug, sd, "decoder set size\n"); - case VIDIOC_S_FMT: { - struct v4l2_format *fmt = (struct v4l2_format *)arg; - struct v4l2_pix_format *pix; - int prescale, h_scale, v_scale; - - pix = &fmt->fmt.pix; - v4l_dbg(1, debug, client, "decoder set size\n"); - - /* FIXME need better bounds checking here */ - if (pix->width < 1 || pix->width > 1440) - return -EINVAL; - if (pix->height < 1 || pix->height > 960) - return -EINVAL; - - /* scaling setting */ - /* NTSC and interlace only */ - prescale = SAA717X_NTSC_WIDTH / pix->width; - if (prescale == 0) - prescale = 1; - h_scale = 1024 * SAA717X_NTSC_WIDTH / prescale / pix->width; - /* interlace */ - v_scale = 512 * 2 * SAA717X_NTSC_HEIGHT / pix->height; - - /* Horizontal prescaling etc */ - set_h_prescale(client, 0, prescale); - set_h_prescale(client, 1, prescale); - - /* Horizontal scaling increment */ - /* TASK A */ - saa717x_write(client, 0x6C, (u8)(h_scale & 0xFF)); - saa717x_write(client, 0x6D, (u8)((h_scale >> 8) & 0xFF)); - /* TASK B */ - saa717x_write(client, 0xAC, (u8)(h_scale & 0xFF)); - saa717x_write(client, 0xAD, (u8)((h_scale >> 8) & 0xFF)); - - /* Vertical prescaling etc */ - set_v_scale(client, 0, v_scale); - set_v_scale(client, 1, v_scale); - - /* set video output size */ - /* video number of pixels at output */ - /* TASK A */ - saa717x_write(client, 0x5C, (u8)(pix->width & 0xFF)); - saa717x_write(client, 0x5D, (u8)((pix->width >> 8) & 0xFF)); - /* TASK B */ - saa717x_write(client, 0x9C, (u8)(pix->width & 0xFF)); - saa717x_write(client, 0x9D, (u8)((pix->width >> 8) & 0xFF)); - - /* video number of lines at output */ - /* TASK A */ - saa717x_write(client, 0x5E, (u8)(pix->height & 0xFF)); - saa717x_write(client, 0x5F, (u8)((pix->height >> 8) & 0xFF)); - /* TASK B */ - saa717x_write(client, 0x9E, (u8)(pix->height & 0xFF)); - saa717x_write(client, 0x9F, (u8)((pix->height >> 8) & 0xFF)); - break; - } + /* FIXME need better bounds checking here */ + if (pix->width < 1 || pix->width > 1440) + return -EINVAL; + if (pix->height < 1 || pix->height > 960) + return -EINVAL; - case AUDC_SET_RADIO: - decoder->radio = 1; - break; + /* scaling setting */ + /* NTSC and interlace only */ + prescale = SAA717X_NTSC_WIDTH / pix->width; + if (prescale == 0) + prescale = 1; + h_scale = 1024 * SAA717X_NTSC_WIDTH / prescale / pix->width; + /* interlace */ + v_scale = 512 * 2 * SAA717X_NTSC_HEIGHT / pix->height; + + /* Horizontal prescaling etc */ + set_h_prescale(sd, 0, prescale); + set_h_prescale(sd, 1, prescale); + + /* Horizontal scaling increment */ + /* TASK A */ + saa717x_write(sd, 0x6C, (u8)(h_scale & 0xFF)); + saa717x_write(sd, 0x6D, (u8)((h_scale >> 8) & 0xFF)); + /* TASK B */ + saa717x_write(sd, 0xAC, (u8)(h_scale & 0xFF)); + saa717x_write(sd, 0xAD, (u8)((h_scale >> 8) & 0xFF)); + + /* Vertical prescaling etc */ + set_v_scale(sd, 0, v_scale); + set_v_scale(sd, 1, v_scale); + + /* set video output size */ + /* video number of pixels at output */ + /* TASK A */ + saa717x_write(sd, 0x5C, (u8)(pix->width & 0xFF)); + saa717x_write(sd, 0x5D, (u8)((pix->width >> 8) & 0xFF)); + /* TASK B */ + saa717x_write(sd, 0x9C, (u8)(pix->width & 0xFF)); + saa717x_write(sd, 0x9D, (u8)((pix->width >> 8) & 0xFF)); + + /* video number of lines at output */ + /* TASK A */ + saa717x_write(sd, 0x5E, (u8)(pix->height & 0xFF)); + saa717x_write(sd, 0x5F, (u8)((pix->height >> 8) & 0xFF)); + /* TASK B */ + saa717x_write(sd, 0x9E, (u8)(pix->height & 0xFF)); + saa717x_write(sd, 0x9F, (u8)((pix->height >> 8) & 0xFF)); + return 0; +} - case VIDIOC_S_STD: { - v4l2_std_id std = *(v4l2_std_id *) arg; +static int saa717x_s_radio(struct v4l2_subdev *sd) +{ + struct saa717x_state *decoder = to_state(sd); - v4l_dbg(1, debug, client, "decoder set norm "); - v4l_dbg(1, debug, client, "(not yet implementd)\n"); + decoder->radio = 1; + return 0; +} - decoder->radio = 0; - decoder->std = std; - break; - } +static int saa717x_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct saa717x_state *decoder = to_state(sd); - case VIDIOC_INT_G_AUDIO_ROUTING: { - struct v4l2_routing *route = arg; + v4l2_dbg(1, debug, sd, "decoder set norm "); + v4l2_dbg(1, debug, sd, "(not yet implementd)\n"); - route->input = decoder->audio_input; - route->output = 0; - break; - } + decoder->radio = 0; + decoder->std = std; + return 0; +} - case VIDIOC_INT_S_AUDIO_ROUTING: { - struct v4l2_routing *route = arg; +static int saa717x_s_audio_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +{ + struct saa717x_state *decoder = to_state(sd); - if (route->input < 3) { /* FIXME! --tadachi */ - decoder->audio_input = route->input; - v4l_dbg(1, debug, client, + if (route->input < 3) { /* FIXME! --tadachi */ + decoder->audio_input = route->input; + v4l2_dbg(1, debug, sd, "set decoder audio input to %d\n", decoder->audio_input); - set_audio_regs(client, decoder); - break; - } - return -ERANGE; - } - - case VIDIOC_INT_S_VIDEO_ROUTING: { - struct v4l2_routing *route = arg; - int inp = route->input; - - return saa717x_set_video_input(client, decoder, inp); + set_audio_regs(sd, decoder); + return 0; } + return -ERANGE; +} - case VIDIOC_STREAMON: { - v4l_dbg(1, debug, client, "decoder enable output\n"); - decoder->enable = 1; - saa717x_write(client, 0x193, 0xa6); - break; - } +static int saa717x_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct saa717x_state *decoder = to_state(sd); - case VIDIOC_STREAMOFF: { - v4l_dbg(1, debug, client, "decoder disable output\n"); - decoder->enable = 0; - saa717x_write(client, 0x193, 0x26); /* right? FIXME!--tadachi */ - break; - } + v4l2_dbg(1, debug, sd, "decoder %s output\n", + enable ? "enable" : "disable"); + decoder->enable = enable; + saa717x_write(sd, 0x193, enable ? 0xa6 : 0x26); + return 0; +} - /* change audio mode */ - case VIDIOC_S_TUNER: { - struct v4l2_tuner *vt = (struct v4l2_tuner *)arg; - int audio_mode; - char *mes[4] = { - "MONO", "STEREO", "LANG1", "LANG2/SAP" - }; +/* change audio mode */ +static int saa717x_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct saa717x_state *decoder = to_state(sd); + int audio_mode; + char *mes[4] = { + "MONO", "STEREO", "LANG1", "LANG2/SAP" + }; - audio_mode = V4L2_TUNER_MODE_STEREO; + audio_mode = V4L2_TUNER_MODE_STEREO; - switch (vt->audmode) { + switch (vt->audmode) { case V4L2_TUNER_MODE_MONO: audio_mode = TUNER_AUDIO_MONO; break; @@ -1353,70 +1332,101 @@ static int saa717x_command(struct i2c_client *client, unsigned cmd, void *arg) case V4L2_TUNER_MODE_LANG1: audio_mode = TUNER_AUDIO_LANG1; break; - } - - v4l_dbg(1, debug, client, "change audio mode to %s\n", - mes[audio_mode]); - decoder->tuner_audio_mode = audio_mode; - /* The registers are not changed here. */ - /* See DECODER_ENABLE_OUTPUT section. */ - set_audio_mode(client, decoder->tuner_audio_mode); - break; } - case VIDIOC_G_TUNER: { - struct v4l2_tuner *vt = (struct v4l2_tuner *)arg; - int dual_f, stereo_f; + v4l2_dbg(1, debug, sd, "change audio mode to %s\n", + mes[audio_mode]); + decoder->tuner_audio_mode = audio_mode; + /* The registers are not changed here. */ + /* See DECODER_ENABLE_OUTPUT section. */ + set_audio_mode(sd, decoder->tuner_audio_mode); + return 0; +} - if (decoder->radio) - break; - get_inf_dev_status(client, &dual_f, &stereo_f); +static int saa717x_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct saa717x_state *decoder = to_state(sd); + int dual_f, stereo_f; - v4l_dbg(1, debug, client, "DETECT==st:%d dual:%d\n", - stereo_f, dual_f); + if (decoder->radio) + return 0; + get_inf_dev_status(sd, &dual_f, &stereo_f); - /* mono */ - if ((dual_f == 0) && (stereo_f == 0)) { - vt->rxsubchans = V4L2_TUNER_SUB_MONO; - v4l_dbg(1, debug, client, "DETECT==MONO\n"); - } + v4l2_dbg(1, debug, sd, "DETECT==st:%d dual:%d\n", + stereo_f, dual_f); - /* stereo */ - if (stereo_f == 1) { - if (vt->audmode == V4L2_TUNER_MODE_STEREO || - vt->audmode == V4L2_TUNER_MODE_LANG1) { - vt->rxsubchans = V4L2_TUNER_SUB_STEREO; - v4l_dbg(1, debug, client, "DETECT==ST(ST)\n"); - } else { - vt->rxsubchans = V4L2_TUNER_SUB_MONO; - v4l_dbg(1, debug, client, "DETECT==ST(MONO)\n"); - } - } + /* mono */ + if ((dual_f == 0) && (stereo_f == 0)) { + vt->rxsubchans = V4L2_TUNER_SUB_MONO; + v4l2_dbg(1, debug, sd, "DETECT==MONO\n"); + } - /* dual */ - if (dual_f == 1) { - if (vt->audmode == V4L2_TUNER_MODE_LANG2) { - vt->rxsubchans = V4L2_TUNER_SUB_LANG2 | V4L2_TUNER_SUB_MONO; - v4l_dbg(1, debug, client, "DETECT==DUAL1\n"); - } else { - vt->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_MONO; - v4l_dbg(1, debug, client, "DETECT==DUAL2\n"); - } + /* stereo */ + if (stereo_f == 1) { + if (vt->audmode == V4L2_TUNER_MODE_STEREO || + vt->audmode == V4L2_TUNER_MODE_LANG1) { + vt->rxsubchans = V4L2_TUNER_SUB_STEREO; + v4l2_dbg(1, debug, sd, "DETECT==ST(ST)\n"); + } else { + vt->rxsubchans = V4L2_TUNER_SUB_MONO; + v4l2_dbg(1, debug, sd, "DETECT==ST(MONO)\n"); } - break; } - case VIDIOC_LOG_STATUS: - /* not yet implemented */ - break; - - default: - return -EINVAL; + /* dual */ + if (dual_f == 1) { + if (vt->audmode == V4L2_TUNER_MODE_LANG2) { + vt->rxsubchans = V4L2_TUNER_SUB_LANG2 | V4L2_TUNER_SUB_MONO; + v4l2_dbg(1, debug, sd, "DETECT==DUAL1\n"); + } else { + vt->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_MONO; + v4l2_dbg(1, debug, sd, "DETECT==DUAL2\n"); + } } - return 0; } +static int saa717x_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops saa717x_core_ops = { +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = saa717x_g_register, + .s_register = saa717x_s_register, +#endif + .queryctrl = saa717x_queryctrl, + .g_ctrl = saa717x_g_ctrl, + .s_ctrl = saa717x_s_ctrl, +}; + +static const struct v4l2_subdev_tuner_ops saa717x_tuner_ops = { + .g_tuner = saa717x_g_tuner, + .s_tuner = saa717x_s_tuner, + .s_std = saa717x_s_std, + .s_radio = saa717x_s_radio, +}; + +static const struct v4l2_subdev_video_ops saa717x_video_ops = { + .s_routing = saa717x_s_video_routing, + .s_fmt = saa717x_s_fmt, + .s_stream = saa717x_s_stream, +}; + +static const struct v4l2_subdev_audio_ops saa717x_audio_ops = { + .s_routing = saa717x_s_audio_routing, +}; + +static const struct v4l2_subdev_ops saa717x_ops = { + .core = &saa717x_core_ops, + .tuner = &saa717x_tuner_ops, + .audio = &saa717x_audio_ops, + .video = &saa717x_video_ops, +}; + /* ----------------------------------------------------------------------- */ @@ -1427,6 +1437,7 @@ static int saa717x_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct saa717x_state *decoder; + struct v4l2_subdev *sd; u8 id = 0; char *p = ""; @@ -1434,16 +1445,24 @@ static int saa717x_probe(struct i2c_client *client, if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -EIO; + decoder = kzalloc(sizeof(struct saa717x_state), GFP_KERNEL); + if (decoder == NULL) + return -ENOMEM; + + sd = &decoder->sd; + v4l2_i2c_subdev_init(sd, client, &saa717x_ops); + #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) snprintf(client->name, sizeof(client->name) - 1, "saa717x"); #endif - if (saa717x_write(client, 0x5a4, 0xfe) && - saa717x_write(client, 0x5a5, 0x0f) && - saa717x_write(client, 0x5a6, 0x00) && - saa717x_write(client, 0x5a7, 0x01)) - id = saa717x_read(client, 0x5a0); + if (saa717x_write(sd, 0x5a4, 0xfe) && + saa717x_write(sd, 0x5a5, 0x0f) && + saa717x_write(sd, 0x5a6, 0x00) && + saa717x_write(sd, 0x5a7, 0x01)) + id = saa717x_read(sd, 0x5a0); if (id != 0xc2 && id != 0x32 && id != 0xf2 && id != 0x6c) { - v4l_dbg(1, debug, client, "saa717x not found (id=%02x)\n", id); + v4l2_dbg(1, debug, sd, "saa717x not found (id=%02x)\n", id); + kfree(decoder); return -ENODEV; } if (id == 0xc2) @@ -1454,14 +1473,8 @@ static int saa717x_probe(struct i2c_client *client, p = "saa7174HL"; else p = "saa7171"; - v4l_info(client, "%s found @ 0x%x (%s)\n", p, + v4l2_info(sd, "%s found @ 0x%x (%s)\n", p, client->addr << 1, client->adapter->name); - - decoder = kzalloc(sizeof(struct saa717x_state), GFP_KERNEL); - i2c_set_clientdata(client, decoder); - - if (decoder == NULL) - return -ENOMEM; decoder->std = V4L2_STD_NTSC; decoder->input = -1; decoder->enable = 1; @@ -1490,15 +1503,15 @@ static int saa717x_probe(struct i2c_client *client, decoder->audio_main_volume = (decoder->audio_main_vol_r + 41) * 65535 / (24 - (-40)); - v4l_dbg(1, debug, client, "writing init values\n"); + v4l2_dbg(1, debug, sd, "writing init values\n"); /* FIXME!! */ - saa717x_write_regs(client, reg_init_initialize); - set_video_output_level_regs(client, decoder); + saa717x_write_regs(sd, reg_init_initialize); + set_video_output_level_regs(sd, decoder); /* set bass,treble to 0db 20041101 K.Ohta */ decoder->audio_main_bass = 0; decoder->audio_main_treble = 0; - set_audio_regs(client, decoder); + set_audio_regs(sd, decoder); set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(2*HZ); @@ -1507,7 +1520,10 @@ static int saa717x_probe(struct i2c_client *client, static int saa717x_remove(struct i2c_client *client) { - kfree(i2c_get_clientdata(client)); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); return 0; } -- cgit v1.2.3 From 912b4df6a7f733ecd3a6b3d58d0661a3e9ec2474 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 24 Nov 2008 22:21:40 +0100 Subject: tuner: convert to v4l2_subdev. From: Hans Verkuil Priority: normal Signed-off-by: Hans Verkuil --- linux/drivers/media/video/tuner-core.c | 391 +++++++++++++++++++-------------- 1 file changed, 229 insertions(+), 162 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/tuner-core.c b/linux/drivers/media/video/tuner-core.c index 5dd4bd660..e649f2eb7 100644 --- a/linux/drivers/media/video/tuner-core.c +++ b/linux/drivers/media/video/tuner-core.c @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include #include #include "mt20xx.h" @@ -79,6 +79,7 @@ struct tuner { /* device */ struct dvb_frontend fe; struct i2c_client *i2c; + struct v4l2_subdev sd; struct list_head list; unsigned int using_v4l2:1; @@ -96,6 +97,11 @@ struct tuner { const char *name; }; +static inline struct tuner *to_tuner(struct v4l2_subdev *sd) +{ + return container_of(sd, struct tuner, sd); +} + /* standard i2c insmod options */ static unsigned short normal_i2c[] = { #if defined(CONFIG_MEDIA_TUNER_TEA5761) || (defined(CONFIG_MEDIA_TUNER_TEA5761_MODULE) && defined(MODULE)) @@ -214,7 +220,7 @@ static int fe_set_config(struct dvb_frontend *fe, void *priv_cfg) static void tuner_status(struct dvb_frontend *fe); -static struct analog_demod_ops tuner_core_ops = { +static struct analog_demod_ops tuner_analog_ops = { .set_params = fe_set_params, .standby = fe_standby, .has_signal = fe_has_signal, @@ -225,7 +231,7 @@ static struct analog_demod_ops tuner_core_ops = { /* Set tuner frequency, freq in Units of 62.5kHz = 1/16MHz */ static void set_tv_freq(struct i2c_client *c, unsigned int freq) { - struct tuner *t = i2c_get_clientdata(c); + struct tuner *t = to_tuner(i2c_get_clientdata(c)); struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; struct analog_parameters params = { @@ -260,7 +266,7 @@ static void set_tv_freq(struct i2c_client *c, unsigned int freq) static void set_radio_freq(struct i2c_client *c, unsigned int freq) { - struct tuner *t = i2c_get_clientdata(c); + struct tuner *t = to_tuner(i2c_get_clientdata(c)); struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; struct analog_parameters params = { @@ -295,7 +301,7 @@ static void set_radio_freq(struct i2c_client *c, unsigned int freq) static void set_freq(struct i2c_client *c, unsigned long freq) { - struct tuner *t = i2c_get_clientdata(c); + struct tuner *t = to_tuner(i2c_get_clientdata(c)); switch (t->mode) { case V4L2_TUNER_RADIO: @@ -348,7 +354,7 @@ static void set_type(struct i2c_client *c, unsigned int type, unsigned int new_mode_mask, unsigned int new_config, int (*tuner_callback) (void *dev, int component, int cmd, int arg)) { - struct tuner *t = i2c_get_clientdata(c); + struct tuner *t = to_tuner(i2c_get_clientdata(c)); struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops; struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; unsigned char buffer[4]; @@ -471,7 +477,7 @@ static void set_type(struct i2c_client *c, unsigned int type, t->name = fe_tuner_ops->info.name; t->fe.analog_demod_priv = t; - memcpy(analog_ops, &tuner_core_ops, + memcpy(analog_ops, &tuner_analog_ops, sizeof(struct analog_demod_ops)); } else { @@ -516,7 +522,7 @@ attach_failed: static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup) { - struct tuner *t = i2c_get_clientdata(c); + struct tuner *t = to_tuner(i2c_get_clientdata(c)); if ( (t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) && (t->mode_mask & tun_setup->mode_mask))) || @@ -758,43 +764,56 @@ static inline int check_v4l2(struct tuner *t) return 0; } -static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) +static int tuner_s_type_addr(struct v4l2_subdev *sd, struct tuner_setup *type) { - struct tuner *t = i2c_get_clientdata(client); - struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops; + struct tuner *t = to_tuner(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + tuner_dbg("Calling set_type_addr for type=%d, addr=0x%02x, mode=0x%02x, config=0x%02x\n", + type->type, + type->addr, + type->mode_mask, + type->config); + + set_addr(client, type); + return 0; +} + +static int tuner_s_radio(struct v4l2_subdev *sd) +{ + struct tuner *t = to_tuner(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO") + == -EINVAL) + return 0; + if (t->radio_freq) + set_freq(client, t->radio_freq); + return 0; +} + +static int tuner_s_standby(struct v4l2_subdev *sd, u32 standby) +{ + struct tuner *t = to_tuner(sd); struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; - if (tuner_debug > 1) { - v4l_i2c_print_ioctl(client,cmd); - printk("\n"); - } + if (check_mode(t, "TUNER_SET_STANDBY") == -EINVAL) + return 0; + t->mode = T_STANDBY; + if (analog_ops->standby) + analog_ops->standby(&t->fe); + return 0; +} - switch (cmd) { - /* --- configuration --- */ - case TUNER_SET_TYPE_ADDR: - tuner_dbg ("Calling set_type_addr for type=%d, addr=0x%02x, mode=0x%02x, config=0x%02x\n", - ((struct tuner_setup *)arg)->type, - ((struct tuner_setup *)arg)->addr, - ((struct tuner_setup *)arg)->mode_mask, - ((struct tuner_setup *)arg)->config); - - set_addr(client, (struct tuner_setup *)arg); - break; - case AUDC_SET_RADIO: - if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO") - == -EINVAL) - return 0; - if (t->radio_freq) - set_freq(client, t->radio_freq); - break; - case TUNER_SET_STANDBY: - if (check_mode(t, "TUNER_SET_STANDBY") == -EINVAL) - return 0; - t->mode = T_STANDBY; - if (analog_ops->standby) - analog_ops->standby(&t->fe); - break; #ifdef CONFIG_VIDEO_ALLOW_V4L1 +static int tuner_ioctl(struct v4l2_subdev *sd, int cmd, void *arg) +{ + struct tuner *t = to_tuner(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; + struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops; + + switch (cmd) { case VIDIOCSAUDIO: if (check_mode(t, "VIDIOCSAUDIO") == -EINVAL) return 0; @@ -907,149 +926,172 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) } return 0; } + } + return -ENOIOCTLCMD; +} #endif - case TUNER_SET_CONFIG: - { - struct v4l2_priv_tun_config *cfg = arg; - if (t->type != cfg->tuner) - break; +static int tuner_s_config(struct v4l2_subdev *sd, const struct v4l2_priv_tun_config *cfg) +{ + struct tuner *t = to_tuner(sd); + struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; - if (analog_ops->set_config) { - analog_ops->set_config(&t->fe, cfg->priv); - break; - } + if (t->type != cfg->tuner) + return 0; - tuner_dbg("Tuner frontend module has no way to set config\n"); - break; + if (analog_ops->set_config) { + analog_ops->set_config(&t->fe, cfg->priv); + return 0; } - /* --- v4l ioctls --- */ - /* take care: bttv does userspace copying, we'll get a - kernel pointer here... */ - case VIDIOC_S_STD: - { - v4l2_std_id *id = arg; - if (set_mode (client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD") - == -EINVAL) - return 0; + tuner_dbg("Tuner frontend module has no way to set config\n"); + return 0; +} - switch_v4l2(); +/* --- v4l ioctls --- */ +/* take care: bttv does userspace copying, we'll get a + kernel pointer here... */ +static int tuner_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct tuner *t = to_tuner(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - t->std = *id; - tuner_fixup_std(t); - if (t->tv_freq) - set_freq(client, t->tv_freq); - break; - } - case VIDIOC_S_FREQUENCY: - { - struct v4l2_frequency *f = arg; + if (set_mode(client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD") + == -EINVAL) + return 0; - if (set_mode (client, t, f->type, "VIDIOC_S_FREQUENCY") - == -EINVAL) - return 0; - switch_v4l2(); - set_freq(client,f->frequency); + switch_v4l2(); - break; - } - case VIDIOC_G_FREQUENCY: - { - struct v4l2_frequency *f = arg; + t->std = std; + tuner_fixup_std(t); + if (t->tv_freq) + set_freq(client, t->tv_freq); + return 0; +} - if (check_mode(t, "VIDIOC_G_FREQUENCY") == -EINVAL) - return 0; - switch_v4l2(); - f->type = t->mode; - if (fe_tuner_ops->get_frequency) { - u32 abs_freq; - - fe_tuner_ops->get_frequency(&t->fe, &abs_freq); - f->frequency = (V4L2_TUNER_RADIO == t->mode) ? - (abs_freq * 2 + 125/2) / 125 : - (abs_freq + 62500/2) / 62500; - break; - } - f->frequency = (V4L2_TUNER_RADIO == t->mode) ? - t->radio_freq : t->tv_freq; - break; - } - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *tuner = arg; +static int tuner_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f) +{ + struct tuner *t = to_tuner(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - if (check_mode(t, "VIDIOC_G_TUNER") == -EINVAL) - return 0; - switch_v4l2(); - - tuner->type = t->mode; - if (analog_ops->get_afc) - tuner->afc = analog_ops->get_afc(&t->fe); - if (t->mode == V4L2_TUNER_ANALOG_TV) - tuner->capability |= V4L2_TUNER_CAP_NORM; - if (t->mode != V4L2_TUNER_RADIO) { - tuner->rangelow = tv_range[0] * 16; - tuner->rangehigh = tv_range[1] * 16; - break; - } + if (set_mode(client, t, f->type, "VIDIOC_S_FREQUENCY") + == -EINVAL) + return 0; + switch_v4l2(); + set_freq(client, f->frequency); - /* radio mode */ - tuner->rxsubchans = - V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; - if (fe_tuner_ops->get_status) { - u32 tuner_status; - - fe_tuner_ops->get_status(&t->fe, &tuner_status); - tuner->rxsubchans = - (tuner_status & TUNER_STATUS_STEREO) ? - V4L2_TUNER_SUB_STEREO : - V4L2_TUNER_SUB_MONO; - } else { - if (analog_ops->is_stereo) { - tuner->rxsubchans = - analog_ops->is_stereo(&t->fe) ? - V4L2_TUNER_SUB_STEREO : - V4L2_TUNER_SUB_MONO; - } - } - if (analog_ops->has_signal) - tuner->signal = analog_ops->has_signal(&t->fe); - tuner->capability |= - V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; - tuner->audmode = t->audmode; - tuner->rangelow = radio_range[0] * 16000; - tuner->rangehigh = radio_range[1] * 16000; - break; - } - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *tuner = arg; + return 0; +} - if (check_mode(t, "VIDIOC_S_TUNER") == -EINVAL) - return 0; +static int tuner_g_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f) +{ + struct tuner *t = to_tuner(sd); + struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops; - switch_v4l2(); + if (check_mode(t, "VIDIOC_G_FREQUENCY") == -EINVAL) + return 0; + switch_v4l2(); + f->type = t->mode; + if (fe_tuner_ops->get_frequency) { + u32 abs_freq; + + fe_tuner_ops->get_frequency(&t->fe, &abs_freq); + f->frequency = (V4L2_TUNER_RADIO == t->mode) ? + (abs_freq * 2 + 125/2) / 125 : + (abs_freq + 62500/2) / 62500; + return 0; + } + f->frequency = (V4L2_TUNER_RADIO == t->mode) ? + t->radio_freq : t->tv_freq; + return 0; +} - /* do nothing unless we're a radio tuner */ - if (t->mode != V4L2_TUNER_RADIO) - break; - t->audmode = tuner->audmode; - set_radio_freq(client, t->radio_freq); - break; +static int tuner_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct tuner *t = to_tuner(sd); + struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; + struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops; + + if (check_mode(t, "VIDIOC_G_TUNER") == -EINVAL) + return 0; + switch_v4l2(); + + vt->type = t->mode; + if (analog_ops->get_afc) + vt->afc = analog_ops->get_afc(&t->fe); + if (t->mode == V4L2_TUNER_ANALOG_TV) + vt->capability |= V4L2_TUNER_CAP_NORM; + if (t->mode != V4L2_TUNER_RADIO) { + vt->rangelow = tv_range[0] * 16; + vt->rangehigh = tv_range[1] * 16; + return 0; + } + + /* radio mode */ + vt->rxsubchans = + V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; + if (fe_tuner_ops->get_status) { + u32 tuner_status; + + fe_tuner_ops->get_status(&t->fe, &tuner_status); + vt->rxsubchans = + (tuner_status & TUNER_STATUS_STEREO) ? + V4L2_TUNER_SUB_STEREO : + V4L2_TUNER_SUB_MONO; + } else { + if (analog_ops->is_stereo) { + vt->rxsubchans = + analog_ops->is_stereo(&t->fe) ? + V4L2_TUNER_SUB_STEREO : + V4L2_TUNER_SUB_MONO; } - case VIDIOC_LOG_STATUS: - if (analog_ops->tuner_status) - analog_ops->tuner_status(&t->fe); - break; } + if (analog_ops->has_signal) + vt->signal = analog_ops->has_signal(&t->fe); + vt->capability |= + V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; + vt->audmode = t->audmode; + vt->rangelow = radio_range[0] * 16000; + vt->rangehigh = radio_range[1] * 16000; + return 0; +} + +static int tuner_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct tuner *t = to_tuner(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (check_mode(t, "VIDIOC_S_TUNER") == -EINVAL) + return 0; + + switch_v4l2(); + + /* do nothing unless we're a radio tuner */ + if (t->mode != V4L2_TUNER_RADIO) + return 0; + t->audmode = vt->audmode; + set_radio_freq(client, t->radio_freq); + return 0; +} + +static int tuner_log_status(struct v4l2_subdev *sd) +{ + struct tuner *t = to_tuner(sd); + struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; + if (analog_ops->tuner_status) + analog_ops->tuner_status(&t->fe); return 0; } +static int tuner_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + static int tuner_suspend(struct i2c_client *c, pm_message_t state) { - struct tuner *t = i2c_get_clientdata(c); + struct tuner *t = to_tuner(i2c_get_clientdata(c)); tuner_dbg("suspend\n"); /* FIXME: power down ??? */ @@ -1058,7 +1100,7 @@ static int tuner_suspend(struct i2c_client *c, pm_message_t state) static int tuner_resume(struct i2c_client *c) { - struct tuner *t = i2c_get_clientdata(c); + struct tuner *t = to_tuner(i2c_get_clientdata(c)); tuner_dbg("resume\n"); if (V4L2_TUNER_RADIO == t->mode) { @@ -1071,6 +1113,30 @@ static int tuner_resume(struct i2c_client *c) return 0; } +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops tuner_core_ops = { + .log_status = tuner_log_status, + .s_standby = tuner_s_standby, + .ioctl = tuner_ioctl, +}; + +static const struct v4l2_subdev_tuner_ops tuner_tuner_ops = { + .s_std = tuner_s_std, + .s_radio = tuner_s_radio, + .g_tuner = tuner_g_tuner, + .s_tuner = tuner_s_tuner, + .s_frequency = tuner_s_frequency, + .g_frequency = tuner_g_frequency, + .s_type_addr = tuner_s_type_addr, + .s_config = tuner_s_config, +}; + +static const struct v4l2_subdev_ops tuner_ops = { + .core = &tuner_core_ops, + .tuner = &tuner_tuner_ops, +}; + /* ---------------------------------------------------------------------- */ static LIST_HEAD(tuner_list); @@ -1119,9 +1185,9 @@ static int tuner_probe(struct i2c_client *client, t = kzalloc(sizeof(struct tuner), GFP_KERNEL); if (NULL == t) return -ENOMEM; + v4l2_i2c_subdev_init(&t->sd, client, &tuner_ops); t->i2c = client; t->name = "(tuner unset)"; - i2c_set_clientdata(client, t); t->type = UNSET; t->audmode = V4L2_TUNER_MODE_STEREO; t->mode_mask = T_UNINITIALIZED; @@ -1271,8 +1337,9 @@ static int tuner_legacy_probe(struct i2c_adapter *adap) static int tuner_remove(struct i2c_client *client) { - struct tuner *t = i2c_get_clientdata(client); + struct tuner *t = to_tuner(i2c_get_clientdata(client)); + v4l2_device_unregister_subdev(&t->sd); tuner_detach(&t->fe); t->fe.analog_demod_priv = NULL; -- cgit v1.2.3 From f29ecdd796231f10eb0808a5a4d213ea2c8a264f Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Tue, 25 Nov 2008 08:47:09 +0100 Subject: gspca: Center the brightness in sonixj. From: Jean-Francois Moine The brightness jumped from max to min at the middle of the control values. Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/sonixj.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/sonixj.c b/linux/drivers/media/video/gspca/sonixj.c index bfd256716..9d9f90669 100644 --- a/linux/drivers/media/video/gspca/sonixj.c +++ b/linux/drivers/media/video/gspca/sonixj.c @@ -89,7 +89,7 @@ static struct ctrl sd_ctrls[] = { #define BRIGHTNESS_MAX 0xffff .maximum = BRIGHTNESS_MAX, .step = 1, -#define BRIGHTNESS_DEF 0x7fff +#define BRIGHTNESS_DEF 0x8000 .default_value = BRIGHTNESS_DEF, }, .set = sd_setbrightness, @@ -1142,7 +1142,7 @@ static void setbrightness(struct gspca_dev *gspca_dev) unsigned int expo; __u8 k2; - k2 = sd->brightness >> 10; + k2 = ((int) sd->brightness - 0x8000) >> 10; switch (sd->sensor) { case SENSOR_HV7131R: expo = sd->brightness << 4; @@ -1160,7 +1160,7 @@ static void setbrightness(struct gspca_dev *gspca_dev) case SENSOR_OM6802: expo = sd->brightness >> 6; sd->exposure = setexposure(gspca_dev, expo); - k2 >>= 1; + k2 = sd->brightness >> 11; break; } -- cgit v1.2.3 From 564d21054d1f61d9448a93b3fa8a254a29713d26 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 25 Nov 2008 09:07:00 -0200 Subject: fix boards for kernels with algo_control From: Mauro Carvalho Chehab changeset 4ecae6da8244 removed algo_control from i2c drivers. However, on older kernels, a dummy function to implement algo_control is required, otherwise, an OOPS is generated. This patch adds a backport for all i2c drivers that defines an i2c_algorithm, except for cafe_ccic, since OLPC hardware only works for kernels newer than 2.6.19. Some of the backported drivers would need also extra changes to work with older kernels, as specified on v4l/versions.txt. Yet, I decided to add the backport for those drivers also, since the lack of algo_control doesn't generate any compilation error, but it is enough to break the driver. So, better to all for all boards than to allow compiling a kernel backported that just generates OOPS. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/common/saa7146_i2c.c | 3 +++ linux/drivers/media/dvb/b2c2/flexcop-i2c.c | 3 +++ linux/drivers/media/dvb/dm1105/dm1105.c | 3 +++ linux/drivers/media/dvb/dvb-usb/af9005.c | 3 +++ linux/drivers/media/dvb/dvb-usb/af9015.c | 3 +++ linux/drivers/media/dvb/dvb-usb/anysee.c | 3 +++ linux/drivers/media/dvb/dvb-usb/au6610.c | 3 +++ linux/drivers/media/dvb/dvb-usb/cxusb.c | 3 +++ linux/drivers/media/dvb/dvb-usb/dib0700_core.c | 3 +++ linux/drivers/media/dvb/dvb-usb/dibusb-common.c | 3 +++ linux/drivers/media/dvb/dvb-usb/digitv.c | 3 +++ linux/drivers/media/dvb/dvb-usb/dtv5100.c | 3 +++ linux/drivers/media/dvb/dvb-usb/dw2102.c | 12 ++++++++++++ linux/drivers/media/dvb/dvb-usb/gl861.c | 3 +++ linux/drivers/media/dvb/dvb-usb/m920x.c | 3 +++ linux/drivers/media/dvb/dvb-usb/opera1.c | 3 +++ linux/drivers/media/dvb/dvb-usb/ttusb2.c | 3 +++ linux/drivers/media/dvb/frontends/cx24123.c | 4 ++++ linux/drivers/media/dvb/frontends/dibx000_common.c | 4 ++++ linux/drivers/media/dvb/frontends/s5h1420.c | 4 ++++ linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c | 3 +++ linux/drivers/media/video/au0828/au0828-i2c.c | 3 +++ linux/drivers/media/video/bt8xx/bttv-i2c.c | 3 +++ linux/drivers/media/video/cx23885/cx23885-i2c.c | 3 +++ linux/drivers/media/video/em28xx/em28xx-i2c.c | 3 +++ linux/drivers/media/video/ivtv/ivtv-i2c.c | 3 +++ linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c | 3 +++ linux/drivers/media/video/saa7134/saa7134-i2c.c | 3 +++ linux/drivers/media/video/usbvision/usbvision-i2c.c | 3 +++ linux/drivers/media/video/w9968cf.c | 3 +++ 30 files changed, 102 insertions(+) (limited to 'linux') diff --git a/linux/drivers/media/common/saa7146_i2c.c b/linux/drivers/media/common/saa7146_i2c.c index ae20d2729..255b14551 100644 --- a/linux/drivers/media/common/saa7146_i2c.c +++ b/linux/drivers/media/common/saa7146_i2c.c @@ -406,6 +406,9 @@ static int saa7146_i2c_xfer(struct i2c_adapter* adapter, struct i2c_msg *msg, in static struct i2c_algorithm saa7146_algo = { .master_xfer = saa7146_i2c_xfer, .functionality = saa7146_i2c_func, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; int saa7146_i2c_adapter_prepare(struct saa7146_dev *dev, struct i2c_adapter *i2c_adapter, u32 bitrate) diff --git a/linux/drivers/media/dvb/b2c2/flexcop-i2c.c b/linux/drivers/media/dvb/b2c2/flexcop-i2c.c index 43a112ec6..05635c453 100644 --- a/linux/drivers/media/dvb/b2c2/flexcop-i2c.c +++ b/linux/drivers/media/dvb/b2c2/flexcop-i2c.c @@ -205,6 +205,9 @@ static u32 flexcop_i2c_func(struct i2c_adapter *adapter) static struct i2c_algorithm flexcop_algo = { .master_xfer = flexcop_master_xfer, .functionality = flexcop_i2c_func, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; int flexcop_i2c_init(struct flexcop_device *fc) diff --git a/linux/drivers/media/dvb/dm1105/dm1105.c b/linux/drivers/media/dvb/dm1105/dm1105.c index 100a7f661..d7034e6a1 100644 --- a/linux/drivers/media/dvb/dm1105/dm1105.c +++ b/linux/drivers/media/dvb/dm1105/dm1105.c @@ -330,6 +330,9 @@ static u32 functionality(struct i2c_adapter *adap) static struct i2c_algorithm dm1105_algo = { .master_xfer = dm1105_i2c_xfer, .functionality = functionality, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; static inline struct dm1105dvb *feed_to_dm1105dvb(struct dvb_demux_feed *feed) diff --git a/linux/drivers/media/dvb/dvb-usb/af9005.c b/linux/drivers/media/dvb/dvb-usb/af9005.c index ca5a0a4d2..fdf592625 100644 --- a/linux/drivers/media/dvb/dvb-usb/af9005.c +++ b/linux/drivers/media/dvb/dvb-usb/af9005.c @@ -500,6 +500,9 @@ static u32 af9005_i2c_func(struct i2c_adapter *adapter) static struct i2c_algorithm af9005_i2c_algo = { .master_xfer = af9005_i2c_xfer, .functionality = af9005_i2c_func, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; int af9005_send_command(struct dvb_usb_device *d, u8 command, u8 * wbuf, diff --git a/linux/drivers/media/dvb/dvb-usb/af9015.c b/linux/drivers/media/dvb/dvb-usb/af9015.c index e6e534e04..f0b57c739 100644 --- a/linux/drivers/media/dvb/dvb-usb/af9015.c +++ b/linux/drivers/media/dvb/dvb-usb/af9015.c @@ -318,6 +318,9 @@ static u32 af9015_i2c_func(struct i2c_adapter *adapter) static struct i2c_algorithm af9015_i2c_algo = { .master_xfer = af9015_i2c_xfer, .functionality = af9015_i2c_func, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; static int af9015_do_reg_bit(struct dvb_usb_device *d, u16 addr, u8 bit, u8 op) diff --git a/linux/drivers/media/dvb/dvb-usb/anysee.c b/linux/drivers/media/dvb/dvb-usb/anysee.c index cd2edbcaa..90a04266a 100644 --- a/linux/drivers/media/dvb/dvb-usb/anysee.c +++ b/linux/drivers/media/dvb/dvb-usb/anysee.c @@ -199,6 +199,9 @@ static u32 anysee_i2c_func(struct i2c_adapter *adapter) static struct i2c_algorithm anysee_i2c_algo = { .master_xfer = anysee_master_xfer, .functionality = anysee_i2c_func, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; static int anysee_mt352_demod_init(struct dvb_frontend *fe) diff --git a/linux/drivers/media/dvb/dvb-usb/au6610.c b/linux/drivers/media/dvb/dvb-usb/au6610.c index eb34cc389..b2400d693 100644 --- a/linux/drivers/media/dvb/dvb-usb/au6610.c +++ b/linux/drivers/media/dvb/dvb-usb/au6610.c @@ -119,6 +119,9 @@ static u32 au6610_i2c_func(struct i2c_adapter *adapter) static struct i2c_algorithm au6610_i2c_algo = { .master_xfer = au6610_i2c_xfer, .functionality = au6610_i2c_func, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; /* Callbacks for DVB USB */ diff --git a/linux/drivers/media/dvb/dvb-usb/cxusb.c b/linux/drivers/media/dvb/dvb-usb/cxusb.c index 406d7fba3..f467cad8a 100644 --- a/linux/drivers/media/dvb/dvb-usb/cxusb.c +++ b/linux/drivers/media/dvb/dvb-usb/cxusb.c @@ -213,6 +213,9 @@ static u32 cxusb_i2c_func(struct i2c_adapter *adapter) static struct i2c_algorithm cxusb_i2c_algo = { .master_xfer = cxusb_i2c_xfer, .functionality = cxusb_i2c_func, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; static int cxusb_power_ctrl(struct dvb_usb_device *d, int onoff) diff --git a/linux/drivers/media/dvb/dvb-usb/dib0700_core.c b/linux/drivers/media/dvb/dvb-usb/dib0700_core.c index 2fbb7049a..807bff0e1 100644 --- a/linux/drivers/media/dvb/dvb-usb/dib0700_core.c +++ b/linux/drivers/media/dvb/dvb-usb/dib0700_core.c @@ -252,6 +252,9 @@ static u32 dib0700_i2c_func(struct i2c_adapter *adapter) struct i2c_algorithm dib0700_i2c_algo = { .master_xfer = dib0700_i2c_xfer, .functionality = dib0700_i2c_func, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; int dib0700_identify_state(struct usb_device *udev, struct dvb_usb_device_properties *props, diff --git a/linux/drivers/media/dvb/dvb-usb/dibusb-common.c b/linux/drivers/media/dvb/dvb-usb/dibusb-common.c index 8ee6cd4da..d774879df 100644 --- a/linux/drivers/media/dvb/dvb-usb/dibusb-common.c +++ b/linux/drivers/media/dvb/dvb-usb/dibusb-common.c @@ -155,6 +155,9 @@ static u32 dibusb_i2c_func(struct i2c_adapter *adapter) struct i2c_algorithm dibusb_i2c_algo = { .master_xfer = dibusb_i2c_xfer, .functionality = dibusb_i2c_func, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; EXPORT_SYMBOL(dibusb_i2c_algo); diff --git a/linux/drivers/media/dvb/dvb-usb/digitv.c b/linux/drivers/media/dvb/dvb-usb/digitv.c index b545cf3ea..3a560dcdd 100644 --- a/linux/drivers/media/dvb/dvb-usb/digitv.c +++ b/linux/drivers/media/dvb/dvb-usb/digitv.c @@ -83,6 +83,9 @@ static u32 digitv_i2c_func(struct i2c_adapter *adapter) static struct i2c_algorithm digitv_i2c_algo = { .master_xfer = digitv_i2c_xfer, .functionality = digitv_i2c_func, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; /* Callbacks for DVB USB */ diff --git a/linux/drivers/media/dvb/dvb-usb/dtv5100.c b/linux/drivers/media/dvb/dvb-usb/dtv5100.c index 078ce92ca..e07a805f6 100644 --- a/linux/drivers/media/dvb/dvb-usb/dtv5100.c +++ b/linux/drivers/media/dvb/dvb-usb/dtv5100.c @@ -104,6 +104,9 @@ static u32 dtv5100_i2c_func(struct i2c_adapter *adapter) static struct i2c_algorithm dtv5100_i2c_algo = { .master_xfer = dtv5100_i2c_xfer, .functionality = dtv5100_i2c_func, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; /* Callbacks for DVB USB */ diff --git a/linux/drivers/media/dvb/dvb-usb/dw2102.c b/linux/drivers/media/dvb/dvb-usb/dw2102.c index bc5e47a2b..9b32c9c0a 100644 --- a/linux/drivers/media/dvb/dvb-usb/dw2102.c +++ b/linux/drivers/media/dvb/dvb-usb/dw2102.c @@ -368,21 +368,33 @@ static u32 dw210x_i2c_func(struct i2c_adapter *adapter) static struct i2c_algorithm dw2102_i2c_algo = { .master_xfer = dw2102_i2c_transfer, .functionality = dw210x_i2c_func, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; static struct i2c_algorithm dw2102_serit_i2c_algo = { .master_xfer = dw2102_serit_i2c_transfer, .functionality = dw210x_i2c_func, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; static struct i2c_algorithm dw2102_earda_i2c_algo = { .master_xfer = dw2102_earda_i2c_transfer, .functionality = dw210x_i2c_func, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; static struct i2c_algorithm dw2104_i2c_algo = { .master_xfer = dw2104_i2c_transfer, .functionality = dw210x_i2c_func, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; static int dw210x_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) diff --git a/linux/drivers/media/dvb/dvb-usb/gl861.c b/linux/drivers/media/dvb/dvb-usb/gl861.c index 6f596ed41..41bec1b58 100644 --- a/linux/drivers/media/dvb/dvb-usb/gl861.c +++ b/linux/drivers/media/dvb/dvb-usb/gl861.c @@ -91,6 +91,9 @@ static u32 gl861_i2c_func(struct i2c_adapter *adapter) static struct i2c_algorithm gl861_i2c_algo = { .master_xfer = gl861_i2c_xfer, .functionality = gl861_i2c_func, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; /* Callbacks for DVB USB */ diff --git a/linux/drivers/media/dvb/dvb-usb/m920x.c b/linux/drivers/media/dvb/dvb-usb/m920x.c index 54626a0db..51c1c8dba 100644 --- a/linux/drivers/media/dvb/dvb-usb/m920x.c +++ b/linux/drivers/media/dvb/dvb-usb/m920x.c @@ -256,6 +256,9 @@ static u32 m920x_i2c_func(struct i2c_adapter *adapter) static struct i2c_algorithm m920x_i2c_algo = { .master_xfer = m920x_i2c_xfer, .functionality = m920x_i2c_func, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; /* pid filter */ diff --git a/linux/drivers/media/dvb/dvb-usb/opera1.c b/linux/drivers/media/dvb/dvb-usb/opera1.c index cba064615..d45ceb83b 100644 --- a/linux/drivers/media/dvb/dvb-usb/opera1.c +++ b/linux/drivers/media/dvb/dvb-usb/opera1.c @@ -167,6 +167,9 @@ static u32 opera1_i2c_func(struct i2c_adapter *adapter) static struct i2c_algorithm opera1_i2c_algo = { .master_xfer = opera1_i2c_xfer, .functionality = opera1_i2c_func, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; static int opera1_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) diff --git a/linux/drivers/media/dvb/dvb-usb/ttusb2.c b/linux/drivers/media/dvb/dvb-usb/ttusb2.c index e79a26473..849c59478 100644 --- a/linux/drivers/media/dvb/dvb-usb/ttusb2.c +++ b/linux/drivers/media/dvb/dvb-usb/ttusb2.c @@ -124,6 +124,9 @@ static u32 ttusb2_i2c_func(struct i2c_adapter *adapter) static struct i2c_algorithm ttusb2_i2c_algo = { .master_xfer = ttusb2_i2c_xfer, .functionality = ttusb2_i2c_func, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; /* Callbacks for DVB USB */ diff --git a/linux/drivers/media/dvb/frontends/cx24123.c b/linux/drivers/media/dvb/frontends/cx24123.c index fb559369e..c9cfc8393 100644 --- a/linux/drivers/media/dvb/frontends/cx24123.c +++ b/linux/drivers/media/dvb/frontends/cx24123.c @@ -29,6 +29,7 @@ #include "dvb_frontend.h" #include "cx24123.h" +#include "compat.h" #define XTAL 10111000 @@ -1065,6 +1066,9 @@ static u32 cx24123_tuner_i2c_func(struct i2c_adapter *adapter) static struct i2c_algorithm cx24123_tuner_i2c_algo = { .master_xfer = cx24123_tuner_i2c_tuner_xfer, .functionality = cx24123_tuner_i2c_func, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; struct i2c_adapter * diff --git a/linux/drivers/media/dvb/frontends/dibx000_common.c b/linux/drivers/media/dvb/frontends/dibx000_common.c index a8e3a950c..6f1f41ae3 100644 --- a/linux/drivers/media/dvb/frontends/dibx000_common.c +++ b/linux/drivers/media/dvb/frontends/dibx000_common.c @@ -1,6 +1,7 @@ #include #include "dibx000_common.h" +#include "compat.h" static int debug; module_param(debug, int, 0644); @@ -107,6 +108,9 @@ static int dibx000_i2c_gated_tuner_xfer(struct i2c_adapter *i2c_adap, struct i2c static struct i2c_algorithm dibx000_i2c_gated_tuner_algo = { .master_xfer = dibx000_i2c_gated_tuner_xfer, .functionality = dibx000_i2c_func, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; struct i2c_adapter * dibx000_get_i2c_adapter(struct dibx000_i2c_master *mst, enum dibx000_i2c_interface intf, int gating) diff --git a/linux/drivers/media/dvb/frontends/s5h1420.c b/linux/drivers/media/dvb/frontends/s5h1420.c index 46d55e393..f357f8233 100644 --- a/linux/drivers/media/dvb/frontends/s5h1420.c +++ b/linux/drivers/media/dvb/frontends/s5h1420.c @@ -37,6 +37,7 @@ #include "dvb_frontend.h" #include "s5h1420.h" #include "s5h1420_priv.h" +#include "compat.h" #define TONE_FREQ 22000 @@ -904,6 +905,9 @@ static int s5h1420_tuner_i2c_tuner_xfer(struct i2c_adapter *i2c_adap, struct i2c static struct i2c_algorithm s5h1420_tuner_i2c_algo = { .master_xfer = s5h1420_tuner_i2c_tuner_xfer, .functionality = s5h1420_tuner_i2c_func, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; struct i2c_adapter *s5h1420_get_tuner_i2c_adapter(struct dvb_frontend *fe) diff --git a/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c b/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c index 8775b78af..086b9987f 100644 --- a/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c +++ b/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c @@ -1657,6 +1657,9 @@ static void frontend_init(struct ttusb* ttusb) static struct i2c_algorithm ttusb_dec_algo = { .master_xfer = master_xfer, .functionality = functionality, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; static int ttusb_probe(struct usb_interface *intf, const struct usb_device_id *id) diff --git a/linux/drivers/media/video/au0828/au0828-i2c.c b/linux/drivers/media/video/au0828/au0828-i2c.c index 03b797b9b..d8cef6ccc 100644 --- a/linux/drivers/media/video/au0828/au0828-i2c.c +++ b/linux/drivers/media/video/au0828/au0828-i2c.c @@ -301,6 +301,9 @@ static u32 au0828_functionality(struct i2c_adapter *adap) static struct i2c_algorithm au0828_i2c_algo_template = { .master_xfer = i2c_xfer, .functionality = au0828_functionality, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; /* ----------------------------------------------------------------------- */ diff --git a/linux/drivers/media/video/bt8xx/bttv-i2c.c b/linux/drivers/media/video/bt8xx/bttv-i2c.c index bcd2cd240..558572589 100644 --- a/linux/drivers/media/video/bt8xx/bttv-i2c.c +++ b/linux/drivers/media/video/bt8xx/bttv-i2c.c @@ -260,6 +260,9 @@ static int bttv_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int static const struct i2c_algorithm bttv_algo = { .master_xfer = bttv_i2c_xfer, .functionality = functionality, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; /* ----------------------------------------------------------------------- */ diff --git a/linux/drivers/media/video/cx23885/cx23885-i2c.c b/linux/drivers/media/video/cx23885/cx23885-i2c.c index ced5648d8..2210b47d2 100644 --- a/linux/drivers/media/video/cx23885/cx23885-i2c.c +++ b/linux/drivers/media/video/cx23885/cx23885-i2c.c @@ -353,6 +353,9 @@ static u32 cx23885_functionality(struct i2c_adapter *adap) static struct i2c_algorithm cx23885_i2c_algo_template = { .master_xfer = i2c_xfer, .functionality = cx23885_functionality, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; /* ----------------------------------------------------------------------- */ diff --git a/linux/drivers/media/video/em28xx/em28xx-i2c.c b/linux/drivers/media/video/em28xx/em28xx-i2c.c index ec3e3b157..4e6fa575d 100644 --- a/linux/drivers/media/video/em28xx/em28xx-i2c.c +++ b/linux/drivers/media/video/em28xx/em28xx-i2c.c @@ -521,6 +521,9 @@ static int attach_inform(struct i2c_client *client) static struct i2c_algorithm em28xx_algo = { .master_xfer = em28xx_i2c_xfer, .functionality = functionality, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; static struct i2c_adapter em28xx_adap_template = { diff --git a/linux/drivers/media/video/ivtv/ivtv-i2c.c b/linux/drivers/media/video/ivtv/ivtv-i2c.c index c251ddb2f..fa18eae17 100644 --- a/linux/drivers/media/video/ivtv/ivtv-i2c.c +++ b/linux/drivers/media/video/ivtv/ivtv-i2c.c @@ -550,6 +550,9 @@ static u32 ivtv_functionality(struct i2c_adapter *adap) static struct i2c_algorithm ivtv_algo = { .master_xfer = ivtv_xfer, .functionality = ivtv_functionality, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; /* template for our-bit banger */ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c index edce8fc34..877c8f7b1 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c @@ -1013,6 +1013,9 @@ static int pvr2_i2c_detach_inform(struct i2c_client *client) static struct i2c_algorithm pvr2_i2c_algo_template = { .master_xfer = pvr2_i2c_xfer, .functionality = pvr2_i2c_functionality, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; static struct i2c_adapter pvr2_i2c_adap_template = { diff --git a/linux/drivers/media/video/saa7134/saa7134-i2c.c b/linux/drivers/media/video/saa7134/saa7134-i2c.c index df97ac987..3f7d4eb80 100644 --- a/linux/drivers/media/video/saa7134/saa7134-i2c.c +++ b/linux/drivers/media/video/saa7134/saa7134-i2c.c @@ -358,6 +358,9 @@ static int attach_inform(struct i2c_client *client) static struct i2c_algorithm saa7134_algo = { .master_xfer = saa7134_i2c_xfer, .functionality = functionality, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; static struct i2c_adapter saa7134_adap_template = { diff --git a/linux/drivers/media/video/usbvision/usbvision-i2c.c b/linux/drivers/media/video/usbvision/usbvision-i2c.c index 888051b80..58f65e03a 100644 --- a/linux/drivers/media/video/usbvision/usbvision-i2c.c +++ b/linux/drivers/media/video/usbvision/usbvision-i2c.c @@ -198,6 +198,9 @@ static struct i2c_algorithm usbvision_algo = { .master_xfer = usbvision_i2c_xfer, .smbus_xfer = NULL, .functionality = functionality, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; diff --git a/linux/drivers/media/video/w9968cf.c b/linux/drivers/media/video/w9968cf.c index ab3c054c4..5763a4234 100644 --- a/linux/drivers/media/video/w9968cf.c +++ b/linux/drivers/media/video/w9968cf.c @@ -1557,6 +1557,9 @@ static int w9968cf_i2c_init(struct w9968cf_device* cam) static struct i2c_algorithm algo = { .smbus_xfer = w9968cf_i2c_smbus_xfer, .functionality = w9968cf_i2c_func, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; static struct i2c_adapter adap = { -- cgit v1.2.3 From 7a8e65e6272706e69c47c96e865ec1170c1e1564 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 25 Nov 2008 10:10:25 -0200 Subject: em28xx: card description cleanups From: Mauro Carvalho Chehab Remove unused em28xx_board.vchannels and em28xx.video_channels. Also, .is_em2800 = 0 is not needed, as all data is zeroed by kernel loader. The table also included a notice that svideo weren't test on Hauppauge USB2. Remove this notice, since this input also works properly. Also, it does some whitespace cleanups. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-cards.c | 181 ++++++++---------------- linux/drivers/media/video/em28xx/em28xx.h | 2 - 2 files changed, 59 insertions(+), 124 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index f6815ae85..42f9fb6a5 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -55,7 +55,6 @@ struct em28xx_hash_table { struct em28xx_board em28xx_boards[] = { [EM2750_BOARD_UNKNOWN] = { .name = "Unknown EM2750/EM2751 webcam grabber", - .vchannels = 1, .xclk = EM28XX_XCLK_FREQUENCY_48MHZ, .input = { { .type = EM28XX_VMUX_COMPOSITE1, @@ -66,10 +65,9 @@ struct em28xx_board em28xx_boards[] = { [EM2800_BOARD_UNKNOWN] = { .name = "Unknown EM2800 video grabber", .is_em2800 = 1, - .vchannels = 2, .tda9887_conf = TDA9887_PRESENT, .decoder = EM28XX_SAA7113, - .input = { { + .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, .amux = EM28XX_AMUX_LINE_IN, @@ -87,7 +85,6 @@ struct em28xx_board em28xx_boards[] = { /* Beijing Huaqi Information Digital Technology Co., Ltd */ .name = "Huaqi DLCW-130", .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 1, .xclk = EM28XX_XCLK_FREQUENCY_48MHZ, .input = { { .type = EM28XX_VMUX_COMPOSITE1, @@ -97,12 +94,10 @@ struct em28xx_board em28xx_boards[] = { }, [EM2820_BOARD_KWORLD_PVRTV2800RF] = { .name = "Kworld PVR TV 2800 RF", - .is_em2800 = 0, - .vchannels = 2, .tuner_type = TUNER_TEMIC_PAL, .tda9887_conf = TDA9887_PRESENT, .decoder = EM28XX_SAA7113, - .input = { { + .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, .amux = EM28XX_AMUX_LINE_IN, @@ -114,11 +109,10 @@ struct em28xx_board em28xx_boards[] = { }, [EM2820_BOARD_TERRATEC_CINERGY_250] = { .name = "Terratec Cinergy 250 USB", - .vchannels = 3, .tuner_type = TUNER_LG_PAL_NEW_TAPC, .tda9887_conf = TDA9887_PRESENT, .decoder = EM28XX_SAA7113, - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, .amux = EM28XX_AMUX_LINE_IN, @@ -134,11 +128,10 @@ struct em28xx_board em28xx_boards[] = { }, [EM2820_BOARD_PINNACLE_USB_2] = { .name = "Pinnacle PCTV USB 2", - .vchannels = 3, .tuner_type = TUNER_LG_PAL_NEW_TAPC, .tda9887_conf = TDA9887_PRESENT, .decoder = EM28XX_SAA7113, - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, .amux = EM28XX_AMUX_VIDEO, @@ -154,15 +147,13 @@ struct em28xx_board em28xx_boards[] = { }, [EM2820_BOARD_HAUPPAUGE_WINTV_USB_2] = { .name = "Hauppauge WinTV USB 2", - .vchannels = 3, .tuner_type = TUNER_PHILIPS_FM1236_MK3, .tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE| TDA9887_PORT2_ACTIVE, .decoder = EM28XX_TVP5150, .has_msp34xx = 1, - /*FIXME: S-Video not tested */ - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, .amux = MSP_INPUT_DEFAULT, @@ -176,12 +167,10 @@ struct em28xx_board em28xx_boards[] = { [EM2820_BOARD_DLINK_USB_TV] = { .name = "D-Link DUB-T210 TV Tuner", .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 3, - .is_em2800 = 0, .tuner_type = TUNER_LG_PAL_NEW_TAPC, .tda9887_conf = TDA9887_PRESENT, .decoder = EM28XX_SAA7113, - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, .amux = EM28XX_AMUX_LINE_IN, @@ -198,7 +187,6 @@ struct em28xx_board em28xx_boards[] = { [EM2820_BOARD_HERCULES_SMART_TV_USB2] = { .name = "Hercules Smart TV USB 2.0", .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 3, .tuner_type = TUNER_LG_PAL_NEW_TAPC, .tda9887_conf = TDA9887_PRESENT, .decoder = EM28XX_SAA7113, @@ -219,12 +207,10 @@ struct em28xx_board em28xx_boards[] = { [EM2820_BOARD_PINNACLE_USB_2_FM1216ME] = { .name = "Pinnacle PCTV USB 2 (Philips FM1216ME)", .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 3, - .is_em2800 = 0, .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, .tda9887_conf = TDA9887_PRESENT, .decoder = EM28XX_SAA7113, - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, .amux = EM28XX_AMUX_VIDEO, @@ -241,11 +227,10 @@ struct em28xx_board em28xx_boards[] = { [EM2820_BOARD_GADMEI_UTV310] = { .name = "Gadmei UTV310", .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 3, .tuner_type = TUNER_TNF_5335MF, .tda9887_conf = TDA9887_PRESENT, .decoder = EM28XX_SAA7113, - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE1, .amux = EM28XX_AMUX_LINE_IN, @@ -262,11 +247,10 @@ struct em28xx_board em28xx_boards[] = { [EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE] = { .name = "Leadtek Winfast USB II Deluxe", .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 3, .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, .tda9887_conf = TDA9887_PRESENT, .decoder = EM28XX_SAA7114, - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = 2, .amux = EM28XX_AMUX_VIDEO, @@ -283,9 +267,8 @@ struct em28xx_board em28xx_boards[] = { [EM2820_BOARD_PINNACLE_DVC_100] = { .name = "Pinnacle Dazzle DVC 100", .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 3, .decoder = EM28XX_SAA7113, - .input = { { + .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, .amux = EM28XX_AMUX_LINE_IN, @@ -296,10 +279,9 @@ struct em28xx_board em28xx_boards[] = { } }, }, [EM2820_BOARD_VIDEOLOGY_20K14XUSB] = { - .name = "Videology 20K14XUSB USB2.0", + .name = "Videology 20K14XUSB USB2.0", .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 1, - .input = { { + .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = 0, .amux = EM28XX_AMUX_VIDEO, @@ -308,12 +290,10 @@ struct em28xx_board em28xx_boards[] = { [EM2821_BOARD_PROLINK_PLAYTV_USB2] = { .name = "SIIG AVTuner-PVR/Prolink PlayTV USB 2.0", .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 3, - .is_em2800 = 0, .tuner_type = TUNER_LG_PAL_NEW_TAPC, /* unknown? */ .tda9887_conf = TDA9887_PRESENT, /* unknown? */ .decoder = EM28XX_SAA7113, - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, .amux = EM28XX_AMUX_LINE_IN, @@ -330,14 +310,12 @@ struct em28xx_board em28xx_boards[] = { [EM2821_BOARD_SUPERCOMP_USB_2] = { .name = "Supercomp USB 2.0 TV", .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 3, - .is_em2800 = 0, .tuner_type = TUNER_PHILIPS_FM1236_MK3, .tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE | TDA9887_PORT2_ACTIVE, .decoder = EM28XX_SAA7113, - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, .amux = EM28XX_AMUX_LINE_IN, @@ -352,11 +330,10 @@ struct em28xx_board em28xx_boards[] = { } }, }, [EM2821_BOARD_USBGEAR_VD204] = { - .name = "Usbgear VD204v9", + .name = "Usbgear VD204v9", .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 2, - .decoder = EM28XX_SAA7113, - .input = { { + .decoder = EM28XX_SAA7113, + .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, .amux = EM28XX_AMUX_LINE_IN, @@ -368,21 +345,19 @@ struct em28xx_board em28xx_boards[] = { }, [EM2860_BOARD_NETGMBH_CAM] = { /* Beijing Huaqi Information Digital Technology Co., Ltd */ - .name = "NetGMBH Cam", - .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 1, - .input = { { + .name = "NetGMBH Cam", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = 0, .amux = EM28XX_AMUX_VIDEO, } }, }, [EM2860_BOARD_TYPHOON_DVD_MAKER] = { - .name = "Typhoon DVD Maker", + .name = "Typhoon DVD Maker", .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 2, - .decoder = EM28XX_SAA7113, - .input = { { + .decoder = EM28XX_SAA7113, + .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, .amux = EM28XX_AMUX_LINE_IN, @@ -395,11 +370,10 @@ struct em28xx_board em28xx_boards[] = { [EM2860_BOARD_GADMEI_UTV330] = { .name = "Gadmei UTV330", .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 3, .tuner_type = TUNER_TNF_5335MF, .tda9887_conf = TDA9887_PRESENT, .decoder = EM28XX_SAA7113, - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, .amux = EM28XX_AMUX_VIDEO, @@ -416,10 +390,9 @@ struct em28xx_board em28xx_boards[] = { [EM2860_BOARD_TERRATEC_HYBRID_XS] = { .name = "Terratec Cinergy A Hybrid XS", .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 3, .tuner_type = TUNER_XC2028, .decoder = EM28XX_TVP5150, - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, .amux = EM28XX_AMUX_VIDEO, @@ -436,10 +409,9 @@ struct em28xx_board em28xx_boards[] = { [EM2861_BOARD_KWORLD_PVRTV_300U] = { .name = "KWorld PVRTV 300U", .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 3, .tuner_type = TUNER_XC2028, .decoder = EM28XX_TVP5150, - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, .amux = EM28XX_AMUX_VIDEO, @@ -455,8 +427,7 @@ struct em28xx_board em28xx_boards[] = { }, [EM2861_BOARD_YAKUMO_MOVIE_MIXER] = { .name = "Yakumo MovieMixer", - .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 1, + .valid = EM28XX_BOARD_NOT_VALIDATED, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -475,11 +446,10 @@ struct em28xx_board em28xx_boards[] = { [EM2861_BOARD_PLEXTOR_PX_TV100U] = { .name = "Plextor ConvertX PX-TV100U", .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 3, .tuner_type = TUNER_TNF_5335MF, .tda9887_conf = TDA9887_PRESENT, .decoder = EM28XX_TVP5150, - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, .amux = EM28XX_AMUX_LINE_IN, @@ -516,7 +486,6 @@ struct em28xx_board em28xx_boards[] = { .name = "Pinnacle PCTV DVB-T", .valid = EM28XX_BOARD_NOT_VALIDATED, .tuner_type = TUNER_ABSENT, /* MT2060 */ - /* djh - I have serious doubts this is right... */ .xclk = EM28XX_XCLK_IR_RC5_MODE | EM28XX_XCLK_FREQUENCY_10MHZ, @@ -529,11 +498,10 @@ struct em28xx_board em28xx_boards[] = { [EM2880_BOARD_TERRATEC_HYBRID_XS_FR] = { .name = "Terratec Hybrid XS Secam", .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 3, .has_msp34xx = 1, .tuner_type = TUNER_XC2028, .decoder = EM28XX_TVP5150, - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, .amux = EM28XX_AMUX_VIDEO, @@ -549,13 +517,12 @@ struct em28xx_board em28xx_boards[] = { }, [EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900] = { .name = "Hauppauge WinTV HVR 900", - .vchannels = 3, .tda9887_conf = TDA9887_PRESENT, .tuner_type = TUNER_XC2028, .mts_firmware = 1, .has_dvb = 1, .decoder = EM28XX_TVP5150, - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, .amux = EM28XX_AMUX_VIDEO, @@ -571,12 +538,11 @@ struct em28xx_board em28xx_boards[] = { }, [EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2] = { .name = "Hauppauge WinTV HVR 900 (R2)", - .vchannels = 3, .tda9887_conf = TDA9887_PRESENT, .tuner_type = TUNER_XC2028, .mts_firmware = 1, .decoder = EM28XX_TVP5150, - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, .amux = EM28XX_AMUX_VIDEO, @@ -592,7 +558,6 @@ struct em28xx_board em28xx_boards[] = { }, [EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950] = { .name = "Hauppauge WinTV HVR 950", - .vchannels = 3, .tuner_type = TUNER_XC2028, .mts_firmware = 1, .has_dvb = 1, @@ -614,7 +579,6 @@ struct em28xx_board em28xx_boards[] = { }, [EM2880_BOARD_PINNACLE_PCTV_HD_PRO] = { .name = "Pinnacle PCTV HD Pro Stick", - .vchannels = 3, .tuner_type = TUNER_XC2028, .mts_firmware = 1, .has_dvb = 1, @@ -636,7 +600,6 @@ struct em28xx_board em28xx_boards[] = { }, [EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600] = { .name = "AMD ATI TV Wonder HD 600", - .vchannels = 3, .tuner_type = TUNER_XC2028, .mts_firmware = 1, .has_dvb = 1, @@ -657,10 +620,9 @@ struct em28xx_board em28xx_boards[] = { } }, }, [EM2880_BOARD_TERRATEC_HYBRID_XS] = { - .name = "Terratec Hybrid XS", - .vchannels = 3, - .tuner_type = TUNER_XC2028, - .decoder = EM28XX_TVP5150, + .name = "Terratec Hybrid XS", + .tuner_type = TUNER_XC2028, + .decoder = EM28XX_TVP5150, .has_dvb = 1, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -681,10 +643,9 @@ struct em28xx_board em28xx_boards[] = { maybe we'll need it lateron */ [EM2880_BOARD_TERRATEC_PRODIGY_XS] = { .name = "Terratec Prodigy XS", - .vchannels = 3, .tuner_type = TUNER_XC2028, .decoder = EM28XX_TVP5150, - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, .amux = EM28XX_AMUX_VIDEO, @@ -700,13 +661,11 @@ struct em28xx_board em28xx_boards[] = { }, [EM2820_BOARD_MSI_VOX_USB_2] = { .name = "MSI VOX USB 2.0", - .vchannels = 3, .tuner_type = TUNER_LG_PAL_NEW_TAPC, .tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE | TDA9887_PORT2_ACTIVE, .max_range_640_480 = 1, - .decoder = EM28XX_SAA7114, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -725,11 +684,10 @@ struct em28xx_board em28xx_boards[] = { [EM2800_BOARD_TERRATEC_CINERGY_200] = { .name = "Terratec Cinergy 200 USB", .is_em2800 = 1, - .vchannels = 3, .tuner_type = TUNER_LG_PAL_NEW_TAPC, .tda9887_conf = TDA9887_PRESENT, .decoder = EM28XX_SAA7113, - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, .amux = EM28XX_AMUX_VIDEO, @@ -746,9 +704,8 @@ struct em28xx_board em28xx_boards[] = { [EM2800_BOARD_GRABBEEX_USB2800] = { .name = "eMPIA Technology, Inc. GrabBeeX+ Video Encoder", .is_em2800 = 1, - .vchannels = 2, .decoder = EM28XX_SAA7113, - .input = { { + .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, .amux = EM28XX_AMUX_LINE_IN, @@ -761,11 +718,10 @@ struct em28xx_board em28xx_boards[] = { [EM2800_BOARD_LEADTEK_WINFAST_USBII] = { .name = "Leadtek Winfast USB II", .is_em2800 = 1, - .vchannels = 3, .tuner_type = TUNER_LG_PAL_NEW_TAPC, .tda9887_conf = TDA9887_PRESENT, .decoder = EM28XX_SAA7113, - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, .amux = EM28XX_AMUX_VIDEO, @@ -782,11 +738,10 @@ struct em28xx_board em28xx_boards[] = { [EM2800_BOARD_KWORLD_USB2800] = { .name = "Kworld USB2800", .is_em2800 = 1, - .vchannels = 3, .tuner_type = TUNER_PHILIPS_FCV1236D, .tda9887_conf = TDA9887_PRESENT, .decoder = EM28XX_SAA7113, - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, .amux = EM28XX_AMUX_VIDEO, @@ -802,10 +757,9 @@ struct em28xx_board em28xx_boards[] = { }, [EM2820_BOARD_PINNACLE_DVC_90] = { .name = "Pinnacle Dazzle DVC 90/DVC 100", - .vchannels = 3, .tuner_type = TUNER_ABSENT, .decoder = EM28XX_SAA7113, - .input = { { + .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, .amux = EM28XX_AMUX_LINE_IN, @@ -818,11 +772,10 @@ struct em28xx_board em28xx_boards[] = { [EM2800_BOARD_VGEAR_POCKETTV] = { .name = "V-Gear PocketTV", .is_em2800 = 1, - .vchannels = 3, .tuner_type = TUNER_LG_PAL_NEW_TAPC, .tda9887_conf = TDA9887_PRESENT, .decoder = EM28XX_SAA7113, - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, .amux = EM28XX_AMUX_VIDEO, @@ -838,11 +791,10 @@ struct em28xx_board em28xx_boards[] = { }, [EM2820_BOARD_PROLINK_PLAYTV_USB2] = { .name = "Pixelview Prolink PlayTV USB 2.0", - .vchannels = 3, .tda9887_conf = TDA9887_PRESENT, .tuner_type = TUNER_YMEC_TVF_5533MF, .decoder = EM28XX_SAA7113, - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, .amux = EM28XX_AMUX_VIDEO, @@ -859,13 +811,12 @@ struct em28xx_board em28xx_boards[] = { } }, }, [EM2860_BOARD_POINTNIX_INTRAORAL_CAMERA] = { - .name = "PointNix Intra-Oral Camera", + .name = "PointNix Intra-Oral Camera", .has_snapshot_button = 1, - .vchannels = 1, - .tda9887_conf = TDA9887_PRESENT, - .tuner_type = TUNER_ABSENT, - .decoder = EM28XX_SAA7113, - .input = { { + .tda9887_conf = TDA9887_PRESENT, + .tuner_type = TUNER_ABSENT, + .decoder = EM28XX_SAA7113, + .input = { { .type = EM28XX_VMUX_SVIDEO, .vmux = SAA7115_SVIDEO3, .amux = EM28XX_AMUX_VIDEO, @@ -874,10 +825,9 @@ struct em28xx_board em28xx_boards[] = { [EM2880_BOARD_MSI_DIGIVOX_AD] = { .name = "MSI DigiVox A/D", .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 3, .tuner_type = TUNER_XC2028, .decoder = EM28XX_TVP5150, - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, .amux = EM28XX_AMUX_VIDEO, @@ -894,10 +844,9 @@ struct em28xx_board em28xx_boards[] = { [EM2880_BOARD_MSI_DIGIVOX_AD_II] = { .name = "MSI DigiVox A/D II", .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 3, .tuner_type = TUNER_XC2028, .decoder = EM28XX_TVP5150, - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, .amux = EM28XX_AMUX_VIDEO, @@ -914,10 +863,9 @@ struct em28xx_board em28xx_boards[] = { [EM2880_BOARD_KWORLD_DVB_305U] = { .name = "KWorld DVB-T 305U", .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 3, .tuner_type = TUNER_XC2028, .decoder = EM28XX_TVP5150, - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, .amux = EM28XX_AMUX_VIDEO, @@ -933,12 +881,11 @@ struct em28xx_board em28xx_boards[] = { }, [EM2880_BOARD_KWORLD_DVB_310U] = { .name = "KWorld DVB-T 310U", - .vchannels = 3, .tuner_type = TUNER_XC2028, .has_dvb = 1, .mts_firmware = 1, .decoder = EM28XX_TVP5150, - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, .amux = EM28XX_AMUX_VIDEO, @@ -955,10 +902,9 @@ struct em28xx_board em28xx_boards[] = { [EM2881_BOARD_DNT_DA2_HYBRID] = { .name = "DNT DA2 Hybrid", .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 3, .tuner_type = TUNER_XC2028, .decoder = EM28XX_TVP5150, - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, .amux = EM28XX_AMUX_VIDEO, @@ -975,10 +921,9 @@ struct em28xx_board em28xx_boards[] = { [EM2881_BOARD_PINNACLE_HYBRID_PRO] = { .name = "Pinnacle Hybrid Pro", .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 3, .tuner_type = TUNER_XC2028, .decoder = EM28XX_TVP5150, - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, .amux = EM28XX_AMUX_VIDEO, @@ -995,11 +940,10 @@ struct em28xx_board em28xx_boards[] = { [EM2882_BOARD_PINNACLE_HYBRID_PRO] = { .name = "Pinnacle Hybrid Pro (2)", .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 3, .tuner_type = TUNER_XC2028, .mts_firmware = 1, .decoder = EM28XX_TVP5150, - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, .amux = EM28XX_AMUX_VIDEO, @@ -1016,10 +960,9 @@ struct em28xx_board em28xx_boards[] = { [EM2882_BOARD_KWORLD_VS_DVBT] = { .name = "Kworld VS-DVB-T 323UR", .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 3, .tuner_type = TUNER_XC2028, .decoder = EM28XX_TVP5150, - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, .amux = EM28XX_AMUX_VIDEO, @@ -1036,10 +979,9 @@ struct em28xx_board em28xx_boards[] = { [EM2882_BOARD_TERRATEC_HYBRID_XS] = { .name = "Terratec Hybrid XS (em2882)", .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 3, .tuner_type = TUNER_XC2028, .decoder = EM28XX_TVP5150, - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, .amux = EM28XX_AMUX_VIDEO, @@ -1056,11 +998,9 @@ struct em28xx_board em28xx_boards[] = { [EM2883_BOARD_KWORLD_HYBRID_A316] = { .name = "Kworld PlusTV HD Hybrid 330", .valid = EM28XX_BOARD_NOT_VALIDATED, - .vchannels = 3, - .is_em2800 = 0, .tuner_type = TUNER_XC2028, .decoder = EM28XX_TVP5150, - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, .amux = EM28XX_AMUX_VIDEO, @@ -1076,11 +1016,10 @@ struct em28xx_board em28xx_boards[] = { }, [EM2820_BOARD_COMPRO_VIDEOMATE_FORYOU] = { .name = "Compro VideoMate ForYou/Stereo", - .vchannels = 2, .tuner_type = TUNER_LG_PAL_NEW_TAPC, .tda9887_conf = TDA9887_PRESENT, .decoder = EM28XX_TVP5150, - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, .amux = EM28XX_AMUX_LINE_IN, @@ -1092,7 +1031,6 @@ struct em28xx_board em28xx_boards[] = { }, [EM2874_BOARD_PINNACLE_PCTV_80E] = { .name = "Pinnacle PCTV HD Mini", - .vchannels = 0, .tuner_type = TUNER_ABSENT, .has_dvb = 1, .ir_codes = ir_codes_pinnacle_pctv_hd, @@ -1101,7 +1039,7 @@ struct em28xx_board em28xx_boards[] = { EM2874_I2C_SECONDARY_BUS_SELECT | EM28XX_I2C_FREQ_400_KHZ, #ifdef DJH_DEBUG - .input = { { + .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, .amux = EM28XX_AMUX_LINE_IN, @@ -1335,7 +1273,6 @@ static void em28xx_set_model(struct em28xx *dev) dev->has_msp34xx = em28xx_boards[dev->model].has_msp34xx; dev->tda9887_conf = em28xx_boards[dev->model].tda9887_conf; dev->decoder = em28xx_boards[dev->model].decoder; - dev->video_inputs = em28xx_boards[dev->model].vchannels; dev->xclk = em28xx_boards[dev->model].xclk; dev->i2c_speed = em28xx_boards[dev->model].i2c_speed; dev->max_range_640_480 = em28xx_boards[dev->model].max_range_640_480; diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index c6f7870b7..f9270f681 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -329,7 +329,6 @@ struct em28xx_reg_seq { struct em28xx_board { char *name; - int vchannels; int tuner_type; int tuner_addr; @@ -450,7 +449,6 @@ struct em28xx { /* GPIO sequences for tuner callbacks */ struct em28xx_reg_seq *tun_analog_gpio, *tun_digital_gpio; - int video_inputs; /* number of video inputs */ struct list_head devlist; u32 i2s_speed; /* I2S speed for audio digital stream */ -- cgit v1.2.3 From 648862d26603ff7d30334417fda9c5d8bfa0871a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 25 Nov 2008 10:39:50 -0200 Subject: Remove duplicated fields on em28xx_board and em28xx structs From: Mauro Carvalho Chehab Several fields are duplicated on both structs. Let's just copy em28xx_board instead. A later cleanup could just copy the fields that are changed, in order to keep em28xx_board const. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-cards.c | 44 ++++++++++--------------- linux/drivers/media/video/em28xx/em28xx-core.c | 8 ++--- linux/drivers/media/video/em28xx/em28xx-dvb.c | 4 +-- linux/drivers/media/video/em28xx/em28xx-i2c.c | 6 ++-- linux/drivers/media/video/em28xx/em28xx-input.c | 4 +-- linux/drivers/media/video/em28xx/em28xx-video.c | 18 +++++----- linux/drivers/media/video/em28xx/em28xx.h | 21 ++++-------- 7 files changed, 43 insertions(+), 62 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 42f9fb6a5..9363aa721 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -1267,19 +1267,9 @@ int em28xx_tuner_callback(void *ptr, int component, int command, int arg) } EXPORT_SYMBOL_GPL(em28xx_tuner_callback); -static void em28xx_set_model(struct em28xx *dev) +static void inline em28xx_set_model(struct em28xx *dev) { - dev->is_em2800 = em28xx_boards[dev->model].is_em2800; - dev->has_msp34xx = em28xx_boards[dev->model].has_msp34xx; - dev->tda9887_conf = em28xx_boards[dev->model].tda9887_conf; - dev->decoder = em28xx_boards[dev->model].decoder; - dev->xclk = em28xx_boards[dev->model].xclk; - dev->i2c_speed = em28xx_boards[dev->model].i2c_speed; - dev->max_range_640_480 = em28xx_boards[dev->model].max_range_640_480; - dev->has_dvb = em28xx_boards[dev->model].has_dvb; - dev->has_snapshot_button = em28xx_boards[dev->model].has_snapshot_button; - dev->ir_codes = em28xx_boards[dev->model].ir_codes; - dev->valid = em28xx_boards[dev->model].valid; + memcpy(&dev->board, &em28xx_boards[dev->model], sizeof(dev->board)); } /* Since em28xx_pre_card_setup() requires a proper dev->model, @@ -1336,16 +1326,16 @@ void em28xx_pre_card_setup(struct em28xx *dev) /* Those are the default values for the majority of boards Use those values if not specified otherwise at boards entry */ - if (!dev->xclk) - dev->xclk = EM28XX_XCLK_IR_RC5_MODE | - EM28XX_XCLK_FREQUENCY_12MHZ; + if (!dev->board.xclk) + dev->board.xclk = EM28XX_XCLK_IR_RC5_MODE | + EM28XX_XCLK_FREQUENCY_12MHZ; - if (!dev->i2c_speed) - dev->i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | - EM28XX_I2C_FREQ_100_KHZ; + if (!dev->board.i2c_speed) + dev->board.i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | + EM28XX_I2C_FREQ_100_KHZ; - em28xx_write_reg(dev, EM28XX_R0F_XCLK, dev->xclk & 0x7f); - em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, dev->i2c_speed); + em28xx_write_reg(dev, EM28XX_R0F_XCLK, dev->board.xclk & 0x7f); + em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, dev->board.i2c_speed); msleep(50); /* request some modules */ @@ -1699,7 +1689,7 @@ void em28xx_card_setup(struct em28xx *dev) if (tv.audio_processor == V4L2_IDENT_MSPX4XX) { dev->i2s_speed = 2048000; - dev->has_msp34xx = 1; + dev->board.has_msp34xx = 1; } #ifdef CONFIG_MODULES if (tv.has_ir) @@ -1732,10 +1722,10 @@ void em28xx_card_setup(struct em28xx *dev) break; } - if (dev->has_snapshot_button) + if (dev->board.has_snapshot_button) em28xx_register_snapshot_button(dev); - if (dev->valid == EM28XX_BOARD_NOT_VALIDATED) { + if (dev->board.valid == EM28XX_BOARD_NOT_VALIDATED) { em28xx_errdev("\n\n"); em28xx_errdev("The support for this board weren't " "valid yet.\n"); @@ -1750,13 +1740,13 @@ void em28xx_card_setup(struct em28xx *dev) #ifdef CONFIG_MODULES /* request some modules */ - if (dev->has_msp34xx) + if (dev->board.has_msp34xx) request_module("msp3400"); - if (dev->decoder == EM28XX_SAA7113 || dev->decoder == EM28XX_SAA7114) + if (dev->board.decoder == EM28XX_SAA7113 || dev->board.decoder == EM28XX_SAA7114) request_module("saa7115"); - if (dev->decoder == EM28XX_TVP5150) + if (dev->board.decoder == EM28XX_TVP5150) request_module("tvp5150"); - if (dev->tuner_type != TUNER_ABSENT) + if (dev->board.tuner_type != TUNER_ABSENT) request_module("tuner"); #endif diff --git a/linux/drivers/media/video/em28xx/em28xx-core.c b/linux/drivers/media/video/em28xx/em28xx-core.c index 64503c04a..339913c0d 100644 --- a/linux/drivers/media/video/em28xx/em28xx-core.c +++ b/linux/drivers/media/video/em28xx/em28xx-core.c @@ -349,7 +349,7 @@ static int em28xx_set_audio_source(struct em28xx *dev) int ret; u8 input; - if (dev->is_em2800) { + if (dev->board.is_em2800) { if (dev->ctl_ainput == EM28XX_AMUX_VIDEO) input = EM2800_AUDIO_SRC_TUNER; else @@ -360,7 +360,7 @@ static int em28xx_set_audio_source(struct em28xx *dev) return ret; } - if (dev->has_msp34xx) + if (dev->board.has_msp34xx) input = EM28XX_AUDIO_SRC_TUNER; else { switch (dev->ctl_ainput) { @@ -417,7 +417,7 @@ int em28xx_audio_analog_set(struct em28xx *dev) } } - xclk = dev->xclk & 0x7f; + xclk = dev->board.xclk & 0x7f; if (!dev->mute) xclk |= 0x80; @@ -658,7 +658,7 @@ static int em28xx_scaler_set(struct em28xx *dev, u16 h, u16 v) { u8 mode; /* the em2800 scaler only supports scaling down to 50% */ - if (dev->is_em2800) + if (dev->board.is_em2800) mode = (v ? 0x20 : 0x00) | (h ? 0x10 : 0x00); else { u8 buf[2]; diff --git a/linux/drivers/media/video/em28xx/em28xx-dvb.c b/linux/drivers/media/video/em28xx/em28xx-dvb.c index 714d35c51..6e6509c7a 100644 --- a/linux/drivers/media/video/em28xx/em28xx-dvb.c +++ b/linux/drivers/media/video/em28xx/em28xx-dvb.c @@ -394,7 +394,7 @@ static int dvb_init(struct em28xx *dev) int result = 0; struct em28xx_dvb *dvb; - if (!dev->has_dvb) { + if (!dev->board.has_dvb) { /* This device does not support the extension */ return 0; } @@ -480,7 +480,7 @@ out_free: static int dvb_fini(struct em28xx *dev) { - if (!dev->has_dvb) { + if (!dev->board.has_dvb) { /* This device does not support the extension */ return 0; } diff --git a/linux/drivers/media/video/em28xx/em28xx-i2c.c b/linux/drivers/media/video/em28xx/em28xx-i2c.c index 4e6fa575d..adc5ab20f 100644 --- a/linux/drivers/media/video/em28xx/em28xx-i2c.c +++ b/linux/drivers/media/video/em28xx/em28xx-i2c.c @@ -250,7 +250,7 @@ static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap, (msgs[i].flags & I2C_M_RD) ? "read" : "write", i == num - 1 ? "stop" : "nonstop", addr, msgs[i].len); if (!msgs[i].len) { /* no len: check only for device presence */ - if (dev->is_em2800) + if (dev->board.is_em2800) rc = em2800_i2c_check_for_device(dev, addr); else rc = em28xx_i2c_check_for_device(dev, addr); @@ -261,7 +261,7 @@ static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap, } else if (msgs[i].flags & I2C_M_RD) { /* read bytes */ - if (dev->is_em2800) + if (dev->board.is_em2800) rc = em2800_i2c_recv_bytes(dev, addr, msgs[i].buf, msgs[i].len); @@ -279,7 +279,7 @@ static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap, for (byte = 0; byte < msgs[i].len; byte++) printk(" %02x", msgs[i].buf[byte]); } - if (dev->is_em2800) + if (dev->board.is_em2800) rc = em2800_i2c_send_bytes(dev, addr, msgs[i].buf, msgs[i].len); diff --git a/linux/drivers/media/video/em28xx/em28xx-input.c b/linux/drivers/media/video/em28xx/em28xx-input.c index 29900920d..d6ef5c461 100644 --- a/linux/drivers/media/video/em28xx/em28xx-input.c +++ b/linux/drivers/media/video/em28xx/em28xx-input.c @@ -345,7 +345,7 @@ int em28xx_ir_init(struct em28xx *dev) u8 ir_config; int err = -ENOMEM; - if (dev->ir_codes == NULL) { + if (dev->board.ir_codes == NULL) { /* No remote control support */ return 0; } @@ -384,7 +384,7 @@ int em28xx_ir_init(struct em28xx *dev) usb_make_path(dev->udev, ir->phys, sizeof(ir->phys)); strlcat(ir->phys, "/input0", sizeof(ir->phys)); - ir_input_init(input_dev, &ir->ir, IR_TYPE_OTHER, dev->ir_codes); + ir_input_init(input_dev, &ir->ir, IR_TYPE_OTHER, dev->board.ir_codes); input_dev->name = ir->name; input_dev->phys = ir->phys; input_dev->id.bustype = BUS_USB; diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index 634b82f4b..a54e904f1 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -521,7 +521,7 @@ static int em28xx_config(struct em28xx *dev) int retval; /* Sets I2C speed to 100 KHz */ - if (!dev->is_em2800) { + if (!dev->board.is_em2800) { retval = em28xx_write_regs_req(dev, 0x00, 0x06, "\x40", 1); if (retval < 0) { em28xx_errdev("%s: em28xx_write_regs_req failed! retval [%d]\n", @@ -578,7 +578,7 @@ static void video_mux(struct em28xx *dev, int index) em28xx_i2c_call_clients(dev, VIDIOC_INT_S_VIDEO_ROUTING, &route); - if (dev->has_msp34xx) { + if (dev->board.has_msp34xx) { if (dev->i2s_speed) { em28xx_i2c_call_clients(dev, VIDIOC_INT_I2S_CLOCK_FREQ, &dev->i2s_speed); @@ -752,7 +752,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, mutex_lock(&dev->lock); - if (dev->is_em2800) { + if (dev->board.is_em2800) { /* the em2800 can only scale down to 50% */ if (height % (maxh / 2)) height = maxh; @@ -1009,7 +1009,7 @@ static int vidioc_queryctrl(struct file *file, void *priv, qc->id = id; - if (!dev->has_msp34xx) { + if (!dev->board.has_msp34xx) { for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) { if (qc->id && qc->id == em28xx_qctrl[i].id) { memcpy(qc, &(em28xx_qctrl[i]), sizeof(*qc)); @@ -1039,7 +1039,7 @@ static int vidioc_g_ctrl(struct file *file, void *priv, return rc; mutex_lock(&dev->lock); - if (!dev->has_msp34xx) + if (!dev->board.has_msp34xx) rc = em28xx_get_ctrl(dev, ctrl); else rc = -EINVAL; @@ -1067,7 +1067,7 @@ static int vidioc_s_ctrl(struct file *file, void *priv, mutex_lock(&dev->lock); - if (dev->has_msp34xx) + if (dev->board.has_msp34xx) em28xx_i2c_call_clients(dev, VIDIOC_S_CTRL, ctrl); else { rc = 1; @@ -2103,7 +2103,7 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, dev->em28xx_read_reg_req_len = em28xx_read_reg_req_len; dev->em28xx_write_regs_req = em28xx_write_regs_req; dev->em28xx_read_reg_req = em28xx_read_reg_req; - dev->is_em2800 = em28xx_boards[dev->model].is_em2800; + dev->board.is_em2800 = em28xx_boards[dev->model].is_em2800; em28xx_pre_card_setup(dev); @@ -2163,7 +2163,7 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, video_set_drvdata(dev->vbi_dev, dev); #endif - if (dev->has_msp34xx) { + if (dev->board.has_msp34xx) { /* Send a reset to other chips via gpio */ errCode = em28xx_write_regs_req(dev, 0x00, 0x08, "\xf7", 1); if (errCode < 0) { @@ -2227,7 +2227,7 @@ static void request_module_async(struct work_struct *work) else if (dev->has_alsa_audio) request_module("em28xx-alsa"); - if (dev->has_dvb) + if (dev->board.has_dvb) request_module("em28xx-dvb"); } diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index f9270f681..c75216145 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -329,6 +329,7 @@ struct em28xx_reg_seq { struct em28xx_board { char *name; + int vchannels; int tuner_type; int tuner_addr; @@ -425,18 +426,12 @@ struct em28xx { int model; /* index in the device_data struct */ int devno; /* marks the number of this device */ enum em28xx_chip_id chip_id; - unsigned int is_em2800:1; - unsigned int has_msp34xx:1; - unsigned int has_tda9887:1; + + struct em28xx_board board; + unsigned int stream_on:1; /* Locks streams */ unsigned int has_audio_class:1; unsigned int has_alsa_audio:1; - unsigned int max_range_640_480:1; - unsigned int has_dvb:1; - unsigned int has_snapshot_button:1; - unsigned int valid:1; /* report for validated boards */ - - unsigned char xclk, i2c_speed; struct em28xx_IR *ir; @@ -453,7 +448,6 @@ struct em28xx { u32 i2s_speed; /* I2S speed for audio digital stream */ - enum em28xx_decoder decoder; struct em28xx_audio_mode audio_mode; int tuner_type; /* type of the tuner */ @@ -536,9 +530,6 @@ struct em28xx { /* Caches GPO and GPIO registers */ unsigned char reg_gpo, reg_gpio; - /* Infrared remote control support */ - IR_KEYTAB_TYPE *ir_codes; - /* Snapshot button */ char snapshot_button_path[30]; /* path of the input dev */ struct input_dev *sbutton_input_dev; @@ -712,7 +703,7 @@ static inline int em28xx_gamma_set(struct em28xx *dev, s32 val) /*FIXME: maxw should be dependent of alt mode */ static inline unsigned int norm_maxw(struct em28xx *dev) { - if (dev->max_range_640_480) + if (dev->board.max_range_640_480) return 640; else return 720; @@ -720,7 +711,7 @@ static inline unsigned int norm_maxw(struct em28xx *dev) static inline unsigned int norm_maxh(struct em28xx *dev) { - if (dev->max_range_640_480) + if (dev->board.max_range_640_480) return 480; else return (dev->norm & V4L2_STD_625_50) ? 576 : 480; -- cgit v1.2.3 From efb46542b7fee04e74f27680711a75dfda1861e2 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 25 Nov 2008 10:43:29 -0200 Subject: em28xx: cleanup: saa7115 module auto-detects saa711x type From: Mauro Carvalho Chehab Since saa7115 has saa711x chip auto-detection, there's no need on differenciating it at cards table. Just use the generic name for all boards that use a philips saa711x decoder. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-cards.c | 50 ++++++++++++------------- linux/drivers/media/video/em28xx/em28xx.h | 3 +- 2 files changed, 26 insertions(+), 27 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 9363aa721..43a768b5f 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -66,7 +66,7 @@ struct em28xx_board em28xx_boards[] = { .name = "Unknown EM2800 video grabber", .is_em2800 = 1, .tda9887_conf = TDA9887_PRESENT, - .decoder = EM28XX_SAA7113, + .decoder = EM28XX_SAA711X, .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, @@ -96,7 +96,7 @@ struct em28xx_board em28xx_boards[] = { .name = "Kworld PVR TV 2800 RF", .tuner_type = TUNER_TEMIC_PAL, .tda9887_conf = TDA9887_PRESENT, - .decoder = EM28XX_SAA7113, + .decoder = EM28XX_SAA711X, .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, @@ -111,7 +111,7 @@ struct em28xx_board em28xx_boards[] = { .name = "Terratec Cinergy 250 USB", .tuner_type = TUNER_LG_PAL_NEW_TAPC, .tda9887_conf = TDA9887_PRESENT, - .decoder = EM28XX_SAA7113, + .decoder = EM28XX_SAA711X, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, @@ -130,7 +130,7 @@ struct em28xx_board em28xx_boards[] = { .name = "Pinnacle PCTV USB 2", .tuner_type = TUNER_LG_PAL_NEW_TAPC, .tda9887_conf = TDA9887_PRESENT, - .decoder = EM28XX_SAA7113, + .decoder = EM28XX_SAA711X, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, @@ -169,7 +169,7 @@ struct em28xx_board em28xx_boards[] = { .valid = EM28XX_BOARD_NOT_VALIDATED, .tuner_type = TUNER_LG_PAL_NEW_TAPC, .tda9887_conf = TDA9887_PRESENT, - .decoder = EM28XX_SAA7113, + .decoder = EM28XX_SAA711X, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, @@ -189,7 +189,7 @@ struct em28xx_board em28xx_boards[] = { .valid = EM28XX_BOARD_NOT_VALIDATED, .tuner_type = TUNER_LG_PAL_NEW_TAPC, .tda9887_conf = TDA9887_PRESENT, - .decoder = EM28XX_SAA7113, + .decoder = EM28XX_SAA711X, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, @@ -209,7 +209,7 @@ struct em28xx_board em28xx_boards[] = { .valid = EM28XX_BOARD_NOT_VALIDATED, .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, .tda9887_conf = TDA9887_PRESENT, - .decoder = EM28XX_SAA7113, + .decoder = EM28XX_SAA711X, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, @@ -229,7 +229,7 @@ struct em28xx_board em28xx_boards[] = { .valid = EM28XX_BOARD_NOT_VALIDATED, .tuner_type = TUNER_TNF_5335MF, .tda9887_conf = TDA9887_PRESENT, - .decoder = EM28XX_SAA7113, + .decoder = EM28XX_SAA711X, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE1, @@ -249,7 +249,7 @@ struct em28xx_board em28xx_boards[] = { .valid = EM28XX_BOARD_NOT_VALIDATED, .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, .tda9887_conf = TDA9887_PRESENT, - .decoder = EM28XX_SAA7114, + .decoder = EM28XX_SAA711X, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = 2, @@ -267,7 +267,7 @@ struct em28xx_board em28xx_boards[] = { [EM2820_BOARD_PINNACLE_DVC_100] = { .name = "Pinnacle Dazzle DVC 100", .valid = EM28XX_BOARD_NOT_VALIDATED, - .decoder = EM28XX_SAA7113, + .decoder = EM28XX_SAA711X, .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, @@ -292,7 +292,7 @@ struct em28xx_board em28xx_boards[] = { .valid = EM28XX_BOARD_NOT_VALIDATED, .tuner_type = TUNER_LG_PAL_NEW_TAPC, /* unknown? */ .tda9887_conf = TDA9887_PRESENT, /* unknown? */ - .decoder = EM28XX_SAA7113, + .decoder = EM28XX_SAA711X, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, @@ -314,7 +314,7 @@ struct em28xx_board em28xx_boards[] = { .tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE | TDA9887_PORT2_ACTIVE, - .decoder = EM28XX_SAA7113, + .decoder = EM28XX_SAA711X, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, @@ -332,7 +332,7 @@ struct em28xx_board em28xx_boards[] = { [EM2821_BOARD_USBGEAR_VD204] = { .name = "Usbgear VD204v9", .valid = EM28XX_BOARD_NOT_VALIDATED, - .decoder = EM28XX_SAA7113, + .decoder = EM28XX_SAA711X, .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, @@ -356,7 +356,7 @@ struct em28xx_board em28xx_boards[] = { [EM2860_BOARD_TYPHOON_DVD_MAKER] = { .name = "Typhoon DVD Maker", .valid = EM28XX_BOARD_NOT_VALIDATED, - .decoder = EM28XX_SAA7113, + .decoder = EM28XX_SAA711X, .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, @@ -372,7 +372,7 @@ struct em28xx_board em28xx_boards[] = { .valid = EM28XX_BOARD_NOT_VALIDATED, .tuner_type = TUNER_TNF_5335MF, .tda9887_conf = TDA9887_PRESENT, - .decoder = EM28XX_SAA7113, + .decoder = EM28XX_SAA711X, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, @@ -666,7 +666,7 @@ struct em28xx_board em28xx_boards[] = { TDA9887_PORT1_ACTIVE | TDA9887_PORT2_ACTIVE, .max_range_640_480 = 1, - .decoder = EM28XX_SAA7114, + .decoder = EM28XX_SAA711X, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE4, @@ -686,7 +686,7 @@ struct em28xx_board em28xx_boards[] = { .is_em2800 = 1, .tuner_type = TUNER_LG_PAL_NEW_TAPC, .tda9887_conf = TDA9887_PRESENT, - .decoder = EM28XX_SAA7113, + .decoder = EM28XX_SAA711X, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, @@ -704,7 +704,7 @@ struct em28xx_board em28xx_boards[] = { [EM2800_BOARD_GRABBEEX_USB2800] = { .name = "eMPIA Technology, Inc. GrabBeeX+ Video Encoder", .is_em2800 = 1, - .decoder = EM28XX_SAA7113, + .decoder = EM28XX_SAA711X, .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, @@ -720,7 +720,7 @@ struct em28xx_board em28xx_boards[] = { .is_em2800 = 1, .tuner_type = TUNER_LG_PAL_NEW_TAPC, .tda9887_conf = TDA9887_PRESENT, - .decoder = EM28XX_SAA7113, + .decoder = EM28XX_SAA711X, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, @@ -740,7 +740,7 @@ struct em28xx_board em28xx_boards[] = { .is_em2800 = 1, .tuner_type = TUNER_PHILIPS_FCV1236D, .tda9887_conf = TDA9887_PRESENT, - .decoder = EM28XX_SAA7113, + .decoder = EM28XX_SAA711X, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, @@ -758,7 +758,7 @@ struct em28xx_board em28xx_boards[] = { [EM2820_BOARD_PINNACLE_DVC_90] = { .name = "Pinnacle Dazzle DVC 90/DVC 100", .tuner_type = TUNER_ABSENT, - .decoder = EM28XX_SAA7113, + .decoder = EM28XX_SAA711X, .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, @@ -774,7 +774,7 @@ struct em28xx_board em28xx_boards[] = { .is_em2800 = 1, .tuner_type = TUNER_LG_PAL_NEW_TAPC, .tda9887_conf = TDA9887_PRESENT, - .decoder = EM28XX_SAA7113, + .decoder = EM28XX_SAA711X, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, @@ -793,7 +793,7 @@ struct em28xx_board em28xx_boards[] = { .name = "Pixelview Prolink PlayTV USB 2.0", .tda9887_conf = TDA9887_PRESENT, .tuner_type = TUNER_YMEC_TVF_5533MF, - .decoder = EM28XX_SAA7113, + .decoder = EM28XX_SAA711X, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, @@ -815,7 +815,7 @@ struct em28xx_board em28xx_boards[] = { .has_snapshot_button = 1, .tda9887_conf = TDA9887_PRESENT, .tuner_type = TUNER_ABSENT, - .decoder = EM28XX_SAA7113, + .decoder = EM28XX_SAA711X, .input = { { .type = EM28XX_VMUX_SVIDEO, .vmux = SAA7115_SVIDEO3, @@ -1742,7 +1742,7 @@ void em28xx_card_setup(struct em28xx *dev) /* request some modules */ if (dev->board.has_msp34xx) request_module("msp3400"); - if (dev->board.decoder == EM28XX_SAA7113 || dev->board.decoder == EM28XX_SAA7114) + if (dev->board.decoder == EM28XX_SAA711X) request_module("saa7115"); if (dev->board.decoder == EM28XX_TVP5150) request_module("tvp5150"); diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index c75216145..895a5a68f 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -317,8 +317,7 @@ struct em28xx_input { enum em28xx_decoder { EM28XX_NODECODER, EM28XX_TVP5150, - EM28XX_SAA7113, - EM28XX_SAA7114 + EM28XX_SAA711X, }; struct em28xx_reg_seq { -- cgit v1.2.3 From 83f9b196743f52d7b3e0566287bf173fac764bd5 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 25 Nov 2008 11:06:21 -0200 Subject: em28xx: improve debug messages From: Mauro Carvalho Chehab Now, the first message states board names. Also, removed printing the alternate settings by default. I2C messages are now clearer. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-cards.c | 11 +++++---- linux/drivers/media/video/em28xx/em28xx-i2c.c | 32 +++++++++++++------------ linux/drivers/media/video/em28xx/em28xx-video.c | 6 ++--- 3 files changed, 26 insertions(+), 23 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 43a768b5f..137ed3399 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -1279,6 +1279,10 @@ void em28xx_pre_card_setup(struct em28xx *dev) { int rc; + em28xx_set_model(dev); + + em28xx_info("Found %s\n", dev->board.name); + /* Set the default GPO/GPIO for legacy devices */ dev->reg_gpo_num = EM2880_R04_GPO; dev->reg_gpio_num = EM28XX_R08_GPIO; @@ -1289,7 +1293,8 @@ void em28xx_pre_card_setup(struct em28xx *dev) rc = em28xx_read_reg(dev, EM28XX_R0A_CHIPID); if (rc > 0) { dev->chip_id = rc; - switch (rc) { + + switch (dev->chip_id) { case CHIP_ID_EM2750: em28xx_info("chip ID is em2750\n"); break; @@ -1312,7 +1317,7 @@ void em28xx_pre_card_setup(struct em28xx *dev) dev->wait_after_write = 0; break; default: - em28xx_info("em28xx chip ID = %d\n", rc); + em28xx_info("em28xx chip ID = %d\n", dev->chip_id); } } @@ -1321,8 +1326,6 @@ void em28xx_pre_card_setup(struct em28xx *dev) if (rc >= 0) dev->reg_gpo = rc; - em28xx_set_model(dev); - /* Those are the default values for the majority of boards Use those values if not specified otherwise at boards entry */ diff --git a/linux/drivers/media/video/em28xx/em28xx-i2c.c b/linux/drivers/media/video/em28xx/em28xx-i2c.c index adc5ab20f..7084728eb 100644 --- a/linux/drivers/media/video/em28xx/em28xx-i2c.c +++ b/linux/drivers/media/video/em28xx/em28xx-i2c.c @@ -388,47 +388,49 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char *eedata, int len) if (em_eeprom->id == 0x9567eb1a) dev->hash = em28xx_hash_mem(eedata, len, 32); - printk(KERN_INFO "EEPROM ID= 0x%08x, hash = 0x%08lx\n", - em_eeprom->id, dev->hash); - printk(KERN_INFO "Vendor/Product ID= %04x:%04x\n", em_eeprom->vendor_ID, - em_eeprom->product_ID); + printk(KERN_INFO "%s: EEPROM ID= 0x%08x, EEPROM hash = 0x%08lx\n", + dev->name, em_eeprom->id, dev->hash); + + printk(KERN_INFO "%s: EEPROM info:\n", dev->name); switch (em_eeprom->chip_conf >> 4 & 0x3) { case 0: - printk(KERN_INFO "No audio on board.\n"); + printk(KERN_INFO "%s:\tNo audio on board.\n", dev->name); break; case 1: - printk(KERN_INFO "AC97 audio (5 sample rates)\n"); + printk(KERN_INFO "%s:\tAC97 audio (5 sample rates)\n", + dev->name); break; case 2: - printk(KERN_INFO "I2S audio, sample rate=32k\n"); + printk(KERN_INFO "%s:\tI2S audio, sample rate=32k\n", dev->name); break; case 3: - printk(KERN_INFO "I2S audio, 3 sample rates\n"); + printk(KERN_INFO "%s:\tI2S audio, 3 sample rates\n", dev->name); break; } if (em_eeprom->chip_conf & 1 << 3) - printk(KERN_INFO "USB Remote wakeup capable\n"); + printk(KERN_INFO "%s:\tUSB Remote wakeup capable\n", dev->name); if (em_eeprom->chip_conf & 1 << 2) - printk(KERN_INFO "USB Self power capable\n"); + printk(KERN_INFO "%s:\tUSB Self power capable\n", dev->name); switch (em_eeprom->chip_conf & 0x3) { case 0: - printk(KERN_INFO "500mA max power\n"); + printk(KERN_INFO "%s:\t500mA max power\n", dev->name); break; case 1: - printk(KERN_INFO "400mA max power\n"); + printk(KERN_INFO "%s:\t400mA max power\n", dev->name); break; case 2: - printk(KERN_INFO "300mA max power\n"); + printk(KERN_INFO "%s:\t300mA max power\n", dev->name); break; case 3: - printk(KERN_INFO "200mA max power\n"); + printk(KERN_INFO "%s:\t200mA max power\n", dev->name); break; } - printk(KERN_INFO "Table at 0x%02x, strings=0x%04x, 0x%04x, 0x%04x\n", + printk(KERN_INFO "%s:\tTable at 0x%02x, strings=0x%04x, 0x%04x, 0x%04x\n", + dev->name, em_eeprom->string_idx_table, em_eeprom->string1, em_eeprom->string2, diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index a54e904f1..12f7fa0a3 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -2354,7 +2354,7 @@ static int em28xx_usb_probe(struct usb_interface *interface, uif = udev->actconfig->interface[0]; dev->num_alt = uif->num_altsetting; - em28xx_info("Alternate settings: %i\n", dev->num_alt); + em28xx_videodbg("Alternate settings: %i\n", dev->num_alt); /* dev->alt_max_pkt_size = kmalloc(sizeof(*dev->alt_max_pkt_size)* */ dev->alt_max_pkt_size = kmalloc(32 * dev->num_alt, GFP_KERNEL); @@ -2370,7 +2370,7 @@ static int em28xx_usb_probe(struct usb_interface *interface, wMaxPacketSize); dev->alt_max_pkt_size[i] = (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); - em28xx_info("Alternate setting %i, max size= %i\n", i, + em28xx_videodbg("Alternate setting %i, max size= %i\n", i, dev->alt_max_pkt_size[i]); } @@ -2386,8 +2386,6 @@ static int em28xx_usb_probe(struct usb_interface *interface, return retval; } - em28xx_info("Found %s\n", em28xx_boards[dev->model].name); - /* save our data pointer in this interface device */ usb_set_intfdata(interface, dev); -- cgit v1.2.3 From 1f3f7671e12715041b3b002db1403ce4da682fda Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 25 Nov 2008 14:10:14 -0200 Subject: em28xx: cleanup: We need just one tuner callback From: Mauro Carvalho Chehab Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-cards.c | 29 ++++++------------------- linux/drivers/media/video/em28xx/em28xx.h | 2 +- 2 files changed, 8 insertions(+), 23 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 137ed3399..f400f80f0 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -1211,15 +1211,6 @@ static struct em28xx_reg_seq default_callback[] = { { -1, -1, -1, -1}, }; -/* Callback for EM2882 TERRATEC HYBRID XS */ -static struct em28xx_reg_seq em2882_terratec_hybrid_xs_digital[] = { - {EM28XX_R08_GPIO, 0x2e, 0xff, 6}, - {EM28XX_R08_GPIO, 0x3e, ~EM_GPIO_4, 6}, - {EM2880_R04_GPO, 0x04, 0xff, 10}, - {EM2880_R04_GPO, 0x0c, 0xff, 10}, - { -1, -1, -1, -1}, -}; - /* Pinnacle PCTV HD Mini (80e) GPIOs 0-5: not used 6: demod reset, active low @@ -1258,10 +1249,7 @@ int em28xx_tuner_callback(void *ptr, int component, int command, int arg) if (command != XC2028_TUNER_RESET) return 0; - if (dev->mode == EM28XX_ANALOG_MODE) - rc = em28xx_gpio_set(dev, dev->tun_analog_gpio); - else - rc = em28xx_gpio_set(dev, dev->tun_digital_gpio); + rc = em28xx_gpio_set(dev, dev->tuner_gpio); return rc; } @@ -1355,16 +1343,12 @@ void em28xx_pre_card_setup(struct em28xx *dev) /* Sets GPO/GPIO sequences for this device */ dev->analog_gpio = hauppauge_wintv_hvr_900_analog; dev->digital_gpio = hauppauge_wintv_hvr_900_digital; - dev->tun_analog_gpio = default_callback; - dev->tun_digital_gpio = default_callback; break; case EM2882_BOARD_TERRATEC_HYBRID_XS: /* Sets GPO/GPIO sequences for this device */ dev->analog_gpio = hauppauge_wintv_hvr_900_analog; dev->digital_gpio = hauppauge_wintv_hvr_900_digital; - dev->tun_analog_gpio = default_callback; - dev->tun_digital_gpio = em2882_terratec_hybrid_xs_digital; break; case EM2880_BOARD_TERRATEC_HYBRID_XS_FR: @@ -1381,8 +1365,6 @@ void em28xx_pre_card_setup(struct em28xx *dev) /* Sets GPO/GPIO sequences for this device */ dev->analog_gpio = default_analog; dev->digital_gpio = default_digital; - dev->tun_analog_gpio = default_callback; - dev->tun_digital_gpio = default_callback; break; case EM2880_BOARD_MSI_DIGIVOX_AD: @@ -1390,8 +1372,6 @@ void em28xx_pre_card_setup(struct em28xx *dev) /* Sets GPO/GPIO sequences for this device */ dev->analog_gpio = em2880_msi_digivox_ad_analog; dev->digital_gpio = em2880_msi_digivox_ad_digital; - dev->tun_analog_gpio = default_callback; - dev->tun_digital_gpio = default_callback; break; case EM2861_BOARD_PLEXTOR_PX_TV100U: @@ -1463,7 +1443,11 @@ void em28xx_pre_card_setup(struct em28xx *dev) break; } - em28xx_gpio_set(dev, dev->tun_analog_gpio); + /* Sets the default callback. Used only for certain tuners */ + if (!dev->tuner_gpio) + dev->tuner_gpio = default_callback; + + em28xx_gpio_set(dev, dev->tuner_gpio); em28xx_set_mode(dev, EM28XX_ANALOG_MODE); /* Unlock device */ @@ -1757,3 +1741,4 @@ void em28xx_card_setup(struct em28xx *dev) em28xx_ir_init(dev); } + diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index 895a5a68f..bea105250 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -441,7 +441,7 @@ struct em28xx { struct em28xx_reg_seq *analog_gpio, *digital_gpio; /* GPIO sequences for tuner callbacks */ - struct em28xx_reg_seq *tun_analog_gpio, *tun_digital_gpio; + struct em28xx_reg_seq *tuner_gpio; struct list_head devlist; -- cgit v1.2.3 From c851fa22bef3a441b457845c01a4d83a748dcd8d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 26 Nov 2008 10:58:48 -0200 Subject: em28xx: Improve register log format Change log format to look more like URB transactions. In fact, setup and IN/OUT transactions are merged. This helps to debug the driver. From: Mauro Carvalho Chehab Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-core.c | 60 ++++++++++++++------------ 1 file changed, 32 insertions(+), 28 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-core.c b/linux/drivers/media/video/em28xx/em28xx-core.c index 339913c0d..f70cffb4a 100644 --- a/linux/drivers/media/video/em28xx/em28xx-core.c +++ b/linux/drivers/media/video/em28xx/em28xx-core.c @@ -66,7 +66,8 @@ MODULE_PARM_DESC(alt, "alternate setting to use for video endpoint"); int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg, char *buf, int len) { - int ret, byte; + int ret; + int pipe = usb_rcvctrlpipe(dev->udev, 0); if (dev->state & DEV_DISCONNECTED) return -ENODEV; @@ -74,10 +75,18 @@ int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg, if (len > URB_MAX_CTRL_SIZE) return -EINVAL; - em28xx_regdbg("req=%02x, reg=%02x ", req, reg); + if (reg_debug) { + printk( KERN_DEBUG "(pipe 0x%08x): " + "IN: %02x %02x %02x %02x %02x %02x %02x %02x ", + pipe, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + req, 0, 0, + reg & 0xff, reg >> 8, + len & 0xff, len >> 8); + } mutex_lock(&dev->ctrl_urb_lock); - ret = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), req, + ret = usb_control_msg(dev->udev, pipe, req, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0x0000, reg, dev->urb_buf, len, HZ); if (ret < 0) { @@ -93,7 +102,9 @@ int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg, mutex_unlock(&dev->ctrl_urb_lock); if (reg_debug) { - printk("%02x values: ", ret); + int byte; + + printk("<<<"); for (byte = 0; byte < len; byte++) printk(" %02x", (unsigned char)buf[byte]); printk("\n"); @@ -108,28 +119,12 @@ int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg, */ int em28xx_read_reg_req(struct em28xx *dev, u8 req, u16 reg) { - u8 val; int ret; + u8 val; - if (dev->state & DEV_DISCONNECTED) - return(-ENODEV); - - em28xx_regdbg("req=%02x, reg=%02x:", req, reg); - - mutex_lock(&dev->ctrl_urb_lock); - ret = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), req, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0x0000, reg, dev->urb_buf, 1, HZ); - val = dev->urb_buf[0]; - mutex_unlock(&dev->ctrl_urb_lock); - - if (ret < 0) { - printk(" failed!\n"); + ret = em28xx_read_reg_req_len(dev, req, reg, &val, 1); + if (ret < 0) return ret; - } - - if (reg_debug) - printk("%02x\n", (unsigned char) val); return val; } @@ -147,6 +142,7 @@ int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf, int len) { int ret; + int pipe = usb_sndctrlpipe(dev->udev, 0); if (dev->state & DEV_DISCONNECTED) return -ENODEV; @@ -154,17 +150,25 @@ int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf, if ((len < 1) || (len > URB_MAX_CTRL_SIZE)) return -EINVAL; - em28xx_regdbg("req=%02x reg=%02x:", req, reg); if (reg_debug) { - int i; - for (i = 0; i < len; ++i) - printk(" %02x", (unsigned char)buf[i]); + int byte; + + printk( KERN_DEBUG "(pipe 0x%08x): " + "OUT: %02x %02x %02x %02x %02x %02x %02x %02x >>>", + pipe, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + req, 0, 0, + reg & 0xff, reg >> 8, + len & 0xff, len >> 8); + + for (byte = 0; byte < len; byte++) + printk(" %02x", (unsigned char)buf[byte]); printk("\n"); } mutex_lock(&dev->ctrl_urb_lock); memcpy(dev->urb_buf, buf, len); - ret = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), req, + ret = usb_control_msg(dev->udev, pipe, req, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0x0000, reg, dev->urb_buf, len, HZ); mutex_unlock(&dev->ctrl_urb_lock); -- cgit v1.2.3 From a6965c34bf836e6d9c5d3888bd7e01105ef32306 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 26 Nov 2008 17:28:42 -0200 Subject: em28xx: replace some magic by register descriptions where known From: Mauro Carvalho Chehab Replaces all occurrences of em28xx_write_regs_req() and em28xx_write_reg() used to setup register names by em28xx_write_reg(). Also, documents the register names that are known. This patch were generated by this small perl script: my %reg_map = ( # Register table - the same as defined on parse_em28xx.pl script ); while (<>) { if (m/(.*)em28xx_write_regs_req\(dev\,\s*0x00\,\s*(.*)\,\s*\"\\x(..)\",\s*1\)\;(.*)/) { my $reg = $2; my $val = $3; $val =~ tr/A-f/a-f/; $reg = $reg_map{$reg} if defined($reg_map{$reg}); printf "$1em28xx_write_reg(dev, %s, 0x%s);$4\n", $reg, $val; } elsif (m/(.*)em28xx_write_regs\(dev\,\s*(.*)\,\s*\"\\x(..)\",\s*1\)\;(.*)/) { my $reg = $2; my $val = $3; $val =~ tr/A-f/a-f/; $reg = $reg_map{$reg} if defined($reg_map{$reg}); printf "$1em28xx_write_reg(dev, %s, 0x%s);$4\n", $reg, $val; } else { print $_; } } Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-core.c | 48 ++++++++++++------------- linux/drivers/media/video/em28xx/em28xx-video.c | 12 +++---- linux/drivers/media/video/em28xx/em28xx.h | 2 +- 3 files changed, 31 insertions(+), 31 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-core.c b/linux/drivers/media/video/em28xx/em28xx-core.c index f70cffb4a..d63e5b997 100644 --- a/linux/drivers/media/video/em28xx/em28xx-core.c +++ b/linux/drivers/media/video/em28xx/em28xx-core.c @@ -558,20 +558,20 @@ EXPORT_SYMBOL_GPL(em28xx_audio_setup); int em28xx_colorlevels_set_default(struct em28xx *dev) { - em28xx_write_regs(dev, EM28XX_R20_YGAIN, "\x10", 1); /* contrast */ - em28xx_write_regs(dev, EM28XX_R21_YOFFSET, "\x00", 1); /* brightness */ - em28xx_write_regs(dev, EM28XX_R22_UVGAIN, "\x10", 1); /* saturation */ - em28xx_write_regs(dev, EM28XX_R23_UOFFSET, "\x00", 1); - em28xx_write_regs(dev, EM28XX_R24_VOFFSET, "\x00", 1); - em28xx_write_regs(dev, EM28XX_R25_SHARPNESS, "\x00", 1); - - em28xx_write_regs(dev, EM28XX_R14_GAMMA, "\x20", 1); - em28xx_write_regs(dev, EM28XX_R15_RGAIN, "\x20", 1); - em28xx_write_regs(dev, EM28XX_R16_GGAIN, "\x20", 1); - em28xx_write_regs(dev, EM28XX_R17_BGAIN, "\x20", 1); - em28xx_write_regs(dev, EM28XX_R18_ROFFSET, "\x00", 1); - em28xx_write_regs(dev, EM28XX_R19_GOFFSET, "\x00", 1); - return em28xx_write_regs(dev, EM28XX_R1A_BOFFSET, "\x00", 1); + em28xx_write_reg(dev, EM28XX_R20_YGAIN, 0x10); /* contrast */ + em28xx_write_reg(dev, EM28XX_R21_YOFFSET, 0x00); /* brightness */ + em28xx_write_reg(dev, EM28XX_R22_UVGAIN, 0x10); /* saturation */ + em28xx_write_reg(dev, EM28XX_R23_UOFFSET, 0x00); + em28xx_write_reg(dev, EM28XX_R24_VOFFSET, 0x00); + em28xx_write_reg(dev, EM28XX_R25_SHARPNESS, 0x00); + + em28xx_write_reg(dev, EM28XX_R14_GAMMA, 0x20); + em28xx_write_reg(dev, EM28XX_R15_RGAIN, 0x20); + em28xx_write_reg(dev, EM28XX_R16_GGAIN, 0x20); + em28xx_write_reg(dev, EM28XX_R17_BGAIN, 0x20); + em28xx_write_reg(dev, EM28XX_R18_ROFFSET, 0x00); + em28xx_write_reg(dev, EM28XX_R19_GOFFSET, 0x00); + return em28xx_write_reg(dev, EM28XX_R1A_BOFFSET, 0x00); } int em28xx_capture_start(struct em28xx *dev, int start) @@ -604,17 +604,17 @@ int em28xx_capture_start(struct em28xx *dev, int start) if (!start) { /* disable video capture */ - rc = em28xx_write_regs(dev, EM28XX_R12_VINENABLE, "\x27", 1); + rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x27); return rc; } /* enable video capture */ - rc = em28xx_write_regs_req(dev, 0x00, 0x48, "\x00", 1); + rc = em28xx_write_reg(dev, 0x48, 0x00); if (dev->mode == EM28XX_ANALOG_MODE) - rc = em28xx_write_regs(dev, EM28XX_R12_VINENABLE, "\x67", 1); + rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x67); else - rc = em28xx_write_regs(dev, EM28XX_R12_VINENABLE, "\x37", 1); + rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x37); msleep(6); @@ -623,9 +623,9 @@ int em28xx_capture_start(struct em28xx *dev, int start) int em28xx_outfmt_set_yuv422(struct em28xx *dev) { - em28xx_write_regs(dev, EM28XX_R27_OUTFMT, "\x34", 1); - em28xx_write_regs(dev, EM28XX_R10_VINMODE, "\x10", 1); - return em28xx_write_regs(dev, EM28XX_R11_VINCTRL, "\x11", 1); + em28xx_write_reg(dev, EM28XX_R27_OUTFMT, 0x34); + em28xx_write_reg(dev, EM28XX_R10_VINMODE, 0x10); + return em28xx_write_reg(dev, EM28XX_R11_VINCTRL, 0x11); } static int em28xx_accumulator_set(struct em28xx *dev, u8 xmin, u8 xmax, @@ -741,11 +741,11 @@ int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio) if (!gpio) return rc; - dev->em28xx_write_regs_req(dev, 0x00, 0x48, "\x00", 1); + em28xx_write_reg(dev, 0x48, 0x00); if (dev->mode == EM28XX_ANALOG_MODE) - dev->em28xx_write_regs_req(dev, 0x00, 0x12, "\x67", 1); + em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x67); else - dev->em28xx_write_regs_req(dev, 0x00, 0x12, "\x37", 1); + em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x37); msleep(6); /* Send GPIO reset sequences specified at board entry */ diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index 12f7fa0a3..bc3a59a57 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -522,7 +522,7 @@ static int em28xx_config(struct em28xx *dev) /* Sets I2C speed to 100 KHz */ if (!dev->board.is_em2800) { - retval = em28xx_write_regs_req(dev, 0x00, 0x06, "\x40", 1); + retval = em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x40); if (retval < 0) { em28xx_errdev("%s: em28xx_write_regs_req failed! retval [%d]\n", __func__, retval); @@ -533,9 +533,9 @@ static int em28xx_config(struct em28xx *dev) #if 1 /* enable vbi capturing */ -/* em28xx_write_regs_req(dev, 0x00, 0x0e, "\xC0", 1); audio register */ -/* em28xx_write_regs_req(dev, 0x00, 0x0f, "\x80", 1); clk register */ - em28xx_write_regs_req(dev, 0x00, 0x11, "\x51", 1); +/* em28xx_write_reg(dev, EM28XX_R0E_AUDIOSRC, 0xc0); audio register */ +/* em28xx_write_reg(dev, EM28XX_R0F_XCLK, 0x80); clk register */ + em28xx_write_reg(dev, EM28XX_R11_VINCTRL, 0x51); #endif dev->mute = 1; /* maybe not the right place... */ @@ -2165,7 +2165,7 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, if (dev->board.has_msp34xx) { /* Send a reset to other chips via gpio */ - errCode = em28xx_write_regs_req(dev, 0x00, 0x08, "\xf7", 1); + errCode = em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xf7); if (errCode < 0) { em28xx_errdev("%s: em28xx_write_regs_req - msp34xx(1) failed! errCode [%d]\n", __func__, errCode); @@ -2173,7 +2173,7 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, } msleep(3); - errCode = em28xx_write_regs_req(dev, 0x00, 0x08, "\xff", 1); + errCode = em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xff); if (errCode < 0) { em28xx_errdev("%s: em28xx_write_regs_req - msp34xx(2) failed! errCode [%d]\n", __func__, errCode); diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index bea105250..81acbd1a2 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -630,7 +630,7 @@ int em28xx_ir_fini(struct em28xx *dev); static inline int em28xx_compression_disable(struct em28xx *dev) { /* side effect of disabling scaler and mixer */ - return em28xx_write_regs(dev, EM28XX_R26_COMPR, "\x00", 1); + return em28xx_write_reg(dev, EM28XX_R26_COMPR, 0x00); } static inline int em28xx_contrast_get(struct em28xx *dev) -- cgit v1.2.3 From ee3056da58ea512f08850e2023e371a89f93fd5b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 25 Nov 2008 14:16:13 -0200 Subject: em28xx: move gpio tables to the top of em28xx-cards From: Mauro Carvalho Chehab Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-cards.c | 141 ++++++++++++------------ 1 file changed, 72 insertions(+), 69 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index f400f80f0..aae578e21 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -52,6 +52,78 @@ struct em28xx_hash_table { unsigned int tuner; }; +/* + * Reset sequences for analog/digital modes + */ + +/* Reset for the most [analog] boards */ +static struct em28xx_reg_seq default_analog[] = { + {EM28XX_R08_GPIO, 0x6d, ~EM_GPIO_4, 10}, + { -1, -1, -1, -1}, +}; + +/* Reset for the most [digital] boards */ +static struct em28xx_reg_seq default_digital[] = { + {EM28XX_R08_GPIO, 0x6e, ~EM_GPIO_4, 10}, + { -1, -1, -1, -1}, +}; + +/* Board Hauppauge WinTV HVR 900 analog */ +static struct em28xx_reg_seq hauppauge_wintv_hvr_900_analog[] = { + {EM28XX_R08_GPIO, 0x2d, ~EM_GPIO_4, 10}, + {0x05, 0xff, 0x10, 10}, + { -1, -1, -1, -1}, +}; + +/* Board Hauppauge WinTV HVR 900 digital */ +static struct em28xx_reg_seq hauppauge_wintv_hvr_900_digital[] = { + {EM28XX_R08_GPIO, 0x2e, ~EM_GPIO_4, 10}, + {EM2880_R04_GPO, 0x04, 0x0f, 10}, + {EM2880_R04_GPO, 0x0c, 0x0f, 10}, + { -1, -1, -1, -1}, +}; + +/* Boards - EM2880 MSI DIGIVOX AD and EM2880_BOARD_MSI_DIGIVOX_AD_II */ +static struct em28xx_reg_seq em2880_msi_digivox_ad_analog[] = { + {EM28XX_R08_GPIO, 0x69, ~EM_GPIO_4, 10}, + { -1, -1, -1, -1}, +}; + +/* Boards - EM2880 MSI DIGIVOX AD and EM2880_BOARD_MSI_DIGIVOX_AD_II */ +static struct em28xx_reg_seq em2880_msi_digivox_ad_digital[] = { + {EM28XX_R08_GPIO, 0x6a, ~EM_GPIO_4, 10}, + { -1, -1, -1, -1}, +}; + +/* Board - EM2870 Kworld 355u + Analog - No input analog */ +static struct em28xx_reg_seq em2870_kworld_355u_digital[] = { + {EM2880_R04_GPO, 0x01, 0xff, 10}, + { -1, -1, -1, -1}, +}; + +/* Callback for the most boards */ +static struct em28xx_reg_seq default_callback[] = { + {EM28XX_R08_GPIO, EM_GPIO_4, EM_GPIO_4, 10}, + {EM28XX_R08_GPIO, 0, EM_GPIO_4, 10}, + {EM28XX_R08_GPIO, EM_GPIO_4, EM_GPIO_4, 10}, + { -1, -1, -1, -1}, +}; + +/* Pinnacle PCTV HD Mini (80e) GPIOs + 0-5: not used + 6: demod reset, active low + 7: LED on, active high */ +static struct em28xx_reg_seq em2874_pinnacle_80e_digital[] = { + {EM28XX_R06_I2C_CLK, 0x45, 0xff, 10}, /*400 KHz*/ + {EM2874_R80_GPIO, 0x80, 0xff, 100},/*Demod reset*/ + {EM2874_R80_GPIO, 0xc0, 0xff, 10}, + { -1, -1, -1, -1}, +}; + +/* + * Board definitions + */ struct em28xx_board em28xx_boards[] = { [EM2750_BOARD_UNKNOWN] = { .name = "Unknown EM2750/EM2751 webcam grabber", @@ -1153,75 +1225,6 @@ struct usb_device_id em28xx_id_table [] = { }; MODULE_DEVICE_TABLE(usb, em28xx_id_table); -/* - * Reset sequences for analog/digital modes - */ - -/* Reset for the most [analog] boards */ -static struct em28xx_reg_seq default_analog[] = { - {EM28XX_R08_GPIO, 0x6d, ~EM_GPIO_4, 10}, - { -1, -1, -1, -1}, -}; - -/* Reset for the most [digital] boards */ -static struct em28xx_reg_seq default_digital[] = { - {EM28XX_R08_GPIO, 0x6e, ~EM_GPIO_4, 10}, - { -1, -1, -1, -1}, -}; - -/* Board Hauppauge WinTV HVR 900 analog */ -static struct em28xx_reg_seq hauppauge_wintv_hvr_900_analog[] = { - {EM28XX_R08_GPIO, 0x2d, ~EM_GPIO_4, 10}, - {0x05, 0xff, 0x10, 10}, - { -1, -1, -1, -1}, -}; - -/* Board Hauppauge WinTV HVR 900 digital */ -static struct em28xx_reg_seq hauppauge_wintv_hvr_900_digital[] = { - {EM28XX_R08_GPIO, 0x2e, ~EM_GPIO_4, 10}, - {EM2880_R04_GPO, 0x04, 0x0f, 10}, - {EM2880_R04_GPO, 0x0c, 0x0f, 10}, - { -1, -1, -1, -1}, -}; - -/* Boards - EM2880 MSI DIGIVOX AD and EM2880_BOARD_MSI_DIGIVOX_AD_II */ -static struct em28xx_reg_seq em2880_msi_digivox_ad_analog[] = { - {EM28XX_R08_GPIO, 0x69, ~EM_GPIO_4, 10}, - { -1, -1, -1, -1}, -}; - -/* Boards - EM2880 MSI DIGIVOX AD and EM2880_BOARD_MSI_DIGIVOX_AD_II */ -static struct em28xx_reg_seq em2880_msi_digivox_ad_digital[] = { - {EM28XX_R08_GPIO, 0x6a, ~EM_GPIO_4, 10}, - { -1, -1, -1, -1}, -}; - -/* Board - EM2870 Kworld 355u - Analog - No input analog */ -static struct em28xx_reg_seq em2870_kworld_355u_digital[] = { - {EM2880_R04_GPO, 0x01, 0xff, 10}, - { -1, -1, -1, -1}, -}; - -/* Callback for the most boards */ -static struct em28xx_reg_seq default_callback[] = { - {EM28XX_R08_GPIO, EM_GPIO_4, EM_GPIO_4, 10}, - {EM28XX_R08_GPIO, 0, EM_GPIO_4, 10}, - {EM28XX_R08_GPIO, EM_GPIO_4, EM_GPIO_4, 10}, - { -1, -1, -1, -1}, -}; - -/* Pinnacle PCTV HD Mini (80e) GPIOs - 0-5: not used - 6: demod reset, active low - 7: LED on, active high */ -static struct em28xx_reg_seq em2874_pinnacle_80e_digital[] = { - {EM28XX_R06_I2C_CLK, 0x45, 0xff, 10}, /*400 KHz*/ - {EM2874_R80_GPIO, 0x80, 0xff, 100},/*Demod reset*/ - {EM2874_R80_GPIO, 0xc0, 0xff, 10}, - { -1, -1, -1, -1}, -}; - /* * EEPROM hash table for devices with generic USB IDs */ -- cgit v1.2.3 From da0e5a7cc6dc76cf99879f3ee26a2256c67e4cde Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Thu, 27 Nov 2008 22:04:21 -0500 Subject: cx18: Eliminate q_io from stream buffer handling From: Andy Walls 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. Priority: normal Signed-off-by: Andy Walls --- linux/drivers/media/video/cx18/cx18-driver.h | 1 - linux/drivers/media/video/cx18/cx18-fileops.c | 9 ++------- linux/drivers/media/video/cx18/cx18-queue.c | 10 ++++++---- linux/drivers/media/video/cx18/cx18-queue.h | 19 +++++++++++++++++-- linux/drivers/media/video/cx18/cx18-streams.c | 1 - 5 files changed, 25 insertions(+), 15 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/cx18/cx18-driver.h b/linux/drivers/media/video/cx18/cx18-driver.h index 94c196a91..f88d82348 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.h +++ b/linux/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/linux/drivers/media/video/cx18/cx18-fileops.c b/linux/drivers/media/video/cx18/cx18-fileops.c index 45b5f402e..b298faa59 100644 --- a/linux/drivers/media/video/cx18/cx18-fileops.c +++ b/linux/drivers/media/video/cx18/cx18-fileops.c @@ -233,11 +233,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) { @@ -413,7 +408,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; @@ -557,7 +552,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/linux/drivers/media/video/cx18/cx18-queue.c b/linux/drivers/media/video/cx18/cx18-queue.c index 7b09c9a3e..fdfc83ee3 100644 --- a/linux/drivers/media/video/cx18/cx18-queue.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-queue.h b/linux/drivers/media/video/cx18/cx18-queue.h index ff50a2b7e..d184e55ce 100644 --- a/linux/drivers/media/video/cx18/cx18-queue.h +++ b/linux/drivers/media/video/cx18/cx18-queue.h @@ -43,9 +43,24 @@ static inline void cx18_buf_sync_for_device(struct cx18_stream *s, void cx18_buf_swap(struct cx18_buffer *buf); /* cx18_queue utility functions */ -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/linux/drivers/media/video/cx18/cx18-streams.c b/linux/drivers/media/video/cx18/cx18-streams.c index f7a7f38d8..67d20b062 100644 --- a/linux/drivers/media/video/cx18/cx18-streams.c +++ b/linux/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 b358f380079ca3db20d7409c3d39c2f5e80f871c Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Mon, 8 Dec 2008 21:02:45 -0500 Subject: cx18: Allow more than 63 capture buffers in rotation per stream From: Andy Walls 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. Priority: normal Signed-off-by: Andy Walls --- linux/drivers/media/video/cx18/cx18-driver.h | 3 +- linux/drivers/media/video/cx18/cx18-fileops.c | 14 ++--- linux/drivers/media/video/cx18/cx18-ioctl.c | 3 +- linux/drivers/media/video/cx18/cx18-mailbox.c | 11 +--- linux/drivers/media/video/cx18/cx18-queue.c | 56 +++++++++---------- linux/drivers/media/video/cx18/cx18-queue.h | 16 +++--- linux/drivers/media/video/cx18/cx18-streams.c | 77 ++++++++++++++++++++++----- linux/drivers/media/video/cx18/cx18-streams.h | 3 ++ 8 files changed, 113 insertions(+), 70 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/cx18/cx18-driver.h b/linux/drivers/media/video/cx18/cx18-driver.h index f88d82348..4d56d0719 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.h +++ b/linux/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/linux/drivers/media/video/cx18/cx18-fileops.c b/linux/drivers/media/video/cx18/cx18-fileops.c index b298faa59..f59525565 100644 --- a/linux/drivers/media/video/cx18/cx18-fileops.c +++ b/linux/drivers/media/video/cx18/cx18-fileops.c @@ -225,7 +225,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; @@ -399,15 +399,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/linux/drivers/media/video/cx18/cx18-ioctl.c b/linux/drivers/media/video/cx18/cx18-ioctl.c index 5d9c1146e..85a2e5a3b 100644 --- a/linux/drivers/media/video/cx18/cx18-ioctl.c +++ b/linux/drivers/media/video/cx18/cx18-ioctl.c @@ -851,8 +851,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/linux/drivers/media/video/cx18/cx18-mailbox.c b/linux/drivers/media/video/cx18/cx18-mailbox.c index f62aee719..2f7060cc1 100644 --- a/linux/drivers/media/video/cx18/cx18-mailbox.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-queue.c b/linux/drivers/media/video/cx18/cx18-queue.c index fdfc83ee3..40379d807 100644 --- a/linux/drivers/media/video/cx18/cx18-queue.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-queue.h b/linux/drivers/media/video/cx18/cx18-queue.h index d184e55ce..456cec3bc 100644 --- a/linux/drivers/media/video/cx18/cx18-queue.h +++ b/linux/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/linux/drivers/media/video/cx18/cx18-streams.c b/linux/drivers/media/video/cx18/cx18-streams.c index 67d20b062..864ce1cf5 100644 --- a/linux/drivers/media/video/cx18/cx18-streams.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-streams.h b/linux/drivers/media/video/cx18/cx18-streams.h index 7218b1504..635d34b75 100644 --- a/linux/drivers/media/video/cx18/cx18-streams.h +++ b/linux/drivers/media/video/cx18/cx18-streams.h @@ -29,6 +29,9 @@ int cx18_streams_register(struct cx18 *cx); void cx18_streams_cleanup(struct cx18 *cx, int unregister); /* Capture related */ +void cx18_stream_load_fw_queue_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 c5e1c44c0f6bebacab6b4fc91acbe1c60e479523 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sun, 7 Dec 2008 21:30:17 -0500 Subject: cx18: Add module parameters for finer control over buffer allocations From: Andy Walls 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. Priority: normal Signed-off-by: Andy Walls --- linux/drivers/media/video/cx18/cx18-driver.c | 154 +++++++++++++++++++++++--- linux/drivers/media/video/cx18/cx18-driver.h | 12 ++ linux/drivers/media/video/cx18/cx18-dvb.c | 4 + linux/drivers/media/video/cx18/cx18-streams.c | 21 ++-- 4 files changed, 167 insertions(+), 24 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/cx18/cx18-driver.c b/linux/drivers/media/video/cx18/cx18-driver.c index 57fcce84b..a358bcce2 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.c +++ b/linux/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]; @@ -773,13 +894,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/linux/drivers/media/video/cx18/cx18-driver.h b/linux/drivers/media/video/cx18/cx18-driver.h index 4d56d0719..29c296f39 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.h +++ b/linux/drivers/media/video/cx18/cx18-driver.h @@ -115,6 +115,17 @@ #define CX18_DEFAULT_ENC_VBI_BUFFERS 1 #define CX18_DEFAULT_ENC_PCM_BUFFERS 1 +/* Maximum firmware DMA buffers per stream */ +#define CX18_MAX_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/linux/drivers/media/video/cx18/cx18-dvb.c b/linux/drivers/media/video/cx18/cx18-dvb.c index 034e09a37..bd5e6f3fd 100644 --- a/linux/drivers/media/video/cx18/cx18-dvb.c +++ b/linux/drivers/media/video/cx18/cx18-dvb.c @@ -217,6 +217,10 @@ int cx18_dvb_register(struct cx18_stream *stream) dvb_net_init(dvb_adapter, &dvb->dvbnet, dmx); CX18_INFO("DVB Frontend registered\n"); + CX18_INFO("Registered DVB adapter%d for %s (%d x %d kB)\n", + stream->dvb.dvb_adapter.num, stream->name, + stream->buffers, stream->buf_size/1024); + mutex_init(&dvb->feedlock); dvb->enabled = 1; return ret; diff --git a/linux/drivers/media/video/cx18/cx18-streams.c b/linux/drivers/media/video/cx18/cx18-streams.c index 864ce1cf5..c80f361c8 100644 --- a/linux/drivers/media/video/cx18/cx18-streams.c +++ b/linux/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 e5df23b4a1dac3d29cb3f3713ecebbac3fd550d1 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Mon, 8 Dec 2008 21:14:46 -0500 Subject: cx18: Increment version number due to siginificant buffering changes From: Andy Walls cx18: Increment version number due to siginificant buffering changes. Now version 1.0.4 Priority: normal Signed-off-by: Andy Walls --- linux/drivers/media/video/cx18/cx18-version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/video/cx18/cx18-version.h b/linux/drivers/media/video/cx18/cx18-version.h index eb043d599..84c0ff13b 100644 --- a/linux/drivers/media/video/cx18/cx18-version.h +++ b/linux/drivers/media/video/cx18/cx18-version.h @@ -25,7 +25,7 @@ #define CX18_DRIVER_NAME "cx18" #define CX18_DRIVER_VERSION_MAJOR 1 #define CX18_DRIVER_VERSION_MINOR 0 -#define CX18_DRIVER_VERSION_PATCHLEVEL 3 +#define CX18_DRIVER_VERSION_PATCHLEVEL 4 #define CX18_VERSION __stringify(CX18_DRIVER_VERSION_MAJOR) "." __stringify(CX18_DRIVER_VERSION_MINOR) "." __stringify(CX18_DRIVER_VERSION_PATCHLEVEL) #define CX18_DRIVER_VERSION KERNEL_VERSION(CX18_DRIVER_VERSION_MAJOR, \ -- cgit v1.2.3 From a00ab36aaa26baf7d88c5763300d06dc47327148 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Fri, 12 Dec 2008 13:50:27 -0500 Subject: cx18: Avoid making firmware API calls with the queue lock held From: Andy Walls 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. Priority: normal Signed-off-by: Andy Walls --- linux/drivers/media/video/cx18/cx18-mailbox.c | 15 ++++++----- linux/drivers/media/video/cx18/cx18-queue.c | 13 +++++----- linux/drivers/media/video/cx18/cx18-streams.c | 37 ++++++++++----------------- linux/drivers/media/video/cx18/cx18-streams.h | 2 +- 4 files changed, 30 insertions(+), 37 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/cx18/cx18-mailbox.c b/linux/drivers/media/video/cx18/cx18-mailbox.c index 2f7060cc1..eb5c31af6 100644 --- a/linux/drivers/media/video/cx18/cx18-mailbox.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-queue.c b/linux/drivers/media/video/cx18/cx18-queue.c index 40379d807..a6b0666f0 100644 --- a/linux/drivers/media/video/cx18/cx18-queue.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-streams.c b/linux/drivers/media/video/cx18/cx18-streams.c index c80f361c8..7dc5b82f1 100644 --- a/linux/drivers/media/video/cx18/cx18-streams.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-streams.h b/linux/drivers/media/video/cx18/cx18-streams.h index 635d34b75..420e0a172 100644 --- a/linux/drivers/media/video/cx18/cx18-streams.h +++ b/linux/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 4b1de397a8c3dfe7e1f5e1b3165b70fb0193f099 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Fri, 12 Dec 2008 14:24:04 -0500 Subject: cx18: Port fix for raw/sliced VBI mixup from ivtv and cx25840 From: Andy Walls 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. Priority: normal Signed-off-by: Andy Walls --- linux/drivers/media/video/cx18/cx18-av-vbi.c | 5 +++-- linux/drivers/media/video/cx18/cx18-driver.c | 2 +- linux/drivers/media/video/cx18/cx18-driver.h | 6 ++++++ linux/drivers/media/video/cx18/cx18-fileops.c | 10 ++++------ linux/drivers/media/video/cx18/cx18-ioctl.c | 11 +++++------ linux/drivers/media/video/cx18/cx18-streams.c | 6 +++--- linux/drivers/media/video/cx18/cx18-vbi.c | 2 +- 7 files changed, 23 insertions(+), 19 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/cx18/cx18-av-vbi.c b/linux/drivers/media/video/cx18/cx18-av-vbi.c index 02fdf57bb..1527ea4f6 100644 --- a/linux/drivers/media/video/cx18/cx18-av-vbi.c +++ b/linux/drivers/media/video/cx18/cx18-av-vbi.c @@ -141,10 +141,11 @@ int cx18_av_vbi(struct cx18 *cx, unsigned int cmd, void *arg) u8 lcr[24]; fmt = arg; - if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) + if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE && + fmt->type != V4L2_BUF_TYPE_VBI_CAPTURE) return -EINVAL; svbi = &fmt->fmt.sliced; - if (svbi->service_set == 0) { + if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) { /* raw VBI */ memset(svbi, 0, sizeof(*svbi)); diff --git a/linux/drivers/media/video/cx18/cx18-driver.c b/linux/drivers/media/video/cx18/cx18-driver.c index a358bcce2..6f1654945 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.c +++ b/linux/drivers/media/video/cx18/cx18-driver.c @@ -599,7 +599,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/linux/drivers/media/video/cx18/cx18-driver.h b/linux/drivers/media/video/cx18/cx18-driver.h index 29c296f39..018d98f94 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.h +++ b/linux/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/linux/drivers/media/video/cx18/cx18-fileops.c b/linux/drivers/media/video/cx18/cx18-fileops.c index f59525565..ade4c66ba 100644 --- a/linux/drivers/media/video/cx18/cx18-fileops.c +++ b/linux/drivers/media/video/cx18/cx18-fileops.c @@ -67,12 +67,11 @@ static int cx18_claim_stream(struct cx18_open_id *id, int type) } s->id = id->open_id; - /* CX18_DEC_STREAM_TYPE_MPG needs to claim CX18_DEC_STREAM_TYPE_VBI, - CX18_ENC_STREAM_TYPE_MPG needs to claim CX18_ENC_STREAM_TYPE_VBI + /* CX18_ENC_STREAM_TYPE_MPG needs to claim CX18_ENC_STREAM_TYPE_VBI (provided VBI insertion is on and sliced VBI is selected), for all other streams we're done */ if (type == CX18_ENC_STREAM_TYPE_MPG && - cx->vbi.insert_mpeg && cx->vbi.sliced_in->service_set) { + cx->vbi.insert_mpeg && !cx18_raw_vbi(cx)) { vbi_type = CX18_ENC_STREAM_TYPE_VBI; } else { return 0; @@ -296,7 +295,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; @@ -371,8 +370,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/linux/drivers/media/video/cx18/cx18-ioctl.c b/linux/drivers/media/video/cx18/cx18-ioctl.c index 85a2e5a3b..c83fcc0c5 100644 --- a/linux/drivers/media/video/cx18/cx18-ioctl.c +++ b/linux/drivers/media/video/cx18/cx18-ioctl.c @@ -284,13 +284,12 @@ static int cx18_s_fmt_vbi_cap(struct file *file, void *fh, if (ret) return ret; - if (id->type == CX18_ENC_STREAM_TYPE_VBI && - cx->vbi.sliced_in->service_set && - atomic_read(&cx->ana_capturing) > 0) + if (!cx18_raw_vbi(cx) && atomic_read(&cx->ana_capturing) > 0) return -EBUSY; cx->vbi.sliced_in->service_set = 0; - cx18_av_cmd(cx, VIDIOC_S_FMT, &cx->vbi.in); + cx->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE; + cx18_av_cmd(cx, VIDIOC_S_FMT, fmt); return cx18_g_fmt_vbi_cap(file, fh, fmt); } @@ -315,9 +314,9 @@ static int cx18_s_fmt_sliced_vbi_cap(struct file *file, void *fh, if (check_service_set(vbifmt, cx->is_50hz) == 0) return -EINVAL; - if (atomic_read(&cx->ana_capturing) > 0 && - cx->vbi.sliced_in->service_set == 0) + if (cx18_raw_vbi(cx) && atomic_read(&cx->ana_capturing) > 0) return -EBUSY; + cx->vbi.in.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; cx18_av_cmd(cx, VIDIOC_S_FMT, fmt); memcpy(cx->vbi.sliced_in, vbifmt, sizeof(*cx->vbi.sliced_in)); return 0; diff --git a/linux/drivers/media/video/cx18/cx18-streams.c b/linux/drivers/media/video/cx18/cx18-streams.c index 7dc5b82f1..081131fab 100644 --- a/linux/drivers/media/video/cx18/cx18-streams.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-vbi.c b/linux/drivers/media/video/cx18/cx18-vbi.c index 22e76ee3f..03f0e8130 100644 --- a/linux/drivers/media/video/cx18/cx18-vbi.c +++ b/linux/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 61a2de789c256e6a8c6c184ed07b18f06b19e1dd Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Fri, 12 Dec 2008 18:00:29 -0500 Subject: cx18: Enable raw VBI capture From: Andy Walls 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. Priority: normal Signed-off-by: Andy Walls Signed-off-by: Hans Verkuil --- linux/drivers/media/video/cx18/cx18-cards.c | 8 ++--- linux/drivers/media/video/cx18/cx18-cards.h | 5 +-- linux/drivers/media/video/cx18/cx18-driver.c | 49 +++++++++++++++++++++++---- linux/drivers/media/video/cx18/cx18-fileops.c | 4 ++- linux/drivers/media/video/cx18/cx18-streams.c | 8 ++--- linux/drivers/media/video/cx18/cx18-vbi.c | 3 ++ 6 files changed, 59 insertions(+), 18 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/cx18/cx18-cards.c b/linux/drivers/media/video/cx18/cx18-cards.c index 23bd871b7..10cddba3e 100644 --- a/linux/drivers/media/video/cx18/cx18-cards.c +++ b/linux/drivers/media/video/cx18/cx18-cards.c @@ -51,7 +51,7 @@ static struct cx18_card_tuner_i2c cx18_i2c_std = { static const struct cx18_card cx18_card_hvr1600_esmt = { .type = CX18_CARD_HVR_1600_ESMT, .name = "Hauppauge HVR-1600", - .comment = "VBI is not yet supported\n", + .comment = "Raw VBI supported; Sliced VBI is not yet supported\n", .v4l2_capabilities = CX18_CAP_ENCODER, .hw_audio_ctrl = CX18_HW_CX23418, .hw_muxer = CX18_HW_CS5345, @@ -97,7 +97,7 @@ static const struct cx18_card cx18_card_hvr1600_esmt = { static const struct cx18_card cx18_card_hvr1600_samsung = { .type = CX18_CARD_HVR_1600_SAMSUNG, .name = "Hauppauge HVR-1600 (Preproduction)", - .comment = "VBI is not yet supported\n", + .comment = "Raw VBI supported; Sliced VBI is not yet supported\n", .v4l2_capabilities = CX18_CAP_ENCODER, .hw_audio_ctrl = CX18_HW_CX23418, .hw_muxer = CX18_HW_CS5345, @@ -152,7 +152,7 @@ static const struct cx18_card_pci_info cx18_pci_h900[] = { static const struct cx18_card cx18_card_h900 = { .type = CX18_CARD_COMPRO_H900, .name = "Compro VideoMate H900", - .comment = "VBI is not yet supported\n", + .comment = "Raw VBI supported; Sliced VBI is not yet supported\n", .v4l2_capabilities = CX18_CAP_ENCODER, .hw_audio_ctrl = CX18_HW_CX23418, .hw_all = CX18_HW_TUNER, @@ -249,7 +249,7 @@ static const struct cx18_card_pci_info cx18_pci_cnxt_raptor_pal[] = { static const struct cx18_card cx18_card_cnxt_raptor_pal = { .type = CX18_CARD_CNXT_RAPTOR_PAL, .name = "Conexant Raptor PAL/SECAM", - .comment = "VBI is not yet supported\n", + .comment = "Raw VBI supported; Sliced VBI is not yet supported\n", .v4l2_capabilities = CX18_CAP_ENCODER, .hw_audio_ctrl = CX18_HW_CX23418, .hw_muxer = CX18_HW_GPIO, diff --git a/linux/drivers/media/video/cx18/cx18-cards.h b/linux/drivers/media/video/cx18/cx18-cards.h index a54aae9ed..6fa7bcb42 100644 --- a/linux/drivers/media/video/cx18/cx18-cards.h +++ b/linux/drivers/media/video/cx18/cx18-cards.h @@ -48,8 +48,9 @@ /* V4L2 capability aliases */ #define CX18_CAP_ENCODER (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | \ - V4L2_CAP_AUDIO | V4L2_CAP_READWRITE) -/* | V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE) not yet */ + V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | \ + V4L2_CAP_VBI_CAPTURE) +/* | V4L2_CAP_SLICED_VBI_CAPTURE) not yet */ struct cx18_card_video_input { u8 video_type; /* video input type */ diff --git a/linux/drivers/media/video/cx18/cx18-driver.c b/linux/drivers/media/video/cx18/cx18-driver.c index 6f1654945..cea4735b6 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.c +++ b/linux/drivers/media/video/cx18/cx18-driver.c @@ -601,13 +601,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; } @@ -640,6 +674,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/linux/drivers/media/video/cx18/cx18-fileops.c b/linux/drivers/media/video/cx18/cx18-fileops.c index ade4c66ba..ef4589ef6 100644 --- a/linux/drivers/media/video/cx18/cx18-fileops.c +++ b/linux/drivers/media/video/cx18/cx18-fileops.c @@ -223,7 +223,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/linux/drivers/media/video/cx18/cx18-streams.c b/linux/drivers/media/video/cx18/cx18-streams.c index 081131fab..9ed542624 100644 --- a/linux/drivers/media/video/cx18/cx18-streams.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-vbi.c b/linux/drivers/media/video/cx18/cx18-vbi.c index 03f0e8130..fb595bd54 100644 --- a/linux/drivers/media/video/cx18/cx18-vbi.c +++ b/linux/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 4d8582967a124a215baac71664035b0d6d4a346f Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 25 Nov 2008 22:26:38 +0100 Subject: uvcvideo: Prevent compat.h from being included in userspace code. From: Laurent Pinchart When used in userspace code, the uvcvideo.h header shouldn't pull compat.h. Make sure this won't happen by moving the #include to a __KERNEL__ protected section. Priority: normal Signed-off-by: Laurent Pinchart --- linux/drivers/media/video/uvc/uvcvideo.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/uvc/uvcvideo.h b/linux/drivers/media/video/uvc/uvcvideo.h index f95040380..09b169b69 100644 --- a/linux/drivers/media/video/uvc/uvcvideo.h +++ b/linux/drivers/media/video/uvc/uvcvideo.h @@ -4,8 +4,6 @@ #include #include -#include "compat.h" - /* * Dynamic controls */ @@ -69,6 +67,7 @@ struct uvc_xu_control { #ifdef __KERNEL__ #include +#include "compat.h" /* -------------------------------------------------------------------------- * UVC constants -- cgit v1.2.3 From 53397dd1cc3dab09ffcc5805bfa3c22c09b567fc Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Tue, 25 Nov 2008 19:43:05 -0500 Subject: cx18: Change to per CX23418 device work queues for deferrable work handling From: Andy Walls 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. Priority: normal Signed-off-by: Andy Walls --- linux/drivers/media/video/cx18/cx18-driver.c | 33 ++++++++++----------------- linux/drivers/media/video/cx18/cx18-driver.h | 2 +- linux/drivers/media/video/cx18/cx18-mailbox.c | 2 +- 3 files changed, 14 insertions(+), 23 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/cx18/cx18-driver.c b/linux/drivers/media/video/cx18/cx18-driver.c index 1fa9a670b..198fdf9a6 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.c +++ b/linux/drivers/media/video/cx18/cx18-driver.c @@ -56,9 +56,6 @@ struct cx18 *cx18_cards[CX18_MAX_CARDS]; /* Protects cx18_cards_active */ DEFINE_SPINLOCK(cx18_cards_lock); -/* Queue for deferrable IRQ handling work for all cx18 cards in system */ -struct workqueue_struct *cx18_work_queue; - /* add your revision and whatnot here */ static struct pci_device_id cx18_pci_tbl[] __devinitdata = { {PCI_VENDOR_ID_CX, PCI_DEVICE_ID_CX23418, @@ -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; @@ -660,12 +663,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); @@ -835,6 +835,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; @@ -943,6 +944,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); @@ -984,17 +987,8 @@ static int module_start(void) printk(KERN_INFO "cx18: Debug value must be >= 0 and <= 511!\n"); } - cx18_work_queue = create_singlethread_workqueue("cx18"); - if (cx18_work_queue == NULL) { - printk(KERN_ERR - "cx18: Unable to create work hander thread\n"); - return -ENOMEM; - } - if (pci_register_driver(&cx18_pci_driver)) { printk(KERN_ERR "cx18: Error detecting PCI card\n"); - destroy_workqueue(cx18_work_queue); - cx18_work_queue = NULL; return -ENODEV; } printk(KERN_INFO "cx18: End initialization\n"); @@ -1007,9 +1001,6 @@ static void module_cleanup(void) pci_unregister_driver(&cx18_pci_driver); - destroy_workqueue(cx18_work_queue); - cx18_work_queue = NULL; - for (i = 0; i < cx18_cards_active; i++) { if (cx18_cards[i] == NULL) continue; diff --git a/linux/drivers/media/video/cx18/cx18-driver.h b/linux/drivers/media/video/cx18/cx18-driver.h index ca1f43781..94c196a91 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.h +++ b/linux/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/linux/drivers/media/video/cx18/cx18-mailbox.c b/linux/drivers/media/video/cx18/cx18-mailbox.c index 8415b9683..f62aee719 100644 --- a/linux/drivers/media/video/cx18/cx18-mailbox.c +++ b/linux/drivers/media/video/cx18/cx18-mailbox.c @@ -460,7 +460,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 96b9b00e9e8f7771c45723ec0c869db9f2260585 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Tue, 25 Nov 2008 20:02:45 -0500 Subject: cx18: Change work_queue teardown to work for kernels earlier than 2.6.22 From: Andy Walls For kernels earlier than 2.6.22, we can't cancel work, so we have to flush the work queue to completion before destroying it. Priority: normal Signed-off-by: Andy Walls --- linux/drivers/media/video/cx18/cx18-driver.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'linux') diff --git a/linux/drivers/media/video/cx18/cx18-driver.c b/linux/drivers/media/video/cx18/cx18-driver.c index 198fdf9a6..57fcce84b 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.c +++ b/linux/drivers/media/video/cx18/cx18-driver.c @@ -918,6 +918,7 @@ int cx18_init_on_first_open(struct cx18 *cx) return 0; } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) static void cx18_cancel_epu_work_orders(struct cx18 *cx) { int i; @@ -925,6 +926,7 @@ static void cx18_cancel_epu_work_orders(struct cx18 *cx) cancel_work_sync(&cx->epu_work_order[i].work); } +#endif static void cx18_remove(struct pci_dev *pci_dev) { struct cx18 *cx = pci_get_drvdata(pci_dev); @@ -938,11 +940,20 @@ static void cx18_remove(struct pci_dev *pci_dev) /* Interrupts */ cx18_sw1_irq_disable(cx, IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) cx18_sw2_irq_disable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK); cx18_halt_firmware(cx); cx18_cancel_epu_work_orders(cx); +#else + + flush_workqueue(cx->work_queue); + + cx18_sw2_irq_disable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK); + + cx18_halt_firmware(cx); +#endif destroy_workqueue(cx->work_queue); -- cgit v1.2.3 From d6fee5ac91e4b7196341e6da1708ce5b4481cd79 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Wed, 26 Nov 2008 08:46:15 +0100 Subject: gspca: Change the colors and add the red and blue controls in sonixj. From: Jean-Francois Moine The colors control (saturation) acted as color balance. Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/sonixj.c | 117 ++++++++++++++++++++++++++----- 1 file changed, 99 insertions(+), 18 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/sonixj.c b/linux/drivers/media/video/gspca/sonixj.c index 9d9f90669..8037a4ec9 100644 --- a/linux/drivers/media/video/gspca/sonixj.c +++ b/linux/drivers/media/video/gspca/sonixj.c @@ -41,6 +41,8 @@ struct sd { unsigned char contrast; unsigned char colors; unsigned char autogain; + __u8 blue; + __u8 red; __u8 vflip; /* ov7630 only */ __u8 infrared; /* mi0360 only */ @@ -72,6 +74,10 @@ static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val); static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setblue_balance(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getblue_balance(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setred_balance(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getred_balance(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val); @@ -116,7 +122,7 @@ static struct ctrl sd_ctrls[] = { .type = V4L2_CTRL_TYPE_INTEGER, .name = "Color", .minimum = 0, - .maximum = 64, + .maximum = 40, .step = 1, #define COLOR_DEF 32 .default_value = COLOR_DEF, @@ -124,7 +130,35 @@ static struct ctrl sd_ctrls[] = { .set = sd_setcolors, .get = sd_getcolors, }, -#define AUTOGAIN_IDX 3 + { + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Blue Balance", + .minimum = 24, + .maximum = 40, + .step = 1, +#define BLUE_BALANCE_DEF 32 + .default_value = BLUE_BALANCE_DEF, + }, + .set = sd_setblue_balance, + .get = sd_getblue_balance, + }, + { + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Red Balance", + .minimum = 24, + .maximum = 40, + .step = 1, +#define RED_BALANCE_DEF 32 + .default_value = RED_BALANCE_DEF, + }, + .set = sd_setred_balance, + .get = sd_getred_balance, + }, +#define AUTOGAIN_IDX 5 { { .id = V4L2_CID_AUTOGAIN, @@ -140,7 +174,7 @@ static struct ctrl sd_ctrls[] = { .get = sd_getautogain, }, /* ov7630 only */ -#define VFLIP_IDX 4 +#define VFLIP_IDX 6 { { .id = V4L2_CID_VFLIP, @@ -156,7 +190,7 @@ static struct ctrl sd_ctrls[] = { .get = sd_getvflip, }, /* mi0360 only */ -#define INFRARED_IDX 5 +#define INFRARED_IDX 7 { { .id = V4L2_CID_INFRARED, @@ -989,6 +1023,8 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->brightness = BRIGHTNESS_DEF; sd->contrast = CONTRAST_DEF; sd->colors = COLOR_DEF; + sd->blue = BLUE_BALANCE_DEF; + sd->red = RED_BALANCE_DEF; sd->autogain = AUTOGAIN_DEF; sd->ag_cnt = -1; sd->vflip = VFLIP_DEF; @@ -1183,18 +1219,27 @@ static void setcontrast(struct gspca_dev *gspca_dev) static void setcolors(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - __u8 blue, red; - - if (sd->colors >= 32) { - red = 32 + (sd->colors - 32) / 2; - blue = 64 - sd->colors; - } else { - red = sd->colors; - blue = 32 + (32 - sd->colors) / 2; + int i, v; + __u8 rega0[12]; /* U & V gains */ + static __s16 uv[6] = { /* same as reg84 in signed decimal */ + -24, -38, 64, /* UR UG UB */ + 62, -51, -9 /* VR VG VB */ + }; + for (i = 0; i < 6; i++) { + v = uv[i] * sd->colors / COLOR_DEF; + rega0[i * 2] = v; + rega0[i * 2 + 1] = (v >> 8) & 0x0f; } - reg_w1(gspca_dev, 0x05, red); + reg_w(gspca_dev, 0x84, rega0, sizeof rega0); +} + +static void setredblue(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + reg_w1(gspca_dev, 0x05, sd->red); /* reg_w1(gspca_dev, 0x07, 32); */ - reg_w1(gspca_dev, 0x06, blue); + reg_w1(gspca_dev, 0x06, sd->blue); } static void setautogain(struct gspca_dev *gspca_dev) @@ -1280,9 +1325,9 @@ static int sd_start(struct gspca_dev *gspca_dev) } reg_w1(gspca_dev, 0x17, reg17); /* set reg1 was here */ - reg_w1(gspca_dev, 0x05, sn9c1xx[5]); - reg_w1(gspca_dev, 0x07, sn9c1xx[7]); - reg_w1(gspca_dev, 0x06, sn9c1xx[6]); + reg_w1(gspca_dev, 0x05, sn9c1xx[5]); /* red */ + reg_w1(gspca_dev, 0x07, sn9c1xx[7]); /* green */ + reg_w1(gspca_dev, 0x06, sn9c1xx[6]); /* blue */ reg_w1(gspca_dev, 0x14, sn9c1xx[0x14]); reg_w(gspca_dev, 0x20, gamma_def, sizeof gamma_def); for (i = 0; i < 8; i++) @@ -1483,7 +1528,7 @@ static void do_autogain(struct gspca_dev *gspca_dev) expotimes = 0; sd->exposure = setexposure(gspca_dev, (unsigned int) expotimes); - setcolors(gspca_dev); + setredblue(gspca_dev); break; } } @@ -1586,6 +1631,42 @@ static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) return 0; } +static int sd_setblue_balance(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->blue = val; + if (gspca_dev->streaming) + setredblue(gspca_dev); + return 0; +} + +static int sd_getblue_balance(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->blue; + return 0; +} + +static int sd_setred_balance(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->red = val; + if (gspca_dev->streaming) + setredblue(gspca_dev); + return 0; +} + +static int sd_getred_balance(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->red; + return 0; +} + static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; -- cgit v1.2.3 From 5e8806761e3146cdf8f1c400b2ff0090bfb6d2f0 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Wed, 26 Nov 2008 20:17:13 +0100 Subject: gspca: Add sensor mi0360 in vc032x. From: Jean-Francois Moine Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/vc032x.c | 477 ++++++++++++++++++------------- 1 file changed, 271 insertions(+), 206 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/vc032x.c b/linux/drivers/media/video/gspca/vc032x.c index f6fae60ae..db5deb957 100644 --- a/linux/drivers/media/video/gspca/vc032x.c +++ b/linux/drivers/media/video/gspca/vc032x.c @@ -41,11 +41,12 @@ struct sd { #define BRIDGE_VC0323 1 char sensor; #define SENSOR_HV7131R 0 -#define SENSOR_MI1320 1 -#define SENSOR_MI1310_SOC 2 -#define SENSOR_OV7660 3 -#define SENSOR_OV7670 4 -#define SENSOR_PO3130NC 5 +#define SENSOR_MI0360 1 +#define SENSOR_MI1320 2 +#define SENSOR_MI1310_SOC 3 +#define SENSOR_OV7660 4 +#define SENSOR_OV7670 5 +#define SENSOR_PO3130NC 6 }; /* V4L2 controls supported by the driver */ @@ -111,15 +112,239 @@ static struct v4l2_pix_format vc0323_mode[] = { .priv = 0}, }; -#if 0 -static const __u8 mi1310soc_gamma[17] = { - 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8, - 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff +static const __u8 mi0360_matrix[9] = { + 0x50, 0xf8, 0xf8, 0xf5, 0x50, 0xfb, 0xff, 0xf1, 0x50 }; -static const __u8 mi1310soc_matrix[9] = { - 0x56, 0xf0, 0xf6, 0xf3, 0x57, 0xf6, 0xf3, 0xef, 0x58 + +static const __u8 mi0360_initVGA_JPG[][4] = { + {0xb0, 0x03, 0x19, 0xcc}, + {0xb0, 0x04, 0x02, 0xcc}, + {0xb3, 0x00, 0x24, 0xcc}, + {0xb3, 0x00, 0x25, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x05, 0x01, 0xcc}, + {0xb3, 0x06, 0x03, 0xcc}, + {0xb3, 0x03, 0x0a, 0xcc}, + {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x22, 0x01, 0xcc}, + {0xb3, 0x23, 0xe0, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, + {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x02, 0xcc}, + {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x35, 0xdd, 0xcc}, + {0xb3, 0x34, 0x02, 0xcc}, + {0xb3, 0x00, 0x25, 0xcc}, + {0xbc, 0x00, 0x71, 0xcc}, + {0xb8, 0x00, 0x13, 0xcc}, + {0xb8, 0x27, 0x20, 0xcc}, + {0xb8, 0x2c, 0x50, 0xcc}, + {0xb8, 0x2d, 0xf8, 0xcc}, + {0xb8, 0x2e, 0xf8, 0xcc}, + {0xb8, 0x2f, 0xf8, 0xcc}, + {0xb8, 0x30, 0x50, 0xcc}, + {0xb8, 0x31, 0xf8, 0xcc}, + {0xb8, 0x32, 0xf8, 0xcc}, + {0xb8, 0x33, 0xf8, 0xcc}, + {0xb8, 0x34, 0x50, 0xcc}, + {0xb8, 0x35, 0x00, 0xcc}, + {0xb8, 0x36, 0x00, 0xcc}, + {0xb8, 0x37, 0x00, 0xcc}, + {0xb8, 0x01, 0x79, 0xcc}, + {0xb8, 0x08, 0xe0, 0xcc}, + {0xb3, 0x01, 0x41, 0xcc}, + {0xb8, 0x01, 0x79, 0xcc}, + {0xb8, 0x14, 0x18, 0xcc}, + {0xb8, 0xb2, 0x0a, 0xcc}, + {0xb8, 0xb4, 0x0a, 0xcc}, + {0xb8, 0xb5, 0x0a, 0xcc}, + {0xb8, 0xfe, 0x00, 0xcc}, + {0xb8, 0xff, 0x28, 0xcc}, + {0xb9, 0x00, 0x28, 0xcc}, + {0xb9, 0x01, 0x28, 0xcc}, + {0xb9, 0x02, 0x28, 0xcc}, + {0xb9, 0x03, 0x00, 0xcc}, + {0xb9, 0x04, 0x00, 0xcc}, + {0xb9, 0x05, 0x3c, 0xcc}, + {0xb9, 0x06, 0x3c, 0xcc}, + {0xb9, 0x07, 0x3c, 0xcc}, + {0xb9, 0x08, 0x3c, 0xcc}, + {0xb8, 0x8e, 0x00, 0xcc}, + {0xb8, 0x8f, 0xff, 0xcc}, + {0xb8, 0x81, 0x09, 0xcc}, + {0x31, 0x00, 0x00, 0xbb}, + {0x09, 0x01, 0xc7, 0xbb}, + {0x34, 0x01, 0x00, 0xbb}, + {0x2b, 0x00, 0x28, 0xbb}, + {0x2c, 0x00, 0x30, 0xbb}, + {0x2d, 0x00, 0x30, 0xbb}, + {0x2e, 0x00, 0x28, 0xbb}, + {0x62, 0x04, 0x11, 0xbb}, + {0x03, 0x01, 0xe0, 0xbb}, + {0x2c, 0x00, 0x2c, 0xbb}, + {0x20, 0xd0, 0x00, 0xbb}, + {0x01, 0x00, 0x08, 0xbb}, + {0x06, 0x00, 0x10, 0xbb}, + {0x05, 0x00, 0x20, 0xbb}, + {0x20, 0x00, 0x00, 0xbb}, + {0xb6, 0x00, 0x00, 0xcc}, + {0xb6, 0x03, 0x02, 0xcc}, + {0xb6, 0x02, 0x80, 0xcc}, + {0xb6, 0x05, 0x01, 0xcc}, + {0xb6, 0x04, 0xe0, 0xcc}, + {0xb6, 0x12, 0x78, 0xcc}, + {0xb6, 0x18, 0x02, 0xcc}, + {0xb6, 0x17, 0x58, 0xcc}, + {0xb6, 0x16, 0x00, 0xcc}, + {0xb6, 0x22, 0x12, 0xcc}, + {0xb6, 0x23, 0x0b, 0xcc}, + {0xb3, 0x02, 0x02, 0xcc}, + {0xbf, 0xc0, 0x39, 0xcc}, + {0xbf, 0xc1, 0x04, 0xcc}, + {0xbf, 0xcc, 0x10, 0xcc}, + {0xb9, 0x12, 0x00, 0xcc}, + {0xb9, 0x13, 0x0a, 0xcc}, + {0xb9, 0x14, 0x0a, 0xcc}, + {0xb9, 0x15, 0x0a, 0xcc}, + {0xb9, 0x16, 0x0a, 0xcc}, + {0xb9, 0x18, 0x00, 0xcc}, + {0xb9, 0x19, 0x0f, 0xcc}, + {0xb9, 0x1a, 0x0f, 0xcc}, + {0xb9, 0x1b, 0x0f, 0xcc}, + {0xb9, 0x1c, 0x0f, 0xcc}, + {0xb8, 0x8e, 0x00, 0xcc}, + {0xb8, 0x8f, 0xff, 0xcc}, + {0xb6, 0x12, 0xf8, 0xcc}, + {0xb8, 0x0c, 0x20, 0xcc}, + {0xb8, 0x0d, 0x70, 0xcc}, + {0xb6, 0x13, 0x13, 0xcc}, + {0x35, 0x00, 0x60, 0xbb}, + {0xb3, 0x5c, 0x01, 0xcc}, + {} }; -#endif +static const __u8 mi0360_initQVGA_JPG[][4] = { + {0xb0, 0x03, 0x19, 0xcc}, + {0xb0, 0x04, 0x02, 0xcc}, + {0xb3, 0x00, 0x24, 0xcc}, + {0xb3, 0x00, 0x25, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x05, 0x01, 0xcc}, + {0xb3, 0x06, 0x03, 0xcc}, + {0xb3, 0x03, 0x0a, 0xcc}, + {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x22, 0x01, 0xcc}, + {0xb3, 0x23, 0xe0, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, + {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x02, 0xcc}, + {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x35, 0xdd, 0xcc}, + {0xb3, 0x34, 0x02, 0xcc}, + {0xb3, 0x00, 0x25, 0xcc}, + {0xbc, 0x00, 0xd1, 0xcc}, + {0xb8, 0x00, 0x13, 0xcc}, + {0xb8, 0x27, 0x20, 0xcc}, + {0xb8, 0x2c, 0x50, 0xcc}, + {0xb8, 0x2d, 0xf8, 0xcc}, + {0xb8, 0x2e, 0xf8, 0xcc}, + {0xb8, 0x2f, 0xf8, 0xcc}, + {0xb8, 0x30, 0x50, 0xcc}, + {0xb8, 0x31, 0xf8, 0xcc}, + {0xb8, 0x32, 0xf8, 0xcc}, + {0xb8, 0x33, 0xf8, 0xcc}, + {0xb8, 0x34, 0x50, 0xcc}, + {0xb8, 0x35, 0x00, 0xcc}, + {0xb8, 0x36, 0x00, 0xcc}, + {0xb8, 0x37, 0x00, 0xcc}, + {0xb8, 0x01, 0x79, 0xcc}, + {0xb8, 0x08, 0xe0, 0xcc}, + {0xb3, 0x01, 0x41, 0xcc}, + {0xb8, 0x01, 0x79, 0xcc}, + {0xb8, 0x14, 0x18, 0xcc}, + {0xb8, 0xb2, 0x0a, 0xcc}, + {0xb8, 0xb4, 0x0a, 0xcc}, + {0xb8, 0xb5, 0x0a, 0xcc}, + {0xb8, 0xfe, 0x00, 0xcc}, + {0xb8, 0xff, 0x28, 0xcc}, + {0xb9, 0x00, 0x28, 0xcc}, + {0xb9, 0x01, 0x28, 0xcc}, + {0xb9, 0x02, 0x28, 0xcc}, + {0xb9, 0x03, 0x00, 0xcc}, + {0xb9, 0x04, 0x00, 0xcc}, + {0xb9, 0x05, 0x3c, 0xcc}, + {0xb9, 0x06, 0x3c, 0xcc}, + {0xb9, 0x07, 0x3c, 0xcc}, + {0xb9, 0x08, 0x3c, 0xcc}, + {0xb8, 0x8e, 0x00, 0xcc}, + {0xb8, 0x8f, 0xff, 0xcc}, + {0xb8, 0x81, 0x09, 0xcc}, + {0x31, 0x00, 0x00, 0xbb}, + {0x09, 0x01, 0xc7, 0xbb}, + {0x34, 0x01, 0x00, 0xbb}, + {0x2b, 0x00, 0x28, 0xbb}, + {0x2c, 0x00, 0x30, 0xbb}, + {0x2d, 0x00, 0x30, 0xbb}, + {0x2e, 0x00, 0x28, 0xbb}, + {0x62, 0x04, 0x11, 0xbb}, + {0x03, 0x01, 0xe0, 0xbb}, + {0x2c, 0x00, 0x2c, 0xbb}, + {0x20, 0xd0, 0x00, 0xbb}, + {0x01, 0x00, 0x08, 0xbb}, + {0x06, 0x00, 0x10, 0xbb}, + {0x05, 0x00, 0x20, 0xbb}, + {0x20, 0x00, 0x00, 0xbb}, + {0xb6, 0x00, 0x00, 0xcc}, + {0xb6, 0x03, 0x01, 0xcc}, + {0xb6, 0x02, 0x40, 0xcc}, + {0xb6, 0x05, 0x00, 0xcc}, + {0xb6, 0x04, 0xf0, 0xcc}, + {0xb6, 0x12, 0x78, 0xcc}, + {0xb6, 0x18, 0x00, 0xcc}, + {0xb6, 0x17, 0x96, 0xcc}, + {0xb6, 0x16, 0x00, 0xcc}, + {0xb6, 0x22, 0x12, 0xcc}, + {0xb6, 0x23, 0x0b, 0xcc}, + {0xb3, 0x02, 0x02, 0xcc}, + {0xbf, 0xc0, 0x39, 0xcc}, + {0xbf, 0xc1, 0x04, 0xcc}, + {0xbf, 0xcc, 0x10, 0xcc}, + {0xb9, 0x12, 0x00, 0xcc}, + {0xb9, 0x13, 0x0a, 0xcc}, + {0xb9, 0x14, 0x0a, 0xcc}, + {0xb9, 0x15, 0x0a, 0xcc}, + {0xb9, 0x16, 0x0a, 0xcc}, + {0xb9, 0x18, 0x00, 0xcc}, + {0xb9, 0x19, 0x0f, 0xcc}, + {0xb9, 0x1a, 0x0f, 0xcc}, + {0xb9, 0x1b, 0x0f, 0xcc}, + {0xb9, 0x1c, 0x0f, 0xcc}, + {0xb8, 0x8e, 0x00, 0xcc}, + {0xb8, 0x8f, 0xff, 0xcc}, + {0xb6, 0x12, 0xf8, 0xcc}, + {0xb6, 0x13, 0x13, 0xcc}, + {0xbc, 0x02, 0x18, 0xcc}, + {0xbc, 0x03, 0x50, 0xcc}, + {0xbc, 0x04, 0x18, 0xcc}, + {0xbc, 0x05, 0x00, 0xcc}, + {0xbc, 0x06, 0x00, 0xcc}, + {0xbc, 0x08, 0x30, 0xcc}, + {0xbc, 0x09, 0x40, 0xcc}, + {0xbc, 0x0a, 0x10, 0xcc}, + {0xb8, 0x0c, 0x20, 0xcc}, + {0xb8, 0x0d, 0x70, 0xcc}, + {0xbc, 0x0b, 0x00, 0xcc}, + {0xbc, 0x0c, 0x00, 0xcc}, + {0x35, 0x00, 0xef, 0xbb}, + {0xb3, 0x5c, 0x01, 0xcc}, + {} +}; + static const __u8 mi1310_socinitVGA_JPG[][4] = { {0xb0, 0x03, 0x19, 0xcc}, {0xb0, 0x04, 0x02, 0xcc}, @@ -1212,178 +1437,6 @@ static const __u8 ov7670_initQVGA_JPG[][4] = { {0x00, 0x77, 0x05, 0xaa }, {}, }; -#if 0 -static const __u8 ov7670_initQVGA_JPG[][4] = { - {0xb3, 0x00, 0x00, 0xcc}, - {0xb0, 0x16, 0x0d, 0xcc}, - {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x16, 0x00, 0xcc}, - {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x19, 0xcc}, - {0x00, 0x00, 0x50, 0xdd}, {0xb3, 0x49, 0x11, 0xcc}, - {0xb3, 0x49, 0x00, 0xcc}, {0xb0, 0x16, 0x00, 0xcc}, - - {0xb3, 0x01, 0x05, 0xcc}, {0x00, 0x00, 0x50, 0xdd}, - {0xb0, 0x03, 0x19, 0xcc}, {0xb0, 0x04, 0x02, 0xcc}, - {0x00, 0x00, 0x50, 0xdd}, {0xb3, 0x00, 0x66, 0xcc}, - {0xb3, 0x00, 0x67, 0xcc}, {0xb3, 0x35, 0xa1, 0xcc}, - {0xb3, 0x34, 0x01, 0xcc}, {0xb3, 0x05, 0x01, 0xcc}, - {0xb3, 0x06, 0x01, 0xcc}, {0xb3, 0x08, 0x01, 0xcc}, - {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x02, 0x02, 0xcc}, - {0xb3, 0x03, 0x1f, 0xcc}, {0xb3, 0x14, 0x00, 0xcc}, - {0xb3, 0x15, 0x00, 0xcc}, {0xb3, 0x16, 0x02, 0xcc}, - {0xb3, 0x17, 0x7f, 0xcc}, {0xb3, 0x04, 0x05, 0xcc}, - {0xb3, 0x20, 0x00, 0xcc}, {0xb3, 0x21, 0x00, 0xcc}, - {0xb3, 0x22, 0x01, 0xcc}, {0xb3, 0x23, 0xe0, 0xcc}, - {0xbc, 0x00, 0xd1, 0xcc}, /* set QVGA */ - {0xbc, 0x01, 0x01, 0xcc},/* */ - {0x00, 0x12, 0x80, 0xaa},/* OV sensor reset */ - {0x00, 0x00, 0x50, 0xdd},/* wait sometimes */ - {0, 0x12, 0x00, 0xaa}, - {0, 0x11, 0x40, 0xaa}, {0, 0x6b, 0x0a, 0xaa}, - {0, 0x3a, 0x04, 0xaa}, {0, 0x40, 0xc0, 0xaa}, - {0, 0x8c, 0x00, 0xaa}, {0, 0x7a, 0x29, 0xaa}, - {0, 0x7b, 0x0e, 0xaa}, {0, 0x7c, 0x1a, 0xaa}, - {0, 0x7d, 0x31, 0xaa}, {0, 0x7e, 0x53, 0xaa}, - {0, 0x7f, 0x60, 0xaa}, {0, 0x80, 0x6b, 0xaa}, - {0, 0x81, 0x73, 0xaa}, {0, 0x82, 0x7b, 0xaa}, - {0, 0x83, 0x82, 0xaa}, {0, 0x84, 0x89, 0xaa}, - {0, 0x85, 0x96, 0xaa}, {0, 0x86, 0xa1, 0xaa}, - {0, 0x87, 0xb7, 0xaa}, {0, 0x88, 0xcc, 0xaa}, - {0, 0x89, 0xe1, 0xaa}, {0, 0x13, 0xe0, 0xaa}, - {0, 0x00, 0x00, 0xaa}, {0, 0x10, 0x00, 0xaa}, - {0, 0x0d, 0x40, 0xaa}, {0, 0x14, 0x28, 0xaa}, - {0, 0xa5, 0x05, 0xaa}, {0, 0xab, 0x07, 0xaa}, - {0, 0x24, 0x95, 0xaa}, {0, 0x25, 0x33, 0xaa}, - {0, 0x26, 0xe3, 0xaa}, {0, 0x9f, 0x88, 0xaa}, - {0, 0xa0, 0x78, 0xaa}, {0, 0x55, 0x90, 0xaa}, - {0, 0xa1, 0x03, 0xaa}, {0, 0xa6, 0xe0, 0xaa}, - {0, 0xa7, 0xd8, 0xaa}, {0, 0xa8, 0xf0, 0xaa}, - {0, 0xa9, 0x90, 0xaa}, {0, 0xaa, 0x14, 0xaa}, - {0, 0x13, 0xe5, 0xaa}, {0, 0x0e, 0x61, 0xaa}, - {0, 0x0f, 0x4b, 0xaa}, {0, 0x16, 0x02, 0xaa}, - {0, 0x1e, 0x07, 0xaa}, {0, 0x21, 0x02, 0xaa}, - {0, 0x22, 0x91, 0xaa}, {0, 0x29, 0x07, 0xaa}, - {0, 0x33, 0x0b, 0xaa}, {0, 0x35, 0x0b, 0xaa}, - {0, 0x37, 0x1d, 0xaa}, {0, 0x38, 0x71, 0xaa}, - {0, 0x39, 0x2a, 0xaa}, {0, 0x3c, 0x78, 0xaa}, - {0, 0x4d, 0x40, 0xaa}, {0, 0x4e, 0x20, 0xaa}, - {0, 0x74, 0x19, 0xaa}, {0, 0x8d, 0x4f, 0xaa}, - {0, 0x8e, 0x00, 0xaa}, {0, 0x8f, 0x00, 0xaa}, - {0, 0x90, 0x00, 0xaa}, {0, 0x91, 0x00, 0xaa}, - {0, 0x96, 0x00, 0xaa}, {0, 0x9a, 0x80, 0xaa}, - {0, 0xb0, 0x84, 0xaa}, {0, 0xb1, 0x0c, 0xaa}, - {0, 0xb2, 0x0e, 0xaa}, {0, 0xb3, 0x82, 0xaa}, - {0, 0xb8, 0x0a, 0xaa}, {0, 0x43, 0x14, 0xaa}, - {0, 0x44, 0xf0, 0xaa}, {0, 0x45, 0x45, 0xaa}, - {0, 0x46, 0x63, 0xaa}, {0, 0x47, 0x2d, 0xaa}, - {0, 0x48, 0x46, 0xaa}, {0, 0x59, 0x88, 0xaa}, - {0, 0x5a, 0xa0, 0xaa}, {0, 0x5b, 0xc6, 0xaa}, - {0, 0x5c, 0x7d, 0xaa}, {0, 0x5d, 0x5f, 0xaa}, - {0, 0x5e, 0x19, 0xaa}, {0, 0x6c, 0x0a, 0xaa}, - {0, 0x6d, 0x55, 0xaa}, {0, 0x6e, 0x11, 0xaa}, - {0, 0x6f, 0x9e, 0xaa}, {0, 0x69, 0x00, 0xaa}, - {0, 0x6a, 0x40, 0xaa}, {0, 0x01, 0x40, 0xaa}, - {0, 0x02, 0x40, 0xaa}, {0, 0x13, 0xe7, 0xaa}, - {0, 0x5f, 0xf0, 0xaa}, {0, 0x60, 0xf0, 0xaa}, - {0, 0x61, 0xf0, 0xaa}, {0, 0x27, 0xa0, 0xaa}, - {0, 0x28, 0x80, 0xaa}, {0, 0x2c, 0x90, 0xaa}, - {0, 0x4f, 0x66, 0xaa}, {0, 0x50, 0x66, 0xaa}, - {0, 0x51, 0x00, 0xaa}, {0, 0x52, 0x22, 0xaa}, - {0, 0x53, 0x5e, 0xaa}, {0, 0x54, 0x80, 0xaa}, - {0, 0x58, 0x9e, 0xaa}, {0, 0x41, 0x08, 0xaa}, - {0, 0x3f, 0x00, 0xaa}, {0, 0x75, 0x85, 0xaa}, - {0, 0x76, 0xe1, 0xaa}, {0, 0x4c, 0x00, 0xaa}, - {0, 0x77, 0x0a, 0xaa}, {0, 0x3d, 0x88, 0xaa}, - {0, 0x4b, 0x09, 0xaa}, {0, 0xc9, 0x60, 0xaa}, - {0, 0x41, 0x38, 0xaa}, {0, 0x62, 0x30, 0xaa}, - {0, 0x63, 0x30, 0xaa}, {0, 0x64, 0x08, 0xaa}, - {0, 0x94, 0x07, 0xaa}, {0, 0x95, 0x0b, 0xaa}, - {0, 0x65, 0x00, 0xaa}, {0, 0x66, 0x05, 0xaa}, - {0, 0x56, 0x50, 0xaa}, {0, 0x34, 0x11, 0xaa}, - {0, 0xa4, 0x88, 0xaa}, {0, 0x96, 0x00, 0xaa}, - {0, 0x97, 0x30, 0xaa}, {0, 0x98, 0x20, 0xaa}, - {0, 0x99, 0x30, 0xaa}, {0, 0x9a, 0x84, 0xaa}, - {0, 0x9b, 0x29, 0xaa}, {0, 0x9c, 0x03, 0xaa}, - {0, 0x78, 0x04, 0xaa}, {0, 0x79, 0x01, 0xaa}, - {0, 0xc8, 0xf0, 0xaa}, {0, 0x79, 0x0f, 0xaa}, - {0, 0xc8, 0x00, 0xaa}, {0, 0x79, 0x10, 0xaa}, - {0, 0xc8, 0x7e, 0xaa}, {0, 0x79, 0x0a, 0xaa}, - {0, 0xc8, 0x80, 0xaa}, {0, 0x79, 0x0b, 0xaa}, - {0, 0xc8, 0x01, 0xaa}, {0, 0x79, 0x0c, 0xaa}, - {0, 0xc8, 0x0f, 0xaa}, {0, 0x79, 0x0d, 0xaa}, - {0, 0xc8, 0x20, 0xaa}, {0, 0x79, 0x09, 0xaa}, - {0, 0xc8, 0x80, 0xaa}, {0, 0x79, 0x02, 0xaa}, - {0, 0xc8, 0xc0, 0xaa}, {0, 0x79, 0x03, 0xaa}, - {0, 0xc8, 0x40, 0xaa}, {0, 0x79, 0x05, 0xaa}, - {0, 0xc8, 0x30, 0xaa}, {0, 0x79, 0x26, 0xaa}, - {0, 0x11, 0x40, 0xaa}, {0, 0x3a, 0x04, 0xaa}, - {0, 0x12, 0x00, 0xaa}, {0, 0x40, 0xc0, 0xaa}, - {0, 0x8c, 0x00, 0xaa}, {0, 0x17, 0x14, 0xaa}, - {0, 0x18, 0x02, 0xaa}, {0, 0x32, 0x92, 0xaa}, - {0, 0x19, 0x02, 0xaa}, {0, 0x1a, 0x7a, 0xaa}, - {0, 0x03, 0x0a, 0xaa}, {0, 0x0c, 0x00, 0xaa}, - {0, 0x3e, 0x00, 0xaa}, {0, 0x70, 0x3a, 0xaa}, - {0, 0x71, 0x35, 0xaa}, {0, 0x72, 0x11, 0xaa}, - {0, 0x73, 0xf0, 0xaa}, {0, 0xa2, 0x02, 0xaa}, - {0, 0xb1, 0x00, 0xaa}, {0, 0xb1, 0x0c, 0xaa}, - {0, 0x1e, 0x37, 0xaa}, {0, 0xaa, 0x14, 0xaa}, - {0, 0x24, 0x80, 0xaa}, {0, 0x25, 0x74, 0xaa}, - {0, 0x26, 0xd3, 0xaa}, {0, 0x0d, 0x00, 0xaa}, - {0, 0x14, 0x18, 0xaa}, {0, 0x9d, 0x99, 0xaa}, - {0, 0x9e, 0x7f, 0xaa}, {0, 0x64, 0x08, 0xaa}, - {0, 0x94, 0x07, 0xaa}, {0, 0x95, 0x06, 0xaa}, - {0, 0x66, 0x05, 0xaa}, {0, 0x41, 0x08, 0xaa}, - {0, 0x3f, 0x00, 0xaa}, {0, 0x75, 0x07, 0xaa}, - {0, 0x76, 0xe1, 0xaa}, {0, 0x4c, 0x00, 0xaa}, - {0, 0x77, 0x00, 0xaa}, {0, 0x3d, 0xc2, 0xaa}, - {0, 0x4b, 0x09, 0xaa}, {0, 0xc9, 0x60, 0xaa}, - {0, 0x41, 0x38, 0xaa}, - {0xb6, 0x00, 0x00, 0xcc}, {0xb6, 0x03, 0x01, 0xcc}, - {0xb6, 0x02, 0x40, 0xcc}, - {0xb6, 0x05, 0x00, 0xcc}, {0xb6, 0x04, 0xf0, 0xcc}, - {0xb6, 0x12, 0xf8, 0xcc}, {0xb6, 0x13, 0x21, 0xcc}, - {0xb6, 0x18, 0x00, 0xcc}, {0xb6, 0x17, 0x96, 0xcc}, - {0xb6, 0x16, 0x00, 0xcc}, {0xb6, 0x22, 0x12, 0xcc}, - {0xb6, 0x23, 0x0b, 0xcc}, - {0xbf, 0xc0, 0x39, 0xcc},/* set jpeg */ - {0xbf, 0xc1, 0x04, 0xcc},/* */ - {0xbf, 0xcc, 0x00, 0xcc},/* */ - {0xbc, 0x02, 0x18, 0xcc}, {0xbc, 0x03, 0x50, 0xcc}, - {0xbc, 0x04, 0x18, 0xcc}, {0xbc, 0x05, 0x00, 0xcc}, - {0xbc, 0x06, 0x00, 0xcc}, {0xbc, 0x08, 0x30, 0xcc}, - {0xbc, 0x09, 0x40, 0xcc}, {0xbc, 0x0a, 0x10, 0xcc}, - {0xbc, 0x0b, 0x00, 0xcc}, {0xbc, 0x0c, 0x00, 0xcc}, - {0xb3, 0x5c, 0x01, 0xcc}, {0xb3, 0x01, 0x45, 0xcc}, - {0, 0x77, 0x05, 0xaa}, {0xb6, 0x12, 0xf8, 0xcc}, - {0xb6, 0x13, 0x20, 0xcc}, - {0, 0x24, 0x6c, 0xaa}, - {0, 0x25, 0x5c, 0xaa}, {0, 0x56, 0x50, 0xaa}, - {0, 0x4f, 0xa8, 0xaa}, {0, 0x50, 0xa5, 0xaa}, - {0, 0x51, 0x02, 0xaa}, {0, 0x52, 0x22, 0xaa}, - {0, 0x53, 0x86, 0xaa}, {0, 0x54, 0xaa, 0xaa}, - {0, 0x7a, 0x19, 0xaa}, {0, 0x7b, 0x0c, 0xaa}, - {0, 0x7c, 0x18, 0xaa}, {0, 0x7d, 0x2f, 0xaa}, - {0, 0x7e, 0x54, 0xaa}, {0, 0x7f, 0x64, 0xaa}, - {0, 0x80, 0x71, 0xaa}, {0, 0x81, 0x7d, 0xaa}, - {0, 0x82, 0x88, 0xaa}, {0, 0x83, 0x91, 0xaa}, - {0, 0x84, 0x98, 0xaa}, {0, 0x85, 0xa7, 0xaa}, - {0, 0x86, 0xb4, 0xaa}, {0, 0x87, 0xcb, 0xaa}, - {0, 0x88, 0xde, 0xaa}, {0, 0x89, 0xed, 0xaa}, - {0, 0x75, 0x86, 0xaa}, {0, 0x92, 0x00, 0xaa}, - {0, 0x3b, 0, 0xbb}, - {0, 0x3b, 0x00, 0xaa}, - {0, 0x13, 0, 0xbb}, {0, 0x13, 0xe7, 0xaa}, - {0, 0x2d, 0x00, 0xaa}, {0, 0x2e, 0x00, 0xaa}, - {0, 0x04, 0x00, 0xaa}, {0, 0x10, 0x00, 0xaa}, - {0, 0x3b, 0, 0xbb}, {0, 0x3b, 0x80, 0xaa}, - {0, 0x13, 0, 0xbb}, - {0, 0x13, 0xe7, 0xaa}, - {0, 0x1e, 0x27, 0xaa}, - {0, 0x1e, 0x27, 0xaa}, - {0, 0x3b, 0xc8, 0xaa}, - {} -}; -#endif - struct sensor_info { int sensorId; __u8 I2cAdd; @@ -1402,6 +1455,8 @@ static const struct sensor_info sensor_info_data[] = { {SENSOR_MI1320, 0x80 | 0xc8, 0x00, 0x148c, 0x64, 0x65, 0x01}, {SENSOR_OV7670, 0x80 | 0x21, 0x0a, 0x7673, 0x66, 0x67, 0x05}, {SENSOR_MI1310_SOC, 0x80 | 0x5d, 0x00, 0x143a, 0x24, 0x25, 0x01}, +/* (tested in vc032x_probe_sensor) */ +/* {SENSOR_MI0360, 0x80 | 0x5d, 0x00, 0x4382, 0x24, 0x25, 0x01}, */ }; /* read 'len' bytes in gspca_dev->usb_buf */ @@ -1463,7 +1518,7 @@ static void read_sensor_register(struct gspca_dev *gspca_dev, mdata = gspca_dev->usb_buf[0]; reg_r(gspca_dev, 0xa1, 0xb33c, 1); ldata = gspca_dev->usb_buf[0]; - PDEBUG(D_PROBE, "Read Sensor h (0x%02X) m (0x%02X) l (0x%02X)", + PDEBUG(D_PROBE, "Read Sensor %02x %02x%02x", hdata, mdata, ldata); reg_r(gspca_dev, 0xa1, 0xb334, 1); if (gspca_dev->usb_buf[0] == 0x02) @@ -1480,7 +1535,7 @@ static int vc032x_probe_sensor(struct gspca_dev *gspca_dev) const struct sensor_info *ptsensor_info; reg_r(gspca_dev, 0xa1, 0xbfcf, 1); - PDEBUG(D_PROBE, "check sensor header %d", gspca_dev->usb_buf[0]); + PDEBUG(D_PROBE, "check sensor header %02x", gspca_dev->usb_buf[0]); for (i = 0; i < ARRAY_SIZE(sensor_info_data); i++) { ptsensor_info = &sensor_info_data[i]; reg_w(dev, 0xa0, 0x02, 0xb334); @@ -1489,16 +1544,15 @@ static int vc032x_probe_sensor(struct gspca_dev *gspca_dev) reg_w(dev, 0xa0, 0x01, 0xb308); reg_w(dev, 0xa0, 0x0c, 0xb309); reg_w(dev, 0xa0, ptsensor_info->I2cAdd, 0xb335); -/* PDEBUG(D_PROBE, - "check sensor VC032X -> %d Add -> ox%02X!", - i, ptsensor_info->I2cAdd); */ reg_w(dev, 0xa0, ptsensor_info->op, 0xb301); read_sensor_register(gspca_dev, ptsensor_info->IdAdd, &value); - if (value == ptsensor_info->VpId) { -/* PDEBUG(D_PROBE, "find sensor VC032X -> ox%04X!", - ptsensor_info->VpId); */ + if (value == ptsensor_info->VpId) + return ptsensor_info->sensorId; + + /* special case for MI0360 */ + if (ptsensor_info->sensorId == SENSOR_MI1310_SOC + && value == 0x4382) return ptsensor_info->sensorId; - } } return -1; } @@ -1600,13 +1654,6 @@ static int sd_config(struct gspca_dev *gspca_dev, cam = &gspca_dev->cam; cam->epaddr = 0x02; sd->bridge = id->driver_info; - if (sd->bridge == BRIDGE_VC0321) { - cam->cam_mode = vc0321_mode; - cam->nmodes = ARRAY_SIZE(vc0321_mode); - } else { - cam->cam_mode = vc0323_mode; - cam->nmodes = ARRAY_SIZE(vc0323_mode); - } vc0321_reset(gspca_dev); sensor = vc032x_probe_sensor(gspca_dev); @@ -1616,29 +1663,36 @@ static int sd_config(struct gspca_dev *gspca_dev, return -EINVAL; case SENSOR_HV7131R: PDEBUG(D_PROBE, "Find Sensor HV7131R"); - sd->sensor = SENSOR_HV7131R; + break; + case SENSOR_MI0360: + PDEBUG(D_PROBE, "Find Sensor MI0360"); + sd->bridge = BRIDGE_VC0323; break; case SENSOR_MI1310_SOC: PDEBUG(D_PROBE, "Find Sensor MI1310_SOC"); - sd->sensor = SENSOR_MI1310_SOC; break; case SENSOR_MI1320: PDEBUG(D_PROBE, "Find Sensor MI1320"); - sd->sensor = SENSOR_MI1320; break; case SENSOR_OV7660: PDEBUG(D_PROBE, "Find Sensor OV7660"); - sd->sensor = SENSOR_OV7660; break; case SENSOR_OV7670: PDEBUG(D_PROBE, "Find Sensor OV7670"); - sd->sensor = SENSOR_OV7670; break; case SENSOR_PO3130NC: PDEBUG(D_PROBE, "Find Sensor PO3130NC"); - sd->sensor = SENSOR_PO3130NC; break; } + sd->sensor = sensor; + + if (sd->bridge == BRIDGE_VC0321) { + cam->cam_mode = vc0321_mode; + cam->nmodes = ARRAY_SIZE(vc0321_mode); + } else { + cam->cam_mode = vc0323_mode; + cam->nmodes = ARRAY_SIZE(vc0323_mode); + } sd->qindex = 7; sd->autogain = AUTOGAIN_DEF; @@ -1748,6 +1802,17 @@ static int sd_start(struct gspca_dev *gspca_dev) usb_exchange(gspca_dev, ov7670_initVGA_JPG); } break; + case SENSOR_MI0360: + GammaT = mi1320_gamma; + MatrixT = mi0360_matrix; + if (mode) { + /* 320x240 */ + usb_exchange(gspca_dev, mi0360_initQVGA_JPG); + } else { + /* 640x480 */ + usb_exchange(gspca_dev, mi0360_initVGA_JPG); + } + break; case SENSOR_MI1310_SOC: if (mode) { /* 320x240 */ -- cgit v1.2.3 From 93b293e2a697005bb0eca0f5a792f02f97d61e4f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 27 Nov 2008 08:42:26 -0200 Subject: em28xx: move gpio lines into board table description From: Mauro Carvalho Chehab Instead of a large, ugly switch specifying the gpio tables for each device, let's move it into the boards struct. This also helps to see what boards have already the gpio's for DVB. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-cards.c | 175 ++++++++++++++++-------- linux/drivers/media/video/em28xx/em28xx.h | 15 +- 2 files changed, 127 insertions(+), 63 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index aae578e21..64f097446 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -90,17 +90,21 @@ static struct em28xx_reg_seq em2880_msi_digivox_ad_analog[] = { }; /* Boards - EM2880 MSI DIGIVOX AD and EM2880_BOARD_MSI_DIGIVOX_AD_II */ +#if 0 /* Still missing the dvb setup */ static struct em28xx_reg_seq em2880_msi_digivox_ad_digital[] = { {EM28XX_R08_GPIO, 0x6a, ~EM_GPIO_4, 10}, { -1, -1, -1, -1}, }; +#endif /* Board - EM2870 Kworld 355u Analog - No input analog */ +#if 0 /* Still missing the dvb setup */ static struct em28xx_reg_seq em2870_kworld_355u_digital[] = { {EM2880_R04_GPO, 0x01, 0xff, 10}, { -1, -1, -1, -1}, }; +#endif /* Callback for the most boards */ static struct em28xx_reg_seq default_callback[] = { @@ -464,18 +468,25 @@ struct em28xx_board em28xx_boards[] = { .valid = EM28XX_BOARD_NOT_VALIDATED, .tuner_type = TUNER_XC2028, .decoder = EM28XX_TVP5150, +#if 0 /* FIXME: add an entry at em28xx-dvb */ + .has_dvb = 1, + .dvb_gpio = hauppauge_wintv_hvr_900_digital, +#endif .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, .amux = EM28XX_AMUX_VIDEO, + .gpio = hauppauge_wintv_hvr_900_analog, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, } }, }, [EM2861_BOARD_KWORLD_PVRTV_300U] = { @@ -539,6 +550,10 @@ struct em28xx_board em28xx_boards[] = { .name = "Terratec Cinergy T XS", .valid = EM28XX_BOARD_NOT_VALIDATED, .tuner_type = TUNER_XC2028, +#if 0 /* FIXME: add an entry at em28xx-dvb */ + .has_dvb = 1, + .dvb_gpio = default_digital, +#endif }, [EM2870_BOARD_TERRATEC_XS_MT2060] = { .name = "Terratec Cinergy T XS (MT2060)", @@ -549,10 +564,18 @@ struct em28xx_board em28xx_boards[] = { .name = "Kworld 350 U DVB-T", .valid = EM28XX_BOARD_NOT_VALIDATED, .tuner_type = TUNER_XC2028, +#if 0 /* FIXME: add an entry at em28xx-dvb */ + .has_dvb = 1, + .dvb_gpio = default_digital, +#endif }, [EM2870_BOARD_KWORLD_355U] = { .name = "Kworld 355 U DVB-T", .valid = EM28XX_BOARD_NOT_VALIDATED, +#if 0 /* FIXME: add an entry at em28xx-dvb */ + .has_dvb = 1, + .dvb_gpio = em2870_kworld_355u_digital, +#endif }, [EM2870_BOARD_PINNACLE_PCTV_DVB] = { .name = "Pinnacle PCTV DVB-T", @@ -573,18 +596,25 @@ struct em28xx_board em28xx_boards[] = { .has_msp34xx = 1, .tuner_type = TUNER_XC2028, .decoder = EM28XX_TVP5150, +#if 0 /* FIXME: add an entry at em28xx-dvb */ + .has_dvb = 1, + .dvb_gpio = default_digital, +#endif .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, .amux = EM28XX_AMUX_VIDEO, + .gpio = default_analog, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, .amux = EM28XX_AMUX_LINE_IN, + .gpio = default_analog, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, .amux = EM28XX_AMUX_LINE_IN, + .gpio = default_analog, } }, }, [EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900] = { @@ -593,19 +623,23 @@ struct em28xx_board em28xx_boards[] = { .tuner_type = TUNER_XC2028, .mts_firmware = 1, .has_dvb = 1, + .dvb_gpio = hauppauge_wintv_hvr_900_digital, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, .amux = EM28XX_AMUX_VIDEO, + .gpio = hauppauge_wintv_hvr_900_analog, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, } }, }, [EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2] = { @@ -614,18 +648,25 @@ struct em28xx_board em28xx_boards[] = { .tuner_type = TUNER_XC2028, .mts_firmware = 1, .decoder = EM28XX_TVP5150, +#if 0 /* FIXME: add an entry at em28xx-dvb */ + .has_dvb = 1, + .dvb_gpio = hauppauge_wintv_hvr_900_digital, +#endif .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, .amux = EM28XX_AMUX_VIDEO, + .gpio = hauppauge_wintv_hvr_900_analog, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, .amux = 3, + .gpio = hauppauge_wintv_hvr_900_analog, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, } }, }, [EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950] = { @@ -633,20 +674,24 @@ struct em28xx_board em28xx_boards[] = { .tuner_type = TUNER_XC2028, .mts_firmware = 1, .has_dvb = 1, + .dvb_gpio = hauppauge_wintv_hvr_900_digital, .ir_codes = ir_codes_hauppauge_new, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, .amux = EM28XX_AMUX_VIDEO, + .gpio = hauppauge_wintv_hvr_900_analog, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, } }, }, [EM2880_BOARD_PINNACLE_PCTV_HD_PRO] = { @@ -654,20 +699,24 @@ struct em28xx_board em28xx_boards[] = { .tuner_type = TUNER_XC2028, .mts_firmware = 1, .has_dvb = 1, + .dvb_gpio = hauppauge_wintv_hvr_900_digital, .ir_codes = ir_codes_pinnacle_pctv_hd, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, .amux = EM28XX_AMUX_VIDEO, + .gpio = hauppauge_wintv_hvr_900_analog, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, } }, }, [EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600] = { @@ -675,20 +724,24 @@ struct em28xx_board em28xx_boards[] = { .tuner_type = TUNER_XC2028, .mts_firmware = 1, .has_dvb = 1, + .dvb_gpio = hauppauge_wintv_hvr_900_digital, .ir_codes = ir_codes_ati_tv_wonder_hd_600, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, .amux = EM28XX_AMUX_VIDEO, + .gpio = hauppauge_wintv_hvr_900_analog, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, } }, }, [EM2880_BOARD_TERRATEC_HYBRID_XS] = { @@ -696,18 +749,22 @@ struct em28xx_board em28xx_boards[] = { .tuner_type = TUNER_XC2028, .decoder = EM28XX_TVP5150, .has_dvb = 1, + .dvb_gpio = default_analog, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, .amux = EM28XX_AMUX_VIDEO, + .gpio = default_analog, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, .amux = EM28XX_AMUX_LINE_IN, + .gpio = default_analog, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, .amux = EM28XX_AMUX_LINE_IN, + .gpio = default_analog, } }, }, /* maybe there's a reason behind it why Terratec sells the Hybrid XS @@ -717,18 +774,25 @@ struct em28xx_board em28xx_boards[] = { .name = "Terratec Prodigy XS", .tuner_type = TUNER_XC2028, .decoder = EM28XX_TVP5150, +#if 0 /* FIXME: add an entry at em28xx-dvb */ + .has_dvb = 1, + .dvb_gpio = hauppauge_wintv_hvr_900_digital, +#endif .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, .amux = EM28XX_AMUX_VIDEO, + .gpio = hauppauge_wintv_hvr_900_analog, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, } }, }, [EM2820_BOARD_MSI_VOX_USB_2] = { @@ -899,18 +963,25 @@ struct em28xx_board em28xx_boards[] = { .valid = EM28XX_BOARD_NOT_VALIDATED, .tuner_type = TUNER_XC2028, .decoder = EM28XX_TVP5150, +#if 0 /* FIXME: add an entry at em28xx-dvb */ + .has_dvb = 1, + .dvb_gpio = em2880_msi_digivox_ad_digital, +#endif .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, .amux = EM28XX_AMUX_VIDEO, + .gpio = em2880_msi_digivox_ad_analog, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, .amux = EM28XX_AMUX_LINE_IN, + .gpio = em2880_msi_digivox_ad_analog, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, .amux = EM28XX_AMUX_LINE_IN, + .gpio = em2880_msi_digivox_ad_analog, } }, }, [EM2880_BOARD_MSI_DIGIVOX_AD_II] = { @@ -918,18 +989,25 @@ struct em28xx_board em28xx_boards[] = { .valid = EM28XX_BOARD_NOT_VALIDATED, .tuner_type = TUNER_XC2028, .decoder = EM28XX_TVP5150, +#if 0 /* FIXME: add an entry at em28xx-dvb */ + .has_dvb = 1, + .dvb_gpio = em2880_msi_digivox_ad_digital, +#endif .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, .amux = EM28XX_AMUX_VIDEO, + .gpio = em2880_msi_digivox_ad_analog, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, .amux = EM28XX_AMUX_LINE_IN, + .gpio = em2880_msi_digivox_ad_analog, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, .amux = EM28XX_AMUX_LINE_IN, + .gpio = em2880_msi_digivox_ad_analog, } }, }, [EM2880_BOARD_KWORLD_DVB_305U] = { @@ -955,20 +1033,24 @@ struct em28xx_board em28xx_boards[] = { .name = "KWorld DVB-T 310U", .tuner_type = TUNER_XC2028, .has_dvb = 1, + .dvb_gpio = default_digital, .mts_firmware = 1, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, .amux = EM28XX_AMUX_VIDEO, + .gpio = default_analog, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, .amux = EM28XX_AMUX_LINE_IN, + .gpio = default_analog, }, { /* S-video has not been tested yet */ .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, .amux = EM28XX_AMUX_LINE_IN, + .gpio = default_analog, } }, }, [EM2881_BOARD_DNT_DA2_HYBRID] = { @@ -976,18 +1058,25 @@ struct em28xx_board em28xx_boards[] = { .valid = EM28XX_BOARD_NOT_VALIDATED, .tuner_type = TUNER_XC2028, .decoder = EM28XX_TVP5150, +#if 0 /* FIXME: add an entry at em28xx-dvb */ + .has_dvb = 1, + .dvb_gpio = default_digital, +#endif .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, .amux = EM28XX_AMUX_VIDEO, + .gpio = default_analog, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, .amux = EM28XX_AMUX_LINE_IN, + .gpio = default_analog, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, .amux = EM28XX_AMUX_LINE_IN, + .gpio = default_analog, } }, }, [EM2881_BOARD_PINNACLE_HYBRID_PRO] = { @@ -995,18 +1084,25 @@ struct em28xx_board em28xx_boards[] = { .valid = EM28XX_BOARD_NOT_VALIDATED, .tuner_type = TUNER_XC2028, .decoder = EM28XX_TVP5150, +#if 0 /* FIXME: add an entry at em28xx-dvb */ + .has_dvb = 1, + .dvb_gpio = default_digital, +#endif .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, .amux = EM28XX_AMUX_VIDEO, + .gpio = default_analog, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, .amux = EM28XX_AMUX_LINE_IN, + .gpio = default_analog, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, .amux = EM28XX_AMUX_LINE_IN, + .gpio = default_analog, } }, }, [EM2882_BOARD_PINNACLE_HYBRID_PRO] = { @@ -1015,18 +1111,25 @@ struct em28xx_board em28xx_boards[] = { .tuner_type = TUNER_XC2028, .mts_firmware = 1, .decoder = EM28XX_TVP5150, +#if 0 /* FIXME: add an entry at em28xx-dvb */ + .has_dvb = 1, + .dvb_gpio = hauppauge_wintv_hvr_900_digital, +#endif .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, .amux = EM28XX_AMUX_VIDEO, + .gpio = hauppauge_wintv_hvr_900_analog, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, } }, }, [EM2882_BOARD_KWORLD_VS_DVBT] = { @@ -1053,18 +1156,25 @@ struct em28xx_board em28xx_boards[] = { .valid = EM28XX_BOARD_NOT_VALIDATED, .tuner_type = TUNER_XC2028, .decoder = EM28XX_TVP5150, +#if 0 /* FIXME: add an entry at em28xx-dvb */ + .has_dvb = 1, + .dvb_gpio = hauppauge_wintv_hvr_900_digital, +#endif .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, .amux = EM28XX_AMUX_VIDEO, + .gpio = hauppauge_wintv_hvr_900_analog, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, } }, }, [EM2883_BOARD_KWORLD_HYBRID_A316] = { @@ -1072,18 +1182,25 @@ struct em28xx_board em28xx_boards[] = { .valid = EM28XX_BOARD_NOT_VALIDATED, .tuner_type = TUNER_XC2028, .decoder = EM28XX_TVP5150, +#if 0 /* FIXME: add an entry at em28xx-dvb */ + .has_dvb = 1, + .dvb_gpio = hauppauge_wintv_hvr_900_digital, +#endif .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, .amux = EM28XX_AMUX_VIDEO, + .gpio = hauppauge_wintv_hvr_900_analog, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, } }, }, [EM2820_BOARD_COMPRO_VIDEOMATE_FORYOU] = { @@ -1105,6 +1222,7 @@ struct em28xx_board em28xx_boards[] = { .name = "Pinnacle PCTV HD Mini", .tuner_type = TUNER_ABSENT, .has_dvb = 1, + .dvb_gpio = em2874_pinnacle_80e_digital, .ir_codes = ir_codes_pinnacle_pctv_hd, .decoder = EM28XX_NODECODER, .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | @@ -1334,55 +1452,11 @@ void em28xx_pre_card_setup(struct em28xx *dev) /* request some modules */ switch (dev->model) { - case EM2880_BOARD_TERRATEC_PRODIGY_XS: - case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: - case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2: - case EM2860_BOARD_TERRATEC_HYBRID_XS: - case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950: - case EM2880_BOARD_PINNACLE_PCTV_HD_PRO: - case EM2882_BOARD_PINNACLE_HYBRID_PRO: - case EM2883_BOARD_KWORLD_HYBRID_A316: - case EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600: - /* Sets GPO/GPIO sequences for this device */ - dev->analog_gpio = hauppauge_wintv_hvr_900_analog; - dev->digital_gpio = hauppauge_wintv_hvr_900_digital; - break; - - case EM2882_BOARD_TERRATEC_HYBRID_XS: - /* Sets GPO/GPIO sequences for this device */ - dev->analog_gpio = hauppauge_wintv_hvr_900_analog; - dev->digital_gpio = hauppauge_wintv_hvr_900_digital; - break; - - case EM2880_BOARD_TERRATEC_HYBRID_XS_FR: - case EM2880_BOARD_TERRATEC_HYBRID_XS: - case EM2870_BOARD_TERRATEC_XS: - case EM2881_BOARD_PINNACLE_HYBRID_PRO: - case EM2880_BOARD_KWORLD_DVB_310U: - case EM2870_BOARD_KWORLD_350U: - case EM2881_BOARD_DNT_DA2_HYBRID: - /* NOTE: EM2881_DNT_DA2_HYBRID spend 140 msleep for digital - and analog commands. If this commands doesn't work, - add this timer. */ - - /* Sets GPO/GPIO sequences for this device */ - dev->analog_gpio = default_analog; - dev->digital_gpio = default_digital; - break; - - case EM2880_BOARD_MSI_DIGIVOX_AD: - case EM2880_BOARD_MSI_DIGIVOX_AD_II: - /* Sets GPO/GPIO sequences for this device */ - dev->analog_gpio = em2880_msi_digivox_ad_analog; - dev->digital_gpio = em2880_msi_digivox_ad_digital; - break; - case EM2861_BOARD_PLEXTOR_PX_TV100U: /* FIXME guess */ /* Turn on analog audio output */ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd); break; - case EM2861_BOARD_KWORLD_PVRTV_300U: case EM2880_BOARD_KWORLD_DVB_305U: em28xx_write_reg(dev, EM28XX_R08_GPIO, 0x6d); @@ -1390,12 +1464,6 @@ void em28xx_pre_card_setup(struct em28xx *dev) em28xx_write_reg(dev, EM28XX_R08_GPIO, 0x7d); msleep(10); break; - - case EM2870_BOARD_KWORLD_355U: - /* Sets GPO/GPIO sequences for this device */ - dev->digital_gpio = em2870_kworld_355u_digital; - break; - case EM2870_BOARD_COMPRO_VIDEOMATE: /* TODO: someone can do some cleanup here... not everything's needed */ @@ -1412,7 +1480,6 @@ void em28xx_pre_card_setup(struct em28xx *dev) em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfc); mdelay(70); break; - case EM2870_BOARD_TERRATEC_XS_MT2060: /* this device needs some gpio writes to get the DVB-T demod work */ @@ -1423,7 +1490,6 @@ void em28xx_pre_card_setup(struct em28xx *dev) em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe); mdelay(70); break; - case EM2870_BOARD_PINNACLE_PCTV_DVB: /* this device needs some gpio writes to get the DVB-T demod work */ @@ -1434,16 +1500,11 @@ void em28xx_pre_card_setup(struct em28xx *dev) em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe); mdelay(70); break; - case EM2820_BOARD_GADMEI_UTV310: case EM2820_BOARD_MSI_VOX_USB_2: /* enables audio for that devices */ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd); break; - - case EM2874_BOARD_PINNACLE_PCTV_80E: - dev->digital_gpio = em2874_pinnacle_80e_digital; - break; } /* Sets the default callback. Used only for certain tuners */ diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index 81acbd1a2..1171578a8 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -305,11 +305,18 @@ enum em28xx_aout { EM28XX_AOUT_SURR = 1 << 4, }; +struct em28xx_reg_seq { + int reg; + unsigned char val, mask; + int sleep; +}; + struct em28xx_input { enum enum28xx_itype type; unsigned int vmux; enum em28xx_amux amux; enum em28xx_aout aout; + struct em28xx_reg_seq *gpio; }; #define INPUT(nr) (&em28xx_boards[dev->model].input[nr]) @@ -320,12 +327,6 @@ enum em28xx_decoder { EM28XX_SAA711X, }; -struct em28xx_reg_seq { - int reg; - unsigned char val, mask; - int sleep; -}; - struct em28xx_board { char *name; int vchannels; @@ -335,6 +336,8 @@ struct em28xx_board { /* i2c flags */ unsigned int tda9887_conf; + struct em28xx_reg_seq *dvb_gpio; + unsigned int is_em2800:1; unsigned int has_msp34xx:1; unsigned int mts_firmware:1; -- cgit v1.2.3 From 227c9555066cd0e4d06443c59239eb4f6f8ae25d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 27 Nov 2008 08:44:00 -0200 Subject: em28xx: replace magic numbers for mux aliases From: Mauro Carvalho Chehab Instead of using magic vmux/amux, let's use an alias where possible. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-cards.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 64f097446..33000a5fd 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -328,15 +328,15 @@ struct em28xx_board em28xx_boards[] = { .decoder = EM28XX_SAA711X, .input = { { .type = EM28XX_VMUX_TELEVISION, - .vmux = 2, + .vmux = SAA7115_COMPOSITE2, .amux = EM28XX_AMUX_VIDEO, }, { .type = EM28XX_VMUX_COMPOSITE1, - .vmux = 0, + .vmux = SAA7115_COMPOSITE0, .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, - .vmux = 9, + .vmux = SAA7115_COMPOSITE0, .amux = EM28XX_AMUX_LINE_IN, } }, }, @@ -660,7 +660,7 @@ struct em28xx_board em28xx_boards[] = { }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, - .amux = 3, + .amux = EM28XX_AMUX_LINE_IN, .gpio = hauppauge_wintv_hvr_900_analog, }, { .type = EM28XX_VMUX_SVIDEO, -- cgit v1.2.3 From 698f109d2265f635cf7216c68a836d0a11a5ac8a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 27 Nov 2008 08:46:59 -0200 Subject: em28xx: fix tuner absent entries From: Mauro Carvalho Chehab Before this patch, several devices without tuner were kept the value 0 for tuner_type. However, this means TUNER_TEMIC_PAL. Replace those entries for the proper TUNER_ABSENT value. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-cards.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 33000a5fd..24d7e8892 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -132,6 +132,7 @@ struct em28xx_board em28xx_boards[] = { [EM2750_BOARD_UNKNOWN] = { .name = "Unknown EM2750/EM2751 webcam grabber", .xclk = EM28XX_XCLK_FREQUENCY_48MHZ, + .tuner_type = TUNER_ABSENT, /* This is a webcam */ .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = 0, @@ -143,6 +144,7 @@ struct em28xx_board em28xx_boards[] = { .is_em2800 = 1, .tda9887_conf = TDA9887_PRESENT, .decoder = EM28XX_SAA711X, + .tuner_type = TUNER_ABSENT, .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, @@ -162,6 +164,7 @@ struct em28xx_board em28xx_boards[] = { .name = "Huaqi DLCW-130", .valid = EM28XX_BOARD_NOT_VALIDATED, .xclk = EM28XX_XCLK_FREQUENCY_48MHZ, + .tuner_type = TUNER_ABSENT, /* This is a webcam */ .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = 0, @@ -342,8 +345,8 @@ struct em28xx_board em28xx_boards[] = { }, [EM2820_BOARD_PINNACLE_DVC_100] = { .name = "Pinnacle Dazzle DVC 100", - .valid = EM28XX_BOARD_NOT_VALIDATED, .decoder = EM28XX_SAA711X, + .tuner_type = TUNER_ABSENT, /* Capture only device */ .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, @@ -357,6 +360,7 @@ struct em28xx_board em28xx_boards[] = { [EM2820_BOARD_VIDEOLOGY_20K14XUSB] = { .name = "Videology 20K14XUSB USB2.0", .valid = EM28XX_BOARD_NOT_VALIDATED, + .tuner_type = TUNER_ABSENT, /* This is a webcam */ .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = 0, @@ -408,6 +412,7 @@ struct em28xx_board em28xx_boards[] = { [EM2821_BOARD_USBGEAR_VD204] = { .name = "Usbgear VD204v9", .valid = EM28XX_BOARD_NOT_VALIDATED, + .tuner_type = TUNER_ABSENT, /* Capture only device */ .decoder = EM28XX_SAA711X, .input = { { .type = EM28XX_VMUX_COMPOSITE1, @@ -423,6 +428,7 @@ struct em28xx_board em28xx_boards[] = { /* Beijing Huaqi Information Digital Technology Co., Ltd */ .name = "NetGMBH Cam", .valid = EM28XX_BOARD_NOT_VALIDATED, + .tuner_type = TUNER_ABSENT, /* This is a webcam */ .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = 0, @@ -431,8 +437,8 @@ struct em28xx_board em28xx_boards[] = { }, [EM2860_BOARD_TYPHOON_DVD_MAKER] = { .name = "Typhoon DVD Maker", - .valid = EM28XX_BOARD_NOT_VALIDATED, .decoder = EM28XX_SAA711X, + .tuner_type = TUNER_ABSENT, /* Capture only device */ .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, @@ -510,7 +516,7 @@ struct em28xx_board em28xx_boards[] = { }, [EM2861_BOARD_YAKUMO_MOVIE_MIXER] = { .name = "Yakumo MovieMixer", - .valid = EM28XX_BOARD_NOT_VALIDATED, + .tuner_type = TUNER_ABSENT, /* Capture only device */ .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -546,6 +552,9 @@ struct em28xx_board em28xx_boards[] = { .amux = EM28XX_AMUX_LINE_IN, } }, }, + + /* Those boards with em2870 are DVB Only*/ + [EM2870_BOARD_TERRATEC_XS] = { .name = "Terratec Cinergy T XS", .valid = EM28XX_BOARD_NOT_VALIDATED, @@ -590,6 +599,7 @@ struct em28xx_board em28xx_boards[] = { .valid = EM28XX_BOARD_NOT_VALIDATED, .tuner_type = TUNER_ABSENT, /* MT2060 */ }, + [EM2880_BOARD_TERRATEC_HYBRID_XS_FR] = { .name = "Terratec Hybrid XS Secam", .valid = EM28XX_BOARD_NOT_VALIDATED, @@ -841,6 +851,7 @@ struct em28xx_board em28xx_boards[] = { .name = "eMPIA Technology, Inc. GrabBeeX+ Video Encoder", .is_em2800 = 1, .decoder = EM28XX_SAA711X, + .tuner_type = TUNER_ABSENT, /* capture only board */ .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, @@ -893,7 +904,7 @@ struct em28xx_board em28xx_boards[] = { }, [EM2820_BOARD_PINNACLE_DVC_90] = { .name = "Pinnacle Dazzle DVC 90/DVC 100", - .tuner_type = TUNER_ABSENT, + .tuner_type = TUNER_ABSENT, /* capture only board */ .decoder = EM28XX_SAA711X, .input = { { .type = EM28XX_VMUX_COMPOSITE1, @@ -1218,6 +1229,9 @@ struct em28xx_board em28xx_boards[] = { .amux = EM28XX_AMUX_LINE_IN, } }, }, + + /* em2874 tuners are DVB only */ + [EM2874_BOARD_PINNACLE_PCTV_80E] = { .name = "Pinnacle PCTV HD Mini", .tuner_type = TUNER_ABSENT, -- cgit v1.2.3 From a1e782e39b16be26c0d2a5a18f29cc85251d2cb1 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 27 Nov 2008 10:00:00 -0200 Subject: em28xx: fix gpio settings From: Mauro Carvalho Chehab A previous changeset moved gpio from em28xx struct into em28xx_board. However, the driver were not updated to properly honor those gpio's. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-core.c | 4 ++-- linux/drivers/media/video/em28xx/em28xx.h | 3 --- 2 files changed, 2 insertions(+), 5 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-core.c b/linux/drivers/media/video/em28xx/em28xx-core.c index d63e5b997..729120807 100644 --- a/linux/drivers/media/video/em28xx/em28xx-core.c +++ b/linux/drivers/media/video/em28xx/em28xx-core.c @@ -784,9 +784,9 @@ int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode) dev->mode = set_mode; if (dev->mode == EM28XX_DIGITAL_MODE) - return em28xx_gpio_set(dev, dev->digital_gpio); + return em28xx_gpio_set(dev, dev->board.dvb_gpio); else - return em28xx_gpio_set(dev, dev->analog_gpio); + return em28xx_gpio_set(dev, INPUT(dev->ctl_input)->gpio); } EXPORT_SYMBOL_GPL(em28xx_set_mode); diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index 1171578a8..62921982f 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -440,9 +440,6 @@ struct em28xx { /* Some older em28xx chips needs a waiting time after writing */ unsigned int wait_after_write; - /* GPIO sequences for analog and digital mode */ - struct em28xx_reg_seq *analog_gpio, *digital_gpio; - /* GPIO sequences for tuner callbacks */ struct em28xx_reg_seq *tuner_gpio; -- cgit v1.2.3 From 3a1f2e4b150aba36c07a25ec58986cf12469ba94 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 27 Nov 2008 10:10:40 -0200 Subject: em28xx: Add support for suspend the device when not used From: Mauro Carvalho Chehab Several chips may be turned off when the device is not used, like audio, video and dvb demods. This patch adds a gpio callback at the core structs to allow turning off such devices. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-cards.c | 2 +- linux/drivers/media/video/em28xx/em28xx-core.c | 23 ++++++++++++++--------- linux/drivers/media/video/em28xx/em28xx-dvb.c | 8 ++++---- linux/drivers/media/video/em28xx/em28xx-video.c | 2 +- linux/drivers/media/video/em28xx/em28xx.h | 3 ++- 5 files changed, 22 insertions(+), 16 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 24d7e8892..cf3bb3c1b 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -1529,7 +1529,7 @@ void em28xx_pre_card_setup(struct em28xx *dev) em28xx_set_mode(dev, EM28XX_ANALOG_MODE); /* Unlock device */ - em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED); + em28xx_set_mode(dev, EM28XX_SUSPEND); } static void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl) diff --git a/linux/drivers/media/video/em28xx/em28xx-core.c b/linux/drivers/media/video/em28xx/em28xx-core.c index 729120807..f62b05eae 100644 --- a/linux/drivers/media/video/em28xx/em28xx-core.c +++ b/linux/drivers/media/video/em28xx/em28xx-core.c @@ -741,12 +741,14 @@ int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio) if (!gpio) return rc; - em28xx_write_reg(dev, 0x48, 0x00); - if (dev->mode == EM28XX_ANALOG_MODE) - em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x67); - else - em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x37); - msleep(6); + if (dev->mode != EM28XX_SUSPEND) { + em28xx_write_reg(dev, 0x48, 0x00); + if (dev->mode == EM28XX_ANALOG_MODE) + em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x67); + else + em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x37); + msleep(6); + } /* Send GPIO reset sequences specified at board entry */ while (gpio->sleep >= 0) { @@ -771,14 +773,17 @@ int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode) if (dev->mode == set_mode) return 0; - if (set_mode == EM28XX_MODE_UNDEFINED) { + if (set_mode == EM28XX_SUSPEND) { dev->mode = set_mode; - return 0; + + /* FIXME: add suspend support for ac97 */ + + return em28xx_gpio_set(dev, dev->board.suspend_gpio); } #if 0 /* Resource is locked */ - if (dev->mode != EM28XX_MODE_UNDEFINED) + if (dev->mode != EM28XX_SUSPEND) return -EINVAL; #endif dev->mode = set_mode; diff --git a/linux/drivers/media/video/em28xx/em28xx-dvb.c b/linux/drivers/media/video/em28xx/em28xx-dvb.c index 6e6509c7a..a06659f28 100644 --- a/linux/drivers/media/video/em28xx/em28xx-dvb.c +++ b/linux/drivers/media/video/em28xx/em28xx-dvb.c @@ -162,7 +162,7 @@ static int stop_streaming(struct em28xx_dvb *dvb) em28xx_uninit_isoc(dev); - em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED); + em28xx_set_mode(dev, EM28XX_SUSPEND); return 0; } @@ -216,7 +216,7 @@ static int em28xx_dvb_bus_ctrl(struct dvb_frontend *fe, int acquire) if (acquire) return em28xx_set_mode(dev, EM28XX_DIGITAL_MODE); else - return em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED); + return em28xx_set_mode(dev, EM28XX_SUSPEND); } /* ------------------------------------------------------------------ */ @@ -467,12 +467,12 @@ static int dvb_init(struct em28xx *dev) if (result < 0) goto out_free; - em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED); + em28xx_set_mode(dev, EM28XX_SUSPEND); printk(KERN_INFO "Successfully loaded em28xx-dvb\n"); return 0; out_free: - em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED); + em28xx_set_mode(dev, EM28XX_SUSPEND); kfree(dvb); dev->dvb = NULL; return result; diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index bc3a59a57..676240bb1 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -1760,7 +1760,7 @@ static int em28xx_v4l2_close(struct inode *inode, struct file *filp) /* do this before setting alternate! */ em28xx_uninit_isoc(dev); - em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED); + em28xx_set_mode(dev, EM28XX_SUSPEND); /* set alternate 0 */ dev->alt = 0; diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index 62921982f..be13c5d2b 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -161,7 +161,7 @@ #define EM2800_I2C_WRITE_TIMEOUT 20 enum em28xx_mode { - EM28XX_MODE_UNDEFINED, + EM28XX_SUSPEND, EM28XX_ANALOG_MODE, EM28XX_DIGITAL_MODE, }; @@ -337,6 +337,7 @@ struct em28xx_board { unsigned int tda9887_conf; struct em28xx_reg_seq *dvb_gpio; + struct em28xx_reg_seq *suspend_gpio; unsigned int is_em2800:1; unsigned int has_msp34xx:1; -- cgit v1.2.3 From ed2bce4cb08784c495e2a63775256ea4e2a75300 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 27 Nov 2008 10:21:55 -0200 Subject: em28xx: move tuner gpio's to the cards struct From: Mauro Carvalho Chehab Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-cards.c | 33 +++++++++++++++++++------ linux/drivers/media/video/em28xx/em28xx.h | 5 ++-- 2 files changed, 28 insertions(+), 10 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index cf3bb3c1b..fe14ecc05 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -107,7 +107,7 @@ static struct em28xx_reg_seq em2870_kworld_355u_digital[] = { #endif /* Callback for the most boards */ -static struct em28xx_reg_seq default_callback[] = { +static struct em28xx_reg_seq default_tuner_gpio[] = { {EM28XX_R08_GPIO, EM_GPIO_4, EM_GPIO_4, 10}, {EM28XX_R08_GPIO, 0, EM_GPIO_4, 10}, {EM28XX_R08_GPIO, EM_GPIO_4, EM_GPIO_4, 10}, @@ -473,7 +473,9 @@ struct em28xx_board em28xx_boards[] = { .name = "Terratec Cinergy A Hybrid XS", .valid = EM28XX_BOARD_NOT_VALIDATED, .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, .decoder = EM28XX_TVP5150, + #if 0 /* FIXME: add an entry at em28xx-dvb */ .has_dvb = 1, .dvb_gpio = hauppauge_wintv_hvr_900_digital, @@ -499,6 +501,7 @@ struct em28xx_board em28xx_boards[] = { .name = "KWorld PVRTV 300U", .valid = EM28XX_BOARD_NOT_VALIDATED, .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -559,6 +562,7 @@ struct em28xx_board em28xx_boards[] = { .name = "Terratec Cinergy T XS", .valid = EM28XX_BOARD_NOT_VALIDATED, .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, #if 0 /* FIXME: add an entry at em28xx-dvb */ .has_dvb = 1, .dvb_gpio = default_digital, @@ -573,6 +577,7 @@ struct em28xx_board em28xx_boards[] = { .name = "Kworld 350 U DVB-T", .valid = EM28XX_BOARD_NOT_VALIDATED, .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, #if 0 /* FIXME: add an entry at em28xx-dvb */ .has_dvb = 1, .dvb_gpio = default_digital, @@ -605,6 +610,7 @@ struct em28xx_board em28xx_boards[] = { .valid = EM28XX_BOARD_NOT_VALIDATED, .has_msp34xx = 1, .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, .decoder = EM28XX_TVP5150, #if 0 /* FIXME: add an entry at em28xx-dvb */ .has_dvb = 1, @@ -631,6 +637,7 @@ struct em28xx_board em28xx_boards[] = { .name = "Hauppauge WinTV HVR 900", .tda9887_conf = TDA9887_PRESENT, .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, .mts_firmware = 1, .has_dvb = 1, .dvb_gpio = hauppauge_wintv_hvr_900_digital, @@ -656,6 +663,7 @@ struct em28xx_board em28xx_boards[] = { .name = "Hauppauge WinTV HVR 900 (R2)", .tda9887_conf = TDA9887_PRESENT, .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, .mts_firmware = 1, .decoder = EM28XX_TVP5150, #if 0 /* FIXME: add an entry at em28xx-dvb */ @@ -682,6 +690,7 @@ struct em28xx_board em28xx_boards[] = { [EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950] = { .name = "Hauppauge WinTV HVR 950", .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, .mts_firmware = 1, .has_dvb = 1, .dvb_gpio = hauppauge_wintv_hvr_900_digital, @@ -707,6 +716,7 @@ struct em28xx_board em28xx_boards[] = { [EM2880_BOARD_PINNACLE_PCTV_HD_PRO] = { .name = "Pinnacle PCTV HD Pro Stick", .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, .mts_firmware = 1, .has_dvb = 1, .dvb_gpio = hauppauge_wintv_hvr_900_digital, @@ -732,6 +742,7 @@ struct em28xx_board em28xx_boards[] = { [EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600] = { .name = "AMD ATI TV Wonder HD 600", .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, .mts_firmware = 1, .has_dvb = 1, .dvb_gpio = hauppauge_wintv_hvr_900_digital, @@ -757,6 +768,7 @@ struct em28xx_board em28xx_boards[] = { [EM2880_BOARD_TERRATEC_HYBRID_XS] = { .name = "Terratec Hybrid XS", .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, .decoder = EM28XX_TVP5150, .has_dvb = 1, .dvb_gpio = default_analog, @@ -783,6 +795,7 @@ struct em28xx_board em28xx_boards[] = { [EM2880_BOARD_TERRATEC_PRODIGY_XS] = { .name = "Terratec Prodigy XS", .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, .decoder = EM28XX_TVP5150, #if 0 /* FIXME: add an entry at em28xx-dvb */ .has_dvb = 1, @@ -973,6 +986,7 @@ struct em28xx_board em28xx_boards[] = { .name = "MSI DigiVox A/D", .valid = EM28XX_BOARD_NOT_VALIDATED, .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, .decoder = EM28XX_TVP5150, #if 0 /* FIXME: add an entry at em28xx-dvb */ .has_dvb = 1, @@ -999,6 +1013,7 @@ struct em28xx_board em28xx_boards[] = { .name = "MSI DigiVox A/D II", .valid = EM28XX_BOARD_NOT_VALIDATED, .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, .decoder = EM28XX_TVP5150, #if 0 /* FIXME: add an entry at em28xx-dvb */ .has_dvb = 1, @@ -1025,6 +1040,7 @@ struct em28xx_board em28xx_boards[] = { .name = "KWorld DVB-T 305U", .valid = EM28XX_BOARD_NOT_VALIDATED, .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -1043,6 +1059,7 @@ struct em28xx_board em28xx_boards[] = { [EM2880_BOARD_KWORLD_DVB_310U] = { .name = "KWorld DVB-T 310U", .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, .has_dvb = 1, .dvb_gpio = default_digital, .mts_firmware = 1, @@ -1068,6 +1085,7 @@ struct em28xx_board em28xx_boards[] = { .name = "DNT DA2 Hybrid", .valid = EM28XX_BOARD_NOT_VALIDATED, .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, .decoder = EM28XX_TVP5150, #if 0 /* FIXME: add an entry at em28xx-dvb */ .has_dvb = 1, @@ -1094,6 +1112,7 @@ struct em28xx_board em28xx_boards[] = { .name = "Pinnacle Hybrid Pro", .valid = EM28XX_BOARD_NOT_VALIDATED, .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, .decoder = EM28XX_TVP5150, #if 0 /* FIXME: add an entry at em28xx-dvb */ .has_dvb = 1, @@ -1120,6 +1139,7 @@ struct em28xx_board em28xx_boards[] = { .name = "Pinnacle Hybrid Pro (2)", .valid = EM28XX_BOARD_NOT_VALIDATED, .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, .mts_firmware = 1, .decoder = EM28XX_TVP5150, #if 0 /* FIXME: add an entry at em28xx-dvb */ @@ -1147,6 +1167,7 @@ struct em28xx_board em28xx_boards[] = { .name = "Kworld VS-DVB-T 323UR", .valid = EM28XX_BOARD_NOT_VALIDATED, .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -1166,6 +1187,7 @@ struct em28xx_board em28xx_boards[] = { .name = "Terratec Hybrid XS (em2882)", .valid = EM28XX_BOARD_NOT_VALIDATED, .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, .decoder = EM28XX_TVP5150, #if 0 /* FIXME: add an entry at em28xx-dvb */ .has_dvb = 1, @@ -1192,6 +1214,7 @@ struct em28xx_board em28xx_boards[] = { .name = "Kworld PlusTV HD Hybrid 330", .valid = EM28XX_BOARD_NOT_VALIDATED, .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, .decoder = EM28XX_TVP5150, #if 0 /* FIXME: add an entry at em28xx-dvb */ .has_dvb = 1, @@ -1384,7 +1407,7 @@ int em28xx_tuner_callback(void *ptr, int component, int command, int arg) if (command != XC2028_TUNER_RESET) return 0; - rc = em28xx_gpio_set(dev, dev->tuner_gpio); + rc = em28xx_gpio_set(dev, dev->board.tuner_gpio); return rc; } @@ -1521,11 +1544,7 @@ void em28xx_pre_card_setup(struct em28xx *dev) break; } - /* Sets the default callback. Used only for certain tuners */ - if (!dev->tuner_gpio) - dev->tuner_gpio = default_callback; - - em28xx_gpio_set(dev, dev->tuner_gpio); + em28xx_gpio_set(dev, dev->board.tuner_gpio); em28xx_set_mode(dev, EM28XX_ANALOG_MODE); /* Unlock device */ diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index be13c5d2b..667cb70cf 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -336,8 +336,10 @@ struct em28xx_board { /* i2c flags */ unsigned int tda9887_conf; + /* GPIO sequences */ struct em28xx_reg_seq *dvb_gpio; struct em28xx_reg_seq *suspend_gpio; + struct em28xx_reg_seq *tuner_gpio; unsigned int is_em2800:1; unsigned int has_msp34xx:1; @@ -441,9 +443,6 @@ struct em28xx { /* Some older em28xx chips needs a waiting time after writing */ unsigned int wait_after_write; - /* GPIO sequences for tuner callbacks */ - struct em28xx_reg_seq *tuner_gpio; - struct list_head devlist; u32 i2s_speed; /* I2S speed for audio digital stream */ -- cgit v1.2.3 From 1396809fc70840b729f6bdc9ef62e6bcb60825c7 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 27 Nov 2008 15:32:17 -0200 Subject: em28xx: improve probe messages From: Mauro Carvalho Chehab Prints usb speed used by em28xx interface. While there, fixes USB ID's endiannes. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-video.c | 42 ++++++++++++++++++------- 1 file changed, 30 insertions(+), 12 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index 676240bb1..23507ac4e 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -2258,6 +2258,7 @@ static int em28xx_usb_probe(struct usb_interface *interface, struct em28xx *dev = NULL; int retval = -ENODEV; int i, nr, ifnum, isoc_pipe; + char *speed; udev = usb_get_dev(interface_to_usbdev(interface)); ifnum = interface->altsetting[0].desc.bInterfaceNumber; @@ -2268,11 +2269,12 @@ static int em28xx_usb_probe(struct usb_interface *interface, /* Don't register audio interfaces */ if (interface->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) { - em28xx_err(DRIVER_NAME " audio device (%04x:%04x): interface %i, class %i\n", - udev->descriptor.idVendor, - udev->descriptor.idProduct, - ifnum, - interface->altsetting[0].desc.bInterfaceClass); + em28xx_err(DRIVER_NAME " audio device (%04x:%04x): " + "interface %i, class %i\n", + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct), + ifnum, + interface->altsetting[0].desc.bInterfaceClass); em28xx_devused &= ~(1<descriptor.idVendor, - udev->descriptor.idProduct, + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct), ifnum, interface->altsetting[0].desc.bInterfaceClass); @@ -2312,14 +2314,30 @@ static int em28xx_usb_probe(struct usb_interface *interface, em28xx_devused &= ~(1<speed) { + case USB_SPEED_LOW: + speed = "1.5"; + break; + case USB_SPEED_UNKNOWN: + case USB_SPEED_FULL: + speed = "12"; + break; + case USB_SPEED_HIGH: + speed = "480"; + break; + default: + speed = "unknown"; } - em28xx_err(DRIVER_NAME " new video device (%04x:%04x): interface %i, class %i\n", - udev->descriptor.idVendor, - udev->descriptor.idProduct, - ifnum, - interface->altsetting[0].desc.bInterfaceClass); + printk(DRIVER_NAME ": New video device @ %s Mbps " + "(%04x:%04x, interface %d, class %d)\n", + speed, + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct), + ifnum, + interface->altsetting->desc.bInterfaceNumber); if (nr >= EM28XX_MAXBOARDS) { printk(DRIVER_NAME ": Supports only %i em28xx boards.\n", -- cgit v1.2.3 From 24e5836b4aa995ffe84be439a2a69517f01201ed Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Fri, 28 Nov 2008 12:51:50 +0100 Subject: gspca: Do the webcam microphone work when present. From: Jean-Francois Moine This patch adds the set/get/enum audio controls. Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/gspca.c | 34 ++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/gspca.c b/linux/drivers/media/video/gspca/gspca.c index baf4aa524..4fda0fca6 100644 --- a/linux/drivers/media/video/gspca/gspca.c +++ b/linux/drivers/media/video/gspca/gspca.c @@ -671,7 +671,7 @@ static int gspca_set_alt0(struct gspca_dev *gspca_dev) ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 0); if (ret < 0) - PDEBUG(D_ERR|D_STREAM, "set interface 0 err %d", ret); + PDEBUG(D_ERR|D_STREAM, "set alt 0 err %d", ret); return ret; } @@ -1081,6 +1081,35 @@ static int vidioc_g_ctrl(struct file *file, void *priv, return -EINVAL; } +/*fixme: have an audio flag in gspca_dev?*/ +static int vidioc_s_audio(struct file *file, void *priv, + struct v4l2_audio *audio) +{ + if (audio->index != 0) + return -EINVAL; + return 0; +} + +static int vidioc_g_audio(struct file *file, void *priv, + struct v4l2_audio *audio) +{ + memset(audio, 0, sizeof *audio); + strcpy(audio->name, "Microphone"); + return 0; +} + +static int vidioc_enumaudio(struct file *file, void *priv, + struct v4l2_audio *audio) +{ + if (audio->index != 0) + return -EINVAL; + + strcpy(audio->name, "Microphone"); + audio->capability = 0; + audio->mode = 0; + return 0; +} + static int vidioc_querymenu(struct file *file, void *priv, struct v4l2_querymenu *qmenu) { @@ -1797,6 +1826,9 @@ static const struct v4l2_ioctl_ops dev_ioctl_ops = { .vidioc_queryctrl = vidioc_queryctrl, .vidioc_g_ctrl = vidioc_g_ctrl, .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_s_audio = vidioc_s_audio, + .vidioc_enumaudio = vidioc_enumaudio, .vidioc_querymenu = vidioc_querymenu, .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, -- cgit v1.2.3 From aa5b5a04837a6bb93286e723b500f6923e366c4d Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Fri, 28 Nov 2008 19:31:43 +0100 Subject: gspca: Align the 640x480 and 320x240 init of tas5130 in zc3xx. From: Jean-Francois Moine Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/zc3xx.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/zc3xx.c b/linux/drivers/media/video/gspca/zc3xx.c index adbca6a9d..ae4e30b52 100644 --- a/linux/drivers/media/video/gspca/zc3xx.c +++ b/linux/drivers/media/video/gspca/zc3xx.c @@ -5760,7 +5760,7 @@ static const struct usb_action tas5130cxx_Initial[] = { {} }; static const struct usb_action tas5130cxx_InitialScale[] = { - {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, +/*?? {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, */ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x40, ZC3XX_R002_CLOCKSELECT}, @@ -6084,7 +6084,7 @@ static const struct usb_action tas5130c_vf0250_InitialScale[] = { {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa, */ {0xaa, 0x13, 0x0002}, /* 00,13,02,aa, */ {0xaa, 0x15, 0x0004}, /* 00,15,04,aa */ - {0xaa, 0x01, 0x0000}, +/*?? {0xaa, 0x01, 0x0000}, */ {0xaa, 0x01, 0x0000}, {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa, */ {0xaa, 0x1c, 0x0017}, /* 00,1c,17,aa, */ @@ -6100,8 +6100,8 @@ static const struct usb_action tas5130c_vf0250_InitialScale[] = { {0xaa, 0x0f, 0x00a0}, /* 00,0f,a0,aa, */ {0xaa, 0x10, 0x0000}, /* 00,10,00,aa, */ {0xaa, 0x11, 0x00a0}, /* 00,11,a0,aa, */ - {0xa0, 0x00, 0x0039}, - {0xa1, 0x01, 0x0037}, +/*?? {0xa0, 0x00, 0x0039}, + {0xa1, 0x01, 0x0037}, */ {0xaa, 0x16, 0x0001}, /* 00,16,01,aa, */ {0xaa, 0x17, 0x00e8}, /* 00,17,e6,aa (e6 -> e8) */ {0xaa, 0x18, 0x0002}, /* 00,18,02,aa, */ -- cgit v1.2.3 From fd418b6a655b77600b58eca51b4e373e03bf6e26 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 29 Nov 2008 00:46:43 -0200 Subject: em28xx: improve board description messages Print manufacturer/product info from USB device and also card entry. From: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-cards.c | 3 ++- linux/drivers/media/video/em28xx/em28xx-video.c | 15 ++++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index fe14ecc05..d38b3ba72 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -1427,7 +1427,8 @@ void em28xx_pre_card_setup(struct em28xx *dev) em28xx_set_model(dev); - em28xx_info("Found %s\n", dev->board.name); + em28xx_info("Identified as %s (card=%d)\n", + dev->board.name, dev->model); /* Set the default GPO/GPIO for legacy devices */ dev->reg_gpo_num = EM2880_R04_GPO; diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index 23507ac4e..00d49c45f 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -2259,6 +2259,7 @@ static int em28xx_usb_probe(struct usb_interface *interface, int retval = -ENODEV; int i, nr, ifnum, isoc_pipe; char *speed; + char descr[255] = ""; udev = usb_get_dev(interface_to_usbdev(interface)); ifnum = interface->altsetting[0].desc.bInterfaceNumber; @@ -2331,8 +2332,20 @@ static int em28xx_usb_probe(struct usb_interface *interface, speed = "unknown"; } - printk(DRIVER_NAME ": New video device @ %s Mbps " + if (udev->manufacturer) + strlcpy(descr, udev->manufacturer, sizeof(descr)); + + if (udev->product) { + if (*descr) + strlcat(descr, " ", sizeof(descr)); + strlcat(descr, udev->product, sizeof(descr)); + } + if (*descr) + strlcat(descr, " ", sizeof(descr)); + + printk(DRIVER_NAME ": New device %s@ %s Mbps " "(%04x:%04x, interface %d, class %d)\n", + descr, speed, le16_to_cpu(udev->descriptor.idVendor), le16_to_cpu(udev->descriptor.idProduct), -- cgit v1.2.3 From 64c1d1df18357731250591e657cbacd874f743ce Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 29 Nov 2008 16:59:33 +0100 Subject: upd64031a: convert to v4l2_subdev. From: Hans Verkuil Priority: normal Signed-off-by: Hans Verkuil --- linux/drivers/media/video/upd64031a.c | 193 ++++++++++++++++++++-------------- 1 file changed, 113 insertions(+), 80 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/upd64031a.c b/linux/drivers/media/video/upd64031a.c index dfca5ed6e..df5f4b0c8 100644 --- a/linux/drivers/media/video/upd64031a.c +++ b/linux/drivers/media/video/upd64031a.c @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #include #include @@ -68,6 +68,7 @@ enum { }; struct upd64031a_state { + struct v4l2_subdev sd; u8 regs[TOT_REGS]; u8 gr_mode; u8 direct_3dycs_connect; @@ -75,6 +76,11 @@ struct upd64031a_state { u8 ext_vert_sync; }; +static inline struct upd64031a_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct upd64031a_state, sd); +} + static u8 upd64031a_init[] = { 0x00, 0xb8, 0x48, 0xd2, 0xe6, 0x03, 0x10, 0x0b, 0xaf, 0x7f, @@ -84,8 +90,9 @@ static u8 upd64031a_init[] = { /* ------------------------------------------------------------------------ */ -static u8 upd64031a_read(struct i2c_client *client, u8 reg) +static u8 upd64031a_read(struct v4l2_subdev *sd, u8 reg) { + struct i2c_client *client = v4l2_get_subdevdata(sd); u8 buf[2]; if (reg >= sizeof(buf)) @@ -96,106 +103,127 @@ static u8 upd64031a_read(struct i2c_client *client, u8 reg) /* ------------------------------------------------------------------------ */ -static void upd64031a_write(struct i2c_client *client, u8 reg, u8 val) +static void upd64031a_write(struct v4l2_subdev *sd, u8 reg, u8 val) { + struct i2c_client *client = v4l2_get_subdevdata(sd); u8 buf[2]; buf[0] = reg; buf[1] = val; - v4l_dbg(1, debug, client, "write reg: %02X val: %02X\n", reg, val); + v4l2_dbg(1, debug, sd, "write reg: %02X val: %02X\n", reg, val); if (i2c_master_send(client, buf, 2) != 2) - v4l_err(client, "I/O error write 0x%02x/0x%02x\n", reg, val); + v4l2_err(sd, "I/O error write 0x%02x/0x%02x\n", reg, val); } /* ------------------------------------------------------------------------ */ /* The input changed due to new input or channel changed */ -static void upd64031a_change(struct i2c_client *client) +static int upd64031a_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) { - struct upd64031a_state *state = i2c_get_clientdata(client); + struct upd64031a_state *state = to_state(sd); u8 reg = state->regs[R00]; - v4l_dbg(1, debug, client, "changed input or channel\n"); - upd64031a_write(client, R00, reg | 0x10); - upd64031a_write(client, R00, reg & ~0x10); + v4l2_dbg(1, debug, sd, "changed input or channel\n"); + upd64031a_write(sd, R00, reg | 0x10); + upd64031a_write(sd, R00, reg & ~0x10); + return 0; } /* ------------------------------------------------------------------------ */ +static int upd64031a_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +{ + struct upd64031a_state *state = to_state(sd); + u8 r00, r05, r08; + + state->gr_mode = (route->input & 3) << 6; + state->direct_3dycs_connect = (route->input & 0xc) << 4; + state->ext_comp_sync = + (route->input & UPD64031A_COMPOSITE_EXTERNAL) << 1; + state->ext_vert_sync = + (route->input & UPD64031A_VERTICAL_EXTERNAL) << 2; + r00 = (state->regs[R00] & ~GR_MODE_MASK) | state->gr_mode; + r05 = (state->regs[R00] & ~SYNC_CIRCUIT_MASK) | + state->ext_comp_sync | state->ext_vert_sync; + r08 = (state->regs[R08] & ~DIRECT_3DYCS_CONNECT_MASK) | + state->direct_3dycs_connect; + upd64031a_write(sd, R00, r00); + upd64031a_write(sd, R05, r05); + upd64031a_write(sd, R08, r08); + return upd64031a_s_frequency(sd, NULL); +} + +static int upd64031a_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_UPD64031A, 0); +} + +static int upd64031a_log_status(struct v4l2_subdev *sd) +{ + v4l2_info(sd, "Status: SA00=0x%02x SA01=0x%02x\n", + upd64031a_read(sd, 0), upd64031a_read(sd, 1)); + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int upd64031a_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, + reg->match_type, reg->match_chip)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reg->val = upd64031a_read(sd, reg->reg & 0xff); + return 0; +} + +static int upd64031a_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, + reg->match_type, reg->match_chip)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + upd64031a_write(sd, reg->reg & 0xff, reg->val & 0xff); + return 0; +} +#endif + static int upd64031a_command(struct i2c_client *client, unsigned cmd, void *arg) { - struct upd64031a_state *state = i2c_get_clientdata(client); - struct v4l2_routing *route = arg; - - switch (cmd) { - case VIDIOC_S_FREQUENCY: - upd64031a_change(client); - break; - - case VIDIOC_INT_G_VIDEO_ROUTING: - route->input = (state->gr_mode >> 6) | - (state->direct_3dycs_connect >> 4) | - (state->ext_comp_sync >> 1) | - (state->ext_vert_sync >> 2); - route->output = 0; - break; - - case VIDIOC_INT_S_VIDEO_ROUTING: - { - u8 r00, r05, r08; - - state->gr_mode = (route->input & 3) << 6; - state->direct_3dycs_connect = (route->input & 0xc) << 4; - state->ext_comp_sync = - (route->input & UPD64031A_COMPOSITE_EXTERNAL) << 1; - state->ext_vert_sync = - (route->input & UPD64031A_VERTICAL_EXTERNAL) << 2; - r00 = (state->regs[R00] & ~GR_MODE_MASK) | state->gr_mode; - r05 = (state->regs[R00] & ~SYNC_CIRCUIT_MASK) | - state->ext_comp_sync | state->ext_vert_sync; - r08 = (state->regs[R08] & ~DIRECT_3DYCS_CONNECT_MASK) | - state->direct_3dycs_connect; - upd64031a_write(client, R00, r00); - upd64031a_write(client, R05, r05); - upd64031a_write(client, R08, r08); - upd64031a_change(client); - break; - } - - case VIDIOC_LOG_STATUS: - v4l_info(client, "Status: SA00=0x%02x SA01=0x%02x\n", - upd64031a_read(client, 0), upd64031a_read(client, 1)); - break; + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + +/* ----------------------------------------------------------------------- */ +static const struct v4l2_subdev_core_ops upd64031a_core_ops = { + .log_status = upd64031a_log_status, + .g_chip_ident = upd64031a_g_chip_ident, #ifdef CONFIG_VIDEO_ADV_DEBUG - case VIDIOC_DBG_G_REGISTER: - case VIDIOC_DBG_S_REGISTER: - { - struct v4l2_register *reg = arg; - - if (!v4l2_chip_match_i2c_client(client, - reg->match_type, reg->match_chip)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - if (cmd == VIDIOC_DBG_G_REGISTER) { - reg->val = upd64031a_read(client, reg->reg & 0xff); - break; - } - upd64031a_write(client, reg->reg & 0xff, reg->val & 0xff); - break; - } + .g_register = upd64031a_g_register, + .s_register = upd64031a_s_register, #endif +}; - case VIDIOC_G_CHIP_IDENT: - return v4l2_chip_ident_i2c_client(client, arg, - V4L2_IDENT_UPD64031A, 0); +static const struct v4l2_subdev_tuner_ops upd64031a_tuner_ops = { + .s_frequency = upd64031a_s_frequency, +}; - default: - break; - } - return 0; -} +static const struct v4l2_subdev_video_ops upd64031a_video_ops = { + .s_routing = upd64031a_s_routing, +}; + +static const struct v4l2_subdev_ops upd64031a_ops = { + .core = &upd64031a_core_ops, + .tuner = &upd64031a_tuner_ops, + .video = &upd64031a_video_ops, +}; /* ------------------------------------------------------------------------ */ @@ -205,6 +233,7 @@ static int upd64031a_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct upd64031a_state *state; + struct v4l2_subdev *sd; int i; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) @@ -216,19 +245,23 @@ static int upd64031a_probe(struct i2c_client *client, state = kmalloc(sizeof(struct upd64031a_state), GFP_KERNEL); if (state == NULL) return -ENOMEM; - i2c_set_clientdata(client, state); + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &upd64031a_ops); memcpy(state->regs, upd64031a_init, sizeof(state->regs)); state->gr_mode = UPD64031A_GR_ON << 6; state->direct_3dycs_connect = UPD64031A_3DYCS_COMPOSITE << 4; state->ext_comp_sync = state->ext_vert_sync = 0; for (i = 0; i < TOT_REGS; i++) - upd64031a_write(client, i, state->regs[i]); + upd64031a_write(sd, i, state->regs[i]); return 0; } static int upd64031a_remove(struct i2c_client *client) { - kfree(i2c_get_clientdata(client)); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); return 0; } -- cgit v1.2.3 From ebd704a6cbea5701de4bf28b48459fe0eb62021f Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 29 Nov 2008 17:00:30 +0100 Subject: upd64083: convert to v4l2_subdev. From: Hans Verkuil Priority: normal Signed-off-by: Hans Verkuil --- linux/drivers/media/video/upd64083.c | 166 ++++++++++++++++++++--------------- 1 file changed, 97 insertions(+), 69 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/upd64083.c b/linux/drivers/media/video/upd64083.c index 5f0c6919d..fe71b038b 100644 --- a/linux/drivers/media/video/upd64083.c +++ b/linux/drivers/media/video/upd64083.c @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #include #include @@ -57,11 +57,17 @@ enum { }; struct upd64083_state { + struct v4l2_subdev sd; u8 mode; u8 ext_y_adc; u8 regs[TOT_REGS]; }; +static inline struct upd64083_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct upd64083_state, sd); +} + /* Initial values when used in combination with the NEC upd64031a ghost reduction chip. */ static u8 upd64083_init[] = { @@ -74,34 +80,24 @@ static u8 upd64083_init[] = { /* ------------------------------------------------------------------------ */ -static void upd64083_log_status(struct i2c_client *client) -{ - u8 buf[7]; - - i2c_master_recv(client, buf, 7); - v4l_info(client, "Status: SA00=%02x SA01=%02x SA02=%02x SA03=%02x " - "SA04=%02x SA05=%02x SA06=%02x\n", - buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]); -} - -/* ------------------------------------------------------------------------ */ - -static void upd64083_write(struct i2c_client *client, u8 reg, u8 val) +static void upd64083_write(struct v4l2_subdev *sd, u8 reg, u8 val) { + struct i2c_client *client = v4l2_get_subdevdata(sd); u8 buf[2]; buf[0] = reg; buf[1] = val; - v4l_dbg(1, debug, client, "write reg: %02x val: %02x\n", reg, val); + v4l2_dbg(1, debug, sd, "write reg: %02x val: %02x\n", reg, val); if (i2c_master_send(client, buf, 2) != 2) - v4l_err(client, "I/O error write 0x%02x/0x%02x\n", reg, val); + v4l2_err(sd, "I/O error write 0x%02x/0x%02x\n", reg, val); } /* ------------------------------------------------------------------------ */ #ifdef CONFIG_VIDEO_ADV_DEBUG -static u8 upd64083_read(struct i2c_client *client, u8 reg) +static u8 upd64083_read(struct v4l2_subdev *sd, u8 reg) { + struct i2c_client *client = v4l2_get_subdevdata(sd); u8 buf[7]; if (reg >= sizeof(buf)) @@ -113,67 +109,94 @@ static u8 upd64083_read(struct i2c_client *client, u8 reg) /* ------------------------------------------------------------------------ */ -static int upd64083_command(struct i2c_client *client, unsigned cmd, void *arg) +static int upd64083_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) { - struct upd64083_state *state = i2c_get_clientdata(client); - struct v4l2_routing *route = arg; - - switch (cmd) { - case VIDIOC_INT_G_VIDEO_ROUTING: - route->input = (state->mode >> 6) | (state->ext_y_adc >> 3); - route->output = 0; - break; - - case VIDIOC_INT_S_VIDEO_ROUTING: - { - u8 r00, r02; - - if (route->input > 7 || (route->input & 6) == 6) - return -EINVAL; - state->mode = (route->input & 3) << 6; - state->ext_y_adc = (route->input & UPD64083_EXT_Y_ADC) << 3; - r00 = (state->regs[R00] & ~(3 << 6)) | state->mode; - r02 = (state->regs[R02] & ~(1 << 5)) | state->ext_y_adc; - upd64083_write(client, R00, r00); - upd64083_write(client, R02, r02); - break; - } - - case VIDIOC_LOG_STATUS: - upd64083_log_status(client); - break; + struct upd64083_state *state = to_state(sd); + u8 r00, r02; + + if (route->input > 7 || (route->input & 6) == 6) + return -EINVAL; + state->mode = (route->input & 3) << 6; + state->ext_y_adc = (route->input & UPD64083_EXT_Y_ADC) << 3; + r00 = (state->regs[R00] & ~(3 << 6)) | state->mode; + r02 = (state->regs[R02] & ~(1 << 5)) | state->ext_y_adc; + upd64083_write(sd, R00, r00); + upd64083_write(sd, R02, r02); + return 0; +} #ifdef CONFIG_VIDEO_ADV_DEBUG - case VIDIOC_DBG_G_REGISTER: - case VIDIOC_DBG_S_REGISTER: - { - struct v4l2_register *reg = arg; +static int upd64083_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, + reg->match_type, reg->match_chip)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reg->val = upd64083_read(sd, reg->reg & 0xff); + return 0; +} - if (!v4l2_chip_match_i2c_client(client, +static int upd64083_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - if (cmd == VIDIOC_DBG_G_REGISTER) { - reg->val = upd64083_read(client, reg->reg & 0xff); - break; - } - upd64083_write(client, reg->reg & 0xff, reg->val & 0xff); - break; - } + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + upd64083_write(sd, reg->reg & 0xff, reg->val & 0xff); + return 0; +} #endif - case VIDIOC_G_CHIP_IDENT: - return v4l2_chip_ident_i2c_client(client, arg, - V4L2_IDENT_UPD64083, 0); +static int upd64083_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_UPD64083, 0); +} - default: - break; - } +static int upd64083_log_status(struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 buf[7]; + i2c_master_recv(client, buf, 7); + v4l2_info(sd, "Status: SA00=%02x SA01=%02x SA02=%02x SA03=%02x " + "SA04=%02x SA05=%02x SA06=%02x\n", + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]); return 0; } +static int upd64083_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops upd64083_core_ops = { + .log_status = upd64083_log_status, + .g_chip_ident = upd64083_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = upd64083_g_register, + .s_register = upd64083_s_register, +#endif +}; + +static const struct v4l2_subdev_video_ops upd64083_video_ops = { + .s_routing = upd64083_s_routing, +}; + +static const struct v4l2_subdev_ops upd64083_ops = { + .core = &upd64083_core_ops, + .video = &upd64083_video_ops, +}; + /* ------------------------------------------------------------------------ */ /* i2c implementation */ @@ -182,6 +205,7 @@ static int upd64083_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct upd64083_state *state; + struct v4l2_subdev *sd; int i; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) @@ -193,19 +217,23 @@ static int upd64083_probe(struct i2c_client *client, state = kmalloc(sizeof(struct upd64083_state), GFP_KERNEL); if (state == NULL) return -ENOMEM; - i2c_set_clientdata(client, state); + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &upd64083_ops); /* Initially assume that a ghost reduction chip is present */ state->mode = 0; /* YCS mode */ state->ext_y_adc = (1 << 5); memcpy(state->regs, upd64083_init, TOT_REGS); for (i = 0; i < TOT_REGS; i++) - upd64083_write(client, i, state->regs[i]); + upd64083_write(sd, i, state->regs[i]); return 0; } static int upd64083_remove(struct i2c_client *client) { - kfree(i2c_get_clientdata(client)); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); return 0; } -- cgit v1.2.3 From 88c733e9ba8fc0b0911bd97cd808ec5b080fa9e4 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 29 Nov 2008 17:01:28 +0100 Subject: vp27smpx: convert to v4l2_subdev. From: Hans Verkuil Priority: normal Signed-off-by: Hans Verkuil --- linux/drivers/media/video/vp27smpx.c | 126 ++++++++++++++++++++++++----------- 1 file changed, 86 insertions(+), 40 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/vp27smpx.c b/linux/drivers/media/video/vp27smpx.c index 80de9e796..cd9df6a1c 100644 --- a/linux/drivers/media/video/vp27smpx.c +++ b/linux/drivers/media/video/vp27smpx.c @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include #include #include "compat.h" @@ -46,13 +46,20 @@ I2C_CLIENT_INSMOD; /* ----------------------------------------------------------------------- */ struct vp27smpx_state { + struct v4l2_subdev sd; int radio; u32 audmode; }; -static void vp27smpx_set_audmode(struct i2c_client *client, u32 audmode) +static inline struct vp27smpx_state *to_state(struct v4l2_subdev *sd) { - struct vp27smpx_state *state = i2c_get_clientdata(client); + return container_of(sd, struct vp27smpx_state, sd); +} + +static void vp27smpx_set_audmode(struct v4l2_subdev *sd, u32 audmode) +{ + struct vp27smpx_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); u8 data[3] = { 0x00, 0x00, 0x04 }; switch (audmode) { @@ -69,55 +76,89 @@ static void vp27smpx_set_audmode(struct i2c_client *client, u32 audmode) } if (i2c_master_send(client, data, sizeof(data)) != sizeof(data)) - v4l_err(client, "%s: I/O error setting audmode\n", - client->name); + v4l2_err(sd, "I/O error setting audmode\n"); else state->audmode = audmode; } -static int vp27smpx_command(struct i2c_client *client, unsigned cmd, void *arg) +static int vp27smpx_s_radio(struct v4l2_subdev *sd) { - struct vp27smpx_state *state = i2c_get_clientdata(client); - struct v4l2_tuner *vt = arg; + struct vp27smpx_state *state = to_state(sd); - switch (cmd) { - case AUDC_SET_RADIO: - state->radio = 1; - break; + state->radio = 1; + return 0; +} - case VIDIOC_S_STD: - state->radio = 0; - break; +static int vp27smpx_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) +{ + struct vp27smpx_state *state = to_state(sd); - case VIDIOC_S_TUNER: - if (!state->radio) - vp27smpx_set_audmode(client, vt->audmode); - break; + state->radio = 0; + return 0; +} - case VIDIOC_G_TUNER: - if (state->radio) - break; - vt->audmode = state->audmode; - vt->capability = V4L2_TUNER_CAP_STEREO | - V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; - vt->rxsubchans = V4L2_TUNER_SUB_MONO; - break; +static int vp27smpx_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct vp27smpx_state *state = to_state(sd); - case VIDIOC_G_CHIP_IDENT: - return v4l2_chip_ident_i2c_client(client, arg, - V4L2_IDENT_VP27SMPX, 0); + if (!state->radio) + vp27smpx_set_audmode(sd, vt->audmode); + return 0; +} - case VIDIOC_LOG_STATUS: - v4l_info(client, "Audio Mode: %u%s\n", state->audmode, - state->radio ? " (Radio)" : ""); - break; +static int vp27smpx_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct vp27smpx_state *state = to_state(sd); + + if (state->radio) + return 0; + vt->audmode = state->audmode; + vt->capability = V4L2_TUNER_CAP_STEREO | + V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; + vt->rxsubchans = V4L2_TUNER_SUB_MONO; + return 0; +} - default: - return -EINVAL; - } +static int vp27smpx_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_VP27SMPX, 0); +} + +static int vp27smpx_log_status(struct v4l2_subdev *sd) +{ + struct vp27smpx_state *state = to_state(sd); + + v4l2_info(sd, "Audio Mode: %u%s\n", state->audmode, + state->radio ? " (Radio)" : ""); return 0; } +static int vp27smpx_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops vp27smpx_core_ops = { + .log_status = vp27smpx_log_status, + .g_chip_ident = vp27smpx_g_chip_ident, +}; + +static const struct v4l2_subdev_tuner_ops vp27smpx_tuner_ops = { + .s_radio = vp27smpx_s_radio, + .s_std = vp27smpx_s_std, + .s_tuner = vp27smpx_s_tuner, + .g_tuner = vp27smpx_g_tuner, +}; + +static const struct v4l2_subdev_ops vp27smpx_ops = { + .core = &vp27smpx_core_ops, + .tuner = &vp27smpx_tuner_ops, +}; + /* ----------------------------------------------------------------------- */ /* i2c implementation */ @@ -131,6 +172,7 @@ static int vp27smpx_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct vp27smpx_state *state; + struct v4l2_subdev *sd; /* Check if the adapter supports the needed features */ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) @@ -145,17 +187,21 @@ static int vp27smpx_probe(struct i2c_client *client, state = kzalloc(sizeof(struct vp27smpx_state), GFP_KERNEL); if (state == NULL) return -ENOMEM; + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &vp27smpx_ops); state->audmode = V4L2_TUNER_MODE_STEREO; - i2c_set_clientdata(client, state); /* initialize vp27smpx */ - vp27smpx_set_audmode(client, state->audmode); + vp27smpx_set_audmode(sd, state->audmode); return 0; } static int vp27smpx_remove(struct i2c_client *client) { - kfree(i2c_get_clientdata(client)); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); return 0; } -- cgit v1.2.3 From b7a1da99296bb70d499b52c1f35f9e8909ef7935 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 29 Nov 2008 17:02:26 +0100 Subject: wm8739: convert to v4l2_subdev. From: Hans Verkuil Priority: normal Signed-off-by: Hans Verkuil --- linux/drivers/media/video/wm8739.c | 188 +++++++++++++++++++++---------------- 1 file changed, 106 insertions(+), 82 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/wm8739.c b/linux/drivers/media/video/wm8739.c index f9d13b89a..ea311672c 100644 --- a/linux/drivers/media/video/wm8739.c +++ b/linux/drivers/media/video/wm8739.c @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include #include #include "compat.h" @@ -58,6 +58,7 @@ enum { }; struct wm8739_state { + struct v4l2_subdev sd; u32 clock_freq; u8 muted; u16 volume; @@ -66,43 +67,49 @@ struct wm8739_state { u8 vol_r; /* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */ }; +static inline struct wm8739_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct wm8739_state, sd); +} + /* ------------------------------------------------------------------------ */ -static int wm8739_write(struct i2c_client *client, int reg, u16 val) +static int wm8739_write(struct v4l2_subdev *sd, int reg, u16 val) { + struct i2c_client *client = v4l2_get_subdevdata(sd); int i; if (reg < 0 || reg >= TOT_REGS) { - v4l_err(client, "Invalid register R%d\n", reg); + v4l2_err(sd, "Invalid register R%d\n", reg); return -1; } - v4l_dbg(1, debug, client, "write: %02x %02x\n", reg, val); + v4l2_dbg(1, debug, sd, "write: %02x %02x\n", reg, val); for (i = 0; i < 3; i++) if (i2c_smbus_write_byte_data(client, (reg << 1) | (val >> 8), val & 0xff) == 0) return 0; - v4l_err(client, "I2C: cannot write %03x to register R%d\n", val, reg); + v4l2_err(sd, "I2C: cannot write %03x to register R%d\n", val, reg); return -1; } /* write regs to set audio volume etc */ -static void wm8739_set_audio(struct i2c_client *client) +static void wm8739_set_audio(struct v4l2_subdev *sd) { - struct wm8739_state *state = i2c_get_clientdata(client); + struct wm8739_state *state = to_state(sd); u16 mute = state->muted ? 0x80 : 0; /* Volume setting: bits 0-4, 0x1f = 12 dB, 0x00 = -34.5 dB * Default setting: 0x17 = 0 dB */ - wm8739_write(client, R0, (state->vol_l & 0x1f) | mute); - wm8739_write(client, R1, (state->vol_r & 0x1f) | mute); + wm8739_write(sd, R0, (state->vol_l & 0x1f) | mute); + wm8739_write(sd, R1, (state->vol_r & 0x1f) | mute); } -static int wm8739_get_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) +static int wm8739_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct wm8739_state *state = i2c_get_clientdata(client); + struct wm8739_state *state = to_state(sd); switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: @@ -123,9 +130,9 @@ static int wm8739_get_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) return 0; } -static int wm8739_set_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) +static int wm8739_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct wm8739_state *state = i2c_get_clientdata(client); + struct wm8739_state *state = to_state(sd); unsigned int work_l, work_r; switch (ctrl->id) { @@ -153,7 +160,7 @@ static int wm8739_set_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) state->vol_r = (long)work_r * 31 / 65535; /* set audio volume etc. */ - wm8739_set_audio(client); + wm8739_set_audio(sd); return 0; } @@ -192,77 +199,89 @@ static struct v4l2_queryctrl wm8739_qctrl[] = { /* ------------------------------------------------------------------------ */ -static int wm8739_command(struct i2c_client *client, unsigned cmd, void *arg) +static int wm8739_s_clock_freq(struct v4l2_subdev *sd, u32 audiofreq) { - struct wm8739_state *state = i2c_get_clientdata(client); - - switch (cmd) { - case VIDIOC_INT_AUDIO_CLOCK_FREQ: - { - u32 audiofreq = *(u32 *)arg; - - state->clock_freq = audiofreq; - /* de-activate */ - wm8739_write(client, R9, 0x000); - switch (audiofreq) { - case 44100: - /* 256fps, fs=44.1k */ - wm8739_write(client, R8, 0x020); - break; - case 48000: - /* 256fps, fs=48k */ - wm8739_write(client, R8, 0x000); - break; - case 32000: - /* 256fps, fs=32k */ - wm8739_write(client, R8, 0x018); - break; - default: - break; - } - /* activate */ - wm8739_write(client, R9, 0x001); + struct wm8739_state *state = to_state(sd); + + state->clock_freq = audiofreq; + /* de-activate */ + wm8739_write(sd, R9, 0x000); + switch (audiofreq) { + case 44100: + /* 256fps, fs=44.1k */ + wm8739_write(sd, R8, 0x020); + break; + case 48000: + /* 256fps, fs=48k */ + wm8739_write(sd, R8, 0x000); + break; + case 32000: + /* 256fps, fs=32k */ + wm8739_write(sd, R8, 0x018); + break; + default: break; } + /* activate */ + wm8739_write(sd, R9, 0x001); + return 0; +} - case VIDIOC_G_CTRL: - return wm8739_get_ctrl(client, arg); - - case VIDIOC_S_CTRL: - return wm8739_set_ctrl(client, arg); +static int wm8739_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) +{ + int i; - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; - int i; - - for (i = 0; i < ARRAY_SIZE(wm8739_qctrl); i++) - if (qc->id && qc->id == wm8739_qctrl[i].id) { - memcpy(qc, &wm8739_qctrl[i], sizeof(*qc)); - return 0; - } - return -EINVAL; - } + for (i = 0; i < ARRAY_SIZE(wm8739_qctrl); i++) + if (qc->id && qc->id == wm8739_qctrl[i].id) { + memcpy(qc, &wm8739_qctrl[i], sizeof(*qc)); + return 0; + } + return -EINVAL; +} - case VIDIOC_G_CHIP_IDENT: - return v4l2_chip_ident_i2c_client(client, - arg, V4L2_IDENT_WM8739, 0); +static int wm8739_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); - case VIDIOC_LOG_STATUS: - v4l_info(client, "Frequency: %u Hz\n", state->clock_freq); - v4l_info(client, "Volume L: %02x%s\n", state->vol_l & 0x1f, - state->muted ? " (muted)" : ""); - v4l_info(client, "Volume R: %02x%s\n", state->vol_r & 0x1f, - state->muted ? " (muted)" : ""); - break; + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_WM8739, 0); +} - default: - return -EINVAL; - } +static int wm8739_log_status(struct v4l2_subdev *sd) +{ + struct wm8739_state *state = to_state(sd); + v4l2_info(sd, "Frequency: %u Hz\n", state->clock_freq); + v4l2_info(sd, "Volume L: %02x%s\n", state->vol_l & 0x1f, + state->muted ? " (muted)" : ""); + v4l2_info(sd, "Volume R: %02x%s\n", state->vol_r & 0x1f, + state->muted ? " (muted)" : ""); return 0; } +static int wm8739_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops wm8739_core_ops = { + .log_status = wm8739_log_status, + .g_chip_ident = wm8739_g_chip_ident, + .queryctrl = wm8739_queryctrl, + .g_ctrl = wm8739_g_ctrl, + .s_ctrl = wm8739_s_ctrl, +}; + +static const struct v4l2_subdev_audio_ops wm8739_audio_ops = { + .s_clock_freq = wm8739_s_clock_freq, +}; + +static const struct v4l2_subdev_ops wm8739_ops = { + .core = &wm8739_core_ops, + .audio = &wm8739_audio_ops, +}; + /* ------------------------------------------------------------------------ */ /* i2c implementation */ @@ -271,6 +290,7 @@ static int wm8739_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct wm8739_state *state; + struct v4l2_subdev *sd; /* Check if the adapter supports the needed features */ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) @@ -282,6 +302,8 @@ static int wm8739_probe(struct i2c_client *client, state = kmalloc(sizeof(struct wm8739_state), GFP_KERNEL); if (state == NULL) return -ENOMEM; + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &wm8739_ops); state->vol_l = 0x17; /* 0dB */ state->vol_r = 0x17; /* 0dB */ state->muted = 0; @@ -289,31 +311,33 @@ static int wm8739_probe(struct i2c_client *client, /* normalize (12dB(31) to -34.5dB(0) [0dB(23)] -> 65535 to 0) */ state->volume = ((long)state->vol_l + 1) * 65535 / 31; state->clock_freq = 48000; - i2c_set_clientdata(client, state); /* Initialize wm8739 */ /* reset */ - wm8739_write(client, R15, 0x00); + wm8739_write(sd, R15, 0x00); /* filter setting, high path, offet clear */ - wm8739_write(client, R5, 0x000); + wm8739_write(sd, R5, 0x000); /* ADC, OSC, Power Off mode Disable */ - wm8739_write(client, R6, 0x000); + wm8739_write(sd, R6, 0x000); /* Digital Audio interface format: Enable Master mode, 24 bit, MSB first/left justified */ - wm8739_write(client, R7, 0x049); + wm8739_write(sd, R7, 0x049); /* sampling control: normal, 256fs, 48KHz sampling rate */ - wm8739_write(client, R8, 0x000); + wm8739_write(sd, R8, 0x000); /* activate */ - wm8739_write(client, R9, 0x001); + wm8739_write(sd, R9, 0x001); /* set volume/mute */ - wm8739_set_audio(client); + wm8739_set_audio(sd); return 0; } static int wm8739_remove(struct i2c_client *client) { - kfree(i2c_get_clientdata(client)); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); return 0; } -- cgit v1.2.3 From f31539f27272076e6d9a47292196d7ca1008b467 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 29 Nov 2008 17:03:22 +0100 Subject: wm8775: convert to v4l2_subdev. From: Hans Verkuil Priority: normal Signed-off-by: Hans Verkuil --- linux/drivers/media/video/wm8775.c | 221 ++++++++++++++++++++++--------------- 1 file changed, 133 insertions(+), 88 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/wm8775.c b/linux/drivers/media/video/wm8775.c index ff7fa162b..526926635 100644 --- a/linux/drivers/media/video/wm8775.c +++ b/linux/drivers/media/video/wm8775.c @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include #include "compat.h" @@ -55,16 +55,23 @@ enum { }; struct wm8775_state { + struct v4l2_subdev sd; u8 input; /* Last selected input (0-0xf) */ u8 muted; }; -static int wm8775_write(struct i2c_client *client, int reg, u16 val) +static inline struct wm8775_state *to_state(struct v4l2_subdev *sd) { + return container_of(sd, struct wm8775_state, sd); +} + +static int wm8775_write(struct v4l2_subdev *sd, int reg, u16 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); int i; if (reg < 0 || reg >= TOT_REGS) { - v4l_err(client, "Invalid register R%d\n", reg); + v4l2_err(sd, "Invalid register R%d\n", reg); return -1; } @@ -72,84 +79,117 @@ static int wm8775_write(struct i2c_client *client, int reg, u16 val) if (i2c_smbus_write_byte_data(client, (reg << 1) | (val >> 8), val & 0xff) == 0) return 0; - v4l_err(client, "I2C: cannot write %03x to register R%d\n", val, reg); + v4l2_err(sd, "I2C: cannot write %03x to register R%d\n", val, reg); return -1; } -static int wm8775_command(struct i2c_client *client, unsigned cmd, void *arg) +static int wm8775_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) { - struct wm8775_state *state = i2c_get_clientdata(client); - struct v4l2_routing *route = arg; - struct v4l2_control *ctrl = arg; - - switch (cmd) { - case VIDIOC_INT_G_AUDIO_ROUTING: - route->input = state->input; - route->output = 0; - break; - - case VIDIOC_INT_S_AUDIO_ROUTING: - /* There are 4 inputs and one output. Zero or more inputs - are multiplexed together to the output. Hence there are - 16 combinations. - If only one input is active (the normal case) then the - input values 1, 2, 4 or 8 should be used. */ - if (route->input > 15) { - v4l_err(client, "Invalid input %d.\n", route->input); - return -EINVAL; - } - state->input = route->input; - if (state->muted) - break; - wm8775_write(client, R21, 0x0c0); - wm8775_write(client, R14, 0x1d4); - wm8775_write(client, R15, 0x1d4); - wm8775_write(client, R21, 0x100 + state->input); - break; - - case VIDIOC_G_CTRL: - if (ctrl->id != V4L2_CID_AUDIO_MUTE) - return -EINVAL; - ctrl->value = state->muted; - break; - - case VIDIOC_S_CTRL: - if (ctrl->id != V4L2_CID_AUDIO_MUTE) - return -EINVAL; - state->muted = ctrl->value; - wm8775_write(client, R21, 0x0c0); - wm8775_write(client, R14, 0x1d4); - wm8775_write(client, R15, 0x1d4); - if (!state->muted) - wm8775_write(client, R21, 0x100 + state->input); - break; - - case VIDIOC_G_CHIP_IDENT: - return v4l2_chip_ident_i2c_client(client, - arg, V4L2_IDENT_WM8775, 0); - - case VIDIOC_LOG_STATUS: - v4l_info(client, "Input: %d%s\n", state->input, - state->muted ? " (muted)" : ""); - break; - - case VIDIOC_S_FREQUENCY: - /* If I remove this, then it can happen that I have no - sound the first time I tune from static to a valid channel. - It's difficult to reproduce and is almost certainly related - to the zero cross detect circuit. */ - wm8775_write(client, R21, 0x0c0); - wm8775_write(client, R14, 0x1d4); - wm8775_write(client, R15, 0x1d4); - wm8775_write(client, R21, 0x100 + state->input); - break; - - default: + struct wm8775_state *state = to_state(sd); + + /* There are 4 inputs and one output. Zero or more inputs + are multiplexed together to the output. Hence there are + 16 combinations. + If only one input is active (the normal case) then the + input values 1, 2, 4 or 8 should be used. */ + if (route->input > 15) { + v4l2_err(sd, "Invalid input %d.\n", route->input); return -EINVAL; } + state->input = route->input; + if (state->muted) + return 0; + wm8775_write(sd, R21, 0x0c0); + wm8775_write(sd, R14, 0x1d4); + wm8775_write(sd, R15, 0x1d4); + wm8775_write(sd, R21, 0x100 + state->input); + return 0; +} + +static int wm8775_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct wm8775_state *state = to_state(sd); + + if (ctrl->id != V4L2_CID_AUDIO_MUTE) + return -EINVAL; + ctrl->value = state->muted; + return 0; +} + +static int wm8775_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct wm8775_state *state = to_state(sd); + + if (ctrl->id != V4L2_CID_AUDIO_MUTE) + return -EINVAL; + state->muted = ctrl->value; + wm8775_write(sd, R21, 0x0c0); + wm8775_write(sd, R14, 0x1d4); + wm8775_write(sd, R15, 0x1d4); + if (!state->muted) + wm8775_write(sd, R21, 0x100 + state->input); + return 0; +} + +static int wm8775_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_WM8775, 0); +} + +static int wm8775_log_status(struct v4l2_subdev *sd) +{ + struct wm8775_state *state = to_state(sd); + + v4l2_info(sd, "Input: %d%s\n", state->input, + state->muted ? " (muted)" : ""); return 0; } +static int wm8775_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) +{ + struct wm8775_state *state = to_state(sd); + + /* If I remove this, then it can happen that I have no + sound the first time I tune from static to a valid channel. + It's difficult to reproduce and is almost certainly related + to the zero cross detect circuit. */ + wm8775_write(sd, R21, 0x0c0); + wm8775_write(sd, R14, 0x1d4); + wm8775_write(sd, R15, 0x1d4); + wm8775_write(sd, R21, 0x100 + state->input); + return 0; +} + +static int wm8775_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops wm8775_core_ops = { + .log_status = wm8775_log_status, + .g_chip_ident = wm8775_g_chip_ident, + .g_ctrl = wm8775_g_ctrl, + .s_ctrl = wm8775_s_ctrl, +}; + +static const struct v4l2_subdev_tuner_ops wm8775_tuner_ops = { + .s_frequency = wm8775_s_frequency, +}; + +static const struct v4l2_subdev_audio_ops wm8775_audio_ops = { + .s_routing = wm8775_s_routing, +}; + +static const struct v4l2_subdev_ops wm8775_ops = { + .core = &wm8775_core_ops, + .tuner = &wm8775_tuner_ops, + .audio = &wm8775_audio_ops, +}; + /* ----------------------------------------------------------------------- */ /* i2c implementation */ @@ -163,56 +203,61 @@ static int wm8775_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct wm8775_state *state; + struct v4l2_subdev *sd; /* Check if the adapter supports the needed features */ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -EIO; - v4l_info(client, "chip found @ 0x%x (%s)\n", + v4l_info(client, "chip found @ 0x%02x (%s)\n", client->addr << 1, client->adapter->name); state = kmalloc(sizeof(struct wm8775_state), GFP_KERNEL); if (state == NULL) return -ENOMEM; + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &wm8775_ops); state->input = 2; state->muted = 0; - i2c_set_clientdata(client, state); /* Initialize wm8775 */ /* RESET */ - wm8775_write(client, R23, 0x000); + wm8775_write(sd, R23, 0x000); /* Disable zero cross detect timeout */ - wm8775_write(client, R7, 0x000); + wm8775_write(sd, R7, 0x000); /* Left justified, 24-bit mode */ - wm8775_write(client, R11, 0x021); + wm8775_write(sd, R11, 0x021); /* Master mode, clock ratio 256fs */ - wm8775_write(client, R12, 0x102); + wm8775_write(sd, R12, 0x102); /* Powered up */ - wm8775_write(client, R13, 0x000); + wm8775_write(sd, R13, 0x000); /* ADC gain +2.5dB, enable zero cross */ - wm8775_write(client, R14, 0x1d4); + wm8775_write(sd, R14, 0x1d4); /* ADC gain +2.5dB, enable zero cross */ - wm8775_write(client, R15, 0x1d4); + wm8775_write(sd, R15, 0x1d4); /* ALC Stereo, ALC target level -1dB FS max gain +8dB */ - wm8775_write(client, R16, 0x1bf); + wm8775_write(sd, R16, 0x1bf); /* Enable gain control, use zero cross detection, ALC hold time 42.6 ms */ - wm8775_write(client, R17, 0x185); + wm8775_write(sd, R17, 0x185); /* ALC gain ramp up delay 34 s, ALC gain ramp down delay 33 ms */ - wm8775_write(client, R18, 0x0a2); + wm8775_write(sd, R18, 0x0a2); /* Enable noise gate, threshold -72dBfs */ - wm8775_write(client, R19, 0x005); + wm8775_write(sd, R19, 0x005); /* Transient window 4ms, lower PGA gain limit -1dB */ - wm8775_write(client, R20, 0x07a); + wm8775_write(sd, R20, 0x07a); /* LRBOTH = 1, use input 2. */ - wm8775_write(client, R21, 0x102); + wm8775_write(sd, R21, 0x102); return 0; } static int wm8775_remove(struct i2c_client *client) { - kfree(i2c_get_clientdata(client)); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); return 0; } -- cgit v1.2.3 From 427b70d46df23ba54523af265fd2bc28d061e435 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 29 Nov 2008 23:38:23 +0100 Subject: ivtv/ivtvfb: convert to v4l2_device/v4l2_subdev. From: Hans Verkuil Priority: normal Signed-off-by: Hans Verkuil --- linux/drivers/media/video/ivtv/ivtv-controls.c | 16 +- linux/drivers/media/video/ivtv/ivtv-driver.c | 214 +++++----------- linux/drivers/media/video/ivtv/ivtv-driver.h | 52 ++-- linux/drivers/media/video/ivtv/ivtv-fileops.c | 44 +--- linux/drivers/media/video/ivtv/ivtv-gpio.c | 324 +++++++++++++++--------- linux/drivers/media/video/ivtv/ivtv-gpio.h | 3 +- linux/drivers/media/video/ivtv/ivtv-i2c.c | 338 +++++-------------------- linux/drivers/media/video/ivtv/ivtv-i2c.h | 13 +- linux/drivers/media/video/ivtv/ivtv-ioctl.c | 73 +++--- linux/drivers/media/video/ivtv/ivtv-routing.c | 12 +- linux/drivers/media/video/ivtv/ivtv-streams.c | 13 +- linux/drivers/media/video/ivtv/ivtv-vbi.c | 17 +- linux/drivers/media/video/ivtv/ivtvfb.c | 91 ++++--- 13 files changed, 503 insertions(+), 707 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/ivtv/ivtv-controls.c b/linux/drivers/media/video/ivtv/ivtv-controls.c index 48e103be7..62aa06f5d 100644 --- a/linux/drivers/media/video/ivtv/ivtv-controls.c +++ b/linux/drivers/media/video/ivtv/ivtv-controls.c @@ -63,7 +63,7 @@ int ivtv_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qctrl) case V4L2_CID_HUE: case V4L2_CID_SATURATION: case V4L2_CID_CONTRAST: - if (itv->video_dec_func(itv, VIDIOC_QUERYCTRL, qctrl)) + if (v4l2_subdev_call(itv->sd_video, core, queryctrl, qctrl)) qctrl->flags |= V4L2_CTRL_FLAG_DISABLED; return 0; @@ -73,7 +73,7 @@ int ivtv_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qctrl) case V4L2_CID_AUDIO_BASS: case V4L2_CID_AUDIO_TREBLE: case V4L2_CID_AUDIO_LOUDNESS: - if (ivtv_i2c_hw(itv, itv->card->hw_audio_ctrl, VIDIOC_QUERYCTRL, qctrl)) + if (v4l2_subdev_call(itv->sd_audio, core, queryctrl, qctrl)) qctrl->flags |= V4L2_CTRL_FLAG_DISABLED; return 0; @@ -122,7 +122,7 @@ static int ivtv_s_ctrl(struct ivtv *itv, struct v4l2_control *vctrl) case V4L2_CID_HUE: case V4L2_CID_SATURATION: case V4L2_CID_CONTRAST: - return itv->video_dec_func(itv, VIDIOC_S_CTRL, vctrl); + return v4l2_subdev_call(itv->sd_video, core, s_ctrl, vctrl); case V4L2_CID_AUDIO_VOLUME: case V4L2_CID_AUDIO_MUTE: @@ -130,7 +130,7 @@ static int ivtv_s_ctrl(struct ivtv *itv, struct v4l2_control *vctrl) case V4L2_CID_AUDIO_BASS: case V4L2_CID_AUDIO_TREBLE: case V4L2_CID_AUDIO_LOUDNESS: - return ivtv_i2c_hw(itv, itv->card->hw_audio_ctrl, VIDIOC_S_CTRL, vctrl); + return v4l2_subdev_call(itv->sd_audio, core, s_ctrl, vctrl); default: IVTV_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id); @@ -147,7 +147,7 @@ static int ivtv_g_ctrl(struct ivtv *itv, struct v4l2_control *vctrl) case V4L2_CID_HUE: case V4L2_CID_SATURATION: case V4L2_CID_CONTRAST: - return itv->video_dec_func(itv, VIDIOC_G_CTRL, vctrl); + return v4l2_subdev_call(itv->sd_video, core, g_ctrl, vctrl); case V4L2_CID_AUDIO_VOLUME: case V4L2_CID_AUDIO_MUTE: @@ -155,7 +155,7 @@ static int ivtv_g_ctrl(struct ivtv *itv, struct v4l2_control *vctrl) case V4L2_CID_AUDIO_BASS: case V4L2_CID_AUDIO_TREBLE: case V4L2_CID_AUDIO_LOUDNESS: - return ivtv_i2c_hw(itv, itv->card->hw_audio_ctrl, VIDIOC_G_CTRL, vctrl); + return v4l2_subdev_call(itv->sd_audio, core, g_ctrl, vctrl); default: IVTV_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id); return -EINVAL; @@ -268,7 +268,7 @@ int ivtv_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c) fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt.fmt.pix.width = itv->params.width / (is_mpeg1 ? 2 : 1); fmt.fmt.pix.height = itv->params.height; - itv->video_dec_func(itv, VIDIOC_S_FMT, &fmt); + v4l2_subdev_call(itv->sd_video, video, s_fmt, &fmt); } err = cx2341x_update(itv, ivtv_api_func, &itv->params, &p); if (!err && itv->params.stream_vbi_fmt != p.stream_vbi_fmt) @@ -279,7 +279,7 @@ int ivtv_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c) /* The audio clock of the digitizer must match the codec sample rate otherwise you get some very strange effects. */ if (idx < sizeof(freqs)) - ivtv_call_i2c_clients(itv, VIDIOC_INT_AUDIO_CLOCK_FREQ, &freqs[idx]); + ivtv_call_all(itv, audio, s_clock_freq, freqs[idx]); return err; } return -EINVAL; diff --git a/linux/drivers/media/video/ivtv/ivtv-driver.c b/linux/drivers/media/video/ivtv/ivtv-driver.c index db866d912..b29625ca2 100644 --- a/linux/drivers/media/video/ivtv/ivtv-driver.c +++ b/linux/drivers/media/video/ivtv/ivtv-driver.c @@ -60,9 +60,6 @@ #include #include "tuner-xc2028.h" -/* var to keep track of the number of array elements in use */ -int ivtv_cards_active; - /* If you have already X v4l cards, then set this to X. This way the device numbers stay matched. Example: you have a WinTV card without radio and a PVR-350 with. Normally this would give a @@ -70,12 +67,6 @@ int ivtv_cards_active; setting this to 1 you ensure that radio0 is now also radio1. */ int ivtv_first_minor; -/* Master variable for all ivtv info */ -struct ivtv *ivtv_cards[IVTV_MAX_CARDS]; - -/* Protects ivtv_cards_active */ -DEFINE_SPINLOCK(ivtv_cards_lock); - /* add your revision and whatnot here */ static struct pci_device_id ivtv_pci_tbl[] __devinitdata = { {PCI_VENDOR_ID_ICOMP, PCI_DEVICE_ID_IVTV15, @@ -87,6 +78,9 @@ static struct pci_device_id ivtv_pci_tbl[] __devinitdata = { MODULE_DEVICE_TABLE(pci,ivtv_pci_tbl); +/* ivtv instance counter */ +static atomic_t ivtv_instance = ATOMIC_INIT(0); + /* Parameter declarations */ static int cardtype[IVTV_MAX_CARDS]; static int tuner[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1, @@ -599,9 +593,9 @@ static void ivtv_process_options(struct ivtv *itv) itv->options.kilobytes[IVTV_DEC_STREAM_TYPE_MPG] = dec_mpg_buffers * 1024; itv->options.kilobytes[IVTV_DEC_STREAM_TYPE_YUV] = dec_yuv_buffers * 1024; itv->options.kilobytes[IVTV_DEC_STREAM_TYPE_VBI] = dec_vbi_buffers; - itv->options.cardtype = cardtype[itv->num]; - itv->options.tuner = tuner[itv->num]; - itv->options.radio = radio[itv->num]; + itv->options.cardtype = cardtype[itv->instance]; + itv->options.tuner = tuner[itv->instance]; + itv->options.radio = radio[itv->instance]; itv->options.newi2c = newi2c; if (tunertype < -1 || tunertype > 1) { IVTV_WARN("Invalid tunertype argument, will autodetect instead\n"); @@ -688,7 +682,7 @@ static int __devinit ivtv_init_struct1(struct ivtv *itv) spin_lock_init(&itv->lock); spin_lock_init(&itv->dma_reg_lock); - itv->irq_work_queues = create_singlethread_workqueue(itv->name); + itv->irq_work_queues = create_singlethread_workqueue(itv->device.name); if (itv->irq_work_queues == NULL) { IVTV_ERR("Could not create ivtv workqueue\n"); return -1; @@ -774,12 +768,6 @@ static void __devinit ivtv_init_struct2(struct ivtv *itv) i = 0; itv->active_input = i; itv->audio_input = itv->card->video_inputs[i].audio_index; - if (itv->card->hw_all & IVTV_HW_CX25840) - itv->video_dec_func = ivtv_cx25840; - else if (itv->card->hw_all & IVTV_HW_SAA717X) - itv->video_dec_func = ivtv_saa717x; - else - itv->video_dec_func = ivtv_saa7115; } static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *dev, @@ -792,21 +780,21 @@ static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *dev, IVTV_DEBUG_INFO("Enabling pci device\n"); if (pci_enable_device(dev)) { - IVTV_ERR("Can't enable device %d!\n", itv->num); + IVTV_ERR("Can't enable device!\n"); return -EIO; } if (pci_set_dma_mask(dev, 0xffffffff)) { - IVTV_ERR("No suitable DMA available on card %d.\n", itv->num); + IVTV_ERR("No suitable DMA available.\n"); return -EIO; } if (!request_mem_region(itv->base_addr, IVTV_ENCODER_SIZE, "ivtv encoder")) { - IVTV_ERR("Cannot request encoder memory region on card %d.\n", itv->num); + IVTV_ERR("Cannot request encoder memory region.\n"); return -EIO; } if (!request_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE, "ivtv registers")) { - IVTV_ERR("Cannot request register memory region on card %d.\n", itv->num); + IVTV_ERR("Cannot request register memory region.\n"); release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE); return -EIO; } @@ -814,7 +802,7 @@ static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *dev, if (itv->has_cx23415 && !request_mem_region(itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE, "ivtv decoder")) { - IVTV_ERR("Cannot request decoder memory region on card %d.\n", itv->num); + IVTV_ERR("Cannot request decoder memory region.\n"); release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE); release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE); return -EIO; @@ -857,69 +845,11 @@ static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *dev, return 0; } -#ifdef MODULE -static u32 ivtv_request_module(struct ivtv *itv, u32 hw, - const char *name, u32 id) -{ - if ((hw & id) == 0) - return hw; - if (request_module(name) != 0) { - IVTV_ERR("Failed to load module %s\n", name); - return hw & ~id; - } - IVTV_DEBUG_INFO("Loaded module %s\n", name); - return hw; -} -#endif - static void ivtv_load_and_init_modules(struct ivtv *itv) { u32 hw = itv->card->hw_all; unsigned i; -#ifdef MODULE - /* load modules */ -#ifdef CONFIG_MEDIA_TUNER_MODULE - hw = ivtv_request_module(itv, hw, "tuner", IVTV_HW_TUNER); -#endif -#ifdef CONFIG_VIDEO_CX25840_MODULE - hw = ivtv_request_module(itv, hw, "cx25840", IVTV_HW_CX25840); -#endif -#ifdef CONFIG_VIDEO_SAA711X_MODULE - hw = ivtv_request_module(itv, hw, "saa7115", IVTV_HW_SAA711X); -#endif -#ifdef CONFIG_VIDEO_SAA7127_MODULE - hw = ivtv_request_module(itv, hw, "saa7127", IVTV_HW_SAA7127); -#endif -#ifdef CONFIG_VIDEO_SAA717X_MODULE - hw = ivtv_request_module(itv, hw, "saa717x", IVTV_HW_SAA717X); -#endif -#ifdef CONFIG_VIDEO_UPD64031A_MODULE - hw = ivtv_request_module(itv, hw, "upd64031a", IVTV_HW_UPD64031A); -#endif -#ifdef CONFIG_VIDEO_UPD64083_MODULE - hw = ivtv_request_module(itv, hw, "upd64083", IVTV_HW_UPD6408X); -#endif -#ifdef CONFIG_VIDEO_MSP3400_MODULE - hw = ivtv_request_module(itv, hw, "msp3400", IVTV_HW_MSP34XX); -#endif -#ifdef CONFIG_VIDEO_VP27SMPX_MODULE - hw = ivtv_request_module(itv, hw, "vp27smpx", IVTV_HW_VP27SMPX); -#endif -#ifdef CONFIG_VIDEO_WM8775_MODULE - hw = ivtv_request_module(itv, hw, "wm8775", IVTV_HW_WM8775); -#endif -#ifdef CONFIG_VIDEO_WM8739_MODULE - hw = ivtv_request_module(itv, hw, "wm8739", IVTV_HW_WM8739); -#endif -#ifdef CONFIG_VIDEO_CS53L32A_MODULE - hw = ivtv_request_module(itv, hw, "cs53l32a", IVTV_HW_CS53L32A); -#endif -#ifdef CONFIG_VIDEO_M52790_MODULE - hw = ivtv_request_module(itv, hw, "m52790", IVTV_HW_M52790); -#endif -#endif - /* check which i2c devices are actually found */ for (i = 0; i < 32; i++) { u32 device = 1 << i; @@ -931,11 +861,21 @@ static void ivtv_load_and_init_modules(struct ivtv *itv) itv->hw_flags |= device; continue; } - ivtv_i2c_register(itv, i); - if (ivtv_i2c_hw_addr(itv, device) > 0) + if (ivtv_i2c_register(itv, i) == 0) itv->hw_flags |= device; } + if (itv->card->hw_all & IVTV_HW_CX25840) + itv->sd_video = ivtv_find_hw(itv, IVTV_HW_CX25840); + else if (itv->card->hw_all & IVTV_HW_SAA717X) + itv->sd_video = ivtv_find_hw(itv, IVTV_HW_SAA717X); + else if (itv->card->hw_all & IVTV_HW_SAA7114) + itv->sd_video = ivtv_find_hw(itv, IVTV_HW_SAA7114); + else + itv->sd_video = ivtv_find_hw(itv, IVTV_HW_SAA7115); + itv->sd_audio = ivtv_find_hw(itv, itv->card->hw_audio_ctrl); + itv->sd_muxer = ivtv_find_hw(itv, itv->card->hw_muxer); + hw = itv->hw_flags; if (itv->card->type == IVTV_CARD_CX23416GYC) { @@ -953,7 +893,7 @@ static void ivtv_load_and_init_modules(struct ivtv *itv) /* The crystal frequency of GVMVPRX is 24.576MHz */ crystal_freq.freq = SAA7115_FREQ_24_576_MHZ; crystal_freq.flags = SAA7115_FREQ_FL_UCGC; - itv->video_dec_func(itv, VIDIOC_INT_S_CRYSTAL_FREQ, &crystal_freq); + v4l2_subdev_call(itv->sd_video, video, s_crystal_freq, &crystal_freq); } if (hw & IVTV_HW_CX25840) { @@ -971,7 +911,7 @@ static void ivtv_load_and_init_modules(struct ivtv *itv) /* determine the exact saa711x model */ itv->hw_flags &= ~IVTV_HW_SAA711X; - ivtv_saa7115(itv, VIDIOC_G_CHIP_IDENT, &v); + ivtv_call_hw(itv, IVTV_HW_SAA711X, core, g_chip_ident, &v); if (v.ident == V4L2_IDENT_SAA7114) { itv->hw_flags |= IVTV_HW_SAA7114; /* VBI is not yet supported by the saa7114 driver. */ @@ -1005,28 +945,20 @@ static int __devinit ivtv_probe(struct pci_dev *dev, int vbi_buf_size; struct ivtv *itv; - spin_lock(&ivtv_cards_lock); - - /* Make sure we've got a place for this card */ - if (ivtv_cards_active == IVTV_MAX_CARDS) { - printk(KERN_ERR "ivtv: Maximum number of cards detected (%d)\n", - ivtv_cards_active); - spin_unlock(&ivtv_cards_lock); - return -ENOMEM; - } - itv = kzalloc(sizeof(struct ivtv), GFP_ATOMIC); - if (itv == NULL) { - spin_unlock(&ivtv_cards_lock); + if (itv == NULL) return -ENOMEM; - } - ivtv_cards[ivtv_cards_active] = itv; itv->dev = dev; - itv->num = ivtv_cards_active++; - snprintf(itv->name, sizeof(itv->name), "ivtv%d", itv->num); - IVTV_INFO("Initializing card #%d\n", itv->num); + itv->instance = atomic_inc_return(&ivtv_instance) - 1; - spin_unlock(&ivtv_cards_lock); + retval = v4l2_device_register(&dev->dev, &itv->device); + if (retval) + return retval; + /* "ivtv + PCI ID" is a bit of a mouthful, so use + "ivtv + instance" instead. */ + snprintf(itv->device.name, sizeof(itv->device.name), + "ivtv%d", itv->instance); + IVTV_INFO("Initializing card %d\n", itv->instance); ivtv_process_options(itv); if (itv->options.cardtype == -1) { @@ -1047,8 +979,6 @@ static int __devinit ivtv_probe(struct pci_dev *dev, else if (retval == -ENXIO) goto free_mem; } - /* save itv in the pci struct for later use */ - pci_set_drvdata(dev, itv); /* map io memory */ IVTV_DEBUG_INFO("attempting ioremap at 0x%08x len 0x%08x\n", @@ -1090,7 +1020,9 @@ static int __devinit ivtv_probe(struct pci_dev *dev, goto free_io; } - ivtv_gpio_init(itv); + retval = ivtv_gpio_init(itv); + if (retval) + goto free_io; /* active i2c */ IVTV_DEBUG_INFO("activating i2c...\n"); @@ -1099,8 +1031,6 @@ static int __devinit ivtv_probe(struct pci_dev *dev, goto free_io; } - IVTV_DEBUG_INFO("Active card count: %d.\n", ivtv_cards_active); - if (itv->card->hw_all & IVTV_HW_TVEEPROM) { /* Based on the model number the cardtype may be changed. The PCI IDs are not always reliable. */ @@ -1195,7 +1125,7 @@ static int __devinit ivtv_probe(struct pci_dev *dev, setup.mode_mask = T_ANALOG_TV; /* matches TV tuners */ setup.tuner_callback = (setup.type == TUNER_XC2028) ? ivtv_reset_tuner_gpio : NULL; - ivtv_call_i2c_clients(itv, TUNER_SET_TYPE_ADDR, &setup); + ivtv_call_all(itv, tuner, s_type_addr, &setup); if (setup.type == TUNER_XC2028) { static struct xc2028_ctrl ctrl = { .fname = XC2028_DEFAULT_FIRMWARE, @@ -1205,7 +1135,7 @@ static int __devinit ivtv_probe(struct pci_dev *dev, .tuner = itv->options.tuner, .priv = &ctrl, }; - ivtv_call_i2c_clients(itv, TUNER_SET_CONFIG, &cfg); + ivtv_call_all(itv, tuner, s_config, &cfg); } } @@ -1214,11 +1144,11 @@ static int __devinit ivtv_probe(struct pci_dev *dev, itv->tuner_std = itv->std; if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) { - ivtv_call_i2c_clients(itv, VIDIOC_INT_S_STD_OUTPUT, &itv->std); + ivtv_call_all(itv, video, s_std_output, itv->std); /* Turn off the output signal. The mpeg decoder is not yet active so without this you would get a green image until the mpeg decoder becomes active. */ - ivtv_saa7127(itv, VIDIOC_STREAMOFF, NULL); + ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 0); } /* clear interrupt mask, effectively disabling interrupts */ @@ -1226,7 +1156,7 @@ static int __devinit ivtv_probe(struct pci_dev *dev, /* Register IRQ */ retval = request_irq(itv->dev->irq, ivtv_irq_handler, - IRQF_SHARED | IRQF_DISABLED, itv->name, (void *)itv); + IRQF_SHARED | IRQF_DISABLED, itv->device.name, (void *)itv); if (retval) { IVTV_ERR("Failed to register irq %d\n", retval); goto free_i2c; @@ -1242,7 +1172,7 @@ static int __devinit ivtv_probe(struct pci_dev *dev, IVTV_ERR("Error %d registering devices\n", retval); goto free_streams; } - IVTV_INFO("Initialized card #%d: %s\n", itv->num, itv->card_name); + IVTV_INFO("Initialized card: %s\n", itv->card_name); return 0; free_streams: @@ -1265,10 +1195,8 @@ err: retval = -ENODEV; IVTV_ERR("Error %d on initialization\n", retval); - spin_lock(&ivtv_cards_lock); - kfree(ivtv_cards[ivtv_cards_active]); - ivtv_cards[ivtv_cards_active] = NULL; - spin_unlock(&ivtv_cards_lock); + v4l2_device_unregister(&itv->device); + kfree(itv); return retval; } @@ -1308,10 +1236,11 @@ int ivtv_init_on_first_open(struct ivtv *itv) if (itv->card->hw_all & IVTV_HW_CX25840) { struct v4l2_control ctrl; + v4l2_subdev_call(itv->sd_video, core, init, 0); /* CX25840_CID_ENABLE_PVR150_WORKAROUND */ ctrl.id = V4L2_CID_PRIVATE_BASE; ctrl.value = itv->pvr150_workaround; - itv->video_dec_func(itv, VIDIOC_S_CTRL, &ctrl); + v4l2_subdev_call(itv->sd_video, core, s_ctrl, &ctrl); } vf.tuner = 0; @@ -1341,7 +1270,7 @@ int ivtv_init_on_first_open(struct ivtv *itv) /* Turn on the TV-out: ivtv_init_mpeg_decoder() initializes the mpeg decoder so now the saa7127 receives a proper signal. */ - ivtv_saa7127(itv, VIDIOC_STREAMON, NULL); + ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 1); ivtv_init_mpeg_decoder(itv); } ivtv_s_std(NULL, &fh, &itv->tuner_std); @@ -1366,9 +1295,11 @@ int ivtv_init_on_first_open(struct ivtv *itv) static void ivtv_remove(struct pci_dev *pci_dev) { - struct ivtv *itv = pci_get_drvdata(pci_dev); + struct v4l2_device *dev = dev_get_drvdata(&pci_dev->dev); + struct ivtv *itv = to_ivtv(dev); + int i; - IVTV_DEBUG_INFO("Removing Card #%d\n", itv->num); + IVTV_DEBUG_INFO("Removing card\n"); if (test_bit(IVTV_F_I_INITED, &itv->i_flags)) { /* Stop all captures */ @@ -1381,7 +1312,7 @@ static void ivtv_remove(struct pci_dev *pci_dev) /* Turn off the TV-out */ if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) - ivtv_saa7127(itv, VIDIOC_STREAMOFF, NULL); + ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 0); if (atomic_read(&itv->decoding) > 0) { int type; @@ -1406,6 +1337,8 @@ static void ivtv_remove(struct pci_dev *pci_dev) ivtv_streams_cleanup(itv, 1); ivtv_udma_free(itv); + v4l2_device_unregister(&itv->device); + exit_ivtv_i2c(itv); free_irq(itv->dev->irq, (void *)itv); @@ -1417,8 +1350,11 @@ static void ivtv_remove(struct pci_dev *pci_dev) release_mem_region(itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE); pci_disable_device(itv->dev); + for (i = 0; i < IVTV_VBI_FRAMES; i++) + kfree(itv->vbi.sliced_mpeg_data[i]); - IVTV_INFO("Removed %s, card #%d\n", itv->card_name, itv->num); + printk(KERN_INFO "ivtv: Removed %s\n", itv->card_name); + kfree(itv); } /* define a pci_driver for card detection */ @@ -1431,54 +1367,36 @@ static struct pci_driver ivtv_pci_driver = { static int module_start(void) { - printk(KERN_INFO "ivtv: Start initialization, version %s\n", IVTV_VERSION); - - memset(ivtv_cards, 0, sizeof(ivtv_cards)); + printk(KERN_INFO "ivtv: Start initialization, version %s\n", IVTV_VERSION); /* Validate parameters */ if (ivtv_first_minor < 0 || ivtv_first_minor >= IVTV_MAX_CARDS) { - printk(KERN_ERR "ivtv: Exiting, ivtv_first_minor must be between 0 and %d\n", + printk(KERN_ERR "ivtv: Exiting, ivtv_first_minor must be between 0 and %d\n", IVTV_MAX_CARDS - 1); return -1; } if (ivtv_debug < 0 || ivtv_debug > 2047) { ivtv_debug = 0; - printk(KERN_INFO "ivtv: Debug value must be >= 0 and <= 2047\n"); + printk(KERN_INFO "ivtv: Debug value must be >= 0 and <= 2047\n"); } if (pci_register_driver(&ivtv_pci_driver)) { - printk(KERN_ERR "ivtv: Error detecting PCI card\n"); + printk(KERN_ERR "ivtv: Error detecting PCI card\n"); return -ENODEV; } - printk(KERN_INFO "ivtv: End initialization\n"); + printk(KERN_INFO "ivtv: End initialization\n"); return 0; } static void module_cleanup(void) { - int i, j; - pci_unregister_driver(&ivtv_pci_driver); - - spin_lock(&ivtv_cards_lock); - for (i = 0; i < ivtv_cards_active; i++) { - if (ivtv_cards[i] == NULL) - continue; - for (j = 0; j < IVTV_VBI_FRAMES; j++) { - kfree(ivtv_cards[i]->vbi.sliced_mpeg_data[j]); - } - kfree(ivtv_cards[i]); - } - spin_unlock(&ivtv_cards_lock); } /* Note: These symbols are exported because they are used by the ivtvfb framebuffer module and an infrared module for the IR-blaster. */ EXPORT_SYMBOL(ivtv_set_irq_mask); -EXPORT_SYMBOL(ivtv_cards_active); -EXPORT_SYMBOL(ivtv_cards); -EXPORT_SYMBOL(ivtv_cards_lock); EXPORT_SYMBOL(ivtv_api); EXPORT_SYMBOL(ivtv_vapi); EXPORT_SYMBOL(ivtv_vapi_result); diff --git a/linux/drivers/media/video/ivtv/ivtv-driver.h b/linux/drivers/media/video/ivtv/ivtv-driver.h index 9128df0f3..51f6e54ba 100644 --- a/linux/drivers/media/video/ivtv/ivtv-driver.h +++ b/linux/drivers/media/video/ivtv/ivtv-driver.h @@ -62,6 +62,7 @@ #include #include #include +#include #include #include @@ -114,9 +115,6 @@ #define IVTV_REG_VPU (0x9058) #define IVTV_REG_APU (0xA064) -/* i2c stuff */ -#define I2C_CLIENTS_MAX 16 - /* debugging */ extern int ivtv_debug; @@ -133,12 +131,10 @@ extern int ivtv_debug; /* Flag to turn on high volume debugging */ #define IVTV_DBGFLG_HIGHVOL (1 << 10) -/* NOTE: extra space before comma in 'itv->num , ## args' is required for - gcc-2.95, otherwise it won't compile. */ #define IVTV_DEBUG(x, type, fmt, args...) \ do { \ if ((x) & ivtv_debug) \ - printk(KERN_INFO "ivtv%d " type ": " fmt, itv->num , ## args); \ + v4l2_info(&itv->device, " " type ": " fmt , ##args); \ } while (0) #define IVTV_DEBUG_WARN(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_WARN, "warn", fmt , ## args) #define IVTV_DEBUG_INFO(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_INFO, "info", fmt , ## args) @@ -153,8 +149,8 @@ extern int ivtv_debug; #define IVTV_DEBUG_HIGH_VOL(x, type, fmt, args...) \ do { \ - if (((x) & ivtv_debug) && (ivtv_debug & IVTV_DBGFLG_HIGHVOL)) \ - printk(KERN_INFO "ivtv%d " type ": " fmt, itv->num , ## args); \ + if (((x) & ivtv_debug) && (ivtv_debug & IVTV_DBGFLG_HIGHVOL)) \ + v4l2_info(&itv->device, " " type ": " fmt , ##args); \ } while (0) #define IVTV_DEBUG_HI_WARN(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_WARN, "warn", fmt , ## args) #define IVTV_DEBUG_HI_INFO(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_INFO, "info", fmt , ## args) @@ -168,9 +164,9 @@ extern int ivtv_debug; #define IVTV_DEBUG_HI_YUV(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_YUV, "yuv", fmt , ## args) /* Standard kernel messages */ -#define IVTV_ERR(fmt, args...) printk(KERN_ERR "ivtv%d: " fmt, itv->num , ## args) -#define IVTV_WARN(fmt, args...) printk(KERN_WARNING "ivtv%d: " fmt, itv->num , ## args) -#define IVTV_INFO(fmt, args...) printk(KERN_INFO "ivtv%d: " fmt, itv->num , ## args) +#define IVTV_ERR(fmt, args...) v4l2_err(&itv->device, fmt , ## args) +#define IVTV_WARN(fmt, args...) v4l2_warn(&itv->device, fmt , ## args) +#define IVTV_INFO(fmt, args...) v4l2_info(&itv->device, fmt , ## args) /* output modes (cx23415 only) */ #define OUT_NONE 0 @@ -597,8 +593,6 @@ struct ivtv_card; /* Struct to hold info about ivtv cards */ struct ivtv { /* General fixed card data */ - int num; /* board number, -1 during init! */ - char name[8]; /* board name for printk and interrupts (e.g. 'ivtv0') */ struct pci_dev *dev; /* PCI device */ const struct ivtv_card *card; /* card information */ const char *card_name; /* full name of the card */ @@ -610,14 +604,18 @@ struct ivtv { u32 v4l2_cap; /* V4L2 capabilities of card */ u32 hw_flags; /* hardware description of the board */ v4l2_std_id tuner_std; /* the norm of the card's tuner (fixed) */ - /* controlling video decoder function */ - int (*video_dec_func)(struct ivtv *, unsigned int, void *); + struct v4l2_subdev *sd_video; /* controlling video decoder subdev */ + struct v4l2_subdev *sd_audio; /* controlling audio subdev */ + struct v4l2_subdev *sd_muxer; /* controlling audio muxer subdev */ u32 base_addr; /* PCI resource base address */ volatile void __iomem *enc_mem; /* pointer to mapped encoder memory */ volatile void __iomem *dec_mem; /* pointer to mapped decoder memory */ volatile void __iomem *reg_mem; /* pointer to mapped registers */ struct ivtv_options options; /* user options */ + struct v4l2_device device; + struct v4l2_subdev sd_gpio; /* GPIO sub-device */ + u16 instance; /* High-level state info */ unsigned long i_flags; /* global ivtv flags */ @@ -677,7 +675,6 @@ struct ivtv { struct i2c_adapter i2c_adap; struct i2c_algo_bit_data i2c_algo; struct i2c_client i2c_client; - struct i2c_client *i2c_clients[I2C_CLIENTS_MAX];/* pointers to all I2C clients */ int i2c_state; /* i2c bit state */ struct mutex i2c_bus_lock; /* lock i2c bus */ @@ -723,11 +720,13 @@ struct ivtv { struct osd_info *osd_info; /* ivtvfb private OSD info */ }; +static inline struct ivtv *to_ivtv(struct v4l2_device *dev) +{ + return container_of(dev, struct ivtv, device); +} + /* Globals */ -extern struct ivtv *ivtv_cards[]; -extern int ivtv_cards_active; extern int ivtv_first_minor; -extern spinlock_t ivtv_cards_lock; /*==============Prototypes==================*/ @@ -787,4 +786,19 @@ static inline int ivtv_raw_vbi(const struct ivtv *itv) #define write_dec_sync(val, addr) \ do { write_dec(val, addr); read_dec(addr); } while (0) +/* Call the specified callback for all subdevs matching hw (if 0, then + match them all). Ignore any errors. */ +#define ivtv_call_hw(itv, hw, o, f, args...) \ + __v4l2_device_call_subdevs(&(itv)->device, !(hw) || (sd->grp_id & (hw)), o, f , ##args) + +#define ivtv_call_all(itv, o, f, args...) ivtv_call_hw(itv, 0, o, f , ##args) + +/* Call the specified callback for all subdevs matching hw (if 0, then + match them all). If the callback returns an error other than 0 or + -ENOIOCTLCMD, then return with that error code. */ +#define ivtv_call_hw_err(itv, hw, o, f, args...) \ + __v4l2_device_call_subdevs_until_err(&(itv)->device, !(hw) || (sd->grp_id & (hw)), o, f , ##args) + +#define ivtv_call_all_err(itv, o, f, args...) ivtv_call_hw_err(itv, 0, o, f , ##args) + #endif diff --git a/linux/drivers/media/video/ivtv/ivtv-fileops.c b/linux/drivers/media/video/ivtv/ivtv-fileops.c index 1c404e454..5eb587592 100644 --- a/linux/drivers/media/video/ivtv/ivtv-fileops.c +++ b/linux/drivers/media/video/ivtv/ivtv-fileops.c @@ -155,7 +155,7 @@ static void ivtv_dualwatch(struct ivtv *itv) new_stereo_mode = itv->params.audio_properties & stereo_mask; memset(&vt, 0, sizeof(vt)); - ivtv_call_i2c_clients(itv, VIDIOC_G_TUNER, &vt); + ivtv_call_all(itv, tuner, g_tuner, &vt); if (vt.audmode == V4L2_TUNER_MODE_LANG1_LANG2 && (vt.rxsubchans & V4L2_TUNER_SUB_LANG2)) new_stereo_mode = dual; @@ -857,7 +857,7 @@ int ivtv_v4l2_close(struct inode *inode, struct file *filp) /* Mark that the radio is no longer in use */ clear_bit(IVTV_F_I_RADIO_USER, &itv->i_flags); /* Switch tuner to TV */ - ivtv_call_i2c_clients(itv, VIDIOC_S_STD, &itv->std); + ivtv_call_all(itv, tuner, s_std, itv->std); /* Select correct audio input (i.e. TV tuner or Line in) */ ivtv_audio_set_io(itv); if (itv->hw_flags & IVTV_HW_SAA711X) @@ -865,7 +865,7 @@ int ivtv_v4l2_close(struct inode *inode, struct file *filp) struct v4l2_crystal_freq crystal_freq; crystal_freq.freq = SAA7115_FREQ_32_11_MHZ; crystal_freq.flags = 0; - ivtv_saa7115(itv, VIDIOC_INT_S_CRYSTAL_FREQ, &crystal_freq); + ivtv_call_hw(itv, IVTV_HW_SAA711X, video, s_crystal_freq, &crystal_freq); } if (atomic_read(&itv->capturing) > 0) { /* Undo video mute */ @@ -952,15 +952,14 @@ static int ivtv_serialized_open(struct ivtv_stream *s, struct file *filp) /* We have the radio */ ivtv_mute(itv); /* Switch tuner to radio */ - ivtv_call_i2c_clients(itv, AUDC_SET_RADIO, NULL); + ivtv_call_all(itv, tuner, s_radio); /* Select the correct audio input (i.e. radio tuner) */ ivtv_audio_set_io(itv); - if (itv->hw_flags & IVTV_HW_SAA711X) - { + if (itv->hw_flags & IVTV_HW_SAA711X) { struct v4l2_crystal_freq crystal_freq; crystal_freq.freq = SAA7115_FREQ_32_11_MHZ; crystal_freq.flags = SAA7115_FREQ_FL_APLL; - ivtv_saa7115(itv, VIDIOC_INT_S_CRYSTAL_FREQ, &crystal_freq); + ivtv_call_hw(itv, IVTV_HW_SAA711X, video, s_crystal_freq, &crystal_freq); } /* Done! Unmute and continue. */ ivtv_unmute(itv); @@ -981,37 +980,18 @@ static int ivtv_serialized_open(struct ivtv_stream *s, struct file *filp) int ivtv_v4l2_open(struct inode *inode, struct file *filp) { - int res, x, y = 0; + int res; struct ivtv *itv = NULL; struct ivtv_stream *s = NULL; - int minor = iminor(inode); - - /* Find which card this open was on */ - spin_lock(&ivtv_cards_lock); - for (x = 0; itv == NULL && x < ivtv_cards_active; x++) { - if (ivtv_cards[x] == NULL) - continue; - /* find out which stream this open was on */ - for (y = 0; y < IVTV_MAX_STREAMS; y++) { - s = &ivtv_cards[x]->streams[y]; - if (s->v4l2dev && s->v4l2dev->minor == minor) { - itv = ivtv_cards[x]; - break; - } - } - } - spin_unlock(&ivtv_cards_lock); + struct video_device *vdev = video_devdata(filp); - if (itv == NULL) { - /* Couldn't find a device registered - on that minor, shouldn't happen! */ - printk(KERN_WARNING "No ivtv device found on minor %d\n", minor); - return -ENXIO; - } + s = video_get_drvdata(vdev); + itv = s->itv; mutex_lock(&itv->serialize_lock); if (ivtv_init_on_first_open(itv)) { - IVTV_ERR("Failed to initialize on minor %d\n", minor); + IVTV_ERR("Failed to initialize on minor %d\n", + s->v4l2dev->minor); mutex_unlock(&itv->serialize_lock); return -ENXIO; } diff --git a/linux/drivers/media/video/ivtv/ivtv-gpio.c b/linux/drivers/media/video/ivtv/ivtv-gpio.c index 74a44844c..dc2850e87 100644 --- a/linux/drivers/media/video/ivtv/ivtv-gpio.c +++ b/linux/drivers/media/video/ivtv/ivtv-gpio.c @@ -144,22 +144,9 @@ int ivtv_reset_tuner_gpio(void *dev, int component, int cmd, int value) return 0; } -void ivtv_gpio_init(struct ivtv *itv) +static inline struct ivtv *sd_to_ivtv(struct v4l2_subdev *sd) { - u16 pin = 0; - - if (itv->card->xceive_pin) - pin = 1 << itv->card->xceive_pin; - - if ((itv->card->gpio_init.direction | pin) == 0) - return; - - IVTV_DEBUG_INFO("GPIO initial dir: %08x out: %08x\n", - read_reg(IVTV_REG_GPIO_DIR), read_reg(IVTV_REG_GPIO_OUT)); - - /* init output data then direction */ - write_reg(itv->card->gpio_init.initial_value | pin, IVTV_REG_GPIO_OUT); - write_reg(itv->card->gpio_init.direction | pin, IVTV_REG_GPIO_DIR); + return container_of(sd, struct ivtv, sd_gpio); } static struct v4l2_queryctrl gpio_ctrl_mute = { @@ -173,134 +160,231 @@ static struct v4l2_queryctrl gpio_ctrl_mute = { .flags = 0, }; -int ivtv_gpio(struct ivtv *itv, unsigned int command, void *arg) +static int subdev_s_clock_freq(struct v4l2_subdev *sd, u32 freq) { - struct v4l2_tuner *tuner = arg; - struct v4l2_control *ctrl = arg; - struct v4l2_routing *route = arg; + struct ivtv *itv = sd_to_ivtv(sd); u16 mask, data; - switch (command) { - case VIDIOC_INT_AUDIO_CLOCK_FREQ: - mask = itv->card->gpio_audio_freq.mask; - switch (*(u32 *)arg) { - case 32000: - data = itv->card->gpio_audio_freq.f32000; - break; - case 44100: - data = itv->card->gpio_audio_freq.f44100; - break; - case 48000: - default: - data = itv->card->gpio_audio_freq.f48000; - break; - } + mask = itv->card->gpio_audio_freq.mask; + switch (freq) { + case 32000: + data = itv->card->gpio_audio_freq.f32000; + break; + case 44100: + data = itv->card->gpio_audio_freq.f44100; + break; + case 48000: + default: + data = itv->card->gpio_audio_freq.f48000; break; + } + if (mask) + write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT); + return 0; +} - case VIDIOC_G_TUNER: - mask = itv->card->gpio_audio_detect.mask; - if (mask == 0 || (read_reg(IVTV_REG_GPIO_IN) & mask)) - tuner->rxsubchans = V4L2_TUNER_MODE_STEREO | - V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2; - else - tuner->rxsubchans = V4L2_TUNER_SUB_MONO; - return 0; +static int subdev_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct ivtv *itv = sd_to_ivtv(sd); + u16 mask; + + mask = itv->card->gpio_audio_detect.mask; + if (mask == 0 || (read_reg(IVTV_REG_GPIO_IN) & mask)) + vt->rxsubchans = V4L2_TUNER_MODE_STEREO | + V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2; + else + vt->rxsubchans = V4L2_TUNER_SUB_MONO; + return 0; +} - case VIDIOC_S_TUNER: - mask = itv->card->gpio_audio_mode.mask; - switch (tuner->audmode) { - case V4L2_TUNER_MODE_LANG1: - data = itv->card->gpio_audio_mode.lang1; - break; - case V4L2_TUNER_MODE_LANG2: - data = itv->card->gpio_audio_mode.lang2; - break; - case V4L2_TUNER_MODE_MONO: - data = itv->card->gpio_audio_mode.mono; - break; - case V4L2_TUNER_MODE_STEREO: - case V4L2_TUNER_MODE_LANG1_LANG2: - default: - data = itv->card->gpio_audio_mode.stereo; - break; - } - break; +static int subdev_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct ivtv *itv = sd_to_ivtv(sd); + u16 mask, data; - case AUDC_SET_RADIO: - mask = itv->card->gpio_audio_input.mask; - data = itv->card->gpio_audio_input.radio; + mask = itv->card->gpio_audio_mode.mask; + switch (vt->audmode) { + case V4L2_TUNER_MODE_LANG1: + data = itv->card->gpio_audio_mode.lang1; + break; + case V4L2_TUNER_MODE_LANG2: + data = itv->card->gpio_audio_mode.lang2; + break; + case V4L2_TUNER_MODE_MONO: + data = itv->card->gpio_audio_mode.mono; break; + case V4L2_TUNER_MODE_STEREO: + case V4L2_TUNER_MODE_LANG1_LANG2: + default: + data = itv->card->gpio_audio_mode.stereo; + break; + } + if (mask) + write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT); + return 0; +} + +static int subdev_s_radio(struct v4l2_subdev *sd) +{ + struct ivtv *itv = sd_to_ivtv(sd); + u16 mask, data; + + mask = itv->card->gpio_audio_input.mask; + data = itv->card->gpio_audio_input.radio; + if (mask) + write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT); + return 0; +} - case VIDIOC_S_STD: - mask = itv->card->gpio_audio_input.mask; +static int subdev_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct ivtv *itv = sd_to_ivtv(sd); + u16 mask, data; + + mask = itv->card->gpio_audio_input.mask; + data = itv->card->gpio_audio_input.tuner; + if (mask) + write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT); + return 0; +} + +static int subdev_s_audio_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +{ + struct ivtv *itv = sd_to_ivtv(sd); + u16 mask, data; + + if (route->input > 2) + return -EINVAL; + mask = itv->card->gpio_audio_input.mask; + switch (route->input) { + case 0: data = itv->card->gpio_audio_input.tuner; break; - - case VIDIOC_INT_S_AUDIO_ROUTING: - if (route->input > 2) - return -EINVAL; - mask = itv->card->gpio_audio_input.mask; - switch (route->input) { - case 0: - data = itv->card->gpio_audio_input.tuner; - break; - case 1: - data = itv->card->gpio_audio_input.linein; - break; - case 2: - default: - data = itv->card->gpio_audio_input.radio; - break; - } + case 1: + data = itv->card->gpio_audio_input.linein; + break; + case 2: + default: + data = itv->card->gpio_audio_input.radio; break; + } + if (mask) + write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT); + return 0; +} - case VIDIOC_G_CTRL: - if (ctrl->id != V4L2_CID_AUDIO_MUTE) - return -EINVAL; - mask = itv->card->gpio_audio_mute.mask; - data = itv->card->gpio_audio_mute.mute; - ctrl->value = (read_reg(IVTV_REG_GPIO_OUT) & mask) == data; - return 0; +static int subdev_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct ivtv *itv = sd_to_ivtv(sd); + u16 mask, data; - case VIDIOC_S_CTRL: - if (ctrl->id != V4L2_CID_AUDIO_MUTE) - return -EINVAL; - mask = itv->card->gpio_audio_mute.mask; - data = ctrl->value ? itv->card->gpio_audio_mute.mute : 0; - break; + if (ctrl->id != V4L2_CID_AUDIO_MUTE) + return -EINVAL; + mask = itv->card->gpio_audio_mute.mask; + data = itv->card->gpio_audio_mute.mute; + ctrl->value = (read_reg(IVTV_REG_GPIO_OUT) & mask) == data; + return 0; +} - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; +static int subdev_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct ivtv *itv = sd_to_ivtv(sd); + u16 mask, data; - if (qc->id != V4L2_CID_AUDIO_MUTE) - return -EINVAL; - *qc = gpio_ctrl_mute; - return 0; - } + if (ctrl->id != V4L2_CID_AUDIO_MUTE) + return -EINVAL; + mask = itv->card->gpio_audio_mute.mask; + data = ctrl->value ? itv->card->gpio_audio_mute.mute : 0; + if (mask) + write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT); + return 0; +} + +static int subdev_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) +{ + if (qc->id != V4L2_CID_AUDIO_MUTE) + return -EINVAL; + *qc = gpio_ctrl_mute; + return 0; +} + +static int subdev_log_status(struct v4l2_subdev *sd) +{ + struct ivtv *itv = sd_to_ivtv(sd); - case VIDIOC_LOG_STATUS: - IVTV_INFO("GPIO status: DIR=0x%04x OUT=0x%04x IN=0x%04x\n", + IVTV_INFO("GPIO status: DIR=0x%04x OUT=0x%04x IN=0x%04x\n", read_reg(IVTV_REG_GPIO_DIR), read_reg(IVTV_REG_GPIO_OUT), read_reg(IVTV_REG_GPIO_IN)); - return 0; + return 0; +} - case VIDIOC_INT_S_VIDEO_ROUTING: - if (route->input > 2) /* 0:Tuner 1:Composite 2:S-Video */ - return -EINVAL; - mask = itv->card->gpio_video_input.mask; - if (route->input == 0) - data = itv->card->gpio_video_input.tuner; - else if (route->input == 1) - data = itv->card->gpio_video_input.composite; - else - data = itv->card->gpio_video_input.svideo; - break; +static int subdev_s_video_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +{ + struct ivtv *itv = sd_to_ivtv(sd); + u16 mask, data; - default: + if (route->input > 2) /* 0:Tuner 1:Composite 2:S-Video */ return -EINVAL; - } + mask = itv->card->gpio_video_input.mask; + if (route->input == 0) + data = itv->card->gpio_video_input.tuner; + else if (route->input == 1) + data = itv->card->gpio_video_input.composite; + else + data = itv->card->gpio_video_input.svideo; if (mask) write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT); return 0; } + +static const struct v4l2_subdev_core_ops subdev_core_ops = { + .log_status = subdev_log_status, + .g_ctrl = subdev_g_ctrl, + .s_ctrl = subdev_s_ctrl, + .queryctrl = subdev_queryctrl, +}; + +static const struct v4l2_subdev_tuner_ops subdev_tuner_ops = { + .s_std = subdev_s_std, + .s_radio = subdev_s_radio, + .g_tuner = subdev_g_tuner, + .s_tuner = subdev_s_tuner, +}; + +static const struct v4l2_subdev_audio_ops subdev_audio_ops = { + .s_clock_freq = subdev_s_clock_freq, + .s_routing = subdev_s_audio_routing, +}; + +static const struct v4l2_subdev_video_ops subdev_video_ops = { + .s_routing = subdev_s_video_routing, +}; + +static const struct v4l2_subdev_ops subdev_ops = { + .core = &subdev_core_ops, + .tuner = &subdev_tuner_ops, + .audio = &subdev_audio_ops, + .video = &subdev_video_ops, +}; + +int ivtv_gpio_init(struct ivtv *itv) +{ + u16 pin = 0; + + if (itv->card->xceive_pin) + pin = 1 << itv->card->xceive_pin; + + if ((itv->card->gpio_init.direction | pin) == 0) + return 0; + + IVTV_DEBUG_INFO("GPIO initial dir: %08x out: %08x\n", + read_reg(IVTV_REG_GPIO_DIR), read_reg(IVTV_REG_GPIO_OUT)); + + /* init output data then direction */ + write_reg(itv->card->gpio_init.initial_value | pin, IVTV_REG_GPIO_OUT); + write_reg(itv->card->gpio_init.direction | pin, IVTV_REG_GPIO_DIR); + v4l2_subdev_init(&itv->sd_gpio, &subdev_ops); + snprintf(itv->sd_gpio.name, sizeof(itv->sd_gpio.name), "%s-gpio", itv->device.name); + itv->sd_gpio.grp_id = IVTV_HW_GPIO; + return v4l2_device_register_subdev(&itv->device, &itv->sd_gpio); +} diff --git a/linux/drivers/media/video/ivtv/ivtv-gpio.h b/linux/drivers/media/video/ivtv/ivtv-gpio.h index 48b629161..0b5d19c8e 100644 --- a/linux/drivers/media/video/ivtv/ivtv-gpio.h +++ b/linux/drivers/media/video/ivtv/ivtv-gpio.h @@ -22,9 +22,8 @@ #define IVTV_GPIO_H /* GPIO stuff */ -void ivtv_gpio_init(struct ivtv *itv); +int ivtv_gpio_init(struct ivtv *itv); void ivtv_reset_ir_gpio(struct ivtv *itv); int ivtv_reset_tuner_gpio(void *dev, int component, int cmd, int value); -int ivtv_gpio(struct ivtv *itv, unsigned int command, void *arg); #endif diff --git a/linux/drivers/media/video/ivtv/ivtv-i2c.c b/linux/drivers/media/video/ivtv/ivtv-i2c.c index fa18eae17..41452e111 100644 --- a/linux/drivers/media/video/ivtv/ivtv-i2c.c +++ b/linux/drivers/media/video/ivtv/ivtv-i2c.c @@ -89,26 +89,6 @@ #define IVTV_VP27SMPX_I2C_ADDR 0x5b #define IVTV_M52790_I2C_ADDR 0x48 -/* This array should match the IVTV_HW_ defines */ -static const u8 hw_driverids[] = { - I2C_DRIVERID_CX25840, - I2C_DRIVERID_SAA711X, - I2C_DRIVERID_SAA7127, - I2C_DRIVERID_MSP3400, - I2C_DRIVERID_TUNER, - I2C_DRIVERID_WM8775, - I2C_DRIVERID_CS53L32A, - I2C_DRIVERID_TVEEPROM, - I2C_DRIVERID_SAA711X, - I2C_DRIVERID_UPD64031A, - I2C_DRIVERID_UPD64083, - I2C_DRIVERID_SAA717X, - I2C_DRIVERID_WM8739, - I2C_DRIVERID_VP27SMPX, - I2C_DRIVERID_M52790, - 0 /* IVTV_HW_GPIO dummy driver ID */ -}; - /* This array should match the IVTV_HW_ defines */ static const u8 hw_addrs[] = { IVTV_CX25840_I2C_ADDR, @@ -129,6 +109,26 @@ static const u8 hw_addrs[] = { 0 /* IVTV_HW_GPIO dummy driver ID */ }; +/* This array should match the IVTV_HW_ defines */ +static const char *hw_modules[] = { + "cx25840", + "saa7115", + "saa7127", + "msp3400", + "tuner", + "wm8775", + "cs53l32a", + NULL, + "saa7115", + "upd64031a", + "upd64083", + "saa717x", + "wm8739", + "vp27smpx", + "m52790", + NULL +}; + /* This array should match the IVTV_HW_ defines */ static const char * const hw_devicenames[] = { "cx25840", @@ -155,104 +155,58 @@ static const char * const hw_devicenames[] = { int ivtv_i2c_register(struct ivtv *itv, unsigned idx) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) - struct i2c_board_info info; - struct i2c_client *c; - u8 id; - int i; + struct v4l2_subdev *sd; + struct i2c_adapter *adap = &itv->i2c_adap; + const char *mod = hw_modules[idx]; + const char *type = hw_devicenames[idx]; + u32 hw = 1 << idx; - IVTV_DEBUG_I2C("i2c client register\n"); - if (idx >= ARRAY_SIZE(hw_driverids) || hw_driverids[idx] == 0) + if (idx >= ARRAY_SIZE(hw_addrs)) return -1; - id = hw_driverids[idx]; - memset(&info, 0, sizeof(info)); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) - strlcpy(info.driver_name, hw_devicenames[idx], - sizeof(info.driver_name)); -#else - strlcpy(info.type, hw_devicenames[idx], sizeof(info.type)); -#endif - info.addr = hw_addrs[idx]; - for (i = 0; itv->i2c_clients[i] && i < I2C_CLIENTS_MAX; i++) {} - - if (i == I2C_CLIENTS_MAX) { - IVTV_ERR("insufficient room for new I2C client!\n"); - return -ENOMEM; + if (hw == IVTV_HW_TUNER) { + /* special tuner handling */ + sd = v4l2_i2c_new_probed_subdev(adap, mod, type, + itv->card_i2c->radio); + if (sd) + sd->grp_id = 1 << idx; + sd = v4l2_i2c_new_probed_subdev(adap, mod, type, + itv->card_i2c->demod); + if (sd) + sd->grp_id = 1 << idx; + sd = v4l2_i2c_new_probed_subdev(adap, mod, type, + itv->card_i2c->tv); + if (sd) + sd->grp_id = 1 << idx; + return sd ? 0 : -1; } + if (!hw_addrs[idx]) + return -1; + if (hw == IVTV_HW_UPD64031A || hw == IVTV_HW_UPD6408X) { + unsigned short addrs[2] = { hw_addrs[idx], I2C_CLIENT_END }; - if (id != I2C_DRIVERID_TUNER) { - if (id == I2C_DRIVERID_UPD64031A || - id == I2C_DRIVERID_UPD64083) { - unsigned short addrs[2] = { info.addr, I2C_CLIENT_END }; - - c = i2c_new_probed_device(&itv->i2c_adap, &info, addrs); - } else - c = i2c_new_device(&itv->i2c_adap, &info); - if (c && c->driver == NULL) - i2c_unregister_device(c); - else if (c) - itv->i2c_clients[i] = c; - return itv->i2c_clients[i] ? 0 : -ENODEV; - } - - /* special tuner handling */ - c = i2c_new_probed_device(&itv->i2c_adap, &info, itv->card_i2c->radio); - if (c && c->driver == NULL) - i2c_unregister_device(c); - else if (c) - itv->i2c_clients[i++] = c; - c = i2c_new_probed_device(&itv->i2c_adap, &info, itv->card_i2c->demod); - if (c && c->driver == NULL) - i2c_unregister_device(c); - else if (c) - itv->i2c_clients[i++] = c; - c = i2c_new_probed_device(&itv->i2c_adap, &info, itv->card_i2c->tv); - if (c && c->driver == NULL) - i2c_unregister_device(c); - else if (c) - itv->i2c_clients[i++] = c; - return 0; -#else - return 0; -#endif -} - -static int attach_inform(struct i2c_client *client) -{ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) - struct ivtv *itv = (struct ivtv *)i2c_get_adapdata(client->adapter); - int i; - - IVTV_DEBUG_I2C("i2c client attach\n"); - for (i = 0; i < I2C_CLIENTS_MAX; i++) { - if (itv->i2c_clients[i] == NULL) { - itv->i2c_clients[i] = client; - break; - } - } - if (i == I2C_CLIENTS_MAX) { - IVTV_ERR("Insufficient room for new I2C client\n"); + sd = v4l2_i2c_new_probed_subdev(adap, mod, type, addrs); + } else { + sd = v4l2_i2c_new_subdev(adap, mod, type, hw_addrs[idx]); } -#endif - return 0; + if (sd) + sd->grp_id = 1 << idx; + return sd ? 0 : -1; } -static int detach_inform(struct i2c_client *client) +struct v4l2_subdev *ivtv_find_hw(struct ivtv *itv, u32 hw) { - int i; - struct ivtv *itv = (struct ivtv *)i2c_get_adapdata(client->adapter); + struct v4l2_subdev *result = NULL; + struct v4l2_subdev *sd; - IVTV_DEBUG_I2C("i2c client detach\n"); - for (i = 0; i < I2C_CLIENTS_MAX; i++) { - if (itv->i2c_clients[i] == client) { - itv->i2c_clients[i] = NULL; + spin_lock(&itv->device.lock); + v4l2_device_for_each_subdev(sd, &itv->device) { + if (sd->grp_id == hw) { + result = sd; break; } } - IVTV_DEBUG_I2C("i2c detach [client=%s,%s]\n", - client->name, (i < I2C_CLIENTS_MAX) ? "ok" : "failed"); - - return 0; + spin_unlock(&itv->device.lock); + return result; } /* Set the serial clock line to the desired state */ @@ -522,7 +476,8 @@ static int ivtv_read(struct ivtv *itv, unsigned char addr, unsigned char *data, intervening stop condition */ static int ivtv_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num) { - struct ivtv *itv = i2c_get_adapdata(i2c_adap); + struct v4l2_device *drv = i2c_get_adapdata(i2c_adap); + struct ivtv *itv = to_ivtv(drv); int retval; int i; @@ -561,8 +516,6 @@ static struct i2c_adapter ivtv_i2c_adap_hw_template = { .id = I2C_HW_B_CX2341X, .algo = &ivtv_algo, .algo_data = NULL, /* filled from template */ - .client_register = attach_inform, - .client_unregister = detach_inform, .owner = THIS_MODULE, #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) .class = I2C_CLASS_TV_ANALOG, @@ -617,8 +570,6 @@ static struct i2c_adapter ivtv_i2c_adap_template = { .id = I2C_HW_B_CX2341X, .algo = NULL, /* set by i2c-algo-bit */ .algo_data = NULL, /* filled from template */ - .client_register = attach_inform, - .client_unregister = detach_inform, .owner = THIS_MODULE, #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) .class = I2C_CLASS_TV_ANALOG, @@ -638,160 +589,6 @@ static struct i2c_client ivtv_i2c_client_template = { .name = "ivtv internal", }; -int ivtv_call_i2c_client(struct ivtv *itv, int addr, unsigned int cmd, void *arg) -{ - struct i2c_client *client; - int retval; - int i; - - IVTV_DEBUG_I2C("call_i2c_client addr=%02x\n", addr); - for (i = 0; i < I2C_CLIENTS_MAX; i++) { - client = itv->i2c_clients[i]; - if (client == NULL || client->driver == NULL || - client->driver->command == NULL) - continue; - if (addr == client->addr) { - retval = client->driver->command(client, cmd, arg); - return retval; - } - } - if (cmd != VIDIOC_G_CHIP_IDENT) - IVTV_ERR("i2c addr 0x%02x not found for command 0x%x\n", addr, cmd); - return -ENODEV; -} - -/* Find the i2c device based on the driver ID and return - its i2c address or -ENODEV if no matching device was found. */ -static int ivtv_i2c_id_addr(struct ivtv *itv, u32 id) -{ - struct i2c_client *client; - int retval = -ENODEV; - int i; - - for (i = 0; i < I2C_CLIENTS_MAX; i++) { - client = itv->i2c_clients[i]; - if (client == NULL || client->driver == NULL) - continue; - if (id == client->driver->id) { - retval = client->addr; - break; - } - } - return retval; -} - -/* Find the i2c device name matching the DRIVERID */ -static const char *ivtv_i2c_id_name(u32 id) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(hw_driverids); i++) - if (hw_driverids[i] == id) - return hw_devicenames[i]; - return "unknown device"; -} - -/* Find the i2c device name matching the IVTV_HW_ flag */ -static const char *ivtv_i2c_hw_name(u32 hw) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(hw_driverids); i++) - if (1 << i == hw) - return hw_devicenames[i]; - return "unknown device"; -} - -/* Find the i2c device matching the IVTV_HW_ flag and return - its i2c address or -ENODEV if no matching device was found. */ -int ivtv_i2c_hw_addr(struct ivtv *itv, u32 hw) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(hw_driverids); i++) - if (1 << i == hw) - return ivtv_i2c_id_addr(itv, hw_driverids[i]); - return -ENODEV; -} - -/* Calls i2c device based on IVTV_HW_ flag. If hw == 0, then do nothing. - If hw == IVTV_HW_GPIO then call the gpio handler. */ -int ivtv_i2c_hw(struct ivtv *itv, u32 hw, unsigned int cmd, void *arg) -{ - int addr; - - if (hw == IVTV_HW_GPIO) - return ivtv_gpio(itv, cmd, arg); - if (hw == 0) - return 0; - - addr = ivtv_i2c_hw_addr(itv, hw); - if (addr < 0) { - IVTV_ERR("i2c hardware 0x%08x (%s) not found for command 0x%x\n", - hw, ivtv_i2c_hw_name(hw), cmd); - return addr; - } - return ivtv_call_i2c_client(itv, addr, cmd, arg); -} - -/* Calls i2c device based on I2C driver ID. */ -int ivtv_i2c_id(struct ivtv *itv, u32 id, unsigned int cmd, void *arg) -{ - int addr; - - addr = ivtv_i2c_id_addr(itv, id); - if (addr < 0) { - if (cmd != VIDIOC_G_CHIP_IDENT) - IVTV_ERR("i2c ID 0x%08x (%s) not found for command 0x%x\n", - id, ivtv_i2c_id_name(id), cmd); - return addr; - } - return ivtv_call_i2c_client(itv, addr, cmd, arg); -} - -int ivtv_cx25840(struct ivtv *itv, unsigned int cmd, void *arg) -{ - return ivtv_call_i2c_client(itv, IVTV_CX25840_I2C_ADDR, cmd, arg); -} - -int ivtv_saa7115(struct ivtv *itv, unsigned int cmd, void *arg) -{ - return ivtv_call_i2c_client(itv, IVTV_SAA7115_I2C_ADDR, cmd, arg); -} - -int ivtv_saa7127(struct ivtv *itv, unsigned int cmd, void *arg) -{ - return ivtv_call_i2c_client(itv, IVTV_SAA7127_I2C_ADDR, cmd, arg); -} -EXPORT_SYMBOL(ivtv_saa7127); - -int ivtv_saa717x(struct ivtv *itv, unsigned int cmd, void *arg) -{ - return ivtv_call_i2c_client(itv, IVTV_SAA717x_I2C_ADDR, cmd, arg); -} - -int ivtv_upd64031a(struct ivtv *itv, unsigned int cmd, void *arg) -{ - return ivtv_call_i2c_client(itv, IVTV_UPD64031A_I2C_ADDR, cmd, arg); -} - -int ivtv_upd64083(struct ivtv *itv, unsigned int cmd, void *arg) -{ - return ivtv_call_i2c_client(itv, IVTV_UPD64083_I2C_ADDR, cmd, arg); -} - -/* broadcast cmd for all I2C clients and for the gpio subsystem */ -void ivtv_call_i2c_clients(struct ivtv *itv, unsigned int cmd, void *arg) -{ - if (itv->i2c_adap.algo == NULL) { - IVTV_ERR("Adapter is not set"); - return; - } - i2c_clients_command(&itv->i2c_adap, cmd, arg); - if (itv->hw_flags & IVTV_HW_GPIO) - ivtv_gpio(itv, cmd, arg); -} - /* init + register i2c algo-bit adapter */ int init_ivtv_i2c(struct ivtv *itv) { @@ -800,10 +597,9 @@ int init_ivtv_i2c(struct ivtv *itv) /* Sanity checks for the I2C hardware arrays. They must be the * same size and GPIO must be the last entry. */ - if (ARRAY_SIZE(hw_driverids) != ARRAY_SIZE(hw_addrs) || - ARRAY_SIZE(hw_devicenames) != ARRAY_SIZE(hw_addrs) || - IVTV_HW_GPIO != (1 << (ARRAY_SIZE(hw_addrs) - 1)) || - hw_driverids[ARRAY_SIZE(hw_addrs) - 1]) { + if (ARRAY_SIZE(hw_devicenames) != ARRAY_SIZE(hw_addrs) || + ARRAY_SIZE(hw_devicenames) != ARRAY_SIZE(hw_modules) || + IVTV_HW_GPIO != (1 << (ARRAY_SIZE(hw_addrs) - 1))) { IVTV_ERR("Mismatched I2C hardware arrays\n"); return -ENODEV; } @@ -820,8 +616,8 @@ int init_ivtv_i2c(struct ivtv *itv) itv->i2c_adap.algo_data = &itv->i2c_algo; sprintf(itv->i2c_adap.name + strlen(itv->i2c_adap.name), " #%d", - itv->num); - i2c_set_adapdata(&itv->i2c_adap, itv); + itv->instance); + i2c_set_adapdata(&itv->i2c_adap, &itv->device); memcpy(&itv->i2c_client, &ivtv_i2c_client_template, sizeof(struct i2c_client)); diff --git a/linux/drivers/media/video/ivtv/ivtv-i2c.h b/linux/drivers/media/video/ivtv/ivtv-i2c.h index 022978cf5..396928a06 100644 --- a/linux/drivers/media/video/ivtv/ivtv-i2c.h +++ b/linux/drivers/media/video/ivtv/ivtv-i2c.h @@ -21,19 +21,8 @@ #ifndef IVTV_I2C_H #define IVTV_I2C_H -int ivtv_cx25840(struct ivtv *itv, unsigned int cmd, void *arg); -int ivtv_saa7115(struct ivtv *itv, unsigned int cmd, void *arg); -int ivtv_saa7127(struct ivtv *itv, unsigned int cmd, void *arg); -int ivtv_saa717x(struct ivtv *itv, unsigned int cmd, void *arg); -int ivtv_upd64031a(struct ivtv *itv, unsigned int cmd, void *arg); -int ivtv_upd64083(struct ivtv *itv, unsigned int cmd, void *arg); - -int ivtv_i2c_hw_addr(struct ivtv *itv, u32 hw); -int ivtv_i2c_hw(struct ivtv *itv, u32 hw, unsigned int cmd, void *arg); -int ivtv_i2c_id(struct ivtv *itv, u32 id, unsigned int cmd, void *arg); -int ivtv_call_i2c_client(struct ivtv *itv, int addr, unsigned int cmd, void *arg); -void ivtv_call_i2c_clients(struct ivtv *itv, unsigned int cmd, void *arg); int ivtv_i2c_register(struct ivtv *itv, unsigned idx); +struct v4l2_subdev *ivtv_find_hw(struct ivtv *itv, u32 hw); /* init + register i2c algo-bit adapter */ int init_ivtv_i2c(struct ivtv *itv); diff --git a/linux/drivers/media/video/ivtv/ivtv-ioctl.c b/linux/drivers/media/video/ivtv/ivtv-ioctl.c index 4bae38d21..cd990a4b8 100644 --- a/linux/drivers/media/video/ivtv/ivtv-ioctl.c +++ b/linux/drivers/media/video/ivtv/ivtv-ioctl.c @@ -393,7 +393,7 @@ static int ivtv_g_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_fo return 0; } - itv->video_dec_func(itv, VIDIOC_G_FMT, fmt); + v4l2_subdev_call(itv->sd_video, video, s_fmt, fmt); vbifmt->service_set = ivtv_get_service_set(vbifmt); return 0; } @@ -581,7 +581,7 @@ static int ivtv_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f p->height = h; if (p->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) fmt->fmt.pix.width /= 2; - itv->video_dec_func(itv, VIDIOC_S_FMT, fmt); + v4l2_subdev_call(itv->sd_video, video, s_fmt, fmt); return ivtv_g_fmt_vid_cap(file, fh, fmt); } @@ -593,7 +593,7 @@ static int ivtv_s_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f return -EBUSY; itv->vbi.sliced_in->service_set = 0; itv->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE; - itv->video_dec_func(itv, VIDIOC_S_FMT, fmt); + v4l2_subdev_call(itv->sd_video, video, s_fmt, fmt); return ivtv_g_fmt_vbi_cap(file, fh, fmt); } @@ -611,7 +611,7 @@ static int ivtv_s_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_fo if (ivtv_raw_vbi(itv) && atomic_read(&itv->capturing) > 0) return -EBUSY; itv->vbi.in.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; - itv->video_dec_func(itv, VIDIOC_S_FMT, fmt); + v4l2_subdev_call(itv->sd_video, video, s_fmt, fmt); memcpy(itv->vbi.sliced_in, vbifmt, sizeof(*itv->vbi.sliced_in)); return 0; } @@ -685,18 +685,17 @@ static int ivtv_g_chip_ident(struct file *file, void *fh, struct v4l2_chip_ident chip->ident = itv->has_cx23415 ? V4L2_IDENT_CX23415 : V4L2_IDENT_CX23416; return 0; } - if (chip->match_type == V4L2_CHIP_MATCH_I2C_DRIVER) - return ivtv_i2c_id(itv, chip->match_chip, VIDIOC_G_CHIP_IDENT, chip); - if (chip->match_type == V4L2_CHIP_MATCH_I2C_ADDR) - return ivtv_call_i2c_client(itv, chip->match_chip, VIDIOC_G_CHIP_IDENT, chip); - return -EINVAL; + if (chip->match_type != V4L2_CHIP_MATCH_I2C_DRIVER && + chip->match_type != V4L2_CHIP_MATCH_I2C_ADDR) + return -EINVAL; + /* TODO: is this correct? */ + return ivtv_call_all_err(itv, core, g_chip_ident, chip); } #ifdef CONFIG_VIDEO_ADV_DEBUG static int ivtv_itvc(struct ivtv *itv, unsigned int cmd, void *arg) { struct v4l2_register *regs = arg; - unsigned long flags; volatile u8 __iomem *reg_start; if (!capable(CAP_SYS_ADMIN)) @@ -711,12 +710,10 @@ static int ivtv_itvc(struct ivtv *itv, unsigned int cmd, void *arg) else return -EINVAL; - spin_lock_irqsave(&ivtv_cards_lock, flags); if (cmd == VIDIOC_DBG_G_REGISTER) regs->val = readl(regs->reg + reg_start); else writel(regs->val, regs->reg + reg_start); - spin_unlock_irqrestore(&ivtv_cards_lock, flags); return 0; } @@ -726,9 +723,10 @@ static int ivtv_g_register(struct file *file, void *fh, struct v4l2_register *re if (v4l2_chip_match_host(reg->match_type, reg->match_chip)) return ivtv_itvc(itv, VIDIOC_DBG_G_REGISTER, reg); - if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER) - return ivtv_i2c_id(itv, reg->match_chip, VIDIOC_DBG_G_REGISTER, reg); - return ivtv_call_i2c_client(itv, reg->match_chip, VIDIOC_DBG_G_REGISTER, reg); + /* TODO: subdev errors should not be ignored, this should become a + subdev helper function. */ + ivtv_call_all(itv, core, g_register, reg); + return 0; } static int ivtv_s_register(struct file *file, void *fh, struct v4l2_register *reg) @@ -737,9 +735,10 @@ static int ivtv_s_register(struct file *file, void *fh, struct v4l2_register *re if (v4l2_chip_match_host(reg->match_type, reg->match_chip)) return ivtv_itvc(itv, VIDIOC_DBG_S_REGISTER, reg); - if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER) - return ivtv_i2c_id(itv, reg->match_chip, VIDIOC_DBG_S_REGISTER, reg); - return ivtv_call_i2c_client(itv, reg->match_chip, VIDIOC_DBG_S_REGISTER, reg); + /* TODO: subdev errors should not be ignored, this should become a + subdev helper function. */ + ivtv_call_all(itv, core, s_register, reg); + return 0; } #endif @@ -884,12 +883,6 @@ static int ivtv_s_crop(struct file *file, void *fh, struct v4l2_crop *crop) streamtype = id->type; - if (ivtv_debug & IVTV_DBGFLG_IOCTL) { - printk(KERN_INFO "ivtv%d ioctl: ", itv->num); - /* Should be replaced */ - /* v4l_printk_ioctl(VIDIOC_S_CROP); */ - } - if (crop->type == V4L2_BUF_TYPE_VIDEO_OUTPUT && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) { if (streamtype == IVTV_DEC_STREAM_TYPE_YUV) { @@ -1050,7 +1043,7 @@ static int ivtv_s_output(struct file *file, void *fh, unsigned int outp) itv->active_output = outp; route.input = SAA7127_INPUT_TYPE_NORMAL; route.output = itv->card->video_outputs[outp].video_output; - ivtv_saa7127(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route); + ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_routing, &route); return 0; } @@ -1062,7 +1055,7 @@ static int ivtv_g_frequency(struct file *file, void *fh, struct v4l2_frequency * if (vf->tuner != 0) return -EINVAL; - ivtv_call_i2c_clients(itv, VIDIOC_G_FREQUENCY, vf); + ivtv_call_all(itv, tuner, g_frequency, vf); return 0; } @@ -1075,7 +1068,7 @@ int ivtv_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf) ivtv_mute(itv); IVTV_DEBUG_INFO("v4l2 ioctl: set frequency %d\n", vf->frequency); - ivtv_call_i2c_clients(itv, VIDIOC_S_FREQUENCY, vf); + ivtv_call_all(itv, tuner, s_frequency, vf); ivtv_unmute(itv); return 0; } @@ -1123,14 +1116,14 @@ int ivtv_s_std(struct file *file, void *fh, v4l2_std_id *std) IVTV_DEBUG_INFO("Switching standard to %llx.\n", (unsigned long long)itv->std); /* Tuner */ - ivtv_call_i2c_clients(itv, VIDIOC_S_STD, &itv->std); + ivtv_call_all(itv, tuner, s_std, itv->std); if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) { /* set display standard */ itv->std_out = *std; itv->is_out_60hz = itv->is_60hz; itv->is_out_50hz = itv->is_50hz; - ivtv_call_i2c_clients(itv, VIDIOC_INT_S_STD_OUTPUT, &itv->std_out); + ivtv_call_all(itv, video, s_std_output, itv->std_out); ivtv_vapi(itv, CX2341X_DEC_SET_STANDARD, 1, itv->is_out_50hz); itv->main_rect.left = itv->main_rect.top = 0; itv->main_rect.width = 720; @@ -1154,7 +1147,7 @@ static int ivtv_s_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) if (vt->index != 0) return -EINVAL; - ivtv_call_i2c_clients(itv, VIDIOC_S_TUNER, vt); + ivtv_call_all(itv, tuner, s_tuner, vt); return 0; } @@ -1166,7 +1159,7 @@ static int ivtv_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) if (vt->index != 0) return -EINVAL; - ivtv_call_i2c_clients(itv, VIDIOC_G_TUNER, vt); + ivtv_call_all(itv, tuner, g_tuner, vt); if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags)) { strlcpy(vt->name, "ivtv Radio Tuner", sizeof(vt->name)); @@ -1444,14 +1437,15 @@ static int ivtv_log_status(struct file *file, void *fh) struct v4l2_audio audin; int i; - IVTV_INFO("================= START STATUS CARD #%d =================\n", itv->num); + IVTV_INFO("================= START STATUS CARD #%d =================\n", + itv->instance); IVTV_INFO("Version: %s Card: %s\n", IVTV_VERSION, itv->card_name); if (itv->hw_flags & IVTV_HW_TVEEPROM) { struct tveeprom tv; ivtv_read_eeprom(itv, &tv); } - ivtv_call_i2c_clients(itv, VIDIOC_LOG_STATUS, NULL); + ivtv_call_all(itv, core, log_status); ivtv_get_input(itv, itv->active_input, &vidin); ivtv_get_audio_input(itv, itv->audio_input, &audin); IVTV_INFO("Video Input: %s\n", vidin.name); @@ -1518,7 +1512,7 @@ static int ivtv_log_status(struct file *file, void *fh) } IVTV_INFO("Tuner: %s\n", test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) ? "Radio" : "TV"); - cx2341x_log_status(&itv->params, itv->name); + cx2341x_log_status(&itv->params, itv->device.name); IVTV_INFO("Status flags: 0x%08lx\n", itv->i_flags); for (i = 0; i < IVTV_MAX_STREAMS; i++) { struct ivtv_stream *s = &itv->streams[i]; @@ -1530,8 +1524,11 @@ static int ivtv_log_status(struct file *file, void *fh) (s->buffers * s->buf_size) / 1024, s->buffers); } - IVTV_INFO("Read MPG/VBI: %lld/%lld bytes\n", (long long)itv->mpg_data_received, (long long)itv->vbi_data_inserted); - IVTV_INFO("================== END STATUS CARD #%d ==================\n", itv->num); + IVTV_INFO("Read MPG/VBI: %lld/%lld bytes\n", + (long long)itv->mpg_data_received, + (long long)itv->vbi_data_inserted); + IVTV_INFO("================== END STATUS CARD #%d ==================\n", + itv->instance); return 0; } @@ -1736,7 +1733,7 @@ static int ivtv_default(struct file *file, void *fh, int cmd, void *arg) case VIDIOC_INT_S_AUDIO_ROUTING: { struct v4l2_routing *route = arg; - ivtv_i2c_hw(itv, itv->card->hw_audio, VIDIOC_INT_S_AUDIO_ROUTING, route); + ivtv_call_hw(itv, itv->card->hw_audio, audio, s_routing, route); break; } @@ -1746,7 +1743,7 @@ static int ivtv_default(struct file *file, void *fh, int cmd, void *arg) if ((val == 0 && itv->options.newi2c) || (val & 0x01)) ivtv_reset_ir_gpio(itv); if (val & 0x02) - itv->video_dec_func(itv, cmd, NULL); + v4l2_subdev_call(itv->sd_video, core, reset, 0); break; } diff --git a/linux/drivers/media/video/ivtv/ivtv-routing.c b/linux/drivers/media/video/ivtv/ivtv-routing.c index 05564919b..3fd302294 100644 --- a/linux/drivers/media/video/ivtv/ivtv-routing.c +++ b/linux/drivers/media/video/ivtv/ivtv-routing.c @@ -47,13 +47,13 @@ void ivtv_audio_set_io(struct ivtv *itv) route.output = 0; if (itv->card->hw_muxer & IVTV_HW_M52790) route.output = M52790_OUT_STEREO; - ivtv_i2c_hw(itv, itv->card->hw_muxer, VIDIOC_INT_S_AUDIO_ROUTING, &route); + v4l2_subdev_call(itv->sd_muxer, audio, s_routing, &route); route.input = in->audio_input; route.output = 0; if (itv->card->hw_audio & IVTV_HW_MSP34XX) route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1); - ivtv_i2c_hw(itv, itv->card->hw_audio, VIDIOC_INT_S_AUDIO_ROUTING, &route); + ivtv_call_hw(itv, itv->card->hw_audio, audio, s_routing, &route); } /* Selects the video input and output according to the current @@ -66,7 +66,7 @@ void ivtv_video_set_io(struct ivtv *itv) route.input = itv->card->video_inputs[inp].video_input; route.output = 0; - itv->video_dec_func(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route); + v4l2_subdev_call(itv->sd_video, video, s_routing, &route); type = itv->card->video_inputs[inp].video_type; @@ -79,7 +79,7 @@ void ivtv_video_set_io(struct ivtv *itv) } if (itv->card->hw_video & IVTV_HW_GPIO) - ivtv_gpio(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route); + ivtv_call_hw(itv, IVTV_HW_GPIO, video, s_routing, &route); if (itv->card->hw_video & IVTV_HW_UPD64031A) { if (type == IVTV_CARD_INPUT_VID_TUNER || @@ -92,7 +92,7 @@ void ivtv_video_set_io(struct ivtv *itv) } route.input |= itv->card->gr_config; - ivtv_upd64031a(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route); + ivtv_call_hw(itv, IVTV_HW_UPD64031A, video, s_routing, &route); } if (itv->card->hw_video & IVTV_HW_UPD6408X) { @@ -110,6 +110,6 @@ void ivtv_video_set_io(struct ivtv *itv) route.input |= UPD64083_EXT_Y_ADC; } } - ivtv_upd64083(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route); + ivtv_call_hw(itv, IVTV_HW_UPD6408X, video, s_routing, &route); } } diff --git a/linux/drivers/media/video/ivtv/ivtv-streams.c b/linux/drivers/media/video/ivtv/ivtv-streams.c index 9b7aa79eb..76279ed30 100644 --- a/linux/drivers/media/video/ivtv/ivtv-streams.c +++ b/linux/drivers/media/video/ivtv/ivtv-streams.c @@ -172,7 +172,7 @@ static int ivtv_prep_dev(struct ivtv *itv, int type) { struct ivtv_stream *s = &itv->streams[type]; int num_offset = ivtv_stream_info[type].num_offset; - int num = itv->num + ivtv_first_minor + num_offset; + int num = itv->instance + ivtv_first_minor + num_offset; /* These four fields are always initialized. If v4l2dev == NULL, then this stream is not in use. In that case no other fields but these @@ -205,8 +205,8 @@ static int ivtv_prep_dev(struct ivtv *itv, int type) return -ENOMEM; } - snprintf(s->v4l2dev->name, sizeof(s->v4l2dev->name), "ivtv%d %s", - itv->num, s->name); + snprintf(s->v4l2dev->name, sizeof(s->v4l2dev->name), "%s %s", + itv->device.name, s->name); s->v4l2dev->num = num; s->v4l2dev->parent = &itv->dev->dev; @@ -260,6 +260,7 @@ static int ivtv_reg_dev(struct ivtv *itv, int type) if (s_mpg->v4l2dev) num = s_mpg->v4l2dev->num + ivtv_stream_info[type].num_offset; } + video_set_drvdata(s->v4l2dev, s); /* Register device. First try the desired minor, then any free one. */ if (video_register_device(s->v4l2dev, vfl_type, num)) { @@ -343,7 +344,7 @@ static void ivtv_vbi_setup(struct ivtv *itv) ivtv_vapi(itv, CX2341X_ENC_SET_VBI_LINE, 5, 0xffff , 0, 0, 0, 0); /* setup VBI registers */ - itv->video_dec_func(itv, VIDIOC_S_FMT, &itv->vbi.in); + v4l2_subdev_call(itv->sd_video, video, s_fmt, &itv->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 @@ -577,10 +578,10 @@ int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s) clear_bit(IVTV_F_I_EOS, &itv->i_flags); /* Initialize Digitizer for Capture */ - itv->video_dec_func(itv, VIDIOC_STREAMOFF, NULL); + v4l2_subdev_call(itv->sd_video, video, s_stream, 0); ivtv_msleep_timeout(300, 1); ivtv_vapi(itv, CX2341X_ENC_INITIALIZE_INPUT, 0); - itv->video_dec_func(itv, VIDIOC_STREAMON, NULL); + v4l2_subdev_call(itv->sd_video, video, s_stream, 1); } /* begin_capture */ diff --git a/linux/drivers/media/video/ivtv/ivtv-vbi.c b/linux/drivers/media/video/ivtv/ivtv-vbi.c index 4a37a7d2e..5c5d1c462 100644 --- a/linux/drivers/media/video/ivtv/ivtv-vbi.c +++ b/linux/drivers/media/video/ivtv/ivtv-vbi.c @@ -21,6 +21,7 @@ #include "ivtv-i2c.h" #include "ivtv-ioctl.h" #include "ivtv-queue.h" +#include "ivtv-cards.h" #include "ivtv-vbi.h" static void ivtv_set_vps(struct ivtv *itv, int enabled) @@ -37,7 +38,7 @@ static void ivtv_set_vps(struct ivtv *itv, int enabled) data.data[9] = itv->vbi.vps_payload.data[2]; data.data[10] = itv->vbi.vps_payload.data[3]; data.data[11] = itv->vbi.vps_payload.data[4]; - ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data); + ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_vbi_data, &data); } static void ivtv_set_cc(struct ivtv *itv, int mode, const struct vbi_cc *cc) @@ -51,12 +52,12 @@ static void ivtv_set_cc(struct ivtv *itv, int mode, const struct vbi_cc *cc) data.line = (mode & 1) ? 21 : 0; data.data[0] = cc->odd[0]; data.data[1] = cc->odd[1]; - ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data); + ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_vbi_data, &data); data.field = 1; data.line = (mode & 2) ? 21 : 0; data.data[0] = cc->even[0]; data.data[1] = cc->even[1]; - ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data); + ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_vbi_data, &data); } static void ivtv_set_wss(struct ivtv *itv, int enabled, int mode) @@ -79,7 +80,7 @@ static void ivtv_set_wss(struct ivtv *itv, int enabled, int mode) data.line = enabled ? 23 : 0; data.data[0] = mode & 0xff; data.data[1] = (mode >> 8) & 0xff; - ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data); + ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_vbi_data, &data); } static int odd_parity(u8 c) @@ -313,7 +314,7 @@ static u32 compress_sliced_buf(struct ivtv *itv, u32 line, u8 *buf, u32 size, u8 continue; } vbi.p = p + 4; - itv->video_dec_func(itv, VIDIOC_INT_DECODE_VBI_LINE, &vbi); + v4l2_subdev_call(itv->sd_video, video, decode_vbi_line, &vbi); if (vbi.type && !(lines & (1 << vbi.line))) { lines |= 1 << vbi.line; itv->vbi.sliced_data[line].id = vbi.type; @@ -437,7 +438,7 @@ void ivtv_vbi_work_handler(struct ivtv *itv) data.id = V4L2_SLICED_WSS_625; data.field = 0; - if (itv->video_dec_func(itv, VIDIOC_INT_G_VBI_DATA, &data) == 0) { + if (v4l2_subdev_call(itv->sd_video, video, g_vbi_data, &data) == 0) { ivtv_set_wss(itv, 1, data.data[0] & 0xf); vi->wss_missing_cnt = 0; } else if (vi->wss_missing_cnt == 4) { @@ -451,13 +452,13 @@ void ivtv_vbi_work_handler(struct ivtv *itv) data.id = V4L2_SLICED_CAPTION_525; data.field = 0; - if (itv->video_dec_func(itv, VIDIOC_INT_G_VBI_DATA, &data) == 0) { + if (v4l2_subdev_call(itv->sd_video, video, g_vbi_data, &data) == 0) { mode |= 1; cc.odd[0] = data.data[0]; cc.odd[1] = data.data[1]; } data.field = 1; - if (itv->video_dec_func(itv, VIDIOC_INT_G_VBI_DATA, &data) == 0) { + if (v4l2_subdev_call(itv->sd_video, video, g_vbi_data, &data) == 0) { mode |= 2; cc.even[0] = data.data[0]; cc.even[1] = data.data[1]; diff --git a/linux/drivers/media/video/ivtv/ivtvfb.c b/linux/drivers/media/video/ivtv/ivtvfb.c index 5e3613d86..26b91d2c8 100644 --- a/linux/drivers/media/video/ivtv/ivtvfb.c +++ b/linux/drivers/media/video/ivtv/ivtvfb.c @@ -48,6 +48,7 @@ #endif #include "ivtv-driver.h" +#include "ivtv-cards.h" #include "ivtv-i2c.h" #include "ivtv-udma.h" #include "ivtv-mailbox.h" @@ -121,15 +122,15 @@ MODULE_LICENSE("GPL"); #define IVTVFB_DEBUG(x, type, fmt, args...) \ do { \ if ((x) & ivtvfb_debug) \ - printk(KERN_INFO "ivtvfb%d " type ": " fmt, itv->num , ## args); \ + printk(KERN_INFO "ivtvfb%d " type ": " fmt, itv->instance , ## args); \ } while (0) #define IVTVFB_DEBUG_WARN(fmt, args...) IVTVFB_DEBUG(IVTVFB_DBGFLG_WARN, "warning", fmt , ## args) #define IVTVFB_DEBUG_INFO(fmt, args...) IVTVFB_DEBUG(IVTVFB_DBGFLG_INFO, "info", fmt , ## args) /* Standard kernel messages */ -#define IVTVFB_ERR(fmt, args...) printk(KERN_ERR "ivtvfb%d: " fmt, itv->num , ## args) -#define IVTVFB_WARN(fmt, args...) printk(KERN_WARNING "ivtvfb%d: " fmt, itv->num , ## args) -#define IVTVFB_INFO(fmt, args...) printk(KERN_INFO "ivtvfb%d: " fmt, itv->num , ## args) +#define IVTVFB_ERR(fmt, args...) printk(KERN_ERR "ivtvfb%d: " fmt, itv->instance , ## args) +#define IVTVFB_WARN(fmt, args...) printk(KERN_WARNING "ivtvfb%d: " fmt, itv->instance , ## args) +#define IVTVFB_INFO(fmt, args...) printk(KERN_INFO "ivtvfb%d: " fmt, itv->instance , ## args) /* --------------------------------------------------------------------- */ @@ -904,16 +905,16 @@ static int ivtvfb_blank(int blank_mode, struct fb_info *info) switch (blank_mode) { case FB_BLANK_UNBLANK: ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 1); - ivtv_saa7127(itv, VIDIOC_STREAMON, NULL); + ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 1); break; case FB_BLANK_NORMAL: case FB_BLANK_HSYNC_SUSPEND: case FB_BLANK_VSYNC_SUSPEND: ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 0); - ivtv_saa7127(itv, VIDIOC_STREAMON, NULL); + ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 1); break; case FB_BLANK_POWERDOWN: - ivtv_saa7127(itv, VIDIOC_STREAMOFF, NULL); + ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 0); ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 0); break; } @@ -1197,10 +1198,45 @@ static int ivtvfb_init_card(struct ivtv *itv) } +static int __init ivtvfb_callback_init(struct device *dev, void *p) +{ + struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); + struct ivtv *itv = container_of(v4l2_dev, struct ivtv, device); + + if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) { + if (ivtvfb_init_card(itv) == 0) { + IVTVFB_INFO("Framebuffer registered on %s\n", + itv->device.name); + (*(int *)p)++; + } + } + return 0; +} + +static int ivtvfb_callback_cleanup(struct device *dev, void *p) +{ + struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); + struct ivtv *itv = container_of(v4l2_dev, struct ivtv, device); + + if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) { + if (unregister_framebuffer(&itv->osd_info->ivtvfb_info)) { + IVTVFB_WARN("Framebuffer %d is in use, cannot unload\n", + itv->instance); + return 0; + } + IVTVFB_INFO("Unregister framebuffer %d\n", itv->instance); + ivtvfb_blank(FB_BLANK_POWERDOWN, &itv->osd_info->ivtvfb_info); + ivtvfb_release_buffers(itv); + itv->osd_video_pbase = 0; + } + return 0; +} + static int __init ivtvfb_init(void) { - struct ivtv *itv; - int i, registered = 0; + struct device_driver *drv; + int registered = 0; + int err; if (ivtvfb_card_id < -1 || ivtvfb_card_id >= IVTV_MAX_CARDS) { printk(KERN_ERR "ivtvfb: ivtvfb_card_id parameter is out of range (valid range: -1 - %d)\n", @@ -1208,20 +1244,11 @@ static int __init ivtvfb_init(void) return -EINVAL; } - /* Locate & initialise all cards supporting an OSD. */ - for (i = 0; i < ivtv_cards_active; i++) { - if (ivtvfb_card_id != -1 && i != ivtvfb_card_id) - continue; - itv = ivtv_cards[i]; - if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) { - if (ivtvfb_init_card(itv) == 0) { - IVTVFB_INFO("Framebuffer registered on ivtv card id %d\n", i); - registered++; - } - } - } + drv = driver_find("ivtv", &pci_bus_type); + err = driver_for_each_device(drv, NULL, ®istered, ivtvfb_callback_init); + put_driver(drv); if (!registered) { - printk(KERN_ERR "ivtvfb: no cards found"); + printk(KERN_ERR "ivtvfb: no cards found\n"); return -ENODEV; } return 0; @@ -1229,24 +1256,14 @@ static int __init ivtvfb_init(void) static void ivtvfb_cleanup(void) { - struct ivtv *itv; - int i; + struct device_driver *drv; + int err; printk(KERN_INFO "ivtvfb: Unloading framebuffer module\n"); - for (i = 0; i < ivtv_cards_active; i++) { - itv = ivtv_cards[i]; - if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) && itv->osd_info) { - if (unregister_framebuffer(&itv->osd_info->ivtvfb_info)) { - IVTVFB_WARN("Framebuffer %d is in use, cannot unload\n", i); - return; - } - IVTVFB_DEBUG_INFO("Unregister framebuffer %d\n", i); - ivtvfb_blank(FB_BLANK_POWERDOWN, &itv->osd_info->ivtvfb_info); - ivtvfb_release_buffers(itv); - itv->osd_video_pbase = 0; - } - } + drv = driver_find("ivtv", &pci_bus_type); + err = driver_for_each_device(drv, NULL, NULL, ivtvfb_callback_cleanup); + put_driver(drv); } module_init(ivtvfb_init); -- cgit v1.2.3 From 8bd850c491873395b5ec35921fca010bb2ae4f75 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sun, 30 Nov 2008 08:01:21 -0500 Subject: cx18: cx18_writel_expect() should not declare success on a PCI read error From: Andy Walls 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. Priority: normal Signed-off-by: Andy Walls --- linux/drivers/media/video/cx18/cx18-io.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/video/cx18/cx18-io.h b/linux/drivers/media/video/cx18/cx18-io.h index e6716dcb1..2635b3a8c 100644 --- a/linux/drivers/media/video/cx18/cx18-io.h +++ b/linux/drivers/media/video/cx18/cx18-io.h @@ -83,10 +83,14 @@ void cx18_writel_expect(struct cx18 *cx, u32 val, void __iomem *addr, u32 eval, u32 mask) { int i; + u32 r; eval &= mask; for (i = 0; i < CX18_MAX_MMIO_WR_RETRIES; i++) { cx18_writel_noretry(cx, val, addr); - if (eval == (cx18_readl(cx, addr) & mask)) + r = cx18_readl(cx, addr); + if (r == 0xffffffff && eval != 0xffffffff) + continue; + if (eval == (r & mask)) break; } } -- cgit v1.2.3 From fd3e91675d5c7b8c8a82af7f8e3c5d706e458791 Mon Sep 17 00:00:00 2001 From: Patrick Boettcher Date: Mon, 1 Dec 2008 10:51:14 +0100 Subject: dib0700: Stop repeating after user stops pushing button From: Devin Heitmueller A user noticed that there would continue to be 4-6 keypresses even after the user stopped holding down the button. This was because we were not reading the bulk pipe faster than the firmware was injecting information, which would result in a backlog. Make the query interval faster, and increase the number of cycles before we start repeating to compensate. Thanks to Knud Poulsen for pointing this out. Priority: high Signed-off-by: Devin Heitmueller Signed-off-by: Patrick Boettcher --- linux/drivers/media/dvb/dvb-usb/dib0700_devices.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/dvb/dvb-usb/dib0700_devices.c b/linux/drivers/media/dvb/dvb-usb/dib0700_devices.c index f28d3ae59..391732788 100644 --- a/linux/drivers/media/dvb/dvb-usb/dib0700_devices.c +++ b/linux/drivers/media/dvb/dvb-usb/dib0700_devices.c @@ -446,13 +446,13 @@ static int stk7700ph_tuner_attach(struct dvb_usb_adapter *adap) == NULL ? -ENODEV : 0; } -#define DEFAULT_RC_INTERVAL 150 +#define DEFAULT_RC_INTERVAL 50 static u8 rc_request[] = { REQUEST_POLL_RC, 0 }; /* Number of keypresses to ignore before start repeating */ -#define RC_REPEAT_DELAY 2 -#define RC_REPEAT_DELAY_V1_20 5 +#define RC_REPEAT_DELAY 6 +#define RC_REPEAT_DELAY_V1_20 10 -- cgit v1.2.3 From 714df90d630e253a910741d99b28b96fbd56ed7d Mon Sep 17 00:00:00 2001 From: Patrick Boettcher Date: Mon, 1 Dec 2008 10:59:37 +0100 Subject: [PATCH] Cablestar 2 I2C retries (fix CableStar2 support) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From: Antti Seppälä At some point the Flexcop driver was changed to support newer Flexcop cards. These modifications however broke the detection of Cablestar 2 DVB-C cards. The reason is that the earlier version of the driver used to retry unsuccessful i2c operations. The demodulator of Cablestar 2 cards (stv0297) seems to be very dependent on these retries and adding them back fixes Cablestar detection. This patch restores this behaviour for the CableStar2. Priority: high Signed-off-by: Antti Seppälä Signed-off-by: Patrick Boettcher --- linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c | 2 ++ linux/drivers/media/dvb/b2c2/flexcop-i2c.c | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c b/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c index a127a4175..5cded3708 100644 --- a/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c +++ b/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c @@ -628,12 +628,14 @@ int flexcop_frontend_init(struct flexcop_device *fc) } /* try the cable dvb (stv0297) */ + fc->fc_i2c_adap[0].no_base_addr = 1; fc->fe = dvb_attach(stv0297_attach, &alps_tdee4_stv0297_config, i2c); if (fc->fe != NULL) { fc->dev_type = FC_CABLE; fc->fe->ops.tuner_ops.set_params = alps_tdee4_stv0297_tuner_set_params; goto fe_found; } + fc->fc_i2c_adap[0].no_base_addr = 0; /* try the sky v2.3 (vp310/Samsung tbdu18132(tsa5059)) */ fc->fe = dvb_attach(mt312_attach, diff --git a/linux/drivers/media/dvb/b2c2/flexcop-i2c.c b/linux/drivers/media/dvb/b2c2/flexcop-i2c.c index 05635c453..01d27613f 100644 --- a/linux/drivers/media/dvb/b2c2/flexcop-i2c.c +++ b/linux/drivers/media/dvb/b2c2/flexcop-i2c.c @@ -47,8 +47,12 @@ static int flexcop_i2c_read4(struct flexcop_i2c_adapter *i2c, int len = r100.tw_sm_c_100.total_bytes, /* remember total_bytes is buflen-1 */ ret; - r100.tw_sm_c_100.no_base_addr_ack_error = i2c->no_base_addr; ret = flexcop_i2c_operation(i2c->fc, &r100); + if (ret != 0) { + deb_i2c("Retrying operation\n"); + r100.tw_sm_c_100.no_base_addr_ack_error = i2c->no_base_addr; + ret = flexcop_i2c_operation(i2c->fc, &r100); + } if (ret != 0) { deb_i2c("read failed. %d\n", ret); return ret; -- cgit v1.2.3 From dbd5de8bd7cd16274c7b4ed29be253cf73bd3300 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 1 Dec 2008 13:44:48 +0100 Subject: Change power on/off sequence on ov772x Signed-off-by: Kuninori Morimoto Signed-off-by: Guennadi Liakhovetski --- drivers/media/video/ov772x.c | 35 ++++++++++++++++++++++------------- 1 files changed, 22 insertions(+), 13 deletions(-) --- linux/drivers/media/video/ov772x.c | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/ov772x.c b/linux/drivers/media/video/ov772x.c index 2ddc2ab70..cf1c7bcfc 100644 --- a/linux/drivers/media/video/ov772x.c +++ b/linux/drivers/media/video/ov772x.c @@ -593,12 +593,30 @@ static int ov772x_reset(struct i2c_client *client) static int ov772x_init(struct soc_camera_device *icd) { - return 0; + struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); + int ret = 0; + + if (priv->info->link.power) { + ret = priv->info->link.power(&priv->client->dev, 1); + if (ret < 0) + return ret; + } + + if (priv->info->link.reset) + ret = priv->info->link.reset(&priv->client->dev); + + return ret; } static int ov772x_release(struct soc_camera_device *icd) { - return 0; + struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); + int ret = 0; + + if (priv->info->link.power) + ret = priv->info->link.power(&priv->client->dev, 0); + + return ret; } static int ov772x_start_capture(struct soc_camera_device *icd) @@ -814,9 +832,6 @@ static int ov772x_video_probe(struct soc_camera_device *icd) icd->formats = ov772x_fmt_lists; icd->num_formats = ARRAY_SIZE(ov772x_fmt_lists); - if (priv->info->link.power) - priv->info->link.power(&priv->client->dev, 1); - /* * check and show product ID and manufacturer ID */ @@ -824,8 +839,8 @@ static int ov772x_video_probe(struct soc_camera_device *icd) ver = i2c_smbus_read_byte_data(priv->client, VER); if (pid != 0x77 || ver != 0x21) { - if (priv->info->link.power) - priv->info->link.power(&priv->client->dev, 0); + dev_err(&icd->dev, + "Product ID error %x:%x\n", pid, ver); return -ENODEV; } @@ -842,13 +857,7 @@ static int ov772x_video_probe(struct soc_camera_device *icd) static void ov772x_video_remove(struct soc_camera_device *icd) { - struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); - soc_camera_video_stop(icd); - - if (priv->info->link.power) - priv->info->link.power(&priv->client->dev, 0); - } static struct soc_camera_ops ov772x_ops = { -- cgit v1.2.3 From 64934a93ee0c4f6edf946a0c044ee7a0ec871a0c Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 1 Dec 2008 13:44:51 +0100 Subject: Register name fix for ov772x driver Signed-off-by: Kuninori Morimoto Signed-off-by: Guennadi Liakhovetski --- drivers/media/video/ov772x.c | 48 +++++++++++++++++++++--------------------- 1 files changed, 24 insertions(+), 24 deletions(-) --- linux/drivers/media/video/ov772x.c | 48 +++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 24 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/ov772x.c b/linux/drivers/media/video/ov772x.c index cf1c7bcfc..bd6dd150c 100644 --- a/linux/drivers/media/video/ov772x.c +++ b/linux/drivers/media/video/ov772x.c @@ -112,34 +112,34 @@ #define AREF7 0x55 /* Analog reference control */ #define UFIX 0x60 /* U channel fixed value output */ #define VFIX 0x61 /* V channel fixed value output */ -#define AW_BB_BLK 0x62 /* AWB option for advanced AWB */ -#define AW_B_CTRL0 0x63 /* AWB control byte 0 */ +#define AWBB_BLK 0x62 /* AWB option for advanced AWB */ +#define AWB_CTRL0 0x63 /* AWB control byte 0 */ #define DSP_CTRL1 0x64 /* DSP control byte 1 */ #define DSP_CTRL2 0x65 /* DSP control byte 2 */ #define DSP_CTRL3 0x66 /* DSP control byte 3 */ #define DSP_CTRL4 0x67 /* DSP control byte 4 */ -#define AW_B_BIAS 0x68 /* AWB BLC level clip */ -#define AW_BCTRL1 0x69 /* AWB control 1 */ -#define AW_BCTRL2 0x6A /* AWB control 2 */ -#define AW_BCTRL3 0x6B /* AWB control 3 */ -#define AW_BCTRL4 0x6C /* AWB control 4 */ -#define AW_BCTRL5 0x6D /* AWB control 5 */ -#define AW_BCTRL6 0x6E /* AWB control 6 */ -#define AW_BCTRL7 0x6F /* AWB control 7 */ -#define AW_BCTRL8 0x70 /* AWB control 8 */ -#define AW_BCTRL9 0x71 /* AWB control 9 */ -#define AW_BCTRL10 0x72 /* AWB control 10 */ -#define AW_BCTRL11 0x73 /* AWB control 11 */ -#define AW_BCTRL12 0x74 /* AWB control 12 */ -#define AW_BCTRL13 0x75 /* AWB control 13 */ -#define AW_BCTRL14 0x76 /* AWB control 14 */ -#define AW_BCTRL15 0x77 /* AWB control 15 */ -#define AW_BCTRL16 0x78 /* AWB control 16 */ -#define AW_BCTRL17 0x79 /* AWB control 17 */ -#define AW_BCTRL18 0x7A /* AWB control 18 */ -#define AW_BCTRL19 0x7B /* AWB control 19 */ -#define AW_BCTRL20 0x7C /* AWB control 20 */ -#define AW_BCTRL21 0x7D /* AWB control 21 */ +#define AWB_BIAS 0x68 /* AWB BLC level clip */ +#define AWB_CTRL1 0x69 /* AWB control 1 */ +#define AWB_CTRL2 0x6A /* AWB control 2 */ +#define AWB_CTRL3 0x6B /* AWB control 3 */ +#define AWB_CTRL4 0x6C /* AWB control 4 */ +#define AWB_CTRL5 0x6D /* AWB control 5 */ +#define AWB_CTRL6 0x6E /* AWB control 6 */ +#define AWB_CTRL7 0x6F /* AWB control 7 */ +#define AWB_CTRL8 0x70 /* AWB control 8 */ +#define AWB_CTRL9 0x71 /* AWB control 9 */ +#define AWB_CTRL10 0x72 /* AWB control 10 */ +#define AWB_CTRL11 0x73 /* AWB control 11 */ +#define AWB_CTRL12 0x74 /* AWB control 12 */ +#define AWB_CTRL13 0x75 /* AWB control 13 */ +#define AWB_CTRL14 0x76 /* AWB control 14 */ +#define AWB_CTRL15 0x77 /* AWB control 15 */ +#define AWB_CTRL16 0x78 /* AWB control 16 */ +#define AWB_CTRL17 0x79 /* AWB control 17 */ +#define AWB_CTRL18 0x7A /* AWB control 18 */ +#define AWB_CTRL19 0x7B /* AWB control 19 */ +#define AWB_CTRL20 0x7C /* AWB control 20 */ +#define AWB_CTRL21 0x7D /* AWB control 21 */ #define GAM1 0x7E /* Gamma Curve 1st segment input end point */ #define GAM2 0x7F /* Gamma Curve 2nd segment input end point */ #define GAM3 0x80 /* Gamma Curve 3rd segment input end point */ -- cgit v1.2.3 From 0b36e297630eb11d129e39d264704119ef3e5bb6 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 1 Dec 2008 13:44:53 +0100 Subject: soc-camera: merge .try_bus_param() into .try_fmt_cap() .try_bus_param() method from struct soc_camera_host_ops is only called at one location immediately before .try_fmt_cap(), there is no value in keeping these two methods separate, merge them. Signed-off-by: Guennadi Liakhovetski --- drivers/media/video/pxa_camera.c | 6 +++++- drivers/media/video/sh_mobile_ceu_camera.c | 6 +++++- drivers/media/video/soc_camera.c | 5 ----- include/media/soc_camera.h | 1 - 4 files changed, 10 insertions(+), 8 deletions(-) --- linux/drivers/media/video/pxa_camera.c | 6 +++++- linux/drivers/media/video/sh_mobile_ceu_camera.c | 6 +++++- linux/drivers/media/video/soc_camera.c | 5 ----- linux/include/media/soc_camera.h | 1 - 4 files changed, 10 insertions(+), 8 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/pxa_camera.c b/linux/drivers/media/video/pxa_camera.c index 08723ed84..a585f4138 100644 --- a/linux/drivers/media/video/pxa_camera.c +++ b/linux/drivers/media/video/pxa_camera.c @@ -918,6 +918,11 @@ static int pxa_camera_set_fmt_cap(struct soc_camera_device *icd, static int pxa_camera_try_fmt_cap(struct soc_camera_device *icd, struct v4l2_format *f) { + int ret = pxa_camera_try_bus_param(icd, f->fmt.pix.pixelformat); + + if (ret < 0) + return ret; + /* limit to pxa hardware capabilities */ if (f->fmt.pix.height < 32) f->fmt.pix.height = 32; @@ -1044,7 +1049,6 @@ static struct soc_camera_host_ops pxa_soc_camera_host_ops = { .reqbufs = pxa_camera_reqbufs, .poll = pxa_camera_poll, .querycap = pxa_camera_querycap, - .try_bus_param = pxa_camera_try_bus_param, .set_bus_param = pxa_camera_set_bus_param, }; diff --git a/linux/drivers/media/video/sh_mobile_ceu_camera.c b/linux/drivers/media/video/sh_mobile_ceu_camera.c index 2fb8bee0b..0cfcc2fec 100644 --- a/linux/drivers/media/video/sh_mobile_ceu_camera.c +++ b/linux/drivers/media/video/sh_mobile_ceu_camera.c @@ -454,6 +454,11 @@ static int sh_mobile_ceu_set_fmt_cap(struct soc_camera_device *icd, static int sh_mobile_ceu_try_fmt_cap(struct soc_camera_device *icd, struct v4l2_format *f) { + int ret = sh_mobile_ceu_try_bus_param(icd, f->fmt.pix.pixelformat); + + if (ret < 0) + return ret; + /* FIXME: calculate using depth and bus width */ if (f->fmt.pix.height < 4) @@ -541,7 +546,6 @@ static struct soc_camera_host_ops sh_mobile_ceu_host_ops = { .reqbufs = sh_mobile_ceu_reqbufs, .poll = sh_mobile_ceu_poll, .querycap = sh_mobile_ceu_querycap, - .try_bus_param = sh_mobile_ceu_try_bus_param, .set_bus_param = sh_mobile_ceu_set_bus_param, .init_videobuf = sh_mobile_ceu_init_videobuf, }; diff --git a/linux/drivers/media/video/soc_camera.c b/linux/drivers/media/video/soc_camera.c index 7a46697c4..1720f8463 100644 --- a/linux/drivers/media/video/soc_camera.c +++ b/linux/drivers/media/video/soc_camera.c @@ -78,11 +78,6 @@ static int soc_camera_try_fmt_vid_cap(struct file *file, void *priv, return -EINVAL; } - /* test physical bus parameters */ - ret = ici->ops->try_bus_param(icd, f->fmt.pix.pixelformat); - if (ret) - return ret; - /* limit format to hardware capabilities */ ret = ici->ops->try_fmt_cap(icd, f); diff --git a/linux/include/media/soc_camera.h b/linux/include/media/soc_camera.h index 9231e2d90..ee0e6b4be 100644 --- a/linux/include/media/soc_camera.h +++ b/linux/include/media/soc_camera.h @@ -73,7 +73,6 @@ struct soc_camera_host_ops { struct soc_camera_device *); int (*reqbufs)(struct soc_camera_file *, struct v4l2_requestbuffers *); int (*querycap)(struct soc_camera_host *, struct v4l2_capability *); - int (*try_bus_param)(struct soc_camera_device *, __u32); int (*set_bus_param)(struct soc_camera_device *, __u32); unsigned int (*poll)(struct file *, poll_table *); }; -- cgit v1.2.3 From a25098a0d06ad54e605ab676785dd412a00be9bc Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 1 Dec 2008 13:44:56 +0100 Subject: soc-camera: formatting fixes Minor formatting fixes Signed-off-by: Guennadi Liakhovetski --- drivers/media/video/soc_camera.c | 16 +++++++--------- 1 files changed, 7 insertions(+), 9 deletions(-) --- linux/drivers/media/video/soc_camera.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/soc_camera.c b/linux/drivers/media/video/soc_camera.c index 1720f8463..8730869a8 100644 --- a/linux/drivers/media/video/soc_camera.c +++ b/linux/drivers/media/video/soc_camera.c @@ -36,8 +36,8 @@ static LIST_HEAD(devices); static DEFINE_MUTEX(list_lock); static DEFINE_MUTEX(video_lock); -const static struct soc_camera_data_format* -format_by_fourcc(struct soc_camera_device *icd, unsigned int fourcc) +const static struct soc_camera_data_format *format_by_fourcc( + struct soc_camera_device *icd, unsigned int fourcc) { unsigned int i; @@ -48,7 +48,7 @@ format_by_fourcc(struct soc_camera_device *icd, unsigned int fourcc) } static int soc_camera_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) + struct v4l2_format *f) { struct soc_camera_file *icf = file->private_data; struct soc_camera_device *icd = icf->icd; @@ -261,7 +261,7 @@ static int soc_camera_close(struct inode *inode, struct file *file) } static ssize_t soc_camera_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) + size_t count, loff_t *ppos) { struct soc_camera_file *icf = file->private_data; struct soc_camera_device *icd = icf->icd; @@ -306,7 +306,6 @@ static unsigned int soc_camera_poll(struct file *file, poll_table *pt) return ici->ops->poll(file, pt); } - static struct file_operations soc_camera_fops = { .owner = THIS_MODULE, .open = soc_camera_open, @@ -318,9 +317,8 @@ static struct file_operations soc_camera_fops = { .llseek = no_llseek, }; - static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) + struct v4l2_format *f) { struct soc_camera_file *icf = file->private_data; struct soc_camera_device *icd = icf->icd; @@ -367,7 +365,7 @@ static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv, } static int soc_camera_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) + struct v4l2_fmtdesc *f) { struct soc_camera_file *icf = file->private_data; struct soc_camera_device *icd = icf->icd; @@ -386,7 +384,7 @@ static int soc_camera_enum_fmt_vid_cap(struct file *file, void *priv, } static int soc_camera_g_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) + struct v4l2_format *f) { struct soc_camera_file *icf = file->private_data; struct soc_camera_device *icd = icf->icd; -- cgit v1.2.3 From 23c99ad0f49a1ec633853e1b2a265308c755abd0 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 1 Dec 2008 13:44:59 +0100 Subject: soc-camera: let camera host drivers decide upon pixel format Pixel format requested by the user is not necessarily the same, as what a sensor driver provides. There are situations, when a camera host driver provides the required format, but requires a different format from the sensor. Further, the list of formats, supported by sensors is pretty static and can be pretty good described with a constant list of structures. Whereas decisions, made by camera host drivers to support requested formats can be quite complex, therefore it is better to let the host driver do the work. Signed-off-by: Guennadi Liakhovetski --- drivers/media/video/pxa_camera.c | 32 +++++++++++++++- drivers/media/video/sh_mobile_ceu_camera.c | 32 +++++++++++++++- drivers/media/video/soc_camera.c | 58 ++++++++++----------------- include/media/soc_camera.h | 3 + 4 files changed, 87 insertions(+), 38 deletions(-) --- linux/drivers/media/video/pxa_camera.c | 32 ++++++++++++- linux/drivers/media/video/sh_mobile_ceu_camera.c | 32 ++++++++++++- linux/drivers/media/video/soc_camera.c | 58 +++++++++--------------- linux/include/media/soc_camera.h | 3 ++ 4 files changed, 87 insertions(+), 38 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/pxa_camera.c b/linux/drivers/media/video/pxa_camera.c index a585f4138..4c206ac74 100644 --- a/linux/drivers/media/video/pxa_camera.c +++ b/linux/drivers/media/video/pxa_camera.c @@ -912,17 +912,43 @@ static int pxa_camera_try_bus_param(struct soc_camera_device *icd, __u32 pixfmt) static int pxa_camera_set_fmt_cap(struct soc_camera_device *icd, __u32 pixfmt, struct v4l2_rect *rect) { - return icd->ops->set_fmt_cap(icd, pixfmt, rect); + const struct soc_camera_data_format *cam_fmt; + int ret; + + /* + * TODO: find a suitable supported by the SoC output format, check + * whether the sensor supports one of acceptable input formats. + */ + if (pixfmt) { + cam_fmt = soc_camera_format_by_fourcc(icd, pixfmt); + if (!cam_fmt) + return -EINVAL; + } + + ret = icd->ops->set_fmt_cap(icd, pixfmt, rect); + if (pixfmt && !ret) + icd->current_fmt = cam_fmt; + + return ret; } static int pxa_camera_try_fmt_cap(struct soc_camera_device *icd, struct v4l2_format *f) { + const struct soc_camera_data_format *cam_fmt; int ret = pxa_camera_try_bus_param(icd, f->fmt.pix.pixelformat); if (ret < 0) return ret; + /* + * TODO: find a suitable supported by the SoC output format, check + * whether the sensor supports one of acceptable input formats. + */ + cam_fmt = soc_camera_format_by_fourcc(icd, f->fmt.pix.pixelformat); + if (!cam_fmt) + return -EINVAL; + /* limit to pxa hardware capabilities */ if (f->fmt.pix.height < 32) f->fmt.pix.height = 32; @@ -934,6 +960,10 @@ static int pxa_camera_try_fmt_cap(struct soc_camera_device *icd, f->fmt.pix.width = 2048; f->fmt.pix.width &= ~0x01; + f->fmt.pix.bytesperline = f->fmt.pix.width * + DIV_ROUND_UP(cam_fmt->depth, 8); + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + /* limit to sensor capabilities */ return icd->ops->try_fmt_cap(icd, f); } diff --git a/linux/drivers/media/video/sh_mobile_ceu_camera.c b/linux/drivers/media/video/sh_mobile_ceu_camera.c index 0cfcc2fec..8f42a1063 100644 --- a/linux/drivers/media/video/sh_mobile_ceu_camera.c +++ b/linux/drivers/media/video/sh_mobile_ceu_camera.c @@ -448,17 +448,43 @@ static int sh_mobile_ceu_try_bus_param(struct soc_camera_device *icd, static int sh_mobile_ceu_set_fmt_cap(struct soc_camera_device *icd, __u32 pixfmt, struct v4l2_rect *rect) { - return icd->ops->set_fmt_cap(icd, pixfmt, rect); + const struct soc_camera_data_format *cam_fmt; + int ret; + + /* + * TODO: find a suitable supported by the SoC output format, check + * whether the sensor supports one of acceptable input formats. + */ + if (pixfmt) { + cam_fmt = soc_camera_format_by_fourcc(icd, pixfmt); + if (!cam_fmt) + return -EINVAL; + } + + ret = icd->ops->set_fmt_cap(icd, pixfmt, rect); + if (pixfmt && !ret) + icd->current_fmt = cam_fmt; + + return ret; } static int sh_mobile_ceu_try_fmt_cap(struct soc_camera_device *icd, struct v4l2_format *f) { + const struct soc_camera_data_format *cam_fmt; int ret = sh_mobile_ceu_try_bus_param(icd, f->fmt.pix.pixelformat); if (ret < 0) return ret; + /* + * TODO: find a suitable supported by the SoC output format, check + * whether the sensor supports one of acceptable input formats. + */ + cam_fmt = soc_camera_format_by_fourcc(icd, f->fmt.pix.pixelformat); + if (!cam_fmt) + return -EINVAL; + /* FIXME: calculate using depth and bus width */ if (f->fmt.pix.height < 4) @@ -472,6 +498,10 @@ static int sh_mobile_ceu_try_fmt_cap(struct soc_camera_device *icd, f->fmt.pix.width &= ~0x01; f->fmt.pix.height &= ~0x03; + f->fmt.pix.bytesperline = f->fmt.pix.width * + DIV_ROUND_UP(cam_fmt->depth, 8); + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + /* limit to sensor capabilities */ return icd->ops->try_fmt_cap(icd, f); } diff --git a/linux/drivers/media/video/soc_camera.c b/linux/drivers/media/video/soc_camera.c index 8730869a8..0c9c86d8b 100644 --- a/linux/drivers/media/video/soc_camera.c +++ b/linux/drivers/media/video/soc_camera.c @@ -36,7 +36,7 @@ static LIST_HEAD(devices); static DEFINE_MUTEX(list_lock); static DEFINE_MUTEX(video_lock); -const static struct soc_camera_data_format *format_by_fourcc( +const struct soc_camera_data_format *soc_camera_format_by_fourcc( struct soc_camera_device *icd, unsigned int fourcc) { unsigned int i; @@ -46,6 +46,7 @@ const static struct soc_camera_data_format *format_by_fourcc( return icd->formats + i; return NULL; } +EXPORT_SYMBOL(soc_camera_format_by_fourcc); static int soc_camera_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) @@ -55,25 +56,19 @@ static int soc_camera_try_fmt_vid_cap(struct file *file, void *priv, struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); enum v4l2_field field; - const struct soc_camera_data_format *fmt; int ret; WARN_ON(priv != file->private_data); - fmt = format_by_fourcc(icd, f->fmt.pix.pixelformat); - if (!fmt) { - dev_dbg(&icd->dev, "invalid format 0x%08x\n", - f->fmt.pix.pixelformat); - return -EINVAL; - } - - dev_dbg(&icd->dev, "fmt: 0x%08x\n", fmt->fourcc); - + /* + * TODO: this might also have to migrate to host-drivers, if anyone + * wishes to support other fields + */ field = f->fmt.pix.field; if (field == V4L2_FIELD_ANY) { - field = V4L2_FIELD_NONE; - } else if (V4L2_FIELD_NONE != field) { + f->fmt.pix.field = V4L2_FIELD_NONE; + } else if (field != V4L2_FIELD_NONE) { dev_err(&icd->dev, "Field type invalid.\n"); return -EINVAL; } @@ -81,13 +76,6 @@ static int soc_camera_try_fmt_vid_cap(struct file *file, void *priv, /* limit format to hardware capabilities */ ret = ici->ops->try_fmt_cap(icd, f); - /* calculate missing fields */ - f->fmt.pix.field = field; - f->fmt.pix.bytesperline = - (f->fmt.pix.width * fmt->depth) >> 3; - f->fmt.pix.sizeimage = - f->fmt.pix.height * f->fmt.pix.bytesperline; - return ret; } @@ -326,18 +314,10 @@ static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv, to_soc_camera_host(icd->dev.parent); int ret; struct v4l2_rect rect; - const static struct soc_camera_data_format *data_fmt; WARN_ON(priv != file->private_data); - data_fmt = format_by_fourcc(icd, f->fmt.pix.pixelformat); - if (!data_fmt) - return -EINVAL; - - /* buswidth may be further adjusted by the ici */ - icd->buswidth = data_fmt->depth; - - ret = soc_camera_try_fmt_vid_cap(file, icf, f); + ret = soc_camera_try_fmt_vid_cap(file, priv, f); if (ret < 0) return ret; @@ -346,14 +326,21 @@ static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv, rect.width = f->fmt.pix.width; rect.height = f->fmt.pix.height; ret = ici->ops->set_fmt_cap(icd, f->fmt.pix.pixelformat, &rect); - if (ret < 0) + if (ret < 0) { return ret; + } else if (!icd->current_fmt || + icd->current_fmt->fourcc != f->fmt.pix.pixelformat) { + dev_err(&ici->dev, "Host driver hasn't set up current " + "format correctly!\n"); + return -EINVAL; + } - icd->current_fmt = data_fmt; + /* buswidth may be further adjusted by the ici */ + icd->buswidth = icd->current_fmt->depth; icd->width = rect.width; icd->height = rect.height; icf->vb_vidq.field = f->fmt.pix.field; - if (V4L2_BUF_TYPE_VIDEO_CAPTURE != f->type) + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) dev_warn(&icd->dev, "Attention! Wrong buf-type %d\n", f->type); @@ -395,10 +382,9 @@ static int soc_camera_g_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.height = icd->height; f->fmt.pix.field = icf->vb_vidq.field; f->fmt.pix.pixelformat = icd->current_fmt->fourcc; - f->fmt.pix.bytesperline = - (f->fmt.pix.width * icd->current_fmt->depth) >> 3; - f->fmt.pix.sizeimage = - f->fmt.pix.height * f->fmt.pix.bytesperline; + f->fmt.pix.bytesperline = f->fmt.pix.width * + DIV_ROUND_UP(icd->current_fmt->depth, 8); + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; dev_dbg(&icd->dev, "current_fmt->fourcc: 0x%08x\n", icd->current_fmt->fourcc); return 0; diff --git a/linux/include/media/soc_camera.h b/linux/include/media/soc_camera.h index ee0e6b4be..8e8fcb75d 100644 --- a/linux/include/media/soc_camera.h +++ b/linux/include/media/soc_camera.h @@ -105,6 +105,9 @@ extern void soc_camera_device_unregister(struct soc_camera_device *icd); extern int soc_camera_video_start(struct soc_camera_device *icd); extern void soc_camera_video_stop(struct soc_camera_device *icd); +extern const struct soc_camera_data_format *soc_camera_format_by_fourcc( + struct soc_camera_device *icd, unsigned int fourcc); + struct soc_camera_data_format { const char *name; unsigned int depth; -- cgit v1.2.3 From 0245bbfa8052eabd0398e5b0d92345feabfcfafc Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 1 Dec 2008 13:45:21 +0100 Subject: soc-camera: simplify naming We anyway don't follow the s_fmt_vid_cap / g_fmt_vid_cap / try_fmt_vid_cap naming, and soc-camera is so far only about video capture, let's simplify operation names a bit further. set_fmt_cap / try_fmt_cap wasn't a very good choice too. Signed-off-by: Guennadi Liakhovetski --- drivers/media/video/mt9m001.c | 14 +++++++------- drivers/media/video/mt9m111.c | 12 ++++++------ drivers/media/video/mt9v022.c | 14 +++++++------- drivers/media/video/ov772x.c | 14 +++++++------- drivers/media/video/pxa_camera.c | 16 ++++++++-------- drivers/media/video/sh_mobile_ceu_camera.c | 16 ++++++++-------- drivers/media/video/soc_camera.c | 6 +++--- drivers/media/video/soc_camera_platform.c | 12 ++++++------ include/media/soc_camera.h | 10 ++++------ 9 files changed, 56 insertions(+), 58 deletions(-) --- linux/drivers/media/video/mt9m001.c | 14 +++++++------- linux/drivers/media/video/mt9m111.c | 12 ++++++------ linux/drivers/media/video/mt9v022.c | 14 +++++++------- linux/drivers/media/video/ov772x.c | 14 +++++++------- linux/drivers/media/video/pxa_camera.c | 16 ++++++++-------- linux/drivers/media/video/sh_mobile_ceu_camera.c | 16 ++++++++-------- linux/drivers/media/video/soc_camera.c | 6 +++--- linux/drivers/media/video/soc_camera_platform.c | 12 ++++++------ linux/include/media/soc_camera.h | 10 ++++------ 9 files changed, 56 insertions(+), 58 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/mt9m001.c b/linux/drivers/media/video/mt9m001.c index 228183c54..edacba723 100644 --- a/linux/drivers/media/video/mt9m001.c +++ b/linux/drivers/media/video/mt9m001.c @@ -285,8 +285,8 @@ static unsigned long mt9m001_query_bus_param(struct soc_camera_device *icd) width_flag; } -static int mt9m001_set_fmt_cap(struct soc_camera_device *icd, - __u32 pixfmt, struct v4l2_rect *rect) +static int mt9m001_set_fmt(struct soc_camera_device *icd, + __u32 pixfmt, struct v4l2_rect *rect) { struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); int ret; @@ -298,7 +298,7 @@ static int mt9m001_set_fmt_cap(struct soc_camera_device *icd, ret = reg_write(icd, MT9M001_VERTICAL_BLANKING, vblank); /* The caller provides a supported format, as verified per - * call to icd->try_fmt_cap() */ + * call to icd->try_fmt() */ if (!ret) ret = reg_write(icd, MT9M001_COLUMN_START, rect->left); if (!ret) @@ -325,8 +325,8 @@ static int mt9m001_set_fmt_cap(struct soc_camera_device *icd, return ret; } -static int mt9m001_try_fmt_cap(struct soc_camera_device *icd, - struct v4l2_format *f) +static int mt9m001_try_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) { if (f->fmt.pix.height < 32 + icd->y_skip_top) f->fmt.pix.height = 32 + icd->y_skip_top; @@ -447,8 +447,8 @@ static struct soc_camera_ops mt9m001_ops = { .release = mt9m001_release, .start_capture = mt9m001_start_capture, .stop_capture = mt9m001_stop_capture, - .set_fmt_cap = mt9m001_set_fmt_cap, - .try_fmt_cap = mt9m001_try_fmt_cap, + .set_fmt = mt9m001_set_fmt, + .try_fmt = mt9m001_try_fmt, .set_bus_param = mt9m001_set_bus_param, .query_bus_param = mt9m001_query_bus_param, .controls = mt9m001_controls, diff --git a/linux/drivers/media/video/mt9m111.c b/linux/drivers/media/video/mt9m111.c index ceff40edc..1fde94514 100644 --- a/linux/drivers/media/video/mt9m111.c +++ b/linux/drivers/media/video/mt9m111.c @@ -452,8 +452,8 @@ static int mt9m111_set_pixfmt(struct soc_camera_device *icd, u32 pixfmt) return ret; } -static int mt9m111_set_fmt_cap(struct soc_camera_device *icd, - __u32 pixfmt, struct v4l2_rect *rect) +static int mt9m111_set_fmt(struct soc_camera_device *icd, + __u32 pixfmt, struct v4l2_rect *rect) { struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); int ret; @@ -473,8 +473,8 @@ static int mt9m111_set_fmt_cap(struct soc_camera_device *icd, return ret; } -static int mt9m111_try_fmt_cap(struct soc_camera_device *icd, - struct v4l2_format *f) +static int mt9m111_try_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) { if (f->fmt.pix.height > MT9M111_MAX_HEIGHT) f->fmt.pix.height = MT9M111_MAX_HEIGHT; @@ -597,8 +597,8 @@ static struct soc_camera_ops mt9m111_ops = { .release = mt9m111_release, .start_capture = mt9m111_start_capture, .stop_capture = mt9m111_stop_capture, - .set_fmt_cap = mt9m111_set_fmt_cap, - .try_fmt_cap = mt9m111_try_fmt_cap, + .set_fmt = mt9m111_set_fmt, + .try_fmt = mt9m111_try_fmt, .query_bus_param = mt9m111_query_bus_param, .set_bus_param = mt9m111_set_bus_param, .controls = mt9m111_controls, diff --git a/linux/drivers/media/video/mt9v022.c b/linux/drivers/media/video/mt9v022.c index bea695a2c..1ca28c087 100644 --- a/linux/drivers/media/video/mt9v022.c +++ b/linux/drivers/media/video/mt9v022.c @@ -337,14 +337,14 @@ static unsigned long mt9v022_query_bus_param(struct soc_camera_device *icd) width_flag; } -static int mt9v022_set_fmt_cap(struct soc_camera_device *icd, - __u32 pixfmt, struct v4l2_rect *rect) +static int mt9v022_set_fmt(struct soc_camera_device *icd, + __u32 pixfmt, struct v4l2_rect *rect) { struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); int ret; /* The caller provides a supported format, as verified per call to - * icd->try_fmt_cap(), datawidth is from our supported format list */ + * icd->try_fmt(), datawidth is from our supported format list */ switch (pixfmt) { case V4L2_PIX_FMT_GREY: case V4L2_PIX_FMT_Y16: @@ -400,8 +400,8 @@ static int mt9v022_set_fmt_cap(struct soc_camera_device *icd, return 0; } -static int mt9v022_try_fmt_cap(struct soc_camera_device *icd, - struct v4l2_format *f) +static int mt9v022_try_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) { if (f->fmt.pix.height < 32 + icd->y_skip_top) f->fmt.pix.height = 32 + icd->y_skip_top; @@ -538,8 +538,8 @@ static struct soc_camera_ops mt9v022_ops = { .release = mt9v022_release, .start_capture = mt9v022_start_capture, .stop_capture = mt9v022_stop_capture, - .set_fmt_cap = mt9v022_set_fmt_cap, - .try_fmt_cap = mt9v022_try_fmt_cap, + .set_fmt = mt9v022_set_fmt, + .try_fmt = mt9v022_try_fmt, .set_bus_param = mt9v022_set_bus_param, .query_bus_param = mt9v022_query_bus_param, .controls = mt9v022_controls, diff --git a/linux/drivers/media/video/ov772x.c b/linux/drivers/media/video/ov772x.c index bd6dd150c..c023bbc84 100644 --- a/linux/drivers/media/video/ov772x.c +++ b/linux/drivers/media/video/ov772x.c @@ -755,9 +755,9 @@ static int ov772x_set_register(struct soc_camera_device *icd, } #endif -static int ov772x_set_fmt_cap(struct soc_camera_device *icd, - __u32 pixfmt, - struct v4l2_rect *rect) +static int ov772x_set_fmt(struct soc_camera_device *icd, + __u32 pixfmt, + struct v4l2_rect *rect) { struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); int ret = -EINVAL; @@ -778,8 +778,8 @@ static int ov772x_set_fmt_cap(struct soc_camera_device *icd, return ret; } -static int ov772x_try_fmt_cap(struct soc_camera_device *icd, - struct v4l2_format *f) +static int ov772x_try_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) { struct v4l2_pix_format *pix = &f->fmt.pix; struct ov772x_priv *priv; @@ -868,8 +868,8 @@ static struct soc_camera_ops ov772x_ops = { .release = ov772x_release, .start_capture = ov772x_start_capture, .stop_capture = ov772x_stop_capture, - .set_fmt_cap = ov772x_set_fmt_cap, - .try_fmt_cap = ov772x_try_fmt_cap, + .set_fmt = ov772x_set_fmt, + .try_fmt = ov772x_try_fmt, .set_bus_param = ov772x_set_bus_param, .query_bus_param = ov772x_query_bus_param, .get_chip_id = ov772x_get_chip_id, diff --git a/linux/drivers/media/video/pxa_camera.c b/linux/drivers/media/video/pxa_camera.c index 4c206ac74..1cdff7381 100644 --- a/linux/drivers/media/video/pxa_camera.c +++ b/linux/drivers/media/video/pxa_camera.c @@ -909,8 +909,8 @@ static int pxa_camera_try_bus_param(struct soc_camera_device *icd, __u32 pixfmt) return soc_camera_bus_param_compatible(camera_flags, bus_flags) ? 0 : -EINVAL; } -static int pxa_camera_set_fmt_cap(struct soc_camera_device *icd, - __u32 pixfmt, struct v4l2_rect *rect) +static int pxa_camera_set_fmt(struct soc_camera_device *icd, + __u32 pixfmt, struct v4l2_rect *rect) { const struct soc_camera_data_format *cam_fmt; int ret; @@ -925,15 +925,15 @@ static int pxa_camera_set_fmt_cap(struct soc_camera_device *icd, return -EINVAL; } - ret = icd->ops->set_fmt_cap(icd, pixfmt, rect); + ret = icd->ops->set_fmt(icd, pixfmt, rect); if (pixfmt && !ret) icd->current_fmt = cam_fmt; return ret; } -static int pxa_camera_try_fmt_cap(struct soc_camera_device *icd, - struct v4l2_format *f) +static int pxa_camera_try_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) { const struct soc_camera_data_format *cam_fmt; int ret = pxa_camera_try_bus_param(icd, f->fmt.pix.pixelformat); @@ -965,7 +965,7 @@ static int pxa_camera_try_fmt_cap(struct soc_camera_device *icd, f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; /* limit to sensor capabilities */ - return icd->ops->try_fmt_cap(icd, f); + return icd->ops->try_fmt(icd, f); } static int pxa_camera_reqbufs(struct soc_camera_file *icf, @@ -1073,8 +1073,8 @@ static struct soc_camera_host_ops pxa_soc_camera_host_ops = { .remove = pxa_camera_remove_device, .suspend = pxa_camera_suspend, .resume = pxa_camera_resume, - .set_fmt_cap = pxa_camera_set_fmt_cap, - .try_fmt_cap = pxa_camera_try_fmt_cap, + .set_fmt = pxa_camera_set_fmt, + .try_fmt = pxa_camera_try_fmt, .init_videobuf = pxa_camera_init_videobuf, .reqbufs = pxa_camera_reqbufs, .poll = pxa_camera_poll, diff --git a/linux/drivers/media/video/sh_mobile_ceu_camera.c b/linux/drivers/media/video/sh_mobile_ceu_camera.c index 8f42a1063..e2e816213 100644 --- a/linux/drivers/media/video/sh_mobile_ceu_camera.c +++ b/linux/drivers/media/video/sh_mobile_ceu_camera.c @@ -445,8 +445,8 @@ static int sh_mobile_ceu_try_bus_param(struct soc_camera_device *icd, return 0; } -static int sh_mobile_ceu_set_fmt_cap(struct soc_camera_device *icd, - __u32 pixfmt, struct v4l2_rect *rect) +static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd, + __u32 pixfmt, struct v4l2_rect *rect) { const struct soc_camera_data_format *cam_fmt; int ret; @@ -461,15 +461,15 @@ static int sh_mobile_ceu_set_fmt_cap(struct soc_camera_device *icd, return -EINVAL; } - ret = icd->ops->set_fmt_cap(icd, pixfmt, rect); + ret = icd->ops->set_fmt(icd, pixfmt, rect); if (pixfmt && !ret) icd->current_fmt = cam_fmt; return ret; } -static int sh_mobile_ceu_try_fmt_cap(struct soc_camera_device *icd, - struct v4l2_format *f) +static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) { const struct soc_camera_data_format *cam_fmt; int ret = sh_mobile_ceu_try_bus_param(icd, f->fmt.pix.pixelformat); @@ -503,7 +503,7 @@ static int sh_mobile_ceu_try_fmt_cap(struct soc_camera_device *icd, f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; /* limit to sensor capabilities */ - return icd->ops->try_fmt_cap(icd, f); + return icd->ops->try_fmt(icd, f); } static int sh_mobile_ceu_reqbufs(struct soc_camera_file *icf, @@ -571,8 +571,8 @@ static struct soc_camera_host_ops sh_mobile_ceu_host_ops = { .owner = THIS_MODULE, .add = sh_mobile_ceu_add_device, .remove = sh_mobile_ceu_remove_device, - .set_fmt_cap = sh_mobile_ceu_set_fmt_cap, - .try_fmt_cap = sh_mobile_ceu_try_fmt_cap, + .set_fmt = sh_mobile_ceu_set_fmt, + .try_fmt = sh_mobile_ceu_try_fmt, .reqbufs = sh_mobile_ceu_reqbufs, .poll = sh_mobile_ceu_poll, .querycap = sh_mobile_ceu_querycap, diff --git a/linux/drivers/media/video/soc_camera.c b/linux/drivers/media/video/soc_camera.c index 0c9c86d8b..5befc748e 100644 --- a/linux/drivers/media/video/soc_camera.c +++ b/linux/drivers/media/video/soc_camera.c @@ -74,7 +74,7 @@ static int soc_camera_try_fmt_vid_cap(struct file *file, void *priv, } /* limit format to hardware capabilities */ - ret = ici->ops->try_fmt_cap(icd, f); + ret = ici->ops->try_fmt(icd, f); return ret; } @@ -325,7 +325,7 @@ static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv, rect.top = icd->y_current; rect.width = f->fmt.pix.width; rect.height = f->fmt.pix.height; - ret = ici->ops->set_fmt_cap(icd, f->fmt.pix.pixelformat, &rect); + ret = ici->ops->set_fmt(icd, f->fmt.pix.pixelformat, &rect); if (ret < 0) { return ret; } else if (!icd->current_fmt || @@ -554,7 +554,7 @@ static int soc_camera_s_crop(struct file *file, void *fh, if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - ret = ici->ops->set_fmt_cap(icd, 0, &a->c); + ret = ici->ops->set_fmt(icd, 0, &a->c); if (!ret) { icd->width = a->c.width; icd->height = a->c.height; diff --git a/linux/drivers/media/video/soc_camera_platform.c b/linux/drivers/media/video/soc_camera_platform.c index bb7a9d480..c23871e4c 100644 --- a/linux/drivers/media/video/soc_camera_platform.c +++ b/linux/drivers/media/video/soc_camera_platform.c @@ -79,14 +79,14 @@ soc_camera_platform_query_bus_param(struct soc_camera_device *icd) return p->bus_param; } -static int soc_camera_platform_set_fmt_cap(struct soc_camera_device *icd, - __u32 pixfmt, struct v4l2_rect *rect) +static int soc_camera_platform_set_fmt(struct soc_camera_device *icd, + __u32 pixfmt, struct v4l2_rect *rect) { return 0; } -static int soc_camera_platform_try_fmt_cap(struct soc_camera_device *icd, - struct v4l2_format *f) +static int soc_camera_platform_try_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) { struct soc_camera_platform_info *p = soc_camera_platform_get_info(icd); @@ -124,8 +124,8 @@ static struct soc_camera_ops soc_camera_platform_ops = { .release = soc_camera_platform_release, .start_capture = soc_camera_platform_start_capture, .stop_capture = soc_camera_platform_stop_capture, - .set_fmt_cap = soc_camera_platform_set_fmt_cap, - .try_fmt_cap = soc_camera_platform_try_fmt_cap, + .set_fmt = soc_camera_platform_set_fmt, + .try_fmt = soc_camera_platform_try_fmt, .set_bus_param = soc_camera_platform_set_bus_param, .query_bus_param = soc_camera_platform_query_bus_param, }; diff --git a/linux/include/media/soc_camera.h b/linux/include/media/soc_camera.h index 8e8fcb75d..b14f6ddc9 100644 --- a/linux/include/media/soc_camera.h +++ b/linux/include/media/soc_camera.h @@ -66,9 +66,8 @@ struct soc_camera_host_ops { void (*remove)(struct soc_camera_device *); int (*suspend)(struct soc_camera_device *, pm_message_t state); int (*resume)(struct soc_camera_device *); - int (*set_fmt_cap)(struct soc_camera_device *, __u32, - struct v4l2_rect *); - int (*try_fmt_cap)(struct soc_camera_device *, struct v4l2_format *); + int (*set_fmt)(struct soc_camera_device *, __u32, struct v4l2_rect *); + int (*try_fmt)(struct soc_camera_device *, struct v4l2_format *); void (*init_videobuf)(struct videobuf_queue *, struct soc_camera_device *); int (*reqbufs)(struct soc_camera_file *, struct v4l2_requestbuffers *); @@ -125,9 +124,8 @@ struct soc_camera_ops { int (*release)(struct soc_camera_device *); int (*start_capture)(struct soc_camera_device *); int (*stop_capture)(struct soc_camera_device *); - int (*set_fmt_cap)(struct soc_camera_device *, __u32, - struct v4l2_rect *); - int (*try_fmt_cap)(struct soc_camera_device *, struct v4l2_format *); + int (*set_fmt)(struct soc_camera_device *, __u32, struct v4l2_rect *); + int (*try_fmt)(struct soc_camera_device *, struct v4l2_format *); unsigned long (*query_bus_param)(struct soc_camera_device *); int (*set_bus_param)(struct soc_camera_device *, unsigned long); int (*get_chip_id)(struct soc_camera_device *, -- cgit v1.2.3 From df64fb6c88d2244e8a6374b34ff395e31ec6ef4f Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 1 Dec 2008 13:45:24 +0100 Subject: soc-camera: add a per-camera device host private data pointer This pointer will be used by pxa_camera.c to point to its pixel format data. Signed-off-by: Guennadi Liakhovetski --- include/media/soc_camera.h | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) --- linux/include/media/soc_camera.h | 1 + 1 file changed, 1 insertion(+) (limited to 'linux') diff --git a/linux/include/media/soc_camera.h b/linux/include/media/soc_camera.h index b14f6ddc9..dddaf45c9 100644 --- a/linux/include/media/soc_camera.h +++ b/linux/include/media/soc_camera.h @@ -42,6 +42,7 @@ struct soc_camera_device { const struct soc_camera_data_format *formats; int num_formats; struct module *owner; + void *host_priv; /* per-device host private data */ /* soc_camera.c private count. Only accessed with video_lock held */ int use_count; }; -- cgit v1.2.3 From 70459071e4ccc3617dc180337b15366b596f9047 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 1 Dec 2008 13:45:27 +0100 Subject: soc-camera: pixel format negotiation - core support Allocate and fill a list of formats, supported by this specific camera-host combination. Use it for format enumeration. Take care to stay backwards-compatible. Camera hosts rely on sensor formats available, as well as host specific translations. We add a structure so that hosts can define a translation table and use it for format check and setup. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Robert Jarzmik --- drivers/media/video/soc_camera.c | 93 +++++++++++++++++++++++++++++++++----- include/media/soc_camera.h | 25 ++++++++++- 2 files changed, 105 insertions(+), 13 deletions(-) --- linux/drivers/media/video/soc_camera.c | 93 +++++++++++++++++++++++++++++----- linux/include/media/soc_camera.h | 25 ++++++++- 2 files changed, 105 insertions(+), 13 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/soc_camera.c b/linux/drivers/media/video/soc_camera.c index 5befc748e..4a9c52c37 100644 --- a/linux/drivers/media/video/soc_camera.c +++ b/linux/drivers/media/video/soc_camera.c @@ -48,6 +48,18 @@ const struct soc_camera_data_format *soc_camera_format_by_fourcc( } EXPORT_SYMBOL(soc_camera_format_by_fourcc); +const struct soc_camera_format_xlate *soc_camera_xlate_by_fourcc( + struct soc_camera_device *icd, unsigned int fourcc) +{ + unsigned int i; + + for (i = 0; i < icd->num_user_formats; i++) + if (icd->user_formats[i].host_fmt->fourcc == fourcc) + return icd->user_formats + i; + return NULL; +} +EXPORT_SYMBOL(soc_camera_xlate_by_fourcc); + static int soc_camera_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { @@ -162,6 +174,59 @@ static int soc_camera_dqbuf(struct file *file, void *priv, return videobuf_dqbuf(&icf->vb_vidq, p, file->f_flags & O_NONBLOCK); } +static int soc_camera_init_user_formats(struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + int i, fmts = 0; + + if (!ici->ops->get_formats) + /* + * Fallback mode - the host will have to serve all + * sensor-provided formats one-to-one to the user + */ + fmts = icd->num_formats; + else + /* + * First pass - only count formats this host-sensor + * configuration can provide + */ + for (i = 0; i < icd->num_formats; i++) + fmts += ici->ops->get_formats(icd, i, NULL); + + if (!fmts) + return -ENXIO; + + icd->user_formats = + vmalloc(fmts * sizeof(struct soc_camera_format_xlate)); + if (!icd->user_formats) + return -ENOMEM; + + icd->num_user_formats = fmts; + fmts = 0; + + dev_dbg(&icd->dev, "Found %d supported formats.\n", fmts); + + /* Second pass - actually fill data formats */ + for (i = 0; i < icd->num_formats; i++) + if (!ici->ops->get_formats) { + icd->user_formats[i].host_fmt = icd->formats + i; + icd->user_formats[i].cam_fmt = icd->formats + i; + icd->user_formats[i].buswidth = icd->formats[i].depth; + } else { + fmts += ici->ops->get_formats(icd, i, + &icd->user_formats[fmts]); + } + + icd->current_fmt = icd->user_formats[0].host_fmt; + + return 0; +} + +static void soc_camera_free_user_formats(struct soc_camera_device *icd) +{ + vfree(icd->user_formats); +} + static int soc_camera_open(struct inode *inode, struct file *file) { struct video_device *vdev; @@ -198,10 +263,12 @@ static int soc_camera_open(struct inode *inode, struct file *file) /* Now we really have to activate the camera */ if (icd->use_count == 1) { + ret = soc_camera_init_user_formats(icd); + if (ret < 0) + goto eiufmt; ret = ici->ops->add(icd); if (ret < 0) { dev_err(&icd->dev, "Couldn't activate the camera: %d\n", ret); - icd->use_count--; goto eiciadd; } } @@ -217,6 +284,9 @@ static int soc_camera_open(struct inode *inode, struct file *file) /* All errors are entered with the video_lock held */ eiciadd: + soc_camera_free_user_formats(icd); +eiufmt: + icd->use_count--; module_put(ici->ops->owner); emgi: module_put(icd->ops->owner); @@ -235,8 +305,10 @@ static int soc_camera_close(struct inode *inode, struct file *file) mutex_lock(&video_lock); icd->use_count--; - if (!icd->use_count) + if (!icd->use_count) { ici->ops->remove(icd); + soc_camera_free_user_formats(icd); + } module_put(icd->ops->owner); module_put(ici->ops->owner); mutex_unlock(&video_lock); @@ -312,6 +384,7 @@ static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv, struct soc_camera_device *icd = icf->icd; struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + __u32 pixfmt = f->fmt.pix.pixelformat; int ret; struct v4l2_rect rect; @@ -329,14 +402,12 @@ static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv, if (ret < 0) { return ret; } else if (!icd->current_fmt || - icd->current_fmt->fourcc != f->fmt.pix.pixelformat) { - dev_err(&ici->dev, "Host driver hasn't set up current " - "format correctly!\n"); + icd->current_fmt->fourcc != pixfmt) { + dev_err(&ici->dev, + "Host driver hasn't set up current format correctly!\n"); return -EINVAL; } - /* buswidth may be further adjusted by the ici */ - icd->buswidth = icd->current_fmt->depth; icd->width = rect.width; icd->height = rect.height; icf->vb_vidq.field = f->fmt.pix.field; @@ -348,7 +419,7 @@ static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv, icd->width, icd->height); /* set physical bus parameters */ - return ici->ops->set_bus_param(icd, f->fmt.pix.pixelformat); + return ici->ops->set_bus_param(icd, pixfmt); } static int soc_camera_enum_fmt_vid_cap(struct file *file, void *priv, @@ -360,10 +431,10 @@ static int soc_camera_enum_fmt_vid_cap(struct file *file, void *priv, WARN_ON(priv != file->private_data); - if (f->index >= icd->num_formats) + if (f->index >= icd->num_user_formats) return -EINVAL; - format = &icd->formats[f->index]; + format = icd->user_formats[f->index].host_fmt; strlcpy(f->description, format->name, sizeof(f->description)); f->pixelformat = format->fourcc; @@ -920,8 +991,6 @@ int soc_camera_video_start(struct soc_camera_device *icd) vdev->minor = -1; vdev->tvnorms = V4L2_STD_UNKNOWN, - icd->current_fmt = &icd->formats[0]; - err = video_register_device(vdev, VFL_TYPE_GRABBER, vdev->minor); if (err < 0) { dev_err(vdev->parent, "video_register_device failed\n"); diff --git a/linux/include/media/soc_camera.h b/linux/include/media/soc_camera.h index dddaf45c9..da57ffdae 100644 --- a/linux/include/media/soc_camera.h +++ b/linux/include/media/soc_camera.h @@ -41,6 +41,8 @@ struct soc_camera_device { const struct soc_camera_data_format *current_fmt; const struct soc_camera_data_format *formats; int num_formats; + struct soc_camera_format_xlate *user_formats; + int num_user_formats; struct module *owner; void *host_priv; /* per-device host private data */ /* soc_camera.c private count. Only accessed with video_lock held */ @@ -65,8 +67,10 @@ struct soc_camera_host_ops { struct module *owner; int (*add)(struct soc_camera_device *); void (*remove)(struct soc_camera_device *); - int (*suspend)(struct soc_camera_device *, pm_message_t state); + int (*suspend)(struct soc_camera_device *, pm_message_t); int (*resume)(struct soc_camera_device *); + int (*get_formats)(struct soc_camera_device *, int, + struct soc_camera_format_xlate *); int (*set_fmt)(struct soc_camera_device *, __u32, struct v4l2_rect *); int (*try_fmt)(struct soc_camera_device *, struct v4l2_format *); void (*init_videobuf)(struct videobuf_queue *, @@ -107,6 +111,8 @@ extern void soc_camera_video_stop(struct soc_camera_device *icd); extern const struct soc_camera_data_format *soc_camera_format_by_fourcc( struct soc_camera_device *icd, unsigned int fourcc); +extern const struct soc_camera_format_xlate *soc_camera_xlate_by_fourcc( + struct soc_camera_device *icd, unsigned int fourcc); struct soc_camera_data_format { const char *name; @@ -115,6 +121,23 @@ struct soc_camera_data_format { enum v4l2_colorspace colorspace; }; +/** + * struct soc_camera_format_xlate - match between host and sensor formats + * @cam_fmt: sensor format provided by the sensor + * @host_fmt: host format after host translation from cam_fmt + * @buswidth: bus width for this format + * + * Host and sensor translation structure. Used in table of host and sensor + * formats matchings in soc_camera_device. A host can override the generic list + * generation by implementing get_formats(), and use it for format checks and + * format setup. + */ +struct soc_camera_format_xlate { + const struct soc_camera_data_format *cam_fmt; + const struct soc_camera_data_format *host_fmt; + unsigned char buswidth; +}; + struct soc_camera_ops { struct module *owner; int (*probe)(struct soc_camera_device *); -- cgit v1.2.3 From e5d82c47206f8bd3ab34f728d8861a816f246d8a Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Mon, 1 Dec 2008 13:45:35 +0100 Subject: pxa-camera: pixel format negotiation Use the new format-negotiation infrastructure, support all four YUV422 packed and the planar formats. The new translation structure enables to build the format list with buswidth, depth, host format and camera format checked, so that it's not done anymore on try_fmt nor set_fmt. Signed-off-by: Robert Jarzmik Signed-off-by: Guennadi Liakhovetski --- drivers/media/video/pxa_camera.c | 207 ++++++++++++++++++++++++++++++-------- 1 files changed, 165 insertions(+), 42 deletions(-) --- linux/drivers/media/video/pxa_camera.c | 207 ++++++++++++++++++++++++++------- 1 file changed, 165 insertions(+), 42 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/pxa_camera.c b/linux/drivers/media/video/pxa_camera.c index 1cdff7381..210a99780 100644 --- a/linux/drivers/media/video/pxa_camera.c +++ b/linux/drivers/media/video/pxa_camera.c @@ -770,6 +770,9 @@ static int test_platform_param(struct pxa_camera_dev *pcdev, if (!(pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_8)) return -EINVAL; *flags |= SOCAM_DATAWIDTH_8; + break; + default: + return -EINVAL; } return 0; @@ -828,12 +831,10 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) * We fix bit-per-pixel equal to data-width... */ switch (common_flags & SOCAM_DATAWIDTH_MASK) { case SOCAM_DATAWIDTH_10: - icd->buswidth = 10; dw = 4; bpp = 0x40; break; case SOCAM_DATAWIDTH_9: - icd->buswidth = 9; dw = 3; bpp = 0x20; break; @@ -841,7 +842,6 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) /* Actually it can only be 8 now, * default is just to silence compiler warnings */ case SOCAM_DATAWIDTH_8: - icd->buswidth = 8; dw = 2; bpp = 0; } @@ -867,7 +867,17 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) case V4L2_PIX_FMT_YUV422P: pcdev->channels = 3; cicr1 |= CICR1_YCBCR_F; + /* + * Normally, pxa bus wants as input UYVY format. We allow all + * reorderings of the YUV422 format, as no processing is done, + * and the YUV stream is just passed through without any + * transformation. Note that UYVY is the only format that + * should be used if pxa framebuffer Overlay2 is used. + */ + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_VYUY: case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_YVYU: cicr1 |= CICR1_COLOR_SP_VAL(2); break; case V4L2_PIX_FMT_RGB555: @@ -893,13 +903,14 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) return 0; } -static int pxa_camera_try_bus_param(struct soc_camera_device *icd, __u32 pixfmt) +static int pxa_camera_try_bus_param(struct soc_camera_device *icd, + unsigned char buswidth) { struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct pxa_camera_dev *pcdev = ici->priv; unsigned long bus_flags, camera_flags; - int ret = test_platform_param(pcdev, icd->buswidth, &bus_flags); + int ret = test_platform_param(pcdev, buswidth, &bus_flags); if (ret < 0) return ret; @@ -909,25 +920,139 @@ static int pxa_camera_try_bus_param(struct soc_camera_device *icd, __u32 pixfmt) return soc_camera_bus_param_compatible(camera_flags, bus_flags) ? 0 : -EINVAL; } +static const struct soc_camera_data_format pxa_camera_formats[] = { + { + .name = "Planar YUV422 16 bit", + .depth = 16, + .fourcc = V4L2_PIX_FMT_YUV422P, + .colorspace = V4L2_COLORSPACE_JPEG, + }, +}; + +static bool buswidth_supported(struct soc_camera_device *icd, int depth) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct pxa_camera_dev *pcdev = ici->priv; + + switch (depth) { + case 8: + return !!(pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_8); + case 9: + return !!(pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_9); + case 10: + return !!(pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_10); + } + return false; +} + +static int required_buswidth(const struct soc_camera_data_format *fmt) +{ + switch (fmt->fourcc) { + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_VYUY: + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_YVYU: + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_RGB555: + return 8; + default: + return fmt->depth; + } +} + +static int pxa_camera_get_formats(struct soc_camera_device *icd, int idx, + struct soc_camera_format_xlate *xlate) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + int formats = 0, buswidth, ret; + + buswidth = required_buswidth(icd->formats + idx); + + if (!buswidth_supported(icd, buswidth)) + return 0; + + ret = pxa_camera_try_bus_param(icd, buswidth); + if (ret < 0) + return 0; + + switch (icd->formats[idx].fourcc) { + case V4L2_PIX_FMT_UYVY: + formats++; + if (xlate) { + xlate->host_fmt = &pxa_camera_formats[0]; + xlate->cam_fmt = icd->formats + idx; + xlate->buswidth = buswidth; + xlate++; + dev_dbg(&ici->dev, "Providing format %s using %s\n", + pxa_camera_formats[0].name, + icd->formats[idx].name); + } + case V4L2_PIX_FMT_VYUY: + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_YVYU: + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_RGB555: + formats++; + if (xlate) { + xlate->host_fmt = icd->formats + idx; + xlate->cam_fmt = icd->formats + idx; + xlate->buswidth = buswidth; + xlate++; + dev_dbg(&ici->dev, "Providing format %s packed\n", + icd->formats[idx].name); + } + break; + default: + /* Generic pass-through */ + formats++; + if (xlate) { + xlate->host_fmt = icd->formats + idx; + xlate->cam_fmt = icd->formats + idx; + xlate->buswidth = icd->formats[idx].depth; + xlate++; + dev_dbg(&ici->dev, + "Providing format %s in pass-through mode\n", + icd->formats[idx].name); + } + } + + return formats; +} + static int pxa_camera_set_fmt(struct soc_camera_device *icd, __u32 pixfmt, struct v4l2_rect *rect) { - const struct soc_camera_data_format *cam_fmt; - int ret; + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + const struct soc_camera_data_format *host_fmt, *cam_fmt = NULL; + const struct soc_camera_format_xlate *xlate; + int ret, buswidth; - /* - * TODO: find a suitable supported by the SoC output format, check - * whether the sensor supports one of acceptable input formats. - */ - if (pixfmt) { - cam_fmt = soc_camera_format_by_fourcc(icd, pixfmt); - if (!cam_fmt) - return -EINVAL; + xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); + if (!xlate) { + dev_warn(&ici->dev, "Format %x not found\n", pixfmt); + return -EINVAL; } - ret = icd->ops->set_fmt(icd, pixfmt, rect); - if (pixfmt && !ret) - icd->current_fmt = cam_fmt; + buswidth = xlate->buswidth; + host_fmt = xlate->host_fmt; + cam_fmt = xlate->cam_fmt; + + switch (pixfmt) { + case 0: /* Only geometry change */ + ret = icd->ops->set_fmt(icd, pixfmt, rect); + break; + default: + ret = icd->ops->set_fmt(icd, cam_fmt->fourcc, rect); + } + + if (ret < 0) + dev_warn(&ici->dev, "Failed to configure for format %x\n", + pixfmt); + + if (pixfmt && !ret) { + icd->buswidth = buswidth; + icd->current_fmt = host_fmt; + } return ret; } @@ -935,34 +1060,31 @@ static int pxa_camera_set_fmt(struct soc_camera_device *icd, static int pxa_camera_try_fmt(struct soc_camera_device *icd, struct v4l2_format *f) { - const struct soc_camera_data_format *cam_fmt; - int ret = pxa_camera_try_bus_param(icd, f->fmt.pix.pixelformat); - - if (ret < 0) - return ret; + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + const struct soc_camera_format_xlate *xlate; + struct v4l2_pix_format *pix = &f->fmt.pix; + __u32 pixfmt = pix->pixelformat; - /* - * TODO: find a suitable supported by the SoC output format, check - * whether the sensor supports one of acceptable input formats. - */ - cam_fmt = soc_camera_format_by_fourcc(icd, f->fmt.pix.pixelformat); - if (!cam_fmt) + xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); + if (!xlate) { + dev_warn(&ici->dev, "Format %x not found\n", pixfmt); return -EINVAL; + } /* limit to pxa hardware capabilities */ - if (f->fmt.pix.height < 32) - f->fmt.pix.height = 32; - if (f->fmt.pix.height > 2048) - f->fmt.pix.height = 2048; - if (f->fmt.pix.width < 48) - f->fmt.pix.width = 48; - if (f->fmt.pix.width > 2048) - f->fmt.pix.width = 2048; - f->fmt.pix.width &= ~0x01; - - f->fmt.pix.bytesperline = f->fmt.pix.width * - DIV_ROUND_UP(cam_fmt->depth, 8); - f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + if (pix->height < 32) + pix->height = 32; + if (pix->height > 2048) + pix->height = 2048; + if (pix->width < 48) + pix->width = 48; + if (pix->width > 2048) + pix->width = 2048; + pix->width &= ~0x01; + + pix->bytesperline = pix->width * + DIV_ROUND_UP(xlate->host_fmt->depth, 8); + pix->sizeimage = pix->height * pix->bytesperline; /* limit to sensor capabilities */ return icd->ops->try_fmt(icd, f); @@ -1073,6 +1195,7 @@ static struct soc_camera_host_ops pxa_soc_camera_host_ops = { .remove = pxa_camera_remove_device, .suspend = pxa_camera_suspend, .resume = pxa_camera_resume, + .get_formats = pxa_camera_get_formats, .set_fmt = pxa_camera_set_fmt, .try_fmt = pxa_camera_try_fmt, .init_videobuf = pxa_camera_init_videobuf, -- cgit v1.2.3 From 7df653d0550763831781c8010ef17527b04a3010 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Mon, 1 Dec 2008 18:44:02 +0100 Subject: gspca: Webcam 06f8:3004 added in sonixj. From: Jean-Francois Moine Priority: normal Signed-off-by: Jean-Francois Moine --- linux/Documentation/video4linux/gspca.txt | 1 + linux/drivers/media/video/gspca/sonixj.c | 1 + 2 files changed, 2 insertions(+) (limited to 'linux') diff --git a/linux/Documentation/video4linux/gspca.txt b/linux/Documentation/video4linux/gspca.txt index 4c96adc0b..8d0210b24 100644 --- a/linux/Documentation/video4linux/gspca.txt +++ b/linux/Documentation/video4linux/gspca.txt @@ -171,6 +171,7 @@ sunplus 06d6:0031 Trust 610 LCD PowerC@m Zoom spca506 06e1:a190 ADS Instant VCD ov534 06f8:3002 Hercules Blog Webcam ov534 06f8:3003 Hercules Dualpix HD Weblog +sonixj 06f8:3004 Hercules Classic Silver spca508 0733:0110 ViewQuest VQ110 spca508 0130:0130 Clone Digital Webcam 11043 spca501 0733:0401 Intel Create and Share diff --git a/linux/drivers/media/video/gspca/sonixj.c b/linux/drivers/media/video/gspca/sonixj.c index 8037a4ec9..fb8b42daf 100644 --- a/linux/drivers/media/video/gspca/sonixj.c +++ b/linux/drivers/media/video/gspca/sonixj.c @@ -1751,6 +1751,7 @@ static const __devinitdata struct usb_device_id device_table[] = { #endif {USB_DEVICE(0x0471, 0x0328), BSI(SN9C105, MI0360, 0x5d)}, {USB_DEVICE(0x0471, 0x0330), BSI(SN9C105, MI0360, 0x5d)}, + {USB_DEVICE(0x06f8, 0x3004), BSI(SN9C105, OV7660, 0x21)}, {USB_DEVICE(0x0c45, 0x6040), BSI(SN9C102P, HV7131R, 0x11)}, /* bw600.inf: {USB_DEVICE(0x0c45, 0x6040), BSI(SN9C102P, MI0360, 0x5d)}, */ -- cgit v1.2.3 From 1c16293ac6a29e20be55c9d6ed97d9ce5970a3d9 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 1 Dec 2008 19:40:09 +0100 Subject: omap2: add OMAP2 camera driver. From: Sakari Ailus Add a driver for the OMAP2 camera block. OMAP2 is used in e.g. Nokia N800/N810 internet tablet. This driver uses the V4L2 internal ioctl interface. Priority: normal Signed-off-by: Sakari Ailus Signed-off-by: Trilok Soni Signed-off-by: Hans Verkuil --- linux/drivers/media/video/Kconfig | 7 + linux/drivers/media/video/Makefile | 3 + linux/drivers/media/video/omap24xxcam-dma.c | 601 +++++++++ linux/drivers/media/video/omap24xxcam.c | 1908 +++++++++++++++++++++++++++ linux/drivers/media/video/omap24xxcam.h | 593 +++++++++ 5 files changed, 3112 insertions(+) create mode 100644 linux/drivers/media/video/omap24xxcam-dma.c create mode 100644 linux/drivers/media/video/omap24xxcam.c create mode 100644 linux/drivers/media/video/omap24xxcam.h (limited to 'linux') diff --git a/linux/drivers/media/video/Kconfig b/linux/drivers/media/video/Kconfig index c4ee10c37..91652d964 100644 --- a/linux/drivers/media/video/Kconfig +++ b/linux/drivers/media/video/Kconfig @@ -770,6 +770,13 @@ config VIDEO_SH_MOBILE_CEU ---help--- This is a v4l2 driver for the SuperH Mobile CEU Interface +config VIDEO_OMAP2 + tristate "OMAP2 Camera Capture Interface driver" + depends on VIDEO_DEV && ARCH_OMAP2 + select VIDEOBUF_DMA_SG + ---help--- + This is a v4l2 driver for the TI OMAP2 camera capture interface + # # USB Multimedia device configuration # diff --git a/linux/drivers/media/video/Makefile b/linux/drivers/media/video/Makefile index 53d43f9e0..ac147b1ae 100644 --- a/linux/drivers/media/video/Makefile +++ b/linux/drivers/media/video/Makefile @@ -8,6 +8,8 @@ msp3400-objs := msp3400-driver.o msp3400-kthreads.o stkwebcam-objs := stk-webcam.o stk-sensor.o +omap2cam-objs := omap24xxcam.o omap24xxcam-dma.o + videodev-objs := v4l2-dev.o v4l2-ioctl.o obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-compat-ioctl32.o v4l2-int-device.o @@ -130,6 +132,7 @@ obj-$(CONFIG_VIDEO_CX23885) += cx23885/ obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o +obj-$(CONFIG_VIDEO_OMAP2) += omap2cam.o obj-$(CONFIG_SOC_CAMERA) += soc_camera.o obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o obj-$(CONFIG_SOC_CAMERA_MT9M111) += mt9m111.o diff --git a/linux/drivers/media/video/omap24xxcam-dma.c b/linux/drivers/media/video/omap24xxcam-dma.c new file mode 100644 index 000000000..1d54b86c9 --- /dev/null +++ b/linux/drivers/media/video/omap24xxcam-dma.c @@ -0,0 +1,601 @@ +/* + * drivers/media/video/omap24xxcam-dma.c + * + * Copyright (C) 2004 MontaVista Software, Inc. + * Copyright (C) 2004 Texas Instruments. + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Sakari Ailus + * + * Based on code from Andy Lowe and + * David Cohen . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include +#include +#include + +#include "omap24xxcam.h" + +/* + * + * DMA hardware. + * + */ + +/* Ack all interrupt on CSR and IRQSTATUS_L0 */ +static void omap24xxcam_dmahw_ack_all(unsigned long base) +{ + u32 csr; + int i; + + for (i = 0; i < NUM_CAMDMA_CHANNELS; ++i) { + csr = omap24xxcam_reg_in(base, CAMDMA_CSR(i)); + /* ack interrupt in CSR */ + omap24xxcam_reg_out(base, CAMDMA_CSR(i), csr); + } + omap24xxcam_reg_out(base, CAMDMA_IRQSTATUS_L0, 0xf); +} + +/* Ack dmach on CSR and IRQSTATUS_L0 */ +static u32 omap24xxcam_dmahw_ack_ch(unsigned long base, int dmach) +{ + u32 csr; + + csr = omap24xxcam_reg_in(base, CAMDMA_CSR(dmach)); + /* ack interrupt in CSR */ + omap24xxcam_reg_out(base, CAMDMA_CSR(dmach), csr); + /* ack interrupt in IRQSTATUS */ + omap24xxcam_reg_out(base, CAMDMA_IRQSTATUS_L0, (1 << dmach)); + + return csr; +} + +static int omap24xxcam_dmahw_running(unsigned long base, int dmach) +{ + return omap24xxcam_reg_in(base, CAMDMA_CCR(dmach)) & CAMDMA_CCR_ENABLE; +} + +static void omap24xxcam_dmahw_transfer_setup(unsigned long base, int dmach, + dma_addr_t start, u32 len) +{ + omap24xxcam_reg_out(base, CAMDMA_CCR(dmach), + CAMDMA_CCR_SEL_SRC_DST_SYNC + | CAMDMA_CCR_BS + | CAMDMA_CCR_DST_AMODE_POST_INC + | CAMDMA_CCR_SRC_AMODE_POST_INC + | CAMDMA_CCR_FS + | CAMDMA_CCR_WR_ACTIVE + | CAMDMA_CCR_RD_ACTIVE + | CAMDMA_CCR_SYNCHRO_CAMERA); + omap24xxcam_reg_out(base, CAMDMA_CLNK_CTRL(dmach), 0); + omap24xxcam_reg_out(base, CAMDMA_CEN(dmach), len); + omap24xxcam_reg_out(base, CAMDMA_CFN(dmach), 1); + omap24xxcam_reg_out(base, CAMDMA_CSDP(dmach), + CAMDMA_CSDP_WRITE_MODE_POSTED + | CAMDMA_CSDP_DST_BURST_EN_32 + | CAMDMA_CSDP_DST_PACKED + | CAMDMA_CSDP_SRC_BURST_EN_32 + | CAMDMA_CSDP_SRC_PACKED + | CAMDMA_CSDP_DATA_TYPE_8BITS); + omap24xxcam_reg_out(base, CAMDMA_CSSA(dmach), 0); + omap24xxcam_reg_out(base, CAMDMA_CDSA(dmach), start); + omap24xxcam_reg_out(base, CAMDMA_CSEI(dmach), 0); + omap24xxcam_reg_out(base, CAMDMA_CSFI(dmach), DMA_THRESHOLD); + omap24xxcam_reg_out(base, CAMDMA_CDEI(dmach), 0); + omap24xxcam_reg_out(base, CAMDMA_CDFI(dmach), 0); + omap24xxcam_reg_out(base, CAMDMA_CSR(dmach), + CAMDMA_CSR_MISALIGNED_ERR + | CAMDMA_CSR_SECURE_ERR + | CAMDMA_CSR_TRANS_ERR + | CAMDMA_CSR_BLOCK + | CAMDMA_CSR_DROP); + omap24xxcam_reg_out(base, CAMDMA_CICR(dmach), + CAMDMA_CICR_MISALIGNED_ERR_IE + | CAMDMA_CICR_SECURE_ERR_IE + | CAMDMA_CICR_TRANS_ERR_IE + | CAMDMA_CICR_BLOCK_IE + | CAMDMA_CICR_DROP_IE); +} + +static void omap24xxcam_dmahw_transfer_start(unsigned long base, int dmach) +{ + omap24xxcam_reg_out(base, CAMDMA_CCR(dmach), + CAMDMA_CCR_SEL_SRC_DST_SYNC + | CAMDMA_CCR_BS + | CAMDMA_CCR_DST_AMODE_POST_INC + | CAMDMA_CCR_SRC_AMODE_POST_INC + | CAMDMA_CCR_ENABLE + | CAMDMA_CCR_FS + | CAMDMA_CCR_SYNCHRO_CAMERA); +} + +static void omap24xxcam_dmahw_transfer_chain(unsigned long base, int dmach, + int free_dmach) +{ + int prev_dmach, ch; + + if (dmach == 0) + prev_dmach = NUM_CAMDMA_CHANNELS - 1; + else + prev_dmach = dmach - 1; + omap24xxcam_reg_out(base, CAMDMA_CLNK_CTRL(prev_dmach), + CAMDMA_CLNK_CTRL_ENABLE_LNK | dmach); + /* Did we chain the DMA transfer before the previous one + * finished? + */ + ch = (dmach + free_dmach) % NUM_CAMDMA_CHANNELS; + while (!(omap24xxcam_reg_in(base, CAMDMA_CCR(ch)) + & CAMDMA_CCR_ENABLE)) { + if (ch == dmach) { + /* The previous transfer has ended and this one + * hasn't started, so we must not have chained + * to the previous one in time. We'll have to + * start it now. + */ + omap24xxcam_dmahw_transfer_start(base, dmach); + break; + } else + ch = (ch + 1) % NUM_CAMDMA_CHANNELS; + } +} + +/* Abort all chained DMA transfers. After all transfers have been + * aborted and the DMA controller is idle, the completion routines for + * any aborted transfers will be called in sequence. The DMA + * controller may not be idle after this routine completes, because + * the completion routines might start new transfers. + */ +static void omap24xxcam_dmahw_abort_ch(unsigned long base, int dmach) +{ + /* mask all interrupts from this channel */ + omap24xxcam_reg_out(base, CAMDMA_CICR(dmach), 0); + /* unlink this channel */ + omap24xxcam_reg_merge(base, CAMDMA_CLNK_CTRL(dmach), 0, + CAMDMA_CLNK_CTRL_ENABLE_LNK); + /* disable this channel */ + omap24xxcam_reg_merge(base, CAMDMA_CCR(dmach), 0, CAMDMA_CCR_ENABLE); +} + +static void omap24xxcam_dmahw_init(unsigned long base) +{ + omap24xxcam_reg_out(base, CAMDMA_OCP_SYSCONFIG, + CAMDMA_OCP_SYSCONFIG_MIDLEMODE_FSTANDBY + | CAMDMA_OCP_SYSCONFIG_SIDLEMODE_FIDLE + | CAMDMA_OCP_SYSCONFIG_AUTOIDLE); + + omap24xxcam_reg_merge(base, CAMDMA_GCR, 0x10, + CAMDMA_GCR_MAX_CHANNEL_FIFO_DEPTH); + + omap24xxcam_reg_out(base, CAMDMA_IRQENABLE_L0, 0xf); +} + +/* + * + * Individual DMA channel handling. + * + */ + +/* Start a DMA transfer from the camera to memory. + * Returns zero if the transfer was successfully started, or non-zero if all + * DMA channels are already in use or starting is currently inhibited. + */ +static int omap24xxcam_dma_start(struct omap24xxcam_dma *dma, dma_addr_t start, + u32 len, dma_callback_t callback, void *arg) +{ + unsigned long flags; + int dmach; + + spin_lock_irqsave(&dma->lock, flags); + + if (!dma->free_dmach || atomic_read(&dma->dma_stop)) { + spin_unlock_irqrestore(&dma->lock, flags); + return -EBUSY; + } + + dmach = dma->next_dmach; + + dma->ch_state[dmach].callback = callback; + dma->ch_state[dmach].arg = arg; + + omap24xxcam_dmahw_transfer_setup(dma->base, dmach, start, len); + + /* We're ready to start the DMA transfer. */ + + if (dma->free_dmach < NUM_CAMDMA_CHANNELS) { + /* A transfer is already in progress, so try to chain to it. */ + omap24xxcam_dmahw_transfer_chain(dma->base, dmach, + dma->free_dmach); + } else { + /* No transfer is in progress, so we'll just start this one + * now. + */ + omap24xxcam_dmahw_transfer_start(dma->base, dmach); + } + + dma->next_dmach = (dma->next_dmach + 1) % NUM_CAMDMA_CHANNELS; + dma->free_dmach--; + + spin_unlock_irqrestore(&dma->lock, flags); + + return 0; +} + +/* Abort all chained DMA transfers. After all transfers have been + * aborted and the DMA controller is idle, the completion routines for + * any aborted transfers will be called in sequence. The DMA + * controller may not be idle after this routine completes, because + * the completion routines might start new transfers. + */ +static void omap24xxcam_dma_abort(struct omap24xxcam_dma *dma, u32 csr) +{ + unsigned long flags; + int dmach, i, free_dmach; + dma_callback_t callback; + void *arg; + + spin_lock_irqsave(&dma->lock, flags); + + /* stop any DMA transfers in progress */ + dmach = (dma->next_dmach + dma->free_dmach) % NUM_CAMDMA_CHANNELS; + for (i = 0; i < NUM_CAMDMA_CHANNELS; i++) { + omap24xxcam_dmahw_abort_ch(dma->base, dmach); + dmach = (dmach + 1) % NUM_CAMDMA_CHANNELS; + } + + /* We have to be careful here because the callback routine + * might start a new DMA transfer, and we only want to abort + * transfers that were started before this routine was called. + */ + free_dmach = dma->free_dmach; + while ((dma->free_dmach < NUM_CAMDMA_CHANNELS) && + (free_dmach < NUM_CAMDMA_CHANNELS)) { + dmach = (dma->next_dmach + dma->free_dmach) + % NUM_CAMDMA_CHANNELS; + callback = dma->ch_state[dmach].callback; + arg = dma->ch_state[dmach].arg; + dma->free_dmach++; + free_dmach++; + if (callback) { + /* leave interrupts disabled during callback */ + spin_unlock(&dma->lock); + (*callback) (dma, csr, arg); + spin_lock(&dma->lock); + } + } + + spin_unlock_irqrestore(&dma->lock, flags); +} + +/* Abort all chained DMA transfers. After all transfers have been + * aborted and the DMA controller is idle, the completion routines for + * any aborted transfers will be called in sequence. If the completion + * routines attempt to start a new DMA transfer it will fail, so the + * DMA controller will be idle after this routine completes. + */ +static void omap24xxcam_dma_stop(struct omap24xxcam_dma *dma, u32 csr) +{ + atomic_inc(&dma->dma_stop); + omap24xxcam_dma_abort(dma, csr); + atomic_dec(&dma->dma_stop); +} + +/* Camera DMA interrupt service routine. */ +void omap24xxcam_dma_isr(struct omap24xxcam_dma *dma) +{ + int dmach; + dma_callback_t callback; + void *arg; + u32 csr; + const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR + | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR + | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP; + + spin_lock(&dma->lock); + + if (dma->free_dmach == NUM_CAMDMA_CHANNELS) { + /* A camera DMA interrupt occurred while all channels + * are idle, so we'll acknowledge the interrupt in the + * IRQSTATUS register and exit. + */ + omap24xxcam_dmahw_ack_all(dma->base); + spin_unlock(&dma->lock); + return; + } + + while (dma->free_dmach < NUM_CAMDMA_CHANNELS) { + dmach = (dma->next_dmach + dma->free_dmach) + % NUM_CAMDMA_CHANNELS; + if (omap24xxcam_dmahw_running(dma->base, dmach)) { + /* This buffer hasn't finished yet, so we're done. */ + break; + } + csr = omap24xxcam_dmahw_ack_ch(dma->base, dmach); + if (csr & csr_error) { + /* A DMA error occurred, so stop all DMA + * transfers in progress. + */ + spin_unlock(&dma->lock); + omap24xxcam_dma_stop(dma, csr); + return; + } else { + callback = dma->ch_state[dmach].callback; + arg = dma->ch_state[dmach].arg; + dma->free_dmach++; + if (callback) { + spin_unlock(&dma->lock); + (*callback) (dma, csr, arg); + spin_lock(&dma->lock); + } + } + } + + spin_unlock(&dma->lock); + + omap24xxcam_sgdma_process( + container_of(dma, struct omap24xxcam_sgdma, dma)); +} + +void omap24xxcam_dma_hwinit(struct omap24xxcam_dma *dma) +{ + unsigned long flags; + + spin_lock_irqsave(&dma->lock, flags); + + omap24xxcam_dmahw_init(dma->base); + + spin_unlock_irqrestore(&dma->lock, flags); +} + +static void omap24xxcam_dma_init(struct omap24xxcam_dma *dma, + unsigned long base) +{ + int ch; + + /* group all channels on DMA IRQ0 and unmask irq */ + spin_lock_init(&dma->lock); + dma->base = base; + dma->free_dmach = NUM_CAMDMA_CHANNELS; + dma->next_dmach = 0; + for (ch = 0; ch < NUM_CAMDMA_CHANNELS; ch++) { + dma->ch_state[ch].callback = NULL; + dma->ch_state[ch].arg = NULL; + } +} + +/* + * + * Scatter-gather DMA. + * + * High-level DMA construct for transferring whole picture frames to + * memory that is discontinuous. + * + */ + +/* DMA completion routine for the scatter-gather DMA fragments. */ +static void omap24xxcam_sgdma_callback(struct omap24xxcam_dma *dma, u32 csr, + void *arg) +{ + struct omap24xxcam_sgdma *sgdma = + container_of(dma, struct omap24xxcam_sgdma, dma); + int sgslot = (int)arg; + struct sgdma_state *sg_state; + const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR + | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR + | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP; + + spin_lock(&sgdma->lock); + + /* We got an interrupt, we can remove the timer */ + del_timer(&sgdma->reset_timer); + + sg_state = sgdma->sg_state + sgslot; + if (!sg_state->queued_sglist) { + spin_unlock(&sgdma->lock); + printk(KERN_ERR "%s: sgdma completed when none queued!\n", + __func__); + return; + } + + sg_state->csr |= csr; + if (!--sg_state->queued_sglist) { + /* Queue for this sglist is empty, so check to see if we're + * done. + */ + if ((sg_state->next_sglist == sg_state->sglen) + || (sg_state->csr & csr_error)) { + sgdma_callback_t callback = sg_state->callback; + void *arg = sg_state->arg; + u32 sg_csr = sg_state->csr; + /* All done with this sglist */ + sgdma->free_sgdma++; + if (callback) { + spin_unlock(&sgdma->lock); + (*callback) (sgdma, sg_csr, arg); + return; + } + } + } + + spin_unlock(&sgdma->lock); +} + +/* Start queued scatter-gather DMA transfers. */ +void omap24xxcam_sgdma_process(struct omap24xxcam_sgdma *sgdma) +{ + unsigned long flags; + int queued_sgdma, sgslot; + struct sgdma_state *sg_state; + const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR + | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR + | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP; + + spin_lock_irqsave(&sgdma->lock, flags); + + queued_sgdma = NUM_SG_DMA - sgdma->free_sgdma; + sgslot = (sgdma->next_sgdma + sgdma->free_sgdma) % NUM_SG_DMA; + while (queued_sgdma > 0) { + sg_state = sgdma->sg_state + sgslot; + while ((sg_state->next_sglist < sg_state->sglen) && + !(sg_state->csr & csr_error)) { + const struct scatterlist *sglist; + unsigned int len; + + sglist = sg_state->sglist + sg_state->next_sglist; + /* try to start the next DMA transfer */ + if (sg_state->next_sglist + 1 == sg_state->sglen) { + /* + * On the last sg, we handle the case where + * cam->img.pix.sizeimage % PAGE_ALIGN != 0 + */ + len = sg_state->len - sg_state->bytes_read; + } else { + len = sg_dma_len(sglist); + } + + if (omap24xxcam_dma_start(&sgdma->dma, + sg_dma_address(sglist), + len, + omap24xxcam_sgdma_callback, + (void *)sgslot)) { + /* DMA start failed */ + spin_unlock_irqrestore(&sgdma->lock, flags); + return; + } else { + unsigned long expires; + /* DMA start was successful */ + sg_state->next_sglist++; + sg_state->bytes_read += len; + sg_state->queued_sglist++; + + /* We start the reset timer */ + expires = jiffies + HZ; + mod_timer(&sgdma->reset_timer, expires); + } + } + queued_sgdma--; + sgslot = (sgslot + 1) % NUM_SG_DMA; + } + + spin_unlock_irqrestore(&sgdma->lock, flags); +} + +/* + * Queue a scatter-gather DMA transfer from the camera to memory. + * Returns zero if the transfer was successfully queued, or non-zero + * if all of the scatter-gather slots are already in use. + */ +int omap24xxcam_sgdma_queue(struct omap24xxcam_sgdma *sgdma, + const struct scatterlist *sglist, int sglen, + int len, sgdma_callback_t callback, void *arg) +{ + unsigned long flags; + struct sgdma_state *sg_state; + + if ((sglen < 0) || ((sglen > 0) & !sglist)) + return -EINVAL; + + spin_lock_irqsave(&sgdma->lock, flags); + + if (!sgdma->free_sgdma) { + spin_unlock_irqrestore(&sgdma->lock, flags); + return -EBUSY; + } + + sg_state = sgdma->sg_state + sgdma->next_sgdma; + + sg_state->sglist = sglist; + sg_state->sglen = sglen; + sg_state->next_sglist = 0; + sg_state->bytes_read = 0; + sg_state->len = len; + sg_state->queued_sglist = 0; + sg_state->csr = 0; + sg_state->callback = callback; + sg_state->arg = arg; + + sgdma->next_sgdma = (sgdma->next_sgdma + 1) % NUM_SG_DMA; + sgdma->free_sgdma--; + + spin_unlock_irqrestore(&sgdma->lock, flags); + + omap24xxcam_sgdma_process(sgdma); + + return 0; +} + +/* Sync scatter-gather DMA by aborting any DMA transfers currently in progress. + * Any queued scatter-gather DMA transactions that have not yet been started + * will remain queued. The DMA controller will be idle after this routine + * completes. When the scatter-gather queue is restarted, the next + * scatter-gather DMA transfer will begin at the start of a new transaction. + */ +void omap24xxcam_sgdma_sync(struct omap24xxcam_sgdma *sgdma) +{ + unsigned long flags; + int sgslot; + struct sgdma_state *sg_state; + u32 csr = CAMDMA_CSR_TRANS_ERR; + + /* stop any DMA transfers in progress */ + omap24xxcam_dma_stop(&sgdma->dma, csr); + + spin_lock_irqsave(&sgdma->lock, flags); + + if (sgdma->free_sgdma < NUM_SG_DMA) { + sgslot = (sgdma->next_sgdma + sgdma->free_sgdma) % NUM_SG_DMA; + sg_state = sgdma->sg_state + sgslot; + if (sg_state->next_sglist != 0) { + /* This DMA transfer was in progress, so abort it. */ + sgdma_callback_t callback = sg_state->callback; + void *arg = sg_state->arg; + sgdma->free_sgdma++; + if (callback) { + /* leave interrupts masked */ + spin_unlock(&sgdma->lock); + (*callback) (sgdma, csr, arg); + spin_lock(&sgdma->lock); + } + } + } + + spin_unlock_irqrestore(&sgdma->lock, flags); +} + +void omap24xxcam_sgdma_init(struct omap24xxcam_sgdma *sgdma, + unsigned long base, + void (*reset_callback)(unsigned long data), + unsigned long reset_callback_data) +{ + int sg; + + spin_lock_init(&sgdma->lock); + sgdma->free_sgdma = NUM_SG_DMA; + sgdma->next_sgdma = 0; + for (sg = 0; sg < NUM_SG_DMA; sg++) { + sgdma->sg_state[sg].sglen = 0; + sgdma->sg_state[sg].next_sglist = 0; + sgdma->sg_state[sg].bytes_read = 0; + sgdma->sg_state[sg].queued_sglist = 0; + sgdma->sg_state[sg].csr = 0; + sgdma->sg_state[sg].callback = NULL; + sgdma->sg_state[sg].arg = NULL; + } + + omap24xxcam_dma_init(&sgdma->dma, base); + setup_timer(&sgdma->reset_timer, reset_callback, reset_callback_data); +} diff --git a/linux/drivers/media/video/omap24xxcam.c b/linux/drivers/media/video/omap24xxcam.c new file mode 100644 index 000000000..85c3c7c92 --- /dev/null +++ b/linux/drivers/media/video/omap24xxcam.c @@ -0,0 +1,1908 @@ +/* + * drivers/media/video/omap24xxcam.c + * + * OMAP 2 camera block driver. + * + * Copyright (C) 2004 MontaVista Software, Inc. + * Copyright (C) 2004 Texas Instruments. + * Copyright (C) 2007-2008 Nokia Corporation. + * + * Contact: Sakari Ailus + * + * Based on code from Andy Lowe + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include +#include +#include +#include +#include /* needed for videobufs */ +#include +#include +#include +#include + +#include +#include + +#include "omap24xxcam.h" + +#define OMAP24XXCAM_VERSION KERNEL_VERSION(0, 0, 0) + +#define RESET_TIMEOUT_NS 10000 + +static void omap24xxcam_reset(struct omap24xxcam_device *cam); +static int omap24xxcam_sensor_if_enable(struct omap24xxcam_device *cam); +static void omap24xxcam_device_unregister(struct v4l2_int_device *s); +static int omap24xxcam_remove(struct platform_device *pdev); + +/* module parameters */ +static int video_nr = -1; /* video device minor (-1 ==> auto assign) */ +/* + * Maximum amount of memory to use for capture buffers. + * Default is 4800KB, enough to double-buffer SXGA. + */ +static int capture_mem = 1280 * 960 * 2 * 2; + +static struct v4l2_int_device omap24xxcam; + +/* + * + * Clocks. + * + */ + +static void omap24xxcam_clock_put(struct omap24xxcam_device *cam) +{ + if (cam->ick != NULL && !IS_ERR(cam->ick)) + clk_put(cam->ick); + if (cam->fck != NULL && !IS_ERR(cam->fck)) + clk_put(cam->fck); + + cam->ick = cam->fck = NULL; +} + +static int omap24xxcam_clock_get(struct omap24xxcam_device *cam) +{ + int rval = 0; + + cam->fck = clk_get(cam->dev, "cam_fck"); + if (IS_ERR(cam->fck)) { + dev_err(cam->dev, "can't get cam_fck"); + rval = PTR_ERR(cam->fck); + omap24xxcam_clock_put(cam); + return rval; + } + + cam->ick = clk_get(cam->dev, "cam_ick"); + if (IS_ERR(cam->ick)) { + dev_err(cam->dev, "can't get cam_ick"); + rval = PTR_ERR(cam->ick); + omap24xxcam_clock_put(cam); + } + + return rval; +} + +static void omap24xxcam_clock_on(struct omap24xxcam_device *cam) +{ + clk_enable(cam->fck); + clk_enable(cam->ick); +} + +static void omap24xxcam_clock_off(struct omap24xxcam_device *cam) +{ + clk_disable(cam->fck); + clk_disable(cam->ick); +} + +/* + * + * Camera core + * + */ + +/* + * Set xclk. + * + * To disable xclk, use value zero. + */ +static void omap24xxcam_core_xclk_set(const struct omap24xxcam_device *cam, + u32 xclk) +{ + if (xclk) { + u32 divisor = CAM_MCLK / xclk; + + if (divisor == 1) + omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, + CC_CTRL_XCLK, + CC_CTRL_XCLK_DIV_BYPASS); + else + omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, + CC_CTRL_XCLK, divisor); + } else + omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, + CC_CTRL_XCLK, CC_CTRL_XCLK_DIV_STABLE_LOW); +} + +static void omap24xxcam_core_hwinit(const struct omap24xxcam_device *cam) +{ + /* + * Setting the camera core AUTOIDLE bit causes problems with frame + * synchronization, so we will clear the AUTOIDLE bit instead. + */ + omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_SYSCONFIG, + CC_SYSCONFIG_AUTOIDLE); + + /* program the camera interface DMA packet size */ + omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_CTRL_DMA, + CC_CTRL_DMA_EN | (DMA_THRESHOLD / 4 - 1)); + + /* enable camera core error interrupts */ + omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_IRQENABLE, + CC_IRQENABLE_FW_ERR_IRQ + | CC_IRQENABLE_FSC_ERR_IRQ + | CC_IRQENABLE_SSC_ERR_IRQ + | CC_IRQENABLE_FIFO_OF_IRQ); +} + +/* + * Enable the camera core. + * + * Data transfer to the camera DMA starts from next starting frame. + */ +static void omap24xxcam_core_enable(const struct omap24xxcam_device *cam) +{ + + omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_CTRL, + cam->cc_ctrl); +} + +/* + * Disable camera core. + * + * The data transfer will be stopped immediately (CC_CTRL_CC_RST). The + * core internal state machines will be reset. Use + * CC_CTRL_CC_FRAME_TRIG instead if you want to transfer the current + * frame completely. + */ +static void omap24xxcam_core_disable(const struct omap24xxcam_device *cam) +{ + omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_CTRL, + CC_CTRL_CC_RST); +} + +/* Interrupt service routine for camera core interrupts. */ +static void omap24xxcam_core_isr(struct omap24xxcam_device *cam) +{ + u32 cc_irqstatus; + const u32 cc_irqstatus_err = + CC_IRQSTATUS_FW_ERR_IRQ + | CC_IRQSTATUS_FSC_ERR_IRQ + | CC_IRQSTATUS_SSC_ERR_IRQ + | CC_IRQSTATUS_FIFO_UF_IRQ + | CC_IRQSTATUS_FIFO_OF_IRQ; + + cc_irqstatus = omap24xxcam_reg_in(cam->mmio_base + CC_REG_OFFSET, + CC_IRQSTATUS); + omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_IRQSTATUS, + cc_irqstatus); + + if (cc_irqstatus & cc_irqstatus_err + && !atomic_read(&cam->in_reset)) { + dev_dbg(cam->dev, "resetting camera, cc_irqstatus 0x%x\n", + cc_irqstatus); + omap24xxcam_reset(cam); + } +} + +/* + * + * videobuf_buffer handling. + * + * Memory for mmapped videobuf_buffers is not allocated + * conventionally, but by several kmalloc allocations and then + * creating the scatterlist on our own. User-space buffers are handled + * normally. + * + */ + +/* + * Free the memory-mapped buffer memory allocated for a + * videobuf_buffer and the associated scatterlist. + */ +static void omap24xxcam_vbq_free_mmap_buffer(struct videobuf_buffer *vb) +{ + struct videobuf_dmabuf *dma = videobuf_to_dma(vb); + size_t alloc_size; + struct page *page; + int i; + + if (dma->sglist == NULL) + return; + + i = dma->sglen; + while (i) { + i--; + alloc_size = sg_dma_len(&dma->sglist[i]); + page = sg_page(&dma->sglist[i]); + do { + ClearPageReserved(page++); + } while (alloc_size -= PAGE_SIZE); + __free_pages(sg_page(&dma->sglist[i]), + get_order(sg_dma_len(&dma->sglist[i]))); + } + + kfree(dma->sglist); + dma->sglist = NULL; +} + +/* Release all memory related to the videobuf_queue. */ +static void omap24xxcam_vbq_free_mmap_buffers(struct videobuf_queue *vbq) +{ + int i; + + mutex_lock(&vbq->vb_lock); + + for (i = 0; i < VIDEO_MAX_FRAME; i++) { + if (NULL == vbq->bufs[i]) + continue; + if (V4L2_MEMORY_MMAP != vbq->bufs[i]->memory) + continue; + vbq->ops->buf_release(vbq, vbq->bufs[i]); + omap24xxcam_vbq_free_mmap_buffer(vbq->bufs[i]); + kfree(vbq->bufs[i]); + vbq->bufs[i] = NULL; + } + + mutex_unlock(&vbq->vb_lock); + + videobuf_mmap_free(vbq); +} + +/* + * Allocate physically as contiguous as possible buffer for video + * frame and allocate and build DMA scatter-gather list for it. + */ +static int omap24xxcam_vbq_alloc_mmap_buffer(struct videobuf_buffer *vb) +{ + unsigned int order; + size_t alloc_size, size = vb->bsize; /* vb->bsize is page aligned */ + struct page *page; + int max_pages, err = 0, i = 0; + struct videobuf_dmabuf *dma = videobuf_to_dma(vb); + + /* + * allocate maximum size scatter-gather list. Note this is + * overhead. We may not use as many entries as we allocate + */ + max_pages = vb->bsize >> PAGE_SHIFT; + dma->sglist = kcalloc(max_pages, sizeof(*dma->sglist), GFP_KERNEL); + if (dma->sglist == NULL) { + err = -ENOMEM; + goto out; + } + + while (size) { + order = get_order(size); + /* + * do not over-allocate even if we would get larger + * contiguous chunk that way + */ + if ((PAGE_SIZE << order) > size) + order--; + + /* try to allocate as many contiguous pages as possible */ + page = alloc_pages(GFP_KERNEL | GFP_DMA, order); + /* if allocation fails, try to allocate smaller amount */ + while (page == NULL) { + order--; + page = alloc_pages(GFP_KERNEL | GFP_DMA, order); + if (page == NULL && !order) { + err = -ENOMEM; + goto out; + } + } + size -= (PAGE_SIZE << order); + + /* append allocated chunk of pages into scatter-gather list */ + sg_set_page(&dma->sglist[i], page, PAGE_SIZE << order, 0); + dma->sglen++; + i++; + + alloc_size = (PAGE_SIZE << order); + + /* clear pages before giving them to user space */ + memset(page_address(page), 0, alloc_size); + + /* mark allocated pages reserved */ + do { + SetPageReserved(page++); + } while (alloc_size -= PAGE_SIZE); + } + /* + * REVISIT: not fully correct to assign nr_pages == sglen but + * video-buf is passing nr_pages for e.g. unmap_sg calls + */ + dma->nr_pages = dma->sglen; + dma->direction = PCI_DMA_FROMDEVICE; + + return 0; + +out: + omap24xxcam_vbq_free_mmap_buffer(vb); + return err; +} + +static int omap24xxcam_vbq_alloc_mmap_buffers(struct videobuf_queue *vbq, + unsigned int count) +{ + int i, err = 0; + struct omap24xxcam_fh *fh = + container_of(vbq, struct omap24xxcam_fh, vbq); + + mutex_lock(&vbq->vb_lock); + + for (i = 0; i < count; i++) { + err = omap24xxcam_vbq_alloc_mmap_buffer(vbq->bufs[i]); + if (err) + goto out; + dev_dbg(fh->cam->dev, "sglen is %d for buffer %d\n", + videobuf_to_dma(vbq->bufs[i])->sglen, i); + } + + mutex_unlock(&vbq->vb_lock); + + return 0; +out: + while (i) { + i--; + omap24xxcam_vbq_free_mmap_buffer(vbq->bufs[i]); + } + + mutex_unlock(&vbq->vb_lock); + + return err; +} + +/* + * This routine is called from interrupt context when a scatter-gather DMA + * transfer of a videobuf_buffer completes. + */ +static void omap24xxcam_vbq_complete(struct omap24xxcam_sgdma *sgdma, + u32 csr, void *arg) +{ + struct omap24xxcam_device *cam = + container_of(sgdma, struct omap24xxcam_device, sgdma); + struct omap24xxcam_fh *fh = cam->streaming->private_data; + struct videobuf_buffer *vb = (struct videobuf_buffer *)arg; + const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR + | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR + | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP; + unsigned long flags; + + spin_lock_irqsave(&cam->core_enable_disable_lock, flags); + if (--cam->sgdma_in_queue == 0) + omap24xxcam_core_disable(cam); + spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags); + + do_gettimeofday(&vb->ts); + vb->field_count = atomic_add_return(2, &fh->field_count); + if (csr & csr_error) { + vb->state = VIDEOBUF_ERROR; + if (!atomic_read(&fh->cam->in_reset)) { + dev_dbg(cam->dev, "resetting camera, csr 0x%x\n", csr); + omap24xxcam_reset(cam); + } + } else + vb->state = VIDEOBUF_DONE; + wake_up(&vb->done); +} + +static void omap24xxcam_vbq_release(struct videobuf_queue *vbq, + struct videobuf_buffer *vb) +{ + struct videobuf_dmabuf *dma = videobuf_to_dma(vb); + + /* wait for buffer, especially to get out of the sgdma queue */ + videobuf_waiton(vb, 0, 0); + if (vb->memory == V4L2_MEMORY_MMAP) { + dma_unmap_sg(vbq->dev, dma->sglist, dma->sglen, + dma->direction); + dma->direction = DMA_NONE; + } else { + videobuf_dma_unmap(vbq, videobuf_to_dma(vb)); + videobuf_dma_free(videobuf_to_dma(vb)); + } + + vb->state = VIDEOBUF_NEEDS_INIT; +} + +/* + * Limit the number of available kernel image capture buffers based on the + * number requested, the currently selected image size, and the maximum + * amount of memory permitted for kernel capture buffers. + */ +static int omap24xxcam_vbq_setup(struct videobuf_queue *vbq, unsigned int *cnt, + unsigned int *size) +{ + struct omap24xxcam_fh *fh = vbq->priv_data; + + if (*cnt <= 0) + *cnt = VIDEO_MAX_FRAME; /* supply a default number of buffers */ + + if (*cnt > VIDEO_MAX_FRAME) + *cnt = VIDEO_MAX_FRAME; + + *size = fh->pix.sizeimage; + + /* accessing fh->cam->capture_mem is ok, it's constant */ + while (*size * *cnt > fh->cam->capture_mem) + (*cnt)--; + + return 0; +} + +static int omap24xxcam_dma_iolock(struct videobuf_queue *vbq, + struct videobuf_dmabuf *dma) +{ + int err = 0; + + dma->direction = PCI_DMA_FROMDEVICE; + if (!dma_map_sg(vbq->dev, dma->sglist, dma->sglen, dma->direction)) { + kfree(dma->sglist); + dma->sglist = NULL; + dma->sglen = 0; + err = -EIO; + } + + return err; +} + +static int omap24xxcam_vbq_prepare(struct videobuf_queue *vbq, + struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct omap24xxcam_fh *fh = vbq->priv_data; + int err = 0; + + /* + * Accessing pix here is okay since it's constant while + * streaming is on (and we only get called then). + */ + if (vb->baddr) { + /* This is a userspace buffer. */ + if (fh->pix.sizeimage > vb->bsize) { + /* The buffer isn't big enough. */ + err = -EINVAL; + } else + vb->size = fh->pix.sizeimage; + } else { + if (vb->state != VIDEOBUF_NEEDS_INIT) { + /* + * We have a kernel bounce buffer that has + * already been allocated. + */ + if (fh->pix.sizeimage > vb->size) { + /* + * The image size has been changed to + * a larger size since this buffer was + * allocated, so we need to free and + * reallocate it. + */ + omap24xxcam_vbq_release(vbq, vb); + vb->size = fh->pix.sizeimage; + } + } else { + /* We need to allocate a new kernel bounce buffer. */ + vb->size = fh->pix.sizeimage; + } + } + + if (err) + return err; + + vb->width = fh->pix.width; + vb->height = fh->pix.height; + vb->field = field; + + if (vb->state == VIDEOBUF_NEEDS_INIT) { + if (vb->memory == V4L2_MEMORY_MMAP) + /* + * we have built the scatter-gather list by ourself so + * do the scatter-gather mapping as well + */ + err = omap24xxcam_dma_iolock(vbq, videobuf_to_dma(vb)); + else + err = videobuf_iolock(vbq, vb, NULL); + } + + if (!err) + vb->state = VIDEOBUF_PREPARED; + else + omap24xxcam_vbq_release(vbq, vb); + + return err; +} + +static void omap24xxcam_vbq_queue(struct videobuf_queue *vbq, + struct videobuf_buffer *vb) +{ + struct omap24xxcam_fh *fh = vbq->priv_data; + struct omap24xxcam_device *cam = fh->cam; + enum videobuf_state state = vb->state; + unsigned long flags; + int err; + + /* + * FIXME: We're marking the buffer active since we have no + * pretty way of marking it active exactly when the + * scatter-gather transfer starts. + */ + vb->state = VIDEOBUF_ACTIVE; + + err = omap24xxcam_sgdma_queue(&fh->cam->sgdma, + videobuf_to_dma(vb)->sglist, + videobuf_to_dma(vb)->sglen, vb->size, + omap24xxcam_vbq_complete, vb); + + if (!err) { + spin_lock_irqsave(&cam->core_enable_disable_lock, flags); + if (++cam->sgdma_in_queue == 1 + && !atomic_read(&cam->in_reset)) + omap24xxcam_core_enable(cam); + spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags); + } else { + /* + * Oops. We're not supposed to get any errors here. + * The only way we could get an error is if we ran out + * of scatter-gather DMA slots, but we are supposed to + * have at least as many scatter-gather DMA slots as + * video buffers so that can't happen. + */ + dev_err(cam->dev, "failed to queue a video buffer for dma!\n"); + dev_err(cam->dev, "likely a bug in the driver!\n"); + vb->state = state; + } +} + +static struct videobuf_queue_ops omap24xxcam_vbq_ops = { + .buf_setup = omap24xxcam_vbq_setup, + .buf_prepare = omap24xxcam_vbq_prepare, + .buf_queue = omap24xxcam_vbq_queue, + .buf_release = omap24xxcam_vbq_release, +}; + +/* + * + * OMAP main camera system + * + */ + +/* + * Reset camera block to power-on state. + */ +static void omap24xxcam_poweron_reset(struct omap24xxcam_device *cam) +{ + int max_loop = RESET_TIMEOUT_NS; + + /* Reset whole camera subsystem */ + omap24xxcam_reg_out(cam->mmio_base, + CAM_SYSCONFIG, + CAM_SYSCONFIG_SOFTRESET); + + /* Wait till it's finished */ + while (!(omap24xxcam_reg_in(cam->mmio_base, CAM_SYSSTATUS) + & CAM_SYSSTATUS_RESETDONE) + && --max_loop) { + ndelay(1); + } + + if (!(omap24xxcam_reg_in(cam->mmio_base, CAM_SYSSTATUS) + & CAM_SYSSTATUS_RESETDONE)) + dev_err(cam->dev, "camera soft reset timeout\n"); +} + +/* + * (Re)initialise the camera block. + */ +static void omap24xxcam_hwinit(struct omap24xxcam_device *cam) +{ + omap24xxcam_poweron_reset(cam); + + /* set the camera subsystem autoidle bit */ + omap24xxcam_reg_out(cam->mmio_base, CAM_SYSCONFIG, + CAM_SYSCONFIG_AUTOIDLE); + + /* set the camera MMU autoidle bit */ + omap24xxcam_reg_out(cam->mmio_base, + CAMMMU_REG_OFFSET + CAMMMU_SYSCONFIG, + CAMMMU_SYSCONFIG_AUTOIDLE); + + omap24xxcam_core_hwinit(cam); + + omap24xxcam_dma_hwinit(&cam->sgdma.dma); +} + +/* + * Callback for dma transfer stalling. + */ +static void omap24xxcam_stalled_dma_reset(unsigned long data) +{ + struct omap24xxcam_device *cam = (struct omap24xxcam_device *)data; + + if (!atomic_read(&cam->in_reset)) { + dev_dbg(cam->dev, "dma stalled, resetting camera\n"); + omap24xxcam_reset(cam); + } +} + +/* + * Stop capture. Mark we're doing a reset, stop DMA transfers and + * core. (No new scatter-gather transfers will be queued whilst + * in_reset is non-zero.) + * + * If omap24xxcam_capture_stop is called from several places at + * once, only the first call will have an effect. Similarly, the last + * call omap24xxcam_streaming_cont will have effect. + * + * Serialisation is ensured by using cam->core_enable_disable_lock. + */ +static void omap24xxcam_capture_stop(struct omap24xxcam_device *cam) +{ + unsigned long flags; + + spin_lock_irqsave(&cam->core_enable_disable_lock, flags); + + if (atomic_inc_return(&cam->in_reset) != 1) { + spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags); + return; + } + + omap24xxcam_core_disable(cam); + + spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags); + + omap24xxcam_sgdma_sync(&cam->sgdma); +} + +/* + * Reset and continue streaming. + * + * Note: Resetting the camera FIFO via the CC_RST bit in the CC_CTRL + * register is supposed to be sufficient to recover from a camera + * interface error, but it doesn't seem to be enough. If we only do + * that then subsequent image captures are out of sync by either one + * or two times DMA_THRESHOLD bytes. Resetting and re-initializing the + * entire camera subsystem prevents the problem with frame + * synchronization. + */ +static void omap24xxcam_capture_cont(struct omap24xxcam_device *cam) +{ + unsigned long flags; + + spin_lock_irqsave(&cam->core_enable_disable_lock, flags); + + if (atomic_read(&cam->in_reset) != 1) + goto out; + + omap24xxcam_hwinit(cam); + + omap24xxcam_sensor_if_enable(cam); + + omap24xxcam_sgdma_process(&cam->sgdma); + + if (cam->sgdma_in_queue) + omap24xxcam_core_enable(cam); + +out: + atomic_dec(&cam->in_reset); + spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags); +} + +static ssize_t +omap24xxcam_streaming_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct omap24xxcam_device *cam = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", cam->streaming ? "active" : "inactive"); +} +static DEVICE_ATTR(streaming, S_IRUGO, omap24xxcam_streaming_show, NULL); + +/* + * Stop capture and restart it. I.e. reset the camera during use. + */ +static void omap24xxcam_reset(struct omap24xxcam_device *cam) +{ + omap24xxcam_capture_stop(cam); + omap24xxcam_capture_cont(cam); +} + +/* + * The main interrupt handler. + */ +static irqreturn_t omap24xxcam_isr(int irq, void *arg) +{ + struct omap24xxcam_device *cam = (struct omap24xxcam_device *)arg; + u32 irqstatus; + unsigned int irqhandled = 0; + + irqstatus = omap24xxcam_reg_in(cam->mmio_base, CAM_IRQSTATUS); + + if (irqstatus & + (CAM_IRQSTATUS_DMA_IRQ2 | CAM_IRQSTATUS_DMA_IRQ1 + | CAM_IRQSTATUS_DMA_IRQ0)) { + omap24xxcam_dma_isr(&cam->sgdma.dma); + irqhandled = 1; + } + if (irqstatus & CAM_IRQSTATUS_CC_IRQ) { + omap24xxcam_core_isr(cam); + irqhandled = 1; + } + if (irqstatus & CAM_IRQSTATUS_MMU_IRQ) + dev_err(cam->dev, "unhandled camera MMU interrupt!\n"); + + return IRQ_RETVAL(irqhandled); +} + +/* + * + * Sensor handling. + * + */ + +/* + * Enable the external sensor interface. Try to negotiate interface + * parameters with the sensor and start using the new ones. The calls + * to sensor_if_enable and sensor_if_disable need not to be balanced. + */ +static int omap24xxcam_sensor_if_enable(struct omap24xxcam_device *cam) +{ + int rval; + struct v4l2_ifparm p; + + rval = vidioc_int_g_ifparm(cam->sdev, &p); + if (rval) { + dev_err(cam->dev, "vidioc_int_g_ifparm failed with %d\n", rval); + return rval; + } + + cam->if_type = p.if_type; + + cam->cc_ctrl = CC_CTRL_CC_EN; + + switch (p.if_type) { + case V4L2_IF_TYPE_BT656: + if (p.u.bt656.frame_start_on_rising_vs) + cam->cc_ctrl |= CC_CTRL_NOBT_SYNCHRO; + if (p.u.bt656.bt_sync_correct) + cam->cc_ctrl |= CC_CTRL_BT_CORRECT; + if (p.u.bt656.swap) + cam->cc_ctrl |= CC_CTRL_PAR_ORDERCAM; + if (p.u.bt656.latch_clk_inv) + cam->cc_ctrl |= CC_CTRL_PAR_CLK_POL; + if (p.u.bt656.nobt_hs_inv) + cam->cc_ctrl |= CC_CTRL_NOBT_HS_POL; + if (p.u.bt656.nobt_vs_inv) + cam->cc_ctrl |= CC_CTRL_NOBT_VS_POL; + + switch (p.u.bt656.mode) { + case V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT: + cam->cc_ctrl |= CC_CTRL_PAR_MODE_NOBT8; + break; + case V4L2_IF_TYPE_BT656_MODE_NOBT_10BIT: + cam->cc_ctrl |= CC_CTRL_PAR_MODE_NOBT10; + break; + case V4L2_IF_TYPE_BT656_MODE_NOBT_12BIT: + cam->cc_ctrl |= CC_CTRL_PAR_MODE_NOBT12; + break; + case V4L2_IF_TYPE_BT656_MODE_BT_8BIT: + cam->cc_ctrl |= CC_CTRL_PAR_MODE_BT8; + break; + case V4L2_IF_TYPE_BT656_MODE_BT_10BIT: + cam->cc_ctrl |= CC_CTRL_PAR_MODE_BT10; + break; + default: + dev_err(cam->dev, + "bt656 interface mode %d not supported\n", + p.u.bt656.mode); + return -EINVAL; + } + /* + * The clock rate that the sensor wants has changed. + * We have to adjust the xclk from OMAP 2 side to + * match the sensor's wish as closely as possible. + */ + if (p.u.bt656.clock_curr != cam->if_u.bt656.xclk) { + u32 xclk = p.u.bt656.clock_curr; + u32 divisor; + + if (xclk == 0) + return -EINVAL; + + if (xclk > CAM_MCLK) + xclk = CAM_MCLK; + + divisor = CAM_MCLK / xclk; + if (divisor * xclk < CAM_MCLK) + divisor++; + if (CAM_MCLK / divisor < p.u.bt656.clock_min + && divisor > 1) + divisor--; + if (divisor > 30) + divisor = 30; + + xclk = CAM_MCLK / divisor; + + if (xclk < p.u.bt656.clock_min + || xclk > p.u.bt656.clock_max) + return -EINVAL; + + cam->if_u.bt656.xclk = xclk; + } + omap24xxcam_core_xclk_set(cam, cam->if_u.bt656.xclk); + break; + default: + /* FIXME: how about other interfaces? */ + dev_err(cam->dev, "interface type %d not supported\n", + p.if_type); + return -EINVAL; + } + + return 0; +} + +static void omap24xxcam_sensor_if_disable(const struct omap24xxcam_device *cam) +{ + switch (cam->if_type) { + case V4L2_IF_TYPE_BT656: + omap24xxcam_core_xclk_set(cam, 0); + break; + } +} + +/* + * Initialise the sensor hardware. + */ +static int omap24xxcam_sensor_init(struct omap24xxcam_device *cam) +{ + int err = 0; + struct v4l2_int_device *sdev = cam->sdev; + + omap24xxcam_clock_on(cam); + err = omap24xxcam_sensor_if_enable(cam); + if (err) { + dev_err(cam->dev, "sensor interface could not be enabled at " + "initialisation, %d\n", err); + cam->sdev = NULL; + goto out; + } + + /* power up sensor during sensor initialization */ + vidioc_int_s_power(sdev, 1); + + err = vidioc_int_dev_init(sdev); + if (err) { + dev_err(cam->dev, "cannot initialize sensor, error %d\n", err); + /* Sensor init failed --- it's nonexistent to us! */ + cam->sdev = NULL; + goto out; + } + + dev_info(cam->dev, "sensor is %s\n", sdev->name); + +out: + omap24xxcam_sensor_if_disable(cam); + omap24xxcam_clock_off(cam); + + vidioc_int_s_power(sdev, 0); + + return err; +} + +static void omap24xxcam_sensor_exit(struct omap24xxcam_device *cam) +{ + if (cam->sdev) + vidioc_int_dev_exit(cam->sdev); +} + +static void omap24xxcam_sensor_disable(struct omap24xxcam_device *cam) +{ + omap24xxcam_sensor_if_disable(cam); + omap24xxcam_clock_off(cam); + vidioc_int_s_power(cam->sdev, 0); +} + +/* + * Power-up and configure camera sensor. It's ready for capturing now. + */ +static int omap24xxcam_sensor_enable(struct omap24xxcam_device *cam) +{ + int rval; + + omap24xxcam_clock_on(cam); + + omap24xxcam_sensor_if_enable(cam); + + rval = vidioc_int_s_power(cam->sdev, 1); + if (rval) + goto out; + + rval = vidioc_int_init(cam->sdev); + if (rval) + goto out; + + return 0; + +out: + omap24xxcam_sensor_disable(cam); + + return rval; +} + +static void omap24xxcam_sensor_reset_work(struct work_struct *work) +{ + struct omap24xxcam_device *cam = + container_of(work, struct omap24xxcam_device, + sensor_reset_work); + + if (atomic_read(&cam->reset_disable)) + return; + + omap24xxcam_capture_stop(cam); + + if (vidioc_int_reset(cam->sdev) == 0) { + vidioc_int_init(cam->sdev); + } else { + /* Can't reset it by vidioc_int_reset. */ + omap24xxcam_sensor_disable(cam); + omap24xxcam_sensor_enable(cam); + } + + omap24xxcam_capture_cont(cam); +} + +/* + * + * IOCTL interface. + * + */ + +static int vidioc_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + struct omap24xxcam_fh *ofh = fh; + struct omap24xxcam_device *cam = ofh->cam; + + strlcpy(cap->driver, CAM_NAME, sizeof(cap->driver)); + strlcpy(cap->card, cam->vfd->name, sizeof(cap->card)); + cap->version = OMAP24XXCAM_VERSION; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + + return 0; +} + +static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_fmtdesc *f) +{ + struct omap24xxcam_fh *ofh = fh; + struct omap24xxcam_device *cam = ofh->cam; + int rval; + + rval = vidioc_int_enum_fmt_cap(cam->sdev, f); + + return rval; +} + +static int vidioc_g_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct omap24xxcam_fh *ofh = fh; + struct omap24xxcam_device *cam = ofh->cam; + int rval; + + mutex_lock(&cam->mutex); + rval = vidioc_int_g_fmt_cap(cam->sdev, f); + mutex_unlock(&cam->mutex); + + return rval; +} + +static int vidioc_s_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct omap24xxcam_fh *ofh = fh; + struct omap24xxcam_device *cam = ofh->cam; + int rval; + + mutex_lock(&cam->mutex); + if (cam->streaming) { + rval = -EBUSY; + goto out; + } + + rval = vidioc_int_s_fmt_cap(cam->sdev, f); + +out: + mutex_unlock(&cam->mutex); + + if (!rval) { + mutex_lock(&ofh->vbq.vb_lock); + ofh->pix = f->fmt.pix; + mutex_unlock(&ofh->vbq.vb_lock); + } + + memset(f, 0, sizeof(*f)); + vidioc_g_fmt_vid_cap(file, fh, f); + + return rval; +} + +static int vidioc_try_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct omap24xxcam_fh *ofh = fh; + struct omap24xxcam_device *cam = ofh->cam; + int rval; + + mutex_lock(&cam->mutex); + rval = vidioc_int_try_fmt_cap(cam->sdev, f); + mutex_unlock(&cam->mutex); + + return rval; +} + +static int vidioc_reqbufs(struct file *file, void *fh, + struct v4l2_requestbuffers *b) +{ + struct omap24xxcam_fh *ofh = fh; + struct omap24xxcam_device *cam = ofh->cam; + int rval; + + mutex_lock(&cam->mutex); + if (cam->streaming) { + mutex_unlock(&cam->mutex); + return -EBUSY; + } + + omap24xxcam_vbq_free_mmap_buffers(&ofh->vbq); + mutex_unlock(&cam->mutex); + + rval = videobuf_reqbufs(&ofh->vbq, b); + + /* + * Either videobuf_reqbufs failed or the buffers are not + * memory-mapped (which would need special attention). + */ + if (rval < 0 || b->memory != V4L2_MEMORY_MMAP) + goto out; + + rval = omap24xxcam_vbq_alloc_mmap_buffers(&ofh->vbq, rval); + if (rval) + omap24xxcam_vbq_free_mmap_buffers(&ofh->vbq); + +out: + return rval; +} + +static int vidioc_querybuf(struct file *file, void *fh, + struct v4l2_buffer *b) +{ + struct omap24xxcam_fh *ofh = fh; + + return videobuf_querybuf(&ofh->vbq, b); +} + +static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *b) +{ + struct omap24xxcam_fh *ofh = fh; + + return videobuf_qbuf(&ofh->vbq, b); +} + +static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b) +{ + struct omap24xxcam_fh *ofh = fh; + struct omap24xxcam_device *cam = ofh->cam; + struct videobuf_buffer *vb; + int rval; + +videobuf_dqbuf_again: + rval = videobuf_dqbuf(&ofh->vbq, b, file->f_flags & O_NONBLOCK); + if (rval) + goto out; + + vb = ofh->vbq.bufs[b->index]; + + mutex_lock(&cam->mutex); + /* _needs_reset returns -EIO if reset is required. */ + rval = vidioc_int_g_needs_reset(cam->sdev, (void *)vb->baddr); + mutex_unlock(&cam->mutex); + if (rval == -EIO) + schedule_work(&cam->sensor_reset_work); + else + rval = 0; + +out: + /* + * This is a hack. We don't want to show -EIO to the user + * space. Requeue the buffer and try again if we're not doing + * this in non-blocking mode. + */ + if (rval == -EIO) { + videobuf_qbuf(&ofh->vbq, b); + if (!(file->f_flags & O_NONBLOCK)) + goto videobuf_dqbuf_again; + /* + * We don't have a videobuf_buffer now --- maybe next + * time... + */ + rval = -EAGAIN; + } + + return rval; +} + +static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i) +{ + struct omap24xxcam_fh *ofh = fh; + struct omap24xxcam_device *cam = ofh->cam; + int rval; + + mutex_lock(&cam->mutex); + if (cam->streaming) { + rval = -EBUSY; + goto out; + } + + rval = omap24xxcam_sensor_if_enable(cam); + if (rval) { + dev_dbg(cam->dev, "vidioc_int_g_ifparm failed\n"); + goto out; + } + + rval = videobuf_streamon(&ofh->vbq); + if (!rval) { + cam->streaming = file; + sysfs_notify(&cam->dev->kobj, NULL, "streaming"); + } + +out: + mutex_unlock(&cam->mutex); + + return rval; +} + +static int vidioc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i) +{ + struct omap24xxcam_fh *ofh = fh; + struct omap24xxcam_device *cam = ofh->cam; + struct videobuf_queue *q = &ofh->vbq; + int rval; + + atomic_inc(&cam->reset_disable); + + flush_scheduled_work(); + + rval = videobuf_streamoff(q); + if (!rval) { + mutex_lock(&cam->mutex); + cam->streaming = NULL; + mutex_unlock(&cam->mutex); + sysfs_notify(&cam->dev->kobj, NULL, "streaming"); + } + + atomic_dec(&cam->reset_disable); + + return rval; +} + +static int vidioc_enum_input(struct file *file, void *fh, + struct v4l2_input *inp) +{ + if (inp->index > 0) + return -EINVAL; + + strlcpy(inp->name, "camera", sizeof(inp->name)); + inp->type = V4L2_INPUT_TYPE_CAMERA; + + return 0; +} + +static int vidioc_g_input(struct file *file, void *fh, unsigned int *i) +{ + *i = 0; + + return 0; +} + +static int vidioc_s_input(struct file *file, void *fh, unsigned int i) +{ + if (i > 0) + return -EINVAL; + + return 0; +} + +static int vidioc_queryctrl(struct file *file, void *fh, + struct v4l2_queryctrl *a) +{ + struct omap24xxcam_fh *ofh = fh; + struct omap24xxcam_device *cam = ofh->cam; + int rval; + + rval = vidioc_int_queryctrl(cam->sdev, a); + + return rval; +} + +static int vidioc_g_ctrl(struct file *file, void *fh, + struct v4l2_control *a) +{ + struct omap24xxcam_fh *ofh = fh; + struct omap24xxcam_device *cam = ofh->cam; + int rval; + + mutex_lock(&cam->mutex); + rval = vidioc_int_g_ctrl(cam->sdev, a); + mutex_unlock(&cam->mutex); + + return rval; +} + +static int vidioc_s_ctrl(struct file *file, void *fh, + struct v4l2_control *a) +{ + struct omap24xxcam_fh *ofh = fh; + struct omap24xxcam_device *cam = ofh->cam; + int rval; + + mutex_lock(&cam->mutex); + rval = vidioc_int_s_ctrl(cam->sdev, a); + mutex_unlock(&cam->mutex); + + return rval; +} + +static int vidioc_g_parm(struct file *file, void *fh, + struct v4l2_streamparm *a) { + struct omap24xxcam_fh *ofh = fh; + struct omap24xxcam_device *cam = ofh->cam; + int rval; + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + mutex_lock(&cam->mutex); + rval = vidioc_int_g_parm(cam->sdev, a); + mutex_unlock(&cam->mutex); + + return rval; +} + +static int vidioc_s_parm(struct file *file, void *fh, + struct v4l2_streamparm *a) +{ + struct omap24xxcam_fh *ofh = fh; + struct omap24xxcam_device *cam = ofh->cam; + struct v4l2_streamparm old_streamparm; + int rval; + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + mutex_lock(&cam->mutex); + if (cam->streaming) { + rval = -EBUSY; + goto out; + } + + old_streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + rval = vidioc_int_g_parm(cam->sdev, &old_streamparm); + if (rval) + goto out; + + rval = vidioc_int_s_parm(cam->sdev, a); + if (rval) + goto out; + + rval = omap24xxcam_sensor_if_enable(cam); + /* + * Revert to old streaming parameters if enabling sensor + * interface with the new ones failed. + */ + if (rval) + vidioc_int_s_parm(cam->sdev, &old_streamparm); + +out: + mutex_unlock(&cam->mutex); + + return rval; +} + +/* + * + * File operations. + * + */ + +static unsigned int omap24xxcam_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct omap24xxcam_fh *fh = file->private_data; + struct omap24xxcam_device *cam = fh->cam; + struct videobuf_buffer *vb; + + mutex_lock(&cam->mutex); + if (cam->streaming != file) { + mutex_unlock(&cam->mutex); + return POLLERR; + } + mutex_unlock(&cam->mutex); + + mutex_lock(&fh->vbq.vb_lock); + if (list_empty(&fh->vbq.stream)) { + mutex_unlock(&fh->vbq.vb_lock); + return POLLERR; + } + vb = list_entry(fh->vbq.stream.next, struct videobuf_buffer, stream); + mutex_unlock(&fh->vbq.vb_lock); + + poll_wait(file, &vb->done, wait); + + if (vb->state == VIDEOBUF_DONE || vb->state == VIDEOBUF_ERROR) + return POLLIN | POLLRDNORM; + + return 0; +} + +static int omap24xxcam_mmap_buffers(struct file *file, + struct vm_area_struct *vma) +{ + struct omap24xxcam_fh *fh = file->private_data; + struct omap24xxcam_device *cam = fh->cam; + struct videobuf_queue *vbq = &fh->vbq; + unsigned int first, last, size, i, j; + int err = 0; + + mutex_lock(&cam->mutex); + if (cam->streaming) { + mutex_unlock(&cam->mutex); + return -EBUSY; + } + mutex_unlock(&cam->mutex); + mutex_lock(&vbq->vb_lock); + + /* look for first buffer to map */ + for (first = 0; first < VIDEO_MAX_FRAME; first++) { + if (NULL == vbq->bufs[first]) + continue; + if (V4L2_MEMORY_MMAP != vbq->bufs[first]->memory) + continue; + if (vbq->bufs[first]->boff == (vma->vm_pgoff << PAGE_SHIFT)) + break; + } + + /* look for last buffer to map */ + for (size = 0, last = first; last < VIDEO_MAX_FRAME; last++) { + if (NULL == vbq->bufs[last]) + continue; + if (V4L2_MEMORY_MMAP != vbq->bufs[last]->memory) + continue; + size += vbq->bufs[last]->bsize; + if (size == (vma->vm_end - vma->vm_start)) + break; + } + + size = 0; + for (i = first; i <= last; i++) { + struct videobuf_dmabuf *dma = videobuf_to_dma(vbq->bufs[i]); + + for (j = 0; j < dma->sglen; j++) { + err = remap_pfn_range( + vma, vma->vm_start + size, + page_to_pfn(sg_page(&dma->sglist[j])), + sg_dma_len(&dma->sglist[j]), vma->vm_page_prot); + if (err) + goto out; + size += sg_dma_len(&dma->sglist[j]); + } + } + +out: + mutex_unlock(&vbq->vb_lock); + + return err; +} + +static int omap24xxcam_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct omap24xxcam_fh *fh = file->private_data; + int rval; + + /* let the video-buf mapper check arguments and set-up structures */ + rval = videobuf_mmap_mapper(&fh->vbq, vma); + if (rval) + return rval; + + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + /* do mapping to our allocated buffers */ + rval = omap24xxcam_mmap_buffers(file, vma); + /* + * In case of error, free vma->vm_private_data allocated by + * videobuf_mmap_mapper. + */ + if (rval) + kfree(vma->vm_private_data); + + return rval; +} + +static int omap24xxcam_open(struct inode *inode, struct file *file) +{ + int minor = iminor(inode); + struct omap24xxcam_device *cam = omap24xxcam.priv; + struct omap24xxcam_fh *fh; + struct v4l2_format format; + + if (!cam || !cam->vfd || (cam->vfd->minor != minor)) + return -ENODEV; + + fh = kzalloc(sizeof(*fh), GFP_KERNEL); + if (fh == NULL) + return -ENOMEM; + + mutex_lock(&cam->mutex); + if (cam->sdev == NULL || !try_module_get(cam->sdev->module)) { + mutex_unlock(&cam->mutex); + goto out_try_module_get; + } + + if (atomic_inc_return(&cam->users) == 1) { + omap24xxcam_hwinit(cam); + if (omap24xxcam_sensor_enable(cam)) { + mutex_unlock(&cam->mutex); + goto out_omap24xxcam_sensor_enable; + } + } + mutex_unlock(&cam->mutex); + + fh->cam = cam; + mutex_lock(&cam->mutex); + vidioc_int_g_fmt_cap(cam->sdev, &format); + mutex_unlock(&cam->mutex); + /* FIXME: how about fh->pix when there are more users? */ + fh->pix = format.fmt.pix; + + file->private_data = fh; + + spin_lock_init(&fh->vbq_lock); + + videobuf_queue_sg_init(&fh->vbq, &omap24xxcam_vbq_ops, NULL, + &fh->vbq_lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_NONE, + sizeof(struct videobuf_buffer), fh); + + return 0; + +out_omap24xxcam_sensor_enable: + omap24xxcam_poweron_reset(cam); + module_put(cam->sdev->module); + +out_try_module_get: + kfree(fh); + + return -ENODEV; +} + +static int omap24xxcam_release(struct inode *inode, struct file *file) +{ + struct omap24xxcam_fh *fh = file->private_data; + struct omap24xxcam_device *cam = fh->cam; + + atomic_inc(&cam->reset_disable); + + flush_scheduled_work(); + + /* stop streaming capture */ + videobuf_streamoff(&fh->vbq); + + mutex_lock(&cam->mutex); + if (cam->streaming == file) { + cam->streaming = NULL; + mutex_unlock(&cam->mutex); + sysfs_notify(&cam->dev->kobj, NULL, "streaming"); + } else { + mutex_unlock(&cam->mutex); + } + + atomic_dec(&cam->reset_disable); + + omap24xxcam_vbq_free_mmap_buffers(&fh->vbq); + + /* + * Make sure the reset work we might have scheduled is not + * pending! It may be run *only* if we have users. (And it may + * not be scheduled anymore since streaming is already + * disabled.) + */ + flush_scheduled_work(); + + mutex_lock(&cam->mutex); + if (atomic_dec_return(&cam->users) == 0) { + omap24xxcam_sensor_disable(cam); + omap24xxcam_poweron_reset(cam); + } + mutex_unlock(&cam->mutex); + + file->private_data = NULL; + + module_put(cam->sdev->module); + kfree(fh); + + return 0; +} + +static struct file_operations omap24xxcam_fops = { + .llseek = no_llseek, + .ioctl = video_ioctl2, + .poll = omap24xxcam_poll, + .mmap = omap24xxcam_mmap, + .open = omap24xxcam_open, + .release = omap24xxcam_release, +}; + +/* + * + * Power management. + * + */ + +#ifdef CONFIG_PM +static int omap24xxcam_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct omap24xxcam_device *cam = platform_get_drvdata(pdev); + + if (atomic_read(&cam->users) == 0) + return 0; + + if (!atomic_read(&cam->reset_disable)) + omap24xxcam_capture_stop(cam); + + omap24xxcam_sensor_disable(cam); + omap24xxcam_poweron_reset(cam); + + return 0; +} + +static int omap24xxcam_resume(struct platform_device *pdev) +{ + struct omap24xxcam_device *cam = platform_get_drvdata(pdev); + + if (atomic_read(&cam->users) == 0) + return 0; + + omap24xxcam_hwinit(cam); + omap24xxcam_sensor_enable(cam); + + if (!atomic_read(&cam->reset_disable)) + omap24xxcam_capture_cont(cam); + + return 0; +} +#endif /* CONFIG_PM */ + +static const struct v4l2_ioctl_ops omap24xxcam_ioctl_fops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_g_parm = vidioc_g_parm, + .vidioc_s_parm = vidioc_s_parm, +}; + +/* + * + * Camera device (i.e. /dev/video). + * + */ + +static int omap24xxcam_device_register(struct v4l2_int_device *s) +{ + struct omap24xxcam_device *cam = s->u.slave->master->priv; + struct video_device *vfd; + int rval; + + /* We already have a slave. */ + if (cam->sdev) + return -EBUSY; + + cam->sdev = s; + + if (device_create_file(cam->dev, &dev_attr_streaming) != 0) { + dev_err(cam->dev, "could not register sysfs entry\n"); + rval = -EBUSY; + goto err; + } + + /* initialize the video_device struct */ + vfd = cam->vfd = video_device_alloc(); + if (!vfd) { + dev_err(cam->dev, "could not allocate video device struct\n"); + rval = -ENOMEM; + goto err; + } + vfd->release = video_device_release; + + vfd->parent = cam->dev; + + strlcpy(vfd->name, CAM_NAME, sizeof(vfd->name)); + vfd->vfl_type = VID_TYPE_CAPTURE | VID_TYPE_CHROMAKEY; + vfd->fops = &omap24xxcam_fops; + vfd->minor = -1; + vfd->ioctl_ops = &omap24xxcam_ioctl_fops; + + omap24xxcam_hwinit(cam); + + rval = omap24xxcam_sensor_init(cam); + if (rval) + goto err; + + if (video_register_device(vfd, VFL_TYPE_GRABBER, video_nr) < 0) { + dev_err(cam->dev, "could not register V4L device\n"); + vfd->minor = -1; + rval = -EBUSY; + goto err; + } + + omap24xxcam_poweron_reset(cam); + + dev_info(cam->dev, "registered device video%d\n", vfd->minor); + + return 0; + +err: + omap24xxcam_device_unregister(s); + + return rval; +} + +static void omap24xxcam_device_unregister(struct v4l2_int_device *s) +{ + struct omap24xxcam_device *cam = s->u.slave->master->priv; + + omap24xxcam_sensor_exit(cam); + + if (cam->vfd) { + if (cam->vfd->minor == -1) { + /* + * The device was never registered, so release the + * video_device struct directly. + */ + video_device_release(cam->vfd); + } else { + /* + * The unregister function will release the + * video_device struct as well as + * unregistering it. + */ + video_unregister_device(cam->vfd); + } + cam->vfd = NULL; + } + + device_remove_file(cam->dev, &dev_attr_streaming); + + cam->sdev = NULL; +} + +static struct v4l2_int_master omap24xxcam_master = { + .attach = omap24xxcam_device_register, + .detach = omap24xxcam_device_unregister, +}; + +static struct v4l2_int_device omap24xxcam = { + .module = THIS_MODULE, + .name = CAM_NAME, + .type = v4l2_int_type_master, + .u = { + .master = &omap24xxcam_master + }, +}; + +/* + * + * Driver initialisation and deinitialisation. + * + */ + +static int __init omap24xxcam_probe(struct platform_device *pdev) +{ + struct omap24xxcam_device *cam; + struct resource *mem; + int irq; + + cam = kzalloc(sizeof(*cam), GFP_KERNEL); + if (!cam) { + dev_err(&pdev->dev, "could not allocate memory\n"); + goto err; + } + + platform_set_drvdata(pdev, cam); + + cam->dev = &pdev->dev; + + /* + * Impose a lower limit on the amount of memory allocated for + * capture. We require at least enough memory to double-buffer + * QVGA (300KB). + */ + if (capture_mem < 320 * 240 * 2 * 2) + capture_mem = 320 * 240 * 2 * 2; + cam->capture_mem = capture_mem; + + /* request the mem region for the camera registers */ + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(cam->dev, "no mem resource?\n"); + goto err; + } + if (!request_mem_region(mem->start, (mem->end - mem->start) + 1, + pdev->name)) { + dev_err(cam->dev, + "cannot reserve camera register I/O region\n"); + goto err; + } + cam->mmio_base_phys = mem->start; + cam->mmio_size = (mem->end - mem->start) + 1; + + /* map the region */ + cam->mmio_base = (unsigned long) + ioremap_nocache(cam->mmio_base_phys, cam->mmio_size); + if (!cam->mmio_base) { + dev_err(cam->dev, "cannot map camera register I/O region\n"); + goto err; + } + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) { + dev_err(cam->dev, "no irq for camera?\n"); + goto err; + } + + /* install the interrupt service routine */ + if (request_irq(irq, omap24xxcam_isr, 0, CAM_NAME, cam)) { + dev_err(cam->dev, + "could not install interrupt service routine\n"); + goto err; + } + cam->irq = irq; + + if (omap24xxcam_clock_get(cam)) + goto err; + + INIT_WORK(&cam->sensor_reset_work, omap24xxcam_sensor_reset_work); + + mutex_init(&cam->mutex); + spin_lock_init(&cam->core_enable_disable_lock); + + omap24xxcam_sgdma_init(&cam->sgdma, + cam->mmio_base + CAMDMA_REG_OFFSET, + omap24xxcam_stalled_dma_reset, + (unsigned long)cam); + + omap24xxcam.priv = cam; + + if (v4l2_int_device_register(&omap24xxcam)) + goto err; + + return 0; + +err: + omap24xxcam_remove(pdev); + return -ENODEV; +} + +static int omap24xxcam_remove(struct platform_device *pdev) +{ + struct omap24xxcam_device *cam = platform_get_drvdata(pdev); + + if (!cam) + return 0; + + if (omap24xxcam.priv != NULL) + v4l2_int_device_unregister(&omap24xxcam); + omap24xxcam.priv = NULL; + + omap24xxcam_clock_put(cam); + + if (cam->irq) { + free_irq(cam->irq, cam); + cam->irq = 0; + } + + if (cam->mmio_base) { + iounmap((void *)cam->mmio_base); + cam->mmio_base = 0; + } + + if (cam->mmio_base_phys) { + release_mem_region(cam->mmio_base_phys, cam->mmio_size); + cam->mmio_base_phys = 0; + } + + kfree(cam); + + return 0; +} + +static struct platform_driver omap24xxcam_driver = { + .probe = omap24xxcam_probe, + .remove = omap24xxcam_remove, +#ifdef CONFIG_PM + .suspend = omap24xxcam_suspend, + .resume = omap24xxcam_resume, +#endif + .driver = { + .name = CAM_NAME, + .owner = THIS_MODULE, + }, +}; + +/* + * + * Module initialisation and deinitialisation + * + */ + +static int __init omap24xxcam_init(void) +{ + return platform_driver_register(&omap24xxcam_driver); +} + +static void __exit omap24xxcam_cleanup(void) +{ + platform_driver_unregister(&omap24xxcam_driver); +} + +MODULE_AUTHOR("Sakari Ailus "); +MODULE_DESCRIPTION("OMAP24xx Video for Linux camera driver"); +MODULE_LICENSE("GPL"); +module_param(video_nr, int, 0); +MODULE_PARM_DESC(video_nr, + "Minor number for video device (-1 ==> auto assign)"); +module_param(capture_mem, int, 0); +MODULE_PARM_DESC(capture_mem, "Maximum amount of memory for capture " + "buffers (default 4800kiB)"); + +module_init(omap24xxcam_init); +module_exit(omap24xxcam_cleanup); diff --git a/linux/drivers/media/video/omap24xxcam.h b/linux/drivers/media/video/omap24xxcam.h new file mode 100644 index 000000000..2ce67f5a4 --- /dev/null +++ b/linux/drivers/media/video/omap24xxcam.h @@ -0,0 +1,593 @@ +/* + * drivers/media/video/omap24xxcam.h + * + * Copyright (C) 2004 MontaVista Software, Inc. + * Copyright (C) 2004 Texas Instruments. + * Copyright (C) 2007 Nokia Corporation. + * + * Contact: Sakari Ailus + * + * Based on code from Andy Lowe . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef OMAP24XXCAM_H +#define OMAP24XXCAM_H + +#include +#include + +/* + * + * General driver related definitions. + * + */ + +#define CAM_NAME "omap24xxcam" + +#define CAM_MCLK 96000000 + +/* number of bytes transferred per DMA request */ +#define DMA_THRESHOLD 32 + +/* + * NUM_CAMDMA_CHANNELS is the number of logical channels provided by + * the camera DMA controller. + */ +#define NUM_CAMDMA_CHANNELS 4 + +/* + * NUM_SG_DMA is the number of scatter-gather DMA transfers that can + * be queued. (We don't have any overlay sglists now.) + */ +#define NUM_SG_DMA (VIDEO_MAX_FRAME) + +/* + * + * Register definitions. + * + */ + +/* subsystem register block offsets */ +#define CC_REG_OFFSET 0x00000400 +#define CAMDMA_REG_OFFSET 0x00000800 +#define CAMMMU_REG_OFFSET 0x00000C00 + +/* define camera subsystem register offsets */ +#define CAM_REVISION 0x000 +#define CAM_SYSCONFIG 0x010 +#define CAM_SYSSTATUS 0x014 +#define CAM_IRQSTATUS 0x018 +#define CAM_GPO 0x040 +#define CAM_GPI 0x050 + +/* define camera core register offsets */ +#define CC_REVISION 0x000 +#define CC_SYSCONFIG 0x010 +#define CC_SYSSTATUS 0x014 +#define CC_IRQSTATUS 0x018 +#define CC_IRQENABLE 0x01C +#define CC_CTRL 0x040 +#define CC_CTRL_DMA 0x044 +#define CC_CTRL_XCLK 0x048 +#define CC_FIFODATA 0x04C +#define CC_TEST 0x050 +#define CC_GENPAR 0x054 +#define CC_CCPFSCR 0x058 +#define CC_CCPFECR 0x05C +#define CC_CCPLSCR 0x060 +#define CC_CCPLECR 0x064 +#define CC_CCPDFR 0x068 + +/* define camera dma register offsets */ +#define CAMDMA_REVISION 0x000 +#define CAMDMA_IRQSTATUS_L0 0x008 +#define CAMDMA_IRQSTATUS_L1 0x00C +#define CAMDMA_IRQSTATUS_L2 0x010 +#define CAMDMA_IRQSTATUS_L3 0x014 +#define CAMDMA_IRQENABLE_L0 0x018 +#define CAMDMA_IRQENABLE_L1 0x01C +#define CAMDMA_IRQENABLE_L2 0x020 +#define CAMDMA_IRQENABLE_L3 0x024 +#define CAMDMA_SYSSTATUS 0x028 +#define CAMDMA_OCP_SYSCONFIG 0x02C +#define CAMDMA_CAPS_0 0x064 +#define CAMDMA_CAPS_2 0x06C +#define CAMDMA_CAPS_3 0x070 +#define CAMDMA_CAPS_4 0x074 +#define CAMDMA_GCR 0x078 +#define CAMDMA_CCR(n) (0x080 + (n)*0x60) +#define CAMDMA_CLNK_CTRL(n) (0x084 + (n)*0x60) +#define CAMDMA_CICR(n) (0x088 + (n)*0x60) +#define CAMDMA_CSR(n) (0x08C + (n)*0x60) +#define CAMDMA_CSDP(n) (0x090 + (n)*0x60) +#define CAMDMA_CEN(n) (0x094 + (n)*0x60) +#define CAMDMA_CFN(n) (0x098 + (n)*0x60) +#define CAMDMA_CSSA(n) (0x09C + (n)*0x60) +#define CAMDMA_CDSA(n) (0x0A0 + (n)*0x60) +#define CAMDMA_CSEI(n) (0x0A4 + (n)*0x60) +#define CAMDMA_CSFI(n) (0x0A8 + (n)*0x60) +#define CAMDMA_CDEI(n) (0x0AC + (n)*0x60) +#define CAMDMA_CDFI(n) (0x0B0 + (n)*0x60) +#define CAMDMA_CSAC(n) (0x0B4 + (n)*0x60) +#define CAMDMA_CDAC(n) (0x0B8 + (n)*0x60) +#define CAMDMA_CCEN(n) (0x0BC + (n)*0x60) +#define CAMDMA_CCFN(n) (0x0C0 + (n)*0x60) +#define CAMDMA_COLOR(n) (0x0C4 + (n)*0x60) + +/* define camera mmu register offsets */ +#define CAMMMU_REVISION 0x000 +#define CAMMMU_SYSCONFIG 0x010 +#define CAMMMU_SYSSTATUS 0x014 +#define CAMMMU_IRQSTATUS 0x018 +#define CAMMMU_IRQENABLE 0x01C +#define CAMMMU_WALKING_ST 0x040 +#define CAMMMU_CNTL 0x044 +#define CAMMMU_FAULT_AD 0x048 +#define CAMMMU_TTB 0x04C +#define CAMMMU_LOCK 0x050 +#define CAMMMU_LD_TLB 0x054 +#define CAMMMU_CAM 0x058 +#define CAMMMU_RAM 0x05C +#define CAMMMU_GFLUSH 0x060 +#define CAMMMU_FLUSH_ENTRY 0x064 +#define CAMMMU_READ_CAM 0x068 +#define CAMMMU_READ_RAM 0x06C +#define CAMMMU_EMU_FAULT_AD 0x070 + +/* Define bit fields within selected registers */ +#define CAM_REVISION_MAJOR (15 << 4) +#define CAM_REVISION_MAJOR_SHIFT 4 +#define CAM_REVISION_MINOR (15 << 0) +#define CAM_REVISION_MINOR_SHIFT 0 + +#define CAM_SYSCONFIG_SOFTRESET (1 << 1) +#define CAM_SYSCONFIG_AUTOIDLE (1 << 0) + +#define CAM_SYSSTATUS_RESETDONE (1 << 0) + +#define CAM_IRQSTATUS_CC_IRQ (1 << 4) +#define CAM_IRQSTATUS_MMU_IRQ (1 << 3) +#define CAM_IRQSTATUS_DMA_IRQ2 (1 << 2) +#define CAM_IRQSTATUS_DMA_IRQ1 (1 << 1) +#define CAM_IRQSTATUS_DMA_IRQ0 (1 << 0) + +#define CAM_GPO_CAM_S_P_EN (1 << 1) +#define CAM_GPO_CAM_CCP_MODE (1 << 0) + +#define CAM_GPI_CC_DMA_REQ1 (1 << 24) +#define CAP_GPI_CC_DMA_REQ0 (1 << 23) +#define CAP_GPI_CAM_MSTANDBY (1 << 21) +#define CAP_GPI_CAM_WAIT (1 << 20) +#define CAP_GPI_CAM_S_DATA (1 << 17) +#define CAP_GPI_CAM_S_CLK (1 << 16) +#define CAP_GPI_CAM_P_DATA (0xFFF << 3) +#define CAP_GPI_CAM_P_DATA_SHIFT 3 +#define CAP_GPI_CAM_P_VS (1 << 2) +#define CAP_GPI_CAM_P_HS (1 << 1) +#define CAP_GPI_CAM_P_CLK (1 << 0) + +#define CC_REVISION_MAJOR (15 << 4) +#define CC_REVISION_MAJOR_SHIFT 4 +#define CC_REVISION_MINOR (15 << 0) +#define CC_REVISION_MINOR_SHIFT 0 + +#define CC_SYSCONFIG_SIDLEMODE (3 << 3) +#define CC_SYSCONFIG_SIDLEMODE_FIDLE (0 << 3) +#define CC_SYSCONFIG_SIDLEMODE_NIDLE (1 << 3) +#define CC_SYSCONFIG_SOFTRESET (1 << 1) +#define CC_SYSCONFIG_AUTOIDLE (1 << 0) + +#define CC_SYSSTATUS_RESETDONE (1 << 0) + +#define CC_IRQSTATUS_FS_IRQ (1 << 19) +#define CC_IRQSTATUS_LE_IRQ (1 << 18) +#define CC_IRQSTATUS_LS_IRQ (1 << 17) +#define CC_IRQSTATUS_FE_IRQ (1 << 16) +#define CC_IRQSTATUS_FW_ERR_IRQ (1 << 10) +#define CC_IRQSTATUS_FSC_ERR_IRQ (1 << 9) +#define CC_IRQSTATUS_SSC_ERR_IRQ (1 << 8) +#define CC_IRQSTATUS_FIFO_NOEMPTY_IRQ (1 << 4) +#define CC_IRQSTATUS_FIFO_FULL_IRQ (1 << 3) +#define CC_IRQSTATUS_FIFO_THR_IRQ (1 << 2) +#define CC_IRQSTATUS_FIFO_OF_IRQ (1 << 1) +#define CC_IRQSTATUS_FIFO_UF_IRQ (1 << 0) + +#define CC_IRQENABLE_FS_IRQ (1 << 19) +#define CC_IRQENABLE_LE_IRQ (1 << 18) +#define CC_IRQENABLE_LS_IRQ (1 << 17) +#define CC_IRQENABLE_FE_IRQ (1 << 16) +#define CC_IRQENABLE_FW_ERR_IRQ (1 << 10) +#define CC_IRQENABLE_FSC_ERR_IRQ (1 << 9) +#define CC_IRQENABLE_SSC_ERR_IRQ (1 << 8) +#define CC_IRQENABLE_FIFO_NOEMPTY_IRQ (1 << 4) +#define CC_IRQENABLE_FIFO_FULL_IRQ (1 << 3) +#define CC_IRQENABLE_FIFO_THR_IRQ (1 << 2) +#define CC_IRQENABLE_FIFO_OF_IRQ (1 << 1) +#define CC_IRQENABLE_FIFO_UF_IRQ (1 << 0) + +#define CC_CTRL_CC_ONE_SHOT (1 << 20) +#define CC_CTRL_CC_IF_SYNCHRO (1 << 19) +#define CC_CTRL_CC_RST (1 << 18) +#define CC_CTRL_CC_FRAME_TRIG (1 << 17) +#define CC_CTRL_CC_EN (1 << 16) +#define CC_CTRL_NOBT_SYNCHRO (1 << 13) +#define CC_CTRL_BT_CORRECT (1 << 12) +#define CC_CTRL_PAR_ORDERCAM (1 << 11) +#define CC_CTRL_PAR_CLK_POL (1 << 10) +#define CC_CTRL_NOBT_HS_POL (1 << 9) +#define CC_CTRL_NOBT_VS_POL (1 << 8) +#define CC_CTRL_PAR_MODE (7 << 1) +#define CC_CTRL_PAR_MODE_SHIFT 1 +#define CC_CTRL_PAR_MODE_NOBT8 (0 << 1) +#define CC_CTRL_PAR_MODE_NOBT10 (1 << 1) +#define CC_CTRL_PAR_MODE_NOBT12 (2 << 1) +#define CC_CTRL_PAR_MODE_BT8 (4 << 1) +#define CC_CTRL_PAR_MODE_BT10 (5 << 1) +#define CC_CTRL_PAR_MODE_FIFOTEST (7 << 1) +#define CC_CTRL_CCP_MODE (1 << 0) + +#define CC_CTRL_DMA_EN (1 << 8) +#define CC_CTRL_DMA_FIFO_THRESHOLD (0x7F << 0) +#define CC_CTRL_DMA_FIFO_THRESHOLD_SHIFT 0 + +#define CC_CTRL_XCLK_DIV (0x1F << 0) +#define CC_CTRL_XCLK_DIV_SHIFT 0 +#define CC_CTRL_XCLK_DIV_STABLE_LOW (0 << 0) +#define CC_CTRL_XCLK_DIV_STABLE_HIGH (1 << 0) +#define CC_CTRL_XCLK_DIV_BYPASS (31 << 0) + +#define CC_TEST_FIFO_RD_POINTER (0xFF << 24) +#define CC_TEST_FIFO_RD_POINTER_SHIFT 24 +#define CC_TEST_FIFO_WR_POINTER (0xFF << 16) +#define CC_TEST_FIFO_WR_POINTER_SHIFT 16 +#define CC_TEST_FIFO_LEVEL (0xFF << 8) +#define CC_TEST_FIFO_LEVEL_SHIFT 8 +#define CC_TEST_FIFO_LEVEL_PEAK (0xFF << 0) +#define CC_TEST_FIFO_LEVEL_PEAK_SHIFT 0 + +#define CC_GENPAR_FIFO_DEPTH (7 << 0) +#define CC_GENPAR_FIFO_DEPTH_SHIFT 0 + +#define CC_CCPDFR_ALPHA (0xFF << 8) +#define CC_CCPDFR_ALPHA_SHIFT 8 +#define CC_CCPDFR_DATAFORMAT (15 << 0) +#define CC_CCPDFR_DATAFORMAT_SHIFT 0 +#define CC_CCPDFR_DATAFORMAT_YUV422BE (0 << 0) +#define CC_CCPDFR_DATAFORMAT_YUV422 (1 << 0) +#define CC_CCPDFR_DATAFORMAT_YUV420 (2 << 0) +#define CC_CCPDFR_DATAFORMAT_RGB444 (4 << 0) +#define CC_CCPDFR_DATAFORMAT_RGB565 (5 << 0) +#define CC_CCPDFR_DATAFORMAT_RGB888NDE (6 << 0) +#define CC_CCPDFR_DATAFORMAT_RGB888 (7 << 0) +#define CC_CCPDFR_DATAFORMAT_RAW8NDE (8 << 0) +#define CC_CCPDFR_DATAFORMAT_RAW8 (9 << 0) +#define CC_CCPDFR_DATAFORMAT_RAW10NDE (10 << 0) +#define CC_CCPDFR_DATAFORMAT_RAW10 (11 << 0) +#define CC_CCPDFR_DATAFORMAT_RAW12NDE (12 << 0) +#define CC_CCPDFR_DATAFORMAT_RAW12 (13 << 0) +#define CC_CCPDFR_DATAFORMAT_JPEG8 (15 << 0) + +#define CAMDMA_REVISION_MAJOR (15 << 4) +#define CAMDMA_REVISION_MAJOR_SHIFT 4 +#define CAMDMA_REVISION_MINOR (15 << 0) +#define CAMDMA_REVISION_MINOR_SHIFT 0 + +#define CAMDMA_OCP_SYSCONFIG_MIDLEMODE (3 << 12) +#define CAMDMA_OCP_SYSCONFIG_MIDLEMODE_FSTANDBY (0 << 12) +#define CAMDMA_OCP_SYSCONFIG_MIDLEMODE_NSTANDBY (1 << 12) +#define CAMDMA_OCP_SYSCONFIG_MIDLEMODE_SSTANDBY (2 << 12) +#define CAMDMA_OCP_SYSCONFIG_FUNC_CLOCK (1 << 9) +#define CAMDMA_OCP_SYSCONFIG_OCP_CLOCK (1 << 8) +#define CAMDMA_OCP_SYSCONFIG_EMUFREE (1 << 5) +#define CAMDMA_OCP_SYSCONFIG_SIDLEMODE (3 << 3) +#define CAMDMA_OCP_SYSCONFIG_SIDLEMODE_FIDLE (0 << 3) +#define CAMDMA_OCP_SYSCONFIG_SIDLEMODE_NIDLE (1 << 3) +#define CAMDMA_OCP_SYSCONFIG_SIDLEMODE_SIDLE (2 << 3) +#define CAMDMA_OCP_SYSCONFIG_SOFTRESET (1 << 1) +#define CAMDMA_OCP_SYSCONFIG_AUTOIDLE (1 << 0) + +#define CAMDMA_SYSSTATUS_RESETDONE (1 << 0) + +#define CAMDMA_GCR_ARBITRATION_RATE (0xFF << 16) +#define CAMDMA_GCR_ARBITRATION_RATE_SHIFT 16 +#define CAMDMA_GCR_MAX_CHANNEL_FIFO_DEPTH (0xFF << 0) +#define CAMDMA_GCR_MAX_CHANNEL_FIFO_DEPTH_SHIFT 0 + +#define CAMDMA_CCR_SEL_SRC_DST_SYNC (1 << 24) +#define CAMDMA_CCR_PREFETCH (1 << 23) +#define CAMDMA_CCR_SUPERVISOR (1 << 22) +#define CAMDMA_CCR_SECURE (1 << 21) +#define CAMDMA_CCR_BS (1 << 18) +#define CAMDMA_CCR_TRANSPARENT_COPY_ENABLE (1 << 17) +#define CAMDMA_CCR_CONSTANT_FILL_ENABLE (1 << 16) +#define CAMDMA_CCR_DST_AMODE (3 << 14) +#define CAMDMA_CCR_DST_AMODE_CONST_ADDR (0 << 14) +#define CAMDMA_CCR_DST_AMODE_POST_INC (1 << 14) +#define CAMDMA_CCR_DST_AMODE_SGL_IDX (2 << 14) +#define CAMDMA_CCR_DST_AMODE_DBL_IDX (3 << 14) +#define CAMDMA_CCR_SRC_AMODE (3 << 12) +#define CAMDMA_CCR_SRC_AMODE_CONST_ADDR (0 << 12) +#define CAMDMA_CCR_SRC_AMODE_POST_INC (1 << 12) +#define CAMDMA_CCR_SRC_AMODE_SGL_IDX (2 << 12) +#define CAMDMA_CCR_SRC_AMODE_DBL_IDX (3 << 12) +#define CAMDMA_CCR_WR_ACTIVE (1 << 10) +#define CAMDMA_CCR_RD_ACTIVE (1 << 9) +#define CAMDMA_CCR_SUSPEND_SENSITIVE (1 << 8) +#define CAMDMA_CCR_ENABLE (1 << 7) +#define CAMDMA_CCR_PRIO (1 << 6) +#define CAMDMA_CCR_FS (1 << 5) +#define CAMDMA_CCR_SYNCHRO ((3 << 19) | (31 << 0)) +#define CAMDMA_CCR_SYNCHRO_CAMERA 0x01 + +#define CAMDMA_CLNK_CTRL_ENABLE_LNK (1 << 15) +#define CAMDMA_CLNK_CTRL_NEXTLCH_ID (0x1F << 0) +#define CAMDMA_CLNK_CTRL_NEXTLCH_ID_SHIFT 0 + +#define CAMDMA_CICR_MISALIGNED_ERR_IE (1 << 11) +#define CAMDMA_CICR_SUPERVISOR_ERR_IE (1 << 10) +#define CAMDMA_CICR_SECURE_ERR_IE (1 << 9) +#define CAMDMA_CICR_TRANS_ERR_IE (1 << 8) +#define CAMDMA_CICR_PACKET_IE (1 << 7) +#define CAMDMA_CICR_BLOCK_IE (1 << 5) +#define CAMDMA_CICR_LAST_IE (1 << 4) +#define CAMDMA_CICR_FRAME_IE (1 << 3) +#define CAMDMA_CICR_HALF_IE (1 << 2) +#define CAMDMA_CICR_DROP_IE (1 << 1) + +#define CAMDMA_CSR_MISALIGNED_ERR (1 << 11) +#define CAMDMA_CSR_SUPERVISOR_ERR (1 << 10) +#define CAMDMA_CSR_SECURE_ERR (1 << 9) +#define CAMDMA_CSR_TRANS_ERR (1 << 8) +#define CAMDMA_CSR_PACKET (1 << 7) +#define CAMDMA_CSR_SYNC (1 << 6) +#define CAMDMA_CSR_BLOCK (1 << 5) +#define CAMDMA_CSR_LAST (1 << 4) +#define CAMDMA_CSR_FRAME (1 << 3) +#define CAMDMA_CSR_HALF (1 << 2) +#define CAMDMA_CSR_DROP (1 << 1) + +#define CAMDMA_CSDP_SRC_ENDIANNESS (1 << 21) +#define CAMDMA_CSDP_SRC_ENDIANNESS_LOCK (1 << 20) +#define CAMDMA_CSDP_DST_ENDIANNESS (1 << 19) +#define CAMDMA_CSDP_DST_ENDIANNESS_LOCK (1 << 18) +#define CAMDMA_CSDP_WRITE_MODE (3 << 16) +#define CAMDMA_CSDP_WRITE_MODE_WRNP (0 << 16) +#define CAMDMA_CSDP_WRITE_MODE_POSTED (1 << 16) +#define CAMDMA_CSDP_WRITE_MODE_POSTED_LAST_WRNP (2 << 16) +#define CAMDMA_CSDP_DST_BURST_EN (3 << 14) +#define CAMDMA_CSDP_DST_BURST_EN_1 (0 << 14) +#define CAMDMA_CSDP_DST_BURST_EN_16 (1 << 14) +#define CAMDMA_CSDP_DST_BURST_EN_32 (2 << 14) +#define CAMDMA_CSDP_DST_BURST_EN_64 (3 << 14) +#define CAMDMA_CSDP_DST_PACKED (1 << 13) +#define CAMDMA_CSDP_WR_ADD_TRSLT (15 << 9) +#define CAMDMA_CSDP_WR_ADD_TRSLT_ENABLE_MREQADD (3 << 9) +#define CAMDMA_CSDP_SRC_BURST_EN (3 << 7) +#define CAMDMA_CSDP_SRC_BURST_EN_1 (0 << 7) +#define CAMDMA_CSDP_SRC_BURST_EN_16 (1 << 7) +#define CAMDMA_CSDP_SRC_BURST_EN_32 (2 << 7) +#define CAMDMA_CSDP_SRC_BURST_EN_64 (3 << 7) +#define CAMDMA_CSDP_SRC_PACKED (1 << 6) +#define CAMDMA_CSDP_RD_ADD_TRSLT (15 << 2) +#define CAMDMA_CSDP_RD_ADD_TRSLT_ENABLE_MREQADD (3 << 2) +#define CAMDMA_CSDP_DATA_TYPE (3 << 0) +#define CAMDMA_CSDP_DATA_TYPE_8BITS (0 << 0) +#define CAMDMA_CSDP_DATA_TYPE_16BITS (1 << 0) +#define CAMDMA_CSDP_DATA_TYPE_32BITS (2 << 0) + +#define CAMMMU_SYSCONFIG_AUTOIDLE (1 << 0) + +/* + * + * Declarations. + * + */ + +/* forward declarations */ +struct omap24xxcam_sgdma; +struct omap24xxcam_dma; + +typedef void (*sgdma_callback_t)(struct omap24xxcam_sgdma *cam, + u32 status, void *arg); +typedef void (*dma_callback_t)(struct omap24xxcam_dma *cam, + u32 status, void *arg); + +struct channel_state { + dma_callback_t callback; + void *arg; +}; + +/* sgdma state for each of the possible videobuf_buffers + 2 overlays */ +struct sgdma_state { + const struct scatterlist *sglist; + int sglen; /* number of sglist entries */ + int next_sglist; /* index of next sglist entry to process */ + unsigned int bytes_read; /* number of bytes read */ + unsigned int len; /* total length of sglist (excluding + * bytes due to page alignment) */ + int queued_sglist; /* number of sglist entries queued for DMA */ + u32 csr; /* DMA return code */ + sgdma_callback_t callback; + void *arg; +}; + +/* physical DMA channel management */ +struct omap24xxcam_dma { + spinlock_t lock; /* Lock for the whole structure. */ + + unsigned long base; /* base address for dma controller */ + + /* While dma_stop!=0, an attempt to start a new DMA transfer will + * fail. + */ + atomic_t dma_stop; + int free_dmach; /* number of dma channels free */ + int next_dmach; /* index of next dma channel to use */ + struct channel_state ch_state[NUM_CAMDMA_CHANNELS]; +}; + +/* scatter-gather DMA (scatterlist stuff) management */ +struct omap24xxcam_sgdma { + struct omap24xxcam_dma dma; + + spinlock_t lock; /* Lock for the fields below. */ + int free_sgdma; /* number of free sg dma slots */ + int next_sgdma; /* index of next sg dma slot to use */ + struct sgdma_state sg_state[NUM_SG_DMA]; + + /* Reset timer data */ + struct timer_list reset_timer; +}; + +/* per-device data structure */ +struct omap24xxcam_device { + /*** mutex ***/ + /* + * mutex serialises access to this structure. Also camera + * opening and releasing is synchronised by this. + */ + struct mutex mutex; + + /*** general driver state information ***/ + atomic_t users; + /* + * Lock to serialise core enabling and disabling and access to + * sgdma_in_queue. + */ + spinlock_t core_enable_disable_lock; + /* + * Number or sgdma requests in scatter-gather queue, protected + * by the lock above. + */ + int sgdma_in_queue; + /* + * Sensor interface parameters: interface type, CC_CTRL + * register value and interface specific data. + */ + int if_type; + union { + struct parallel { + u32 xclk; + } bt656; + } if_u; + u32 cc_ctrl; + + /*** subsystem structures ***/ + struct omap24xxcam_sgdma sgdma; + + /*** hardware resources ***/ + unsigned int irq; + unsigned long mmio_base; + unsigned long mmio_base_phys; + unsigned long mmio_size; + + /*** interfaces and device ***/ + struct v4l2_int_device *sdev; + struct device *dev; + struct video_device *vfd; + + /*** camera and sensor reset related stuff ***/ + struct work_struct sensor_reset_work; + /* + * We're in the middle of a reset. Don't enable core if this + * is non-zero! This exists to help decisionmaking in a case + * where videobuf_qbuf is called while we are in the middle of + * a reset. + */ + atomic_t in_reset; + /* + * Non-zero if we don't want any resets for now. Used to + * prevent reset work to run when we're about to stop + * streaming. + */ + atomic_t reset_disable; + + /*** video device parameters ***/ + int capture_mem; + + /*** camera module clocks ***/ + struct clk *fck; + struct clk *ick; + + /*** capture data ***/ + /* file handle, if streaming is on */ + struct file *streaming; +}; + +/* Per-file handle data. */ +struct omap24xxcam_fh { + spinlock_t vbq_lock; /* spinlock for the videobuf queue */ + struct videobuf_queue vbq; + struct v4l2_pix_format pix; /* serialise pix by vbq->lock */ + atomic_t field_count; /* field counter for videobuf_buffer */ + /* accessing cam here doesn't need serialisation: it's constant */ + struct omap24xxcam_device *cam; +}; + +/* + * + * Register I/O functions. + * + */ + +static inline u32 omap24xxcam_reg_in(unsigned long base, u32 offset) +{ + return readl(base + offset); +} + +static inline u32 omap24xxcam_reg_out(unsigned long base, u32 offset, + u32 val) +{ + writel(val, base + offset); + return val; +} + +static inline u32 omap24xxcam_reg_merge(unsigned long base, u32 offset, + u32 val, u32 mask) +{ + u32 addr = base + offset; + u32 new_val = (readl(addr) & ~mask) | (val & mask); + + writel(new_val, addr); + return new_val; +} + +/* + * + * Function prototypes. + * + */ + +/* dma prototypes */ + +void omap24xxcam_dma_hwinit(struct omap24xxcam_dma *dma); +void omap24xxcam_dma_isr(struct omap24xxcam_dma *dma); + +/* sgdma prototypes */ + +void omap24xxcam_sgdma_process(struct omap24xxcam_sgdma *sgdma); +int omap24xxcam_sgdma_queue(struct omap24xxcam_sgdma *sgdma, + const struct scatterlist *sglist, int sglen, + int len, sgdma_callback_t callback, void *arg); +void omap24xxcam_sgdma_sync(struct omap24xxcam_sgdma *sgdma); +void omap24xxcam_sgdma_init(struct omap24xxcam_sgdma *sgdma, + unsigned long base, + void (*reset_callback)(unsigned long data), + unsigned long reset_callback_data); +void omap24xxcam_sgdma_exit(struct omap24xxcam_sgdma *sgdma); + +#endif -- cgit v1.2.3 From dc1b57bb7e3d558107e4706599fa76dcb3165977 Mon Sep 17 00:00:00 2001 From: Douglas Schilling Landgraf Date: Mon, 1 Dec 2008 22:01:04 -0200 Subject: em28xx: Add specific entry for WinTV-HVR 850 From: Douglas Schilling Landgraf Added specific entry for WinTV-HVR 850 Priority: normal Signed-off-by: Douglas Schilling Landgraf --- linux/Documentation/video4linux/CARDLIST.em28xx | 3 ++- linux/drivers/media/video/em28xx/em28xx-cards.c | 32 +++++++++++++++++++++++-- linux/drivers/media/video/em28xx/em28xx-dvb.c | 1 + linux/drivers/media/video/em28xx/em28xx.h | 1 + 4 files changed, 34 insertions(+), 3 deletions(-) (limited to 'linux') diff --git a/linux/Documentation/video4linux/CARDLIST.em28xx b/linux/Documentation/video4linux/CARDLIST.em28xx index ea537f5ae..a6734eb7b 100644 --- a/linux/Documentation/video4linux/CARDLIST.em28xx +++ b/linux/Documentation/video4linux/CARDLIST.em28xx @@ -14,7 +14,7 @@ 13 -> Terratec Prodigy XS (em2880) [0ccd:0047] 14 -> Pixelview Prolink PlayTV USB 2.0 (em2820/em2840) [eb1a:2821] 15 -> V-Gear PocketTV (em2800) - 16 -> Hauppauge WinTV HVR 950 (em2883) [2040:6513,2040:6517,2040:651b,2040:651f] + 16 -> Hauppauge WinTV HVR 950 (em2883) [2040:6513,2040:6517,2040:651b] 17 -> Pinnacle PCTV HD Pro Stick (em2880) [2304:0227] 18 -> Hauppauge WinTV HVR 900 (R2) (em2880) [2040:6502] 19 -> PointNix Intra-Oral Camera (em2860) @@ -58,3 +58,4 @@ 57 -> Kworld PlusTV HD Hybrid 330 (em2883) [eb1a:a316] 58 -> Compro VideoMate ForYou/Stereo (em2820/em2840) [185b:2041] 59 -> Pinnacle PCTV HD Mini (em2874) [2304:023f] + 60 -> Hauppauge WinTV HVR 850 (em2883) [2040:651f] diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index d38b3ba72..2ecc98af7 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -687,6 +687,32 @@ struct em28xx_board em28xx_boards[] = { .gpio = hauppauge_wintv_hvr_900_analog, } }, }, + [EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850] = { + .name = "Hauppauge WinTV HVR 850", + .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, + .mts_firmware = 1, + .has_dvb = 1, + .dvb_gpio = hauppauge_wintv_hvr_900_digital, + .ir_codes = ir_codes_hauppauge_new, + .decoder = EM28XX_TVP5150, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = EM28XX_AMUX_VIDEO, + .gpio = hauppauge_wintv_hvr_900_analog, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = hauppauge_wintv_hvr_900_analog, + } }, + }, [EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950] = { .name = "Hauppauge WinTV HVR 950", .tuner_type = TUNER_XC2028, @@ -1354,8 +1380,8 @@ struct usb_device_id em28xx_id_table [] = { .driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950 }, { USB_DEVICE(0x2040, 0x651b), /* RP HVR-950 */ .driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950 }, - { USB_DEVICE(0x2040, 0x651f), /* HCW HVR-850 */ - .driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950 }, + { USB_DEVICE(0x2040, 0x651f), + .driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850 }, { USB_DEVICE(0x0438, 0xb002), .driver_info = EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600 }, { USB_DEVICE(0x2001, 0xf112), @@ -1575,6 +1601,7 @@ static void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl) ctl->demod = XC3028_FE_DEFAULT; ctl->fname = XC3028L_DEFAULT_FIRMWARE; break; + case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850: case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950: case EM2880_BOARD_PINNACLE_PCTV_HD_PRO: /* FIXME: Better to specify the needed IF */ @@ -1759,6 +1786,7 @@ void em28xx_card_setup(struct em28xx *dev) case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2: case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2: + case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850: case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950: { struct tveeprom tv; diff --git a/linux/drivers/media/video/em28xx/em28xx-dvb.c b/linux/drivers/media/video/em28xx/em28xx-dvb.c index a06659f28..ddc4f6a99 100644 --- a/linux/drivers/media/video/em28xx/em28xx-dvb.c +++ b/linux/drivers/media/video/em28xx/em28xx-dvb.c @@ -410,6 +410,7 @@ static int dvb_init(struct em28xx *dev) em28xx_set_mode(dev, EM28XX_DIGITAL_MODE); /* init frontend */ switch (dev->model) { + case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850: case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950: case EM2880_BOARD_PINNACLE_PCTV_HD_PRO: case EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600: diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index 667cb70cf..4de9a8190 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -99,6 +99,7 @@ #define EM2883_BOARD_KWORLD_HYBRID_A316 57 #define EM2820_BOARD_COMPRO_VIDEOMATE_FORYOU 58 #define EM2874_BOARD_PINNACLE_PCTV_80E 59 +#define EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850 60 /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 -- cgit v1.2.3 From ce9193f864e672d15847eac5e1c86e9c4094904d Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Tue, 2 Dec 2008 10:56:47 +0100 Subject: gspca: Add the webcam 0c45:613a in the gspca documentation. From: Jean-Francois Moine Priority: normal Signed-off-by: Jean-Francois Moine --- linux/Documentation/video4linux/gspca.txt | 1 + 1 file changed, 1 insertion(+) (limited to 'linux') diff --git a/linux/Documentation/video4linux/gspca.txt b/linux/Documentation/video4linux/gspca.txt index 8d0210b24..acf6c9f8e 100644 --- a/linux/Documentation/video4linux/gspca.txt +++ b/linux/Documentation/video4linux/gspca.txt @@ -257,6 +257,7 @@ sonixj 0c45:612a Avant Camera sonixj 0c45:612c Typhoon Rasy Cam 1.3MPix sonixj 0c45:6130 Sonix Pccam sonixj 0c45:6138 Sn9c120 Mo4000 +sonixj 0c45:613a Microdia Sonix PC Camera sonixj 0c45:613b Surfer SN-206 sonixj 0c45:613c Sonix Pccam168 sonixj 0c45:6143 Sonix Pccam168 -- cgit v1.2.3 From 18a1649ef4ce188d4f62e37be85055bdfa5d6c97 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Tue, 2 Dec 2008 10:58:57 +0100 Subject: gspca: Bad color control in sonixj. From: Jean-Francois Moine Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/sonixj.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/sonixj.c b/linux/drivers/media/video/gspca/sonixj.c index fb8b42daf..a0ff265cd 100644 --- a/linux/drivers/media/video/gspca/sonixj.c +++ b/linux/drivers/media/video/gspca/sonixj.c @@ -1220,17 +1220,17 @@ static void setcolors(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; int i, v; - __u8 rega0[12]; /* U & V gains */ + __u8 reg8a[12]; /* U & V gains */ static __s16 uv[6] = { /* same as reg84 in signed decimal */ -24, -38, 64, /* UR UG UB */ 62, -51, -9 /* VR VG VB */ }; for (i = 0; i < 6; i++) { v = uv[i] * sd->colors / COLOR_DEF; - rega0[i * 2] = v; - rega0[i * 2 + 1] = (v >> 8) & 0x0f; + reg8a[i * 2] = v; + reg8a[i * 2 + 1] = (v >> 8) & 0x0f; } - reg_w(gspca_dev, 0x84, rega0, sizeof rega0); + reg_w(gspca_dev, 0x8a, reg8a, sizeof reg8a); } static void setredblue(struct gspca_dev *gspca_dev) -- cgit v1.2.3 From b999679a2733164c8241db55370190c699fd55fd Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Tue, 2 Dec 2008 19:00:57 +0100 Subject: gspca: Fix image problem at low resolutions with ov7660 in sonixj. From: Jean-Francois Moine Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/sonixj.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/sonixj.c b/linux/drivers/media/video/gspca/sonixj.c index a0ff265cd..368382cf7 100644 --- a/linux/drivers/media/video/gspca/sonixj.c +++ b/linux/drivers/media/video/gspca/sonixj.c @@ -1394,19 +1394,15 @@ static int sd_start(struct gspca_dev *gspca_dev) default: /* case SENSOR_OV7660: */ ov7660_InitSensor(gspca_dev); - if (mode) { -/* reg17 = 0x21; * 320 */ -/* reg1 = 0x44; */ -/* reg1 = 0x46; (done) */ - } else { /* 640 */ - if (sd->bridge == BRIDGE_SN9C120) { + if (sd->bridge == BRIDGE_SN9C120) { + if (mode) { /* 320x240 - 160x120 */ reg17 = 0xa2; reg1 = 0x44; /* 48 Mhz, video trf eneble */ - } else { - reg17 = 0x22; - reg1 = 0x06; /* 24 Mhz, video trf eneble - * inverse power down */ } + } else { + reg17 = 0x22; + reg1 = 0x06; /* 24 Mhz, video trf eneble + * inverse power down */ } break; } -- cgit v1.2.3 From 5be2df264845a545c345c924c23153459140a308 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 2 Dec 2008 22:34:52 +0000 Subject: drivers/media/video/cx88/cx88-alsa.c: Adjust error-handling code From: Julia Lawall In the function cx88_audio_initdev, the value card has been created using snd_card_new. The other error handling code in this function frees the value using snd_card_free. I have thus changed the first error case to do the same. On the other hand, it may be that card is not sufficiently initialized at this point to use snd_card_free, in which case something else should be done to free the memory in the error case. In the function snd_cx88_create the call kfree(chip) in one error case looks suspicious, both because it is not done in the other error code, and because chip points into the middle of the memory allocated by snd_card_new, ie it is not itself associated with a separate kmalloc. Therefore I have removed it. The semantic match that finds the first problem is as follows: (http://www.emn.fr/x-info/coccinelle/) // @r exists@ local idexpression x; statement S,S1; position p1,p2,p3; expression E,E1; type T,T1; expression *ptr != NULL; @@ ( if ((x@p1 = snd_card_new(...)) == NULL) S | x@p1 = snd_card_new(...); ) ... when != snd_card_free(...,(T)x,...) when != if (...) { <+... snd_card_free(...,(T)x,...) ...+> } when != true x == NULL || ... when != x = E when != E = (T)x when any ( if (x == NULL || ...) S1 | if@p2 (...) { ... when != snd_card_free(...,(T1)x,...) when != if (...) { <+... snd_card_free(...,(T1)x,...) ...+> } when != x = E1 when != E1 = (T1)x ( return \(0\|<+...x...+>\|ptr\); | return@p3 ...; ) } ) @ script:python @ p1 << r.p1; p3 << r.p3; @@ print "* file: %s snd_card_new: %s return: %s" % (p1[0].file,p1[0].line,p3[0].line) // Signed-off-by: Julia Lawall Signed-off-by: Andrew Morton Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/cx88/cx88-alsa.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/cx88/cx88-alsa.c b/linux/drivers/media/video/cx88/cx88-alsa.c index 3dd2f6629..f7171586d 100644 --- a/linux/drivers/media/video/cx88/cx88-alsa.c +++ b/linux/drivers/media/video/cx88/cx88-alsa.c @@ -795,7 +795,6 @@ static int __devinit snd_cx88_create(struct snd_card *card, core = cx88_core_get(pci); if (NULL == core) { err = -EINVAL; - kfree (chip); return err; } @@ -872,7 +871,7 @@ static int __devinit cx88_audio_initdev(struct pci_dev *pci, err = snd_cx88_create(card, pci, &chip); if (err < 0) - return (err); + goto error; err = snd_cx88_pcm(chip, 0, "CX88 Digital"); if (err < 0) -- cgit v1.2.3 From 26a19b1a88a4f99ab366bfd80734b34453b35a8b Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Wed, 3 Dec 2008 11:19:22 +0100 Subject: gspca: Webcam 093a:2622 added in pac7311. From: Jean-Francois Moine Priority: normal Signed-off-by: Jean-Francois Moine --- linux/Documentation/video4linux/gspca.txt | 1 + linux/drivers/media/video/gspca/pac7311.c | 1 + 2 files changed, 2 insertions(+) (limited to 'linux') diff --git a/linux/Documentation/video4linux/gspca.txt b/linux/Documentation/video4linux/gspca.txt index acf6c9f8e..72d31bd64 100644 --- a/linux/Documentation/video4linux/gspca.txt +++ b/linux/Documentation/video4linux/gspca.txt @@ -217,6 +217,7 @@ pac7311 093a:2608 Trust WB-3300p pac7311 093a:260e Gigaware VGA PC Camera, Trust WB-3350p, SIGMA cam 2350 pac7311 093a:260f SnakeCam pac7311 093a:2621 PAC731x +pac7311 093a:2622 Genius Eye 312 pac7311 093a:2624 PAC7302 pac7311 093a:2626 Labtec 2200 pac7311 093a:262a Webcam 300k diff --git a/linux/drivers/media/video/gspca/pac7311.c b/linux/drivers/media/video/gspca/pac7311.c index f443df77e..b0a9d0687 100644 --- a/linux/drivers/media/video/gspca/pac7311.c +++ b/linux/drivers/media/video/gspca/pac7311.c @@ -1079,6 +1079,7 @@ static __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x093a, 0x260e), .driver_info = SENSOR_PAC7311}, {USB_DEVICE(0x093a, 0x260f), .driver_info = SENSOR_PAC7311}, {USB_DEVICE(0x093a, 0x2621), .driver_info = SENSOR_PAC7302}, + {USB_DEVICE(0x093a, 0x2622), .driver_info = SENSOR_PAC7302}, {USB_DEVICE(0x093a, 0x2624), .driver_info = SENSOR_PAC7302}, {USB_DEVICE(0x093a, 0x2626), .driver_info = SENSOR_PAC7302}, {USB_DEVICE(0x093a, 0x262a), .driver_info = SENSOR_PAC7302}, -- cgit v1.2.3 From 21f9b7166963112dcef346971b1ba1df42014c79 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Wed, 3 Dec 2008 11:29:26 +0100 Subject: gspca: Add the webcam 0c45:60fe in the gspca documentation. From: Jean-Francois Moine Priority: normal Signed-off-by: Jean-Francois Moine --- linux/Documentation/video4linux/gspca.txt | 1 + 1 file changed, 1 insertion(+) (limited to 'linux') diff --git a/linux/Documentation/video4linux/gspca.txt b/linux/Documentation/video4linux/gspca.txt index 72d31bd64..a38c87246 100644 --- a/linux/Documentation/video4linux/gspca.txt +++ b/linux/Documentation/video4linux/gspca.txt @@ -253,6 +253,7 @@ sonixj 0c45:60c0 Sangha Sn535 sonixj 0c45:60ec SN9C105+MO4000 sonixj 0c45:60fb Surfer NoName sonixj 0c45:60fc LG-LIC300 +sonixj 0c45:60fe Microdia Audio sonixj 0c45:6128 Microdia/Sonix SNP325 sonixj 0c45:612a Avant Camera sonixj 0c45:612c Typhoon Rasy Cam 1.3MPix -- cgit v1.2.3 From 5b67d9e00e803d51963fb07cd32160f3314abfaa Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Wed, 3 Dec 2008 18:01:54 +0100 Subject: gspca: Simplify frame rate setting and debug in ov534. From: Antonio Ospite Priority: normal Signed-off-by: Antonio Ospite Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/ov534.c | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/ov534.c b/linux/drivers/media/video/gspca/ov534.c index a574be09b..b26faae8d 100644 --- a/linux/drivers/media/video/gspca/ov534.c +++ b/linux/drivers/media/video/gspca/ov534.c @@ -48,7 +48,6 @@ static int frame_rate; /* specific webcam descriptor */ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - __u8 frame_rate; }; /* V4L2 controls supported by the driver */ @@ -358,45 +357,38 @@ static int sd_config(struct gspca_dev *gspca_dev, /* this function is called at probe and resume time */ static int sd_init(struct gspca_dev *gspca_dev) { - struct sd *sd = (struct sd *)gspca_dev; - ov534_setup(gspca_dev->dev); + int fr; - if (frame_rate > 0) - sd->frame_rate = frame_rate; + ov534_setup(gspca_dev->dev); - PDEBUG(D_PROBE, "frame_rate = %d", sd->frame_rate); + fr = frame_rate; - switch (sd->frame_rate) { + switch (fr) { case 50: sccb_reg_write(gspca_dev->dev, 0x11, 0x01); - sccb_check_status(gspca_dev->dev); sccb_reg_write(gspca_dev->dev, 0x0d, 0x41); - sccb_check_status(gspca_dev->dev); ov534_reg_verify_write(gspca_dev->dev, 0xe5, 0x02); break; case 40: sccb_reg_write(gspca_dev->dev, 0x11, 0x02); - sccb_check_status(gspca_dev->dev); sccb_reg_write(gspca_dev->dev, 0x0d, 0xc1); - sccb_check_status(gspca_dev->dev); ov534_reg_verify_write(gspca_dev->dev, 0xe5, 0x04); break; - case 30: +/* case 30: */ default: + fr = 30; sccb_reg_write(gspca_dev->dev, 0x11, 0x04); - sccb_check_status(gspca_dev->dev); sccb_reg_write(gspca_dev->dev, 0x0d, 0x81); - sccb_check_status(gspca_dev->dev); ov534_reg_verify_write(gspca_dev->dev, 0xe5, 0x02); break; case 15: sccb_reg_write(gspca_dev->dev, 0x11, 0x03); - sccb_check_status(gspca_dev->dev); sccb_reg_write(gspca_dev->dev, 0x0d, 0x41); - sccb_check_status(gspca_dev->dev); ov534_reg_verify_write(gspca_dev->dev, 0xe5, 0x04); break; - }; + } + + PDEBUG(D_PROBE, "frame_rate: %d", fr); return 0; } -- cgit v1.2.3 From ae07805bc9b851fe075b043bd085d234a7c786fb Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Wed, 3 Dec 2008 18:10:01 +0100 Subject: gspca: Use u8 values for USB control messages in ov534. From: Antonio Ospite Priority: normal Signed-off-by: Antonio Ospite Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/ov534.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/ov534.c b/linux/drivers/media/video/gspca/ov534.c index b26faae8d..530f0b712 100644 --- a/linux/drivers/media/video/gspca/ov534.c +++ b/linux/drivers/media/video/gspca/ov534.c @@ -62,12 +62,12 @@ static struct v4l2_pix_format vga_mode[] = { .priv = 0}, }; -static void ov534_reg_write(struct usb_device *udev, u16 reg, u16 val) +static void ov534_reg_write(struct usb_device *udev, u16 reg, u8 val) { - u16 data = val; + u8 data = val; int ret; - PDEBUG(D_USBO, "reg=0x%04x, val=0%04x", reg, val); + PDEBUG(D_USBO, "reg=0x%04x, val=0%02x", reg, val); ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x1, @@ -77,9 +77,9 @@ static void ov534_reg_write(struct usb_device *udev, u16 reg, u16 val) PDEBUG(D_ERR, "write failed"); } -static u16 ov534_reg_read(struct usb_device *udev, u16 reg) +static u8 ov534_reg_read(struct usb_device *udev, u16 reg) { - u16 data; + u8 data; int ret; ret = usb_control_msg(udev, @@ -87,21 +87,21 @@ static u16 ov534_reg_read(struct usb_device *udev, u16 reg) 0x1, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0x0, reg, &data, 1, CTRL_TIMEOUT); - PDEBUG(D_USBI, "reg=0x%04x, data=0x%04x", reg, data); + PDEBUG(D_USBI, "reg=0x%04x, data=0x%02x", reg, data); if (ret < 0) PDEBUG(D_ERR, "read failed"); return data; } -static void ov534_reg_verify_write(struct usb_device *udev, u16 reg, u16 val) +static void ov534_reg_verify_write(struct usb_device *udev, u16 reg, u8 val) { - u16 data; + u8 data; ov534_reg_write(udev, reg, val); data = ov534_reg_read(udev, reg); if (data != val) { PDEBUG(D_ERR | D_USBO, - "unexpected result from read: 0x%04x != 0x%04x", val, + "unexpected result from read: 0x%02x != 0x%02x", val, data); } } @@ -110,7 +110,7 @@ static void ov534_reg_verify_write(struct usb_device *udev, u16 reg, u16 val) * (direction and output)? */ static void ov534_set_led(struct usb_device *udev, int status) { - u16 data; + u8 data; PDEBUG(D_CONF, "led status: %d", status); @@ -129,13 +129,13 @@ static void ov534_set_led(struct usb_device *udev, int status) static int sccb_check_status(struct usb_device *udev) { - u16 data; + u8 data; int i; for (i = 0; i < 5; i++) { data = ov534_reg_read(udev, OV534_REG_STATUS); - switch (data & 0xFF) { + switch (data) { case 0x00: return 1; case 0x04: @@ -150,9 +150,9 @@ static int sccb_check_status(struct usb_device *udev) return 0; } -static void sccb_reg_write(struct usb_device *udev, u16 reg, u16 val) +static void sccb_reg_write(struct usb_device *udev, u16 reg, u8 val) { - PDEBUG(D_USBO, "reg: 0x%04x, val: 0x%04x", reg, val); + PDEBUG(D_USBO, "reg: 0x%04x, val: 0x%02x", reg, val); ov534_reg_write(udev, OV534_REG_SUBADDR, reg); ov534_reg_write(udev, OV534_REG_WRITE, val); ov534_reg_write(udev, OV534_REG_OPERATION, OV534_OP_WRITE_3); -- cgit v1.2.3 From 81755eaa892b63b3357fca8123471febe60b9f66 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 3 Dec 2008 15:32:11 -0200 Subject: backport commit 72e9622c2a2eb73d82c716504cc93d22cd3cfd8e From: Mauro Carvalho Chehab Author: Guennadi Liakhovetski Date: Tue Nov 25 18:57:08 2008 +0100 [ARM] pxa/pcm990: use negative number for an invalid GPIO in camera data 0 is a valid GPIO number, use a negative number to specify, that this camera doesn't have a GPIO for bus-width switching. kernel-sync: Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/arch/arm/mach-pxa/pcm990-baseboard.c | 1 + 1 file changed, 1 insertion(+) (limited to 'linux') diff --git a/linux/arch/arm/mach-pxa/pcm990-baseboard.c b/linux/arch/arm/mach-pxa/pcm990-baseboard.c index e8eb8cbc3..da25913c4 100644 --- a/linux/arch/arm/mach-pxa/pcm990-baseboard.c +++ b/linux/arch/arm/mach-pxa/pcm990-baseboard.c @@ -403,6 +403,7 @@ static struct soc_camera_link iclink[] = { .gpio = NR_BUILTIN_GPIO + 1, }, { .bus_id = 0, /* Must match with the camera ID above */ + .gpio = -ENXIO, } }; -- cgit v1.2.3 From c1e15caf1b787cceea76c8a1868516001f1e4588 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Wed, 3 Dec 2008 20:21:08 +0100 Subject: gspca: Use smaller chunks for urb buffer in ov534. From: Antonio Ospite Priority: normal Signed-off-by: Antonio Ospite Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/ov534.c | 51 ++++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 16 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/ov534.c b/linux/drivers/media/video/gspca/ov534.c index 530f0b712..f86158dd1 100644 --- a/linux/drivers/media/video/gspca/ov534.c +++ b/linux/drivers/media/video/gspca/ov534.c @@ -346,7 +346,14 @@ static int sd_config(struct gspca_dev *gspca_dev, cam->cam_mode = vga_mode; cam->nmodes = ARRAY_SIZE(vga_mode); - cam->bulk_size = vga_mode[0].sizeimage; + /* + * On some architectures we need contiguous memory for urb buffers, and + * in low memory situation 'sizeimage' can be too much. + * 16kiB chunks should be available even when we are low in memory. + * TODO: CHECK this description: is the problem arch-dependent or more + * general? + */ + cam->bulk_size = 16 * 1024; cam->bulk_nurbs = 2; PDEBUG(D_PROBE, "bulk_size = %d", cam->bulk_size); @@ -395,15 +402,19 @@ static int sd_init(struct gspca_dev *gspca_dev) static int sd_start(struct gspca_dev *gspca_dev) { - PDEBUG(D_PROBE, "width = %d, height = %d", - gspca_dev->width, gspca_dev->height); - - gspca_dev->cam.bulk_size = gspca_dev->width * gspca_dev->height * 2; + struct gspca_frame *frame; /* start streaming data */ ov534_set_led(gspca_dev->dev, 1); ov534_reg_write(gspca_dev->dev, 0xe0, 0x00); + frame = gspca_get_i_frame(gspca_dev); + if (frame == NULL) { + PDEBUG(D_ERR, "NULL frame!"); + return -1; + } + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, gspca_dev->usb_buf, 0); + return 0; } @@ -421,18 +432,26 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, * The current camera setup doesn't stream the last pixel, so we set it * to a dummy value */ - __u8 last_pixel[4] = { 0, 0, 0, 0 }; - int framesize = gspca_dev->cam.bulk_size; - - if (len == framesize - 4) { - frame = - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, len); - frame = - gspca_frame_add(gspca_dev, LAST_PACKET, frame, last_pixel, - 4); + __u8 last[4] = { 0, 0, 0, 0 }; + int framesize = frame->v4l2_buf.length; + + PDEBUG(D_PACK, ""); + PDEBUG(D_PACK, "** packet len = %d, framesize = %d", len, framesize); + PDEBUG(D_PACK, "** frame->data_end - frame->data + len = %d", + frame->data_end - frame->data + len); + +#if 0 + /* Can this check mask some tranfer errors? */ + if (len < gspca_dev->cam.bulk_size) { +#else + if (frame->data_end - frame->data + len == framesize - 4) { +#endif + PDEBUG(D_PACK, " end of frame!"); + gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, last, 4); + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, 0); } else - PDEBUG(D_PACK, "packet len = %d, framesize = %d", len, - framesize); + gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); } /* sub-driver description */ -- cgit v1.2.3 From a31709b99b19630a583553b1b0dd95f862579258 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Thu, 4 Dec 2008 08:28:27 +0100 Subject: gspca - ov534: Initialization cleanup. From: Jim Paris Priority: normal Signed-off-by: Jim Paris Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/ov534.c | 337 +++++++++++++++----------------- 1 file changed, 159 insertions(+), 178 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/ov534.c b/linux/drivers/media/video/gspca/ov534.c index f86158dd1..f0b096612 100644 --- a/linux/drivers/media/video/gspca/ov534.c +++ b/linux/drivers/media/video/gspca/ov534.c @@ -93,19 +93,6 @@ static u8 ov534_reg_read(struct usb_device *udev, u16 reg) return data; } -static void ov534_reg_verify_write(struct usb_device *udev, u16 reg, u8 val) -{ - u8 data; - - ov534_reg_write(udev, reg, val); - data = ov534_reg_read(udev, reg); - if (data != val) { - PDEBUG(D_ERR | D_USBO, - "unexpected result from read: 0x%02x != 0x%02x", val, - data); - } -} - /* Two bits control LED: 0x21 bit 7 and 0x23 bit 7. * (direction and output)? */ static void ov534_set_led(struct usb_device *udev, int status) @@ -161,174 +148,168 @@ static void sccb_reg_write(struct usb_device *udev, u16 reg, u8 val) PDEBUG(D_ERR, "sccb_reg_write failed"); } -/* setup method */ -static void ov534_setup(struct usb_device *udev) -{ - ov534_reg_verify_write(udev, 0xe7, 0x3a); - - ov534_reg_write(udev, OV534_REG_ADDRESS, 0x60); - ov534_reg_write(udev, OV534_REG_ADDRESS, 0x60); - ov534_reg_write(udev, OV534_REG_ADDRESS, 0x60); - ov534_reg_write(udev, OV534_REG_ADDRESS, 0x42); - - ov534_reg_verify_write(udev, 0xc2, 0x0c); - ov534_reg_verify_write(udev, 0x88, 0xf8); - ov534_reg_verify_write(udev, 0xc3, 0x69); - ov534_reg_verify_write(udev, 0x89, 0xff); - ov534_reg_verify_write(udev, 0x76, 0x03); - ov534_reg_verify_write(udev, 0x92, 0x01); - ov534_reg_verify_write(udev, 0x93, 0x18); - ov534_reg_verify_write(udev, 0x94, 0x10); - ov534_reg_verify_write(udev, 0x95, 0x10); - ov534_reg_verify_write(udev, 0xe2, 0x00); - ov534_reg_verify_write(udev, 0xe7, 0x3e); - - ov534_reg_write(udev, 0x1c, 0x0a); - ov534_reg_write(udev, 0x1d, 0x22); - ov534_reg_write(udev, 0x1d, 0x06); - - ov534_reg_verify_write(udev, 0x96, 0x00); - - ov534_reg_write(udev, 0x97, 0x20); - ov534_reg_write(udev, 0x97, 0x20); - ov534_reg_write(udev, 0x97, 0x20); - ov534_reg_write(udev, 0x97, 0x0a); - ov534_reg_write(udev, 0x97, 0x3f); - ov534_reg_write(udev, 0x97, 0x4a); - ov534_reg_write(udev, 0x97, 0x20); - ov534_reg_write(udev, 0x97, 0x15); - ov534_reg_write(udev, 0x97, 0x0b); - - ov534_reg_verify_write(udev, 0x8e, 0x40); - ov534_reg_verify_write(udev, 0x1f, 0x81); - ov534_reg_verify_write(udev, 0x34, 0x05); - ov534_reg_verify_write(udev, 0xe3, 0x04); - ov534_reg_verify_write(udev, 0x88, 0x00); - ov534_reg_verify_write(udev, 0x89, 0x00); - ov534_reg_verify_write(udev, 0x76, 0x00); - ov534_reg_verify_write(udev, 0xe7, 0x2e); - ov534_reg_verify_write(udev, 0x31, 0xf9); - ov534_reg_verify_write(udev, 0x25, 0x42); - ov534_reg_verify_write(udev, 0x21, 0xf0); - - ov534_reg_write(udev, 0x1c, 0x00); - ov534_reg_write(udev, 0x1d, 0x40); - ov534_reg_write(udev, 0x1d, 0x02); - ov534_reg_write(udev, 0x1d, 0x00); - ov534_reg_write(udev, 0x1d, 0x02); - ov534_reg_write(udev, 0x1d, 0x57); - ov534_reg_write(udev, 0x1d, 0xff); - - ov534_reg_verify_write(udev, 0x8d, 0x1c); - ov534_reg_verify_write(udev, 0x8e, 0x80); - ov534_reg_verify_write(udev, 0xe5, 0x04); - - ov534_set_led(udev, 1); - - sccb_reg_write(udev, 0x12, 0x80); - sccb_reg_write(udev, 0x11, 0x01); - sccb_reg_write(udev, 0x11, 0x01); - sccb_reg_write(udev, 0x11, 0x01); - sccb_reg_write(udev, 0x11, 0x01); - sccb_reg_write(udev, 0x11, 0x01); - sccb_reg_write(udev, 0x11, 0x01); - sccb_reg_write(udev, 0x11, 0x01); - sccb_reg_write(udev, 0x11, 0x01); - sccb_reg_write(udev, 0x11, 0x01); - sccb_reg_write(udev, 0x11, 0x01); - sccb_reg_write(udev, 0x11, 0x01); - - ov534_set_led(udev, 0); - - sccb_reg_write(udev, 0x3d, 0x03); - sccb_reg_write(udev, 0x17, 0x26); - sccb_reg_write(udev, 0x18, 0xa0); - sccb_reg_write(udev, 0x19, 0x07); - sccb_reg_write(udev, 0x1a, 0xf0); - sccb_reg_write(udev, 0x32, 0x00); - sccb_reg_write(udev, 0x29, 0xa0); - sccb_reg_write(udev, 0x2c, 0xf0); - sccb_reg_write(udev, 0x65, 0x20); - sccb_reg_write(udev, 0x11, 0x01); - sccb_reg_write(udev, 0x42, 0x7f); - sccb_reg_write(udev, 0x63, 0xe0); - sccb_reg_write(udev, 0x64, 0xff); - sccb_reg_write(udev, 0x66, 0x00); - sccb_reg_write(udev, 0x13, 0xf0); - sccb_reg_write(udev, 0x0d, 0x41); - sccb_reg_write(udev, 0x0f, 0xc5); - sccb_reg_write(udev, 0x14, 0x11); - - ov534_set_led(udev, 1); - - sccb_reg_write(udev, 0x22, 0x7f); - sccb_reg_write(udev, 0x23, 0x03); - sccb_reg_write(udev, 0x24, 0x40); - sccb_reg_write(udev, 0x25, 0x30); - sccb_reg_write(udev, 0x26, 0xa1); - sccb_reg_write(udev, 0x2a, 0x00); - sccb_reg_write(udev, 0x2b, 0x00); - sccb_reg_write(udev, 0x6b, 0xaa); - sccb_reg_write(udev, 0x13, 0xff); +static const __u8 ov534_reg_initdata[][2] = { + { 0xe7, 0x3a }, + + { OV534_REG_ADDRESS, 0x42 }, /* select OV772x sensor */ + + { 0xc2, 0x0c }, + { 0x88, 0xf8 }, + { 0xc3, 0x69 }, + { 0x89, 0xff }, + { 0x76, 0x03 }, + { 0x92, 0x01 }, + { 0x93, 0x18 }, + { 0x94, 0x10 }, + { 0x95, 0x10 }, + { 0xe2, 0x00 }, + { 0xe7, 0x3e }, + + { 0x1c, 0x0a }, + { 0x1d, 0x22 }, + { 0x1d, 0x06 }, + + { 0x96, 0x00 }, + + { 0x97, 0x20 }, + { 0x97, 0x20 }, + { 0x97, 0x20 }, + { 0x97, 0x0a }, + { 0x97, 0x3f }, + { 0x97, 0x4a }, + { 0x97, 0x20 }, + { 0x97, 0x15 }, + { 0x97, 0x0b }, + + { 0x8e, 0x40 }, + { 0x1f, 0x81 }, + { 0x34, 0x05 }, + { 0xe3, 0x04 }, + { 0x88, 0x00 }, + { 0x89, 0x00 }, + { 0x76, 0x00 }, + { 0xe7, 0x2e }, + { 0x31, 0xf9 }, + { 0x25, 0x42 }, + { 0x21, 0xf0 }, + + { 0x1c, 0x00 }, + { 0x1d, 0x40 }, + { 0x1d, 0x02 }, + { 0x1d, 0x00 }, + { 0x1d, 0x02 }, + { 0x1d, 0x57 }, + { 0x1d, 0xff }, + + { 0x8d, 0x1c }, + { 0x8e, 0x80 }, + { 0xe5, 0x04 }, + + { 0xc0, 0x50 }, + { 0xc1, 0x3c }, + { 0xc2, 0x0c }, +}; - ov534_set_led(udev, 0); +static const __u8 ov772x_reg_initdata[][2] = { + { 0x12, 0x80 }, + { 0x11, 0x01 }, + + { 0x3d, 0x03 }, + { 0x17, 0x26 }, + { 0x18, 0xa0 }, + { 0x19, 0x07 }, + { 0x1a, 0xf0 }, + { 0x32, 0x00 }, + { 0x29, 0xa0 }, + { 0x2c, 0xf0 }, + { 0x65, 0x20 }, + { 0x11, 0x01 }, + { 0x42, 0x7f }, + { 0x63, 0xe0 }, + { 0x64, 0xff }, + { 0x66, 0x00 }, + { 0x13, 0xf0 }, + { 0x0d, 0x41 }, + { 0x0f, 0xc5 }, + { 0x14, 0x11 }, + + { 0x22, 0x7f }, + { 0x23, 0x03 }, + { 0x24, 0x40 }, + { 0x25, 0x30 }, + { 0x26, 0xa1 }, + { 0x2a, 0x00 }, + { 0x2b, 0x00 }, + { 0x6b, 0xaa }, + { 0x13, 0xff }, + + { 0x90, 0x05 }, + { 0x91, 0x01 }, + { 0x92, 0x03 }, + { 0x93, 0x00 }, + { 0x94, 0x60 }, + { 0x95, 0x3c }, + { 0x96, 0x24 }, + { 0x97, 0x1e }, + { 0x98, 0x62 }, + { 0x99, 0x80 }, + { 0x9a, 0x1e }, + { 0x9b, 0x08 }, + { 0x9c, 0x20 }, + { 0x9e, 0x81 }, + + { 0xa6, 0x04 }, + { 0x7e, 0x0c }, + { 0x7f, 0x16 }, + { 0x80, 0x2a }, + { 0x81, 0x4e }, + { 0x82, 0x61 }, + { 0x83, 0x6f }, + { 0x84, 0x7b }, + { 0x85, 0x86 }, + { 0x86, 0x8e }, + { 0x87, 0x97 }, + { 0x88, 0xa4 }, + { 0x89, 0xaf }, + { 0x8a, 0xc5 }, + { 0x8b, 0xd7 }, + { 0x8c, 0xe8 }, + { 0x8d, 0x20 }, + + { 0x0c, 0x90 }, + + { 0x2b, 0x00 }, + { 0x22, 0x7f }, + { 0x23, 0x03 }, + { 0x11, 0x01 }, + { 0x0c, 0xd0 }, + { 0x64, 0xff }, + { 0x0d, 0x41 }, + + { 0x14, 0x41 }, + { 0x0e, 0xcd }, + { 0xac, 0xbf }, + { 0x8e, 0x00 }, + { 0x0c, 0xd0 } +}; - sccb_reg_write(udev, 0x90, 0x05); - sccb_reg_write(udev, 0x91, 0x01); - sccb_reg_write(udev, 0x92, 0x03); - sccb_reg_write(udev, 0x93, 0x00); - sccb_reg_write(udev, 0x94, 0x60); - sccb_reg_write(udev, 0x95, 0x3c); - sccb_reg_write(udev, 0x96, 0x24); - sccb_reg_write(udev, 0x97, 0x1e); - sccb_reg_write(udev, 0x98, 0x62); - sccb_reg_write(udev, 0x99, 0x80); - sccb_reg_write(udev, 0x9a, 0x1e); - sccb_reg_write(udev, 0x9b, 0x08); - sccb_reg_write(udev, 0x9c, 0x20); - sccb_reg_write(udev, 0x9e, 0x81); - ov534_set_led(udev, 1); +/* setup method */ +static void ov534_setup(struct usb_device *udev) +{ + int i; - sccb_reg_write(udev, 0xa6, 0x04); - sccb_reg_write(udev, 0x7e, 0x0c); - sccb_reg_write(udev, 0x7f, 0x16); - sccb_reg_write(udev, 0x80, 0x2a); - sccb_reg_write(udev, 0x81, 0x4e); - sccb_reg_write(udev, 0x82, 0x61); - sccb_reg_write(udev, 0x83, 0x6f); - sccb_reg_write(udev, 0x84, 0x7b); - sccb_reg_write(udev, 0x85, 0x86); - sccb_reg_write(udev, 0x86, 0x8e); - sccb_reg_write(udev, 0x87, 0x97); - sccb_reg_write(udev, 0x88, 0xa4); - sccb_reg_write(udev, 0x89, 0xaf); - sccb_reg_write(udev, 0x8a, 0xc5); - sccb_reg_write(udev, 0x8b, 0xd7); - sccb_reg_write(udev, 0x8c, 0xe8); - sccb_reg_write(udev, 0x8d, 0x20); - - sccb_reg_write(udev, 0x0c, 0x90); - - ov534_reg_verify_write(udev, 0xc0, 0x50); - ov534_reg_verify_write(udev, 0xc1, 0x3c); - ov534_reg_verify_write(udev, 0xc2, 0x0c); + /* Initialize bridge chip */ + for (i = 0; i < ARRAY_SIZE(ov534_reg_initdata); i++) + ov534_reg_write(udev, ov534_reg_initdata[i][0], + ov534_reg_initdata[i][1]); ov534_set_led(udev, 1); - sccb_reg_write(udev, 0x2b, 0x00); - sccb_reg_write(udev, 0x22, 0x7f); - sccb_reg_write(udev, 0x23, 0x03); - sccb_reg_write(udev, 0x11, 0x01); - sccb_reg_write(udev, 0x0c, 0xd0); - sccb_reg_write(udev, 0x64, 0xff); - sccb_reg_write(udev, 0x0d, 0x41); - - sccb_reg_write(udev, 0x14, 0x41); - sccb_reg_write(udev, 0x0e, 0xcd); - sccb_reg_write(udev, 0xac, 0xbf); - sccb_reg_write(udev, 0x8e, 0x00); - sccb_reg_write(udev, 0x0c, 0xd0); + /* Initialize sensor */ + for (i = 0; i < ARRAY_SIZE(ov772x_reg_initdata); i++) + sccb_reg_write(udev, ov772x_reg_initdata[i][0], + ov772x_reg_initdata[i][1]); ov534_reg_write(udev, 0xe0, 0x09); ov534_set_led(udev, 0); @@ -374,24 +355,24 @@ static int sd_init(struct gspca_dev *gspca_dev) case 50: sccb_reg_write(gspca_dev->dev, 0x11, 0x01); sccb_reg_write(gspca_dev->dev, 0x0d, 0x41); - ov534_reg_verify_write(gspca_dev->dev, 0xe5, 0x02); + ov534_reg_write(gspca_dev->dev, 0xe5, 0x02); break; case 40: sccb_reg_write(gspca_dev->dev, 0x11, 0x02); sccb_reg_write(gspca_dev->dev, 0x0d, 0xc1); - ov534_reg_verify_write(gspca_dev->dev, 0xe5, 0x04); + ov534_reg_write(gspca_dev->dev, 0xe5, 0x04); break; /* case 30: */ default: fr = 30; sccb_reg_write(gspca_dev->dev, 0x11, 0x04); sccb_reg_write(gspca_dev->dev, 0x0d, 0x81); - ov534_reg_verify_write(gspca_dev->dev, 0xe5, 0x02); + ov534_reg_write(gspca_dev->dev, 0xe5, 0x02); break; case 15: sccb_reg_write(gspca_dev->dev, 0x11, 0x03); sccb_reg_write(gspca_dev->dev, 0x0d, 0x41); - ov534_reg_verify_write(gspca_dev->dev, 0xe5, 0x04); + ov534_reg_write(gspca_dev->dev, 0xe5, 0x04); break; } -- cgit v1.2.3 From 813bf1a92a9d665e013b0c0a52450d68af7ae80c Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Thu, 4 Dec 2008 08:36:14 +0100 Subject: gspca - ov534: Fix frame size so we don't miss the last pixel. From: Jim Paris The frame size is too small, so we lose the last YUYV pixel. Fix the setup and remove the last_pixel hack. Priority: normal Signed-off-by: Jim Paris Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/ov534.c | 33 +++++++++------------------------ 1 file changed, 9 insertions(+), 24 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/ov534.c b/linux/drivers/media/video/gspca/ov534.c index f0b096612..79bb6241d 100644 --- a/linux/drivers/media/video/gspca/ov534.c +++ b/linux/drivers/media/video/gspca/ov534.c @@ -197,9 +197,9 @@ static const __u8 ov534_reg_initdata[][2] = { { 0x1d, 0x40 }, { 0x1d, 0x02 }, { 0x1d, 0x00 }, - { 0x1d, 0x02 }, - { 0x1d, 0x57 }, - { 0x1d, 0xff }, + { 0x1d, 0x02 }, /* frame size 0x025800 * 4 = 614400 */ + { 0x1d, 0x58 }, /* frame size */ + { 0x1d, 0x00 }, /* frame size */ { 0x8d, 0x1c }, { 0x8e, 0x80 }, @@ -409,30 +409,15 @@ static void sd_stopN(struct gspca_dev *gspca_dev) static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, __u8 *data, int len) { - /* - * The current camera setup doesn't stream the last pixel, so we set it - * to a dummy value - */ - __u8 last[4] = { 0, 0, 0, 0 }; int framesize = frame->v4l2_buf.length; - PDEBUG(D_PACK, ""); - PDEBUG(D_PACK, "** packet len = %d, framesize = %d", len, framesize); - PDEBUG(D_PACK, "** frame->data_end - frame->data + len = %d", - frame->data_end - frame->data + len); - -#if 0 - /* Can this check mask some tranfer errors? */ - if (len < gspca_dev->cam.bulk_size) { -#else - if (frame->data_end - frame->data + len == framesize - 4) { -#endif - PDEBUG(D_PACK, " end of frame!"); - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, last, 4); - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, 0); + if (len == framesize) { + frame = gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + data, len); + frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, data, 0); } else - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + PDEBUG(D_PACK, "packet len = %d, framesize = %d", len, + framesize); } /* sub-driver description */ -- cgit v1.2.3 From 8b10a9746fefdebe48ea6080a647adfe08be3338 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Thu, 4 Dec 2008 08:52:40 +0100 Subject: gspca - ov534: Frame transfer improvements. From: Jim Paris The indirect registers at 0x1c/0x1d control frame settings. If we leave the values at 0x0a and 0x0b at their reset-time defaults, frame data from the camera matches the UVC payload format. This lets us better reassemble the data into frames and know when data was lost. This also lets us relax the bulk_size requirement from 600K to 2K, which should help systems on with limited RAM (like the PS3). Priority: normal Signed-off-by: Jim Paris Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/ov534.c | 103 ++++++++++++++++++++++---------- 1 file changed, 71 insertions(+), 32 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/ov534.c b/linux/drivers/media/video/gspca/ov534.c index 79bb6241d..e02c8f70f 100644 --- a/linux/drivers/media/video/gspca/ov534.c +++ b/linux/drivers/media/video/gspca/ov534.c @@ -165,10 +165,6 @@ static const __u8 ov534_reg_initdata[][2] = { { 0xe2, 0x00 }, { 0xe7, 0x3e }, - { 0x1c, 0x0a }, - { 0x1d, 0x22 }, - { 0x1d, 0x06 }, - { 0x96, 0x00 }, { 0x97, 0x20 }, @@ -327,18 +323,9 @@ static int sd_config(struct gspca_dev *gspca_dev, cam->cam_mode = vga_mode; cam->nmodes = ARRAY_SIZE(vga_mode); - /* - * On some architectures we need contiguous memory for urb buffers, and - * in low memory situation 'sizeimage' can be too much. - * 16kiB chunks should be available even when we are low in memory. - * TODO: CHECK this description: is the problem arch-dependent or more - * general? - */ - cam->bulk_size = 16 * 1024; + cam->bulk_size = 2048; cam->bulk_nurbs = 2; - PDEBUG(D_PROBE, "bulk_size = %d", cam->bulk_size); - return 0; } @@ -383,19 +370,10 @@ static int sd_init(struct gspca_dev *gspca_dev) static int sd_start(struct gspca_dev *gspca_dev) { - struct gspca_frame *frame; - /* start streaming data */ ov534_set_led(gspca_dev->dev, 1); ov534_reg_write(gspca_dev->dev, 0xe0, 0x00); - frame = gspca_get_i_frame(gspca_dev); - if (frame == NULL) { - PDEBUG(D_ERR, "NULL frame!"); - return -1; - } - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, gspca_dev->usb_buf, 0); - return 0; } @@ -406,18 +384,79 @@ static void sd_stopN(struct gspca_dev *gspca_dev) ov534_set_led(gspca_dev->dev, 0); } +/* Values for bmHeaderInfo (Video and Still Image Payload Headers, 2.4.3.3) */ +#define UVC_STREAM_EOH (1 << 7) +#define UVC_STREAM_ERR (1 << 6) +#define UVC_STREAM_STI (1 << 5) +#define UVC_STREAM_RES (1 << 4) +#define UVC_STREAM_SCR (1 << 3) +#define UVC_STREAM_PTS (1 << 2) +#define UVC_STREAM_EOF (1 << 1) +#define UVC_STREAM_FID (1 << 0) + static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, __u8 *data, int len) { - int framesize = frame->v4l2_buf.length; - - if (len == framesize) { - frame = gspca_frame_add(gspca_dev, FIRST_PACKET, frame, - data, len); - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, data, 0); - } else - PDEBUG(D_PACK, "packet len = %d, framesize = %d", len, - framesize); + static __u32 last_pts; + __u32 this_pts; + static int last_fid; + int this_fid; + + /* Payloads are prefixed with a the UVC-style header. We + consider a frame to start when the FID toggles, or the PTS + changes. A frame ends when EOF is set, and we've received + the correct number of bytes. */ + + /* Verify UVC header. Header length is always 12 */ + if (data[0] != 12 || len < 12) { + PDEBUG(D_PACK, "bad header"); + goto discard; + } + + /* Check errors */ + if (data[1] & UVC_STREAM_ERR) { + PDEBUG(D_PACK, "payload error"); + goto discard; + } + + /* Extract PTS and FID */ + if (!(data[1] & UVC_STREAM_PTS)) { + PDEBUG(D_PACK, "PTS not present"); + goto discard; + } + this_pts = (data[5] << 24) | (data[4] << 16) | (data[3] << 8) | data[2]; + this_fid = (data[1] & UVC_STREAM_FID) ? 1 : 0; + + /* If PTS or FID has changed, start a new frame. */ + if (this_pts != last_pts || this_fid != last_fid) { + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, NULL, 0); + last_pts = this_pts; + last_fid = this_fid; + } + + /* Add the data from this payload */ + gspca_frame_add(gspca_dev, INTER_PACKET, frame, + data + 12, len - 12); + + /* If this packet is marked as EOF, end the frame */ + if (data[1] & UVC_STREAM_EOF) { + last_pts = 0; + + if ((frame->data_end - frame->data) != + (gspca_dev->width * gspca_dev->height * 2)) { + PDEBUG(D_PACK, "short frame"); + goto discard; + } + + gspca_frame_add(gspca_dev, LAST_PACKET, frame, NULL, 0); + } + + /* Done */ + return; + +discard: + /* Discard data until a new frame starts. */ + gspca_frame_add(gspca_dev, DISCARD_PACKET, frame, NULL, 0); } /* sub-driver description */ -- cgit v1.2.3 From a9c96b1af32e0ce19b406f21d9ee63a9b6883d33 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Thu, 4 Dec 2008 09:06:08 +0100 Subject: gspca - ov534: Accept many simultaneous webcams. From: Jean-Francois Moine This patch moves the last fid and pts from static to the sd structure. Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/ov534.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/ov534.c b/linux/drivers/media/video/gspca/ov534.c index e02c8f70f..28a798279 100644 --- a/linux/drivers/media/video/gspca/ov534.c +++ b/linux/drivers/media/video/gspca/ov534.c @@ -48,6 +48,8 @@ static int frame_rate; /* specific webcam descriptor */ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ + __u32 last_fid; + __u32 last_pts; }; /* V4L2 controls supported by the driver */ @@ -397,9 +399,8 @@ static void sd_stopN(struct gspca_dev *gspca_dev) static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, __u8 *data, int len) { - static __u32 last_pts; + struct sd *sd = (struct sd *) gspca_dev; __u32 this_pts; - static int last_fid; int this_fid; /* Payloads are prefixed with a the UVC-style header. We @@ -428,10 +429,10 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, this_fid = (data[1] & UVC_STREAM_FID) ? 1 : 0; /* If PTS or FID has changed, start a new frame. */ - if (this_pts != last_pts || this_fid != last_fid) { + if (this_pts != sd->last_pts || this_fid != sd->last_fid) { gspca_frame_add(gspca_dev, FIRST_PACKET, frame, NULL, 0); - last_pts = this_pts; - last_fid = this_fid; + sd->last_pts = this_pts; + sd->last_fid = this_fid; } /* Add the data from this payload */ @@ -440,7 +441,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* If this packet is marked as EOF, end the frame */ if (data[1] & UVC_STREAM_EOF) { - last_pts = 0; + sd->last_pts = 0; if ((frame->data_end - frame->data) != (gspca_dev->width * gspca_dev->height * 2)) { -- cgit v1.2.3 From e9548d6c7e62b32a90ad51b519844be602571b9d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 4 Dec 2008 15:49:23 +0000 Subject: Fix stv0299 support in dw2102 USB DVB-S/S2 driver From: Igor M. Liplianin register 0x00 contains 0xa1 for STV0299 and STV0299B register 0x00 might contain 0x80 when returning from standby Signed-off-by: Igor M. Liplianin Acked-by: Sergey Silkin Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/dvb/dvb-usb/dw2102.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/dvb/dvb-usb/dw2102.c b/linux/drivers/media/dvb/dvb-usb/dw2102.c index 9b32c9c0a..2b8ee587b 100644 --- a/linux/drivers/media/dvb/dvb-usb/dw2102.c +++ b/linux/drivers/media/dvb/dvb-usb/dw2102.c @@ -672,7 +672,7 @@ static int dw2102_load_firmware(struct usb_device *dev, /* check STV0299 frontend */ dw210x_op_rw(dev, 0xb5, 0, 0, &reset16[0], 2, DW210X_READ_MSG); - if (reset16[0] == 0xa1) { + if ((reset16[0] == 0xa1) || (reset16[0] == 0x80)) { dw2102_properties.i2c_algo = &dw2102_i2c_algo; dw2102_properties.adapter->tuner_attach = &dw2102_tuner_attach; break; -- cgit v1.2.3 From 798b92bbda5976e2ca0fca8ca21ef0c11757a244 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Fri, 5 Dec 2008 10:18:37 +0100 Subject: gspca - sonixj: Cleanup / simplify code. From: Jean-Francois Moine Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/sonixj.c | 70 ++++++++++++++++---------------- 1 file changed, 34 insertions(+), 36 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/sonixj.c b/linux/drivers/media/video/gspca/sonixj.c index 368382cf7..ff94cd639 100644 --- a/linux/drivers/media/video/gspca/sonixj.c +++ b/linux/drivers/media/video/gspca/sonixj.c @@ -37,26 +37,26 @@ struct sd { atomic_t avg_lum; unsigned int exposure; - unsigned short brightness; - unsigned char contrast; - unsigned char colors; - unsigned char autogain; + __u16 brightness; + __u8 contrast; + __u8 colors; + __u8 autogain; __u8 blue; __u8 red; __u8 vflip; /* ov7630 only */ __u8 infrared; /* mi0360 only */ - signed char ag_cnt; + __s8 ag_cnt; #define AG_CNT_START 13 - char qindex; - unsigned char bridge; + __u8 qindex; + __u8 bridge; #define BRIDGE_SN9C102P 0 #define BRIDGE_SN9C105 1 #define BRIDGE_SN9C110 2 #define BRIDGE_SN9C120 3 #define BRIDGE_SN9C325 4 - char sensor; /* Type of image sensor chip */ + __u8 sensor; /* Type of image sensor chip */ #define SENSOR_HV7131R 0 #define SENSOR_MI0360 1 #define SENSOR_MO4000 2 @@ -64,7 +64,7 @@ struct sd { #define SENSOR_OV7630 4 #define SENSOR_OV7648 5 #define SENSOR_OV7660 6 - unsigned char i2c_base; + __u8 i2c_base; }; /* V4L2 controls supported by the driver */ @@ -207,6 +207,24 @@ static struct ctrl sd_ctrls[] = { }, }; +/* table of the disabled controls */ +static __u32 ctrl_dis[] = { + (1 << INFRARED_IDX) | (1 << VFLIP_IDX), + /* SENSOR_HV7131R 0 */ + (1 << VFLIP_IDX), + /* SENSOR_MI0360 1 */ + (1 << INFRARED_IDX) | (1 << VFLIP_IDX), + /* SENSOR_MO4000 2 */ + (1 << INFRARED_IDX) | (1 << VFLIP_IDX), + /* SENSOR_OM6802 3 */ + (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX), + /* SENSOR_OV7630 4 */ + (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX), + /* SENSOR_OV7648 5 */ + (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX), + /* SENSOR_OV7660 6 */ +}; + static struct v4l2_pix_format vga_mode[] = { {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 160, @@ -801,8 +819,6 @@ static void i2c_r5(struct gspca_dev *gspca_dev, __u8 reg) static int probesensor(struct gspca_dev *gspca_dev) { - struct sd *sd = (struct sd *) gspca_dev; - i2c_w1(gspca_dev, 0x02, 0); /* sensor wakeup */ msleep(10); reg_w1(gspca_dev, 0x02, 0x66); /* Gpio on */ @@ -814,8 +830,7 @@ static int probesensor(struct gspca_dev *gspca_dev) && gspca_dev->usb_buf[3] == 0x00 && gspca_dev->usb_buf[4] == 0x00) { PDEBUG(D_PROBE, "Find Sensor sn9c102P HV7131R"); - sd->sensor = SENSOR_HV7131R; - return SENSOR_HV7131R; + return 0; } PDEBUG(D_PROBE, "Find Sensor 0x%02x 0x%02x 0x%02x", gspca_dev->usb_buf[0], gspca_dev->usb_buf[1], @@ -1030,17 +1045,7 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->vflip = VFLIP_DEF; sd->infrared = INFRARED_DEF; - switch (sd->sensor) { - case SENSOR_OV7630: - case SENSOR_OV7648: - case SENSOR_OV7660: - gspca_dev->ctrl_dis = (1 << AUTOGAIN_IDX); - break; - } - if (sd->sensor != SENSOR_OV7630) - gspca_dev->ctrl_dis |= (1 << VFLIP_IDX); - if (sd->sensor != SENSOR_MI0360) - gspca_dev->ctrl_dis |= (1 << INFRARED_IDX); + gspca_dev->ctrl_dis = ctrl_dis[sd->sensor]; return 0; } @@ -1048,7 +1053,6 @@ static int sd_config(struct gspca_dev *gspca_dev, static int sd_init(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; -/* const __u8 *sn9c1xx; */ __u8 regGpio[] = { 0x29, 0x74 }; __u8 regF1; @@ -1087,12 +1091,7 @@ static int sd_init(struct gspca_dev *gspca_dev) reg_w1(gspca_dev, 0xf1, 0x01); -#if 1 /*jfm: from win trace*/ return 0; -#else - sn9c1xx = sn_tb[(int) sd->sensor]; - return configure_gpio(gspca_dev, sn9c1xx); -#endif } static unsigned int setexposure(struct gspca_dev *gspca_dev, @@ -1207,13 +1206,16 @@ static void setcontrast(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; __u8 k2; - __u8 contrast[] = { 0x00, 0x00, 0x28, 0x00, 0x07, 0x00 }; + __u8 contrast[6]; k2 = sd->contrast * 0x30 / (CONTRAST_MAX + 1) + 0x10; /* 10..40 */ contrast[0] = (k2 + 1) / 2; /* red */ + contrast[1] = 0; contrast[2] = k2; /* green */ + contrast[3] = 0; contrast[4] = (k2 + 1) / 5; /* blue */ - reg_w(gspca_dev, 0x84, contrast, 6); + contrast[5] = 0; + reg_w(gspca_dev, 0x84, contrast, sizeof contrast); } static void setcolors(struct gspca_dev *gspca_dev) @@ -1386,10 +1388,6 @@ static int sd_start(struct gspca_dev *gspca_dev) ov7648_InitSensor(gspca_dev); reg17 = 0x21; /* reg1 = 0x42; * 42 - 46? */ -/* if (mode) - ; * 320x2... - else - ; * 640x... */ break; default: /* case SENSOR_OV7660: */ -- cgit v1.2.3 From b4bd1e6e9284910c01185a58e17ce30342ecfd3d Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Fri, 5 Dec 2008 10:25:26 +0100 Subject: gspca - ov519: Change copyright information. From: Romain Beauxis Priority: normal Signed-off-by: Romain Beauxis Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/ov519.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/ov519.c b/linux/drivers/media/video/gspca/ov519.c index b18d98191..5fb519da2 100644 --- a/linux/drivers/media/video/gspca/ov519.c +++ b/linux/drivers/media/video/gspca/ov519.c @@ -3,7 +3,18 @@ * * Copyright (C) 2008 Jean-Francois Moine (http://moinejf.free.fr) * - * (This module is adapted from the ov51x-jpeg package) + * This module is adapted from the ov51x-jpeg package, which itself + * was adapted from the ov511 driver. + * + * Original copyright for the ov511 driver is: + * + * Copyright (c) 1999-2004 Mark W. McClelland + * Support for OV519, OV8610 Copyright (c) 2003 Joerg Heckenbach + * + * ov51x-jpeg original copyright is: + * + * Copyright (c) 2004-2007 Romain Beauxis + * Support for OV7670 sensors was contributed by Sam Skipsey * * 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 638261a778c7bf8bea3491501854038d0a524dbe Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 5 Dec 2008 14:13:40 +0100 Subject: v4l2-int-if: add three new ioctls for std handling and routing From: Vaibhav Hiremath These changes are needed for the new TVP514x driver. Priority: normal Signed-off-by: Brijesh Jadav Signed-off-by: Hardik Shah Signed-off-by: Manjunath Hadli Signed-off-by: R Sivaraj Signed-off-by: Vaibhav Hiremath Signed-off-by: Karicheri Muralidharan Signed-off-by: Hans Verkuil --- linux/include/media/v4l2-int-device.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'linux') diff --git a/linux/include/media/v4l2-int-device.h b/linux/include/media/v4l2-int-device.h index 9c2df41db..ecda3c725 100644 --- a/linux/include/media/v4l2-int-device.h +++ b/linux/include/media/v4l2-int-device.h @@ -183,6 +183,9 @@ enum v4l2_int_ioctl_num { vidioc_int_s_crop_num, vidioc_int_g_parm_num, vidioc_int_s_parm_num, + vidioc_int_querystd_num, + vidioc_int_s_std_num, + vidioc_int_s_video_routing_num, /* * @@ -284,6 +287,9 @@ V4L2_INT_WRAPPER_1(g_crop, struct v4l2_crop, *); V4L2_INT_WRAPPER_1(s_crop, struct v4l2_crop, *); V4L2_INT_WRAPPER_1(g_parm, struct v4l2_streamparm, *); V4L2_INT_WRAPPER_1(s_parm, struct v4l2_streamparm, *); +V4L2_INT_WRAPPER_1(querystd, v4l2_std_id, *); +V4L2_INT_WRAPPER_1(s_std, v4l2_std_id, *); +V4L2_INT_WRAPPER_1(s_video_routing, struct v4l2_routing, *); V4L2_INT_WRAPPER_0(dev_init); V4L2_INT_WRAPPER_0(dev_exit); -- cgit v1.2.3 From c3e98daadb06990a815e0bd027c2d578c1e2ffee Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 5 Dec 2008 14:19:36 +0100 Subject: v4l: add new tvp514x I2C video decoder driver From: Vaibhav Hiremath Priority: normal Signed-off-by: Brijesh Jadav Signed-off-by: Hardik Shah Signed-off-by: Manjunath Hadli Signed-off-by: R Sivaraj Signed-off-by: Vaibhav Hiremath Signed-off-by: Karicheri Muralidharan Signed-off-by: Hans Verkuil Reviewed-by: Hans Verkuil Reviewed-by: David Brownell --- linux/drivers/media/video/Kconfig | 11 + linux/drivers/media/video/Makefile | 1 + linux/drivers/media/video/tvp514x.c | 1569 ++++++++++++++++++++++++++++++ linux/drivers/media/video/tvp514x_regs.h | 297 ++++++ linux/include/media/tvp514x.h | 118 +++ 5 files changed, 1996 insertions(+) create mode 100644 linux/drivers/media/video/tvp514x.c create mode 100644 linux/drivers/media/video/tvp514x_regs.h create mode 100644 linux/include/media/tvp514x.h (limited to 'linux') diff --git a/linux/drivers/media/video/Kconfig b/linux/drivers/media/video/Kconfig index 91652d964..cc224744b 100644 --- a/linux/drivers/media/video/Kconfig +++ b/linux/drivers/media/video/Kconfig @@ -361,6 +361,17 @@ config VIDEO_SAA7191 To compile this driver as a module, choose M here: the module will be called saa7191. +config VIDEO_TVP514X + tristate "Texas Instruments TVP514x video decoder" + depends on VIDEO_V4L2 && I2C + ---help--- + This is a Video4Linux2 sensor-level driver for the TI TVP5146/47 + decoder. It is currently working with the TI OMAP3 camera + controller. + + To compile this driver as a module, choose M here: the + module will be called tvp514x. + config VIDEO_TVP5150 tristate "Texas Instruments TVP5150 video decoder" depends on VIDEO_V4L2 && I2C diff --git a/linux/drivers/media/video/Makefile b/linux/drivers/media/video/Makefile index ac147b1ae..492ab3dce 100644 --- a/linux/drivers/media/video/Makefile +++ b/linux/drivers/media/video/Makefile @@ -69,6 +69,7 @@ obj-$(CONFIG_VIDEO_CX88) += cx88/ obj-$(CONFIG_VIDEO_EM28XX) += em28xx/ obj-$(CONFIG_VIDEO_USBVISION) += usbvision/ obj-$(CONFIG_VIDEO_TVP5150) += tvp5150.o +obj-$(CONFIG_VIDEO_TVP514X) += tvp514x.o obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2/ obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o obj-$(CONFIG_VIDEO_CS5345) += cs5345.o diff --git a/linux/drivers/media/video/tvp514x.c b/linux/drivers/media/video/tvp514x.c new file mode 100644 index 000000000..ac9aa40d0 --- /dev/null +++ b/linux/drivers/media/video/tvp514x.c @@ -0,0 +1,1569 @@ +/* + * drivers/media/video/tvp514x.c + * + * TI TVP5146/47 decoder driver + * + * Copyright (C) 2008 Texas Instruments Inc + * Author: Vaibhav Hiremath + * + * Contributors: + * Sivaraj R + * Brijesh R Jadav + * Hardik Shah + * Manjunath Hadli + * Karicheri Muralidharan + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include + +#include "tvp514x_regs.h" + +/* Module Name */ +#define TVP514X_MODULE_NAME "tvp514x" + +/* Private macros for TVP */ +#define I2C_RETRY_COUNT (5) +#define LOCK_RETRY_COUNT (5) +#define LOCK_RETRY_DELAY (200) + +/* Debug functions */ +static int debug; +module_param(debug, bool, 0644); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +#define dump_reg(client, reg, val) \ + do { \ + val = tvp514x_read_reg(client, reg); \ + v4l_info(client, "Reg(0x%.2X): 0x%.2X\n", reg, val); \ + } while (0) + +/** + * enum tvp514x_std - enum for supported standards + */ +enum tvp514x_std { + STD_NTSC_MJ = 0, + STD_PAL_BDGHIN, + STD_INVALID +}; + +/** + * enum tvp514x_state - enum for different decoder states + */ +enum tvp514x_state { + STATE_NOT_DETECTED, + STATE_DETECTED +}; + +/** + * struct tvp514x_std_info - Structure to store standard informations + * @width: Line width in pixels + * @height:Number of active lines + * @video_std: Value to write in REG_VIDEO_STD register + * @standard: v4l2 standard structure information + */ +struct tvp514x_std_info { + unsigned long width; + unsigned long height; + u8 video_std; + struct v4l2_standard standard; +}; + +/** + * struct tvp514x_decoded - TVP5146/47 decoder object + * @v4l2_int_device: Slave handle + * @pdata: Board specific + * @client: I2C client data + * @id: Entry from I2C table + * @ver: Chip version + * @state: TVP5146/47 decoder state - detected or not-detected + * @pix: Current pixel format + * @num_fmts: Number of formats + * @fmt_list: Format list + * @current_std: Current standard + * @num_stds: Number of standards + * @std_list: Standards list + * @route: input and output routing at chip level + */ +struct tvp514x_decoder { + struct v4l2_int_device *v4l2_int_device; + const struct tvp514x_platform_data *pdata; + struct i2c_client *client; + + struct i2c_device_id *id; + + int ver; + enum tvp514x_state state; + + struct v4l2_pix_format pix; + int num_fmts; + const struct v4l2_fmtdesc *fmt_list; + + enum tvp514x_std current_std; + int num_stds; + struct tvp514x_std_info *std_list; + + struct v4l2_routing route; +}; + +/* TVP514x default register values */ +static struct tvp514x_reg tvp514x_reg_list[] = { + {TOK_WRITE, REG_INPUT_SEL, 0x05}, /* Composite selected */ + {TOK_WRITE, REG_AFE_GAIN_CTRL, 0x0F}, + {TOK_WRITE, REG_VIDEO_STD, 0x00}, /* Auto mode */ + {TOK_WRITE, REG_OPERATION_MODE, 0x00}, + {TOK_SKIP, REG_AUTOSWITCH_MASK, 0x3F}, + {TOK_WRITE, REG_COLOR_KILLER, 0x10}, + {TOK_WRITE, REG_LUMA_CONTROL1, 0x00}, + {TOK_WRITE, REG_LUMA_CONTROL2, 0x00}, + {TOK_WRITE, REG_LUMA_CONTROL3, 0x02}, + {TOK_WRITE, REG_BRIGHTNESS, 0x80}, + {TOK_WRITE, REG_CONTRAST, 0x80}, + {TOK_WRITE, REG_SATURATION, 0x80}, + {TOK_WRITE, REG_HUE, 0x00}, + {TOK_WRITE, REG_CHROMA_CONTROL1, 0x00}, + {TOK_WRITE, REG_CHROMA_CONTROL2, 0x0E}, + {TOK_SKIP, 0x0F, 0x00}, /* Reserved */ + {TOK_WRITE, REG_COMP_PR_SATURATION, 0x80}, + {TOK_WRITE, REG_COMP_Y_CONTRAST, 0x80}, + {TOK_WRITE, REG_COMP_PB_SATURATION, 0x80}, + {TOK_SKIP, 0x13, 0x00}, /* Reserved */ + {TOK_WRITE, REG_COMP_Y_BRIGHTNESS, 0x80}, + {TOK_SKIP, 0x15, 0x00}, /* Reserved */ + {TOK_SKIP, REG_AVID_START_PIXEL_LSB, 0x55}, /* NTSC timing */ + {TOK_SKIP, REG_AVID_START_PIXEL_MSB, 0x00}, + {TOK_SKIP, REG_AVID_STOP_PIXEL_LSB, 0x25}, + {TOK_SKIP, REG_AVID_STOP_PIXEL_MSB, 0x03}, + {TOK_SKIP, REG_HSYNC_START_PIXEL_LSB, 0x00}, /* NTSC timing */ + {TOK_SKIP, REG_HSYNC_START_PIXEL_MSB, 0x00}, + {TOK_SKIP, REG_HSYNC_STOP_PIXEL_LSB, 0x40}, + {TOK_SKIP, REG_HSYNC_STOP_PIXEL_MSB, 0x00}, + {TOK_SKIP, REG_VSYNC_START_LINE_LSB, 0x04}, /* NTSC timing */ + {TOK_SKIP, REG_VSYNC_START_LINE_MSB, 0x00}, + {TOK_SKIP, REG_VSYNC_STOP_LINE_LSB, 0x07}, + {TOK_SKIP, REG_VSYNC_STOP_LINE_MSB, 0x00}, + {TOK_SKIP, REG_VBLK_START_LINE_LSB, 0x01}, /* NTSC timing */ + {TOK_SKIP, REG_VBLK_START_LINE_MSB, 0x00}, + {TOK_SKIP, REG_VBLK_STOP_LINE_LSB, 0x15}, + {TOK_SKIP, REG_VBLK_STOP_LINE_MSB, 0x00}, + {TOK_SKIP, 0x26, 0x00}, /* Reserved */ + {TOK_SKIP, 0x27, 0x00}, /* Reserved */ + {TOK_SKIP, REG_FAST_SWTICH_CONTROL, 0xCC}, + {TOK_SKIP, 0x29, 0x00}, /* Reserved */ + {TOK_SKIP, REG_FAST_SWTICH_SCART_DELAY, 0x00}, + {TOK_SKIP, 0x2B, 0x00}, /* Reserved */ + {TOK_SKIP, REG_SCART_DELAY, 0x00}, + {TOK_SKIP, REG_CTI_DELAY, 0x00}, + {TOK_SKIP, REG_CTI_CONTROL, 0x00}, + {TOK_SKIP, 0x2F, 0x00}, /* Reserved */ + {TOK_SKIP, 0x30, 0x00}, /* Reserved */ + {TOK_SKIP, 0x31, 0x00}, /* Reserved */ + {TOK_WRITE, REG_SYNC_CONTROL, 0x00}, /* HS, VS active high */ + {TOK_WRITE, REG_OUTPUT_FORMATTER1, 0x00}, /* 10-bit BT.656 */ + {TOK_WRITE, REG_OUTPUT_FORMATTER2, 0x11}, /* Enable clk & data */ + {TOK_WRITE, REG_OUTPUT_FORMATTER3, 0xEE}, /* Enable AVID & FLD */ + {TOK_WRITE, REG_OUTPUT_FORMATTER4, 0xAF}, /* Enable VS & HS */ + {TOK_WRITE, REG_OUTPUT_FORMATTER5, 0xFF}, + {TOK_WRITE, REG_OUTPUT_FORMATTER6, 0xFF}, + {TOK_WRITE, REG_CLEAR_LOST_LOCK, 0x01}, /* Clear status */ + {TOK_TERM, 0, 0}, +}; + +/* List of image formats supported by TVP5146/47 decoder + * Currently we are using 8 bit mode only, but can be + * extended to 10/20 bit mode. + */ +static const struct v4l2_fmtdesc tvp514x_fmt_list[] = { + { + .index = 0, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .flags = 0, + .description = "8-bit UYVY 4:2:2 Format", + .pixelformat = V4L2_PIX_FMT_UYVY, + }, +}; + +/* + * Supported standards - + * + * Currently supports two standards only, need to add support for rest of the + * modes, like SECAM, etc... + */ +static struct tvp514x_std_info tvp514x_std_list[] = { + /* Standard: STD_NTSC_MJ */ + [STD_NTSC_MJ] = { + .width = NTSC_NUM_ACTIVE_PIXELS, + .height = NTSC_NUM_ACTIVE_LINES, + .video_std = VIDEO_STD_NTSC_MJ_BIT, + .standard = { + .index = 0, + .id = V4L2_STD_NTSC, + .name = "NTSC", + .frameperiod = {1001, 30000}, + .framelines = 525 + }, + /* Standard: STD_PAL_BDGHIN */ + }, + [STD_PAL_BDGHIN] = { + .width = PAL_NUM_ACTIVE_PIXELS, + .height = PAL_NUM_ACTIVE_LINES, + .video_std = VIDEO_STD_PAL_BDGHIN_BIT, + .standard = { + .index = 1, + .id = V4L2_STD_PAL, + .name = "PAL", + .frameperiod = {1, 25}, + .framelines = 625 + }, + }, + /* Standard: need to add for additional standard */ +}; +/* + * Control structure for Auto Gain + * This is temporary data, will get replaced once + * v4l2_ctrl_query_fill supports it. + */ +static const struct v4l2_queryctrl tvp514x_autogain_ctrl = { + .id = V4L2_CID_AUTOGAIN, + .name = "Gain, Automatic", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, +}; + +/* + * Read a value from a register in an TVP5146/47 decoder device. + * Returns value read if successful, or non-zero (-1) otherwise. + */ +static int tvp514x_read_reg(struct i2c_client *client, u8 reg) +{ + int err; + int retry = 0; +read_again: + + err = i2c_smbus_read_byte_data(client, reg); + if (err == -1) { + if (retry <= I2C_RETRY_COUNT) { + v4l_warn(client, "Read: retry ... %d\n", retry); + retry++; + msleep_interruptible(10); + goto read_again; + } + } + + return err; +} + +/* + * Write a value to a register in an TVP5146/47 decoder device. + * Returns zero if successful, or non-zero otherwise. + */ +static int tvp514x_write_reg(struct i2c_client *client, u8 reg, u8 val) +{ + int err; + int retry = 0; +write_again: + + err = i2c_smbus_write_byte_data(client, reg, val); + if (err) { + if (retry <= I2C_RETRY_COUNT) { + v4l_warn(client, "Write: retry ... %d\n", retry); + retry++; + msleep_interruptible(10); + goto write_again; + } + } + + return err; +} + +/* + * tvp514x_write_regs : Initializes a list of TVP5146/47 registers + * if token is TOK_TERM, then entire write operation terminates + * if token is TOK_DELAY, then a delay of 'val' msec is introduced + * if token is TOK_SKIP, then the register write is skipped + * if token is TOK_WRITE, then the register write is performed + * + * reglist - list of registers to be written + * Returns zero if successful, or non-zero otherwise. + */ +static int tvp514x_write_regs(struct i2c_client *client, + const struct tvp514x_reg reglist[]) +{ + int err; + const struct tvp514x_reg *next = reglist; + + for (; next->token != TOK_TERM; next++) { + if (next->token == TOK_DELAY) { + msleep(next->val); + continue; + } + + if (next->token == TOK_SKIP) + continue; + + err = tvp514x_write_reg(client, next->reg, (u8) next->val); + if (err) { + v4l_err(client, "Write failed. Err[%d]\n", err); + return err; + } + } + return 0; +} + +/* + * tvp514x_get_current_std: + * Returns the current standard detected by TVP5146/47 + */ +static enum tvp514x_std tvp514x_get_current_std(struct tvp514x_decoder + *decoder) +{ + u8 std, std_status; + + std = tvp514x_read_reg(decoder->client, REG_VIDEO_STD); + if ((std & VIDEO_STD_MASK) == VIDEO_STD_AUTO_SWITCH_BIT) { + /* use the standard status register */ + std_status = tvp514x_read_reg(decoder->client, + REG_VIDEO_STD_STATUS); + } else + std_status = std; /* use the standard register itself */ + + switch (std_status & VIDEO_STD_MASK) { + case VIDEO_STD_NTSC_MJ_BIT: + return STD_NTSC_MJ; + + case VIDEO_STD_PAL_BDGHIN_BIT: + return STD_PAL_BDGHIN; + + default: + return STD_INVALID; + } + + return STD_INVALID; +} + +/* + * TVP5146/47 register dump function + */ +static void tvp514x_reg_dump(struct tvp514x_decoder *decoder) +{ + u8 value; + + dump_reg(decoder->client, REG_INPUT_SEL, value); + dump_reg(decoder->client, REG_AFE_GAIN_CTRL, value); + dump_reg(decoder->client, REG_VIDEO_STD, value); + dump_reg(decoder->client, REG_OPERATION_MODE, value); + dump_reg(decoder->client, REG_COLOR_KILLER, value); + dump_reg(decoder->client, REG_LUMA_CONTROL1, value); + dump_reg(decoder->client, REG_LUMA_CONTROL2, value); + dump_reg(decoder->client, REG_LUMA_CONTROL3, value); + dump_reg(decoder->client, REG_BRIGHTNESS, value); + dump_reg(decoder->client, REG_CONTRAST, value); + dump_reg(decoder->client, REG_SATURATION, value); + dump_reg(decoder->client, REG_HUE, value); + dump_reg(decoder->client, REG_CHROMA_CONTROL1, value); + dump_reg(decoder->client, REG_CHROMA_CONTROL2, value); + dump_reg(decoder->client, REG_COMP_PR_SATURATION, value); + dump_reg(decoder->client, REG_COMP_Y_CONTRAST, value); + dump_reg(decoder->client, REG_COMP_PB_SATURATION, value); + dump_reg(decoder->client, REG_COMP_Y_BRIGHTNESS, value); + dump_reg(decoder->client, REG_AVID_START_PIXEL_LSB, value); + dump_reg(decoder->client, REG_AVID_START_PIXEL_MSB, value); + dump_reg(decoder->client, REG_AVID_STOP_PIXEL_LSB, value); + dump_reg(decoder->client, REG_AVID_STOP_PIXEL_MSB, value); + dump_reg(decoder->client, REG_HSYNC_START_PIXEL_LSB, value); + dump_reg(decoder->client, REG_HSYNC_START_PIXEL_MSB, value); + dump_reg(decoder->client, REG_HSYNC_STOP_PIXEL_LSB, value); + dump_reg(decoder->client, REG_HSYNC_STOP_PIXEL_MSB, value); + dump_reg(decoder->client, REG_VSYNC_START_LINE_LSB, value); + dump_reg(decoder->client, REG_VSYNC_START_LINE_MSB, value); + dump_reg(decoder->client, REG_VSYNC_STOP_LINE_LSB, value); + dump_reg(decoder->client, REG_VSYNC_STOP_LINE_MSB, value); + dump_reg(decoder->client, REG_VBLK_START_LINE_LSB, value); + dump_reg(decoder->client, REG_VBLK_START_LINE_MSB, value); + dump_reg(decoder->client, REG_VBLK_STOP_LINE_LSB, value); + dump_reg(decoder->client, REG_VBLK_STOP_LINE_MSB, value); + dump_reg(decoder->client, REG_SYNC_CONTROL, value); + dump_reg(decoder->client, REG_OUTPUT_FORMATTER1, value); + dump_reg(decoder->client, REG_OUTPUT_FORMATTER2, value); + dump_reg(decoder->client, REG_OUTPUT_FORMATTER3, value); + dump_reg(decoder->client, REG_OUTPUT_FORMATTER4, value); + dump_reg(decoder->client, REG_OUTPUT_FORMATTER5, value); + dump_reg(decoder->client, REG_OUTPUT_FORMATTER6, value); + dump_reg(decoder->client, REG_CLEAR_LOST_LOCK, value); +} + +/* + * Configure the TVP5146/47 with the current register settings + * Returns zero if successful, or non-zero otherwise. + */ +static int tvp514x_configure(struct tvp514x_decoder *decoder) +{ + int err; + + /* common register initialization */ + err = + tvp514x_write_regs(decoder->client, tvp514x_reg_list); + if (err) + return err; + + if (debug) + tvp514x_reg_dump(decoder); + + return 0; +} + +/* + * Detect if an tvp514x is present, and if so which revision. + * A device is considered to be detected if the chip ID (LSB and MSB) + * registers match the expected values. + * Any value of the rom version register is accepted. + * Returns ENODEV error number if no device is detected, or zero + * if a device is detected. + */ +static int tvp514x_detect(struct tvp514x_decoder *decoder) +{ + u8 chip_id_msb, chip_id_lsb, rom_ver; + + chip_id_msb = tvp514x_read_reg(decoder->client, REG_CHIP_ID_MSB); + chip_id_lsb = tvp514x_read_reg(decoder->client, REG_CHIP_ID_LSB); + rom_ver = tvp514x_read_reg(decoder->client, REG_ROM_VERSION); + + v4l_dbg(1, debug, decoder->client, + "chip id detected msb:0x%x lsb:0x%x rom version:0x%x\n", + chip_id_msb, chip_id_lsb, rom_ver); + if ((chip_id_msb != TVP514X_CHIP_ID_MSB) + || ((chip_id_lsb != TVP5146_CHIP_ID_LSB) + && (chip_id_lsb != TVP5147_CHIP_ID_LSB))) { + /* We didn't read the values we expected, so this must not be + * an TVP5146/47. + */ + v4l_err(decoder->client, + "chip id mismatch msb:0x%x lsb:0x%x\n", + chip_id_msb, chip_id_lsb); + return -ENODEV; + } + + decoder->ver = rom_ver; + decoder->state = STATE_DETECTED; + + v4l_info(decoder->client, + "%s found at 0x%x (%s)\n", decoder->client->name, + decoder->client->addr << 1, + decoder->client->adapter->name); + return 0; +} + +/* + * Following are decoder interface functions implemented by + * TVP5146/47 decoder driver. + */ + +/** + * ioctl_querystd - V4L2 decoder interface handler for VIDIOC_QUERYSTD ioctl + * @s: pointer to standard V4L2 device structure + * @std_id: standard V4L2 std_id ioctl enum + * + * Returns the current standard detected by TVP5146/47. If no active input is + * detected, returns -EINVAL + */ +static int ioctl_querystd(struct v4l2_int_device *s, v4l2_std_id *std_id) +{ + struct tvp514x_decoder *decoder = s->priv; + enum tvp514x_std current_std; + enum tvp514x_input input_sel; + u8 sync_lock_status, lock_mask; + + if (std_id == NULL) + return -EINVAL; + + /* get the current standard */ + current_std = tvp514x_get_current_std(decoder); + if (current_std == STD_INVALID) + return -EINVAL; + + input_sel = decoder->route.input; + + switch (input_sel) { + case INPUT_CVBS_VI1A: + case INPUT_CVBS_VI1B: + case INPUT_CVBS_VI1C: + case INPUT_CVBS_VI2A: + case INPUT_CVBS_VI2B: + case INPUT_CVBS_VI2C: + case INPUT_CVBS_VI3A: + case INPUT_CVBS_VI3B: + case INPUT_CVBS_VI3C: + case INPUT_CVBS_VI4A: + lock_mask = STATUS_CLR_SUBCAR_LOCK_BIT | + STATUS_HORZ_SYNC_LOCK_BIT | + STATUS_VIRT_SYNC_LOCK_BIT; + break; + + case INPUT_SVIDEO_VI2A_VI1A: + case INPUT_SVIDEO_VI2B_VI1B: + case INPUT_SVIDEO_VI2C_VI1C: + case INPUT_SVIDEO_VI2A_VI3A: + case INPUT_SVIDEO_VI2B_VI3B: + case INPUT_SVIDEO_VI2C_VI3C: + case INPUT_SVIDEO_VI4A_VI1A: + case INPUT_SVIDEO_VI4A_VI1B: + case INPUT_SVIDEO_VI4A_VI1C: + case INPUT_SVIDEO_VI4A_VI3A: + case INPUT_SVIDEO_VI4A_VI3B: + case INPUT_SVIDEO_VI4A_VI3C: + lock_mask = STATUS_HORZ_SYNC_LOCK_BIT | + STATUS_VIRT_SYNC_LOCK_BIT; + break; + /*Need to add other interfaces*/ + default: + return -EINVAL; + } + /* check whether signal is locked */ + sync_lock_status = tvp514x_read_reg(decoder->client, REG_STATUS1); + if (lock_mask != (sync_lock_status & lock_mask)) + return -EINVAL; /* No input detected */ + + decoder->current_std = current_std; + *std_id = decoder->std_list[current_std].standard.id; + + v4l_dbg(1, debug, decoder->client, "Current STD: %s", + decoder->std_list[current_std].standard.name); + return 0; +} + +/** + * ioctl_s_std - V4L2 decoder interface handler for VIDIOC_S_STD ioctl + * @s: pointer to standard V4L2 device structure + * @std_id: standard V4L2 v4l2_std_id ioctl enum + * + * If std_id is supported, sets the requested standard. Otherwise, returns + * -EINVAL + */ +static int ioctl_s_std(struct v4l2_int_device *s, v4l2_std_id *std_id) +{ + struct tvp514x_decoder *decoder = s->priv; + int err, i; + + if (std_id == NULL) + return -EINVAL; + + for (i = 0; i < decoder->num_stds; i++) + if (*std_id & decoder->std_list[i].standard.id) + break; + + if ((i == decoder->num_stds) || (i == STD_INVALID)) + return -EINVAL; + + err = tvp514x_write_reg(decoder->client, REG_VIDEO_STD, + decoder->std_list[i].video_std); + if (err) + return err; + + decoder->current_std = i; + tvp514x_reg_list[REG_VIDEO_STD].val = decoder->std_list[i].video_std; + + v4l_dbg(1, debug, decoder->client, "Standard set to: %s", + decoder->std_list[i].standard.name); + return 0; +} + +/** + * ioctl_s_routing - V4L2 decoder interface handler for VIDIOC_S_INPUT ioctl + * @s: pointer to standard V4L2 device structure + * @index: number of the input + * + * If index is valid, selects the requested input. Otherwise, returns -EINVAL if + * the input is not supported or there is no active signal present in the + * selected input. + */ +static int ioctl_s_routing(struct v4l2_int_device *s, + struct v4l2_routing *route) +{ + struct tvp514x_decoder *decoder = s->priv; + int err; + enum tvp514x_input input_sel; + enum tvp514x_output output_sel; + enum tvp514x_std current_std = STD_INVALID; + u8 sync_lock_status, lock_mask; + int try_count = LOCK_RETRY_COUNT; + + if ((!route) || (route->input >= INPUT_INVALID) || + (route->output >= OUTPUT_INVALID)) + return -EINVAL; /* Index out of bound */ + + input_sel = route->input; + output_sel = route->output; + + err = tvp514x_write_reg(decoder->client, REG_INPUT_SEL, input_sel); + if (err) + return err; + + output_sel |= tvp514x_read_reg(decoder->client, + REG_OUTPUT_FORMATTER1) & 0x7; + err = tvp514x_write_reg(decoder->client, REG_OUTPUT_FORMATTER1, + output_sel); + if (err) + return err; + + tvp514x_reg_list[REG_INPUT_SEL].val = input_sel; + tvp514x_reg_list[REG_OUTPUT_FORMATTER1].val = output_sel; + + /* Clear status */ + msleep(LOCK_RETRY_DELAY); + err = + tvp514x_write_reg(decoder->client, REG_CLEAR_LOST_LOCK, 0x01); + if (err) + return err; + + switch (input_sel) { + case INPUT_CVBS_VI1A: + case INPUT_CVBS_VI1B: + case INPUT_CVBS_VI1C: + case INPUT_CVBS_VI2A: + case INPUT_CVBS_VI2B: + case INPUT_CVBS_VI2C: + case INPUT_CVBS_VI3A: + case INPUT_CVBS_VI3B: + case INPUT_CVBS_VI3C: + case INPUT_CVBS_VI4A: + lock_mask = STATUS_CLR_SUBCAR_LOCK_BIT | + STATUS_HORZ_SYNC_LOCK_BIT | + STATUS_VIRT_SYNC_LOCK_BIT; + break; + + case INPUT_SVIDEO_VI2A_VI1A: + case INPUT_SVIDEO_VI2B_VI1B: + case INPUT_SVIDEO_VI2C_VI1C: + case INPUT_SVIDEO_VI2A_VI3A: + case INPUT_SVIDEO_VI2B_VI3B: + case INPUT_SVIDEO_VI2C_VI3C: + case INPUT_SVIDEO_VI4A_VI1A: + case INPUT_SVIDEO_VI4A_VI1B: + case INPUT_SVIDEO_VI4A_VI1C: + case INPUT_SVIDEO_VI4A_VI3A: + case INPUT_SVIDEO_VI4A_VI3B: + case INPUT_SVIDEO_VI4A_VI3C: + lock_mask = STATUS_HORZ_SYNC_LOCK_BIT | + STATUS_VIRT_SYNC_LOCK_BIT; + break; + /*Need to add other interfaces*/ + default: + return -EINVAL; + } + + while (try_count-- > 0) { + /* Allow decoder to sync up with new input */ + msleep(LOCK_RETRY_DELAY); + + /* get the current standard for future reference */ + current_std = tvp514x_get_current_std(decoder); + if (current_std == STD_INVALID) + continue; + + sync_lock_status = tvp514x_read_reg(decoder->client, + REG_STATUS1); + if (lock_mask == (sync_lock_status & lock_mask)) + break; /* Input detected */ + } + + if ((current_std == STD_INVALID) || (try_count < 0)) + return -EINVAL; + + decoder->current_std = current_std; + decoder->route.input = route->input; + decoder->route.output = route->output; + + v4l_dbg(1, debug, decoder->client, + "Input set to: %d, std : %d", + input_sel, current_std); + + return 0; +} + +/** + * ioctl_queryctrl - V4L2 decoder interface handler for VIDIOC_QUERYCTRL ioctl + * @s: pointer to standard V4L2 device structure + * @qctrl: standard V4L2 v4l2_queryctrl structure + * + * If the requested control is supported, returns the control information. + * Otherwise, returns -EINVAL if the control is not supported. + */ +static int +ioctl_queryctrl(struct v4l2_int_device *s, struct v4l2_queryctrl *qctrl) +{ + struct tvp514x_decoder *decoder = s->priv; + int err = -EINVAL; + + if (qctrl == NULL) + return err; + + switch (qctrl->id) { + case V4L2_CID_BRIGHTNESS: + /* Brightness supported is same as standard one (0-255), + * so make use of standard API provided. + */ + err = v4l2_ctrl_query_fill_std(qctrl); + break; + case V4L2_CID_CONTRAST: + case V4L2_CID_SATURATION: + /* Saturation and Contrast supported is - + * Contrast: 0 - 255 (Default - 128) + * Saturation: 0 - 255 (Default - 128) + */ + err = v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 128); + break; + case V4L2_CID_HUE: + /* Hue Supported is - + * Hue - -180 - +180 (Default - 0, Step - +180) + */ + err = v4l2_ctrl_query_fill(qctrl, -180, 180, 180, 0); + break; + case V4L2_CID_AUTOGAIN: + /* Autogain is either 0 or 1*/ + memcpy(qctrl, &tvp514x_autogain_ctrl, + sizeof(struct v4l2_queryctrl)); + err = 0; + break; + default: + v4l_err(decoder->client, + "invalid control id %d\n", qctrl->id); + return err; + } + + v4l_dbg(1, debug, decoder->client, + "Query Control: %s : Min - %d, Max - %d, Def - %d", + qctrl->name, + qctrl->minimum, + qctrl->maximum, + qctrl->default_value); + + return err; +} + +/** + * ioctl_g_ctrl - V4L2 decoder interface handler for VIDIOC_G_CTRL ioctl + * @s: pointer to standard V4L2 device structure + * @ctrl: pointer to v4l2_control structure + * + * If the requested control is supported, returns the control's current + * value from the decoder. Otherwise, returns -EINVAL if the control is not + * supported. + */ +static int +ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl) +{ + struct tvp514x_decoder *decoder = s->priv; + + if (ctrl == NULL) + return -EINVAL; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + ctrl->value = tvp514x_reg_list[REG_BRIGHTNESS].val; + break; + case V4L2_CID_CONTRAST: + ctrl->value = tvp514x_reg_list[REG_CONTRAST].val; + break; + case V4L2_CID_SATURATION: + ctrl->value = tvp514x_reg_list[REG_SATURATION].val; + break; + case V4L2_CID_HUE: + ctrl->value = tvp514x_reg_list[REG_HUE].val; + if (ctrl->value == 0x7F) + ctrl->value = 180; + else if (ctrl->value == 0x80) + ctrl->value = -180; + else + ctrl->value = 0; + + break; + case V4L2_CID_AUTOGAIN: + ctrl->value = tvp514x_reg_list[REG_AFE_GAIN_CTRL].val; + if ((ctrl->value & 0x3) == 3) + ctrl->value = 1; + else + ctrl->value = 0; + + break; + default: + v4l_err(decoder->client, + "invalid control id %d\n", ctrl->id); + return -EINVAL; + } + + v4l_dbg(1, debug, decoder->client, + "Get Control: ID - %d - %d", + ctrl->id, ctrl->value); + return 0; +} + +/** + * ioctl_s_ctrl - V4L2 decoder interface handler for VIDIOC_S_CTRL ioctl + * @s: pointer to standard V4L2 device structure + * @ctrl: pointer to v4l2_control structure + * + * If the requested control is supported, sets the control's current + * value in HW. Otherwise, returns -EINVAL if the control is not supported. + */ +static int +ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl) +{ + struct tvp514x_decoder *decoder = s->priv; + int err = -EINVAL, value; + + if (ctrl == NULL) + return err; + + value = (__s32) ctrl->value; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + if (ctrl->value < 0 || ctrl->value > 255) { + v4l_err(decoder->client, + "invalid brightness setting %d\n", + ctrl->value); + return -ERANGE; + } + err = tvp514x_write_reg(decoder->client, REG_BRIGHTNESS, + value); + if (err) + return err; + tvp514x_reg_list[REG_BRIGHTNESS].val = value; + break; + case V4L2_CID_CONTRAST: + if (ctrl->value < 0 || ctrl->value > 255) { + v4l_err(decoder->client, + "invalid contrast setting %d\n", + ctrl->value); + return -ERANGE; + } + err = tvp514x_write_reg(decoder->client, REG_CONTRAST, + value); + if (err) + return err; + tvp514x_reg_list[REG_CONTRAST].val = value; + break; + case V4L2_CID_SATURATION: + if (ctrl->value < 0 || ctrl->value > 255) { + v4l_err(decoder->client, + "invalid saturation setting %d\n", + ctrl->value); + return -ERANGE; + } + err = tvp514x_write_reg(decoder->client, REG_SATURATION, + value); + if (err) + return err; + tvp514x_reg_list[REG_SATURATION].val = value; + break; + case V4L2_CID_HUE: + if (value == 180) + value = 0x7F; + else if (value == -180) + value = 0x80; + else if (value == 0) + value = 0; + else { + v4l_err(decoder->client, + "invalid hue setting %d\n", + ctrl->value); + return -ERANGE; + } + err = tvp514x_write_reg(decoder->client, REG_HUE, + value); + if (err) + return err; + tvp514x_reg_list[REG_HUE].val = value; + break; + case V4L2_CID_AUTOGAIN: + if (value == 1) + value = 0x0F; + else if (value == 0) + value = 0x0C; + else { + v4l_err(decoder->client, + "invalid auto gain setting %d\n", + ctrl->value); + return -ERANGE; + } + err = tvp514x_write_reg(decoder->client, REG_AFE_GAIN_CTRL, + value); + if (err) + return err; + tvp514x_reg_list[REG_AFE_GAIN_CTRL].val = value; + break; + default: + v4l_err(decoder->client, + "invalid control id %d\n", ctrl->id); + return err; + } + + v4l_dbg(1, debug, decoder->client, + "Set Control: ID - %d - %d", + ctrl->id, ctrl->value); + + return err; +} + +/** + * ioctl_enum_fmt_cap - Implement the CAPTURE buffer VIDIOC_ENUM_FMT ioctl + * @s: pointer to standard V4L2 device structure + * @fmt: standard V4L2 VIDIOC_ENUM_FMT ioctl structure + * + * Implement the VIDIOC_ENUM_FMT ioctl to enumerate supported formats + */ +static int +ioctl_enum_fmt_cap(struct v4l2_int_device *s, struct v4l2_fmtdesc *fmt) +{ + struct tvp514x_decoder *decoder = s->priv; + int index; + + if (fmt == NULL) + return -EINVAL; + + index = fmt->index; + if ((index >= decoder->num_fmts) || (index < 0)) + return -EINVAL; /* Index out of bound */ + + if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; /* only capture is supported */ + + memcpy(fmt, &decoder->fmt_list[index], + sizeof(struct v4l2_fmtdesc)); + + v4l_dbg(1, debug, decoder->client, + "Current FMT: index - %d (%s)", + decoder->fmt_list[index].index, + decoder->fmt_list[index].description); + return 0; +} + +/** + * ioctl_try_fmt_cap - Implement the CAPTURE buffer VIDIOC_TRY_FMT ioctl + * @s: pointer to standard V4L2 device structure + * @f: pointer to standard V4L2 VIDIOC_TRY_FMT ioctl structure + * + * Implement the VIDIOC_TRY_FMT ioctl for the CAPTURE buffer type. This + * ioctl is used to negotiate the image capture size and pixel format + * without actually making it take effect. + */ +static int +ioctl_try_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) +{ + struct tvp514x_decoder *decoder = s->priv; + int ifmt; + struct v4l2_pix_format *pix; + enum tvp514x_std current_std; + + if (f == NULL) + return -EINVAL; + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + pix = &f->fmt.pix; + + /* Calculate height and width based on current standard */ + current_std = tvp514x_get_current_std(decoder); + if (current_std == STD_INVALID) + return -EINVAL; + + decoder->current_std = current_std; + pix->width = decoder->std_list[current_std].width; + pix->height = decoder->std_list[current_std].height; + + for (ifmt = 0; ifmt < decoder->num_fmts; ifmt++) { + if (pix->pixelformat == + decoder->fmt_list[ifmt].pixelformat) + break; + } + if (ifmt == decoder->num_fmts) + ifmt = 0; /* None of the format matched, select default */ + pix->pixelformat = decoder->fmt_list[ifmt].pixelformat; + + pix->field = V4L2_FIELD_INTERLACED; + pix->bytesperline = pix->width * 2; + pix->sizeimage = pix->bytesperline * pix->height; + pix->colorspace = V4L2_COLORSPACE_SMPTE170M; + pix->priv = 0; + + v4l_dbg(1, debug, decoder->client, + "Try FMT: pixelformat - %s, bytesperline - %d" + "Width - %d, Height - %d", + decoder->fmt_list[ifmt].description, pix->bytesperline, + pix->width, pix->height); + return 0; +} + +/** + * ioctl_s_fmt_cap - V4L2 decoder interface handler for VIDIOC_S_FMT ioctl + * @s: pointer to standard V4L2 device structure + * @f: pointer to standard V4L2 VIDIOC_S_FMT ioctl structure + * + * If the requested format is supported, configures the HW to use that + * format, returns error code if format not supported or HW can't be + * correctly configured. + */ +static int +ioctl_s_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) +{ + struct tvp514x_decoder *decoder = s->priv; + struct v4l2_pix_format *pix; + int rval; + + if (f == NULL) + return -EINVAL; + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; /* only capture is supported */ + + pix = &f->fmt.pix; + rval = ioctl_try_fmt_cap(s, f); + if (rval) + return rval; + + decoder->pix = *pix; + + return rval; +} + +/** + * ioctl_g_fmt_cap - V4L2 decoder interface handler for ioctl_g_fmt_cap + * @s: pointer to standard V4L2 device structure + * @f: pointer to standard V4L2 v4l2_format structure + * + * Returns the decoder's current pixel format in the v4l2_format + * parameter. + */ +static int +ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) +{ + struct tvp514x_decoder *decoder = s->priv; + + if (f == NULL) + return -EINVAL; + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; /* only capture is supported */ + + f->fmt.pix = decoder->pix; + + v4l_dbg(1, debug, decoder->client, + "Current FMT: bytesperline - %d" + "Width - %d, Height - %d", + decoder->pix.bytesperline, + decoder->pix.width, decoder->pix.height); + return 0; +} + +/** + * ioctl_g_parm - V4L2 decoder interface handler for VIDIOC_G_PARM ioctl + * @s: pointer to standard V4L2 device structure + * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure + * + * Returns the decoder's video CAPTURE parameters. + */ +static int +ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) +{ + struct tvp514x_decoder *decoder = s->priv; + struct v4l2_captureparm *cparm; + enum tvp514x_std current_std; + + if (a == NULL) + return -EINVAL; + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; /* only capture is supported */ + + memset(a, 0, sizeof(*a)); + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + /* get the current standard */ + current_std = tvp514x_get_current_std(decoder); + if (current_std == STD_INVALID) + return -EINVAL; + + decoder->current_std = current_std; + + cparm = &a->parm.capture; + cparm->capability = V4L2_CAP_TIMEPERFRAME; + cparm->timeperframe = + decoder->std_list[current_std].standard.frameperiod; + + return 0; +} + +/** + * ioctl_s_parm - V4L2 decoder interface handler for VIDIOC_S_PARM ioctl + * @s: pointer to standard V4L2 device structure + * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure + * + * Configures the decoder to use the input parameters, if possible. If + * not possible, returns the appropriate error code. + */ +static int +ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) +{ + struct tvp514x_decoder *decoder = s->priv; + struct v4l2_fract *timeperframe; + enum tvp514x_std current_std; + + if (a == NULL) + return -EINVAL; + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; /* only capture is supported */ + + timeperframe = &a->parm.capture.timeperframe; + + /* get the current standard */ + current_std = tvp514x_get_current_std(decoder); + if (current_std == STD_INVALID) + return -EINVAL; + + decoder->current_std = current_std; + + *timeperframe = + decoder->std_list[current_std].standard.frameperiod; + + return 0; +} + +/** + * ioctl_g_ifparm - V4L2 decoder interface handler for vidioc_int_g_ifparm_num + * @s: pointer to standard V4L2 device structure + * @p: pointer to standard V4L2 vidioc_int_g_ifparm_num ioctl structure + * + * Gets slave interface parameters. + * Calculates the required xclk value to support the requested + * clock parameters in p. This value is returned in the p + * parameter. + */ +static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p) +{ + struct tvp514x_decoder *decoder = s->priv; + int rval; + + if (p == NULL) + return -EINVAL; + + if (NULL == decoder->pdata->ifparm) + return -EINVAL; + + rval = decoder->pdata->ifparm(p); + if (rval) { + v4l_err(decoder->client, "g_ifparm.Err[%d]\n", rval); + return rval; + } + + p->u.bt656.clock_curr = TVP514X_XCLK_BT656; + + return 0; +} + +/** + * ioctl_g_priv - V4L2 decoder interface handler for vidioc_int_g_priv_num + * @s: pointer to standard V4L2 device structure + * @p: void pointer to hold decoder's private data address + * + * Returns device's (decoder's) private data area address in p parameter + */ +static int ioctl_g_priv(struct v4l2_int_device *s, void *p) +{ + struct tvp514x_decoder *decoder = s->priv; + + if (NULL == decoder->pdata->priv_data_set) + return -EINVAL; + + return decoder->pdata->priv_data_set(p); +} + +/** + * ioctl_s_power - V4L2 decoder interface handler for vidioc_int_s_power_num + * @s: pointer to standard V4L2 device structure + * @on: power state to which device is to be set + * + * Sets devices power state to requrested state, if possible. + */ +static int ioctl_s_power(struct v4l2_int_device *s, enum v4l2_power on) +{ + struct tvp514x_decoder *decoder = s->priv; + int err = 0; + + switch (on) { + case V4L2_POWER_OFF: + /* Power Down Sequence */ + err = + tvp514x_write_reg(decoder->client, REG_OPERATION_MODE, + 0x01); + /* Disable mux for TVP5146/47 decoder data path */ + if (decoder->pdata->power_set) + err |= decoder->pdata->power_set(on); + decoder->state = STATE_NOT_DETECTED; + break; + + case V4L2_POWER_STANDBY: + if (decoder->pdata->power_set) + err = decoder->pdata->power_set(on); + break; + + case V4L2_POWER_ON: + /* Enable mux for TVP5146/47 decoder data path */ + if ((decoder->pdata->power_set) && + (decoder->state == STATE_NOT_DETECTED)) { + int i; + struct tvp514x_init_seq *int_seq = + (struct tvp514x_init_seq *) + decoder->id->driver_data; + + err = decoder->pdata->power_set(on); + + /* Power Up Sequence */ + for (i = 0; i < int_seq->no_regs; i++) { + err |= tvp514x_write_reg(decoder->client, + int_seq->init_reg_seq[i].reg, + int_seq->init_reg_seq[i].val); + } + /* Detect the sensor is not already detected */ + err |= tvp514x_detect(decoder); + if (err) { + v4l_err(decoder->client, + "Unable to detect decoder\n"); + return err; + } + } + err |= tvp514x_configure(decoder); + break; + + default: + err = -ENODEV; + break; + } + + return err; +} + +/** + * ioctl_init - V4L2 decoder interface handler for VIDIOC_INT_INIT + * @s: pointer to standard V4L2 device structure + * + * Initialize the decoder device (calls tvp514x_configure()) + */ +static int ioctl_init(struct v4l2_int_device *s) +{ + struct tvp514x_decoder *decoder = s->priv; + + /* Set default standard to auto */ + tvp514x_reg_list[REG_VIDEO_STD].val = + VIDEO_STD_AUTO_SWITCH_BIT; + + return tvp514x_configure(decoder); +} + +/** + * ioctl_dev_exit - V4L2 decoder interface handler for vidioc_int_dev_exit_num + * @s: pointer to standard V4L2 device structure + * + * Delinitialise the dev. at slave detach. The complement of ioctl_dev_init. + */ +static int ioctl_dev_exit(struct v4l2_int_device *s) +{ + return 0; +} + +/** + * ioctl_dev_init - V4L2 decoder interface handler for vidioc_int_dev_init_num + * @s: pointer to standard V4L2 device structure + * + * Initialise the device when slave attaches to the master. Returns 0 if + * TVP5146/47 device could be found, otherwise returns appropriate error. + */ +static int ioctl_dev_init(struct v4l2_int_device *s) +{ + struct tvp514x_decoder *decoder = s->priv; + int err; + + err = tvp514x_detect(decoder); + if (err < 0) { + v4l_err(decoder->client, + "Unable to detect decoder\n"); + return err; + } + + v4l_info(decoder->client, + "chip version 0x%.2x detected\n", decoder->ver); + + return 0; +} + +static struct v4l2_int_ioctl_desc tvp514x_ioctl_desc[] = { + {vidioc_int_dev_init_num, (v4l2_int_ioctl_func*) ioctl_dev_init}, + {vidioc_int_dev_exit_num, (v4l2_int_ioctl_func*) ioctl_dev_exit}, + {vidioc_int_s_power_num, (v4l2_int_ioctl_func*) ioctl_s_power}, + {vidioc_int_g_priv_num, (v4l2_int_ioctl_func*) ioctl_g_priv}, + {vidioc_int_g_ifparm_num, (v4l2_int_ioctl_func*) ioctl_g_ifparm}, + {vidioc_int_init_num, (v4l2_int_ioctl_func*) ioctl_init}, + {vidioc_int_enum_fmt_cap_num, + (v4l2_int_ioctl_func *) ioctl_enum_fmt_cap}, + {vidioc_int_try_fmt_cap_num, + (v4l2_int_ioctl_func *) ioctl_try_fmt_cap}, + {vidioc_int_g_fmt_cap_num, + (v4l2_int_ioctl_func *) ioctl_g_fmt_cap}, + {vidioc_int_s_fmt_cap_num, + (v4l2_int_ioctl_func *) ioctl_s_fmt_cap}, + {vidioc_int_g_parm_num, (v4l2_int_ioctl_func *) ioctl_g_parm}, + {vidioc_int_s_parm_num, (v4l2_int_ioctl_func *) ioctl_s_parm}, + {vidioc_int_queryctrl_num, + (v4l2_int_ioctl_func *) ioctl_queryctrl}, + {vidioc_int_g_ctrl_num, (v4l2_int_ioctl_func *) ioctl_g_ctrl}, + {vidioc_int_s_ctrl_num, (v4l2_int_ioctl_func *) ioctl_s_ctrl}, + {vidioc_int_querystd_num, (v4l2_int_ioctl_func *) ioctl_querystd}, + {vidioc_int_s_std_num, (v4l2_int_ioctl_func *) ioctl_s_std}, + {vidioc_int_s_video_routing_num, + (v4l2_int_ioctl_func *) ioctl_s_routing}, +}; + +static struct v4l2_int_slave tvp514x_slave = { + .ioctls = tvp514x_ioctl_desc, + .num_ioctls = ARRAY_SIZE(tvp514x_ioctl_desc), +}; + +static struct tvp514x_decoder tvp514x_dev = { + .state = STATE_NOT_DETECTED, + + .fmt_list = tvp514x_fmt_list, + .num_fmts = ARRAY_SIZE(tvp514x_fmt_list), + + .pix = { /* Default to NTSC 8-bit YUV 422 */ + .width = NTSC_NUM_ACTIVE_PIXELS, + .height = NTSC_NUM_ACTIVE_LINES, + .pixelformat = V4L2_PIX_FMT_UYVY, + .field = V4L2_FIELD_INTERLACED, + .bytesperline = NTSC_NUM_ACTIVE_PIXELS * 2, + .sizeimage = + NTSC_NUM_ACTIVE_PIXELS * 2 * NTSC_NUM_ACTIVE_LINES, + .colorspace = V4L2_COLORSPACE_SMPTE170M, + }, + + .current_std = STD_NTSC_MJ, + .std_list = tvp514x_std_list, + .num_stds = ARRAY_SIZE(tvp514x_std_list), + +}; + +static struct v4l2_int_device tvp514x_int_device = { + .module = THIS_MODULE, + .name = TVP514X_MODULE_NAME, + .priv = &tvp514x_dev, + .type = v4l2_int_type_slave, + .u = { + .slave = &tvp514x_slave, + }, +}; + +/** + * tvp514x_probe - decoder driver i2c probe handler + * @client: i2c driver client device structure + * + * Register decoder as an i2c client device and V4L2 + * device. + */ +static int +tvp514x_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct tvp514x_decoder *decoder = &tvp514x_dev; + int err; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + decoder->pdata = client->dev.platform_data; + if (!decoder->pdata) { + v4l_err(client, "No platform data\n!!"); + return -ENODEV; + } + /* + * Fetch platform specific data, and configure the + * tvp514x_reg_list[] accordingly. Since this is one + * time configuration, no need to preserve. + */ + tvp514x_reg_list[REG_OUTPUT_FORMATTER2].val |= + (decoder->pdata->clk_polarity << 1); + tvp514x_reg_list[REG_SYNC_CONTROL].val |= + ((decoder->pdata->hs_polarity << 2) | + (decoder->pdata->vs_polarity << 3)); + /* + * Save the id data, required for power up sequence + */ + decoder->id = (struct i2c_device_id *)id; + /* Attach to Master */ + strcpy(tvp514x_int_device.u.slave->attach_to, decoder->pdata->master); + decoder->v4l2_int_device = &tvp514x_int_device; + decoder->client = client; + i2c_set_clientdata(client, decoder); + + /* Register with V4L2 layer as slave device */ + err = v4l2_int_device_register(decoder->v4l2_int_device); + if (err) { + i2c_set_clientdata(client, NULL); + v4l_err(client, + "Unable to register to v4l2. Err[%d]\n", err); + + } else + v4l_info(client, "Registered to v4l2 master %s!!\n", + decoder->pdata->master); + + return 0; +} + +/** + * tvp514x_remove - decoder driver i2c remove handler + * @client: i2c driver client device structure + * + * Unregister decoder as an i2c client device and V4L2 + * device. Complement of tvp514x_probe(). + */ +static int __exit tvp514x_remove(struct i2c_client *client) +{ + struct tvp514x_decoder *decoder = i2c_get_clientdata(client); + + if (!client->adapter) + return -ENODEV; /* our client isn't attached */ + + v4l2_int_device_unregister(decoder->v4l2_int_device); + i2c_set_clientdata(client, NULL); + + return 0; +} +/* + * TVP5146 Init/Power on Sequence + */ +static const struct tvp514x_reg tvp5146_init_reg_seq[] = { + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x02}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0x80}, + {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x60}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xB0}, + {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01}, + {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x00}, + {TOK_WRITE, REG_OPERATION_MODE, 0x01}, + {TOK_WRITE, REG_OPERATION_MODE, 0x00}, +}; +static const struct tvp514x_init_seq tvp5146_init = { + .no_regs = ARRAY_SIZE(tvp5146_init_reg_seq), + .init_reg_seq = tvp5146_init_reg_seq, +}; +/* + * TVP5147 Init/Power on Sequence + */ +static const struct tvp514x_reg tvp5147_init_reg_seq[] = { + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x02}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0x80}, + {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x60}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xB0}, + {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x16}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xA0}, + {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x16}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x60}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, + {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xB0}, + {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x00}, + {TOK_WRITE, REG_OPERATION_MODE, 0x01}, + {TOK_WRITE, REG_OPERATION_MODE, 0x00}, +}; +static const struct tvp514x_init_seq tvp5147_init = { + .no_regs = ARRAY_SIZE(tvp5147_init_reg_seq), + .init_reg_seq = tvp5147_init_reg_seq, +}; +/* + * TVP5146M2/TVP5147M1 Init/Power on Sequence + */ +static const struct tvp514x_reg tvp514xm_init_reg_seq[] = { + {TOK_WRITE, REG_OPERATION_MODE, 0x01}, + {TOK_WRITE, REG_OPERATION_MODE, 0x00}, +}; +static const struct tvp514x_init_seq tvp514xm_init = { + .no_regs = ARRAY_SIZE(tvp514xm_init_reg_seq), + .init_reg_seq = tvp514xm_init_reg_seq, +}; +/* + * I2C Device Table - + * + * name - Name of the actual device/chip. + * driver_data - Driver data + */ +static const struct i2c_device_id tvp514x_id[] = { + {"tvp5146", (unsigned long)&tvp5146_init}, + {"tvp5146m2", (unsigned long)&tvp514xm_init}, + {"tvp5147", (unsigned long)&tvp5147_init}, + {"tvp5147m1", (unsigned long)&tvp514xm_init}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, tvp514x_id); + +static struct i2c_driver tvp514x_i2c_driver = { + .driver = { + .name = TVP514X_MODULE_NAME, + .owner = THIS_MODULE, + }, + .probe = tvp514x_probe, + .remove = __exit_p(tvp514x_remove), + .id_table = tvp514x_id, +}; + +/** + * tvp514x_init + * + * Module init function + */ +static int __init tvp514x_init(void) +{ + return i2c_add_driver(&tvp514x_i2c_driver); +} + +/** + * tvp514x_cleanup + * + * Module exit function + */ +static void __exit tvp514x_cleanup(void) +{ + i2c_del_driver(&tvp514x_i2c_driver); +} + +module_init(tvp514x_init); +module_exit(tvp514x_cleanup); + +MODULE_AUTHOR("Texas Instruments"); +MODULE_DESCRIPTION("TVP514X linux decoder driver"); +MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/video/tvp514x_regs.h b/linux/drivers/media/video/tvp514x_regs.h new file mode 100644 index 000000000..351620aee --- /dev/null +++ b/linux/drivers/media/video/tvp514x_regs.h @@ -0,0 +1,297 @@ +/* + * drivers/media/video/tvp514x_regs.h + * + * Copyright (C) 2008 Texas Instruments Inc + * Author: Vaibhav Hiremath + * + * Contributors: + * Sivaraj R + * Brijesh R Jadav + * Hardik Shah + * Manjunath Hadli + * Karicheri Muralidharan + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _TVP514X_REGS_H +#define _TVP514X_REGS_H + +/* + * TVP5146/47 registers + */ +#define REG_INPUT_SEL (0x00) +#define REG_AFE_GAIN_CTRL (0x01) +#define REG_VIDEO_STD (0x02) +#define REG_OPERATION_MODE (0x03) +#define REG_AUTOSWITCH_MASK (0x04) + +#define REG_COLOR_KILLER (0x05) +#define REG_LUMA_CONTROL1 (0x06) +#define REG_LUMA_CONTROL2 (0x07) +#define REG_LUMA_CONTROL3 (0x08) + +#define REG_BRIGHTNESS (0x09) +#define REG_CONTRAST (0x0A) +#define REG_SATURATION (0x0B) +#define REG_HUE (0x0C) + +#define REG_CHROMA_CONTROL1 (0x0D) +#define REG_CHROMA_CONTROL2 (0x0E) + +/* 0x0F Reserved */ + +#define REG_COMP_PR_SATURATION (0x10) +#define REG_COMP_Y_CONTRAST (0x11) +#define REG_COMP_PB_SATURATION (0x12) + +/* 0x13 Reserved */ + +#define REG_COMP_Y_BRIGHTNESS (0x14) + +/* 0x15 Reserved */ + +#define REG_AVID_START_PIXEL_LSB (0x16) +#define REG_AVID_START_PIXEL_MSB (0x17) +#define REG_AVID_STOP_PIXEL_LSB (0x18) +#define REG_AVID_STOP_PIXEL_MSB (0x19) + +#define REG_HSYNC_START_PIXEL_LSB (0x1A) +#define REG_HSYNC_START_PIXEL_MSB (0x1B) +#define REG_HSYNC_STOP_PIXEL_LSB (0x1C) +#define REG_HSYNC_STOP_PIXEL_MSB (0x1D) + +#define REG_VSYNC_START_LINE_LSB (0x1E) +#define REG_VSYNC_START_LINE_MSB (0x1F) +#define REG_VSYNC_STOP_LINE_LSB (0x20) +#define REG_VSYNC_STOP_LINE_MSB (0x21) + +#define REG_VBLK_START_LINE_LSB (0x22) +#define REG_VBLK_START_LINE_MSB (0x23) +#define REG_VBLK_STOP_LINE_LSB (0x24) +#define REG_VBLK_STOP_LINE_MSB (0x25) + +/* 0x26 - 0x27 Reserved */ + +#define REG_FAST_SWTICH_CONTROL (0x28) + +/* 0x29 Reserved */ + +#define REG_FAST_SWTICH_SCART_DELAY (0x2A) + +/* 0x2B Reserved */ + +#define REG_SCART_DELAY (0x2C) +#define REG_CTI_DELAY (0x2D) +#define REG_CTI_CONTROL (0x2E) + +/* 0x2F - 0x31 Reserved */ + +#define REG_SYNC_CONTROL (0x32) +#define REG_OUTPUT_FORMATTER1 (0x33) +#define REG_OUTPUT_FORMATTER2 (0x34) +#define REG_OUTPUT_FORMATTER3 (0x35) +#define REG_OUTPUT_FORMATTER4 (0x36) +#define REG_OUTPUT_FORMATTER5 (0x37) +#define REG_OUTPUT_FORMATTER6 (0x38) +#define REG_CLEAR_LOST_LOCK (0x39) + +#define REG_STATUS1 (0x3A) +#define REG_STATUS2 (0x3B) + +#define REG_AGC_GAIN_STATUS_LSB (0x3C) +#define REG_AGC_GAIN_STATUS_MSB (0x3D) + +/* 0x3E Reserved */ + +#define REG_VIDEO_STD_STATUS (0x3F) +#define REG_GPIO_INPUT1 (0x40) +#define REG_GPIO_INPUT2 (0x41) + +/* 0x42 - 0x45 Reserved */ + +#define REG_AFE_COARSE_GAIN_CH1 (0x46) +#define REG_AFE_COARSE_GAIN_CH2 (0x47) +#define REG_AFE_COARSE_GAIN_CH3 (0x48) +#define REG_AFE_COARSE_GAIN_CH4 (0x49) + +#define REG_AFE_FINE_GAIN_PB_B_LSB (0x4A) +#define REG_AFE_FINE_GAIN_PB_B_MSB (0x4B) +#define REG_AFE_FINE_GAIN_Y_G_CHROMA_LSB (0x4C) +#define REG_AFE_FINE_GAIN_Y_G_CHROMA_MSB (0x4D) +#define REG_AFE_FINE_GAIN_PR_R_LSB (0x4E) +#define REG_AFE_FINE_GAIN_PR_R_MSB (0x4F) +#define REG_AFE_FINE_GAIN_CVBS_LUMA_LSB (0x50) +#define REG_AFE_FINE_GAIN_CVBS_LUMA_MSB (0x51) + +/* 0x52 - 0x68 Reserved */ + +#define REG_FBIT_VBIT_CONTROL1 (0x69) + +/* 0x6A - 0x6B Reserved */ + +#define REG_BACKEND_AGC_CONTROL (0x6C) + +/* 0x6D - 0x6E Reserved */ + +#define REG_AGC_DECREMENT_SPEED_CONTROL (0x6F) +#define REG_ROM_VERSION (0x70) + +/* 0x71 - 0x73 Reserved */ + +#define REG_AGC_WHITE_PEAK_PROCESSING (0x74) +#define REG_FBIT_VBIT_CONTROL2 (0x75) +#define REG_VCR_TRICK_MODE_CONTROL (0x76) +#define REG_HORIZONTAL_SHAKE_INCREMENT (0x77) +#define REG_AGC_INCREMENT_SPEED (0x78) +#define REG_AGC_INCREMENT_DELAY (0x79) + +/* 0x7A - 0x7F Reserved */ + +#define REG_CHIP_ID_MSB (0x80) +#define REG_CHIP_ID_LSB (0x81) + +/* 0x82 Reserved */ + +#define REG_CPLL_SPEED_CONTROL (0x83) + +/* 0x84 - 0x96 Reserved */ + +#define REG_STATUS_REQUEST (0x97) + +/* 0x98 - 0x99 Reserved */ + +#define REG_VERTICAL_LINE_COUNT_LSB (0x9A) +#define REG_VERTICAL_LINE_COUNT_MSB (0x9B) + +/* 0x9C - 0x9D Reserved */ + +#define REG_AGC_DECREMENT_DELAY (0x9E) + +/* 0x9F - 0xB0 Reserved */ + +#define REG_VDP_TTX_FILTER_1_MASK1 (0xB1) +#define REG_VDP_TTX_FILTER_1_MASK2 (0xB2) +#define REG_VDP_TTX_FILTER_1_MASK3 (0xB3) +#define REG_VDP_TTX_FILTER_1_MASK4 (0xB4) +#define REG_VDP_TTX_FILTER_1_MASK5 (0xB5) +#define REG_VDP_TTX_FILTER_2_MASK1 (0xB6) +#define REG_VDP_TTX_FILTER_2_MASK2 (0xB7) +#define REG_VDP_TTX_FILTER_2_MASK3 (0xB8) +#define REG_VDP_TTX_FILTER_2_MASK4 (0xB9) +#define REG_VDP_TTX_FILTER_2_MASK5 (0xBA) +#define REG_VDP_TTX_FILTER_CONTROL (0xBB) +#define REG_VDP_FIFO_WORD_COUNT (0xBC) +#define REG_VDP_FIFO_INTERRUPT_THRLD (0xBD) + +/* 0xBE Reserved */ + +#define REG_VDP_FIFO_RESET (0xBF) +#define REG_VDP_FIFO_OUTPUT_CONTROL (0xC0) +#define REG_VDP_LINE_NUMBER_INTERRUPT (0xC1) +#define REG_VDP_PIXEL_ALIGNMENT_LSB (0xC2) +#define REG_VDP_PIXEL_ALIGNMENT_MSB (0xC3) + +/* 0xC4 - 0xD5 Reserved */ + +#define REG_VDP_LINE_START (0xD6) +#define REG_VDP_LINE_STOP (0xD7) +#define REG_VDP_GLOBAL_LINE_MODE (0xD8) +#define REG_VDP_FULL_FIELD_ENABLE (0xD9) +#define REG_VDP_FULL_FIELD_MODE (0xDA) + +/* 0xDB - 0xDF Reserved */ + +#define REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR (0xE0) +#define REG_VBUS_DATA_ACCESS_VBUS_ADDR_INCR (0xE1) +#define REG_FIFO_READ_DATA (0xE2) + +/* 0xE3 - 0xE7 Reserved */ + +#define REG_VBUS_ADDRESS_ACCESS1 (0xE8) +#define REG_VBUS_ADDRESS_ACCESS2 (0xE9) +#define REG_VBUS_ADDRESS_ACCESS3 (0xEA) + +/* 0xEB - 0xEF Reserved */ + +#define REG_INTERRUPT_RAW_STATUS0 (0xF0) +#define REG_INTERRUPT_RAW_STATUS1 (0xF1) +#define REG_INTERRUPT_STATUS0 (0xF2) +#define REG_INTERRUPT_STATUS1 (0xF3) +#define REG_INTERRUPT_MASK0 (0xF4) +#define REG_INTERRUPT_MASK1 (0xF5) +#define REG_INTERRUPT_CLEAR0 (0xF6) +#define REG_INTERRUPT_CLEAR1 (0xF7) + +/* 0xF8 - 0xFF Reserved */ + +/* + * Mask and bit definitions of TVP5146/47 registers + */ +/* The ID values we are looking for */ +#define TVP514X_CHIP_ID_MSB (0x51) +#define TVP5146_CHIP_ID_LSB (0x46) +#define TVP5147_CHIP_ID_LSB (0x47) + +#define VIDEO_STD_MASK (0x07) +#define VIDEO_STD_AUTO_SWITCH_BIT (0x00) +#define VIDEO_STD_NTSC_MJ_BIT (0x01) +#define VIDEO_STD_PAL_BDGHIN_BIT (0x02) +#define VIDEO_STD_PAL_M_BIT (0x03) +#define VIDEO_STD_PAL_COMBINATION_N_BIT (0x04) +#define VIDEO_STD_NTSC_4_43_BIT (0x05) +#define VIDEO_STD_SECAM_BIT (0x06) +#define VIDEO_STD_PAL_60_BIT (0x07) + +/* + * Status bit + */ +#define STATUS_TV_VCR_BIT (1<<0) +#define STATUS_HORZ_SYNC_LOCK_BIT (1<<1) +#define STATUS_VIRT_SYNC_LOCK_BIT (1<<2) +#define STATUS_CLR_SUBCAR_LOCK_BIT (1<<3) +#define STATUS_LOST_LOCK_DETECT_BIT (1<<4) +#define STATUS_FEILD_RATE_BIT (1<<5) +#define STATUS_LINE_ALTERNATING_BIT (1<<6) +#define STATUS_PEAK_WHITE_DETECT_BIT (1<<7) + +/* Tokens for register write */ +#define TOK_WRITE (0) /* token for write operation */ +#define TOK_TERM (1) /* terminating token */ +#define TOK_DELAY (2) /* delay token for reg list */ +#define TOK_SKIP (3) /* token to skip a register */ +/** + * struct tvp514x_reg - Structure for TVP5146/47 register initialization values + * @token - Token: TOK_WRITE, TOK_TERM etc.. + * @reg - Register offset + * @val - Register Value for TOK_WRITE or delay in ms for TOK_DELAY + */ +struct tvp514x_reg { + u8 token; + u8 reg; + u32 val; +}; + +/** + * struct tvp514x_init_seq - Structure for TVP5146/47/46M2/47M1 power up + * Sequence. + * @ no_regs - Number of registers to write for power up sequence. + * @ init_reg_seq - Array of registers and respective value to write. + */ +struct tvp514x_init_seq { + unsigned int no_regs; + const struct tvp514x_reg *init_reg_seq; +}; +#endif /* ifndef _TVP514X_REGS_H */ diff --git a/linux/include/media/tvp514x.h b/linux/include/media/tvp514x.h new file mode 100644 index 000000000..5e7ee968c --- /dev/null +++ b/linux/include/media/tvp514x.h @@ -0,0 +1,118 @@ +/* + * drivers/media/video/tvp514x.h + * + * Copyright (C) 2008 Texas Instruments Inc + * Author: Vaibhav Hiremath + * + * Contributors: + * Sivaraj R + * Brijesh R Jadav + * Hardik Shah + * Manjunath Hadli + * Karicheri Muralidharan + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _TVP514X_H +#define _TVP514X_H + +/* + * Other macros + */ +#define TVP514X_MODULE_NAME "tvp514x" + +#define TVP514X_XCLK_BT656 (27000000) + +/* Number of pixels and number of lines per frame for different standards */ +#define NTSC_NUM_ACTIVE_PIXELS (720) +#define NTSC_NUM_ACTIVE_LINES (480) +#define PAL_NUM_ACTIVE_PIXELS (720) +#define PAL_NUM_ACTIVE_LINES (576) + +/** + * enum tvp514x_input - enum for different decoder input pin + * configuration. + */ +enum tvp514x_input { + /* + * CVBS input selection + */ + INPUT_CVBS_VI1A = 0x0, + INPUT_CVBS_VI1B, + INPUT_CVBS_VI1C, + INPUT_CVBS_VI2A = 0x04, + INPUT_CVBS_VI2B, + INPUT_CVBS_VI2C, + INPUT_CVBS_VI3A = 0x08, + INPUT_CVBS_VI3B, + INPUT_CVBS_VI3C, + INPUT_CVBS_VI4A = 0x0C, + /* + * S-Video input selection + */ + INPUT_SVIDEO_VI2A_VI1A = 0x44, + INPUT_SVIDEO_VI2B_VI1B, + INPUT_SVIDEO_VI2C_VI1C, + INPUT_SVIDEO_VI2A_VI3A = 0x54, + INPUT_SVIDEO_VI2B_VI3B, + INPUT_SVIDEO_VI2C_VI3C, + INPUT_SVIDEO_VI4A_VI1A = 0x4C, + INPUT_SVIDEO_VI4A_VI1B, + INPUT_SVIDEO_VI4A_VI1C, + INPUT_SVIDEO_VI4A_VI3A = 0x5C, + INPUT_SVIDEO_VI4A_VI3B, + INPUT_SVIDEO_VI4A_VI3C, + + /* Need to add entries for + * RGB, YPbPr and SCART. + */ + INPUT_INVALID +}; + +/** + * enum tvp514x_output - enum for output format + * supported. + * + */ +enum tvp514x_output { + OUTPUT_10BIT_422_EMBEDDED_SYNC = 0, + OUTPUT_20BIT_422_SEPERATE_SYNC, + OUTPUT_10BIT_422_SEPERATE_SYNC = 3, + OUTPUT_INVALID +}; + +/** + * struct tvp514x_platform_data - Platform data values and access functions. + * @power_set: Power state access function, zero is off, non-zero is on. + * @ifparm: Interface parameters access function. + * @priv_data_set: Device private data (pointer) access function. + * @clk_polarity: Clock polarity of the current interface. + * @ hs_polarity: HSYNC Polarity configuration for current interface. + * @ vs_polarity: VSYNC Polarity configuration for current interface. + */ +struct tvp514x_platform_data { + char *master; + int (*power_set) (enum v4l2_power on); + int (*ifparm) (struct v4l2_ifparm *p); + int (*priv_data_set) (void *); + /* Interface control params */ + bool clk_polarity; + bool hs_polarity; + bool vs_polarity; +}; + + +#endif /* ifndef _TVP514X_H */ -- cgit v1.2.3 From e743836c92c2f0fb9bf167dc094bb22ebbded955 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 5 Dec 2008 11:31:16 -0200 Subject: tuner-xc2028: powers device of when not used From: Mauro Carvalho Chehab Since the firmware load is now fast on most boards, better to keep the tuner off by default. A modprobe parameter were added to keep the old behavior, to be used by old devices. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/common/tuners/tuner-xc2028.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/common/tuners/tuner-xc2028.c b/linux/drivers/media/common/tuners/tuner-xc2028.c index 7f43272ca..c7ed924df 100644 --- a/linux/drivers/media/common/tuners/tuner-xc2028.c +++ b/linux/drivers/media/common/tuners/tuner-xc2028.c @@ -32,6 +32,12 @@ static int debug; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "enable verbose debug messages"); +static int no_poweroff; +module_param(no_poweroff, int, 0644); +MODULE_PARM_DESC(debug, "0 (default) powers device off when not used.\n" + "1 keep device energized and with tuner ready all the times.\n" + " Faster, but consumes more power and keeps the device hotter\n"); + static char audio_std[8]; module_param_string(audio_std, audio_std, sizeof(audio_std), 0); MODULE_PARM_DESC(audio_std, @@ -1095,16 +1101,16 @@ static int xc2028_set_params(struct dvb_frontend *fe, T_DIGITAL_TV, type, 0, demod); } -#if 0 -/* This is needed at sleep (S1/S3), but not at fe_standby. Otherwise, - firmware will be loaded on every open() - */ static int xc2028_sleep(struct dvb_frontend *fe) { struct xc2028_data *priv = fe->tuner_priv; int rc = 0; - tuner_dbg("%s called\n", __func__); + /* Avoid firmware reload on slow devices */ + if (no_poweroff) + return; + + tuner_dbg("Putting xc2028/3028 into poweroff mode.\n"); mutex_lock(&priv->lock); @@ -1119,7 +1125,6 @@ static int xc2028_sleep(struct dvb_frontend *fe) return rc; } -#endif static int xc2028_dvb_release(struct dvb_frontend *fe) { @@ -1200,9 +1205,7 @@ static const struct dvb_tuner_ops xc2028_dvb_tuner_ops = { .get_frequency = xc2028_get_frequency, .get_rf_strength = xc2028_signal, .set_params = xc2028_set_params, -#if 0 .sleep = xc2028_sleep, -#endif #if 0 int (*get_bandwidth)(struct dvb_frontend *fe, u32 *bandwidth); int (*get_status)(struct dvb_frontend *fe, u32 *status); -- cgit v1.2.3 From 53f02dd170952065cf1c6c18fefa194bd403c4ee Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 5 Dec 2008 11:39:12 -0200 Subject: em28xx: turn off tuner when not used From: Mauro Carvalho Chehab em28xx devices generally get hot when xc3028 tuner is powered on. This patch solves this by turning power off when the device is not used, at the expense of having a higher load time, when calling a TV application. Since firmware load happens on 1 or 2 seconds on most devices, this is not a pain. Also, it helps to save the planet by saving some power :) Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-video.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index 00d49c45f..ff960f90c 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -1758,6 +1758,9 @@ static int em28xx_v4l2_close(struct inode *inode, struct file *filp) return 0; } + /* Save some power by putting tuner to sleep */ + em28xx_i2c_call_clients(dev, TUNER_SET_STANDBY, NULL); + /* do this before setting alternate! */ em28xx_uninit_isoc(dev); em28xx_set_mode(dev, EM28XX_SUSPEND); @@ -2203,6 +2206,9 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, mutex_unlock(&em28xx_extension_devlist_lock); mutex_unlock(&em28xx_devlist_mutex); + /* Save some power by putting tuner to sleep */ + em28xx_i2c_call_clients(dev, TUNER_SET_STANDBY, NULL); + return 0; fail_reg_devices: -- cgit v1.2.3 From 929fe04b1e4235f1078c9ecbc1824e9ba43baba4 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 5 Dec 2008 11:49:53 -0200 Subject: tuner-xc2028: fix a small warning From: Mauro Carvalho Chehab /home/mauro/v4l-dvb/v4l/tuner-xc2028.c: In function 'xc2028_sleep': /home/mauro/v4l-dvb/v4l/tuner-xc2028.c:1111: warning: 'return' with no value, in function returning non-void Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/common/tuners/tuner-xc2028.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/common/tuners/tuner-xc2028.c b/linux/drivers/media/common/tuners/tuner-xc2028.c index c7ed924df..9536e3d3b 100644 --- a/linux/drivers/media/common/tuners/tuner-xc2028.c +++ b/linux/drivers/media/common/tuners/tuner-xc2028.c @@ -1108,7 +1108,7 @@ static int xc2028_sleep(struct dvb_frontend *fe) /* Avoid firmware reload on slow devices */ if (no_poweroff) - return; + return 0; tuner_dbg("Putting xc2028/3028 into poweroff mode.\n"); -- cgit v1.2.3 From 499a1c5e7f4f6f5918e0c0e39860cd6f5630e799 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 5 Dec 2008 22:49:34 +0000 Subject: saa7134: add analog and DVB-T support for Medion/Creatix CTX946 From: Hermann Pitton How to enable the mpeg encoder is not found yet. The card comes up with gpio 0x0820000 for DVB-T. Priority: normal Signed-off-by: Hermann Pitton Signed-off-by: Mauro Carvalho Chehab --- linux/Documentation/video4linux/CARDLIST.saa7134 | 2 +- linux/drivers/media/video/saa7134/saa7134-cards.c | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/Documentation/video4linux/CARDLIST.saa7134 b/linux/Documentation/video4linux/CARDLIST.saa7134 index dd979dca8..335aef4dc 100644 --- a/linux/Documentation/video4linux/CARDLIST.saa7134 +++ b/linux/Documentation/video4linux/CARDLIST.saa7134 @@ -10,7 +10,7 @@ 9 -> Medion 5044 10 -> Kworld/KuroutoShikou SAA7130-TVPCI 11 -> Terratec Cinergy 600 TV [153b:1143] - 12 -> Medion 7134 [16be:0003] + 12 -> Medion 7134 [16be:0003,16be:5000] 13 -> Typhoon TV+Radio 90031 14 -> ELSA EX-VISION 300TV [1048:226b] 15 -> ELSA EX-VISION 500TV [1048:226a] diff --git a/linux/drivers/media/video/saa7134/saa7134-cards.c b/linux/drivers/media/video/saa7134/saa7134-cards.c index e2dc0822b..54be8314f 100644 --- a/linux/drivers/media/video/saa7134/saa7134-cards.c +++ b/linux/drivers/media/video/saa7134/saa7134-cards.c @@ -4811,6 +4811,12 @@ struct pci_device_id saa7134_pci_tbl[] = { .subdevice = 0x0003, .driver_data = SAA7134_BOARD_MD7134, },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x16be, /* CTX946 analog TV, HW mpeg, DVB-T */ + .subdevice = 0x5000, /* only analog TV and DVB-T for now */ + .driver_data = SAA7134_BOARD_MD7134, + }, { .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7130, .subvendor = 0x1048, -- cgit v1.2.3 From 3ccc9245fe4ae35845a4d5ed89581bf56165ae78 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 6 Dec 2008 20:25:14 +0100 Subject: uvcvideo: Add nodrop module parameter to turn incomplete frame drop off. From: Laurent Pinchart The driver drops incomplete uncompressed video frames to avoid confusing userspace with corrupt data. Add a nodrop module parameter to turn that behaviour off and make all frames available to userspace. Priority: normal Signed-off-by: Laurent Pinchart --- linux/drivers/media/video/uvc/uvc_driver.c | 3 +++ linux/drivers/media/video/uvc/uvc_video.c | 3 ++- linux/drivers/media/video/uvc/uvcvideo.h | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/video/uvc/uvc_driver.c b/linux/drivers/media/video/uvc/uvc_driver.c index 0a0ef6611..424a02965 100644 --- a/linux/drivers/media/video/uvc/uvc_driver.c +++ b/linux/drivers/media/video/uvc/uvc_driver.c @@ -44,6 +44,7 @@ #define DRIVER_VERSION "v0.1.0" #endif +unsigned int uvc_no_drop_param; static unsigned int uvc_quirks_param; unsigned int uvc_trace_param; @@ -1945,6 +1946,8 @@ static void __exit uvc_cleanup(void) module_init(uvc_init); module_exit(uvc_cleanup); +module_param_named(nodrop, uvc_no_drop_param, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(nodrop, "Don't drop incomplete frames"); module_param_named(quirks, uvc_quirks_param, uint, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(quirks, "Forced device quirks"); module_param_named(trace, uvc_trace_param, uint, S_IRUGO|S_IWUSR); diff --git a/linux/drivers/media/video/uvc/uvc_video.c b/linux/drivers/media/video/uvc/uvc_video.c index 3be776a47..b19e91dd5 100644 --- a/linux/drivers/media/video/uvc/uvc_video.c +++ b/linux/drivers/media/video/uvc/uvc_video.c @@ -1007,7 +1007,8 @@ int uvc_video_enable(struct uvc_video_device *video, int enable) return 0; } - if (video->streaming->cur_format->flags & UVC_FMT_FLAG_COMPRESSED) + if ((video->streaming->cur_format->flags & UVC_FMT_FLAG_COMPRESSED) || + uvc_no_drop_param) video->queue.flags &= ~UVC_QUEUE_DROP_INCOMPLETE; else video->queue.flags |= UVC_QUEUE_DROP_INCOMPLETE; diff --git a/linux/drivers/media/video/uvc/uvcvideo.h b/linux/drivers/media/video/uvc/uvcvideo.h index 09b169b69..4533e34ef 100644 --- a/linux/drivers/media/video/uvc/uvcvideo.h +++ b/linux/drivers/media/video/uvc/uvcvideo.h @@ -683,6 +683,7 @@ struct uvc_driver { #define UVC_WARN_MINMAX 0 #define UVC_WARN_PROBE_DEF 1 +extern unsigned int uvc_no_drop_param; extern unsigned int uvc_trace_param; #define uvc_trace(flag, msg...) \ -- cgit v1.2.3 From ed31b2b95aaeb2d32f11053cc521b798b3280195 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 6 Dec 2008 21:43:40 +0100 Subject: uvcvideo: Add a device quirk to prune bogus controls. From: Laurent Pinchart Bogus controls currently include processing unit auto controls for which no corresponding manual control is available. Such auto controls make little sense if any, and are known to crash at least the SiGma Micro webcam. Priority: normal Signed-off-by: Laurent Pinchart --- linux/drivers/media/video/uvc/uvc_ctrl.c | 57 ++++++++++++++++++++++++++++-- linux/drivers/media/video/uvc/uvc_driver.c | 3 +- linux/drivers/media/video/uvc/uvcvideo.h | 1 + 3 files changed, 58 insertions(+), 3 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/uvc/uvc_ctrl.c b/linux/drivers/media/video/uvc/uvc_ctrl.c index 9de8a1db5..01c72935d 100644 --- a/linux/drivers/media/video/uvc/uvc_ctrl.c +++ b/linux/drivers/media/video/uvc/uvc_ctrl.c @@ -545,11 +545,16 @@ static inline __u8 *uvc_ctrl_data(struct uvc_control *ctrl, int id) return ctrl->data + id * ctrl->info->size; } -static inline int uvc_get_bit(const __u8 *data, int bit) +static inline int uvc_test_bit(const __u8 *data, int bit) { return (data[bit >> 3] >> (bit & 7)) & 1; } +static inline void uvc_clear_bit(__u8 *data, int bit) +{ + data[bit >> 3] &= ~(1 << (bit & 7)); +} + /* Extract the bit string specified by mapping->offset and mapping->size * from the little-endian data stored at 'data' and return the result as * a signed 32bit integer. Sign extension will be performed if the mapping @@ -1307,6 +1312,51 @@ end: return ret; } +/* + * Prune an entity of its bogus controls. This currently includes processing + * unit auto controls for which no corresponding manual control is available. + * Such auto controls make little sense if any, and are known to crash at + * least the SiGma Micro webcam. + */ +static void +uvc_ctrl_prune_entity(struct uvc_entity *entity) +{ + static const struct { + u8 idx_manual; + u8 idx_auto; + } blacklist[] = { + { 2, 11 }, /* Hue */ + { 6, 12 }, /* White Balance Temperature */ + { 7, 13 }, /* White Balance Component */ + }; + + u8 *controls; + unsigned int size; + unsigned int i; + + if (UVC_ENTITY_TYPE(entity) != VC_PROCESSING_UNIT) + return; + + controls = entity->processing.bmControls; + size = entity->processing.bControlSize; + + for (i = 0; i < ARRAY_SIZE(blacklist); ++i) { + if (blacklist[i].idx_auto >= 8 * size || + blacklist[i].idx_manual >= 8 * size) + continue; + + if (!uvc_test_bit(controls, blacklist[i].idx_auto) || + uvc_test_bit(controls, blacklist[i].idx_manual)) + continue; + + uvc_trace(UVC_TRACE_CONTROL, "Auto control %u/%u has no " + "matching manual control, removing it.\n", entity->id, + blacklist[i].idx_auto); + + uvc_clear_bit(controls, blacklist[i].idx_auto); + } +} + /* * Initialize device controls. */ @@ -1333,6 +1383,9 @@ int uvc_ctrl_init_device(struct uvc_device *dev) bControlSize = entity->camera.bControlSize; } + if (dev->quirks & UVC_QUIRK_PRUNE_CONTROLS) + uvc_ctrl_prune_entity(entity); + for (i = 0; i < bControlSize; ++i) ncontrols += hweight8(bmControls[i]); @@ -1347,7 +1400,7 @@ int uvc_ctrl_init_device(struct uvc_device *dev) ctrl = entity->controls; for (i = 0; i < bControlSize * 8; ++i) { - if (uvc_get_bit(bmControls, i) == 0) + if (uvc_test_bit(bmControls, i) == 0) continue; ctrl->entity = entity; diff --git a/linux/drivers/media/video/uvc/uvc_driver.c b/linux/drivers/media/video/uvc/uvc_driver.c index 424a02965..343792f0f 100644 --- a/linux/drivers/media/video/uvc/uvc_driver.c +++ b/linux/drivers/media/video/uvc/uvc_driver.c @@ -1896,7 +1896,8 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_PROBE_MINMAX - | UVC_QUIRK_IGNORE_SELECTOR_UNIT}, + | UVC_QUIRK_IGNORE_SELECTOR_UNIT + | UVC_QUIRK_PRUNE_CONTROLS }, /* Generic USB Video Class */ { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) }, {} diff --git a/linux/drivers/media/video/uvc/uvcvideo.h b/linux/drivers/media/video/uvc/uvcvideo.h index 4533e34ef..a699c0bb1 100644 --- a/linux/drivers/media/video/uvc/uvcvideo.h +++ b/linux/drivers/media/video/uvc/uvcvideo.h @@ -316,6 +316,7 @@ struct uvc_xu_control { #define UVC_QUIRK_BUILTIN_ISIGHT 0x00000008 #define UVC_QUIRK_STREAM_NO_FID 0x00000010 #define UVC_QUIRK_IGNORE_SELECTOR_UNIT 0x00000020 +#define UVC_QUIRK_PRUNE_CONTROLS 0x00000040 /* Format flags */ #define UVC_FMT_FLAG_COMPRESSED 0x00000001 -- cgit v1.2.3 From 60738570eec2254bed5147ebd299cc8c408f365e Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 7 Dec 2008 14:19:29 -0200 Subject: saa7134: Add support for Kworld Plus TV Analog Lite PCI From: Mauro Carvalho Chehab Thanks to Sistema Fenix (http://www.sistemafenix.com.br/) for sponsoring this development. Signed-off-by: Gilberto Signed-off-by: Mauro Carvalho Chehab --- linux/Documentation/video4linux/CARDLIST.saa7134 | 1 + linux/drivers/media/common/ir-keymaps.c | 61 +++++++++++++++++++++++ linux/drivers/media/video/saa7134/saa7134-cards.c | 44 ++++++++++++++++ linux/drivers/media/video/saa7134/saa7134-input.c | 14 ++++++ linux/drivers/media/video/saa7134/saa7134.h | 1 + linux/include/media/ir-common.h | 1 + 6 files changed, 122 insertions(+) (limited to 'linux') diff --git a/linux/Documentation/video4linux/CARDLIST.saa7134 b/linux/Documentation/video4linux/CARDLIST.saa7134 index dc67eef38..dd979dca8 100644 --- a/linux/Documentation/video4linux/CARDLIST.saa7134 +++ b/linux/Documentation/video4linux/CARDLIST.saa7134 @@ -151,3 +151,4 @@ 150 -> Zogis Real Angel 220 151 -> ADS Tech Instant HDTV [1421:0380] 152 -> Asus Tiger Rev:1.00 [1043:4857] +153 -> Kworld Plus TV Analog Lite PCI [17de:7128] diff --git a/linux/drivers/media/common/ir-keymaps.c b/linux/drivers/media/common/ir-keymaps.c index 6c0a80f45..ee9931f56 100644 --- a/linux/drivers/media/common/ir-keymaps.c +++ b/linux/drivers/media/common/ir-keymaps.c @@ -2441,6 +2441,67 @@ IR_KEYTAB_TYPE ir_codes_powercolor_real_angel[IR_KEYTAB_SIZE] = { }; EXPORT_SYMBOL_GPL(ir_codes_powercolor_real_angel); +/* Kworld Plus TV Analog Lite PCI IR + Mauro Carvalho Chehab + */ +IR_KEYTAB_TYPE ir_codes_kworld_plus_tv_analog[IR_KEYTAB_SIZE] = { + [0x0c] = KEY_PROG1, /* Kworld key */ + [0x16] = KEY_CLOSECD, /* -> ) */ + [0x1d] = KEY_POWER2, + + [0x00] = KEY_1, + [0x01] = KEY_2, + [0x02] = KEY_3, /* Two keys have the same code: 3 and left */ + [0x03] = KEY_4, /* Two keys have the same code: 3 and right */ + [0x04] = KEY_5, + [0x05] = KEY_6, + [0x06] = KEY_7, + [0x07] = KEY_8, + [0x08] = KEY_9, + [0x0a] = KEY_0, + + [0x09] = KEY_AGAIN, + [0x14] = KEY_MUTE, + + [0x20] = KEY_UP, + [0x21] = KEY_DOWN, + [0x0b] = KEY_ENTER, + + [0x10] = KEY_CHANNELUP, + [0x11] = KEY_CHANNELDOWN, + + /* Couldn't map key left/key right since those + conflict with '3' and '4' scancodes + I dunno what the original driver does + */ + + [0x13] = KEY_VOLUMEUP, + [0x12] = KEY_VOLUMEDOWN, + + /* The lower part of the IR + There are several duplicated keycodes there. + Most of them conflict with digits. + Add mappings just to the unused scancodes. + Somehow, the original driver has a way to know, + but this doesn't seem to be on some GPIO. + Also, it is not related to the time between keyup + and keydown. + */ + [0x19] = KEY_PAUSE, /* Timeshift */ + [0x1a] = KEY_STOP, + [0x1b] = KEY_RECORD, + + [0x22] = KEY_TEXT, + + [0x15] = KEY_AUDIO, /* ((*)) */ + [0x0f] = KEY_ZOOM, + [0x1c] = KEY_SHUFFLE, /* snapshot */ + + [0x18] = KEY_RED, /* B */ + [0x23] = KEY_GREEN, /* C */ +}; +EXPORT_SYMBOL_GPL(ir_codes_kworld_plus_tv_analog); + IR_KEYTAB_TYPE ir_codes_avermedia_a16d[IR_KEYTAB_SIZE] = { [0x20] = KEY_LIST, [0x00] = KEY_POWER, diff --git a/linux/drivers/media/video/saa7134/saa7134-cards.c b/linux/drivers/media/video/saa7134/saa7134-cards.c index d4c432cb3..e2dc0822b 100644 --- a/linux/drivers/media/video/saa7134/saa7134-cards.c +++ b/linux/drivers/media/video/saa7134/saa7134-cards.c @@ -4645,6 +4645,43 @@ struct saa7134_board saa7134_boards[] = { .gpio = 0x0200000, }, }, + [SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG] = { + .name = "Kworld Plus TV Analog Lite PCI", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_YMEC_TVF_5533MF, + .radio_type = TUNER_TEA5767, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 0x80000700, + .inputs = { { + .name = name_tv, + .vmux = 1, + .amux = LINE2, + .tv = 1, + .gpio = 0x100, + }, { + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + .gpio = 0x200, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + .gpio = 0x200, + } }, + .radio = { + .name = name_radio, + .vmux = 1, + .amux = LINE1, + .gpio = 0x100, + }, + .mute = { + .name = name_mute, + .vmux = 8, + .amux = 2, + }, + }, }; const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards); @@ -5691,6 +5728,12 @@ struct pci_device_id saa7134_pci_tbl[] = { .subvendor = 0x1043, .subdevice = 0x4878, /* REV:1.02G */ .driver_data = SAA7134_BOARD_ASUSTeK_TIGER_3IN1, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x17de, + .subdevice = 0x7128, + .driver_data = SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG, }, { /* --- boards without eeprom + subsystem ID --- */ .vendor = PCI_VENDOR_ID_PHILIPS, @@ -5979,6 +6022,7 @@ int saa7134_board_init1(struct saa7134_dev *dev) case SAA7134_BOARD_BEHOLD_507_9FM: case SAA7134_BOARD_GENIUS_TVGO_A11MCE: case SAA7134_BOARD_REAL_ANGEL_220: + case SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG: dev->has_remote = SAA7134_REMOTE_GPIO; break; case SAA7134_BOARD_FLYDVBS_LR300: diff --git a/linux/drivers/media/video/saa7134/saa7134-input.c b/linux/drivers/media/video/saa7134/saa7134-input.c index 1c4bd413e..272490b92 100644 --- a/linux/drivers/media/video/saa7134/saa7134-input.c +++ b/linux/drivers/media/video/saa7134/saa7134-input.c @@ -97,6 +97,15 @@ static int build_key(struct saa7134_dev *dev) dprintk("build_key gpio=0x%x mask=0x%x data=%d\n", gpio, ir->mask_keycode, data); + switch (dev->board) { + case SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG: + if (data == ir->mask_keycode) + ir_input_nokey(ir->dev, &ir->ir); + else + ir_input_keydown(ir->dev, &ir->ir, data, data); + return 0; + } + if (ir->polling) { if ((ir->mask_keydown && (0 != (gpio & ir->mask_keydown))) || (ir->mask_keyup && (0 == (gpio & ir->mask_keyup)))) { @@ -586,6 +595,11 @@ int saa7134_input_init1(struct saa7134_dev *dev) mask_keyup = 0x4000; polling = 50; /* ms */ break; + case SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG: + ir_codes = ir_codes_kworld_plus_tv_analog; + mask_keycode = 0x7f; + polling = 40; /* ms */ + break; } if (NULL == ir_codes) { printk("%s: Oops: IR config error [card=%d]\n", diff --git a/linux/drivers/media/video/saa7134/saa7134.h b/linux/drivers/media/video/saa7134/saa7134.h index 82f9a889a..3bb599673 100644 --- a/linux/drivers/media/video/saa7134/saa7134.h +++ b/linux/drivers/media/video/saa7134/saa7134.h @@ -276,6 +276,7 @@ struct saa7134_format { #define SAA7134_BOARD_REAL_ANGEL_220 150 #define SAA7134_BOARD_ADS_INSTANT_HDTV_PCI 151 #define SAA7134_BOARD_ASUSTeK_TIGER 152 +#define SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG 153 #define SAA7134_MAXBOARDS 32 #define SAA7134_INPUT_MAX 8 diff --git a/linux/include/media/ir-common.h b/linux/include/media/ir-common.h index 3a88e13a2..5bf2ea006 100644 --- a/linux/include/media/ir-common.h +++ b/linux/include/media/ir-common.h @@ -158,6 +158,7 @@ extern IR_KEYTAB_TYPE ir_codes_encore_enltv_fm53[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_real_audio_220_32_keys[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_msi_tvanywhere_plus[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_ati_tv_wonder_hd_600[IR_KEYTAB_SIZE]; +extern IR_KEYTAB_TYPE ir_codes_kworld_plus_tv_analog[IR_KEYTAB_SIZE]; #endif /* -- cgit v1.2.3 From 981e24fc10ca11ec7c751ed1c639ac72fa37d1c7 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 8 Dec 2008 00:01:57 -0200 Subject: tda827x: fix printk message when in FM mode From: Mauro Carvalho Chehab CC: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/common/tuners/tda827x.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/common/tuners/tda827x.c b/linux/drivers/media/common/tuners/tda827x.c index 83a017c53..501b05ba1 100644 --- a/linux/drivers/media/common/tuners/tda827x.c +++ b/linux/drivers/media/common/tuners/tda827x.c @@ -81,10 +81,11 @@ static void tda827x_set_std(struct dvb_frontend *fe, mode = "xx"; } - if (params->mode == V4L2_TUNER_RADIO) + if (params->mode == V4L2_TUNER_RADIO) { priv->sgIF = 88; /* if frequency is 5.5 MHz */ - - dprintk("setting tda827x to system %s\n", mode); + dprintk("setting tda827x to radio FM\n"); + } else + dprintk("setting tda827x to system %s\n", mode); } -- cgit v1.2.3 From 1d0ec557faade5b5844db7b6996e118080a87db3 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 8 Dec 2008 01:49:33 -0200 Subject: tda827x: fix returned frequency From: Mauro Carvalho Chehab Probably due to a removed code, tda827x were doing some wrong calculus at the returned frequency. I suspect that the original idea were to return the programmed divisor converted into frequency again. However, the current code is sometimes multiplying the programmed frequency by 62500, and, on other cases, like radio, it dividing it by 1000. Instead of doing such math, let's just store the frequency value as requested by the caller module. CC: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/common/tuners/tda827x.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/common/tuners/tda827x.c b/linux/drivers/media/common/tuners/tda827x.c index 501b05ba1..a14fe817d 100644 --- a/linux/drivers/media/common/tuners/tda827x.c +++ b/linux/drivers/media/common/tuners/tda827x.c @@ -201,7 +201,7 @@ static int tda827xo_set_params(struct dvb_frontend *fe, fe->ops.i2c_gate_ctrl(fe, 1); i2c_transfer(priv->i2c_adap, &msg, 1); - priv->frequency = tuner_freq - if_freq; // FIXME + priv->frequency = params->frequency; priv->bandwidth = (fe->ops.info.type == FE_OFDM) ? params->u.ofdm.bandwidth : 0; return 0; @@ -306,7 +306,7 @@ static int tda827xo_set_analog_params(struct dvb_frontend *fe, reg2[1] = 0x08; /* Vsync en */ i2c_transfer(priv->i2c_adap, &msg, 1); - priv->frequency = freq * 62500; + priv->frequency = params->frequency; return 0; } @@ -593,7 +593,7 @@ static int tda827xa_set_params(struct dvb_frontend *fe, fe->ops.i2c_gate_ctrl(fe, 1); i2c_transfer(priv->i2c_adap, &msg, 1); - priv->frequency = tuner_freq - if_freq; // FIXME + priv->frequency = params->frequency; priv->bandwidth = (fe->ops.info.type == FE_OFDM) ? params->u.ofdm.bandwidth : 0; return 0; @@ -693,7 +693,7 @@ static int tda827xa_set_analog_params(struct dvb_frontend *fe, tuner_reg[1] = 0x19 + (priv->lpsel << 1); i2c_transfer(priv->i2c_adap, &msg, 1); - priv->frequency = freq * 62500; + priv->frequency = params->frequency; return 0; } -- cgit v1.2.3 From ddfa62c7544e846044782ec43aad56cc331c69a9 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Mon, 8 Dec 2008 08:48:21 +0100 Subject: gspca - vc032x: Bad check of the sensor mi0360. From: Jean-Francois Moine Error was due to bad variable names. Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/vc032x.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/vc032x.c b/linux/drivers/media/video/gspca/vc032x.c index db5deb957..7c0586fe6 100644 --- a/linux/drivers/media/video/gspca/vc032x.c +++ b/linux/drivers/media/video/gspca/vc032x.c @@ -1456,7 +1456,7 @@ static const struct sensor_info sensor_info_data[] = { {SENSOR_OV7670, 0x80 | 0x21, 0x0a, 0x7673, 0x66, 0x67, 0x05}, {SENSOR_MI1310_SOC, 0x80 | 0x5d, 0x00, 0x143a, 0x24, 0x25, 0x01}, /* (tested in vc032x_probe_sensor) */ -/* {SENSOR_MI0360, 0x80 | 0x5d, 0x00, 0x4382, 0x24, 0x25, 0x01}, */ +/* {SENSOR_MI0360, 0x80 | 0x5d, 0x00, 0x8243, 0x24, 0x25, 0x01}, */ }; /* read 'len' bytes in gspca_dev->usb_buf */ @@ -1513,18 +1513,18 @@ static void read_sensor_register(struct gspca_dev *gspca_dev, msleep(1); } reg_r(gspca_dev, 0xa1, 0xb33e, 1); - hdata = gspca_dev->usb_buf[0]; + ldata = gspca_dev->usb_buf[0]; reg_r(gspca_dev, 0xa1, 0xb33d, 1); mdata = gspca_dev->usb_buf[0]; reg_r(gspca_dev, 0xa1, 0xb33c, 1); - ldata = gspca_dev->usb_buf[0]; - PDEBUG(D_PROBE, "Read Sensor %02x %02x%02x", + hdata = gspca_dev->usb_buf[0]; + PDEBUG(D_PROBE, "Read Sensor %02x%02x %02x", hdata, mdata, ldata); reg_r(gspca_dev, 0xa1, 0xb334, 1); if (gspca_dev->usb_buf[0] == 0x02) - *value = (ldata << 8) + mdata; + *value = (hdata << 8) + mdata; else - *value = ldata; + *value = hdata; } static int vc032x_probe_sensor(struct gspca_dev *gspca_dev) @@ -1551,7 +1551,7 @@ static int vc032x_probe_sensor(struct gspca_dev *gspca_dev) /* special case for MI0360 */ if (ptsensor_info->sensorId == SENSOR_MI1310_SOC - && value == 0x4382) + && value == 0x8243) return ptsensor_info->sensorId; } return -1; -- cgit v1.2.3 From 1ae43e8ab9d6411a573a667d5dab1f36faa95c65 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Mon, 8 Dec 2008 09:13:53 +0100 Subject: gspca - vc032x: V and H flips added for sensors ov7660 and 7670. From: Jean-Francois Moine Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/vc032x.c | 137 +++++++++++++++++++++++++++++-- 1 file changed, 128 insertions(+), 9 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/vc032x.c b/linux/drivers/media/video/gspca/vc032x.c index 7c0586fe6..7b899a735 100644 --- a/linux/drivers/media/video/gspca/vc032x.c +++ b/linux/drivers/media/video/gspca/vc032x.c @@ -32,8 +32,10 @@ MODULE_LICENSE("GPL"); struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - unsigned char autogain; - unsigned char lightfreq; + __u8 autogain; + __u8 hflip; + __u8 vflip; + __u8 lightfreq; char qindex; char bridge; @@ -52,6 +54,10 @@ struct sd { /* V4L2 controls supported by the driver */ static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val); +static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val); static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val); @@ -70,7 +76,38 @@ static struct ctrl sd_ctrls[] = { .set = sd_setautogain, .get = sd_getautogain, }, -#define LIGHTFREQ_IDX 1 +/* next 2 controls work with ov7660 and ov7670 only */ +#define HFLIP_IDX 1 + { + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Mirror", + .minimum = 0, + .maximum = 1, + .step = 1, +#define HFLIP_DEF 0 + .default_value = HFLIP_DEF, + }, + .set = sd_sethflip, + .get = sd_gethflip, + }, +#define VFLIP_IDX 2 + { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Vflip", + .minimum = 0, + .maximum = 1, + .step = 1, +#define VFLIP_DEF 0 + .default_value = VFLIP_DEF, + }, + .set = sd_setvflip, + .get = sd_getvflip, + }, +#define LIGHTFREQ_IDX 3 { { .id = V4L2_CID_POWER_LINE_FREQUENCY, @@ -112,6 +149,11 @@ static struct v4l2_pix_format vc0323_mode[] = { .priv = 0}, }; +/* OV7660/7670 registers */ +#define OV7660_REG_MVFP 0x1e +#define OV7660_MVFP_MIRROR 0x20 +#define OV7660_MVFP_VFLIP 0x10 + static const __u8 mi0360_matrix[9] = { 0x50, 0xf8, 0xf8, 0xf5, 0x50, 0xfb, 0xff, 0xf1, 0x50 }; @@ -1057,7 +1099,7 @@ static const __u8 ov7660_initVGA_data[][4] = { {0x00, 0x01, 0x80, 0xaa}, {0x00, 0x02, 0x80, 0xaa}, {0x00, 0x12, 0x80, 0xaa}, {0x00, 0x12, 0x05, 0xaa}, - {0x00, 0x1e, 0x01, 0xaa}, + {0x00, 0x1e, 0x01, 0xaa}, /* MVFP */ {0x00, 0x3d, 0x40, 0xaa}, /* 0x3d <-40 gamma 01 */ {0x00, 0x41, 0x00, 0xaa}, /* edge 00 */ {0x00, 0x0d, 0x48, 0xaa}, {0x00, 0x0e, 0x04, 0xaa}, @@ -1111,7 +1153,7 @@ static const __u8 ov7660_initQVGA_data[][4] = { {0xb8, 0x27, 0x20, 0xcc}, {0xb8, 0x8f, 0x50, 0xcc}, {0x00, 0x01, 0x80, 0xaa}, {0x00, 0x02, 0x80, 0xaa}, {0x00, 0x12, 0x80, 0xaa}, {0x00, 0x12, 0x05, 0xaa}, - {0x00, 0x1e, 0x01, 0xaa}, + {0x00, 0x1e, 0x01, 0xaa}, /* MVFP */ {0x00, 0x3d, 0x40, 0xaa}, /* 0x3d <-40 gamma 01 */ {0x00, 0x41, 0x00, 0xaa}, /* edge 00 */ {0x00, 0x0d, 0x48, 0xaa}, {0x00, 0x0e, 0x04, 0xaa}, @@ -1217,7 +1259,8 @@ static const __u8 ov7670_initVGA_JPG[][4] = { {0x00, 0xa9, 0x90, 0xaa}, {0x00, 0xaa, 0x14, 0xaa}, {0x00, 0x13, 0xe5, 0xaa}, {0x00, 0x0e, 0x61, 0xaa}, {0x00, 0x0f, 0x4b, 0xaa}, {0x00, 0x16, 0x02, 0xaa}, - {0x00, 0x1e, 0x07, 0xaa}, {0x00, 0x21, 0x02, 0xaa}, + {0x00, 0x1e, 0x07, 0xaa}, /* MVFP */ + {0x00, 0x21, 0x02, 0xaa}, {0x00, 0x22, 0x91, 0xaa}, {0x00, 0x29, 0x07, 0xaa}, {0x00, 0x33, 0x0b, 0xaa}, {0x00, 0x35, 0x0b, 0xaa}, {0x00, 0x37, 0x1d, 0xaa}, {0x00, 0x38, 0x71, 0xaa}, @@ -1282,7 +1325,8 @@ static const __u8 ov7670_initVGA_JPG[][4] = { {0x00, 0x71, 0x35, 0xaa}, {0x00, 0x72, 0x11, 0xaa}, {0x00, 0x73, 0xf0, 0xaa}, {0x00, 0xa2, 0x02, 0xaa}, {0x00, 0xb1, 0x00, 0xaa}, {0x00, 0xb1, 0x0c, 0xaa}, - {0x00, 0x1e, 0x37, 0xaa}, {0x00, 0xaa, 0x14, 0xaa}, + {0x00, 0x1e, 0x37, 0xaa}, /* MVFP */ + {0x00, 0xaa, 0x14, 0xaa}, {0x00, 0x24, 0x80, 0xaa}, {0x00, 0x25, 0x74, 0xaa}, {0x00, 0x26, 0xd3, 0xaa}, {0x00, 0x0d, 0x00, 0xaa}, {0x00, 0x14, 0x18, 0xaa}, {0x00, 0x9d, 0x99, 0xaa}, @@ -1344,7 +1388,8 @@ static const __u8 ov7670_initQVGA_JPG[][4] = { {0x00, 0xa9, 0x90, 0xaa}, {0x00, 0xaa, 0x14, 0xaa}, {0x00, 0x13, 0xe5, 0xaa}, {0x00, 0x0e, 0x61, 0xaa}, {0x00, 0x0f, 0x4b, 0xaa}, {0x00, 0x16, 0x02, 0xaa}, - {0x00, 0x1e, 0x07, 0xaa}, {0x00, 0x21, 0x02, 0xaa}, + {0x00, 0x1e, 0x07, 0xaa}, /* MVFP */ + {0x00, 0x21, 0x02, 0xaa}, {0x00, 0x22, 0x91, 0xaa}, {0x00, 0x29, 0x07, 0xaa}, {0x00, 0x33, 0x0b, 0xaa}, {0x00, 0x35, 0x0b, 0xaa}, {0x00, 0x37, 0x1d, 0xaa}, {0x00, 0x38, 0x71, 0xaa}, @@ -1409,7 +1454,8 @@ static const __u8 ov7670_initQVGA_JPG[][4] = { {0x00, 0x71, 0x35, 0xaa}, {0x00, 0x72, 0x11, 0xaa}, {0x00, 0x73, 0xf0, 0xaa}, {0x00, 0xa2, 0x02, 0xaa}, {0x00, 0xb1, 0x00, 0xaa}, {0x00, 0xb1, 0x0c, 0xaa}, - {0x00, 0x1e, 0x37, 0xaa}, {0x00, 0xaa, 0x14, 0xaa}, + {0x00, 0x1e, 0x37, 0xaa}, /* MVFP */ + {0x00, 0xaa, 0x14, 0xaa}, {0x00, 0x24, 0x80, 0xaa}, {0x00, 0x25, 0x74, 0xaa}, {0x00, 0x26, 0xd3, 0xaa}, {0x00, 0x0d, 0x00, 0xaa}, {0x00, 0x14, 0x18, 0xaa}, {0x00, 0x9d, 0x99, 0xaa}, @@ -1695,10 +1741,25 @@ static int sd_config(struct gspca_dev *gspca_dev, } sd->qindex = 7; + sd->hflip = HFLIP_DEF; + sd->vflip = VFLIP_DEF; + if (sd->sensor == SENSOR_OV7670) { + sd->hflip = 1; + sd->vflip = 1; + } sd->autogain = AUTOGAIN_DEF; sd->lightfreq = FREQ_DEF; if (sd->sensor != SENSOR_OV7670) gspca_dev->ctrl_dis = (1 << LIGHTFREQ_IDX); + switch (sd->sensor) { + case SENSOR_OV7660: + case SENSOR_OV7670: + break; + default: + gspca_dev->ctrl_dis = (1 << HFLIP_IDX) + | (1 << VFLIP_IDX); + break; + } if (sd->bridge == BRIDGE_VC0321) { reg_r(gspca_dev, 0x8a, 0, 3); @@ -1727,6 +1788,27 @@ static void setquality(struct gspca_dev *gspca_dev) #endif } +/* for OV7660 and OV7670 only */ +static void sethvflip(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 data; + + switch (sd->sensor) { + case SENSOR_OV7660: + data = 1; + break; + case SENSOR_OV7670: + data = 7; + break; + default: + return; + } + data |= OV7660_MVFP_MIRROR * sd->hflip + | OV7660_MVFP_VFLIP * sd->vflip; + i2c_write(gspca_dev, OV7660_REG_MVFP, &data, 1); +} + static void setautogain(struct gspca_dev *gspca_dev) { #if 0 @@ -1880,6 +1962,7 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_w(gspca_dev->dev, 0x89, 0xffff, 0xfdff); msleep(100); setquality(gspca_dev); + sethvflip(gspca_dev); setautogain(gspca_dev); setlightfreq(gspca_dev); } @@ -1948,6 +2031,42 @@ static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) return 0; } +static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->hflip = val; + if (gspca_dev->streaming) + sethvflip(gspca_dev); + return 0; +} + +static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->hflip; + return 0; +} + +static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->vflip = val; + if (gspca_dev->streaming) + sethvflip(gspca_dev); + return 0; +} + +static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->vflip; + return 0; +} + static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; -- cgit v1.2.3 From 4b21644e75fa6a4ac9284d07045c28ac414a8f1d Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Mon, 8 Dec 2008 09:17:32 +0100 Subject: gspca - vc032x: Remove the autogain control. From: Jean-Francois Moine This control was not coded. Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/vc032x.c | 57 ++------------------------------ 1 file changed, 3 insertions(+), 54 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/vc032x.c b/linux/drivers/media/video/gspca/vc032x.c index 7b899a735..244f30fc6 100644 --- a/linux/drivers/media/video/gspca/vc032x.c +++ b/linux/drivers/media/video/gspca/vc032x.c @@ -32,7 +32,6 @@ MODULE_LICENSE("GPL"); struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - __u8 autogain; __u8 hflip; __u8 vflip; __u8 lightfreq; @@ -52,8 +51,6 @@ struct sd { }; /* V4L2 controls supported by the driver */ -static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val); static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val); @@ -62,22 +59,8 @@ static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val); static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val); static struct ctrl sd_ctrls[] = { - { - { - .id = V4L2_CID_AUTOGAIN, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Auto Gain", - .minimum = 0, - .maximum = 1, - .step = 1, -#define AUTOGAIN_DEF 1 - .default_value = AUTOGAIN_DEF, - }, - .set = sd_setautogain, - .get = sd_getautogain, - }, /* next 2 controls work with ov7660 and ov7670 only */ -#define HFLIP_IDX 1 +#define HFLIP_IDX 0 { { .id = V4L2_CID_HFLIP, @@ -92,7 +75,7 @@ static struct ctrl sd_ctrls[] = { .set = sd_sethflip, .get = sd_gethflip, }, -#define VFLIP_IDX 2 +#define VFLIP_IDX 1 { { .id = V4L2_CID_VFLIP, @@ -107,7 +90,7 @@ static struct ctrl sd_ctrls[] = { .set = sd_setvflip, .get = sd_getvflip, }, -#define LIGHTFREQ_IDX 3 +#define LIGHTFREQ_IDX 2 { { .id = V4L2_CID_POWER_LINE_FREQUENCY, @@ -1747,7 +1730,6 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->hflip = 1; sd->vflip = 1; } - sd->autogain = AUTOGAIN_DEF; sd->lightfreq = FREQ_DEF; if (sd->sensor != SENSOR_OV7670) gspca_dev->ctrl_dis = (1 << LIGHTFREQ_IDX); @@ -1809,20 +1791,6 @@ static void sethvflip(struct gspca_dev *gspca_dev) i2c_write(gspca_dev, OV7660_REG_MVFP, &data, 1); } -static void setautogain(struct gspca_dev *gspca_dev) -{ -#if 0 - struct sd *sd = (struct sd *) gspca_dev; - __u8 autoval; - - if (sd->autogain) - autoval = 0x42; - else - autoval = 0x02; - reg_w(gspca_dev->dev, 0xa0, autoval, 0x0180); -#endif -} - static void setlightfreq(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -1963,7 +1931,6 @@ static int sd_start(struct gspca_dev *gspca_dev) msleep(100); setquality(gspca_dev); sethvflip(gspca_dev); - setautogain(gspca_dev); setlightfreq(gspca_dev); } return 0; @@ -2013,24 +1980,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); } -static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->autogain = val; - if (gspca_dev->streaming) - setautogain(gspca_dev); - return 0; -} - -static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->autogain; - return 0; -} - static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; -- cgit v1.2.3 From 603200bf18af9978fc1d1f81485d51aa2b9ec05c Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Mon, 8 Dec 2008 10:25:40 +0100 Subject: gspca - zc3xx: Remove the duplicated register names From: Jean-Francois Moine Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/zc3xx-reg.h | 8 ---- linux/drivers/media/video/gspca/zc3xx.c | 72 ++++++++++++++--------------- 2 files changed, 36 insertions(+), 44 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/zc3xx-reg.h b/linux/drivers/media/video/gspca/zc3xx-reg.h index f52e09c2c..bfb559c3b 100644 --- a/linux/drivers/media/video/gspca/zc3xx-reg.h +++ b/linux/drivers/media/video/gspca/zc3xx-reg.h @@ -244,14 +244,6 @@ #define ZC3XX_R1CA_SHARPNESS04 0x01ca #define ZC3XX_R1CB_SHARPNESS05 0x01cb -/* Synchronization */ -#define ZC3XX_R190_SYNC00LOW 0x0190 -#define ZC3XX_R191_SYNC00MID 0x0191 -#define ZC3XX_R192_SYNC00HIGH 0x0192 -#define ZC3XX_R195_SYNC01LOW 0x0195 -#define ZC3XX_R196_SYNC01MID 0x0196 -#define ZC3XX_R197_SYNC01HIGH 0x0197 - /* Dead pixels */ #define ZC3XX_R250_DEADPIXELSMODE 0x0250 diff --git a/linux/drivers/media/video/gspca/zc3xx.c b/linux/drivers/media/video/gspca/zc3xx.c index ae4e30b52..210ac4979 100644 --- a/linux/drivers/media/video/gspca/zc3xx.c +++ b/linux/drivers/media/video/gspca/zc3xx.c @@ -2368,12 +2368,12 @@ static const struct usb_action hv7131b_50HZ[] = { /* 640x480*/ {0xaa, 0x21, 0x0050}, /* 00,21,50,aa */ {0xaa, 0x22, 0x001b}, /* 00,22,1b,aa */ {0xaa, 0x23, 0x00fc}, /* 00,23,fc,aa */ - {0xa0, 0x2f, ZC3XX_R190_SYNC00LOW}, /* 01,90,2f,cc */ - {0xa0, 0x9b, ZC3XX_R191_SYNC00MID}, /* 01,91,9b,cc */ - {0xa0, 0x80, ZC3XX_R192_SYNC00HIGH}, /* 01,92,80,cc */ - {0xa0, 0x00, ZC3XX_R195_SYNC01LOW}, /* 01,95,00,cc */ - {0xa0, 0xea, ZC3XX_R196_SYNC01MID}, /* 01,96,ea,cc */ - {0xa0, 0x60, ZC3XX_R197_SYNC01HIGH}, /* 01,97,60,cc */ + {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */ + {0xa0, 0x9b, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,9b,cc */ + {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,80,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0xea, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,ea,cc */ + {0xa0, 0x60, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,60,cc */ {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0c,cc */ {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,18,cc */ {0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,18,cc */ @@ -2393,12 +2393,12 @@ static const struct usb_action hv7131b_50HZScale[] = { /* 320x240 */ {0xaa, 0x21, 0x0050}, /* 00,21,50,aa */ {0xaa, 0x22, 0x0012}, /* 00,22,12,aa */ {0xaa, 0x23, 0x0080}, /* 00,23,80,aa */ - {0xa0, 0x2f, ZC3XX_R190_SYNC00LOW}, /* 01,90,2f,cc */ - {0xa0, 0x9b, ZC3XX_R191_SYNC00MID}, /* 01,91,9b,cc */ - {0xa0, 0x80, ZC3XX_R192_SYNC00HIGH}, /* 01,92,80,cc */ - {0xa0, 0x01, ZC3XX_R195_SYNC01LOW}, /* 01,95,01,cc */ - {0xa0, 0xd4, ZC3XX_R196_SYNC01MID}, /* 01,96,d4,cc */ - {0xa0, 0xc0, ZC3XX_R197_SYNC01HIGH}, /* 01,97,c0,cc */ + {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */ + {0xa0, 0x9b, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,9b,cc */ + {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,80,cc */ + {0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,01,cc */ + {0xa0, 0xd4, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,d4,cc */ + {0xa0, 0xc0, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,c0,cc */ {0xa0, 0x07, ZC3XX_R18C_AEFREEZE}, /* 01,8c,07,cc */ {0xa0, 0x0f, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,0f,cc */ {0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,18,cc */ @@ -2418,12 +2418,12 @@ static const struct usb_action hv7131b_60HZ[] = { /* 640x480*/ {0xaa, 0x21, 0x0040}, /* 00,21,40,aa */ {0xaa, 0x22, 0x0013}, /* 00,22,13,aa */ {0xaa, 0x23, 0x004c}, /* 00,23,4c,aa */ - {0xa0, 0x2f, ZC3XX_R190_SYNC00LOW}, /* 01,90,2f,cc */ - {0xa0, 0x4d, ZC3XX_R191_SYNC00MID}, /* 01,91,4d,cc */ - {0xa0, 0x60, ZC3XX_R192_SYNC00HIGH}, /* 01,92,60,cc */ - {0xa0, 0x00, ZC3XX_R195_SYNC01LOW}, /* 01,95,00,cc */ - {0xa0, 0xc3, ZC3XX_R196_SYNC01MID}, /* 01,96,c3,cc */ - {0xa0, 0x50, ZC3XX_R197_SYNC01HIGH}, /* 01,97,50,cc */ + {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */ + {0xa0, 0x4d, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,4d,cc */ + {0xa0, 0x60, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,60,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0xc3, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,c3,cc */ + {0xa0, 0x50, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,50,cc */ {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0c,cc */ {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,18,cc */ {0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,18,cc */ @@ -2443,12 +2443,12 @@ static const struct usb_action hv7131b_60HZScale[] = { /* 320x240 */ {0xaa, 0x21, 0x00a0}, /* 00,21,a0,aa */ {0xaa, 0x22, 0x0016}, /* 00,22,16,aa */ {0xaa, 0x23, 0x0040}, /* 00,23,40,aa */ - {0xa0, 0x2f, ZC3XX_R190_SYNC00LOW}, /* 01,90,2f,cc */ - {0xa0, 0x4d, ZC3XX_R191_SYNC00MID}, /* 01,91,4d,cc */ - {0xa0, 0x60, ZC3XX_R192_SYNC00HIGH}, /* 01,92,60,cc */ - {0xa0, 0x01, ZC3XX_R195_SYNC01LOW}, /* 01,95,01,cc */ - {0xa0, 0x86, ZC3XX_R196_SYNC01MID}, /* 01,96,86,cc */ - {0xa0, 0xa0, ZC3XX_R197_SYNC01HIGH}, /* 01,97,a0,cc */ + {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */ + {0xa0, 0x4d, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,4d,cc */ + {0xa0, 0x60, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,60,cc */ + {0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,01,cc */ + {0xa0, 0x86, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,86,cc */ + {0xa0, 0xa0, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,a0,cc */ {0xa0, 0x07, ZC3XX_R18C_AEFREEZE}, /* 01,8c,07,cc */ {0xa0, 0x0f, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,0f,cc */ {0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,18,cc */ @@ -2468,12 +2468,12 @@ static const struct usb_action hv7131b_NoFliker[] = { /* 640x480*/ {0xaa, 0x21, 0x0010}, /* 00,21,10,aa */ {0xaa, 0x22, 0x0000}, /* 00,22,00,aa */ {0xaa, 0x23, 0x0003}, /* 00,23,03,aa */ - {0xa0, 0x2f, ZC3XX_R190_SYNC00LOW}, /* 01,90,2f,cc */ - {0xa0, 0xf8, ZC3XX_R191_SYNC00MID}, /* 01,91,f8,cc */ - {0xa0, 0x00, ZC3XX_R192_SYNC00HIGH}, /* 01,92,00,cc */ - {0xa0, 0x00, ZC3XX_R195_SYNC01LOW}, /* 01,95,00,cc */ - {0xa0, 0x02, ZC3XX_R196_SYNC01MID}, /* 01,96,02,cc */ - {0xa0, 0x00, ZC3XX_R197_SYNC01HIGH}, /* 01,97,00,cc */ + {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */ + {0xa0, 0xf8, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,f8,cc */ + {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,00,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x02, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,02,cc */ + {0xa0, 0x00, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,00,cc */ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ @@ -2493,12 +2493,12 @@ static const struct usb_action hv7131b_NoFlikerScale[] = { /* 320x240 */ {0xaa, 0x21, 0x00a0}, /* 00,21,a0,aa */ {0xaa, 0x22, 0x0016}, /* 00,22,16,aa */ {0xaa, 0x23, 0x0040}, /* 00,23,40,aa */ - {0xa0, 0x2f, ZC3XX_R190_SYNC00LOW}, /* 01,90,2f,cc */ - {0xa0, 0xf8, ZC3XX_R191_SYNC00MID}, /* 01,91,f8,cc */ - {0xa0, 0x00, ZC3XX_R192_SYNC00HIGH}, /* 01,92,00,cc */ - {0xa0, 0x00, ZC3XX_R195_SYNC01LOW}, /* 01,95,00,cc */ - {0xa0, 0x02, ZC3XX_R196_SYNC01MID}, /* 01,96,02,cc */ - {0xa0, 0x00, ZC3XX_R197_SYNC01HIGH}, /* 01,97,00,cc */ + {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */ + {0xa0, 0xf8, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,f8,cc */ + {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,00,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x02, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,02,cc */ + {0xa0, 0x00, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,00,cc */ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ -- cgit v1.2.3 From 37fbb382d3b08a70e2c0b8b33734f5e88132efe6 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Mon, 8 Dec 2008 10:41:04 +0100 Subject: gspca - zc3xx: Change the USB exchanges for the sensor pas202b. From: Jean-Francois Moine This sensor was declared as hdcs2020. The new exchanges have been extracted from the ms-win file usbvm31b.inf. The light frequency control has been added. Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/zc3xx.c | 727 ++++++++++++++++---------------- 1 file changed, 352 insertions(+), 375 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/zc3xx.c b/linux/drivers/media/video/gspca/zc3xx.c index 210ac4979..678070b93 100644 --- a/linux/drivers/media/video/gspca/zc3xx.c +++ b/linux/drivers/media/video/gspca/zc3xx.c @@ -51,16 +51,16 @@ struct sd { #define SENSOR_CS2102 0 #define SENSOR_CS2102K 1 #define SENSOR_GC0305 2 -#define SENSOR_HDCS2020 3 -#define SENSOR_HDCS2020b 4 -#define SENSOR_HV7131B 5 -#define SENSOR_HV7131C 6 -#define SENSOR_ICM105A 7 -#define SENSOR_MC501CB 8 -#define SENSOR_OV7620 9 -/*#define SENSOR_OV7648 9 - same values */ -#define SENSOR_OV7630C 10 -#define SENSOR_PAS106 11 +#define SENSOR_HDCS2020b 3 +#define SENSOR_HV7131B 4 +#define SENSOR_HV7131C 5 +#define SENSOR_ICM105A 6 +#define SENSOR_MC501CB 7 +#define SENSOR_OV7620 8 +/*#define SENSOR_OV7648 8 - same values */ +#define SENSOR_OV7630C 9 +#define SENSOR_PAS106 10 +#define SENSOR_PAS202B 11 #define SENSOR_PB0330 12 #define SENSOR_PO2030 13 #define SENSOR_TAS5130CK 14 @@ -1653,295 +1653,6 @@ static const struct usb_action gc0305_NoFliker[] = { {} }; -/* play poker with registers at your own risk !! */ -static const struct usb_action hdcs2020xx_Initial[] = { - {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, - {0xa0, 0x0e, ZC3XX_R010_CMOSSENSORSELECT}, - {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, - {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, - {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, - {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, - {0xa0, 0xd0, ZC3XX_R006_FRAMEHEIGHTLOW}, - /* D0 ?? E0 did not start */ - {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, - {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, - {0xa0, 0x08, ZC3XX_R098_WINYSTARTLOW}, - {0xa0, 0x02, ZC3XX_R09A_WINXSTARTLOW}, - {0xa0, 0x08, ZC3XX_R11A_FIRSTYLOW}, - {0xa0, 0x02, ZC3XX_R11C_FIRSTXLOW}, - {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, - {0xa0, 0xd8, ZC3XX_R09C_WINHEIGHTLOW}, - {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, - {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, - {0xaa, 0x02, 0x0002}, - {0xaa, 0x07, 0x0006}, - {0xaa, 0x08, 0x0002}, - {0xaa, 0x09, 0x0006}, - {0xaa, 0x0a, 0x0001}, - {0xaa, 0x0b, 0x0001}, - {0xaa, 0x0c, 0x0008}, - {0xaa, 0x0d, 0x0000}, - {0xaa, 0x10, 0x0000}, - {0xaa, 0x12, 0x0005}, - {0xaa, 0x13, 0x0063}, - {0xaa, 0x15, 0x0070}, - {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, - {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, - {0xa0, 0x00, 0x01ad}, - {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, - {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, - {0xa0, 0x70, ZC3XX_R18D_YTARGET}, - {0xa1, 0x01, 0x0002}, - {0xa1, 0x01, 0x0008}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ - {0xa0, 0x04, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ - {0xa1, 0x01, 0x01c8}, - {0xa1, 0x01, 0x01c9}, - {0xa1, 0x01, 0x01ca}, - {0xa0, 0x07, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ - {0xa0, 0x11, ZC3XX_R120_GAMMA00}, /* gamma ~4 */ - {0xa0, 0x37, ZC3XX_R121_GAMMA01}, - {0xa0, 0x58, ZC3XX_R122_GAMMA02}, - {0xa0, 0x79, ZC3XX_R123_GAMMA03}, - {0xa0, 0x91, ZC3XX_R124_GAMMA04}, - {0xa0, 0xa6, ZC3XX_R125_GAMMA05}, - {0xa0, 0xb8, ZC3XX_R126_GAMMA06}, - {0xa0, 0xc7, ZC3XX_R127_GAMMA07}, - {0xa0, 0xd3, ZC3XX_R128_GAMMA08}, - {0xa0, 0xde, ZC3XX_R129_GAMMA09}, - {0xa0, 0xe6, ZC3XX_R12A_GAMMA0A}, - {0xa0, 0xed, ZC3XX_R12B_GAMMA0B}, - {0xa0, 0xf3, ZC3XX_R12C_GAMMA0C}, - {0xa0, 0xf8, ZC3XX_R12D_GAMMA0D}, - {0xa0, 0xfb, ZC3XX_R12E_GAMMA0E}, - {0xa0, 0xff, ZC3XX_R12F_GAMMA0F}, - {0xa0, 0x26, ZC3XX_R130_GAMMA10}, - {0xa0, 0x23, ZC3XX_R131_GAMMA11}, - {0xa0, 0x20, ZC3XX_R132_GAMMA12}, - {0xa0, 0x1c, ZC3XX_R133_GAMMA13}, - {0xa0, 0x16, ZC3XX_R134_GAMMA14}, - {0xa0, 0x13, ZC3XX_R135_GAMMA15}, - {0xa0, 0x10, ZC3XX_R136_GAMMA16}, - {0xa0, 0x0d, ZC3XX_R137_GAMMA17}, - {0xa0, 0x0b, ZC3XX_R138_GAMMA18}, - {0xa0, 0x09, ZC3XX_R139_GAMMA19}, - {0xa0, 0x07, ZC3XX_R13A_GAMMA1A}, - {0xa0, 0x06, ZC3XX_R13B_GAMMA1B}, - {0xa0, 0x05, ZC3XX_R13C_GAMMA1C}, - {0xa0, 0x04, ZC3XX_R13D_GAMMA1D}, - {0xa0, 0x03, ZC3XX_R13E_GAMMA1E}, - {0xa0, 0x02, ZC3XX_R13F_GAMMA1F}, - - {0xa0, 0x4c, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xf5, ZC3XX_R10B_RGB01}, - {0xa0, 0xff, ZC3XX_R10C_RGB02}, - {0xa0, 0xf9, ZC3XX_R10D_RGB10}, - {0xa0, 0x51, ZC3XX_R10E_RGB11}, - {0xa0, 0xf5, ZC3XX_R10F_RGB12}, - {0xa0, 0xfb, ZC3XX_R110_RGB20}, - {0xa0, 0xed, ZC3XX_R111_RGB21}, - {0xa0, 0x5f, ZC3XX_R112_RGB22}, - - {0xa1, 0x01, 0x0180}, - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, - {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, - {0xaa, 0x20, 0x0004}, - {0xaa, 0x21, 0x003d}, - {0xaa, 0x03, 0x0041}, - {0xaa, 0x04, 0x0010}, - {0xaa, 0x05, 0x003d}, - {0xaa, 0x0e, 0x0001}, - {0xaa, 0x0f, 0x0000}, - {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0x3d, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x9b, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x41, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0x6f, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0xad, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, - {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, - {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, - {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa1, 0x01, 0x0195}, - {0xa1, 0x01, 0x0196}, - {0xa1, 0x01, 0x0197}, - {0xa0, 0x3d, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x1d, ZC3XX_R116_RGAIN}, - {0xa0, 0x40, ZC3XX_R117_GGAIN}, - {0xa0, 0x85, ZC3XX_R118_BGAIN}, - {0xa1, 0x01, 0x0116}, - {0xa1, 0x01, 0x0118}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x1d, ZC3XX_R116_RGAIN}, - {0xa0, 0x40, ZC3XX_R117_GGAIN}, - {0xa0, 0x85, ZC3XX_R118_BGAIN}, - {0xa1, 0x01, 0x0116}, - {0xa1, 0x01, 0x0118}, -/* {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING}, */ - {0xa0, 0x00, 0x0007}, - {} -}; - -static const struct usb_action hdcs2020xx_InitialScale[] = { - {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, - {0xa0, 0x0e, ZC3XX_R010_CMOSSENSORSELECT}, - {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, - {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, - {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, - {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, - {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, - {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, - {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, - {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, - {0xa0, 0x03, ZC3XX_R09A_WINXSTARTLOW}, - {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, - {0xa0, 0x03, ZC3XX_R11C_FIRSTXLOW}, - {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, - {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW}, - {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, - {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, - {0xaa, 0x02, 0x0002}, - {0xaa, 0x07, 0x0006}, - {0xaa, 0x08, 0x0002}, - {0xaa, 0x09, 0x0006}, - {0xaa, 0x0a, 0x0001}, - {0xaa, 0x0b, 0x0001}, - {0xaa, 0x0c, 0x0008}, - {0xaa, 0x0d, 0x0000}, - {0xaa, 0x10, 0x0000}, - {0xaa, 0x12, 0x0005}, - {0xaa, 0x13, 0x0063}, - {0xaa, 0x15, 0x0070}, - {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, - {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, - {0xa0, 0x00, 0x01ad}, - {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, - {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, - {0xa0, 0x70, ZC3XX_R18D_YTARGET}, - {0xa1, 0x01, 0x0002}, - {0xa1, 0x01, 0x0008}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ - {0xa0, 0x04, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ - {0xa1, 0x01, 0x01c8}, - {0xa1, 0x01, 0x01c9}, - {0xa1, 0x01, 0x01ca}, - {0xa0, 0x07, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ - {0xa0, 0x11, ZC3XX_R120_GAMMA00}, /* gamma ~4*/ - {0xa0, 0x37, ZC3XX_R121_GAMMA01}, - {0xa0, 0x58, ZC3XX_R122_GAMMA02}, - {0xa0, 0x79, ZC3XX_R123_GAMMA03}, - {0xa0, 0x91, ZC3XX_R124_GAMMA04}, - {0xa0, 0xa6, ZC3XX_R125_GAMMA05}, - {0xa0, 0xb8, ZC3XX_R126_GAMMA06}, - {0xa0, 0xc7, ZC3XX_R127_GAMMA07}, - {0xa0, 0xd3, ZC3XX_R128_GAMMA08}, - {0xa0, 0xde, ZC3XX_R129_GAMMA09}, - {0xa0, 0xe6, ZC3XX_R12A_GAMMA0A}, - {0xa0, 0xed, ZC3XX_R12B_GAMMA0B}, - {0xa0, 0xf3, ZC3XX_R12C_GAMMA0C}, - {0xa0, 0xf8, ZC3XX_R12D_GAMMA0D}, - {0xa0, 0xfb, ZC3XX_R12E_GAMMA0E}, - {0xa0, 0xff, ZC3XX_R12F_GAMMA0F}, - {0xa0, 0x26, ZC3XX_R130_GAMMA10}, - {0xa0, 0x23, ZC3XX_R131_GAMMA11}, - {0xa0, 0x20, ZC3XX_R132_GAMMA12}, - {0xa0, 0x1c, ZC3XX_R133_GAMMA13}, - {0xa0, 0x16, ZC3XX_R134_GAMMA14}, - {0xa0, 0x13, ZC3XX_R135_GAMMA15}, - {0xa0, 0x10, ZC3XX_R136_GAMMA16}, - {0xa0, 0x0d, ZC3XX_R137_GAMMA17}, - {0xa0, 0x0b, ZC3XX_R138_GAMMA18}, - {0xa0, 0x09, ZC3XX_R139_GAMMA19}, - {0xa0, 0x07, ZC3XX_R13A_GAMMA1A}, - {0xa0, 0x06, ZC3XX_R13B_GAMMA1B}, - {0xa0, 0x05, ZC3XX_R13C_GAMMA1C}, - {0xa0, 0x04, ZC3XX_R13D_GAMMA1D}, - {0xa0, 0x03, ZC3XX_R13E_GAMMA1E}, - {0xa0, 0x02, ZC3XX_R13F_GAMMA1F}, - {0xa0, 0x60, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xff, ZC3XX_R10B_RGB01}, - {0xa0, 0xff, ZC3XX_R10C_RGB02}, - {0xa0, 0xff, ZC3XX_R10D_RGB10}, - {0xa0, 0x60, ZC3XX_R10E_RGB11}, - {0xa0, 0xff, ZC3XX_R10F_RGB12}, - {0xa0, 0xff, ZC3XX_R110_RGB20}, - {0xa0, 0xff, ZC3XX_R111_RGB21}, - {0xa0, 0x60, ZC3XX_R112_RGB22}, - - {0xa1, 0x01, 0x0180}, - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, - {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, - {0xaa, 0x20, 0x0002}, - {0xaa, 0x21, 0x001b}, - {0xaa, 0x03, 0x0044}, - {0xaa, 0x04, 0x0008}, - {0xaa, 0x05, 0x001b}, - {0xaa, 0x0e, 0x0001}, - {0xaa, 0x0f, 0x0000}, - {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0x1b, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x4d, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x44, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0x6f, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0xad, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0xeb, ZC3XX_R020_HSYNC_3}, - {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, - {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, - {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa1, 0x01, 0x0195}, - {0xa1, 0x01, 0x0196}, - {0xa1, 0x01, 0x0197}, - {0xa0, 0x1b, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x1d, ZC3XX_R116_RGAIN}, - {0xa0, 0x40, ZC3XX_R117_GGAIN}, - {0xa0, 0x99, ZC3XX_R118_BGAIN}, - {0xa1, 0x01, 0x0116}, - {0xa1, 0x01, 0x0118}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x1d, ZC3XX_R116_RGAIN}, - {0xa0, 0x40, ZC3XX_R117_GGAIN}, - {0xa0, 0x99, ZC3XX_R118_BGAIN}, -/* {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING}, */ - {0xa0, 0x00, 0x0007}, -/* {0xa0, 0x18, 0x00fe}, */ - {} -}; static const struct usb_action hdcs2020xb_Initial[] = { {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x11, ZC3XX_R002_CLOCKSELECT}, @@ -4424,6 +4135,270 @@ static const struct usb_action pas106b_NoFliker[] = { {} }; +/* from usbvm31b.inf */ +static const struct usb_action pas202b_Initial[] = { /* 640x480 */ + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */ + {0xa0, 0x00, ZC3XX_R008_CLOCKSETTING}, /* 00,08,00,cc */ + {0xa0, 0x0e, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0e,cc */ + {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, /* 00,02,00,cc */ + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */ + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */ + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */ + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,e0,cc */ + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */ + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */ + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */ + {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, /* 00,8d,08,cc */ + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */ + {0xa0, 0x03, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,03,cc */ + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */ + {0xa0, 0x03, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,03,cc */ + {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, /* 00,9b,01,cc */ + {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,e6,cc */ + {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, /* 00,9d,02,cc */ + {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,86,cc */ + {0xaa, 0x02, 0x0002}, /* 00,02,04,aa --> 02 */ + {0xaa, 0x07, 0x0006}, /* 00,07,06,aa */ + {0xaa, 0x08, 0x0002}, /* 00,08,02,aa */ + {0xaa, 0x09, 0x0006}, /* 00,09,06,aa */ + {0xaa, 0x0a, 0x0001}, /* 00,0a,01,aa */ + {0xaa, 0x0b, 0x0001}, /* 00,0b,01,aa */ + {0xaa, 0x0c, 0x0008}, /* 00,0c,08,aa */ + {0xaa, 0x0d, 0x0000}, /* 00,0d,00,aa */ + {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */ + {0xaa, 0x12, 0x0005}, /* 00,12,05,aa */ + {0xaa, 0x13, 0x0063}, /* 00,13,63,aa */ + {0xaa, 0x15, 0x0070}, /* 00,15,70,aa */ + {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,b7,cc */ + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */ + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */ + {0xa0, 0x00, 0x01ad}, /* 01,ad,00,cc */ + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */ + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */ + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */ + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */ + {0xa0, 0x70, ZC3XX_R18D_YTARGET}, /* 01,8d,70,cc */ + {} +}; +static const struct usb_action pas202b_InitialScale[] = { /* 320x240 */ + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */ + {0xa0, 0x00, ZC3XX_R008_CLOCKSETTING}, /* 00,08,00,cc */ + {0xa0, 0x0e, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0e,cc */ + {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, /* 00,02,10,cc */ + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */ + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */ + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */ + {0xa0, 0xd0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,d0,cc */ + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */ + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */ + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */ + {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, /* 00,8d,08,cc */ + {0xa0, 0x08, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,08,cc */ + {0xa0, 0x02, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,02,cc */ + {0xa0, 0x08, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,08,cc */ + {0xa0, 0x02, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,02,cc */ + {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, /* 00,9b,01,cc */ + {0xa0, 0xd8, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,d8,cc */ + {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, /* 00,9d,02,cc */ + {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc */ + {0xaa, 0x02, 0x0002}, /* 00,02,02,aa */ + {0xaa, 0x07, 0x0006}, /* 00,07,06,aa */ + {0xaa, 0x08, 0x0002}, /* 00,08,02,aa */ + {0xaa, 0x09, 0x0006}, /* 00,09,06,aa */ + {0xaa, 0x0a, 0x0001}, /* 00,0a,01,aa */ + {0xaa, 0x0b, 0x0001}, /* 00,0b,01,aa */ + {0xaa, 0x0c, 0x0008}, /* 00,0c,08,aa */ + {0xaa, 0x0d, 0x0000}, /* 00,0d,00,aa */ + {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */ + {0xaa, 0x12, 0x0005}, /* 00,12,05,aa */ + {0xaa, 0x13, 0x0063}, /* 00,13,63,aa */ + {0xaa, 0x15, 0x0070}, /* 00,15,70,aa */ + {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,37,cc */ + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */ + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */ + {0xa0, 0x00, 0x01ad}, /* 01,ad,00,cc */ + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */ + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */ + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */ + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */ + {0xa0, 0x70, ZC3XX_R18D_YTARGET}, /* 01,8d,70,cc */ + {} +}; +static const struct usb_action pas202b_50HZ[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */ + {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */ + {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */ + {0xaa, 0x21, 0x0068}, /* 00,21,68,aa */ + {0xaa, 0x03, 0x0044}, /* 00,03,44,aa */ + {0xaa, 0x04, 0x0009}, /* 00,04,09,aa */ + {0xaa, 0x05, 0x0028}, /* 00,05,28,aa */ + {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */ + {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */ + {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,14,cc */ + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */ + {0xa0, 0xd2, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,d2,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x4d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,4d,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x44, ZC3XX_R01D_HSYNC_0}, /* 00,1d,44,cc */ + {0xa0, 0x6f, ZC3XX_R01E_HSYNC_1}, /* 00,1e,6f,cc */ + {0xa0, 0xad, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ad,cc */ + {0xa0, 0xeb, ZC3XX_R020_HSYNC_3}, /* 00,20,eb,cc */ + {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */ + {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */ + {} +}; +static const struct usb_action pas202b_50HZScale[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */ + {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */ + {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */ + {0xaa, 0x21, 0x006c}, /* 00,21,6c,aa */ + {0xaa, 0x03, 0x0041}, /* 00,03,41,aa */ + {0xaa, 0x04, 0x0009}, /* 00,04,09,aa */ + {0xaa, 0x05, 0x002c}, /* 00,05,2c,aa */ + {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */ + {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */ + {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,14,cc */ + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x0f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,0f,cc */ + {0xa0, 0xbe, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,be,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x9b, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,9b,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x41, ZC3XX_R01D_HSYNC_0}, /* 00,1d,41,cc */ + {0xa0, 0x6f, ZC3XX_R01E_HSYNC_1}, /* 00,1e,6f,cc */ + {0xa0, 0xad, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ad,cc */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ + {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */ + {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */ + {} +}; +static const struct usb_action pas202b_60HZ[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */ + {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */ + {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */ + {0xaa, 0x21, 0x0000}, /* 00,21,00,aa */ + {0xaa, 0x03, 0x0045}, /* 00,03,45,aa */ + {0xaa, 0x04, 0x0008}, /* 00,04,08,aa */ + {0xaa, 0x05, 0x0000}, /* 00,05,00,aa */ + {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */ + {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */ + {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,14,cc */ + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */ + {0xa0, 0xc0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,c0,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x40, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,40,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x45, ZC3XX_R01D_HSYNC_0}, /* 00,1d,45,cc */ + {0xa0, 0x8e, ZC3XX_R01E_HSYNC_1}, /* 00,1e,8e,cc */ + {0xa0, 0xc1, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c1,cc */ + {0xa0, 0xf5, ZC3XX_R020_HSYNC_3}, /* 00,20,f5,cc */ + {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */ + {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */ + {} +}; +static const struct usb_action pas202b_60HZScale[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */ + {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */ + {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */ + {0xaa, 0x21, 0x0004}, /* 00,21,04,aa */ + {0xaa, 0x03, 0x0042}, /* 00,03,42,aa */ + {0xaa, 0x04, 0x0008}, /* 00,04,08,aa */ + {0xaa, 0x05, 0x0004}, /* 00,05,04,aa */ + {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */ + {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */ + {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,14,cc */ + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x0f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,0f,cc */ + {0xa0, 0x9f, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,9f,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x81, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,81,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x42, ZC3XX_R01D_HSYNC_0}, /* 00,1d,42,cc */ + {0xa0, 0x6f, ZC3XX_R01E_HSYNC_1}, /* 00,1e,6f,cc */ + {0xa0, 0xaf, ZC3XX_R01F_HSYNC_2}, /* 00,1f,af,cc */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ + {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */ + {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */ + {} +}; +static const struct usb_action pas202b_NoFliker[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */ + {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */ + {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */ + {0xaa, 0x21, 0x0020}, /* 00,21,20,aa */ + {0xaa, 0x03, 0x0040}, /* 00,03,40,aa */ + {0xaa, 0x04, 0x0008}, /* 00,04,08,aa */ + {0xaa, 0x05, 0x0020}, /* 00,05,20,aa */ + {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */ + {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */ + {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x02, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,02,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */ + {0xa0, 0x40, ZC3XX_R01D_HSYNC_0}, /* 00,1d,40,cc */ + {0xa0, 0x60, ZC3XX_R01E_HSYNC_1}, /* 00,1e,60,cc */ + {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ + {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */ + {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */ + {} +}; +static const struct usb_action pas202b_NoFlikerScale[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */ + {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */ + {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */ + {0xaa, 0x21, 0x0010}, /* 00,21,10,aa */ + {0xaa, 0x03, 0x0040}, /* 00,03,40,aa */ + {0xaa, 0x04, 0x0008}, /* 00,04,08,aa */ + {0xaa, 0x05, 0x0010}, /* 00,05,10,aa */ + {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */ + {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x0f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,0f,cc */ + {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x02, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,02,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */ + {0xa0, 0x40, ZC3XX_R01D_HSYNC_0}, /* 00,1d,40,cc */ + {0xa0, 0x60, ZC3XX_R01E_HSYNC_1}, /* 00,1e,60,cc */ + {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ + {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */ + {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */ + {} +}; + static const struct usb_action pb03303x_Initial[] = { {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, @@ -6338,7 +6313,7 @@ static __u8 i2c_write(struct gspca_dev *gspca_dev, reg_w_i(gspca_dev->dev, valL, 0x93); reg_w_i(gspca_dev->dev, valH, 0x94); reg_w_i(gspca_dev->dev, 0x01, 0x90); /* <- write command */ - msleep(5); + msleep(15); retbyte = reg_r_i(gspca_dev, 0x0091); /* read status */ PDEBUG(D_USBO, "i2c w [%02x] = %02x%02x (%02x)", reg, valH, valL, retbyte); @@ -6381,31 +6356,35 @@ static void setmatrix(struct gspca_dev *gspca_dev) {0x50, 0xf8, 0xf8, 0xf8, 0x50, 0xf8, 0xf8, 0xf8, 0x50}; static const __u8 ov7620_matrix[9] = {0x58, 0xf4, 0xf4, 0xf4, 0x58, 0xf4, 0xf4, 0xf4, 0x58}; + static const __u8 pas202b_matrix[9] = + {0x4c, 0xf5, 0xff, 0xf9, 0x51, 0xf5, 0xfb, 0xed, 0x5f}; static const __u8 po2030_matrix[9] = {0x60, 0xf0, 0xf0, 0xf0, 0x60, 0xf0, 0xf0, 0xf0, 0x60}; static const __u8 vf0250_matrix[9] = {0x7b, 0xea, 0xea, 0xea, 0x7b, 0xea, 0xea, 0xea, 0x7b}; + static const __u8 *matrix_tb[SENSOR_MAX] = { + NULL, /* SENSOR_CS2102 0 */ + NULL, /* SENSOR_CS2102K 1 */ + gc0305_matrix, /* SENSOR_GC0305 2 */ + NULL, /* SENSOR_HDCS2020b 3 */ + NULL, /* SENSOR_HV7131B 4 */ + NULL, /* SENSOR_HV7131C 5 */ + NULL, /* SENSOR_ICM105A 6 */ + NULL, /* SENSOR_MC501CB 7 */ + ov7620_matrix, /* SENSOR_OV7620 8 */ + NULL, /* SENSOR_OV7630C 9 */ + NULL, /* SENSOR_PAS106 10 */ + pas202b_matrix, /* SENSOR_PAS202B 11 */ + NULL, /* SENSOR_PB0330 12 */ + po2030_matrix, /* SENSOR_PO2030 13 */ + NULL, /* SENSOR_TAS5130CK 14 */ + NULL, /* SENSOR_TAS5130CXX 15 */ + vf0250_matrix, /* SENSOR_TAS5130C_VF0250 16 */ + }; - switch (sd->sensor) { - case SENSOR_GC0305: - case SENSOR_HV7131B: - matrix = gc0305_matrix; - break; - case SENSOR_MC501CB: - return; /* no matrix? */ - case SENSOR_OV7620: -/* case SENSOR_OV7648: */ - matrix = ov7620_matrix; - break; - case SENSOR_PO2030: - matrix = po2030_matrix; - break; - case SENSOR_TAS5130C_VF0250: - matrix = vf0250_matrix; - break; - default: /* matrix already loaded */ - return; - } + matrix = matrix_tb[sd->sensor]; + if (matrix == NULL) + return; /* matrix already loaded */ for (i = 0; i < ARRAY_SIZE(ov7620_matrix); i++) reg_w(gspca_dev->dev, matrix[i], 0x010a + i); } @@ -6621,42 +6600,42 @@ static int setlightfreq(struct gspca_dev *gspca_dev) {gc0305_NoFliker, gc0305_NoFliker, gc0305_50HZ, gc0305_50HZ, gc0305_60HZ, gc0305_60HZ}, -/* SENSOR_HDCS2020 3 */ - {NULL, NULL, - NULL, NULL, - NULL, NULL}, -/* SENSOR_HDCS2020b 4 */ +/* SENSOR_HDCS2020b 3 */ {hdcs2020b_NoFliker, hdcs2020b_NoFliker, hdcs2020b_50HZ, hdcs2020b_50HZ, hdcs2020b_60HZ, hdcs2020b_60HZ}, -/* SENSOR_HV7131B 5 */ +/* SENSOR_HV7131B 4 */ {hv7131b_NoFlikerScale, hv7131b_NoFliker, hv7131b_50HZScale, hv7131b_50HZ, hv7131b_60HZScale, hv7131b_60HZ}, -/* SENSOR_HV7131C 6 */ +/* SENSOR_HV7131C 5 */ {NULL, NULL, NULL, NULL, NULL, NULL}, -/* SENSOR_ICM105A 7 */ +/* SENSOR_ICM105A 6 */ {icm105a_NoFliker, icm105a_NoFlikerScale, icm105a_50HZ, icm105a_50HZScale, icm105a_60HZ, icm105a_60HZScale}, -/* SENSOR_MC501CB 8 */ +/* SENSOR_MC501CB 7 */ {MC501CB_NoFliker, MC501CB_NoFlikerScale, MC501CB_50HZ, MC501CB_50HZScale, MC501CB_60HZ, MC501CB_60HZScale}, -/* SENSOR_OV7620 9 */ +/* SENSOR_OV7620 8 */ {OV7620_NoFliker, OV7620_NoFliker, OV7620_50HZ, OV7620_50HZ, OV7620_60HZ, OV7620_60HZ}, -/* SENSOR_OV7630C 10 */ +/* SENSOR_OV7630C 9 */ {NULL, NULL, NULL, NULL, NULL, NULL}, -/* SENSOR_PAS106 11 */ +/* SENSOR_PAS106 10 */ {pas106b_NoFliker, pas106b_NoFliker, pas106b_50HZ, pas106b_50HZ, pas106b_60HZ, pas106b_60HZ}, +/* SENSOR_PAS202B 11 */ + {pas202b_NoFlikerScale, pas202b_NoFliker, + pas202b_50HZScale, pas202b_50HZ, + pas202b_60HZScale, pas202b_60HZ}, /* SENSOR_PB0330 12 */ {pb0330_NoFliker, pb0330_NoFlikerScale, pb0330_50HZ, pb0330_50HZScale, @@ -7038,15 +7017,15 @@ static int sd_config(struct gspca_dev *gspca_dev, 5, /* SENSOR_CS2102 0 */ 5, /* SENSOR_CS2102K 1 */ 4, /* SENSOR_GC0305 2 */ - 4, /* SENSOR_HDCS2020 3 */ - 4, /* SENSOR_HDCS2020b 4 */ - 4, /* SENSOR_HV7131B 5 */ - 4, /* SENSOR_HV7131C 6 */ - 4, /* SENSOR_ICM105A 7 */ - 4, /* SENSOR_MC501CB 8 */ - 3, /* SENSOR_OV7620 9 */ - 4, /* SENSOR_OV7630C 10 */ - 4, /* SENSOR_PAS106 11 */ + 4, /* SENSOR_HDCS2020b 3 */ + 4, /* SENSOR_HV7131B 4 */ + 4, /* SENSOR_HV7131C 5 */ + 4, /* SENSOR_ICM105A 6 */ + 4, /* SENSOR_MC501CB 7 */ + 3, /* SENSOR_OV7620 8 */ + 4, /* SENSOR_OV7630C 9 */ + 4, /* SENSOR_PAS106 10 */ + 4, /* SENSOR_PAS202B 11 */ 4, /* SENSOR_PB0330 12 */ 4, /* SENSOR_PO2030 13 */ 4, /* SENSOR_TAS5130CK 14 */ @@ -7102,8 +7081,8 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->sensor = SENSOR_ICM105A; break; case 0x0e: - PDEBUG(D_PROBE, "Find Sensor HDCS2020"); - sd->sensor = SENSOR_HDCS2020; + PDEBUG(D_PROBE, "Find Sensor PAS202B"); + sd->sensor = SENSOR_PAS202B; sd->sharpness = 1; break; case 0x0f: @@ -7196,7 +7175,6 @@ static int sd_config(struct gspca_dev *gspca_dev, case SENSOR_PO2030: gspca_dev->ctrl_dis = (1 << BRIGHTNESS_IDX); break; - case SENSOR_HDCS2020: case SENSOR_HV7131B: case SENSOR_HV7131C: case SENSOR_OV7630C: @@ -7226,15 +7204,15 @@ static int sd_start(struct gspca_dev *gspca_dev) {cs2102_InitialScale, cs2102_Initial}, /* 0 */ {cs2102K_InitialScale, cs2102K_Initial}, /* 1 */ {gc0305_Initial, gc0305_InitialScale}, /* 2 */ - {hdcs2020xx_InitialScale, hdcs2020xx_Initial}, /* 3 */ - {hdcs2020xb_InitialScale, hdcs2020xb_Initial}, /* 4 */ - {hv7131bxx_InitialScale, hv7131bxx_Initial}, /* 5 */ - {hv7131cxx_InitialScale, hv7131cxx_Initial}, /* 6 */ - {icm105axx_InitialScale, icm105axx_Initial}, /* 7 */ - {MC501CB_InitialScale, MC501CB_Initial}, /* 9 */ - {OV7620_mode0, OV7620_mode1}, /* 9 */ - {ov7630c_InitialScale, ov7630c_Initial}, /* 10 */ - {pas106b_InitialScale, pas106b_Initial}, /* 11 */ + {hdcs2020xb_InitialScale, hdcs2020xb_Initial}, /* 3 */ + {hv7131bxx_InitialScale, hv7131bxx_Initial}, /* 4 */ + {hv7131cxx_InitialScale, hv7131cxx_Initial}, /* 5 */ + {icm105axx_InitialScale, icm105axx_Initial}, /* 6 */ + {MC501CB_InitialScale, MC501CB_Initial}, /* 7 */ + {OV7620_mode0, OV7620_mode1}, /* 8 */ + {ov7630c_InitialScale, ov7630c_Initial}, /* 9 */ + {pas106b_InitialScale, pas106b_Initial}, /* 10 */ + {pas202b_Initial, pas202b_InitialScale}, /* 11 */ {pb0330xx_InitialScale, pb0330xx_Initial}, /* 12 */ /* or {pb03303x_InitialScale, pb03303x_Initial}, */ {PO2030_mode0, PO2030_mode1}, /* 13 */ @@ -7291,6 +7269,11 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_r(gspca_dev, 0x0008); reg_w(dev, 0x00, 0x0008); break; + case SENSOR_PAS202B: +#if 0/*fixme*/ + reg_r(gspca_dev, ZC3XX_R002_CLOCKSELECT); + /* fall thru */ +#endif case SENSOR_GC0305: reg_r(gspca_dev, 0x0008); /* fall thru */ @@ -7304,7 +7287,6 @@ static int sd_start(struct gspca_dev *gspca_dev) switch (sd->sensor) { case SENSOR_CS2102: /* gamma set in xxx_Initial */ case SENSOR_CS2102K: - case SENSOR_HDCS2020: case SENSOR_HDCS2020b: case SENSOR_PB0330: /* pb with chip_revision - see above */ case SENSOR_OV7630C: @@ -7317,6 +7299,7 @@ static int sd_start(struct gspca_dev *gspca_dev) setmatrix(gspca_dev); /* one more time? */ switch (sd->sensor) { case SENSOR_OV7620: + case SENSOR_PAS202B: reg_r(gspca_dev, 0x0180); /* from win */ reg_w(dev, 0x00, 0x0180); break; @@ -7328,37 +7311,29 @@ static int sd_start(struct gspca_dev *gspca_dev) switch (sd->sensor) { case SENSOR_GC0305: - case SENSOR_OV7620: reg_w(dev, 0x09, 0x01ad); /* (from win traces) */ reg_w(dev, 0x15, 0x01ae); - sd->autogain = 0; - break; + /* fall thru */ + case SENSOR_PAS202B: case SENSOR_PO2030: - reg_w(dev, 0x40, 0x0117); /* (from win traces) */ - reg_r(gspca_dev, 0x0180); - break; - } - - setautogain(gspca_dev); - switch (sd->sensor) { - case SENSOR_GC0305: -/* setlightfreq(gspca_dev); ?? (end: 80 -> [18d]) */ - reg_w(dev, 0x09, 0x01ad); /* (from win traces) */ - reg_w(dev, 0x15, 0x01ae); - reg_w(dev, 0x40, 0x0180); - reg_w(dev, 0x40, 0x0117); +/* reg_w(dev, 0x40, ZC3XX_R117_GGAIN); * (from win traces) */ reg_r(gspca_dev, 0x0180); - sd->autogain = 1; - setautogain(gspca_dev); break; case SENSOR_OV7620: + reg_w(dev, 0x09, 0x01ad); + reg_w(dev, 0x15, 0x01ae); i2c_read(gspca_dev, 0x13); /*fixme: returns 0xa3 */ i2c_write(gspca_dev, 0x13, 0xa3, 0x00); /*fixme: returned value to send? */ - reg_w(dev, 0x40, 0x0117); /* (from win traces) */ + reg_w(dev, 0x40, 0x0117); reg_r(gspca_dev, 0x0180); - setautogain(gspca_dev); - msleep(500); + break; + } + + setautogain(gspca_dev); + switch (sd->sensor) { + case SENSOR_PAS202B: + reg_w(dev, 0x00, 0x0007); /* (from win traces) */ break; case SENSOR_PO2030: msleep(500); @@ -7368,6 +7343,8 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_w(dev, 0x02, 0x0008); break; } + if (sd->sensor == SENSOR_PAS202B) + reg_w(dev, 0x02, ZC3XX_R008_CLOCKSETTING); return 0; } -- cgit v1.2.3 From f604f345801f710bdeeb769a51b4e203c91e5f24 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 8 Dec 2008 11:36:57 -0200 Subject: tda8290: fix FM radio From: Mauro Carvalho Chehab tda8290 were using some random video standard for FM. This results on random errors. Instead, program tda8290 in expert mode, using a configuration near the one specified on NXP datasheet for tda8295 (available on their site). Also, properly display that the device is on radio mode. Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/common/tuners/tda8290.c | 63 ++++++++++++++++++++++++----- 1 file changed, 53 insertions(+), 10 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/common/tuners/tda8290.c b/linux/drivers/media/common/tuners/tda8290.c index 637849ec8..6eaaacca0 100644 --- a/linux/drivers/media/common/tuners/tda8290.c +++ b/linux/drivers/media/common/tuners/tda8290.c @@ -33,6 +33,9 @@ static int debug; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "enable verbose debug messages"); +static int deemphasis_50; +MODULE_PARM_DESC(deemphasis_50, "0 - 75us deemphasis; 1 - 50us deemphasis"); + /* ---------------------------------------------------------------------- */ struct tda8290_priv { @@ -162,9 +165,34 @@ static void set_audio(struct dvb_frontend *fe, mode = "xx"; } - tuner_dbg("setting tda829x to system %s\n", mode); + if (params->mode == V4L2_TUNER_RADIO) { + priv->tda8290_easy_mode = 0x01; /* Start with MN values */ + tuner_dbg("setting to radio FM\n"); + } else { + tuner_dbg("setting tda829x to system %s\n", mode); + } } +struct { + unsigned char seq[2]; +} fm_mode[] = { + { { 0x01, 0x81} }, /* Put device into expert mode */ + { { 0x03, 0x48} }, /* Disable NOTCH and VIDEO filters */ + { { 0x04, 0x04} }, /* Disable color carrier filter (SSIF) */ + { { 0x05, 0x04} }, /* ADC headroom */ + { { 0x06, 0x10} }, /* group delay flat */ + + { { 0x07, 0x00} }, /* use the same radio DTO values as a tda8295 */ + { { 0x08, 0x00} }, + { { 0x09, 0x80} }, + { { 0x0a, 0xda} }, + { { 0x0b, 0x4b} }, + { { 0x0c, 0x68} }, + + { { 0x0d, 0x00} }, /* PLL off, no video carrier detect */ + { { 0x14, 0x00} }, /* disable auto mute if no video */ +}; + static void tda8290_set_params(struct dvb_frontend *fe, struct analog_parameters *params) { @@ -201,15 +229,30 @@ static void tda8290_set_params(struct dvb_frontend *fe, tuner_i2c_xfer_send(&priv->i2c_props, soft_reset, 2); msleep(1); - expert_mode[1] = priv->tda8290_easy_mode + 0x80; - tuner_i2c_xfer_send(&priv->i2c_props, expert_mode, 2); - tuner_i2c_xfer_send(&priv->i2c_props, gainset_off, 2); - tuner_i2c_xfer_send(&priv->i2c_props, if_agc_spd, 2); - if (priv->tda8290_easy_mode & 0x60) - tuner_i2c_xfer_send(&priv->i2c_props, adc_head_9, 2); - else - tuner_i2c_xfer_send(&priv->i2c_props, adc_head_6, 2); - tuner_i2c_xfer_send(&priv->i2c_props, pll_bw_nom, 2); + if (params->mode == V4L2_TUNER_RADIO) { + int i; + unsigned char deemphasis[] = { 0x13, 1 }; + + /* FIXME: allow using a different deemphasis */ + + if (deemphasis_50) + deemphasis[1] = 2; + + for (i = 0; i < ARRAY_SIZE(fm_mode); i++) + tuner_i2c_xfer_send(&priv->i2c_props, fm_mode[i].seq, 2); + + tuner_i2c_xfer_send(&priv->i2c_props, deemphasis, 2); + } else { + expert_mode[1] = priv->tda8290_easy_mode + 0x80; + tuner_i2c_xfer_send(&priv->i2c_props, expert_mode, 2); + tuner_i2c_xfer_send(&priv->i2c_props, gainset_off, 2); + tuner_i2c_xfer_send(&priv->i2c_props, if_agc_spd, 2); + if (priv->tda8290_easy_mode & 0x60) + tuner_i2c_xfer_send(&priv->i2c_props, adc_head_9, 2); + else + tuner_i2c_xfer_send(&priv->i2c_props, adc_head_6, 2); + tuner_i2c_xfer_send(&priv->i2c_props, pll_bw_nom, 2); + } tda8290_i2c_bridge(fe, 1); -- cgit v1.2.3 From 97b478e31ccddc358a73628542bb2d32983abeac Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 9 Dec 2008 16:43:10 -0200 Subject: em28xx: fix Kworld Hybrid 330 (A316) support From: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-cards.c | 8 +++----- linux/drivers/media/video/em28xx/em28xx-dvb.c | 1 + 2 files changed, 4 insertions(+), 5 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 2ecc98af7..23d3820a2 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -1238,19 +1238,17 @@ struct em28xx_board em28xx_boards[] = { }, [EM2883_BOARD_KWORLD_HYBRID_A316] = { .name = "Kworld PlusTV HD Hybrid 330", - .valid = EM28XX_BOARD_NOT_VALIDATED, .tuner_type = TUNER_XC2028, .tuner_gpio = default_tuner_gpio, .decoder = EM28XX_TVP5150, -#if 0 /* FIXME: add an entry at em28xx-dvb */ + .mts_firmware = 1, .has_dvb = 1, - .dvb_gpio = hauppauge_wintv_hvr_900_digital, -#endif + .dvb_gpio = default_digital, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, .amux = EM28XX_AMUX_VIDEO, - .gpio = hauppauge_wintv_hvr_900_analog, + .gpio = default_analog, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, diff --git a/linux/drivers/media/video/em28xx/em28xx-dvb.c b/linux/drivers/media/video/em28xx/em28xx-dvb.c index ddc4f6a99..8d3ed7dc8 100644 --- a/linux/drivers/media/video/em28xx/em28xx-dvb.c +++ b/linux/drivers/media/video/em28xx/em28xx-dvb.c @@ -413,6 +413,7 @@ static int dvb_init(struct em28xx *dev) case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850: case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950: case EM2880_BOARD_PINNACLE_PCTV_HD_PRO: + case EM2883_BOARD_KWORLD_HYBRID_A316: case EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600: dvb->frontend = dvb_attach(lgdt330x_attach, &em2880_lgdt3303_dev, -- cgit v1.2.3 From 08ec824664f6a447c209655719260db8b329576a Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Wed, 10 Dec 2008 09:06:13 +0100 Subject: gspca - vc032x: Webcam 15b8:6002 and sensor po1200 added. From: Jean-Francois Moine Priority: normal Signed-off-by: Jean-Francois Moine --- linux/Documentation/video4linux/gspca.txt | 1 + linux/drivers/media/video/gspca/vc032x.c | 331 +++++++++++++++++++++++++++++- 2 files changed, 324 insertions(+), 8 deletions(-) (limited to 'linux') diff --git a/linux/Documentation/video4linux/gspca.txt b/linux/Documentation/video4linux/gspca.txt index a38c87246..81d7d891c 100644 --- a/linux/Documentation/video4linux/gspca.txt +++ b/linux/Documentation/video4linux/gspca.txt @@ -271,6 +271,7 @@ spca561 10fd:7e50 FlyCam Usb 100 zc3xx 10fd:8050 Typhoon Webshot II USB 300k ov534 1415:2000 Sony HD Eye for PS3 (SLEH 00201) pac207 145f:013a Trust WB-1300N +vc032x 15b8:6002 HP 2.0 Megapixel rz406aa spca501 1776:501c Arowana 300K CMOS Camera t613 17a1:0128 TASCORP JPEG Webcam, NGS Cyclops vc032x 17ef:4802 Lenovo Vc0323+MI1310_SOC diff --git a/linux/drivers/media/video/gspca/vc032x.c b/linux/drivers/media/video/gspca/vc032x.c index 244f30fc6..b898f6def 100644 --- a/linux/drivers/media/video/gspca/vc032x.c +++ b/linux/drivers/media/video/gspca/vc032x.c @@ -47,7 +47,8 @@ struct sd { #define SENSOR_MI1310_SOC 3 #define SENSOR_OV7660 4 #define SENSOR_OV7670 5 -#define SENSOR_PO3130NC 6 +#define SENSOR_PO1200 6 +#define SENSOR_PO3130NC 7 }; /* V4L2 controls supported by the driver */ @@ -132,6 +133,14 @@ static struct v4l2_pix_format vc0323_mode[] = { .priv = 0}, }; +static struct v4l2_pix_format svga_mode[] = { + {800, 600, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 800, + .sizeimage = 800 * 600 * 1 / 4 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, +}; + /* OV7660/7670 registers */ #define OV7660_REG_MVFP 0x1e #define OV7660_MVFP_MIRROR 0x20 @@ -1466,6 +1475,293 @@ static const __u8 ov7670_initQVGA_JPG[][4] = { {0x00, 0x77, 0x05, 0xaa }, {}, }; + +/* PO1200 - values from usbvm326.inf and ms-win trace */ +static const __u8 po1200_gamma[17] = { +#if 1 + 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8, + 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff +#else +/*ms-win trace*/ + 0x01, 0x0b, 0x1e, 0x38, 0x51, 0x6b, 0x83, 0x9a, 0xaf, + 0xc1, 0xd0, 0xdd, 0xe8, 0xf2, 0xf9, 0xff, 0xff +#endif +}; +static const __u8 po1200_matrix[9] = { + 0x60, 0xf9, 0xe5, 0xe7, 0x50, 0x05, 0xf3, 0xe6, 0x5e +}; +static const __u8 po1200_initVGA_data[][4] = { + {0xb0, 0x03, 0x19, 0xcc}, /* reset? */ +#if 0 + {0x00, 0x00, 0x64, 0xdd}, + {0xb3, 0x49, 0x11, 0xcc}, + {0x00, 0x00, 0x33, 0xdd}, +/*read b349*/ +#endif + {0xb0, 0x03, 0x19, 0xcc}, +/* {0x00, 0x00, 0x33, 0xdd}, */ + {0xb0, 0x04, 0x02, 0xcc}, + {0xb0, 0x02, 0x02, 0xcc}, + {0xb3, 0x5d, 0x00, 0xcc}, + {0xb3, 0x01, 0x01, 0xcc}, + {0xb3, 0x00, 0x64, 0xcc}, + {0xb3, 0x00, 0x65, 0xcc}, + {0xb3, 0x05, 0x01, 0xcc}, + {0xb3, 0x06, 0x01, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x00, 0x67, 0xcc}, + {0xb3, 0x02, 0xb2, 0xcc}, + {0xb3, 0x03, 0x18, 0xcc}, + {0xb3, 0x04, 0x15, 0xcc}, + {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x22, 0x02, 0xcc}, + {0xb3, 0x23, 0x58, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, + {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x03, 0xcc}, + {0xb3, 0x17, 0x1f, 0xcc}, + {0xbc, 0x00, 0x71, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, + {0xb0, 0x54, 0x13, 0xcc}, + {0xb3, 0x00, 0x67, 0xcc}, + {0xb3, 0x34, 0x01, 0xcc}, + {0xb3, 0x35, 0xdc, 0xcc}, + {0x00, 0x03, 0x00, 0xaa}, + {0x00, 0x12, 0x05, 0xaa}, + {0x00, 0x13, 0x02, 0xaa}, + {0x00, 0x1e, 0xc6, 0xaa}, + {0x00, 0x21, 0x00, 0xaa}, + {0x00, 0x25, 0x02, 0xaa}, + {0x00, 0x3c, 0x4f, 0xaa}, + {0x00, 0x3f, 0xe0, 0xaa}, + {0x00, 0x42, 0xff, 0xaa}, + {0x00, 0x45, 0x34, 0xaa}, + {0x00, 0x55, 0xfe, 0xaa}, + {0x00, 0x59, 0xd3, 0xaa}, + {0x00, 0x5e, 0x04, 0xaa}, + {0x00, 0x61, 0xb8, 0xaa}, + {0x00, 0x62, 0x02, 0xaa}, + {0x00, 0xa7, 0x31, 0xaa}, + {0x00, 0xa9, 0x66, 0xaa}, + {0x00, 0xb0, 0x00, 0xaa}, + {0x00, 0xb1, 0x00, 0xaa}, + {0x00, 0xb3, 0x11, 0xaa}, + {0x00, 0xb6, 0x26, 0xaa}, + {0x00, 0xb7, 0x20, 0xaa}, + {0x00, 0xba, 0x04, 0xaa}, + {0x00, 0x88, 0x42, 0xaa}, + {0x00, 0x89, 0x9a, 0xaa}, + {0x00, 0x8a, 0x88, 0xaa}, + {0x00, 0x8b, 0x8e, 0xaa}, + {0x00, 0x8c, 0x3e, 0xaa}, + {0x00, 0x8d, 0x90, 0xaa}, + {0x00, 0x8e, 0x87, 0xaa}, + {0x00, 0x8f, 0x96, 0xaa}, + {0x00, 0x90, 0x3d, 0xaa}, + {0x00, 0x64, 0x00, 0xaa}, + {0x00, 0x65, 0x10, 0xaa}, + {0x00, 0x66, 0x20, 0xaa}, + {0x00, 0x67, 0x2b, 0xaa}, + {0x00, 0x68, 0x36, 0xaa}, + {0x00, 0x69, 0x49, 0xaa}, + {0x00, 0x6a, 0x5a, 0xaa}, + {0x00, 0x6b, 0x7f, 0xaa}, + {0x00, 0x6c, 0x9b, 0xaa}, + {0x00, 0x6d, 0xba, 0xaa}, + {0x00, 0x6e, 0xd4, 0xaa}, + {0x00, 0x6f, 0xea, 0xaa}, + {0x00, 0x70, 0x00, 0xaa}, + {0x00, 0x71, 0x10, 0xaa}, + {0x00, 0x72, 0x20, 0xaa}, + {0x00, 0x73, 0x2b, 0xaa}, + {0x00, 0x74, 0x36, 0xaa}, + {0x00, 0x75, 0x49, 0xaa}, + {0x00, 0x76, 0x5a, 0xaa}, + {0x00, 0x77, 0x7f, 0xaa}, + {0x00, 0x78, 0x9b, 0xaa}, + {0x00, 0x79, 0xba, 0xaa}, + {0x00, 0x7a, 0xd4, 0xaa}, + {0x00, 0x7b, 0xea, 0xaa}, + {0x00, 0x7c, 0x00, 0xaa}, + {0x00, 0x7d, 0x10, 0xaa}, + {0x00, 0x7e, 0x20, 0xaa}, + {0x00, 0x7f, 0x2b, 0xaa}, + {0x00, 0x80, 0x36, 0xaa}, + {0x00, 0x81, 0x49, 0xaa}, + {0x00, 0x82, 0x5a, 0xaa}, + {0x00, 0x83, 0x7f, 0xaa}, + {0x00, 0x84, 0x9b, 0xaa}, + {0x00, 0x85, 0xba, 0xaa}, + {0x00, 0x86, 0xd4, 0xaa}, + {0x00, 0x87, 0xea, 0xaa}, + {0x00, 0x57, 0x2a, 0xaa}, + {0x00, 0x03, 0x01, 0xaa}, + {0x00, 0x04, 0x10, 0xaa}, + {0x00, 0x05, 0x10, 0xaa}, + {0x00, 0x06, 0x10, 0xaa}, + {0x00, 0x07, 0x10, 0xaa}, + {0x00, 0x08, 0x13, 0xaa}, + {0x00, 0x0a, 0x00, 0xaa}, + {0x00, 0x0b, 0x10, 0xaa}, + {0x00, 0x0c, 0x20, 0xaa}, + {0x00, 0x0d, 0x18, 0xaa}, + {0x00, 0x22, 0x01, 0xaa}, + {0x00, 0x23, 0x60, 0xaa}, + {0x00, 0x25, 0x08, 0xaa}, + {0x00, 0x26, 0x82, 0xaa}, + {0x00, 0x2e, 0x0f, 0xaa}, + {0x00, 0x2f, 0x1e, 0xaa}, + {0x00, 0x30, 0x2d, 0xaa}, + {0x00, 0x31, 0x3c, 0xaa}, + {0x00, 0x32, 0x4b, 0xaa}, + {0x00, 0x33, 0x5a, 0xaa}, + {0x00, 0x34, 0x69, 0xaa}, + {0x00, 0x35, 0x78, 0xaa}, + {0x00, 0x36, 0x87, 0xaa}, + {0x00, 0x37, 0x96, 0xaa}, + {0x00, 0x38, 0xa5, 0xaa}, + {0x00, 0x39, 0xb4, 0xaa}, + {0x00, 0x3a, 0xc3, 0xaa}, + {0x00, 0x3b, 0xd2, 0xaa}, + {0x00, 0x3c, 0xe1, 0xaa}, + {0x00, 0x3e, 0xff, 0xaa}, + {0x00, 0x3f, 0xff, 0xaa}, + {0x00, 0x40, 0xff, 0xaa}, + {0x00, 0x41, 0xff, 0xaa}, + {0x00, 0x42, 0xff, 0xaa}, + {0x00, 0x43, 0xff, 0xaa}, + {0x00, 0x03, 0x00, 0xaa}, + {0x00, 0x03, 0x00, 0xaa}, + {0x00, 0x20, 0xc4, 0xaa}, + {0x00, 0x13, 0x03, 0xaa}, + {0x00, 0x3c, 0x50, 0xaa}, + {0x00, 0x61, 0x6a, 0xaa}, + {0x00, 0x51, 0x5b, 0xaa}, + {0x00, 0x52, 0x91, 0xaa}, + {0x00, 0x53, 0x4c, 0xaa}, + {0x00, 0x54, 0x50, 0xaa}, + {0x00, 0x56, 0x02, 0xaa}, + {0xb6, 0x00, 0x00, 0xcc}, + {0xb6, 0x03, 0x03, 0xcc}, + {0xb6, 0x02, 0x20, 0xcc}, + {0xb6, 0x05, 0x02, 0xcc}, + {0xb6, 0x04, 0x58, 0xcc}, + {0xb6, 0x12, 0xf8, 0xcc}, + {0xb6, 0x13, 0x21, 0xcc}, + {0xb6, 0x18, 0x03, 0xcc}, + {0xb6, 0x17, 0xa9, 0xcc}, + {0xb6, 0x16, 0x80, 0xcc}, + {0xb6, 0x22, 0x12, 0xcc}, + {0xb6, 0x23, 0x0b, 0xcc}, + {0xbf, 0xc0, 0x39, 0xcc}, + {0xbf, 0xc1, 0x04, 0xcc}, + {0xbf, 0xcc, 0x00, 0xcc}, + {0xb8, 0x06, 0x20, 0xcc}, + {0xb8, 0x07, 0x03, 0xcc}, + {0xb8, 0x08, 0x58, 0xcc}, + {0xb8, 0x09, 0x02, 0xcc}, + {0xb3, 0x01, 0x41, 0xcc}, + {0x00, 0x03, 0x00, 0xaa}, + {0x00, 0xd9, 0x0f, 0xaa}, + {0x00, 0xda, 0xaa, 0xaa}, + {0x00, 0xd9, 0x10, 0xaa}, + {0x00, 0xda, 0xaa, 0xaa}, + {0x00, 0xd9, 0x11, 0xaa}, + {0x00, 0xda, 0x00, 0xaa}, + {0x00, 0xd9, 0x12, 0xaa}, + {0x00, 0xda, 0xff, 0xaa}, + {0x00, 0xd9, 0x13, 0xaa}, + {0x00, 0xda, 0xff, 0xaa}, + {0x00, 0xe8, 0x11, 0xaa}, + {0x00, 0xe9, 0x12, 0xaa}, + {0x00, 0xea, 0x5c, 0xaa}, + {0x00, 0xeb, 0xff, 0xaa}, + {0x00, 0xd8, 0x80, 0xaa}, + {0x00, 0xe6, 0x02, 0xaa}, + {0x00, 0xd6, 0x40, 0xaa}, + {0x00, 0xe3, 0x05, 0xaa}, + {0x00, 0xe0, 0x40, 0xaa}, + {0x00, 0xde, 0x03, 0xaa}, + {0x00, 0xdf, 0x03, 0xaa}, + {0x00, 0xdb, 0x02, 0xaa}, + {0x00, 0xdc, 0x00, 0xaa}, + {0x00, 0xdd, 0x03, 0xaa}, + {0x00, 0xe1, 0x08, 0xaa}, + {0x00, 0xe2, 0x01, 0xaa}, + {0x00, 0xd6, 0x40, 0xaa}, + {0x00, 0xe4, 0x40, 0xaa}, + {0x00, 0xa8, 0x9f, 0xaa}, + {0x00, 0xb4, 0x16, 0xaa}, + {0xb0, 0x02, 0x06, 0xcc}, + {0xb0, 0x18, 0x06, 0xcc}, + {0xb0, 0x19, 0x06, 0xcc}, + {0xb3, 0x5d, 0x18, 0xcc}, + {0xb3, 0x05, 0x00, 0xcc}, + {0xb3, 0x06, 0x00, 0xcc}, + {0x00, 0xb4, 0x0e, 0xaa}, + {0x00, 0xb5, 0x49, 0xaa}, + {0x00, 0xb6, 0x1c, 0xaa}, + {0x00, 0xb7, 0x96, 0xaa}, +/* end of usbvm326.inf - start of ms-win trace */ + {0xb6, 0x12, 0xf8, 0xcc}, + {0xb6, 0x13, 0x3d, 0xcc}, +/*read b306*/ + {0x00, 0x03, 0x00, 0xaa}, + {0x00, 0x1a, 0x09, 0xaa}, + {0x00, 0x1b, 0x8a, 0xaa}, +/*read b827*/ + {0xb8, 0x27, 0x00, 0xcc}, + {0xb8, 0x26, 0x60, 0xcc}, + {0xb8, 0x26, 0x60, 0xcc}, +/*gamma - to do?*/ + {0x00, 0x03, 0x00, 0xaa}, + {0x00, 0xae, 0x84, 0xaa}, +/*gamma again*/ + {0x00, 0x03, 0x00, 0xaa}, + {0x00, 0x96, 0xa0, 0xaa}, +/*matrix*/ + {0x00, 0x03, 0x00, 0xaa}, + {0x00, 0x91, 0x35, 0xaa}, + {0x00, 0x92, 0x22, 0xaa}, +/*gamma*/ + {0x00, 0x03, 0x00, 0xaa}, + {0x00, 0x95, 0x85, 0xaa}, +/*matrix*/ + {0x00, 0x03, 0x00, 0xaa}, + {0x00, 0x61, 0xb8, 0xaa}, + {0x00, 0x03, 0x00, 0xaa}, + {0x00, 0x4d, 0x20, 0xaa}, + {0xb8, 0x22, 0x40, 0xcc}, + {0xb8, 0x23, 0x40, 0xcc}, + {0xb8, 0x24, 0x40, 0xcc}, + {0xb8, 0x81, 0x09, 0xcc}, + {0x00, 0x00, 0x64, 0xdd}, + {0x00, 0x03, 0x01, 0xaa}, +/*read 46*/ + {0x00, 0x46, 0x3c, 0xaa}, + {0x00, 0x03, 0x00, 0xaa}, + {0x00, 0x16, 0x40, 0xaa}, + {0x00, 0x17, 0x40, 0xaa}, + {0x00, 0x18, 0x40, 0xaa}, + {0x00, 0x19, 0x41, 0xaa}, + {0x00, 0x03, 0x01, 0xaa}, + {0x00, 0x46, 0x3c, 0xaa}, + {0x00, 0x00, 0x18, 0xdd}, +/*read bfff*/ + {0x00, 0x03, 0x00, 0xaa}, + {0x00, 0x1e, 0x46, 0xaa}, + {0x00, 0xa8, 0x8f, 0xaa}, + {0x00, 0x03, 0x00, 0xaa}, + {0x00, 0xb4, 0x1c, 0xaa}, + {0x00, 0xb5, 0x92, 0xaa}, + {0x00, 0xb6, 0x39, 0xaa}, + {0x00, 0xb7, 0x24, 0xaa}, +/*write 89 0400 1415*/ +}; + struct sensor_info { int sensorId; __u8 I2cAdd; @@ -1486,6 +1782,7 @@ static const struct sensor_info sensor_info_data[] = { {SENSOR_MI1310_SOC, 0x80 | 0x5d, 0x00, 0x143a, 0x24, 0x25, 0x01}, /* (tested in vc032x_probe_sensor) */ /* {SENSOR_MI0360, 0x80 | 0x5d, 0x00, 0x8243, 0x24, 0x25, 0x01}, */ + {SENSOR_PO1200, 0x80 | 0x5c, 0x00, 0x1200, 0x67, 0x67, 0x01}, }; /* read 'len' bytes in gspca_dev->usb_buf */ @@ -1709,6 +2006,9 @@ static int sd_config(struct gspca_dev *gspca_dev, case SENSOR_OV7670: PDEBUG(D_PROBE, "Find Sensor OV7670"); break; + case SENSOR_PO1200: + PDEBUG(D_PROBE, "Find Sensor PO1200"); + break; case SENSOR_PO3130NC: PDEBUG(D_PROBE, "Find Sensor PO3130NC"); break; @@ -1719,8 +2019,13 @@ static int sd_config(struct gspca_dev *gspca_dev, cam->cam_mode = vc0321_mode; cam->nmodes = ARRAY_SIZE(vc0321_mode); } else { - cam->cam_mode = vc0323_mode; - cam->nmodes = ARRAY_SIZE(vc0323_mode); + if (sensor != SENSOR_PO1200) { + cam->cam_mode = vc0323_mode; + cam->nmodes = ARRAY_SIZE(vc0323_mode); + } else { + cam->cam_mode = svga_mode; + cam->nmodes = ARRAY_SIZE(svga_mode); + } } sd->qindex = 7; @@ -1895,6 +2200,11 @@ static int sd_start(struct gspca_dev *gspca_dev) } usb_exchange(gspca_dev, po3130_rundata); break; + case SENSOR_PO1200: + GammaT = po1200_gamma; + MatrixT = po1200_matrix; + usb_exchange(gspca_dev, po1200_initVGA_data); + break; default: PDEBUG(D_PROBE, "Damned !! no sensor found Bye"); return -EMEDIUMTYPE; @@ -1927,11 +2237,15 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_w(gspca_dev->dev, 0xa0, 0x23, 0xb800); * ISP CTRL_BAS */ /* set the led on 0x0892 0x0896 */ - reg_w(gspca_dev->dev, 0x89, 0xffff, 0xfdff); - msleep(100); - setquality(gspca_dev); - sethvflip(gspca_dev); - setlightfreq(gspca_dev); + if (sd->sensor != SENSOR_PO1200) { + reg_w(gspca_dev->dev, 0x89, 0xffff, 0xfdff); + msleep(100); + setquality(gspca_dev); + sethvflip(gspca_dev); + setlightfreq(gspca_dev); + } else { + reg_w(gspca_dev->dev, 0x89, 0x0400, 0x1415); + } } return 0; } @@ -2078,6 +2392,7 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x0ac8, 0x0328), .driver_info = BRIDGE_VC0321}, {USB_DEVICE(0x0ac8, 0xc001), .driver_info = BRIDGE_VC0321}, {USB_DEVICE(0x0ac8, 0xc002), .driver_info = BRIDGE_VC0321}, + {USB_DEVICE(0x15b8, 0x6002), .driver_info = BRIDGE_VC0323}, {USB_DEVICE(0x17ef, 0x4802), .driver_info = BRIDGE_VC0323}, {} }; -- cgit v1.2.3 From eb8aba0ce02c6d663348dfc8c7caaa38af7a6549 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Wed, 10 Dec 2008 09:13:05 +0100 Subject: gspca - vc032x: Bad detection of sensor mi0360. From: Jean-Francois Moine Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/vc032x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/vc032x.c b/linux/drivers/media/video/gspca/vc032x.c index b898f6def..15dc660a5 100644 --- a/linux/drivers/media/video/gspca/vc032x.c +++ b/linux/drivers/media/video/gspca/vc032x.c @@ -1878,7 +1878,7 @@ static int vc032x_probe_sensor(struct gspca_dev *gspca_dev) /* special case for MI0360 */ if (ptsensor_info->sensorId == SENSOR_MI1310_SOC && value == 0x8243) - return ptsensor_info->sensorId; + return SENSOR_MI0360; } return -1; } -- cgit v1.2.3 From 81fedcd49f2a2f04adc9e38e7cf4747218d596b2 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Wed, 10 Dec 2008 09:45:14 +0100 Subject: gspca - ov534: Improve payload handling. From: Jim Paris Frame data in bulk transfers is separated into 2048-byte payloads. Each payload has its own header. Priority: normal Signed-off-by: Jim Paris Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/ov534.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/ov534.c b/linux/drivers/media/video/gspca/ov534.c index 28a798279..74bb7962e 100644 --- a/linux/drivers/media/video/gspca/ov534.c +++ b/linux/drivers/media/video/gspca/ov534.c @@ -1,6 +1,7 @@ /* * ov534/ov772x gspca driver * Copyright (C) 2008 Antonio Ospite + * Copyright (C) 2008 Jim Paris * * Based on a prototype written by Mark Ferrell * USB protocol reverse engineered by Jim Paris @@ -193,8 +194,8 @@ static const __u8 ov534_reg_initdata[][2] = { { 0x1c, 0x00 }, { 0x1d, 0x40 }, - { 0x1d, 0x02 }, - { 0x1d, 0x00 }, + { 0x1d, 0x02 }, /* payload size 0x0200 * 4 = 2048 bytes */ + { 0x1d, 0x00 }, /* payload size */ { 0x1d, 0x02 }, /* frame size 0x025800 * 4 = 614400 */ { 0x1d, 0x58 }, /* frame size */ { 0x1d, 0x00 }, /* frame size */ @@ -325,7 +326,7 @@ static int sd_config(struct gspca_dev *gspca_dev, cam->cam_mode = vga_mode; cam->nmodes = ARRAY_SIZE(vga_mode); - cam->bulk_size = 2048; + cam->bulk_size = 16384; cam->bulk_nurbs = 2; return 0; @@ -402,6 +403,17 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, struct sd *sd = (struct sd *) gspca_dev; __u32 this_pts; int this_fid; + int remaining_len = len; + __u8 *next_data = data; + +scan_next: + if (remaining_len <= 0) + return; + + data = next_data; + len = min(remaining_len, 2048); + remaining_len -= len; + next_data += len; /* Payloads are prefixed with a the UVC-style header. We consider a frame to start when the FID toggles, or the PTS @@ -452,12 +464,13 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, gspca_frame_add(gspca_dev, LAST_PACKET, frame, NULL, 0); } - /* Done */ - return; + /* Done this payload */ + goto scan_next; discard: /* Discard data until a new frame starts. */ gspca_frame_add(gspca_dev, DISCARD_PACKET, frame, NULL, 0); + goto scan_next; } /* sub-driver description */ -- cgit v1.2.3 From 88fe68b0680d49807a552b016bee47a5020b87b3 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Wed, 10 Dec 2008 09:47:44 +0100 Subject: gspca - ov534: Explicitly initialize frame format. From: Jim Paris Set frame format registers 0x0a and 0x0b to explicit values rather than relying on reset-time defaults. Priority: normal Signed-off-by: Jim Paris Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/ov534.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/ov534.c b/linux/drivers/media/video/gspca/ov534.c index 74bb7962e..66a7bd2f6 100644 --- a/linux/drivers/media/video/gspca/ov534.c +++ b/linux/drivers/media/video/gspca/ov534.c @@ -200,6 +200,10 @@ static const __u8 ov534_reg_initdata[][2] = { { 0x1d, 0x58 }, /* frame size */ { 0x1d, 0x00 }, /* frame size */ + { 0x1c, 0x0a }, + { 0x1d, 0x08 }, /* turn on UVC header */ + { 0x1d, 0x0e }, /* .. */ + { 0x8d, 0x1c }, { 0x8e, 0x80 }, { 0xe5, 0x04 }, -- cgit v1.2.3 From e826ea04411b7254088a42b69162390d6432f3bc Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Wed, 10 Dec 2008 09:50:44 +0100 Subject: gspca - main: Fix vidioc_s_jpegcomp locking. From: Jim Paris Priority: high Signed-off-by: Jim Paris Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/gspca.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/gspca.c b/linux/drivers/media/video/gspca/gspca.c index 4fda0fca6..7066c9daf 100644 --- a/linux/drivers/media/video/gspca/gspca.c +++ b/linux/drivers/media/video/gspca/gspca.c @@ -1320,10 +1320,10 @@ static int vidioc_s_jpegcomp(struct file *file, void *priv, struct gspca_dev *gspca_dev = priv; int ret; - if (mutex_lock_interruptible(&gspca_dev->usb_lock)) - return -ERESTARTSYS; if (!gspca_dev->sd_desc->set_jcomp) return -EINVAL; + if (mutex_lock_interruptible(&gspca_dev->usb_lock)) + return -ERESTARTSYS; ret = gspca_dev->sd_desc->set_jcomp(gspca_dev, jpegcomp); mutex_unlock(&gspca_dev->usb_lock); return ret; -- cgit v1.2.3 From 52e7cacf69005e1a115cceac3f79808852bb09dd Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Wed, 10 Dec 2008 10:02:42 +0100 Subject: gspca - main: Allow subdrivers to handle v4l2_streamparm requests. From: Jim Paris Add get_streamparm and set_streamparm operations so subdrivers can get/set stream parameters such as framerate. Priority: normal Signed-off-by: Jim Paris Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/gspca.c | 22 ++++++++++++++++++++++ linux/drivers/media/video/gspca/gspca.h | 4 ++++ 2 files changed, 26 insertions(+) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/gspca.c b/linux/drivers/media/video/gspca/gspca.c index 7066c9daf..71e7afe1b 100644 --- a/linux/drivers/media/video/gspca/gspca.c +++ b/linux/drivers/media/video/gspca/gspca.c @@ -1337,6 +1337,17 @@ static int vidioc_g_parm(struct file *filp, void *priv, memset(parm, 0, sizeof *parm); parm->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; parm->parm.capture.readbuffers = gspca_dev->nbufread; + + if (gspca_dev->sd_desc->get_streamparm) { + int ret; + + if (mutex_lock_interruptible(&gspca_dev->usb_lock)) + return -ERESTARTSYS; + ret = gspca_dev->sd_desc->get_streamparm(gspca_dev, parm); + mutex_unlock(&gspca_dev->usb_lock); + return ret; + } + return 0; } @@ -1351,6 +1362,17 @@ static int vidioc_s_parm(struct file *filp, void *priv, parm->parm.capture.readbuffers = gspca_dev->nbufread; else gspca_dev->nbufread = n; + + if (gspca_dev->sd_desc->set_streamparm) { + int ret; + + if (mutex_lock_interruptible(&gspca_dev->usb_lock)) + return -ERESTARTSYS; + ret = gspca_dev->sd_desc->set_streamparm(gspca_dev, parm); + mutex_unlock(&gspca_dev->usb_lock); + return ret; + } + return 0; } diff --git a/linux/drivers/media/video/gspca/gspca.h b/linux/drivers/media/video/gspca/gspca.h index 15db13296..79cef31a5 100644 --- a/linux/drivers/media/video/gspca/gspca.h +++ b/linux/drivers/media/video/gspca/gspca.h @@ -74,6 +74,8 @@ typedef void (*cam_v_op) (struct gspca_dev *); typedef int (*cam_cf_op) (struct gspca_dev *, const struct usb_device_id *); typedef int (*cam_jpg_op) (struct gspca_dev *, struct v4l2_jpegcompression *); +typedef int (*cam_streamparm_op) (struct gspca_dev *, + struct v4l2_streamparm *); typedef int (*cam_qmnu_op) (struct gspca_dev *, struct v4l2_querymenu *); typedef void (*cam_pkt_op) (struct gspca_dev *gspca_dev, @@ -106,6 +108,8 @@ struct sd_desc { cam_jpg_op get_jcomp; cam_jpg_op set_jcomp; cam_qmnu_op querymenu; + cam_streamparm_op get_streamparm; + cam_streamparm_op set_streamparm; }; /* packet types when moving from iso buf to frame buf */ -- cgit v1.2.3 From cd7fe8de6217a244b405e379bb843357c872fc78 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Wed, 10 Dec 2008 10:06:20 +0100 Subject: gspca - ov534: Add framerate support. From: Jim Paris Add support for getting and setting framerate via v4l2 controls, rather than setting a fixed value at module insertion. Priority: normal Signed-off-by: Jim Paris Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/ov534.c | 115 ++++++++++++++++++++++---------- 1 file changed, 78 insertions(+), 37 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/ov534.c b/linux/drivers/media/video/gspca/ov534.c index 66a7bd2f6..cd5c302d6 100644 --- a/linux/drivers/media/video/gspca/ov534.c +++ b/linux/drivers/media/video/gspca/ov534.c @@ -43,14 +43,12 @@ MODULE_AUTHOR("Antonio Ospite "); MODULE_DESCRIPTION("GSPCA/OV534 USB Camera Driver"); MODULE_LICENSE("GPL"); -/* global parameters */ -static int frame_rate; - /* specific webcam descriptor */ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ __u32 last_fid; __u32 last_pts; + int frame_rate; }; /* V4L2 controls supported by the driver */ @@ -296,6 +294,40 @@ static const __u8 ov772x_reg_initdata[][2] = { { 0x0c, 0xd0 } }; +/* set framerate */ +static void ov534_set_frame_rate(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int fr = sd->frame_rate; + + switch (fr) { + case 50: + sccb_reg_write(gspca_dev->dev, 0x11, 0x01); + sccb_reg_write(gspca_dev->dev, 0x0d, 0x41); + ov534_reg_write(gspca_dev->dev, 0xe5, 0x02); + break; + case 40: + sccb_reg_write(gspca_dev->dev, 0x11, 0x02); + sccb_reg_write(gspca_dev->dev, 0x0d, 0xc1); + ov534_reg_write(gspca_dev->dev, 0xe5, 0x04); + break; +/* case 30: */ + default: + fr = 30; + sccb_reg_write(gspca_dev->dev, 0x11, 0x04); + sccb_reg_write(gspca_dev->dev, 0x0d, 0x81); + ov534_reg_write(gspca_dev->dev, 0xe5, 0x02); + break; + case 15: + sccb_reg_write(gspca_dev->dev, 0x11, 0x03); + sccb_reg_write(gspca_dev->dev, 0x0d, 0x41); + ov534_reg_write(gspca_dev->dev, 0xe5, 0x04); + break; + } + + sd->frame_rate = fr; + PDEBUG(D_PROBE, "frame_rate: %d", fr); +} /* setup method */ static void ov534_setup(struct usb_device *udev) @@ -339,38 +371,8 @@ static int sd_config(struct gspca_dev *gspca_dev, /* this function is called at probe and resume time */ static int sd_init(struct gspca_dev *gspca_dev) { - int fr; - ov534_setup(gspca_dev->dev); - - fr = frame_rate; - - switch (fr) { - case 50: - sccb_reg_write(gspca_dev->dev, 0x11, 0x01); - sccb_reg_write(gspca_dev->dev, 0x0d, 0x41); - ov534_reg_write(gspca_dev->dev, 0xe5, 0x02); - break; - case 40: - sccb_reg_write(gspca_dev->dev, 0x11, 0x02); - sccb_reg_write(gspca_dev->dev, 0x0d, 0xc1); - ov534_reg_write(gspca_dev->dev, 0xe5, 0x04); - break; -/* case 30: */ - default: - fr = 30; - sccb_reg_write(gspca_dev->dev, 0x11, 0x04); - sccb_reg_write(gspca_dev->dev, 0x0d, 0x81); - ov534_reg_write(gspca_dev->dev, 0xe5, 0x02); - break; - case 15: - sccb_reg_write(gspca_dev->dev, 0x11, 0x03); - sccb_reg_write(gspca_dev->dev, 0x0d, 0x41); - ov534_reg_write(gspca_dev->dev, 0xe5, 0x04); - break; - } - - PDEBUG(D_PROBE, "frame_rate: %d", fr); + ov534_set_frame_rate(gspca_dev); return 0; } @@ -477,6 +479,46 @@ discard: goto scan_next; } +/* get stream parameters (framerate) */ +int sd_get_streamparm(struct gspca_dev *gspca_dev, + struct v4l2_streamparm *parm) +{ + struct v4l2_captureparm *cp = &parm->parm.capture; + struct v4l2_fract *tpf = &cp->timeperframe; + struct sd *sd = (struct sd *) gspca_dev; + + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + cp->capability |= V4L2_CAP_TIMEPERFRAME; + tpf->numerator = 1; + tpf->denominator = sd->frame_rate; + + return 0; +} + +/* set stream parameters (framerate) */ +int sd_set_streamparm(struct gspca_dev *gspca_dev, + struct v4l2_streamparm *parm) +{ + struct v4l2_captureparm *cp = &parm->parm.capture; + struct v4l2_fract *tpf = &cp->timeperframe; + struct sd *sd = (struct sd *) gspca_dev; + + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + /* Set requested framerate */ + sd->frame_rate = tpf->denominator / tpf->numerator; + ov534_set_frame_rate(gspca_dev); + + /* Return the actual framerate */ + tpf->numerator = 1; + tpf->denominator = sd->frame_rate; + + return 0; +} + /* sub-driver description */ static const struct sd_desc sd_desc = { .name = MODULE_NAME, @@ -487,6 +529,8 @@ static const struct sd_desc sd_desc = { .start = sd_start, .stopN = sd_stopN, .pkt_scan = sd_pkt_scan, + .get_streamparm = sd_get_streamparm, + .set_streamparm = sd_set_streamparm, }; /* -- module initialisation -- */ @@ -534,6 +578,3 @@ static void __exit sd_mod_exit(void) module_init(sd_mod_init); module_exit(sd_mod_exit); - -module_param(frame_rate, int, 0644); -MODULE_PARM_DESC(frame_rate, "Frame rate (15, 30, 40, 50)"); -- cgit v1.2.3 From 12583b9f4af4d04cce76742c9859bff4068e6345 Mon Sep 17 00:00:00 2001 From: Patrick Boettcher Date: Wed, 10 Dec 2008 22:27:39 +0100 Subject: [PATCH] short help for Technisat cards to select the right configuration From: Patrick Boettcher This patch adds a short help for Technisat cards to help the user selecting the right configuration for his card(s). Priority: normal Signed-off-by: Uwe Bugla Signed-off-by: Patrick Boettcher --- linux/Documentation/dvb/technisat.txt | 69 +++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 linux/Documentation/dvb/technisat.txt (limited to 'linux') diff --git a/linux/Documentation/dvb/technisat.txt b/linux/Documentation/dvb/technisat.txt new file mode 100644 index 000000000..cdf6ee4b2 --- /dev/null +++ b/linux/Documentation/dvb/technisat.txt @@ -0,0 +1,69 @@ +How to set up the Technisat devices +=================================== + +1) Find out what device you have +================================ + +First start your linux box with a shipped kernel: +lspci -vvv for a PCI device (lsusb -vvv for an USB device) will show you for example: +02:0b.0 Network controller: Techsan Electronics Co Ltd B2C2 FlexCopII DVB chip / Technisat SkyStar2 DVB card (rev 02) + +dmesg | grep frontend may show you for example: +DVB: registering frontend 0 (Conexant CX24123/CX24109)... + +2) Kernel compilation: +====================== + +If the Technisat is the only TV device in your box get rid of unnecessary modules and check this one: +"Multimedia devices" => "Customise analog and hybrid tuner modules to build" +In this directory uncheck every driver which is activated there. + +Then please activate: +2a) Main module part: + +a.)"Multimedia devices" => "DVB/ATSC adapters" => "Technisat/B2C2 FlexcopII(b) and FlexCopIII adapters" +b.)"Multimedia devices" => "DVB/ATSC adapters" => "Technisat/B2C2 FlexcopII(b) and FlexCopIII adapters" => "Technisat/B2C2 Air/Sky/Cable2PC PCI" in case of a PCI card OR +c.)"Multimedia devices" => "DVB/ATSC adapters" => "Technisat/B2C2 FlexcopII(b) and FlexCopIII adapters" => "Technisat/B2C2 Air/Sky/Cable2PC USB" in case of an USB 1.1 adapter +d.)"Multimedia devices" => "DVB/ATSC adapters" => "Technisat/B2C2 FlexcopII(b) and FlexCopIII adapters" => "Enable debug for the B2C2 FlexCop drivers" +Notice: d.) is helpful for troubleshooting + +2b) Frontend module part: + +1.) Revision 2.3: +a.)"Multimedia devices" => "Customise DVB frontends" => "Customise the frontend modules to build" +b.)"Multimedia devices" => "Customise DVB frontends" => "Zarlink VP310/MT312/ZL10313 based" + +2.) Revision 2.6: +a.)"Multimedia devices" => "Customise DVB frontends" => "Customise the frontend modules to build" +b.)"Multimedia devices" => "Customise DVB frontends" => "ST STV0299 based" + +3.) Revision 2.7: +a.)"Multimedia devices" => "Customise DVB frontends" => "Customise the frontend modules to build" +b.)"Multimedia devices" => "Customise DVB frontends" => "Samsung S5H1420 based" +c.)"Multimedia devices" => "Customise DVB frontends" => "Integrant ITD1000 Zero IF tuner for DVB-S/DSS" +d.)"Multimedia devices" => "Customise DVB frontends" => "ISL6421 SEC controller" + +4.) Revision 2.8: +a.)"Multimedia devices" => "Customise DVB frontends" => "Customise the frontend modules to build" +b.)"Multimedia devices" => "Customise DVB frontends" => "Conexant CX24113/CX24128 tuner for DVB-S/DSS" +c.)"Multimedia devices" => "Customise DVB frontends" => "Conexant CX24123 based" +d.)"Multimedia devices" => "Customise DVB frontends" => "ISL6421 SEC controller" + +5.) DVB-T card: +a.)"Multimedia devices" => "Customise DVB frontends" => "Customise the frontend modules to build" +b.)"Multimedia devices" => "Customise DVB frontends" => "Zarlink MT352 based" + +6.) DVB-C card: +a.)"Multimedia devices" => "Customise DVB frontends" => "Customise the frontend modules to build" +b.)"Multimedia devices" => "Customise DVB frontends" => "ST STV0297 based" + +7.) ATSC card 1st generation: +a.)"Multimedia devices" => "Customise DVB frontends" => "Customise the frontend modules to build" +b.)"Multimedia devices" => "Customise DVB frontends" => "Broadcom BCM3510" + +8.) ATSC card 2nd generation: +a.)"Multimedia devices" => "Customise DVB frontends" => "Customise the frontend modules to build" +b.)"Multimedia devices" => "Customise DVB frontends" => "NxtWave Communications NXT2002/NXT2004 based" +c.)"Multimedia devices" => "Customise DVB frontends" => "LG Electronics LGDT3302/LGDT3303 based" + +Author: Uwe Bugla December 2008 -- cgit v1.2.3 From d2c689d834f3c29dd15a07d5d421fe6c3124880e Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Thu, 11 Dec 2008 09:02:18 +0100 Subject: gspca - vc032x: Fix frame overflow errors with vc0321. From: Hans de Goede The vc0321 sends some additional data after sending the complete frame, we ignore this. Priority: normal Signed-off-by: Hans de Goede Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/vc032x.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/vc032x.c b/linux/drivers/media/video/gspca/vc032x.c index 15dc660a5..fb05d866f 100644 --- a/linux/drivers/media/video/gspca/vc032x.c +++ b/linux/drivers/media/video/gspca/vc032x.c @@ -2291,6 +2291,12 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, data, len); return; } + + /* The vc0321 sends some additional data after sending the complete + * frame, we ignore this. */ + if (sd->bridge == BRIDGE_VC0321 + && len > frame->v4l2_buf.length - (frame->data_end - frame->data)) + len = frame->v4l2_buf.length - (frame->data_end - frame->data); gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); } -- cgit v1.2.3 From 387f75a867f3844275f727de62d0dc8e7f25a64b Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Fri, 12 Dec 2008 08:48:06 +0100 Subject: gspca - vc032x: Remove the unused quality/qindex. From: Jean-Francois Moine Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/vc032x.c | 14 -------------- 1 file changed, 14 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/vc032x.c b/linux/drivers/media/video/gspca/vc032x.c index fb05d866f..5812af963 100644 --- a/linux/drivers/media/video/gspca/vc032x.c +++ b/linux/drivers/media/video/gspca/vc032x.c @@ -36,7 +36,6 @@ struct sd { __u8 vflip; __u8 lightfreq; - char qindex; char bridge; #define BRIDGE_VC0321 0 #define BRIDGE_VC0323 1 @@ -2028,7 +2027,6 @@ static int sd_config(struct gspca_dev *gspca_dev, } } - sd->qindex = 7; sd->hflip = HFLIP_DEF; sd->vflip = VFLIP_DEF; if (sd->sensor == SENSOR_OV7670) { @@ -2064,17 +2062,6 @@ static int sd_init(struct gspca_dev *gspca_dev) return 0; } -static void setquality(struct gspca_dev *gspca_dev) -{ -#if 0 - struct sd *sd = (struct sd *) gspca_dev; - __u8 quality = 0; - - quality = sd->qindex & 0xff; - reg_w(gspca_dev->dev, 0xa0, quality, 0x0008); -#endif -} - /* for OV7660 and OV7670 only */ static void sethvflip(struct gspca_dev *gspca_dev) { @@ -2240,7 +2227,6 @@ static int sd_start(struct gspca_dev *gspca_dev) if (sd->sensor != SENSOR_PO1200) { reg_w(gspca_dev->dev, 0x89, 0xffff, 0xfdff); msleep(100); - setquality(gspca_dev); sethvflip(gspca_dev); setlightfreq(gspca_dev); } else { -- cgit v1.2.3 From 1433f6ed5f77ef1a1fedc0aa910800f4e6b60c12 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Sat, 13 Dec 2008 11:45:34 +0100 Subject: gspca - vc032x: Add V&H flips and sharpness controls for sensor po1200. From: Jean-Francois Moine Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/vc032x.c | 83 ++++++++++++++++++++++++++++++-- 1 file changed, 78 insertions(+), 5 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/vc032x.c b/linux/drivers/media/video/gspca/vc032x.c index 5812af963..4cc1f69f1 100644 --- a/linux/drivers/media/video/gspca/vc032x.c +++ b/linux/drivers/media/video/gspca/vc032x.c @@ -35,6 +35,7 @@ struct sd { __u8 hflip; __u8 vflip; __u8 lightfreq; + __u8 sharpness; char bridge; #define BRIDGE_VC0321 0 @@ -57,6 +58,8 @@ static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val); static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val); static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val); static struct ctrl sd_ctrls[] = { /* next 2 controls work with ov7660 and ov7670 only */ @@ -105,6 +108,22 @@ static struct ctrl sd_ctrls[] = { .set = sd_setfreq, .get = sd_getfreq, }, +/* po1200 only */ +#define SHARPNESS_IDX 3 + { + { + .id = V4L2_CID_SHARPNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Sharpness", + .minimum = 0, + .maximum = 2, + .step = 1, +#define SHARPNESS_DEF 1 + .default_value = SHARPNESS_DEF, + }, + .set = sd_setsharpness, + .get = sd_getsharpness, + }, }; static struct v4l2_pix_format vc0321_mode[] = { @@ -1531,7 +1550,7 @@ static const __u8 po1200_initVGA_data[][4] = { {0x00, 0x03, 0x00, 0xaa}, {0x00, 0x12, 0x05, 0xaa}, {0x00, 0x13, 0x02, 0xaa}, - {0x00, 0x1e, 0xc6, 0xaa}, + {0x00, 0x1e, 0xc6, 0xaa}, /* h/v flip */ {0x00, 0x21, 0x00, 0xaa}, {0x00, 0x25, 0x02, 0xaa}, {0x00, 0x3c, 0x4f, 0xaa}, @@ -1541,7 +1560,7 @@ static const __u8 po1200_initVGA_data[][4] = { {0x00, 0x55, 0xfe, 0xaa}, {0x00, 0x59, 0xd3, 0xaa}, {0x00, 0x5e, 0x04, 0xaa}, - {0x00, 0x61, 0xb8, 0xaa}, + {0x00, 0x61, 0xb8, 0xaa}, /* sharpness */ {0x00, 0x62, 0x02, 0xaa}, {0x00, 0xa7, 0x31, 0xaa}, {0x00, 0xa9, 0x66, 0xaa}, @@ -1637,7 +1656,7 @@ static const __u8 po1200_initVGA_data[][4] = { {0x00, 0x20, 0xc4, 0xaa}, {0x00, 0x13, 0x03, 0xaa}, {0x00, 0x3c, 0x50, 0xaa}, - {0x00, 0x61, 0x6a, 0xaa}, + {0x00, 0x61, 0x6a, 0xaa}, /* sharpness? */ {0x00, 0x51, 0x5b, 0xaa}, {0x00, 0x52, 0x91, 0xaa}, {0x00, 0x53, 0x4c, 0xaa}, @@ -1692,7 +1711,12 @@ static const __u8 po1200_initVGA_data[][4] = { {0x00, 0xe2, 0x01, 0xaa}, {0x00, 0xd6, 0x40, 0xaa}, {0x00, 0xe4, 0x40, 0xaa}, +#if 1 + {0x00, 0xa8, 0x8f, 0xaa}, +#else +/*modified later*/ {0x00, 0xa8, 0x9f, 0xaa}, +#endif {0x00, 0xb4, 0x16, 0xaa}, {0xb0, 0x02, 0x06, 0xcc}, {0xb0, 0x18, 0x06, 0xcc}, @@ -1729,8 +1753,10 @@ static const __u8 po1200_initVGA_data[][4] = { {0x00, 0x03, 0x00, 0xaa}, {0x00, 0x95, 0x85, 0xaa}, /*matrix*/ +#if 0 {0x00, 0x03, 0x00, 0xaa}, - {0x00, 0x61, 0xb8, 0xaa}, + {0x00, 0x61, 0xb8, 0xaa}, /* sharpness */ +#endif {0x00, 0x03, 0x00, 0xaa}, {0x00, 0x4d, 0x20, 0xaa}, {0xb8, 0x22, 0x40, 0xcc}, @@ -1750,9 +1776,11 @@ static const __u8 po1200_initVGA_data[][4] = { {0x00, 0x46, 0x3c, 0xaa}, {0x00, 0x00, 0x18, 0xdd}, /*read bfff*/ +#if 0 {0x00, 0x03, 0x00, 0xaa}, - {0x00, 0x1e, 0x46, 0xaa}, + {0x00, 0x1e, 0x46, 0xaa}, /* h/v flip */ {0x00, 0xa8, 0x8f, 0xaa}, +#endif {0x00, 0x03, 0x00, 0xaa}, {0x00, 0xb4, 0x1c, 0xaa}, {0x00, 0xb5, 0x92, 0xaa}, @@ -2039,6 +2067,7 @@ static int sd_config(struct gspca_dev *gspca_dev, switch (sd->sensor) { case SENSOR_OV7660: case SENSOR_OV7670: + case SENSOR_PO1200: break; default: gspca_dev->ctrl_dis = (1 << HFLIP_IDX) @@ -2046,6 +2075,8 @@ static int sd_config(struct gspca_dev *gspca_dev, break; } + sd->sharpness = SHARPNESS_DEF; + if (sd->bridge == BRIDGE_VC0321) { reg_r(gspca_dev, 0x8a, 0, 3); reg_w(dev, 0x87, 0x00, 0x0f0f); @@ -2075,6 +2106,14 @@ static void sethvflip(struct gspca_dev *gspca_dev) case SENSOR_OV7670: data = 7; break; + case SENSOR_PO1200: + data = 0; + i2c_write(gspca_dev, 0x03, &data, 1); + data = 0x80 * sd->hflip + | 0x40 * sd->vflip + | 0x06; + i2c_write(gspca_dev, 0x1e, &data, 1); + return; default: return; } @@ -2094,6 +2133,20 @@ static void setlightfreq(struct gspca_dev *gspca_dev) usb_exchange(gspca_dev, ov7660_freq_tb[sd->lightfreq]); } +/* po1200 only */ +static void setsharpness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 data; + + if (sd->sensor != SENSOR_PO1200) + return; + data = 0; + i2c_write(gspca_dev, 0x03, &data, 1); + data = 0xb5 + sd->sharpness * 3; + i2c_write(gspca_dev, 0x61, &data, 1); +} + static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -2230,6 +2283,8 @@ static int sd_start(struct gspca_dev *gspca_dev) sethvflip(gspca_dev); setlightfreq(gspca_dev); } else { + setsharpness(gspca_dev); + sethvflip(gspca_dev); reg_w(gspca_dev->dev, 0x89, 0x0400, 0x1415); } } @@ -2340,6 +2395,24 @@ static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val) return 0; } +static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->sharpness = val; + if (gspca_dev->streaming) + setsharpness(gspca_dev); + return 0; +} + +static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->sharpness; + return 0; +} + static int sd_querymenu(struct gspca_dev *gspca_dev, struct v4l2_querymenu *menu) { -- cgit v1.2.3 From f5fcababcd2cc816a001298326635ea7c52b1cbd Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Sun, 14 Dec 2008 09:41:56 +0100 Subject: gspca - ov534: Fix typo. From: Antonio Ospite Priority: normal Signed-off-by: Antonio Ospite Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/ov534.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/ov534.c b/linux/drivers/media/video/gspca/ov534.c index cd5c302d6..edcd92557 100644 --- a/linux/drivers/media/video/gspca/ov534.c +++ b/linux/drivers/media/video/gspca/ov534.c @@ -421,7 +421,7 @@ scan_next: remaining_len -= len; next_data += len; - /* Payloads are prefixed with a the UVC-style header. We + /* Payloads are prefixed with a UVC-style header. We consider a frame to start when the FID toggles, or the PTS changes. A frame ends when EOF is set, and we've received the correct number of bytes. */ -- cgit v1.2.3 From f74cfcac767db1991854892ac24f5ff39596b851 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Sun, 14 Dec 2008 09:48:07 +0100 Subject: gspca - ov534: Show sensor ID. From: Antonio Ospite The original version of sccb_read_reg() is from Jim Paris. NOTE: as it is now reading sensor ID won't work for sensors on different i2c slave address. Priority: normal Signed-off-by: Antonio Ospite Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/ov534.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/ov534.c b/linux/drivers/media/video/gspca/ov534.c index edcd92557..ad97b0be5 100644 --- a/linux/drivers/media/video/gspca/ov534.c +++ b/linux/drivers/media/video/gspca/ov534.c @@ -149,6 +149,20 @@ static void sccb_reg_write(struct usb_device *udev, u16 reg, u8 val) PDEBUG(D_ERR, "sccb_reg_write failed"); } +static u8 sccb_reg_read(struct usb_device *udev, u16 reg) +{ + ov534_reg_write(udev, OV534_REG_SUBADDR, reg); + ov534_reg_write(udev, OV534_REG_OPERATION, OV534_OP_WRITE_2); + if (!sccb_check_status(udev)) + PDEBUG(D_ERR, "sccb_reg_read failed 1"); + + ov534_reg_write(udev, OV534_REG_OPERATION, OV534_OP_READ_2); + if (!sccb_check_status(udev)) + PDEBUG(D_ERR, "sccb_reg_read failed 2"); + + return ov534_reg_read(udev, OV534_REG_READ); +} + static const __u8 ov534_reg_initdata[][2] = { { 0xe7, 0x3a }, @@ -339,6 +353,9 @@ static void ov534_setup(struct usb_device *udev) ov534_reg_write(udev, ov534_reg_initdata[i][0], ov534_reg_initdata[i][1]); + PDEBUG(D_PROBE, "sensor is ov%02x%02x", + sccb_reg_read(udev, 0x0a), sccb_reg_read(udev, 0x0b)); + ov534_set_led(udev, 1); /* Initialize sensor */ -- cgit v1.2.3 From 9735b6754d8660f40063776b9197b952cb1a4e19 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Sun, 14 Dec 2008 09:50:24 +0100 Subject: gspca - ov534: Fix a warning when compilation without GSPCA_DEBUG. From: Jean-Francois Moine Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/ov534.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/ov534.c b/linux/drivers/media/video/gspca/ov534.c index ad97b0be5..7332947cb 100644 --- a/linux/drivers/media/video/gspca/ov534.c +++ b/linux/drivers/media/video/gspca/ov534.c @@ -149,6 +149,7 @@ static void sccb_reg_write(struct usb_device *udev, u16 reg, u8 val) PDEBUG(D_ERR, "sccb_reg_write failed"); } +#ifdef GSPCA_DEBUG static u8 sccb_reg_read(struct usb_device *udev, u16 reg) { ov534_reg_write(udev, OV534_REG_SUBADDR, reg); @@ -162,6 +163,7 @@ static u8 sccb_reg_read(struct usb_device *udev, u16 reg) return ov534_reg_read(udev, OV534_REG_READ); } +#endif static const __u8 ov534_reg_initdata[][2] = { { 0xe7, 0x3a }, -- cgit v1.2.3 From 66c33fb391b11fa484e3a4f6ba4275be3cd2f4dd Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 14 Dec 2008 09:28:07 -0200 Subject: changeset: 9885:77f72ebfa794 tag: tip user: Mauro Carvalho Chehab date: Sun Dec 14 09:16:55 2008 -0200 files: linux/drivers/media/dvb/b2c2/Kconfig linux/drivers/media/dvb/bt8xx/Kconfig linux/drivers/media/dvb/dvb-usb/Kconfig linux/drivers/media/dvb/ttpci/Kconfig linux/drivers/media/video/cx18/Kconfig linux/drivers/media/video/cx23885/Kconfig linux/drivers/media/video/cx88/Kconfig linux/drivers/media/video/pvrusb2/Kconfig linux/drivers/media/video/saa7134/Kconfig description: media Kconfigs: fix bugzilla #12204 From: Mauro Carvalho Chehab 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 A manual reordering were done to have the media selects just after the dvb ones. Priority: high Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/dvb/b2c2/Kconfig | 2 +- linux/drivers/media/dvb/bt8xx/Kconfig | 2 +- linux/drivers/media/dvb/dvb-usb/Kconfig | 46 +++++++++++++++---------------- linux/drivers/media/dvb/ttpci/Kconfig | 2 +- linux/drivers/media/video/cx18/Kconfig | 2 +- linux/drivers/media/video/cx23885/Kconfig | 4 +-- linux/drivers/media/video/cx88/Kconfig | 2 +- linux/drivers/media/video/pvrusb2/Kconfig | 2 +- linux/drivers/media/video/saa7134/Kconfig | 4 +-- 9 files changed, 33 insertions(+), 33 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/dvb/b2c2/Kconfig b/linux/drivers/media/dvb/b2c2/Kconfig index e5c27e355..bd21c3bf2 100644 --- a/linux/drivers/media/dvb/b2c2/Kconfig +++ b/linux/drivers/media/dvb/b2c2/Kconfig @@ -9,12 +9,12 @@ config DVB_B2C2_FLEXCOP select DVB_STV0297 if !DVB_FE_CUSTOMISE select DVB_BCM3510 if !DVB_FE_CUSTOMISE select DVB_LGDT330X if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_SIMPLE if !DVB_FE_CUSTOMISE select DVB_S5H1420 if !DVB_FE_CUSTOMISE select DVB_TUNER_ITD1000 if !DVB_FE_CUSTOMISE select DVB_ISL6421 if !DVB_FE_CUSTOMISE select DVB_CX24123 if !DVB_FE_CUSTOMISE select DVB_TUNER_CX24113 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMIZE help Support for the digital TV receiver chip made by B2C2 Inc. included in Technisats PCI cards and USB boxes. diff --git a/linux/drivers/media/dvb/bt8xx/Kconfig b/linux/drivers/media/dvb/bt8xx/Kconfig index 7e9c090fc..27edb0ece 100644 --- a/linux/drivers/media/dvb/bt8xx/Kconfig +++ b/linux/drivers/media/dvb/bt8xx/Kconfig @@ -8,7 +8,7 @@ config DVB_BT8XX select DVB_OR51211 if !DVB_FE_CUSTOMISE select DVB_LGDT330X if !DVB_FE_CUSTOMISE select DVB_ZL10353 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_SIMPLE if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMIZE help Support for PCI cards based on the Bt8xx PCI bridge. Examples are the Nebula cards, the Pinnacle PCTV cards, the Twinhan DST cards, diff --git a/linux/drivers/media/dvb/dvb-usb/Kconfig b/linux/drivers/media/dvb/dvb-usb/Kconfig index 62b68c291..49f7b20c2 100644 --- a/linux/drivers/media/dvb/dvb-usb/Kconfig +++ b/linux/drivers/media/dvb/dvb-usb/Kconfig @@ -24,8 +24,8 @@ config DVB_USB_A800 tristate "AVerMedia AverTV DVB-T USB 2.0 (A800)" depends on DVB_USB select DVB_DIB3000MC - select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE select DVB_PLL if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMIZE help Say Y here to support the AVerMedia AverTV DVB-T USB 2.0 (A800) receiver. @@ -34,7 +34,7 @@ config DVB_USB_DIBUSB_MB depends on DVB_USB select DVB_PLL if !DVB_FE_CUSTOMISE select DVB_DIB3000MB - select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMIZE help Support for USB 1.1 and 2.0 DVB-T receivers based on reference designs made by DiBcom () equipped with a DiB3000M-B demodulator. @@ -55,7 +55,7 @@ config DVB_USB_DIBUSB_MC tristate "DiBcom USB DVB-T devices (based on the DiB3000M-C/P) (see help for device list)" depends on DVB_USB select DVB_DIB3000MC - select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMIZE help Support for USB2.0 DVB-T receivers based on reference designs made by DiBcom () equipped with a DiB3000M-C/P demodulator. @@ -73,11 +73,11 @@ config DVB_USB_DIB0700 select DVB_DIB7000M select DVB_DIB3000MC select DVB_S5H1411 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_MT2266 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_XC2028 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_XC5000 if !DVB_FE_CUSTOMIZE select DVB_TUNER_DIB0070 + select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMIZE + select MEDIA_TUNER_MT2266 if !MEDIA_TUNER_CUSTOMIZE + select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMIZE + select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMIZE help Support for USB2.0/1.1 DVB receivers based on the DiB0700 USB bridge. The USB bridge is also present in devices having the DiB7700 DVB-T-USB @@ -95,7 +95,7 @@ config DVB_USB_UMT_010 depends on DVB_USB select DVB_PLL if !DVB_FE_CUSTOMISE select DVB_DIB3000MC - select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMIZE help Say Y here to support the HanfTek UMT-010 USB2.0 stick-sized DVB-T receiver. @@ -107,11 +107,11 @@ config DVB_USB_CXUSB select DVB_LGDT330X if !DVB_FE_CUSTOMISE select DVB_MT352 if !DVB_FE_CUSTOMISE select DVB_ZL10353 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_SIMPLE if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_XC2028 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_MXL5005S if !DVB_FE_CUSTOMISE select DVB_DIB7000P if !DVB_FE_CUSTOMISE select DVB_TUNER_DIB0070 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMIZE + select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMIZE + select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMIZE help Say Y here to support the Conexant USB2.0 hybrid reference design. Currently, only DVB and ATSC modes are supported, analog mode @@ -124,9 +124,9 @@ config DVB_USB_M920X tristate "Uli m920x DVB-T USB2.0 support" depends on DVB_USB select DVB_MT352 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_QT1010 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_TDA827X if !DVB_FE_CUSTOMISE select DVB_TDA1004X if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMIZE + select MEDIA_TUNER_TDA827X if !MEDIA_TUNER_CUSTOMIZE help Say Y here to support the MSI Mega Sky 580 USB2.0 DVB-T receiver. Currently, only devices with a product id of @@ -137,7 +137,7 @@ config DVB_USB_GL861 tristate "Genesys Logic GL861 USB2.0 support" depends on DVB_USB select DVB_ZL10353 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_QT1010 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMIZE help Say Y here to support the MSI Megasky 580 (55801) DVB-T USB2.0 receiver with USB ID 0db0:5581. @@ -146,7 +146,7 @@ config DVB_USB_AU6610 tristate "Alcor Micro AU6610 USB2.0 support" depends on DVB_USB select DVB_ZL10353 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_QT1010 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMIZE help Say Y here to support the Sigmatek DVB-110 DVB-T USB2.0 receiver. @@ -198,8 +198,8 @@ config DVB_USB_NOVA_T_USB2 tristate "Hauppauge WinTV-NOVA-T usb2 DVB-T USB2.0 support" depends on DVB_USB select DVB_DIB3000MC - select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE select DVB_PLL if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMIZE help Say Y here to support the Hauppauge WinTV-NOVA-T usb2 DVB-T USB2.0 receiver. @@ -235,8 +235,8 @@ config DVB_USB_OPERA1 config DVB_USB_AF9005 tristate "Afatech AF9005 DVB-T USB1.1 support" depends on DVB_USB && EXPERIMENTAL - select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_QT1010 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMIZE + select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMIZE help Say Y here to support the Afatech AF9005 based DVB-T USB1.1 receiver and the TerraTec Cinergy T USB XE (Rev.1) @@ -284,7 +284,7 @@ config DVB_USB_DTV5100 tristate "AME DTV-5100 USB2.0 DVB-T support" depends on DVB_USB select DVB_ZL10353 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_QT1010 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMIZE help Say Y here to support the AME DTV-5100 USB2.0 DVB-T receiver. @@ -293,9 +293,9 @@ config DVB_USB_AF9015 depends on DVB_USB && EXPERIMENTAL select DVB_AF9013 select DVB_PLL if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_QT1010 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_TDA18271 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_MXL5005S if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMIZE + select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMIZE + select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMIZE + select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMIZE help Say Y here to support the Afatech AF9015 based DVB-T USB2.0 receiver diff --git a/linux/drivers/media/dvb/ttpci/Kconfig b/linux/drivers/media/dvb/ttpci/Kconfig index 9e2ece9e4..ab0bcd208 100644 --- a/linux/drivers/media/dvb/ttpci/Kconfig +++ b/linux/drivers/media/dvb/ttpci/Kconfig @@ -108,7 +108,7 @@ config DVB_BUDGET_CI select DVB_STB6100 if !DVB_FE_CUSTOMISE select DVB_LNBP21 if !DVB_FE_CUSTOMISE select DVB_TDA10023 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_TDA827X if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_TDA827X if !MEDIA_TUNER_CUSTOMIZE select VIDEO_IR help Support for simple SAA7146 based DVB cards diff --git a/linux/drivers/media/video/cx18/Kconfig b/linux/drivers/media/video/cx18/Kconfig index ef48565de..8940b5387 100644 --- a/linux/drivers/media/video/cx18/Kconfig +++ b/linux/drivers/media/video/cx18/Kconfig @@ -9,7 +9,7 @@ config VIDEO_CX18 select VIDEO_CX2341X select VIDEO_CS5345 select DVB_S5H1409 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_MXL5005S if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMIZE ---help--- This is a video4linux driver for Conexant cx23418 based PCI combo video recorder devices. diff --git a/linux/drivers/media/video/cx23885/Kconfig b/linux/drivers/media/video/cx23885/Kconfig index 8c1b7fa47..00f1e2e88 100644 --- a/linux/drivers/media/video/cx23885/Kconfig +++ b/linux/drivers/media/video/cx23885/Kconfig @@ -11,16 +11,16 @@ config VIDEO_CX23885 select VIDEO_CX25840 select VIDEO_CX2341X select DVB_DIB7000P if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_MT2131 if !DVB_FE_CUSTOMISE select DVB_S5H1409 if !DVB_FE_CUSTOMISE select DVB_S5H1411 if !DVB_FE_CUSTOMISE select DVB_LGDT330X if !DVB_FE_CUSTOMISE select DVB_ZL10353 if !DVB_FE_CUSTOMISE + select DVB_TDA10048 if !DVB_FE_CUSTOMIZE + select MEDIA_TUNER_MT2131 if !MEDIA_TUNER_CUSTOMIZE select MEDIA_TUNER_XC2028 if !DVB_FE_CUSTOMIZE select MEDIA_TUNER_TDA8290 if !DVB_FE_CUSTOMIZE select MEDIA_TUNER_TDA18271 if !DVB_FE_CUSTOMIZE select MEDIA_TUNER_XC5000 if !DVB_FE_CUSTOMIZE - select DVB_TDA10048 if !DVB_FE_CUSTOMIZE ---help--- This is a video4linux driver for Conexant 23885 based TV cards. diff --git a/linux/drivers/media/video/cx88/Kconfig b/linux/drivers/media/video/cx88/Kconfig index 0b9e5fac6..b0f837588 100644 --- a/linux/drivers/media/video/cx88/Kconfig +++ b/linux/drivers/media/video/cx88/Kconfig @@ -56,12 +56,12 @@ config VIDEO_CX88_DVB select DVB_NXT200X if !DVB_FE_CUSTOMISE select DVB_CX24123 if !DVB_FE_CUSTOMISE select DVB_ISL6421 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_SIMPLE if !DVB_FE_CUSTOMISE select DVB_S5H1411 if !DVB_FE_CUSTOMISE select DVB_CX24116 if !DVB_FE_CUSTOMISE select DVB_STV0299 if !DVB_FE_CUSTOMISE select DVB_STV0288 if !DVB_FE_CUSTOMISE select DVB_STB6000 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMIZE ---help--- This adds support for DVB/ATSC cards based on the Conexant 2388x chip. diff --git a/linux/drivers/media/video/pvrusb2/Kconfig b/linux/drivers/media/video/pvrusb2/Kconfig index 19eb274c9..854c2a885 100644 --- a/linux/drivers/media/video/pvrusb2/Kconfig +++ b/linux/drivers/media/video/pvrusb2/Kconfig @@ -42,7 +42,7 @@ config VIDEO_PVRUSB2_DVB select DVB_S5H1411 if !DVB_FE_CUSTOMISE select DVB_TDA10048 if !DVB_FE_CUSTOMIZE select MEDIA_TUNER_TDA18271 if !DVB_FE_CUSTOMIZE - select MEDIA_TUNER_SIMPLE if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMIZE select MEDIA_TUNER_TDA8290 if !DVB_FE_CUSTOMIZE ---help--- diff --git a/linux/drivers/media/video/saa7134/Kconfig b/linux/drivers/media/video/saa7134/Kconfig index 7021bbf58..fc2164e28 100644 --- a/linux/drivers/media/video/saa7134/Kconfig +++ b/linux/drivers/media/video/saa7134/Kconfig @@ -34,9 +34,9 @@ config VIDEO_SAA7134_DVB select DVB_NXT200X if !DVB_FE_CUSTOMISE select DVB_TDA10086 if !DVB_FE_CUSTOMISE select DVB_TDA826X if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_TDA827X if !DVB_FE_CUSTOMISE select DVB_ISL6421 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_SIMPLE if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_TDA827X if !MEDIA_TUNER_CUSTOMIZE + select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMIZE ---help--- This adds support for DVB cards based on the Philips saa7134 chip. -- cgit v1.2.3 From c8e6336414973fe34750afe8327988ddab7d1a58 Mon Sep 17 00:00:00 2001 From: Patrick Boettcher Date: Sun, 14 Dec 2008 12:59:55 +0100 Subject: Minor fixes for cx24113-driver (codingstyle) From: Patrick Boettcher Fixed some minor coding style issues and some driver information printed when using the driver on a board. Priority: normal Signed-off-by: Patrick Boettcher --- linux/drivers/media/dvb/frontends/cx24113.c | 70 ++++++++++++++++------------- 1 file changed, 39 insertions(+), 31 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/dvb/frontends/cx24113.c b/linux/drivers/media/dvb/frontends/cx24113.c index e2e5df9de..8850f2303 100644 --- a/linux/drivers/media/dvb/frontends/cx24113.c +++ b/linux/drivers/media/dvb/frontends/cx24113.c @@ -50,46 +50,46 @@ struct cx24113_state { u8 rev; u8 ver; - u8 icp_mode:1; + u8 icp_mode:1; #define ICP_LEVEL1 0 #define ICP_LEVEL2 1 #define ICP_LEVEL3 2 #define ICP_LEVEL4 3 - u8 icp_man:2; - u8 icp_auto_low:2; - u8 icp_auto_mlow:2; - u8 icp_auto_mhi:2; - u8 icp_auto_hi:2; - u8 icp_dig; + u8 icp_man:2; + u8 icp_auto_low:2; + u8 icp_auto_mlow:2; + u8 icp_auto_mhi:2; + u8 icp_auto_hi:2; + u8 icp_dig; #define LNA_MIN_GAIN 0 #define LNA_MID_GAIN 1 #define LNA_MAX_GAIN 2 - u8 lna_gain:2; + u8 lna_gain:2; - u8 acp_on:1; + u8 acp_on:1; - u8 vco_mode:2; - u8 vco_shift:1; + u8 vco_mode:2; + u8 vco_shift:1; #define VCOBANDSEL_6 0x80 #define VCOBANDSEL_5 0x01 #define VCOBANDSEL_4 0x02 #define VCOBANDSEL_3 0x04 #define VCOBANDSEL_2 0x08 #define VCOBANDSEL_1 0x10 - u8 vco_band; + u8 vco_band; #define VCODIV4 4 #define VCODIV2 2 u8 vcodiv; - u8 bs_delay:4; - u16 bs_freqcnt:13; - u16 bs_rdiv; - u8 prescaler_mode:1; + u8 bs_delay:4; + u16 bs_freqcnt:13; + u16 bs_rdiv; + u8 prescaler_mode:1; - u8 rfvga_bias_ctrl; + u8 rfvga_bias_ctrl; s16 tuner_gain_thres; u8 gain_level; @@ -345,12 +345,12 @@ static void cx24113_calc_pll_nf(struct cx24113_state *state, u16 *n, s32 *f) } F = freq_hz; F *= (u64) (R * vcodiv * 262144); - dprintk("1 N: %d, F: %lld, R: %d\n", N, F, R); + dprintk("1 N: %d, F: %lld, R: %d\n", N, F, R); do_div(F, state->config->xtal_khz*1000 * factor * 2); - dprintk("2 N: %d, F: %lld, R: %d\n", N, F, R); + dprintk("2 N: %d, F: %lld, R: %d\n", N, F, R); F -= (N + 32) * 262144; - dprintk("3 N: %d, F: %lld, R: %d\n", N, F, R); + dprintk("3 N: %d, F: %lld, R: %d\n", N, F, R); if (state->Fwindow_enabled) { if (F > (262144 / 2 - 1638)) @@ -392,21 +392,21 @@ static int cx24113_set_frequency(struct cx24113_state *state, u32 frequency) u16 n = 6; s32 f = 0; - r = cx24113_readreg(state, 0x14); + r = cx24113_readreg(state, 0x14); cx24113_writereg(state, 0x14, r & 0x3f); - r = cx24113_readreg(state, 0x10); - cx24113_writereg(state, 0x10, r & 0xbf); + r = cx24113_readreg(state, 0x10); + cx24113_writereg(state, 0x10, r & 0xbf); state->frequency = frequency; dprintk("tuning to frequency: %d\n", frequency); - cx24113_calc_pll_nf(state, &n, &f); - cx24113_set_nfr(state, n, f, state->refdiv); + cx24113_calc_pll_nf(state, &n, &f); + cx24113_set_nfr(state, n, f, state->refdiv); r = cx24113_readreg(state, 0x18) & 0xbf; - if (state->vcodiv != VCODIV2) + if (state->vcodiv != VCODIV2) r |= 1 << 6; cx24113_writereg(state, 0x18, r); @@ -527,7 +527,7 @@ static int cx24113_release(struct dvb_frontend *fe) { struct cx24113_state *state = fe->tuner_priv; dprintk("\n"); - fe->tuner_priv = NULL; + fe->tuner_priv = NULL; kfree(state); return 0; } @@ -557,6 +557,7 @@ struct dvb_frontend *cx24113_attach(struct dvb_frontend *fe, /* allocate memory for the internal state */ struct cx24113_state *state = kzalloc(sizeof(struct cx24113_state), GFP_KERNEL); + int rc; if (state == NULL) { err("Unable to kmalloc\n"); goto error; @@ -572,15 +573,22 @@ struct dvb_frontend *cx24113_attach(struct dvb_frontend *fe, * after power on */ cx24113_readreg(state, 0x00); - switch (state->rev = cx24113_readreg(state, 0x00)) { + rc = cx24113_readreg(state, 0x00); + if (rc < 0) { + info("cx24113 not found.\n"); + goto error; + } + state->rev = rc; + + switch (rc) { case 0x43: - info("unknown device\n"); + info("detected Cx24113 variant\n"); break; case REV_CX24113: - info("CX24113\n"); + info("sucessfully detected\n"); break; default: - err("unsupported revision: %x\n", state->rev); + err("unsupported device id: %x\n", state->rev); goto error; } state->ver = cx24113_readreg(state, 0x01); -- cgit v1.2.3 From 89634f12bf2fabd82e3043863cc01da40ec3bfc4 Mon Sep 17 00:00:00 2001 From: Patrick Boettcher Date: Sun, 14 Dec 2008 13:11:53 +0100 Subject: Patch: fix a typo in cx24113.c From: Uwe Bugla This patch fixes a typo in cx24113.c. Priority: normal Signed-off-by: Uwe Bugla Signed-off-by: Patrick Boettcher --- linux/drivers/media/dvb/frontends/cx24113.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/dvb/frontends/cx24113.c b/linux/drivers/media/dvb/frontends/cx24113.c index 8850f2303..e130b228e 100644 --- a/linux/drivers/media/dvb/frontends/cx24113.c +++ b/linux/drivers/media/dvb/frontends/cx24113.c @@ -1,5 +1,5 @@ /* - * Driver for Conexant CX24113/CX24128 Tuner (Satelite) + * Driver for Conexant CX24113/CX24128 Tuner (Satellite) * * Copyright (C) 2007-8 Patrick Boettcher * -- cgit v1.2.3 From 40f4f221fb5a7e2f157453743e545ddeff42bd55 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Sun, 14 Dec 2008 13:37:02 +0100 Subject: gspca - pac207: Update my email address. From: Hans de Goede Priority: normal Signed-off-by: Hans de Goede Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/pac207.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/pac207.c b/linux/drivers/media/video/gspca/pac207.c index 39473e6b9..84bcaaf4d 100644 --- a/linux/drivers/media/video/gspca/pac207.c +++ b/linux/drivers/media/video/gspca/pac207.c @@ -1,7 +1,7 @@ /* * Pixart PAC207BCA library * - * Copyright (C) 2008 Hans de Goede + * Copyright (C) 2008 Hans de Goede * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li * Copyleft (C) 2005 Michel Xhaard mxhaard@magic.fr * @@ -27,7 +27,7 @@ #include "gspca.h" -MODULE_AUTHOR("Hans de Goede "); +MODULE_AUTHOR("Hans de Goede "); MODULE_DESCRIPTION("Pixart PAC207"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 3bef875602189a42ca85a89ace6fc225d739512c Mon Sep 17 00:00:00 2001 From: Patrick Boettcher Date: Sun, 14 Dec 2008 15:56:28 +0100 Subject: CX24113: Fixed more typos From: Patrick Boettcher Uwe Bugla pointed out that there are some more typos in some print-statements. Fixed. Priority: normal Signed-off-by: Patrick Boettcher --- linux/drivers/media/dvb/frontends/cx24113.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/dvb/frontends/cx24113.c b/linux/drivers/media/dvb/frontends/cx24113.c index e130b228e..93f35941a 100644 --- a/linux/drivers/media/dvb/frontends/cx24113.c +++ b/linux/drivers/media/dvb/frontends/cx24113.c @@ -575,14 +575,14 @@ struct dvb_frontend *cx24113_attach(struct dvb_frontend *fe, rc = cx24113_readreg(state, 0x00); if (rc < 0) { - info("cx24113 not found.\n"); + info("CX24113 not found.\n"); goto error; } state->rev = rc; switch (rc) { case 0x43: - info("detected Cx24113 variant\n"); + info("detected CX24113 variant\n"); break; case REV_CX24113: info("sucessfully detected\n"); -- cgit v1.2.3 From 99627092016191ade350fe68ac3c2d17e3cd6a11 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 14 Dec 2008 20:21:16 +0100 Subject: v4l2: Add camera zoom controls From: Laurent Pinchart The zoom controls move the zoom lens group to a an absolute position, as a relative displacement or at a given speed until reaching physical device limits. Positive values move the zoom lens group towards the telephoto direction, negative values towards the wide-angle direction. Priority: normal Signed-off-by: Laurent Pinchart --- linux/include/linux/videodev2.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'linux') diff --git a/linux/include/linux/videodev2.h b/linux/include/linux/videodev2.h index 00e539e8f..2d7f01059 100644 --- a/linux/include/linux/videodev2.h +++ b/linux/include/linux/videodev2.h @@ -1118,6 +1118,10 @@ enum v4l2_exposure_auto_type { #define V4L2_CID_FOCUS_RELATIVE (V4L2_CID_CAMERA_CLASS_BASE+11) #define V4L2_CID_FOCUS_AUTO (V4L2_CID_CAMERA_CLASS_BASE+12) +#define V4L2_CID_ZOOM_ABSOLUTE (V4L2_CID_CAMERA_CLASS_BASE+13) +#define V4L2_CID_ZOOM_RELATIVE (V4L2_CID_CAMERA_CLASS_BASE+14) +#define V4L2_CID_ZOOM_CONTINUOUS (V4L2_CID_CAMERA_CLASS_BASE+15) + /* * T U N I N G */ -- cgit v1.2.3 From a535d91c3ff903c13b31791f5053373a8ec94bc9 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 14 Dec 2008 20:22:05 +0100 Subject: v4l2: Add privacy control From: Laurent Pinchart The privacy control prevents video from being acquired by the camera. A true value indicates that no image can be captured. Devices that implement the privacy control must support read access and may support write access. Priority: normal Signed-off-by: Laurent Pinchart --- linux/include/linux/videodev2.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'linux') diff --git a/linux/include/linux/videodev2.h b/linux/include/linux/videodev2.h index 2d7f01059..6ee771108 100644 --- a/linux/include/linux/videodev2.h +++ b/linux/include/linux/videodev2.h @@ -1122,6 +1122,8 @@ enum v4l2_exposure_auto_type { #define V4L2_CID_ZOOM_RELATIVE (V4L2_CID_CAMERA_CLASS_BASE+14) #define V4L2_CID_ZOOM_CONTINUOUS (V4L2_CID_CAMERA_CLASS_BASE+15) +#define V4L2_CID_PRIVACY (V4L2_CID_CAMERA_CLASS_BASE+16) + /* * T U N I N G */ -- cgit v1.2.3 From 771b1cc58b98391259bcdb5e4c3d62eb7d4a43d6 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 14 Dec 2008 20:24:04 +0100 Subject: v4l2: Add missing control names From: Laurent Pinchart Update v4l2_ctrl_get_name() and v4l2_ctrl_get_menu() with missing control names and menu values. Priority: normal Signed-off-by: Laurent Pinchart --- linux/drivers/media/video/v4l2-common.c | 88 ++++++++++++++++++++++++++++----- 1 file changed, 77 insertions(+), 11 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/v4l2-common.c b/linux/drivers/media/video/v4l2-common.c index 6d5e41ecc..02eb97fa4 100644 --- a/linux/drivers/media/video/v4l2-common.c +++ b/linux/drivers/media/video/v4l2-common.c @@ -322,6 +322,19 @@ const char **v4l2_ctrl_get_menu(u32 id) "Private packet, IVTV format", NULL }; + static const char *camera_power_line_frequency[] = { + "Disabled", + "50 Hz", + "60 Hz", + NULL + }; + static const char *camera_exposure_auto[] = { + "Auto Mode", + "Manual Mode", + "Shutter Priority Mode", + "Aperture Priority Mode", + NULL + }; switch (id) { case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: @@ -354,6 +367,10 @@ const char **v4l2_ctrl_get_menu(u32 id) return mpeg_stream_type; case V4L2_CID_MPEG_STREAM_VBI_FMT: return mpeg_stream_vbi_fmt; + case V4L2_CID_POWER_LINE_FREQUENCY: + return camera_power_line_frequency; + case V4L2_CID_EXPOSURE_AUTO: + return camera_exposure_auto; default: return NULL; } @@ -365,17 +382,37 @@ const char *v4l2_ctrl_get_name(u32 id) { switch (id) { /* USER controls */ - case V4L2_CID_USER_CLASS: return "User Controls"; - case V4L2_CID_AUDIO_VOLUME: return "Volume"; - case V4L2_CID_AUDIO_MUTE: return "Mute"; - case V4L2_CID_AUDIO_BALANCE: return "Balance"; - case V4L2_CID_AUDIO_BASS: return "Bass"; - case V4L2_CID_AUDIO_TREBLE: return "Treble"; - case V4L2_CID_AUDIO_LOUDNESS: return "Loudness"; - case V4L2_CID_BRIGHTNESS: return "Brightness"; - case V4L2_CID_CONTRAST: return "Contrast"; - case V4L2_CID_SATURATION: return "Saturation"; - case V4L2_CID_HUE: return "Hue"; + case V4L2_CID_USER_CLASS: return "User Controls"; + case V4L2_CID_AUDIO_VOLUME: return "Volume"; + case V4L2_CID_AUDIO_MUTE: return "Mute"; + case V4L2_CID_AUDIO_BALANCE: return "Balance"; + case V4L2_CID_AUDIO_BASS: return "Bass"; + case V4L2_CID_AUDIO_TREBLE: return "Treble"; + case V4L2_CID_AUDIO_LOUDNESS: return "Loudness"; + case V4L2_CID_BRIGHTNESS: return "Brightness"; + case V4L2_CID_CONTRAST: return "Contrast"; + case V4L2_CID_SATURATION: return "Saturation"; + case V4L2_CID_HUE: return "Hue"; + case V4L2_CID_BLACK_LEVEL: return "Black Level"; + case V4L2_CID_AUTO_WHITE_BALANCE: return "White Balance, Automatic"; + case V4L2_CID_DO_WHITE_BALANCE: return "Do White Balance"; + case V4L2_CID_RED_BALANCE: return "Red Balance"; + case V4L2_CID_BLUE_BALANCE: return "Blue Balance"; + case V4L2_CID_GAMMA: return "Gamma"; + case V4L2_CID_EXPOSURE: return "Exposure"; + case V4L2_CID_AUTOGAIN: return "Gain, Automatic"; + case V4L2_CID_GAIN: return "Gain"; + case V4L2_CID_HFLIP: return "Horizontal Flip"; + case V4L2_CID_VFLIP: return "Vertical Flip"; + case V4L2_CID_HCENTER: return "Horizontal Center"; + case V4L2_CID_VCENTER: return "Vertical Center"; + case V4L2_CID_POWER_LINE_FREQUENCY: return "Power Line Frequency"; + case V4L2_CID_HUE_AUTO: return "Hue, Automatic"; + case V4L2_CID_WHITE_BALANCE_TEMPERATURE: return "White Balance Temperature"; + case V4L2_CID_SHARPNESS: return "Sharpness"; + case V4L2_CID_BACKLIGHT_COMPENSATION: return "Backlight Compensation"; + case V4L2_CID_CHROMA_AGC: return "Chroma AGC"; + case V4L2_CID_COLOR_KILLER: return "Color Killer"; /* MPEG controls */ case V4L2_CID_MPEG_CLASS: return "MPEG Encoder Controls"; @@ -412,6 +449,25 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_STREAM_PES_ID_VIDEO: return "Stream PES Video ID"; case V4L2_CID_MPEG_STREAM_VBI_FMT: return "Stream VBI Format"; + /* CAMERA controls */ + case V4L2_CID_CAMERA_CLASS: return "Camera Controls"; + case V4L2_CID_EXPOSURE_AUTO: return "Auto Exposure"; + case V4L2_CID_EXPOSURE_ABSOLUTE: return "Exposure Time, Absolute"; + case V4L2_CID_EXPOSURE_AUTO_PRIORITY: return "Exposure, Dynamic Framerate"; + case V4L2_CID_PAN_RELATIVE: return "Pan, Relative"; + case V4L2_CID_TILT_RELATIVE: return "Tilt, Relative"; + case V4L2_CID_PAN_RESET: return "Pan, Reset"; + case V4L2_CID_TILT_RESET: return "Tilt, Reset"; + case V4L2_CID_PAN_ABSOLUTE: return "Pan, Absolute"; + case V4L2_CID_TILT_ABSOLUTE: return "Tilt, Absolute"; + case V4L2_CID_FOCUS_ABSOLUTE: return "Focus, Absolute"; + case V4L2_CID_FOCUS_RELATIVE: return "Focus, Relative"; + case V4L2_CID_FOCUS_AUTO: return "Focus, Automatic"; + case V4L2_CID_ZOOM_ABSOLUTE: return "Zoom, Absolute"; + case V4L2_CID_ZOOM_RELATIVE: return "Zoom, Relative"; + case V4L2_CID_ZOOM_CONTINUOUS: return "Zoom, Continuous"; + case V4L2_CID_PRIVACY: return "Privacy"; + default: return NULL; } @@ -430,14 +486,22 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste switch (qctrl->id) { case V4L2_CID_AUDIO_MUTE: case V4L2_CID_AUDIO_LOUDNESS: + case V4L2_CID_AUTO_WHITE_BALANCE: + case V4L2_CID_AUTOGAIN: + case V4L2_CID_HFLIP: + case V4L2_CID_VFLIP: + case V4L2_CID_HUE_AUTO: case V4L2_CID_MPEG_AUDIO_MUTE: case V4L2_CID_MPEG_VIDEO_MUTE: case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: case V4L2_CID_MPEG_VIDEO_PULLDOWN: + case V4L2_CID_EXPOSURE_AUTO_PRIORITY: + case V4L2_CID_PRIVACY: qctrl->type = V4L2_CTRL_TYPE_BOOLEAN; min = 0; max = step = 1; break; + case V4L2_CID_POWER_LINE_FREQUENCY: case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: case V4L2_CID_MPEG_AUDIO_ENCODING: case V4L2_CID_MPEG_AUDIO_L1_BITRATE: @@ -453,10 +517,12 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: case V4L2_CID_MPEG_STREAM_TYPE: case V4L2_CID_MPEG_STREAM_VBI_FMT: + case V4L2_CID_EXPOSURE_AUTO: qctrl->type = V4L2_CTRL_TYPE_MENU; step = 1; break; case V4L2_CID_USER_CLASS: + case V4L2_CID_CAMERA_CLASS: case V4L2_CID_MPEG_CLASS: qctrl->type = V4L2_CTRL_TYPE_CTRL_CLASS; qctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; -- cgit v1.2.3 From 75cb6237407f2d533da57987203cfc43c221e380 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sun, 14 Dec 2008 19:26:25 -0500 Subject: cx18: Refine the firmware load and firmware startup process From: Andy Walls 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. Priority: normal Signed-off-by: Andy Walls --- linux/drivers/media/video/cx18/cx18-driver.c | 1 + linux/drivers/media/video/cx18/cx18-firmware.c | 101 +++++++++++++------------ linux/drivers/media/video/cx18/cx18-mailbox.c | 3 +- linux/drivers/media/video/cx18/cx18-scb.c | 1 - linux/drivers/media/video/cx18/cx23418.h | 6 ++ 5 files changed, 60 insertions(+), 52 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/cx18/cx18-driver.c b/linux/drivers/media/video/cx18/cx18-driver.c index f2a5b2c4d..e447b4ce7 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.c +++ b/linux/drivers/media/video/cx18/cx18-driver.c @@ -883,6 +883,7 @@ static int __devinit cx18_probe(struct pci_dev *dev, goto free_i2c; } cx18_init_memory(cx); + cx18_init_scb(cx); /* Register IRQ */ retval = request_irq(cx->dev->irq, cx18_irq_handler, diff --git a/linux/drivers/media/video/cx18/cx18-firmware.c b/linux/drivers/media/video/cx18/cx18-firmware.c index 8eac84314..e74f76d47 100644 --- a/linux/drivers/media/video/cx18/cx18-firmware.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-mailbox.c b/linux/drivers/media/video/cx18/cx18-mailbox.c index f20fbf091..89a45f51f 100644 --- a/linux/drivers/media/video/cx18/cx18-mailbox.c +++ b/linux/drivers/media/video/cx18/cx18-mailbox.c @@ -82,8 +82,9 @@ static const struct cx18_api_info api_info[] = { API_ENTRY(CPU, CX18_CPU_GET_ENC_PTS, 0), API_ENTRY(CPU, CX18_CPU_DE_SET_MDL_ACK, 0), API_ENTRY(CPU, CX18_CPU_DE_SET_MDL, API_FAST), - API_ENTRY(CPU, CX18_APU_RESETAI, API_FAST), API_ENTRY(CPU, CX18_CPU_DE_RELEASE_MDL, API_SLOW), + API_ENTRY(APU, CX18_APU_RESETAI, 0), + API_ENTRY(CPU, CX18_CPU_DEBUG_PEEK32, 0), API_ENTRY(0, 0, 0), }; diff --git a/linux/drivers/media/video/cx18/cx18-scb.c b/linux/drivers/media/video/cx18/cx18-scb.c index ac18bd932..34b4d03c5 100644 --- a/linux/drivers/media/video/cx18/cx18-scb.c +++ b/linux/drivers/media/video/cx18/cx18-scb.c @@ -118,6 +118,5 @@ void cx18_init_scb(struct cx18 *cx) cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, cpu_state), &cx->scb->ipc_offset); - cx18_writel(cx, 1, &cx->scb->hpu_state); cx18_writel(cx, 1, &cx->scb->epu_state); } diff --git a/linux/drivers/media/video/cx18/cx23418.h b/linux/drivers/media/video/cx18/cx23418.h index 668f968d7..601f3a2ab 100644 --- a/linux/drivers/media/video/cx18/cx23418.h +++ b/linux/drivers/media/video/cx18/cx23418.h @@ -44,6 +44,7 @@ /* All commands for CPU have the following mask set */ #define CPU_CMD_MASK 0x20000000 +#define CPU_CMD_MASK_DEBUG (CPU_CMD_MASK | 0x00000000) #define CPU_CMD_MASK_ACK (CPU_CMD_MASK | 0x80000000) #define CPU_CMD_MASK_CAPTURE (CPU_CMD_MASK | 0x00020000) #define CPU_CMD_MASK_TS (CPU_CMD_MASK | 0x00040000) @@ -71,6 +72,11 @@ 0/zero/NULL means "I have nothing to say" */ #define CX18_EPU_DEBUG (EPU_CMD_MASK_DEBUG | 0x0003) +/* Reads memory/registers (32-bit) + IN[0] - Address + OUT[1] - Value */ +#define CX18_CPU_DEBUG_PEEK32 (CPU_CMD_MASK_DEBUG | 0x0003) + /* Description: This command starts streaming with the set channel type IN[0] - Task handle. Handle of the task to start ReturnCode - One of the ERR_CAPTURE_... */ -- cgit v1.2.3 From c5e070eecd80d55b36be4bd4ad717e1c53387c78 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Mon, 15 Dec 2008 08:12:57 +0100 Subject: gspca - pac7311: Webcam 093a:262c added. From: Jean-Francois Moine Priority: normal Signed-off-by: Jean-Francois Moine --- linux/Documentation/video4linux/gspca.txt | 1 + linux/drivers/media/video/gspca/pac7311.c | 1 + 2 files changed, 2 insertions(+) (limited to 'linux') diff --git a/linux/Documentation/video4linux/gspca.txt b/linux/Documentation/video4linux/gspca.txt index 81d7d891c..a55459456 100644 --- a/linux/Documentation/video4linux/gspca.txt +++ b/linux/Documentation/video4linux/gspca.txt @@ -221,6 +221,7 @@ pac7311 093a:2622 Genius Eye 312 pac7311 093a:2624 PAC7302 pac7311 093a:2626 Labtec 2200 pac7311 093a:262a Webcam 300k +pac7311 093a:262c Philips SPC 230 NC zc3xx 0ac8:0302 Z-star Vimicro zc0302 vc032x 0ac8:0321 Vimicro generic vc0321 vc032x 0ac8:0323 Vimicro Vc0323 diff --git a/linux/drivers/media/video/gspca/pac7311.c b/linux/drivers/media/video/gspca/pac7311.c index b0a9d0687..51c8203e3 100644 --- a/linux/drivers/media/video/gspca/pac7311.c +++ b/linux/drivers/media/video/gspca/pac7311.c @@ -1083,6 +1083,7 @@ static __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x093a, 0x2624), .driver_info = SENSOR_PAC7302}, {USB_DEVICE(0x093a, 0x2626), .driver_info = SENSOR_PAC7302}, {USB_DEVICE(0x093a, 0x262a), .driver_info = SENSOR_PAC7302}, + {USB_DEVICE(0x093a, 0x262c), .driver_info = SENSOR_PAC7302}, {} }; MODULE_DEVICE_TABLE(usb, device_table); -- cgit v1.2.3 From 0bb40d367f4b5d1a17b71c1fa354799cb5abf1e0 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 15 Dec 2008 11:15:32 +0100 Subject: v4l: fix compile warning. From: Hans Verkuil Fix (bogus) compile warning about uninitialized variable. Priority: normal Signed-off-by: Hans Verkuil --- linux/drivers/media/video/sh_mobile_ceu_camera.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/video/sh_mobile_ceu_camera.c b/linux/drivers/media/video/sh_mobile_ceu_camera.c index e2e816213..bd2a1754b 100644 --- a/linux/drivers/media/video/sh_mobile_ceu_camera.c +++ b/linux/drivers/media/video/sh_mobile_ceu_camera.c @@ -448,7 +448,7 @@ static int sh_mobile_ceu_try_bus_param(struct soc_camera_device *icd, static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd, __u32 pixfmt, struct v4l2_rect *rect) { - const struct soc_camera_data_format *cam_fmt; + const struct soc_camera_data_format *cam_fmt = NULL; int ret; /* -- cgit v1.2.3 From f5435c98aa929cc30a19191341329ca7a6a91008 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 15 Dec 2008 11:19:20 +0100 Subject: v4l2-compat32: add missing newline after kernel message From: Hans Verkuil Priority: normal Signed-off-by: Hans Verkuil --- linux/drivers/media/video/v4l2-compat-ioctl32.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'linux') diff --git a/linux/drivers/media/video/v4l2-compat-ioctl32.c b/linux/drivers/media/video/v4l2-compat-ioctl32.c index 09d5c5693..e2e7a2061 100644 --- a/linux/drivers/media/video/v4l2-compat-ioctl32.c +++ b/linux/drivers/media/video/v4l2-compat-ioctl32.c @@ -977,6 +977,8 @@ long v4l_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) #endif default: v4l_print_ioctl("compat_ioctl32", cmd); + printk(KERN_CONT "\n"); + break; } return ret; } -- cgit v1.2.3 From 7981a739191a95dbcf8fd3aedc2c6a897f1cd3a1 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 16 Dec 2008 10:44:11 +0100 Subject: uvcvideo: V4L2 privacy control support From: Laurent Pinchart Priority: normal Signed-off-by: Laurent Pinchart --- linux/drivers/media/video/uvc/uvc_ctrl.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'linux') diff --git a/linux/drivers/media/video/uvc/uvc_ctrl.c b/linux/drivers/media/video/uvc/uvc_ctrl.c index 01c72935d..92b16c3d5 100644 --- a/linux/drivers/media/video/uvc/uvc_ctrl.c +++ b/linux/drivers/media/video/uvc/uvc_ctrl.c @@ -534,6 +534,16 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, }, + { + .id = V4L2_CID_PRIVACY, + .name = "Privacy", + .entity = UVC_GUID_UVC_CAMERA, + .selector = CT_PRIVACY_CONTROL, + .size = 1, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, + .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, + }, }; /* ------------------------------------------------------------------------ -- cgit v1.2.3 From 7dee6796e962e12a9ee483b99bd808b2a3218c13 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 16 Dec 2008 10:46:32 +0100 Subject: uvcvideo: V4L2 zoom controls support From: Laurent Pinchart Add support for absolute and continuous zoom controls (mapped to absolute and relative UVC zoom controls). Priority: normal Signed-off-by: Laurent Pinchart --- linux/drivers/media/video/uvc/uvc_ctrl.c | 76 +++++++++++++++++++++++++++----- linux/drivers/media/video/uvc/uvc_v4l2.c | 4 +- linux/drivers/media/video/uvc/uvcvideo.h | 5 +++ 3 files changed, 71 insertions(+), 14 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/uvc/uvc_ctrl.c b/linux/drivers/media/video/uvc/uvc_ctrl.c index 92b16c3d5..e9a75b064 100644 --- a/linux/drivers/media/video/uvc/uvc_ctrl.c +++ b/linux/drivers/media/video/uvc/uvc_ctrl.c @@ -329,6 +329,31 @@ static struct uvc_menu_info exposure_auto_controls[] = { { 8, "Aperture Priority Mode" }, }; +static __s32 uvc_ctrl_get_zoom(struct uvc_control_mapping *mapping, + __u8 query, const __u8 *data) +{ + __s8 zoom = (__s8)data[0]; + + switch (query) { + case GET_CUR: + return (zoom == 0) ? 0 : (zoom > 0 ? data[2] : -data[2]); + + case GET_MIN: + case GET_MAX: + case GET_RES: + case GET_DEF: + default: + return data[2]; + } +} + +static void uvc_ctrl_set_zoom(struct uvc_control_mapping *mapping, + __s32 value, __u8 *data) +{ + data[0] = value == 0 ? 0 : (value > 0) ? 1 : 0xff; + data[2] = min(abs(value), 0xff); +} + static struct uvc_control_mapping uvc_ctrl_mappings[] = { { .id = V4L2_CID_BRIGHTNESS, @@ -534,6 +559,28 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, }, + { + .id = V4L2_CID_ZOOM_ABSOLUTE, + .name = "Zoom, Absolute", + .entity = UVC_GUID_UVC_CAMERA, + .selector = CT_ZOOM_ABSOLUTE_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_ZOOM_CONTINUOUS, + .name = "Zoom, Continuous", + .entity = UVC_GUID_UVC_CAMERA, + .selector = CT_ZOOM_RELATIVE_CONTROL, + .size = 0, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_SIGNED, + .get = uvc_ctrl_get_zoom, + .set = uvc_ctrl_set_zoom, + }, { .id = V4L2_CID_PRIVACY, .name = "Privacy", @@ -570,8 +617,8 @@ static inline void uvc_clear_bit(__u8 *data, int bit) * a signed 32bit integer. Sign extension will be performed if the mapping * references a signed data type. */ -static __s32 uvc_get_le_value(const __u8 *data, - struct uvc_control_mapping *mapping) +static __s32 uvc_get_le_value(struct uvc_control_mapping *mapping, + __u8 query, const __u8 *data) { int bits = mapping->size; int offset = mapping->offset; @@ -600,8 +647,8 @@ static __s32 uvc_get_le_value(const __u8 *data, /* Set the bit string specified by mapping->offset and mapping->size * in the little-endian data stored at 'data' to the value 'value'. */ -static void uvc_set_le_value(__s32 value, __u8 *data, - struct uvc_control_mapping *mapping) +static void uvc_set_le_value(struct uvc_control_mapping *mapping, + __s32 value, __u8 *data) { int bits = mapping->size; int offset = mapping->offset; @@ -753,7 +800,7 @@ int uvc_query_v4l2_ctrl(struct uvc_video_device *video, video->dev->intfnum, ctrl->info->selector, data, ctrl->info->size)) < 0) goto out; - v4l2_ctrl->default_value = uvc_get_le_value(data, mapping); + v4l2_ctrl->default_value = mapping->get(mapping, GET_DEF, data); } switch (mapping->v4l2_type) { @@ -789,21 +836,21 @@ int uvc_query_v4l2_ctrl(struct uvc_video_device *video, video->dev->intfnum, ctrl->info->selector, data, ctrl->info->size)) < 0) goto out; - v4l2_ctrl->minimum = uvc_get_le_value(data, mapping); + v4l2_ctrl->minimum = mapping->get(mapping, GET_MIN, data); } if (ctrl->info->flags & UVC_CONTROL_GET_MAX) { if ((ret = uvc_query_ctrl(video->dev, GET_MAX, ctrl->entity->id, video->dev->intfnum, ctrl->info->selector, data, ctrl->info->size)) < 0) goto out; - v4l2_ctrl->maximum = uvc_get_le_value(data, mapping); + v4l2_ctrl->maximum = mapping->get(mapping, GET_MAX, data); } if (ctrl->info->flags & UVC_CONTROL_GET_RES) { if ((ret = uvc_query_ctrl(video->dev, GET_RES, ctrl->entity->id, video->dev->intfnum, ctrl->info->selector, data, ctrl->info->size)) < 0) goto out; - v4l2_ctrl->step = uvc_get_le_value(data, mapping); + v4l2_ctrl->step = mapping->get(mapping, GET_RES, data); } ret = 0; @@ -940,8 +987,8 @@ int uvc_ctrl_get(struct uvc_video_device *video, ctrl->loaded = 1; } - xctrl->value = uvc_get_le_value( - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), mapping); + xctrl->value = mapping->get(mapping, GET_CUR, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT)); if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) { menu = mapping->menu_info; @@ -997,8 +1044,8 @@ int uvc_ctrl_set(struct uvc_video_device *video, ctrl->info->size); } - uvc_set_le_value(value, - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), mapping); + mapping->set(mapping, value, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT)); ctrl->dirty = 1; ctrl->modified = 1; @@ -1274,6 +1321,11 @@ int uvc_ctrl_add_mapping(struct uvc_control_mapping *mapping) struct uvc_control_mapping *map; int ret = -EINVAL; + if (mapping->get == NULL) + mapping->get = uvc_get_le_value; + if (mapping->set == NULL) + mapping->set = uvc_set_le_value; + if (mapping->id & ~V4L2_CTRL_ID_MASK) { uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s' with " "invalid control id 0x%08x\n", mapping->name, diff --git a/linux/drivers/media/video/uvc/uvc_v4l2.c b/linux/drivers/media/video/uvc/uvc_v4l2.c index 95eb6f3ad..b6ee9aac6 100644 --- a/linux/drivers/media/video/uvc/uvc_v4l2.c +++ b/linux/drivers/media/video/uvc/uvc_v4l2.c @@ -920,7 +920,7 @@ static int uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) if (!capable(CAP_SYS_ADMIN)) return -EPERM; - info = kmalloc(sizeof *info, GFP_KERNEL); + info = kzalloc(sizeof *info, GFP_KERNEL); if (info == NULL) return -ENOMEM; @@ -947,7 +947,7 @@ static int uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) if (!capable(CAP_SYS_ADMIN)) return -EPERM; - map = kmalloc(sizeof *map, GFP_KERNEL); + map = kzalloc(sizeof *map, GFP_KERNEL); if (map == NULL) return -ENOMEM; diff --git a/linux/drivers/media/video/uvc/uvcvideo.h b/linux/drivers/media/video/uvc/uvcvideo.h index a699c0bb1..549d09fd3 100644 --- a/linux/drivers/media/video/uvc/uvcvideo.h +++ b/linux/drivers/media/video/uvc/uvcvideo.h @@ -384,6 +384,11 @@ struct uvc_control_mapping { struct uvc_menu_info *menu_info; __u32 menu_count; + + __s32 (*get) (struct uvc_control_mapping *mapping, __u8 query, + const __u8 *data); + void (*set) (struct uvc_control_mapping *mapping, __s32 value, + __u8 *data); }; struct uvc_control { -- cgit v1.2.3 From bd1a1f2bd484ed3eaa12def931e17c4444535ef8 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 16 Dec 2008 12:32:37 +0100 Subject: v4l2-compat: test for unlocked_ioctl as well. From: Hans Verkuil The v4l_compat_ioctl32() function only tested for the presence of the ioctl op, not for unlocked_ioctl. So it would always return an error when used with drivers that use unlocked_ioctl instead of ioctl. Priority: high Signed-off-by: Hans Verkuil --- linux/drivers/media/video/v4l2-compat-ioctl32.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/video/v4l2-compat-ioctl32.c b/linux/drivers/media/video/v4l2-compat-ioctl32.c index e2e7a2061..171f1ccd1 100644 --- a/linux/drivers/media/video/v4l2-compat-ioctl32.c +++ b/linux/drivers/media/video/v4l2-compat-ioctl32.c @@ -887,7 +887,7 @@ long v4l_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) { int ret = -ENOIOCTLCMD; - if (!file->f_op->ioctl) + if (!file->f_op->ioctl && !file->f_op->unlocked_ioctl) return ret; switch (cmd) { -- cgit v1.2.3 From 1e115775994e75b023a2b0a53fecb75bb949cc95 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 16 Dec 2008 16:25:46 -0200 Subject: Revert changeset 9886 From: Mauro Carvalho Chehab Changeset 9886 were applied by mistake, reverting changeset 9885, that got a wrong comment. Let's revert it. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/dvb/b2c2/Kconfig | 2 +- linux/drivers/media/dvb/bt8xx/Kconfig | 2 +- linux/drivers/media/dvb/dvb-usb/Kconfig | 46 +++++++++++++++---------------- linux/drivers/media/dvb/ttpci/Kconfig | 2 +- linux/drivers/media/video/cx18/Kconfig | 2 +- linux/drivers/media/video/cx23885/Kconfig | 4 +-- linux/drivers/media/video/cx88/Kconfig | 2 +- linux/drivers/media/video/pvrusb2/Kconfig | 2 +- linux/drivers/media/video/saa7134/Kconfig | 4 +-- 9 files changed, 33 insertions(+), 33 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/dvb/b2c2/Kconfig b/linux/drivers/media/dvb/b2c2/Kconfig index e5c27e355..bd21c3bf2 100644 --- a/linux/drivers/media/dvb/b2c2/Kconfig +++ b/linux/drivers/media/dvb/b2c2/Kconfig @@ -9,12 +9,12 @@ config DVB_B2C2_FLEXCOP select DVB_STV0297 if !DVB_FE_CUSTOMISE select DVB_BCM3510 if !DVB_FE_CUSTOMISE select DVB_LGDT330X if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_SIMPLE if !DVB_FE_CUSTOMISE select DVB_S5H1420 if !DVB_FE_CUSTOMISE select DVB_TUNER_ITD1000 if !DVB_FE_CUSTOMISE select DVB_ISL6421 if !DVB_FE_CUSTOMISE select DVB_CX24123 if !DVB_FE_CUSTOMISE select DVB_TUNER_CX24113 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMIZE help Support for the digital TV receiver chip made by B2C2 Inc. included in Technisats PCI cards and USB boxes. diff --git a/linux/drivers/media/dvb/bt8xx/Kconfig b/linux/drivers/media/dvb/bt8xx/Kconfig index 7e9c090fc..27edb0ece 100644 --- a/linux/drivers/media/dvb/bt8xx/Kconfig +++ b/linux/drivers/media/dvb/bt8xx/Kconfig @@ -8,7 +8,7 @@ config DVB_BT8XX select DVB_OR51211 if !DVB_FE_CUSTOMISE select DVB_LGDT330X if !DVB_FE_CUSTOMISE select DVB_ZL10353 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_SIMPLE if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMIZE help Support for PCI cards based on the Bt8xx PCI bridge. Examples are the Nebula cards, the Pinnacle PCTV cards, the Twinhan DST cards, diff --git a/linux/drivers/media/dvb/dvb-usb/Kconfig b/linux/drivers/media/dvb/dvb-usb/Kconfig index 62b68c291..49f7b20c2 100644 --- a/linux/drivers/media/dvb/dvb-usb/Kconfig +++ b/linux/drivers/media/dvb/dvb-usb/Kconfig @@ -24,8 +24,8 @@ config DVB_USB_A800 tristate "AVerMedia AverTV DVB-T USB 2.0 (A800)" depends on DVB_USB select DVB_DIB3000MC - select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE select DVB_PLL if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMIZE help Say Y here to support the AVerMedia AverTV DVB-T USB 2.0 (A800) receiver. @@ -34,7 +34,7 @@ config DVB_USB_DIBUSB_MB depends on DVB_USB select DVB_PLL if !DVB_FE_CUSTOMISE select DVB_DIB3000MB - select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMIZE help Support for USB 1.1 and 2.0 DVB-T receivers based on reference designs made by DiBcom () equipped with a DiB3000M-B demodulator. @@ -55,7 +55,7 @@ config DVB_USB_DIBUSB_MC tristate "DiBcom USB DVB-T devices (based on the DiB3000M-C/P) (see help for device list)" depends on DVB_USB select DVB_DIB3000MC - select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMIZE help Support for USB2.0 DVB-T receivers based on reference designs made by DiBcom () equipped with a DiB3000M-C/P demodulator. @@ -73,11 +73,11 @@ config DVB_USB_DIB0700 select DVB_DIB7000M select DVB_DIB3000MC select DVB_S5H1411 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_MT2266 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_XC2028 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_XC5000 if !DVB_FE_CUSTOMIZE select DVB_TUNER_DIB0070 + select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMIZE + select MEDIA_TUNER_MT2266 if !MEDIA_TUNER_CUSTOMIZE + select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMIZE + select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMIZE help Support for USB2.0/1.1 DVB receivers based on the DiB0700 USB bridge. The USB bridge is also present in devices having the DiB7700 DVB-T-USB @@ -95,7 +95,7 @@ config DVB_USB_UMT_010 depends on DVB_USB select DVB_PLL if !DVB_FE_CUSTOMISE select DVB_DIB3000MC - select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMIZE help Say Y here to support the HanfTek UMT-010 USB2.0 stick-sized DVB-T receiver. @@ -107,11 +107,11 @@ config DVB_USB_CXUSB select DVB_LGDT330X if !DVB_FE_CUSTOMISE select DVB_MT352 if !DVB_FE_CUSTOMISE select DVB_ZL10353 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_SIMPLE if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_XC2028 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_MXL5005S if !DVB_FE_CUSTOMISE select DVB_DIB7000P if !DVB_FE_CUSTOMISE select DVB_TUNER_DIB0070 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMIZE + select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMIZE + select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMIZE help Say Y here to support the Conexant USB2.0 hybrid reference design. Currently, only DVB and ATSC modes are supported, analog mode @@ -124,9 +124,9 @@ config DVB_USB_M920X tristate "Uli m920x DVB-T USB2.0 support" depends on DVB_USB select DVB_MT352 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_QT1010 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_TDA827X if !DVB_FE_CUSTOMISE select DVB_TDA1004X if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMIZE + select MEDIA_TUNER_TDA827X if !MEDIA_TUNER_CUSTOMIZE help Say Y here to support the MSI Mega Sky 580 USB2.0 DVB-T receiver. Currently, only devices with a product id of @@ -137,7 +137,7 @@ config DVB_USB_GL861 tristate "Genesys Logic GL861 USB2.0 support" depends on DVB_USB select DVB_ZL10353 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_QT1010 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMIZE help Say Y here to support the MSI Megasky 580 (55801) DVB-T USB2.0 receiver with USB ID 0db0:5581. @@ -146,7 +146,7 @@ config DVB_USB_AU6610 tristate "Alcor Micro AU6610 USB2.0 support" depends on DVB_USB select DVB_ZL10353 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_QT1010 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMIZE help Say Y here to support the Sigmatek DVB-110 DVB-T USB2.0 receiver. @@ -198,8 +198,8 @@ config DVB_USB_NOVA_T_USB2 tristate "Hauppauge WinTV-NOVA-T usb2 DVB-T USB2.0 support" depends on DVB_USB select DVB_DIB3000MC - select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE select DVB_PLL if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMIZE help Say Y here to support the Hauppauge WinTV-NOVA-T usb2 DVB-T USB2.0 receiver. @@ -235,8 +235,8 @@ config DVB_USB_OPERA1 config DVB_USB_AF9005 tristate "Afatech AF9005 DVB-T USB1.1 support" depends on DVB_USB && EXPERIMENTAL - select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_QT1010 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMIZE + select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMIZE help Say Y here to support the Afatech AF9005 based DVB-T USB1.1 receiver and the TerraTec Cinergy T USB XE (Rev.1) @@ -284,7 +284,7 @@ config DVB_USB_DTV5100 tristate "AME DTV-5100 USB2.0 DVB-T support" depends on DVB_USB select DVB_ZL10353 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_QT1010 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMIZE help Say Y here to support the AME DTV-5100 USB2.0 DVB-T receiver. @@ -293,9 +293,9 @@ config DVB_USB_AF9015 depends on DVB_USB && EXPERIMENTAL select DVB_AF9013 select DVB_PLL if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_QT1010 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_TDA18271 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_MXL5005S if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMIZE + select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMIZE + select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMIZE + select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMIZE help Say Y here to support the Afatech AF9015 based DVB-T USB2.0 receiver diff --git a/linux/drivers/media/dvb/ttpci/Kconfig b/linux/drivers/media/dvb/ttpci/Kconfig index 9e2ece9e4..ab0bcd208 100644 --- a/linux/drivers/media/dvb/ttpci/Kconfig +++ b/linux/drivers/media/dvb/ttpci/Kconfig @@ -108,7 +108,7 @@ config DVB_BUDGET_CI select DVB_STB6100 if !DVB_FE_CUSTOMISE select DVB_LNBP21 if !DVB_FE_CUSTOMISE select DVB_TDA10023 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_TDA827X if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_TDA827X if !MEDIA_TUNER_CUSTOMIZE select VIDEO_IR help Support for simple SAA7146 based DVB cards diff --git a/linux/drivers/media/video/cx18/Kconfig b/linux/drivers/media/video/cx18/Kconfig index ef48565de..8940b5387 100644 --- a/linux/drivers/media/video/cx18/Kconfig +++ b/linux/drivers/media/video/cx18/Kconfig @@ -9,7 +9,7 @@ config VIDEO_CX18 select VIDEO_CX2341X select VIDEO_CS5345 select DVB_S5H1409 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_MXL5005S if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMIZE ---help--- This is a video4linux driver for Conexant cx23418 based PCI combo video recorder devices. diff --git a/linux/drivers/media/video/cx23885/Kconfig b/linux/drivers/media/video/cx23885/Kconfig index 8c1b7fa47..00f1e2e88 100644 --- a/linux/drivers/media/video/cx23885/Kconfig +++ b/linux/drivers/media/video/cx23885/Kconfig @@ -11,16 +11,16 @@ config VIDEO_CX23885 select VIDEO_CX25840 select VIDEO_CX2341X select DVB_DIB7000P if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_MT2131 if !DVB_FE_CUSTOMISE select DVB_S5H1409 if !DVB_FE_CUSTOMISE select DVB_S5H1411 if !DVB_FE_CUSTOMISE select DVB_LGDT330X if !DVB_FE_CUSTOMISE select DVB_ZL10353 if !DVB_FE_CUSTOMISE + select DVB_TDA10048 if !DVB_FE_CUSTOMIZE + select MEDIA_TUNER_MT2131 if !MEDIA_TUNER_CUSTOMIZE select MEDIA_TUNER_XC2028 if !DVB_FE_CUSTOMIZE select MEDIA_TUNER_TDA8290 if !DVB_FE_CUSTOMIZE select MEDIA_TUNER_TDA18271 if !DVB_FE_CUSTOMIZE select MEDIA_TUNER_XC5000 if !DVB_FE_CUSTOMIZE - select DVB_TDA10048 if !DVB_FE_CUSTOMIZE ---help--- This is a video4linux driver for Conexant 23885 based TV cards. diff --git a/linux/drivers/media/video/cx88/Kconfig b/linux/drivers/media/video/cx88/Kconfig index 0b9e5fac6..b0f837588 100644 --- a/linux/drivers/media/video/cx88/Kconfig +++ b/linux/drivers/media/video/cx88/Kconfig @@ -56,12 +56,12 @@ config VIDEO_CX88_DVB select DVB_NXT200X if !DVB_FE_CUSTOMISE select DVB_CX24123 if !DVB_FE_CUSTOMISE select DVB_ISL6421 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_SIMPLE if !DVB_FE_CUSTOMISE select DVB_S5H1411 if !DVB_FE_CUSTOMISE select DVB_CX24116 if !DVB_FE_CUSTOMISE select DVB_STV0299 if !DVB_FE_CUSTOMISE select DVB_STV0288 if !DVB_FE_CUSTOMISE select DVB_STB6000 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMIZE ---help--- This adds support for DVB/ATSC cards based on the Conexant 2388x chip. diff --git a/linux/drivers/media/video/pvrusb2/Kconfig b/linux/drivers/media/video/pvrusb2/Kconfig index 19eb274c9..854c2a885 100644 --- a/linux/drivers/media/video/pvrusb2/Kconfig +++ b/linux/drivers/media/video/pvrusb2/Kconfig @@ -42,7 +42,7 @@ config VIDEO_PVRUSB2_DVB select DVB_S5H1411 if !DVB_FE_CUSTOMISE select DVB_TDA10048 if !DVB_FE_CUSTOMIZE select MEDIA_TUNER_TDA18271 if !DVB_FE_CUSTOMIZE - select MEDIA_TUNER_SIMPLE if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMIZE select MEDIA_TUNER_TDA8290 if !DVB_FE_CUSTOMIZE ---help--- diff --git a/linux/drivers/media/video/saa7134/Kconfig b/linux/drivers/media/video/saa7134/Kconfig index 7021bbf58..fc2164e28 100644 --- a/linux/drivers/media/video/saa7134/Kconfig +++ b/linux/drivers/media/video/saa7134/Kconfig @@ -34,9 +34,9 @@ config VIDEO_SAA7134_DVB select DVB_NXT200X if !DVB_FE_CUSTOMISE select DVB_TDA10086 if !DVB_FE_CUSTOMISE select DVB_TDA826X if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_TDA827X if !DVB_FE_CUSTOMISE select DVB_ISL6421 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_SIMPLE if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_TDA827X if !MEDIA_TUNER_CUSTOMIZE + select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMIZE ---help--- This adds support for DVB cards based on the Philips saa7134 chip. -- cgit v1.2.3 From a0eaf112882fd10323cb5a701bffffb4e5dfcb7b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 16 Dec 2008 21:00:49 -0200 Subject: em28xx: move dev->lock from res_free to the caller routines From: Mauro Carvalho Chehab Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-video.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index ff960f90c..9ac643abb 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -622,10 +622,8 @@ static void res_free(struct em28xx_fh *fh) { struct em28xx *dev = fh->dev; - mutex_lock(&dev->lock); fh->stream_on = 0; dev->stream_on = 0; - mutex_unlock(&dev->lock); } /* @@ -1293,7 +1291,9 @@ static int vidioc_streamoff(struct file *file, void *priv, return -EINVAL; videobuf_streamoff(&fh->vb_vidq); + mutex_lock(&dev->lock); res_free(fh); + mutex_unlock(&dev->lock); return 0; } @@ -1740,11 +1740,10 @@ static int em28xx_v4l2_close(struct inode *inode, struct file *filp) em28xx_videodbg("users=%d\n", dev->users); + mutex_lock(&dev->lock); if (res_check(fh)) res_free(fh); - mutex_lock(&dev->lock); - if (dev->users == 1) { videobuf_stop(&fh->vb_vidq); videobuf_mmap_free(&fh->vb_vidq); -- cgit v1.2.3 From a382f22c815364a55ab62b31eb5f3b6157dc250c Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 16 Dec 2008 21:19:24 -0200 Subject: em28xx: move res_get locks to the caller routines From: Mauro Carvalho Chehab Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-video.c | 36 +++++++++++++++++-------- 1 file changed, 25 insertions(+), 11 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index 9ac643abb..d486bc204 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -604,12 +604,10 @@ static int res_get(struct em28xx_fh *fh) return rc; if (dev->stream_on) - return -EINVAL; + return -EBUSY; - mutex_lock(&dev->lock); dev->stream_on = 1; fh->stream_on = 1; - mutex_unlock(&dev->lock); return rc; } @@ -1268,8 +1266,12 @@ static int vidioc_streamon(struct file *file, void *priv, return rc; - if (unlikely(res_get(fh) < 0)) - return -EBUSY; + mutex_lock(&dev->lock); + rc = res_get(fh); + mutex_unlock(&dev->lock); + + if (unlikely(rc < 0)) + return rc; return (videobuf_streamon(&fh->vb_vidq)); } @@ -1801,8 +1803,12 @@ em28xx_v4l2_read(struct file *filp, char __user *buf, size_t count, */ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - if (unlikely(res_get(fh))) - return -EBUSY; + mutex_lock(&dev->lock); + rc = res_get(fh); + mutex_unlock(&dev->lock); + + if (unlikely(rc < 0)) + return rc; return videobuf_read_stream(&fh->vb_vidq, buf, count, pos, 0, filp->f_flags & O_NONBLOCK); @@ -1824,7 +1830,11 @@ static unsigned int em28xx_v4l2_poll(struct file *filp, poll_table * wait) if (rc < 0) return rc; - if (unlikely(res_get(fh) < 0)) + mutex_lock(&dev->lock); + rc = res_get(fh); + mutex_unlock(&dev->lock); + + if (unlikely(rc < 0)) return POLLERR; if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type) @@ -1842,13 +1852,17 @@ static int em28xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma) struct em28xx *dev = fh->dev; int rc; - if (unlikely(res_get(fh) < 0)) - return -EBUSY; - rc = check_dev(dev); if (rc < 0) return rc; + mutex_lock(&dev->lock); + rc = res_get(fh); + mutex_unlock(&dev->lock); + + if (unlikely(rc < 0)) + return rc; + rc = videobuf_mmap_mapper(&fh->vb_vidq, vma); em28xx_videodbg("vma start=0x%08lx, size=%ld, ret=%d\n", -- cgit v1.2.3 From e19130557b656d2b6aeaf8da6a9a58e428ef1b00 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 16 Dec 2008 21:36:13 -0200 Subject: em28xx: vidioc_try_fmt_vid_cap() doesn't need any lock From: Mauro Carvalho Chehab vidioc_try_fmt_vid_cap() just checks if a given resolution is supported. It doesn't touch on struct em28xx device descriptor. so, there's no need to lock. While there, use unlikely() for those values that aren't likely to occur. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-video.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index d486bc204..415ac9861 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -735,19 +735,17 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, /* width must even because of the YUYV format height must be even because of interlacing */ height &= 0xfffe; - width &= 0xfffe; + width &= 0xfffe; - if (height < 32) + if (unlikely(height < 32)) height = 32; - if (height > maxh) + if (unlikely(height > maxh)) height = maxh; - if (width < 48) + if (unlikely(width < 48)) width = 48; - if (width > maxw) + if (unlikely(width > maxw)) width = maxw; - mutex_lock(&dev->lock); - if (dev->board.is_em2800) { /* the em2800 can only scale down to 50% */ if (height % (maxh / 2)) @@ -777,7 +775,6 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; f->fmt.pix.field = V4L2_FIELD_INTERLACED; - mutex_unlock(&dev->lock); return 0; } -- cgit v1.2.3 From 9052d2a1095d9cf39e4f9d3fa8989beefbd2933e Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 16 Dec 2008 23:04:56 -0200 Subject: em28xx: fix/improve em28xx locking schema From: Mauro Carvalho Chehab Changes/fixes on em28xx dev->lock: - em28xx_init_dev() were unlocking without a previous lock; - some read ioctls need to lock after the removal of KBL, since a write may be happening at the same time an ioctl is reading; - keep the device locked during all device initialization; - lock/unlock while reading/writing registers. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-video.c | 55 +++++++++++++++++++------ 1 file changed, 43 insertions(+), 12 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index 415ac9861..acf6141aa 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -789,10 +789,10 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, if (rc < 0) return rc; - vidioc_try_fmt_vid_cap(file, priv, f); - mutex_lock(&dev->lock); + vidioc_try_fmt_vid_cap(file, priv, f); + if (videobuf_queue_is_busy(&fh->vb_vidq)) { em28xx_errdev("%s queue busy\n", __func__); rc = -EBUSY; @@ -833,15 +833,12 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id * norm) mutex_lock(&dev->lock); dev->norm = *norm; - mutex_unlock(&dev->lock); /* Adjusts width/height, if needed */ f.fmt.pix.width = dev->width; f.fmt.pix.height = dev->height; vidioc_try_fmt_vid_cap(file, priv, &f); - mutex_lock(&dev->lock); - /* set new image size */ dev->width = f.fmt.pix.width; dev->height = f.fmt.pix.height; @@ -976,11 +973,15 @@ static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a) if (a->index != dev->ctl_ainput) return -EINVAL; #else + mutex_lock(&dev->lock); + dev->ctl_ainput = INPUT(a->index)->amux; dev->ctl_aoutput = INPUT(a->index)->aout; if (!dev->ctl_aoutput) dev->ctl_aoutput = EM28XX_AOUT_MASTER; + + mutex_unlock(&dev->lock); #endif return 0; } @@ -1030,6 +1031,7 @@ static int vidioc_g_ctrl(struct file *file, void *priv, rc = check_dev(dev); if (rc < 0) return rc; + mutex_lock(&dev->lock); if (!dev->board.has_msp34xx) @@ -1140,8 +1142,10 @@ static int vidioc_g_frequency(struct file *file, void *priv, struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; + mutex_lock(&dev->lock); f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; f->frequency = dev->ctl_freq; + mutex_unlock(&dev->lock); return 0; } @@ -1171,6 +1175,7 @@ static int vidioc_s_frequency(struct file *file, void *priv, em28xx_i2c_call_clients(dev, VIDIOC_S_FREQUENCY, f); mutex_unlock(&dev->lock); + return 0; } @@ -1198,15 +1203,20 @@ static int vidioc_g_register(struct file *file, void *priv, return -EINVAL; if (em28xx_reg_len(reg->reg) == 1) { + mutex_lock(&dev->lock); ret = em28xx_read_reg(dev, reg->reg); + mutex_unlock(&dev->lock); + if (ret < 0) return ret; reg->val = ret; } else { __le64 val = 0; + mutex_lock(&dev->lock); ret = em28xx_read_reg_req_len(dev, USB_REQ_GET_STATUS, reg->reg, (char *)&val, 2); + mutex_unlock(&dev->lock); if (ret < 0) return ret; @@ -1222,11 +1232,16 @@ static int vidioc_s_register(struct file *file, void *priv, struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; __le64 buf; + int rc; buf = cpu_to_le64(reg->val); - return em28xx_write_regs(dev, reg->reg, (char *)&buf, - em28xx_reg_len(reg->reg)); + mutex_lock(&dev->lock); + rc = em28xx_write_regs(dev, reg->reg, (char *)&buf, + em28xx_reg_len(reg->reg)); + mutex_unlock(&dev->lock); + + return rc; } #endif @@ -1265,12 +1280,15 @@ static int vidioc_streamon(struct file *file, void *priv, mutex_lock(&dev->lock); rc = res_get(fh); - mutex_unlock(&dev->lock); if (unlikely(rc < 0)) return rc; - return (videobuf_streamon(&fh->vb_vidq)); + rc = videobuf_streamon(&fh->vb_vidq); + + mutex_unlock(&dev->lock); + + return rc; } static int vidioc_streamoff(struct file *file, void *priv, @@ -1289,9 +1307,11 @@ static int vidioc_streamoff(struct file *file, void *priv, if (type != fh->type) return -EINVAL; - videobuf_streamoff(&fh->vb_vidq); mutex_lock(&dev->lock); + + videobuf_streamoff(&fh->vb_vidq); res_free(fh); + mutex_unlock(&dev->lock); return 0; @@ -1513,7 +1533,10 @@ static int radio_g_tuner(struct file *file, void *priv, strcpy(t->name, "Radio"); t->type = V4L2_TUNER_RADIO; + mutex_lock(&dev->lock); em28xx_i2c_call_clients(dev, VIDIOC_G_TUNER, t); + mutex_unlock(&dev->lock); + return 0; } @@ -1545,7 +1568,9 @@ static int radio_s_tuner(struct file *file, void *priv, if (0 != t->index) return -EINVAL; + mutex_lock(&dev->lock); em28xx_i2c_call_clients(dev, VIDIOC_S_TUNER, t); + mutex_unlock(&dev->lock); return 0; } @@ -1609,6 +1634,7 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp) } } mutex_unlock(&em28xx_devlist_mutex); + if (NULL == dev) return -ENODEV; @@ -2104,7 +2130,6 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, unsigned int maxh, maxw; dev->udev = udev; - mutex_init(&dev->lock); mutex_init(&dev->ctrl_urb_lock); spin_lock_init(&dev->slock); init_waitqueue_head(&dev->open); @@ -2222,7 +2247,6 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, return 0; fail_reg_devices: - mutex_unlock(&dev->lock); return retval; } @@ -2425,6 +2449,8 @@ static int em28xx_usb_probe(struct usb_interface *interface, dev->model = card[nr]; /* allocate device struct */ + mutex_init(&dev->lock); + mutex_lock(&dev->lock); retval = em28xx_init_dev(&dev, udev, nr); if (retval) { em28xx_devused &= ~(1<devno); @@ -2438,6 +2464,11 @@ static int em28xx_usb_probe(struct usb_interface *interface, request_modules(dev); + /* Should be the last thing to do, to avoid newer udev's to + open the device before fully initializing it + */ + mutex_unlock(&dev->lock); + return 0; } -- cgit v1.2.3 From 0d788195b491c7c18524b30618f44059eb35bc06 Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Tue, 16 Dec 2008 21:09:35 -0500 Subject: em28xx: fix NULL pointer dereference in call to VIDIOC_INT_RESET command From: Devin Heitmueller Fix a NULL pointer dereference that would occur if the video decoder tied to the em28xx supports the VIDIOC_INT_RESET call (for example: the cx25840 driver) Priority: high Signed-off-by: Devin Heitmueller --- linux/drivers/media/video/em28xx/em28xx-video.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index ff960f90c..f3c9e2331 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -555,10 +555,11 @@ static int em28xx_config(struct em28xx *dev) static void em28xx_config_i2c(struct em28xx *dev) { struct v4l2_routing route; + int zero = 0; route.input = INPUT(dev->ctl_input)->vmux; route.output = 0; - em28xx_i2c_call_clients(dev, VIDIOC_INT_RESET, NULL); + em28xx_i2c_call_clients(dev, VIDIOC_INT_RESET, &zero); em28xx_i2c_call_clients(dev, VIDIOC_INT_S_VIDEO_ROUTING, &route); em28xx_i2c_call_clients(dev, VIDIOC_STREAMON, NULL); } -- cgit v1.2.3 From 92b4058a43959c8e3cba8044d4bd3adba9cabdd5 Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Tue, 16 Dec 2008 21:15:33 -0500 Subject: em28xx: add chip id for em2874 From: Devin Heitmueller Add the em2870 to the list of known em28xx chip ids. Priority: normal Signed-off-by: Devin Heitmueller --- linux/drivers/media/video/em28xx/em28xx-cards.c | 4 ++++ linux/drivers/media/video/em28xx/em28xx-reg.h | 1 + 2 files changed, 5 insertions(+) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 2ecc98af7..3eb4408b4 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -1480,6 +1480,10 @@ void em28xx_pre_card_setup(struct em28xx *dev) case CHIP_ID_EM2860: em28xx_info("chip ID is em2860\n"); break; + case CHIP_ID_EM2870: + em28xx_info("chip ID is em2870\n"); + dev->wait_after_write = 0; + break; case CHIP_ID_EM2874: em28xx_info("chip ID is em2874\n"); dev->reg_gpio_num = EM2874_R80_GPIO; diff --git a/linux/drivers/media/video/em28xx/em28xx-reg.h b/linux/drivers/media/video/em28xx/em28xx-reg.h index 45d588c3a..a459b7c6a 100644 --- a/linux/drivers/media/video/em28xx/em28xx-reg.h +++ b/linux/drivers/media/video/em28xx/em28xx-reg.h @@ -151,6 +151,7 @@ enum em28xx_chip_id { CHIP_ID_EM2840 = 20, CHIP_ID_EM2750 = 33, CHIP_ID_EM2860 = 34, + CHIP_ID_EM2870 = 35, CHIP_ID_EM2883 = 36, CHIP_ID_EM2874 = 65, }; -- cgit v1.2.3 From 1a40501f3c36a45f405ee3bc6c76bb87df9c358d Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Tue, 16 Dec 2008 21:35:23 -0500 Subject: em28xx: don't assume every eb1a:2820 reference design is a Prolink PlayTV USB2 From: Devin Heitmueller Don't operate under the assumption that every device that uses the em2820 default USB ID is a Prolink PlayTV USB. We have an eeprom hash, so use that, since otherwise we cannot support other devices with the 2820 default USB ID (such as the ADS Tech Instant TV USB USBAV-704) Priority: normal Signed-off-by: Devin Heitmueller --- linux/Documentation/video4linux/CARDLIST.em28xx | 4 ++-- linux/drivers/media/video/em28xx/em28xx-cards.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'linux') diff --git a/linux/Documentation/video4linux/CARDLIST.em28xx b/linux/Documentation/video4linux/CARDLIST.em28xx index a6734eb7b..0c4c721da 100644 --- a/linux/Documentation/video4linux/CARDLIST.em28xx +++ b/linux/Documentation/video4linux/CARDLIST.em28xx @@ -1,5 +1,5 @@ 0 -> Unknown EM2800 video grabber (em2800) [eb1a:2800] - 1 -> Unknown EM2750/28xx video grabber (em2820/em2840) [eb1a:2820,eb1a:2860,eb1a:2861,eb1a:2870,eb1a:2881,eb1a:2883] + 1 -> Unknown EM2750/28xx video grabber (em2820/em2840) [eb1a:2820,eb1a:2821,eb1a:2860,eb1a:2861,eb1a:2870,eb1a:2881,eb1a:2883] 2 -> Terratec Cinergy 250 USB (em2820/em2840) [0ccd:0036] 3 -> Pinnacle PCTV USB 2 (em2820/em2840) [2304:0208] 4 -> Hauppauge WinTV USB 2 (em2820/em2840) [2040:4200,2040:4201] @@ -12,7 +12,7 @@ 11 -> Terratec Hybrid XS (em2880) [0ccd:0042] 12 -> Kworld PVR TV 2800 RF (em2820/em2840) 13 -> Terratec Prodigy XS (em2880) [0ccd:0047] - 14 -> Pixelview Prolink PlayTV USB 2.0 (em2820/em2840) [eb1a:2821] + 14 -> Pixelview Prolink PlayTV USB 2.0 (em2820/em2840) 15 -> V-Gear PocketTV (em2800) 16 -> Hauppauge WinTV HVR 950 (em2883) [2040:6513,2040:6517,2040:651b] 17 -> Pinnacle PCTV HD Pro Stick (em2880) [2304:0227] diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 3eb4408b4..7a2553c4d 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -1313,7 +1313,7 @@ struct usb_device_id em28xx_id_table [] = { { USB_DEVICE(0xeb1a, 0x2820), .driver_info = EM2820_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0x2821), - .driver_info = EM2820_BOARD_PROLINK_PLAYTV_USB2 }, + .driver_info = EM2820_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0x2860), .driver_info = EM2820_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0x2861), -- cgit v1.2.3 From 0a596528dd15515efbbf6ce078155e61610b57e9 Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Wed, 17 Dec 2008 00:25:00 -0500 Subject: xc5000: remove init_fw option From: Devin Heitmueller The init_fw option was broken for the HVR-950q because we would call the reset callback inside of dvb_attach() and the callback had not been setup yet. Michael Krufky (who added the init_fw feature) says it's no longer required, so just remove the option completely. Thanks to user Zzeiss from #linuxtv chat for reporting the issue and Michael Krufky for proposing the fix. Priority: normal Signed-off-by: Devin Heitmueller Acked-by: Michael Krufky --- linux/drivers/media/common/tuners/xc5000.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/common/tuners/xc5000.c b/linux/drivers/media/common/tuners/xc5000.c index 8cfac7002..36c81febb 100644 --- a/linux/drivers/media/common/tuners/xc5000.c +++ b/linux/drivers/media/common/tuners/xc5000.c @@ -36,10 +36,6 @@ static int debug; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); -static int xc5000_load_fw_on_attach; -module_param_named(init_fw, xc5000_load_fw_on_attach, int, 0644); -MODULE_PARM_DESC(init_fw, "Load firmware during driver initialization."); - static DEFINE_MUTEX(xc5000_list_mutex); static LIST_HEAD(hybrid_tuner_instance_list); @@ -1036,9 +1032,6 @@ struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe, memcpy(&fe->ops.tuner_ops, &xc5000_tuner_ops, sizeof(struct dvb_tuner_ops)); - if (xc5000_load_fw_on_attach) - xc5000_init(fe); - return fe; fail: mutex_unlock(&xc5000_list_mutex); -- cgit v1.2.3 From 7e4100d01b2feacf1b8b3836bc93d5f60b965ce3 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Wed, 17 Dec 2008 18:34:53 +0100 Subject: gspca - spca561: Cleanup source. From: Jean-Francois Moine Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/spca561.c | 260 ++++++++++++++---------------- 1 file changed, 117 insertions(+), 143 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/spca561.c b/linux/drivers/media/video/gspca/spca561.c index 4c0046cc7..3a27faf60 100644 --- a/linux/drivers/media/video/gspca/spca561.c +++ b/linux/drivers/media/video/gspca/spca561.c @@ -146,98 +146,7 @@ static struct v4l2_pix_format sif_072a_mode[] = { #define SPCA561_SNAPBIT 0x20 #define SPCA561_SNAPCTRL 0x40 -static void reg_w_val(struct usb_device *dev, __u16 index, __u8 value) -{ - int ret; - - ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - 0, /* request */ - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, index, NULL, 0, 500); - PDEBUG(D_USBO, "reg write: 0x%02x:0x%02x", index, value); - if (ret < 0) - PDEBUG(D_ERR, "reg write: error %d", ret); -} - -static void write_vector(struct gspca_dev *gspca_dev, - const __u16 data[][2]) -{ - struct usb_device *dev = gspca_dev->dev; - int i; - - i = 0; - while (data[i][1] != 0) { - reg_w_val(dev, data[i][1], data[i][0]); - i++; - } -} - -/* read 'len' bytes to gspca_dev->usb_buf */ -static void reg_r(struct gspca_dev *gspca_dev, - __u16 index, __u16 length) -{ - usb_control_msg(gspca_dev->dev, - usb_rcvctrlpipe(gspca_dev->dev, 0), - 0, /* request */ - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, /* value */ - index, gspca_dev->usb_buf, length, 500); -} - -static void reg_w_buf(struct gspca_dev *gspca_dev, - __u16 index, const __u8 *buffer, __u16 len) -{ - memcpy(gspca_dev->usb_buf, buffer, len); - usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - 0, /* request */ - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, /* value */ - index, gspca_dev->usb_buf, len, 500); -} - -static void i2c_write(struct gspca_dev *gspca_dev, __u16 valeur, __u16 reg) -{ - int retry = 60; - __u8 DataLow; - __u8 DataHight; - - DataLow = valeur; - DataHight = valeur >> 8; - reg_w_val(gspca_dev->dev, 0x8801, reg); - reg_w_val(gspca_dev->dev, 0x8805, DataLow); - reg_w_val(gspca_dev->dev, 0x8800, DataHight); - while (retry--) { - reg_r(gspca_dev, 0x8803, 1); - if (!gspca_dev->usb_buf[0]) - break; - } -} - -static int i2c_read(struct gspca_dev *gspca_dev, __u16 reg, __u8 mode) -{ - int retry = 60; - __u8 value; - __u8 vallsb; - - reg_w_val(gspca_dev->dev, 0x8804, 0x92); - reg_w_val(gspca_dev->dev, 0x8801, reg); - reg_w_val(gspca_dev->dev, 0x8802, (mode | 0x01)); - do { - reg_r(gspca_dev, 0x8803, 1); - if (!gspca_dev->usb_buf[0]) - break; - } while (--retry); - if (retry == 0) - return -1; - reg_r(gspca_dev, 0x8800, 1); - value = gspca_dev->usb_buf[0]; - reg_r(gspca_dev, 0x8805, 1); - vallsb = gspca_dev->usb_buf[0]; - return ((int) value << 8) | vallsb; -} - -static const __u16 spca561_init_data[][2] = { +static const __u16 rev72a_init_data[][2] = { {0x0000, 0x8114}, /* Software GPIO output data */ {0x0001, 0x8114}, /* Software GPIO output data */ {0x0000, 0x8112}, /* Some kind of reset */ @@ -324,7 +233,7 @@ static const __u16 spca561_init_data[][2] = { {0x0002, 0x865b}, /* Horizontal offset for valid pixels */ {0x0003, 0x865c}, /* Vertical offset for valid lines */ - /***************//* sensor active */ + /***************/ /* sensor active */ {0x0003, 0x8801}, /* 0x03 <- 0x01 0x21 //289 */ {0x0021, 0x8805}, {0x0001, 0x8800}, @@ -434,21 +343,6 @@ static const __u16 spca561_init_data[][2] = { {} }; -#if 0 -static void sensor_reset(struct gspca_dev *gspca_dev) -{ - reg_w_val(gspca_dev->dev, 0x8631, 0xc8); - reg_w_val(gspca_dev->dev, 0x8634, 0xc8); - reg_w_val(gspca_dev->dev, 0x8112, 0x00); - reg_w_val(gspca_dev->dev, 0x8114, 0x00); - reg_w_val(gspca_dev->dev, 0x8118, 0x21); - reg_w_val(gspca_dev->dev, 0x8804, 0x92); /* i2c init */ - reg_w_val(gspca_dev->dev, 0x8802, 0x14); - i2c_write(gspca_dev, 0x0001, 0x0d); - i2c_write(gspca_dev, 0x0000, 0x0d); -} -#endif - /******************** QC Express etch2 stuff ********************/ static const __u16 Pb100_1map8300[][2] = { /* reg, value */ @@ -529,22 +423,103 @@ static const __u16 spca561_161rev12A_data2[][2] = { {} }; -static void sensor_mapwrite(struct gspca_dev *gspca_dev, - const __u16 sensormap[][2]) +static void reg_w_val(struct usb_device *dev, __u16 index, __u8 value) { - int i = 0; - __u8 usbval[2]; + int ret; + + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, NULL, 0, 500); + PDEBUG(D_USBO, "reg write: 0x%02x:0x%02x", index, value); + if (ret < 0) + PDEBUG(D_ERR, "reg write: error %d", ret); +} + +static void write_vector(struct gspca_dev *gspca_dev, + const __u16 data[][2]) +{ + struct usb_device *dev = gspca_dev->dev; + int i; - while (sensormap[i][0]) { - usbval[0] = sensormap[i][1]; - usbval[1] = sensormap[i][1] >> 8; - reg_w_buf(gspca_dev, sensormap[i][0], usbval, 2); + i = 0; + while (data[i][1] != 0) { + reg_w_val(dev, data[i][1], data[i][0]); i++; } } + +/* read 'len' bytes to gspca_dev->usb_buf */ +static void reg_r(struct gspca_dev *gspca_dev, + __u16 index, __u16 length) +{ + usb_control_msg(gspca_dev->dev, + usb_rcvctrlpipe(gspca_dev->dev, 0), + 0, /* request */ + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, gspca_dev->usb_buf, length, 500); +} + +/* write 'len' bytes from gspca_dev->usb_buf */ +static void reg_w_buf(struct gspca_dev *gspca_dev, + __u16 index, __u16 len) +{ + usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, gspca_dev->usb_buf, len, 500); +} + +static void i2c_write(struct gspca_dev *gspca_dev, __u16 value, __u16 reg) +{ + int retry = 60; + + reg_w_val(gspca_dev->dev, 0x8801, reg); + reg_w_val(gspca_dev->dev, 0x8805, value); + reg_w_val(gspca_dev->dev, 0x8800, value >> 8); + do { + reg_r(gspca_dev, 0x8803, 1); + if (!gspca_dev->usb_buf[0]) + return; + } while (--retry); +} + +static int i2c_read(struct gspca_dev *gspca_dev, __u16 reg, __u8 mode) +{ + int retry = 60; + __u8 value; + + reg_w_val(gspca_dev->dev, 0x8804, 0x92); + reg_w_val(gspca_dev->dev, 0x8801, reg); + reg_w_val(gspca_dev->dev, 0x8802, mode | 0x01); + do { + reg_r(gspca_dev, 0x8803, 1); + if (!gspca_dev->usb_buf[0]) { + reg_r(gspca_dev, 0x8800, 1); + value = gspca_dev->usb_buf[0]; + reg_r(gspca_dev, 0x8805, 1); + return ((int) value << 8) | gspca_dev->usb_buf[0]; + } + } while (--retry); + return -1; +} + +static void sensor_mapwrite(struct gspca_dev *gspca_dev, + const __u16 (*sensormap)[2]) +{ + while ((*sensormap)[0]) { + gspca_dev->usb_buf[0] = (*sensormap)[1]; + gspca_dev->usb_buf[1] = (*sensormap)[1] >> 8; + reg_w_buf(gspca_dev, (*sensormap)[0], 2); + sensormap++; + } +} + static void init_161rev12A(struct gspca_dev *gspca_dev) { -/* sensor_reset(gspca_dev); (not in win) */ write_vector(gspca_dev, spca561_161rev12A_data1); sensor_mapwrite(gspca_dev, Pb100_1map8300); /*fixme: should be in sd_start*/ @@ -612,7 +587,7 @@ static int sd_init_12a(struct gspca_dev *gspca_dev) static int sd_init_72a(struct gspca_dev *gspca_dev) { PDEBUG(D_STREAM, "Chip revision: 072a"); - write_vector(gspca_dev, spca561_init_data); + write_vector(gspca_dev, rev72a_init_data); return 0; } @@ -635,8 +610,9 @@ static void setcontrast(struct gspca_dev *gspca_dev) static const __u8 Reg8391[] = { 0x92, 0x30, 0x20, 0x00, 0x0c, 0x00, 0x00, 0x00 }; - reg_w_buf(gspca_dev, 0x8391, Reg8391, 8); - reg_w_buf(gspca_dev, 0x8390, Reg8391, 8); + memcpy(gspca_dev->usb_buf, Reg8391, 8); + reg_w_buf(gspca_dev, 0x8391, 8); + reg_w_buf(gspca_dev, 0x8390, 8); break; } } @@ -663,7 +639,6 @@ static void setexposure(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; int expo; int clock_divider; - __u8 data[2]; /* Register 0x8309 controls exposure for the spca561, the basic exposure setting goes from 1-2047, where 1 is completely @@ -687,20 +662,19 @@ static void setexposure(struct gspca_dev *gspca_dev) clock_divider = 3; } expo |= clock_divider << 11; - data[0] = expo; - data[1] = expo >> 8; - reg_w_buf(gspca_dev, 0x8309, data, 2); + gspca_dev->usb_buf[0] = expo; + gspca_dev->usb_buf[1] = expo >> 8; + reg_w_buf(gspca_dev, 0x8309, 2); } /* rev 12a only */ static void setgain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - __u8 data[2]; - data[0] = sd->gain; - data[1] = 0; - reg_w_buf(gspca_dev, 0x8335, data, 2); + gspca_dev->usb_buf[0] = sd->gain; + gspca_dev->usb_buf[1] = 0; + reg_w_buf(gspca_dev, 0x8335, 2); } static void setautogain(struct gspca_dev *gspca_dev) @@ -730,7 +704,9 @@ static int sd_start_12a(struct gspca_dev *gspca_dev) * is sufficient to push raw frames at ~20fps */ reg_w_val(dev, 0x8500, mode); } /* -- qq@kuku.eu.org */ - reg_w_buf(gspca_dev, 0x8307, Reg8307, 2); + + memcpy(gspca_dev->usb_buf, Reg8307, sizeof Reg8307); + reg_w_buf(gspca_dev, 0x8307, sizeof Reg8307); reg_w_val(gspca_dev->dev, 0x8700, Clck); /* 0x8f 0x85 0x27 clock */ reg_w_val(gspca_dev->dev, 0x8112, 0x1e | 0x20); @@ -805,7 +781,6 @@ static void do_autogain(struct gspca_dev *gspca_dev) __u8 luma_mean = 110; __u8 luma_delta = 20; __u8 spring = 4; - __u8 reg8339[2]; if (sd->ag_cnt < 0) return; @@ -848,13 +823,13 @@ static void do_autogain(struct gspca_dev *gspca_dev) if (gainG > 0x3f) gainG = 0x3f; - else if (gainG < 4) + else if (gainG < 3) gainG = 3; i2c_write(gspca_dev, gainG, 0x35); - if (expotimes >= 0x0256) + if (expotimes > 0x0256) expotimes = 0x0256; - else if (expotimes < 4) + else if (expotimes < 3) expotimes = 3; i2c_write(gspca_dev, expotimes | pixelclk, 0x09); } @@ -862,13 +837,13 @@ static void do_autogain(struct gspca_dev *gspca_dev) case Rev012A: reg_r(gspca_dev, 0x8330, 2); if (gspca_dev->usb_buf[1] > 0x08) { - reg8339[0] = ++sd->expo12a; - reg8339[1] = 0; - reg_w_buf(gspca_dev, 0x8339, reg8339, 2); + gspca_dev->usb_buf[0] = ++sd->expo12a; + gspca_dev->usb_buf[1] = 0; + reg_w_buf(gspca_dev, 0x8339, 2); } else if (gspca_dev->usb_buf[1] < 0x02) { - reg8339[0] = --sd->expo12a; - reg8339[1] = 0; - reg_w_buf(gspca_dev, 0x8339, reg8339, 2); + gspca_dev->usb_buf[0] = --sd->expo12a; + gspca_dev->usb_buf[1] = 0; + reg_w_buf(gspca_dev, 0x8339, 2); } break; } @@ -881,8 +856,8 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, { struct sd *sd = (struct sd *) gspca_dev; - switch (data[0]) { - case 0: /* start of frame */ + switch (data[0]) { /* sequence number */ + case 0: /* start of frame */ frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, data, 0); data += SPCA561_OFFSET_DATA; @@ -904,8 +879,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, frame, data, len); } return; - case 0xff: /* drop */ -/* gspca_dev->last_packet_type = DISCARD_PACKET; */ + case 0xff: /* drop (empty mpackets) */ return; } data++; -- cgit v1.2.3 From 6a40b4fae51fd46e2d381f81d77b12696762302e Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Wed, 17 Dec 2008 18:43:05 +0100 Subject: gspca - spca561: Don't get the control values from the webcam. From: Jean-Francois Moine Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/spca561.c | 38 ------------------------------- 1 file changed, 38 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/spca561.c b/linux/drivers/media/video/gspca/spca561.c index 3a27faf60..ee24629f0 100644 --- a/linux/drivers/media/video/gspca/spca561.c +++ b/linux/drivers/media/video/gspca/spca561.c @@ -900,42 +900,6 @@ static void setbrightness(struct gspca_dev *gspca_dev) reg_w_val(gspca_dev->dev, 0x8614, value); } -static void getbrightness(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - __u16 tot; - - tot = 0; - reg_r(gspca_dev, 0x8611, 1); - tot += gspca_dev->usb_buf[0]; - reg_r(gspca_dev, 0x8612, 1); - tot += gspca_dev->usb_buf[0]; - reg_r(gspca_dev, 0x8613, 1); - tot += gspca_dev->usb_buf[0]; - reg_r(gspca_dev, 0x8614, 1); - tot += gspca_dev->usb_buf[0]; - sd->brightness = tot >> 2; -} - -/* rev72a only */ -static void getcontrast(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - __u16 tot; - - tot = 0; - reg_r(gspca_dev, 0x8651, 1); - tot += gspca_dev->usb_buf[0]; - reg_r(gspca_dev, 0x8652, 1); - tot += gspca_dev->usb_buf[0]; - reg_r(gspca_dev, 0x8653, 1); - tot += gspca_dev->usb_buf[0]; - reg_r(gspca_dev, 0x8654, 1); - tot += gspca_dev->usb_buf[0]; - sd->contrast = tot << 6; - PDEBUG(D_CONF, "get contrast %d", sd->contrast); -} - /* rev 72a only */ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) { @@ -951,7 +915,6 @@ static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; - getbrightness(gspca_dev); *val = sd->brightness; return 0; } @@ -971,7 +934,6 @@ static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; - getcontrast(gspca_dev); *val = sd->contrast; return 0; } -- cgit v1.2.3 From 8a118c204b8f52bd5b51f99be1d641360696bb21 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 18 Dec 2008 07:00:25 -0200 Subject: tuner-xc2028: allow printing stack trace as debug on sleep code From: Mauro Carvalho Chehab tuner-xc3028 can be put to sleep to save power. However, if not properly recovered from sleep, the device won't work. It is noticed that some devices doesn't recover properly, so let's add a dump_stack() as a debug option, to allow us to track when this happens. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/common/tuners/tuner-xc2028.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'linux') diff --git a/linux/drivers/media/common/tuners/tuner-xc2028.c b/linux/drivers/media/common/tuners/tuner-xc2028.c index 9536e3d3b..bd010379b 100644 --- a/linux/drivers/media/common/tuners/tuner-xc2028.c +++ b/linux/drivers/media/common/tuners/tuner-xc2028.c @@ -1111,6 +1111,10 @@ static int xc2028_sleep(struct dvb_frontend *fe) return 0; tuner_dbg("Putting xc2028/3028 into poweroff mode.\n"); + if (debug > 1) { + tuner_dbg("Printing sleep stack trace:\n"); + dump_stack(); + } mutex_lock(&priv->lock); -- cgit v1.2.3 From 6b758467e8ad1f6f860d74878d4aa59d85c558b8 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 18 Dec 2008 07:21:51 -0200 Subject: cx24116: bugfix: add missing delsys in FEC lookup From: Darron Broad Delsys was missing when searching for FEC in table. This fixes FEC lookup for DVB-S2 QPSK modulation. Priority: normal CC: Steven Toth Signed-off-by: Darron Broad Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/dvb/frontends/cx24116.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/dvb/frontends/cx24116.c b/linux/drivers/media/dvb/frontends/cx24116.c index 497ea635e..46341cfba 100644 --- a/linux/drivers/media/dvb/frontends/cx24116.c +++ b/linux/drivers/media/dvb/frontends/cx24116.c @@ -160,6 +160,7 @@ struct cx24116_tuning { fe_spectral_inversion_t inversion; fe_code_rate_t fec; + fe_delivery_system_t delsys; fe_modulation_t modulation; fe_pilot_t pilot; fe_rolloff_t rolloff; @@ -411,14 +412,15 @@ struct cx24116_modfec { }; static int cx24116_lookup_fecmod(struct cx24116_state *state, - fe_modulation_t m, fe_code_rate_t f) + fe_delivery_system_t d, fe_modulation_t m, fe_code_rate_t f) { int i, ret = -EOPNOTSUPP; dprintk("%s(0x%02x,0x%02x)\n", __func__, m, f); for (i = 0; i < ARRAY_SIZE(CX24116_MODFEC_MODES); i++) { - if ((m == CX24116_MODFEC_MODES[i].modulation) && + if ((d == CX24116_MODFEC_MODES[i].delivery_system) && + (m == CX24116_MODFEC_MODES[i].modulation) && (f == CX24116_MODFEC_MODES[i].fec)) { ret = i; break; @@ -429,13 +431,13 @@ static int cx24116_lookup_fecmod(struct cx24116_state *state, } static int cx24116_set_fec(struct cx24116_state *state, - fe_modulation_t mod, fe_code_rate_t fec) + fe_delivery_system_t delsys, fe_modulation_t mod, fe_code_rate_t fec) { int ret = 0; dprintk("%s(0x%02x,0x%02x)\n", __func__, mod, fec); - ret = cx24116_lookup_fecmod(state, mod, fec); + ret = cx24116_lookup_fecmod(state, delsys, mod, fec); if (ret < 0) return ret; @@ -1304,6 +1306,7 @@ static int cx24116_set_frontend(struct dvb_frontend *fe, __func__, c->delivery_system); return -EOPNOTSUPP; } + state->dnxt.delsys = c->delivery_system; state->dnxt.modulation = c->modulation; state->dnxt.frequency = c->frequency; state->dnxt.pilot = c->pilot; @@ -1314,7 +1317,7 @@ static int cx24116_set_frontend(struct dvb_frontend *fe, return ret; /* FEC_NONE/AUTO for DVB-S2 is not supported and detected here */ - ret = cx24116_set_fec(state, c->modulation, c->fec_inner); + ret = cx24116_set_fec(state, c->delivery_system, c->modulation, c->fec_inner); if (ret != 0) return ret; @@ -1325,6 +1328,7 @@ static int cx24116_set_frontend(struct dvb_frontend *fe, /* discard the 'current' tuning parameters and prepare to tune */ cx24116_clone_params(fe); + dprintk("%s: delsys = %d\n", __func__, state->dcur.delsys); dprintk("%s: modulation = %d\n", __func__, state->dcur.modulation); dprintk("%s: frequency = %d\n", __func__, state->dcur.frequency); dprintk("%s: pilot = %d (val = 0x%02x)\n", __func__, -- cgit v1.2.3 From afa40a80ec7ad439b805d50be5c10a90b776b6fe Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 18 Dec 2008 07:27:23 -0200 Subject: cx24116: fix retune regression introduced in 70ee86a7c630 From: Darron Broad Thanks to AKPM for spotting this. Priority: normal CC: Steven Toth Signed-off-by: Darron Broad Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/dvb/frontends/cx24116.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/dvb/frontends/cx24116.c b/linux/drivers/media/dvb/frontends/cx24116.c index 46341cfba..44818232d 100644 --- a/linux/drivers/media/dvb/frontends/cx24116.c +++ b/linux/drivers/media/dvb/frontends/cx24116.c @@ -1224,7 +1224,7 @@ static int cx24116_set_frontend(struct dvb_frontend *fe, struct dtv_frontend_properties *c = &fe->dtv_property_cache; struct cx24116_cmd cmd; fe_status_t tunerstat; - int i, status, ret, retune; + int i, status, ret, retune = 1; dprintk("%s()\n", __func__); @@ -1241,7 +1241,6 @@ static int cx24116_set_frontend(struct dvb_frontend *fe, /* Pilot doesn't exist in DVB-S, turn bit off */ state->dnxt.pilot_val = CX24116_PILOT_OFF; - retune = 1; /* DVB-S only supports 0.35 */ if (c->rolloff != ROLLOFF_35) { @@ -1269,7 +1268,7 @@ static int cx24116_set_frontend(struct dvb_frontend *fe, case PILOT_AUTO: /* Not supported but emulated */ state->dnxt.pilot_val = (c->modulation == QPSK) ? CX24116_PILOT_OFF : CX24116_PILOT_ON; - retune = 2; + retune++; break; case PILOT_OFF: state->dnxt.pilot_val = CX24116_PILOT_OFF; -- cgit v1.2.3 From bf795fdb460dc7a6726f60f46d3f934ac83e9ad3 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 18 Dec 2008 07:27:50 -0200 Subject: dvb-core: don't add an event when in ONE SHOT mode for algo type HW From: Darron Broad It has been noticed that in HW tuning mode in cx24123.c that a check is made to not alter the status var when in one shot mode, a simpler solution is to not update here for all cards. Priority: normal Signed-off-by: Darron Broad CC: Steven Toth Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/dvb/dvb-core/dvb_frontend.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c index e7adf9f47..d1a9e587c 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -591,7 +591,7 @@ restart: if (fe->ops.tune) fe->ops.tune(fe, params, fepriv->tune_mode_flags, &fepriv->delay, &s); - if (s != fepriv->status) { + if (s != fepriv->status && !(fepriv->tune_mode_flags & FE_TUNE_MODE_ONESHOT)) { dprintk("%s: state changed, adding current state\n", __func__); dvb_frontend_add_event(fe, s); fepriv->status = s; -- cgit v1.2.3 From 5f031edf628f28dd28156ee7df488107f5dc3725 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 18 Dec 2008 07:28:18 -0200 Subject: cx24116: change to ALGO_HW From: Darron Broad Slow tuning, EG: > szap-s2 -r -c 39.FTATV "1=Pgm1;Net1" reading channels from file '39.FTATV' zapping to 3 '1=Pgm1;Net1': delivery DVB-S, modulation QPSK sat 0, frequency 11140 MHz V, symbolrate 1425000, coderate 5/6, rolloff 0.35 vpid 0x0200, apid 0x0300, sid 0x0001 using '/dev/dvb/adapter0/frontend0' and '/dev/dvb/adapter0/demux0' status 00 | signal c2c0 | snr 0000 | ber 00012d4a | unc 00000000 | status 03 | signal ff80 | snr 0000 | ber 00000000 | unc 00000000 | status 01 | signal ff80 | snr 0000 | ber 00000000 | unc 00000000 | status 01 | signal ff80 | snr 0000 | ber 00000000 | unc 00000000 | status 03 | signal c040 | snr 0000 | ber 00000000 | unc 00000000 | status 03 | signal ff80 | snr 0000 | ber 00000000 | unc 00000000 | status 01 | signal ff80 | snr 0000 | ber 00000000 | unc 00000000 | status 03 | signal c040 | snr 0000 | ber 00000000 | unc 00000000 | status 01 | signal ff80 | snr 0000 | ber 00000000 | unc 00000000 | status 01 | signal ff80 | snr 0000 | ber 00000000 | unc 00000000 | status 01 | signal c040 | snr 0000 | ber 00000000 | unc 00000000 | status 01 | signal ff80 | snr 0000 | ber 00000000 | unc 00000000 | status 01 | signal ff80 | snr 0000 | ber 00000000 | unc 00000000 | status 01 | signal c040 | snr 0000 | ber 00000000 | unc 00000000 | status 03 | signal c040 | snr 0000 | ber 00000000 | unc 00000000 | status 1f | signal c040 | snr bb33 | ber 00000000 | unc 00000000 | FE_HAS_LOCK Appears to be resolved by dropping zig-zag tuning and relying on hardware only: > szap-s2 -r -c 39.FTATV "1=Pgm1;Net1" reading channels from file '39.FTATV' zapping to 3 '1=Pgm1;Net1': delivery DVB-S, modulation QPSK sat 0, frequency 11140 MHz V, symbolrate 1425000, coderate 5/6, rolloff 0.35 vpid 0x0200, apid 0x0300, sid 0x0001 using '/dev/dvb/adapter0/frontend0' and '/dev/dvb/adapter0/demux0' status 01 | signal ff80 | snr 0000 | ber 00000000 | unc 00000000 | status 1f | signal ff80 | snr c199 | ber 00000000 | unc 00000000 | FE_HAS_LOCK status 1f | signal c040 | snr c199 | ber 00000000 | unc 00000000 | FE_HAS_LOCK status 1f | signal ff80 | snr c199 | ber 00000000 | unc 00000000 | FE_HAS_LOCK Tuning to a weaker channel shows that the hardware recovers sync: status 1f | signal fe40 | snr 5199 | ber 00000000 | unc 00000000 | FE_HAS_LOCK status 03 | signal fd40 | snr 5000 | ber 00000000 | unc 00000000 | status 1f | signal fe40 | snr 5000 | ber 00000000 | unc 00000000 | FE_HAS_LOCK status 03 | signal fd80 | snr 5000 | ber 00000000 | unc 00000000 | status 01 | signal fd80 | snr 4e66 | ber 00000000 | unc 00000000 | status 1f | signal fe40 | snr 5000 | ber 00000000 | unc 00000000 | FE_HAS_LOCK status 03 | signal fe40 | snr 5000 | ber 00000000 | unc 00000000 | status 01 | signal fd80 | snr 0000 | ber 00000000 | unc 00000000 | status 1f | signal fe40 | snr 5199 | ber 00000000 | unc 00000000 | FE_HAS_LOCK Also, tuning, moving the dish and then restoring the dish also retunes. Priority: normal Signed-off-by: Darron Broad CC: Steven Toth Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/dvb/frontends/cx24116.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/dvb/frontends/cx24116.c b/linux/drivers/media/dvb/frontends/cx24116.c index 44818232d..6413c2c90 100644 --- a/linux/drivers/media/dvb/frontends/cx24116.c +++ b/linux/drivers/media/dvb/frontends/cx24116.c @@ -106,7 +106,7 @@ MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)"); #define CX24116_HAS_SYNCLOCK (0x08) #define CX24116_HAS_UNKNOWN1 (0x10) #define CX24116_HAS_UNKNOWN2 (0x20) -#define CX24116_STATUS_MASK (0x3f) +#define CX24116_STATUS_MASK (0x0f) #define CX24116_SIGNAL_MASK (0xc0) #define CX24116_DISEQC_TONEOFF (0) /* toneburst never sent */ @@ -681,7 +681,8 @@ static int cx24116_read_status(struct dvb_frontend *fe, fe_status_t *status) { struct cx24116_state *state = fe->demodulator_priv; - int lock = cx24116_readreg(state, CX24116_REG_SSTATUS); + int lock = cx24116_readreg(state, CX24116_REG_SSTATUS) & + CX24116_STATUS_MASK; dprintk("%s: status = 0x%02x\n", __func__, lock); @@ -1447,6 +1448,23 @@ tuned: /* Set/Reset B/W */ return ret; } +static int cx24116_tune(struct dvb_frontend *fe, struct dvb_frontend_parameters *params, + unsigned int mode_flags, unsigned int *delay, fe_status_t *status) +{ + *delay = HZ / 5; + if (params) { + int ret = cx24116_set_frontend(fe, params); + if (ret) + return ret; + } + return cx24116_read_status(fe, status); +} + +static int cx24116_get_algo(struct dvb_frontend *fe) +{ + return DVBFE_ALGO_HW; +} + static struct dvb_frontend_ops cx24116_ops = { .info = { @@ -1478,6 +1496,8 @@ static struct dvb_frontend_ops cx24116_ops = { .set_voltage = cx24116_set_voltage, .diseqc_send_master_cmd = cx24116_send_diseqc_msg, .diseqc_send_burst = cx24116_diseqc_send_burst, + .get_frontend_algo = cx24116_get_algo, + .tune = cx24116_tune, .set_property = cx24116_set_property, .get_property = cx24116_get_property, -- cgit v1.2.3 From f7e86b8ee653d423731cb9a1c8a081a111d3d83b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 18 Dec 2008 07:28:35 -0200 Subject: cx88: advise/acquire clean-up for HVR-1300/3000/4000 From: Darron Broad This cleans-up the advise/acquire methods. This has been tested on the hvr-1300/4000 and assumed to be correct on the hvr-3000. This update also fixes analogue tuning on the hvr-1300 when in blackbird mode. Priority: normal Signed-off-by: Darron Broad CC: Steven Toth Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/cx88/cx88-blackbird.c | 12 +++++-- linux/drivers/media/video/cx88/cx88-dvb.c | 48 +++++++++++++------------ 2 files changed, 36 insertions(+), 24 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/cx88/cx88-blackbird.c b/linux/drivers/media/video/cx88/cx88-blackbird.c index b090c6620..561dc7f0b 100644 --- a/linux/drivers/media/video/cx88/cx88-blackbird.c +++ b/linux/drivers/media/video/cx88/cx88-blackbird.c @@ -1263,8 +1263,16 @@ static int cx8802_blackbird_advise_acquire(struct cx8802_driver *drv) * We're being given access to re-arrange the GPIOs. * Take the bus off the cx22702 and put the cx23416 on it. */ - cx_clear(MO_GP0_IO, 0x00000080); /* cx22702 in reset */ - cx_set(MO_GP0_IO, 0x00000004); /* Disable the cx22702 */ + /* Toggle reset on cx22702 leaving i2c active */ + cx_set(MO_GP0_IO, 0x00000080); + udelay(1000); + cx_clear(MO_GP0_IO, 0x00000080); + udelay(50); + cx_set(MO_GP0_IO, 0x00000080); + udelay(1000); + /* tri-state the cx22702 pins */ + cx_set(MO_GP0_IO, 0x00000004); + udelay(1000); break; default: err = -ENODEV; diff --git a/linux/drivers/media/video/cx88/cx88-dvb.c b/linux/drivers/media/video/cx88/cx88-dvb.c index 5a9fafd25..fc3a080ac 100644 --- a/linux/drivers/media/video/cx88/cx88-dvb.c +++ b/linux/drivers/media/video/cx88/cx88-dvb.c @@ -1135,40 +1135,44 @@ static int cx8802_dvb_advise_acquire(struct cx8802_driver *drv) * on the bus. Take the bus from the cx23416 and enable the * cx22702 demod */ - cx_set(MO_GP0_IO, 0x00000080); /* cx22702 out of reset and enable */ + /* Toggle reset on cx22702 leaving i2c active */ + cx_set(MO_GP0_IO, 0x00000080); + udelay(1000); + cx_clear(MO_GP0_IO, 0x00000080); + udelay(50); + cx_set(MO_GP0_IO, 0x00000080); + udelay(1000); + /* enable the cx22702 pins */ cx_clear(MO_GP0_IO, 0x00000004); udelay(1000); break; case CX88_BOARD_HAUPPAUGE_HVR3000: case CX88_BOARD_HAUPPAUGE_HVR4000: - if(core->dvbdev->frontends.active_fe_id == 1) { - /* DVB-S/S2 Enabled */ - - /* Toggle reset on cx22702 leaving i2c active */ - cx_write(MO_GP0_IO, (core->board.input[0].gpio0 & 0x0000ff00) | 0x00000080); - udelay(1000); - cx_clear(MO_GP0_IO, 0x00000080); - udelay(50); - cx_set(MO_GP0_IO, 0x00000080); /* cx22702 out of reset */ - cx_set(MO_GP0_IO, 0x00000004); /* tri-state the cx22702 pins */ - udelay(1000); - - cx_write(MO_SRST_IO, 1); /* Take the cx24116/cx24123 out of reset */ + /* Toggle reset on cx22702 leaving i2c active */ + cx_set(MO_GP0_IO, 0x00000080); + udelay(1000); + cx_clear(MO_GP0_IO, 0x00000080); + udelay(50); + cx_set(MO_GP0_IO, 0x00000080); + udelay(1000); + switch (core->dvbdev->frontends.active_fe_id) { + case 1: /* DVB-S/S2 Enabled */ + /* tri-state the cx22702 pins */ + cx_set(MO_GP0_IO, 0x00000004); + /* Take the cx24116/cx24123 out of reset */ + cx_write(MO_SRST_IO, 1); core->dvbdev->ts_gen_cntrl = 0x02; /* Parallel IO */ - } else - if (core->dvbdev->frontends.active_fe_id == 2) { - /* DVB-T Enabled */ - + break; + case 2: /* DVB-T Enabled */ /* Put the cx24116/cx24123 into reset */ cx_write(MO_SRST_IO, 0); - - /* cx22702 out of reset and enable it */ - cx_set(MO_GP0_IO, 0x00000080); + /* enable the cx22702 pins */ cx_clear(MO_GP0_IO, 0x00000004); core->dvbdev->ts_gen_cntrl = 0x0c; /* Serial IO */ - udelay(1000); + break; } + udelay(1000); break; default: -- cgit v1.2.3 From 9197c4fd6177feb18b8102adb92d8c465687d443 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 18 Dec 2008 15:16:24 +0100 Subject: v4l2-subdev: add g_sliced_vbi_cap and add NULL pointer checks From: Hans Verkuil Priority: normal Signed-off-by: Hans Verkuil --- linux/drivers/media/video/v4l2-subdev.c | 10 ++++++---- linux/include/media/v4l2-subdev.h | 1 + 2 files changed, 7 insertions(+), 4 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/v4l2-subdev.c b/linux/drivers/media/video/v4l2-subdev.c index fe1f01c97..e3612f29d 100644 --- a/linux/drivers/media/video/v4l2-subdev.c +++ b/linux/drivers/media/video/v4l2-subdev.c @@ -40,13 +40,13 @@ int v4l2_subdev_command(struct v4l2_subdev *sd, unsigned cmd, void *arg) case VIDIOC_G_CHIP_IDENT: return v4l2_subdev_call(sd, core, g_chip_ident, arg); case VIDIOC_INT_S_STANDBY: - return v4l2_subdev_call(sd, core, s_standby, *(u32 *)arg); + return v4l2_subdev_call(sd, core, s_standby, arg ? (*(u32 *)arg) : 0); case VIDIOC_INT_RESET: - return v4l2_subdev_call(sd, core, reset, *(u32 *)arg); + return v4l2_subdev_call(sd, core, reset, arg ? (*(u32 *)arg) : 0); case VIDIOC_INT_S_GPIO: - return v4l2_subdev_call(sd, core, s_gpio, *(u32 *)arg); + return v4l2_subdev_call(sd, core, s_gpio, arg ? (*(u32 *)arg) : 0); case VIDIOC_INT_INIT: - return v4l2_subdev_call(sd, core, init, *(u32 *)arg); + return v4l2_subdev_call(sd, core, init, arg ? (*(u32 *)arg) : 0); #ifdef CONFIG_VIDEO_ADV_DEBUG case VIDIOC_DBG_G_REGISTER: return v4l2_subdev_call(sd, core, g_register, arg); @@ -90,6 +90,8 @@ int v4l2_subdev_command(struct v4l2_subdev *sd, unsigned cmd, void *arg) return v4l2_subdev_call(sd, video, s_vbi_data, arg); case VIDIOC_INT_G_VBI_DATA: return v4l2_subdev_call(sd, video, g_vbi_data, arg); + case VIDIOC_G_SLICED_VBI_CAP: + return v4l2_subdev_call(sd, video, g_sliced_vbi_cap, arg); case VIDIOC_S_FMT: return v4l2_subdev_call(sd, video, s_fmt, arg); case VIDIOC_G_FMT: diff --git a/linux/include/media/v4l2-subdev.h b/linux/include/media/v4l2-subdev.h index bc9e0fbf2..bca25e8ea 100644 --- a/linux/include/media/v4l2-subdev.h +++ b/linux/include/media/v4l2-subdev.h @@ -110,6 +110,7 @@ struct v4l2_subdev_video_ops { int (*decode_vbi_line)(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi_line); int (*s_vbi_data)(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *vbi_data); int (*g_vbi_data)(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_data *vbi_data); + int (*g_sliced_vbi_cap)(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_cap *cap); int (*s_std_output)(struct v4l2_subdev *sd, v4l2_std_id std); int (*s_stream)(struct v4l2_subdev *sd, int enable); int (*s_fmt)(struct v4l2_subdev *sd, struct v4l2_format *fmt); -- cgit v1.2.3 From 9371b503a9b26eda909aa27f396e0cfce9eea569 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 18 Dec 2008 15:17:25 +0100 Subject: tvp5150: convert to v4l2_subdev. From: Hans Verkuil Priority: normal Signed-off-by: Hans Verkuil --- linux/drivers/media/video/tvp5150.c | 933 +++++++++++++++++------------------- 1 file changed, 436 insertions(+), 497 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/tvp5150.c b/linux/drivers/media/video/tvp5150.c index ac8205d87..37421fb03 100644 --- a/linux/drivers/media/video/tvp5150.c +++ b/linux/drivers/media/video/tvp5150.c @@ -10,8 +10,9 @@ #include #include #include -#include +#include #include +#include #include "tvp5150_reg.h" @@ -30,21 +31,7 @@ I2C_CLIENT_INSMOD; static int debug; module_param(debug, int, 0); -MODULE_PARM_DESC(debug, "Debug level (0-1)"); - -#define tvp5150_err(fmt, arg...) do { \ - printk(KERN_ERR "%s %d-%04x: " fmt, c->driver->driver.name, \ - i2c_adapter_id(c->adapter), c->addr , ## arg); } while (0) -#define tvp5150_info(fmt, arg...) do { \ - printk(KERN_INFO "%s %d-%04x: " fmt, c->driver->driver.name, \ - i2c_adapter_id(c->adapter), c->addr , ## arg); } while (0) -#define tvp5150_dbg(num, fmt, arg...) \ - do { \ - if (debug >= num) \ - printk(KERN_DEBUG "%s debug %d-%04x: " fmt,\ - c->driver->driver.name, \ - i2c_adapter_id(c->adapter), \ - c->addr , ## arg); } while (0) +MODULE_PARM_DESC(debug, "Debug level (0-2)"); /* supported controls */ static struct v4l2_queryctrl tvp5150_qctrl[] = { @@ -88,7 +75,7 @@ static struct v4l2_queryctrl tvp5150_qctrl[] = { }; struct tvp5150 { - struct i2c_client *client; + struct v4l2_subdev sd; v4l2_std_id norm; /* Current set standard */ struct v4l2_routing route; @@ -99,49 +86,57 @@ struct tvp5150 { int sat; }; -static int tvp5150_read(struct i2c_client *c, unsigned char addr) +static inline struct tvp5150 *to_tvp5150(struct v4l2_subdev *sd) { + return container_of(sd, struct tvp5150, sd); +} + +static int tvp5150_read(struct v4l2_subdev *sd, unsigned char addr) +{ + struct i2c_client *c = v4l2_get_subdevdata(sd); unsigned char buffer[1]; int rc; buffer[0] = addr; if (1 != (rc = i2c_master_send(c, buffer, 1))) - tvp5150_dbg(0, "i2c i/o error: rc == %d (should be 1)\n", rc); + v4l2_dbg(0, debug, sd, "i2c i/o error: rc == %d (should be 1)\n", rc); msleep(10); if (1 != (rc = i2c_master_recv(c, buffer, 1))) - tvp5150_dbg(0, "i2c i/o error: rc == %d (should be 1)\n", rc); + v4l2_dbg(0, debug, sd, "i2c i/o error: rc == %d (should be 1)\n", rc); - tvp5150_dbg(2, "tvp5150: read 0x%02x = 0x%02x\n", addr, buffer[0]); + v4l2_dbg(2, debug, sd, "tvp5150: read 0x%02x = 0x%02x\n", addr, buffer[0]); return (buffer[0]); } -static inline void tvp5150_write(struct i2c_client *c, unsigned char addr, +static inline void tvp5150_write(struct v4l2_subdev *sd, unsigned char addr, unsigned char value) { + struct i2c_client *c = v4l2_get_subdevdata(sd); unsigned char buffer[2]; int rc; buffer[0] = addr; buffer[1] = value; - tvp5150_dbg(2, "tvp5150: writing 0x%02x 0x%02x\n", buffer[0], buffer[1]); + v4l2_dbg(2, debug, sd, "tvp5150: writing 0x%02x 0x%02x\n", buffer[0], buffer[1]); if (2 != (rc = i2c_master_send(c, buffer, 2))) - tvp5150_dbg(0, "i2c i/o error: rc == %d (should be 2)\n", rc); + v4l2_dbg(0, debug, sd, "i2c i/o error: rc == %d (should be 2)\n", rc); } -static void dump_reg_range(struct i2c_client *c, char *s, u8 init, const u8 end,int max_line) +static void dump_reg_range(struct v4l2_subdev *sd, char *s, u8 init, + const u8 end, int max_line) { - int i=0; + int i = 0; - while (init!=(u8)(end+1)) { - if ((i%max_line) == 0) { - if (i>0) + while (init != (u8)(end + 1)) { + if ((i % max_line) == 0) { + if (i > 0) printk("\n"); - printk("tvp5150: %s reg 0x%02x = ",s,init); + printk("tvp5150: %s reg 0x%02x = ", s, init); } - printk("%02x ",tvp5150_read(c, init)); + printk("%02x ", tvp5150_read(sd, init)); init++; i++; @@ -149,151 +144,152 @@ static void dump_reg_range(struct i2c_client *c, char *s, u8 init, const u8 end, printk("\n"); } -static void dump_reg(struct i2c_client *c) +static int tvp5150_log_status(struct v4l2_subdev *sd) { printk("tvp5150: Video input source selection #1 = 0x%02x\n", - tvp5150_read(c, TVP5150_VD_IN_SRC_SEL_1)); + tvp5150_read(sd, TVP5150_VD_IN_SRC_SEL_1)); printk("tvp5150: Analog channel controls = 0x%02x\n", - tvp5150_read(c, TVP5150_ANAL_CHL_CTL)); + tvp5150_read(sd, TVP5150_ANAL_CHL_CTL)); printk("tvp5150: Operation mode controls = 0x%02x\n", - tvp5150_read(c, TVP5150_OP_MODE_CTL)); + tvp5150_read(sd, TVP5150_OP_MODE_CTL)); printk("tvp5150: Miscellaneous controls = 0x%02x\n", - tvp5150_read(c, TVP5150_MISC_CTL)); + tvp5150_read(sd, TVP5150_MISC_CTL)); printk("tvp5150: Autoswitch mask= 0x%02x\n", - tvp5150_read(c, TVP5150_AUTOSW_MSK)); + tvp5150_read(sd, TVP5150_AUTOSW_MSK)); printk("tvp5150: Color killer threshold control = 0x%02x\n", - tvp5150_read(c, TVP5150_COLOR_KIL_THSH_CTL)); + tvp5150_read(sd, TVP5150_COLOR_KIL_THSH_CTL)); printk("tvp5150: Luminance processing controls #1 #2 and #3 = %02x %02x %02x\n", - tvp5150_read(c, TVP5150_LUMA_PROC_CTL_1), - tvp5150_read(c, TVP5150_LUMA_PROC_CTL_2), - tvp5150_read(c, TVP5150_LUMA_PROC_CTL_3)); + tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_1), + tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_2), + tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_3)); printk("tvp5150: Brightness control = 0x%02x\n", - tvp5150_read(c, TVP5150_BRIGHT_CTL)); + tvp5150_read(sd, TVP5150_BRIGHT_CTL)); printk("tvp5150: Color saturation control = 0x%02x\n", - tvp5150_read(c, TVP5150_SATURATION_CTL)); + tvp5150_read(sd, TVP5150_SATURATION_CTL)); printk("tvp5150: Hue control = 0x%02x\n", - tvp5150_read(c, TVP5150_HUE_CTL)); + tvp5150_read(sd, TVP5150_HUE_CTL)); printk("tvp5150: Contrast control = 0x%02x\n", - tvp5150_read(c, TVP5150_CONTRAST_CTL)); + tvp5150_read(sd, TVP5150_CONTRAST_CTL)); printk("tvp5150: Outputs and data rates select = 0x%02x\n", - tvp5150_read(c, TVP5150_DATA_RATE_SEL)); + tvp5150_read(sd, TVP5150_DATA_RATE_SEL)); printk("tvp5150: Configuration shared pins = 0x%02x\n", - tvp5150_read(c, TVP5150_CONF_SHARED_PIN)); + tvp5150_read(sd, TVP5150_CONF_SHARED_PIN)); printk("tvp5150: Active video cropping start = 0x%02x%02x\n", - tvp5150_read(c, TVP5150_ACT_VD_CROP_ST_MSB), - tvp5150_read(c, TVP5150_ACT_VD_CROP_ST_LSB)); + tvp5150_read(sd, TVP5150_ACT_VD_CROP_ST_MSB), + tvp5150_read(sd, TVP5150_ACT_VD_CROP_ST_LSB)); printk("tvp5150: Active video cropping stop = 0x%02x%02x\n", - tvp5150_read(c, TVP5150_ACT_VD_CROP_STP_MSB), - tvp5150_read(c, TVP5150_ACT_VD_CROP_STP_LSB)); + tvp5150_read(sd, TVP5150_ACT_VD_CROP_STP_MSB), + tvp5150_read(sd, TVP5150_ACT_VD_CROP_STP_LSB)); printk("tvp5150: Genlock/RTC = 0x%02x\n", - tvp5150_read(c, TVP5150_GENLOCK)); + tvp5150_read(sd, TVP5150_GENLOCK)); printk("tvp5150: Horizontal sync start = 0x%02x\n", - tvp5150_read(c, TVP5150_HORIZ_SYNC_START)); + tvp5150_read(sd, TVP5150_HORIZ_SYNC_START)); printk("tvp5150: Vertical blanking start = 0x%02x\n", - tvp5150_read(c, TVP5150_VERT_BLANKING_START)); + tvp5150_read(sd, TVP5150_VERT_BLANKING_START)); printk("tvp5150: Vertical blanking stop = 0x%02x\n", - tvp5150_read(c, TVP5150_VERT_BLANKING_STOP)); + tvp5150_read(sd, TVP5150_VERT_BLANKING_STOP)); printk("tvp5150: Chrominance processing control #1 and #2 = %02x %02x\n", - tvp5150_read(c, TVP5150_CHROMA_PROC_CTL_1), - tvp5150_read(c, TVP5150_CHROMA_PROC_CTL_2)); + tvp5150_read(sd, TVP5150_CHROMA_PROC_CTL_1), + tvp5150_read(sd, TVP5150_CHROMA_PROC_CTL_2)); printk("tvp5150: Interrupt reset register B = 0x%02x\n", - tvp5150_read(c, TVP5150_INT_RESET_REG_B)); + tvp5150_read(sd, TVP5150_INT_RESET_REG_B)); printk("tvp5150: Interrupt enable register B = 0x%02x\n", - tvp5150_read(c, TVP5150_INT_ENABLE_REG_B)); + tvp5150_read(sd, TVP5150_INT_ENABLE_REG_B)); printk("tvp5150: Interrupt configuration register B = 0x%02x\n", - tvp5150_read(c, TVP5150_INTT_CONFIG_REG_B)); + tvp5150_read(sd, TVP5150_INTT_CONFIG_REG_B)); printk("tvp5150: Video standard = 0x%02x\n", - tvp5150_read(c, TVP5150_VIDEO_STD)); + tvp5150_read(sd, TVP5150_VIDEO_STD)); printk("tvp5150: Chroma gain factor: Cb=0x%02x Cr=0x%02x\n", - tvp5150_read(c, TVP5150_CB_GAIN_FACT), - tvp5150_read(c, TVP5150_CR_GAIN_FACTOR)); + tvp5150_read(sd, TVP5150_CB_GAIN_FACT), + tvp5150_read(sd, TVP5150_CR_GAIN_FACTOR)); printk("tvp5150: Macrovision on counter = 0x%02x\n", - tvp5150_read(c, TVP5150_MACROVISION_ON_CTR)); + tvp5150_read(sd, TVP5150_MACROVISION_ON_CTR)); printk("tvp5150: Macrovision off counter = 0x%02x\n", - tvp5150_read(c, TVP5150_MACROVISION_OFF_CTR)); + tvp5150_read(sd, TVP5150_MACROVISION_OFF_CTR)); printk("tvp5150: ITU-R BT.656.%d timing(TVP5150AM1 only)\n", - (tvp5150_read(c, TVP5150_REV_SELECT)&1)?3:4); + (tvp5150_read(sd, TVP5150_REV_SELECT) & 1) ? 3 : 4); printk("tvp5150: Device ID = %02x%02x\n", - tvp5150_read(c, TVP5150_MSB_DEV_ID), - tvp5150_read(c, TVP5150_LSB_DEV_ID)); + tvp5150_read(sd, TVP5150_MSB_DEV_ID), + tvp5150_read(sd, TVP5150_LSB_DEV_ID)); printk("tvp5150: ROM version = (hex) %02x.%02x\n", - tvp5150_read(c, TVP5150_ROM_MAJOR_VER), - tvp5150_read(c, TVP5150_ROM_MINOR_VER)); + tvp5150_read(sd, TVP5150_ROM_MAJOR_VER), + tvp5150_read(sd, TVP5150_ROM_MINOR_VER)); printk("tvp5150: Vertical line count = 0x%02x%02x\n", - tvp5150_read(c, TVP5150_VERT_LN_COUNT_MSB), - tvp5150_read(c, TVP5150_VERT_LN_COUNT_LSB)); + tvp5150_read(sd, TVP5150_VERT_LN_COUNT_MSB), + tvp5150_read(sd, TVP5150_VERT_LN_COUNT_LSB)); printk("tvp5150: Interrupt status register B = 0x%02x\n", - tvp5150_read(c, TVP5150_INT_STATUS_REG_B)); + tvp5150_read(sd, TVP5150_INT_STATUS_REG_B)); printk("tvp5150: Interrupt active register B = 0x%02x\n", - tvp5150_read(c, TVP5150_INT_ACTIVE_REG_B)); + tvp5150_read(sd, TVP5150_INT_ACTIVE_REG_B)); printk("tvp5150: Status regs #1 to #5 = %02x %02x %02x %02x %02x\n", - tvp5150_read(c, TVP5150_STATUS_REG_1), - tvp5150_read(c, TVP5150_STATUS_REG_2), - tvp5150_read(c, TVP5150_STATUS_REG_3), - tvp5150_read(c, TVP5150_STATUS_REG_4), - tvp5150_read(c, TVP5150_STATUS_REG_5)); + tvp5150_read(sd, TVP5150_STATUS_REG_1), + tvp5150_read(sd, TVP5150_STATUS_REG_2), + tvp5150_read(sd, TVP5150_STATUS_REG_3), + tvp5150_read(sd, TVP5150_STATUS_REG_4), + tvp5150_read(sd, TVP5150_STATUS_REG_5)); #if 0 /* This will pop a value from vbi reg */ printk("tvp5150: VBI FIFO read data = 0x%02x\n", - tvp5150_read(c, TVP5150_VBI_FIFO_READ_DATA)); + tvp5150_read(sd, TVP5150_VBI_FIFO_READ_DATA)); #endif - dump_reg_range(c,"Teletext filter 1", TVP5150_TELETEXT_FIL1_INI, - TVP5150_TELETEXT_FIL1_END,8); - dump_reg_range(c,"Teletext filter 2", TVP5150_TELETEXT_FIL2_INI, - TVP5150_TELETEXT_FIL2_END,8); + dump_reg_range(sd, "Teletext filter 1", TVP5150_TELETEXT_FIL1_INI, + TVP5150_TELETEXT_FIL1_END, 8); + dump_reg_range(sd, "Teletext filter 2", TVP5150_TELETEXT_FIL2_INI, + TVP5150_TELETEXT_FIL2_END, 8); printk("tvp5150: Teletext filter enable = 0x%02x\n", - tvp5150_read(c, TVP5150_TELETEXT_FIL_ENA)); + tvp5150_read(sd, TVP5150_TELETEXT_FIL_ENA)); printk("tvp5150: Interrupt status register A = 0x%02x\n", - tvp5150_read(c, TVP5150_INT_STATUS_REG_A)); + tvp5150_read(sd, TVP5150_INT_STATUS_REG_A)); printk("tvp5150: Interrupt enable register A = 0x%02x\n", - tvp5150_read(c, TVP5150_INT_ENABLE_REG_A)); + tvp5150_read(sd, TVP5150_INT_ENABLE_REG_A)); printk("tvp5150: Interrupt configuration = 0x%02x\n", - tvp5150_read(c, TVP5150_INT_CONF)); + tvp5150_read(sd, TVP5150_INT_CONF)); printk("tvp5150: VDP status register = 0x%02x\n", - tvp5150_read(c, TVP5150_VDP_STATUS_REG)); + tvp5150_read(sd, TVP5150_VDP_STATUS_REG)); printk("tvp5150: FIFO word count = 0x%02x\n", - tvp5150_read(c, TVP5150_FIFO_WORD_COUNT)); + tvp5150_read(sd, TVP5150_FIFO_WORD_COUNT)); printk("tvp5150: FIFO interrupt threshold = 0x%02x\n", - tvp5150_read(c, TVP5150_FIFO_INT_THRESHOLD)); + tvp5150_read(sd, TVP5150_FIFO_INT_THRESHOLD)); printk("tvp5150: FIFO reset = 0x%02x\n", - tvp5150_read(c, TVP5150_FIFO_RESET)); + tvp5150_read(sd, TVP5150_FIFO_RESET)); printk("tvp5150: Line number interrupt = 0x%02x\n", - tvp5150_read(c, TVP5150_LINE_NUMBER_INT)); + tvp5150_read(sd, TVP5150_LINE_NUMBER_INT)); printk("tvp5150: Pixel alignment register = 0x%02x%02x\n", - tvp5150_read(c, TVP5150_PIX_ALIGN_REG_HIGH), - tvp5150_read(c, TVP5150_PIX_ALIGN_REG_LOW)); + tvp5150_read(sd, TVP5150_PIX_ALIGN_REG_HIGH), + tvp5150_read(sd, TVP5150_PIX_ALIGN_REG_LOW)); printk("tvp5150: FIFO output control = 0x%02x\n", - tvp5150_read(c, TVP5150_FIFO_OUT_CTRL)); + tvp5150_read(sd, TVP5150_FIFO_OUT_CTRL)); printk("tvp5150: Full field enable = 0x%02x\n", - tvp5150_read(c, TVP5150_FULL_FIELD_ENA)); + tvp5150_read(sd, TVP5150_FULL_FIELD_ENA)); printk("tvp5150: Full field mode register = 0x%02x\n", - tvp5150_read(c, TVP5150_FULL_FIELD_MODE_REG)); + tvp5150_read(sd, TVP5150_FULL_FIELD_MODE_REG)); - dump_reg_range(c,"CC data", TVP5150_CC_DATA_INI, - TVP5150_CC_DATA_END,8); + dump_reg_range(sd, "CC data", TVP5150_CC_DATA_INI, + TVP5150_CC_DATA_END, 8); - dump_reg_range(c,"WSS data", TVP5150_WSS_DATA_INI, - TVP5150_WSS_DATA_END,8); + dump_reg_range(sd, "WSS data", TVP5150_WSS_DATA_INI, + TVP5150_WSS_DATA_END, 8); - dump_reg_range(c,"VPS data", TVP5150_VPS_DATA_INI, - TVP5150_VPS_DATA_END,8); + dump_reg_range(sd, "VPS data", TVP5150_VPS_DATA_INI, + TVP5150_VPS_DATA_END, 8); - dump_reg_range(c,"VITC data", TVP5150_VITC_DATA_INI, - TVP5150_VITC_DATA_END,10); + dump_reg_range(sd, "VITC data", TVP5150_VITC_DATA_INI, + TVP5150_VITC_DATA_END, 10); - dump_reg_range(c,"Line mode", TVP5150_LINE_MODE_INI, - TVP5150_LINE_MODE_END,8); + dump_reg_range(sd, "Line mode", TVP5150_LINE_MODE_INI, + TVP5150_LINE_MODE_END, 8); + return 0; } /**************************************************************************** Basic functions ****************************************************************************/ -static inline void tvp5150_selmux(struct i2c_client *c) +static inline void tvp5150_selmux(struct v4l2_subdev *sd) { int opmode=0; - struct tvp5150 *decoder = i2c_get_clientdata(c); + struct tvp5150 *decoder = to_tvp5150(sd); int input = 0; unsigned char val; @@ -314,23 +310,23 @@ static inline void tvp5150_selmux(struct i2c_client *c) break; } - tvp5150_dbg( 1, "Selecting video route: route input=%i, output=%i " + v4l2_dbg(1, debug, sd, "Selecting video route: route input=%i, output=%i " "=> tvp5150 input=%i, opmode=%i\n", decoder->route.input,decoder->route.output, input, opmode ); - tvp5150_write(c, TVP5150_OP_MODE_CTL, opmode); - tvp5150_write(c, TVP5150_VD_IN_SRC_SEL_1, input); + tvp5150_write(sd, TVP5150_OP_MODE_CTL, opmode); + tvp5150_write(sd, TVP5150_VD_IN_SRC_SEL_1, input); /* Svideo should enable YCrCb output and disable GPCL output * For Composite and TV, it should be the reverse */ - val = tvp5150_read(c, TVP5150_MISC_CTL); + val = tvp5150_read(sd, TVP5150_MISC_CTL); if (decoder->route.input == TVP5150_SVIDEO) val = (val & ~0x40) | 0x10; else val = (val & ~0x10) | 0x40; - tvp5150_write(c, TVP5150_MISC_CTL, val); + tvp5150_write(sd, TVP5150_MISC_CTL, val); }; struct i2c_reg_value { @@ -605,35 +601,35 @@ static struct i2c_vbi_ram_value vbi_ram_default[] = { (u16)-1 } }; -static int tvp5150_write_inittab(struct i2c_client *c, +static int tvp5150_write_inittab(struct v4l2_subdev *sd, const struct i2c_reg_value *regs) { while (regs->reg != 0xff) { - tvp5150_write(c, regs->reg, regs->value); + tvp5150_write(sd, regs->reg, regs->value); regs++; } return 0; } -static int tvp5150_vdp_init(struct i2c_client *c, +static int tvp5150_vdp_init(struct v4l2_subdev *sd, const struct i2c_vbi_ram_value *regs) { unsigned int i; /* Disable Full Field */ - tvp5150_write(c, TVP5150_FULL_FIELD_ENA, 0); + tvp5150_write(sd, TVP5150_FULL_FIELD_ENA, 0); /* Before programming, Line mode should be at 0xff */ - for (i=TVP5150_LINE_MODE_INI; i<=TVP5150_LINE_MODE_END; i++) - tvp5150_write(c, i, 0xff); + for (i = TVP5150_LINE_MODE_INI; i <= TVP5150_LINE_MODE_END; i++) + tvp5150_write(sd, i, 0xff); /* Load Ram Table */ - while (regs->reg != (u16)-1 ) { - tvp5150_write(c, TVP5150_CONF_RAM_ADDR_HIGH,regs->reg>>8); - tvp5150_write(c, TVP5150_CONF_RAM_ADDR_LOW,regs->reg); + while (regs->reg != (u16)-1) { + tvp5150_write(sd, TVP5150_CONF_RAM_ADDR_HIGH, regs->reg >> 8); + tvp5150_write(sd, TVP5150_CONF_RAM_ADDR_LOW, regs->reg); - for (i=0;i<16;i++) - tvp5150_write(c, TVP5150_VDP_CONF_RAM_DATA,regs->values[i]); + for (i = 0; i < 16; i++) + tvp5150_write(sd, TVP5150_VDP_CONF_RAM_DATA, regs->values[i]); regs++; } @@ -641,11 +637,13 @@ static int tvp5150_vdp_init(struct i2c_client *c, } /* Fills VBI capabilities based on i2c_vbi_ram_value struct */ -static void tvp5150_vbi_get_cap(const struct i2c_vbi_ram_value *regs, +static int tvp5150_g_sliced_vbi_cap(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_cap *cap) { + const struct i2c_vbi_ram_value *regs = vbi_ram_default; int line; + v4l2_dbg(1, debug, sd, "VIDIOC_G_SLICED_VBI_CAP\n"); memset(cap, 0, sizeof *cap); while (regs->reg != (u16)-1 ) { @@ -656,6 +654,7 @@ static void tvp5150_vbi_get_cap(const struct i2c_vbi_ram_value *regs, regs++; } + return 0; } /* Set vbi processing @@ -671,18 +670,18 @@ static void tvp5150_vbi_get_cap(const struct i2c_vbi_ram_value *regs, * LSB = field1 * MSB = field2 */ -static int tvp5150_set_vbi(struct i2c_client *c, +static int tvp5150_set_vbi(struct v4l2_subdev *sd, const struct i2c_vbi_ram_value *regs, unsigned int type,u8 flags, int line, const int fields) { - struct tvp5150 *decoder = i2c_get_clientdata(c); - v4l2_std_id std=decoder->norm; + struct tvp5150 *decoder = to_tvp5150(sd); + v4l2_std_id std = decoder->norm; u8 reg; int pos=0; if (std == V4L2_STD_ALL) { - tvp5150_err("VBI can't be configured without knowing number of lines\n"); + v4l2_err(sd, "VBI can't be configured without knowing number of lines\n"); return 0; } else if (std & V4L2_STD_625_50) { /* Don't follow NTSC Line number convension */ @@ -710,174 +709,186 @@ static int tvp5150_set_vbi(struct i2c_client *c, reg=((line-6)<<1)+TVP5150_LINE_MODE_INI; if (fields&1) { - tvp5150_write(c, reg, type); + tvp5150_write(sd, reg, type); } if (fields&2) { - tvp5150_write(c, reg+1, type); + tvp5150_write(sd, reg+1, type); } return type; } -static int tvp5150_get_vbi(struct i2c_client *c, +static int tvp5150_get_vbi(struct v4l2_subdev *sd, const struct i2c_vbi_ram_value *regs, int line) { - struct tvp5150 *decoder = i2c_get_clientdata(c); - v4l2_std_id std=decoder->norm; + struct tvp5150 *decoder = to_tvp5150(sd); + v4l2_std_id std = decoder->norm; u8 reg; - int pos, type=0; + int pos, type = 0; if (std == V4L2_STD_ALL) { - tvp5150_err("VBI can't be configured without knowing number of lines\n"); + v4l2_err(sd, "VBI can't be configured without knowing number of lines\n"); return 0; } else if (std & V4L2_STD_625_50) { /* Don't follow NTSC Line number convension */ line += 3; } - if (line<6||line>27) + if (line < 6 || line > 27) return 0; - reg=((line-6)<<1)+TVP5150_LINE_MODE_INI; + reg = ((line - 6) << 1) + TVP5150_LINE_MODE_INI; - pos=tvp5150_read(c, reg)&0x0f; - if (pos<0x0f) - type=regs[pos].type.vbi_type; + pos = tvp5150_read(sd, reg) & 0x0f; + if (pos < 0x0f) + type = regs[pos].type.vbi_type; - pos=tvp5150_read(c, reg+1)&0x0f; - if (pos<0x0f) - type|=regs[pos].type.vbi_type; + pos = tvp5150_read(sd, reg + 1) & 0x0f; + if (pos < 0x0f) + type |= regs[pos].type.vbi_type; return type; } -#if 0 -static int decode_vbi_data(struct i2c_client *c, vbi) -{ - count=tvp5150_read(c, TVP5150_FIFO_WORD_COUNT); - for (i=0;inorm=std; + decoder->norm = std; /* First tests should be against specific std */ if (std == V4L2_STD_ALL) { - fmt=0; /* Autodetect mode */ + fmt = 0; /* Autodetect mode */ } else if (std & V4L2_STD_NTSC_443) { - fmt=0xa; + fmt = 0xa; } else if (std & V4L2_STD_PAL_M) { - fmt=0x6; - } else if (std & (V4L2_STD_PAL_N| V4L2_STD_PAL_Nc)) { - fmt=0x8; + fmt = 0x6; + } else if (std & (V4L2_STD_PAL_N | V4L2_STD_PAL_Nc)) { + fmt = 0x8; } else { /* Then, test against generic ones */ - if (std & V4L2_STD_NTSC) { - fmt=0x2; - } else if (std & V4L2_STD_PAL) { - fmt=0x4; - } else if (std & V4L2_STD_SECAM) { - fmt=0xc; - } + if (std & V4L2_STD_NTSC) + fmt = 0x2; + else if (std & V4L2_STD_PAL) + fmt = 0x4; + else if (std & V4L2_STD_SECAM) + fmt = 0xc; } - tvp5150_dbg(1,"Set video std register to %d.\n",fmt); - tvp5150_write(c, TVP5150_VIDEO_STD, fmt); - + v4l2_dbg(1, debug, sd, "Set video std register to %d.\n", fmt); + tvp5150_write(sd, TVP5150_VIDEO_STD, fmt); return 0; } -static inline void tvp5150_reset(struct i2c_client *c) +static int tvp5150_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct tvp5150 *decoder = to_tvp5150(sd); + + if (decoder->norm == std) + return 0; + + return tvp5150_set_std(sd, std); +} + +static int tvp5150_reset(struct v4l2_subdev *sd, u32 val) { + struct tvp5150 *decoder = to_tvp5150(sd); u8 msb_id, lsb_id, msb_rom, lsb_rom; - struct tvp5150 *decoder = i2c_get_clientdata(c); - msb_id=tvp5150_read(c,TVP5150_MSB_DEV_ID); - lsb_id=tvp5150_read(c,TVP5150_LSB_DEV_ID); - msb_rom=tvp5150_read(c,TVP5150_ROM_MAJOR_VER); - lsb_rom=tvp5150_read(c,TVP5150_ROM_MINOR_VER); + msb_id = tvp5150_read(sd, TVP5150_MSB_DEV_ID); + lsb_id = tvp5150_read(sd, TVP5150_LSB_DEV_ID); + msb_rom = tvp5150_read(sd, TVP5150_ROM_MAJOR_VER); + lsb_rom = tvp5150_read(sd, TVP5150_ROM_MINOR_VER); - if ((msb_rom==4)&&(lsb_rom==0)) { /* Is TVP5150AM1 */ - tvp5150_info("tvp%02x%02xam1 detected.\n",msb_id, lsb_id); + if (msb_rom == 4 && lsb_rom == 0) { /* Is TVP5150AM1 */ + v4l2_info(sd, "tvp%02x%02xam1 detected.\n", msb_id, lsb_id); /* ITU-T BT.656.4 timing */ - tvp5150_write(c,TVP5150_REV_SELECT,0); + tvp5150_write(sd, TVP5150_REV_SELECT, 0); } else { - if ((msb_rom==3)||(lsb_rom==0x21)) { /* Is TVP5150A */ - tvp5150_info("tvp%02x%02xa detected.\n",msb_id, lsb_id); + if (msb_rom == 3 || lsb_rom == 0x21) { /* Is TVP5150A */ + v4l2_info(sd, "tvp%02x%02xa detected.\n", msb_id, lsb_id); } else { - tvp5150_info("*** unknown tvp%02x%02x chip detected.\n",msb_id,lsb_id); - tvp5150_info("*** Rom ver is %d.%d\n",msb_rom,lsb_rom); + v4l2_info(sd, "*** unknown tvp%02x%02x chip detected.\n", + msb_id, lsb_id); + v4l2_info(sd, "*** Rom ver is %d.%d\n", msb_rom, lsb_rom); } } /* Initializes TVP5150 to its default values */ - tvp5150_write_inittab(c, tvp5150_init_default); + tvp5150_write_inittab(sd, tvp5150_init_default); /* Initializes VDP registers */ - tvp5150_vdp_init(c, vbi_ram_default); + tvp5150_vdp_init(sd, vbi_ram_default); /* Selects decoder input */ - tvp5150_selmux(c); + tvp5150_selmux(sd); /* Initializes TVP5150 to stream enabled values */ - tvp5150_write_inittab(c, tvp5150_init_enable); + tvp5150_write_inittab(sd, tvp5150_init_enable); /* Initialize image preferences */ - tvp5150_write(c, TVP5150_BRIGHT_CTL, decoder->bright); - tvp5150_write(c, TVP5150_CONTRAST_CTL, decoder->contrast); - tvp5150_write(c, TVP5150_SATURATION_CTL, decoder->contrast); - tvp5150_write(c, TVP5150_HUE_CTL, decoder->hue); + tvp5150_write(sd, TVP5150_BRIGHT_CTL, decoder->bright); + tvp5150_write(sd, TVP5150_CONTRAST_CTL, decoder->contrast); + tvp5150_write(sd, TVP5150_SATURATION_CTL, decoder->contrast); + tvp5150_write(sd, TVP5150_HUE_CTL, decoder->hue); - tvp5150_set_std(c, decoder->norm); + tvp5150_set_std(sd, decoder->norm); + return 0; }; -static int tvp5150_get_ctrl(struct i2c_client *c, struct v4l2_control *ctrl) +static int tvp5150_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { -/* struct tvp5150 *decoder = i2c_get_clientdata(c); */ + v4l2_dbg(1, debug, sd, "VIDIOC_G_CTRL called\n"); switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - ctrl->value = tvp5150_read(c, TVP5150_BRIGHT_CTL); + ctrl->value = tvp5150_read(sd, TVP5150_BRIGHT_CTL); return 0; case V4L2_CID_CONTRAST: - ctrl->value = tvp5150_read(c, TVP5150_CONTRAST_CTL); + ctrl->value = tvp5150_read(sd, TVP5150_CONTRAST_CTL); return 0; case V4L2_CID_SATURATION: - ctrl->value = tvp5150_read(c, TVP5150_SATURATION_CTL); + ctrl->value = tvp5150_read(sd, TVP5150_SATURATION_CTL); return 0; case V4L2_CID_HUE: - ctrl->value = tvp5150_read(c, TVP5150_HUE_CTL); + ctrl->value = tvp5150_read(sd, TVP5150_HUE_CTL); return 0; } return -EINVAL; } -static int tvp5150_set_ctrl(struct i2c_client *c, struct v4l2_control *ctrl) +static int tvp5150_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { -/* struct tvp5150 *decoder = i2c_get_clientdata(c); */ + u8 i, n; + n = ARRAY_SIZE(tvp5150_qctrl); + + for (i = 0; i < n; i++) { + if (ctrl->id != tvp5150_qctrl[i].id) + continue; + if (ctrl->value < tvp5150_qctrl[i].minimum || + ctrl->value > tvp5150_qctrl[i].maximum) + return -ERANGE; + v4l2_dbg(1, debug, sd, "VIDIOC_S_CTRL: id=%d, value=%d\n", + ctrl->id, ctrl->value); + break; + } switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - tvp5150_write(c, TVP5150_BRIGHT_CTL, ctrl->value); + tvp5150_write(sd, TVP5150_BRIGHT_CTL, ctrl->value); return 0; case V4L2_CID_CONTRAST: - tvp5150_write(c, TVP5150_CONTRAST_CTL, ctrl->value); + tvp5150_write(sd, TVP5150_CONTRAST_CTL, ctrl->value); return 0; case V4L2_CID_SATURATION: - tvp5150_write(c, TVP5150_SATURATION_CTL, ctrl->value); + tvp5150_write(sd, TVP5150_SATURATION_CTL, ctrl->value); return 0; case V4L2_CID_HUE: - tvp5150_write(c, TVP5150_HUE_CTL, ctrl->value); + tvp5150_write(sd, TVP5150_HUE_CTL, ctrl->value); return 0; } return -EINVAL; @@ -886,289 +897,250 @@ static int tvp5150_set_ctrl(struct i2c_client *c, struct v4l2_control *ctrl) /**************************************************************************** I2C Command ****************************************************************************/ -static int tvp5150_command(struct i2c_client *c, - unsigned int cmd, void *arg) -{ - struct tvp5150 *decoder = i2c_get_clientdata(c); - - switch (cmd) { - - case 0: - case VIDIOC_INT_RESET: - tvp5150_reset(c); - break; - case VIDIOC_INT_G_VIDEO_ROUTING: - { - struct v4l2_routing *route = arg; - *route = decoder->route; - break; - } - case VIDIOC_INT_S_VIDEO_ROUTING: - { - struct v4l2_routing *route = arg; - - decoder->route = *route; - tvp5150_selmux(c); - break; - } - case VIDIOC_S_STD: - if (decoder->norm == *(v4l2_std_id *)arg) - break; - return tvp5150_set_std(c, *(v4l2_std_id *)arg); - case VIDIOC_G_STD: - *(v4l2_std_id *)arg = decoder->norm; - break; +static int tvp5150_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +{ + struct tvp5150 *decoder = to_tvp5150(sd); - case VIDIOC_G_SLICED_VBI_CAP: - { - struct v4l2_sliced_vbi_cap *cap = arg; - tvp5150_dbg(1, "VIDIOC_G_SLICED_VBI_CAP\n"); + decoder->route = *route; + tvp5150_selmux(sd); + return 0; +} - tvp5150_vbi_get_cap(vbi_ram_default, cap); - break; +static int tvp5150_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) +{ + struct v4l2_sliced_vbi_format *svbi; + int i; + + /* raw vbi */ + if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + /* this is for capturing 36 raw vbi lines + if there's a way to cut off the beginning 2 vbi lines + with the tvp5150 then the vbi line count could be lowered + to 17 lines/field again, although I couldn't find a register + which could do that cropping */ + if (fmt->fmt.vbi.sample_format == V4L2_PIX_FMT_GREY) + tvp5150_write(sd, TVP5150_LUMA_PROC_CTL_1, 0x70); + if (fmt->fmt.vbi.count[0] == 18 && fmt->fmt.vbi.count[1] == 18) { + tvp5150_write(sd, TVP5150_VERT_BLANKING_START, 0x00); + tvp5150_write(sd, TVP5150_VERT_BLANKING_STOP, 0x01); + } + return 0; } - case VIDIOC_S_FMT: - { - struct v4l2_format *fmt; - struct v4l2_sliced_vbi_format *svbi; - int i; - - fmt = arg; - /* raw vbi */ - if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - /* this is for capturing 36 raw vbi lines - if there's a way to cut off the beginning 2 vbi lines - with the tvp5150 then the vbi line count could be lowered - to 17 lines/field again, although I couldn't find a register - which could do that cropping */ - if (fmt->fmt.vbi.sample_format == V4L2_PIX_FMT_GREY) - tvp5150_write(c, TVP5150_LUMA_PROC_CTL_1, 0x70); - if (fmt->fmt.vbi.count[0] == 18 && fmt->fmt.vbi.count[1] == 18) { - tvp5150_write(c, TVP5150_VERT_BLANKING_START, 0x00); - tvp5150_write(c, TVP5150_VERT_BLANKING_STOP, 0x01); - } - break; + if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) + return -EINVAL; + svbi = &fmt->fmt.sliced; + if (svbi->service_set != 0) { + for (i = 0; i <= 23; i++) { + svbi->service_lines[1][i] = 0; + svbi->service_lines[0][i] = + tvp5150_set_vbi(sd, vbi_ram_default, + svbi->service_lines[0][i], 0xf0, i, 3); } - if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) - return -EINVAL; - svbi = &fmt->fmt.sliced; - if (svbi->service_set != 0) { - for (i = 0; i <= 23; i++) { - svbi->service_lines[1][i] = 0; - - svbi->service_lines[0][i]=tvp5150_set_vbi(c, - vbi_ram_default, - svbi->service_lines[0][i],0xf0,i,3); - } - /* Enables FIFO */ - tvp5150_write(c, TVP5150_FIFO_OUT_CTRL,1); - } else { - /* Disables FIFO*/ - tvp5150_write(c, TVP5150_FIFO_OUT_CTRL,0); + /* Enables FIFO */ + tvp5150_write(sd, TVP5150_FIFO_OUT_CTRL, 1); + } else { + /* Disables FIFO*/ + tvp5150_write(sd, TVP5150_FIFO_OUT_CTRL, 0); - /* Disable Full Field */ - tvp5150_write(c, TVP5150_FULL_FIELD_ENA, 0); + /* Disable Full Field */ + tvp5150_write(sd, TVP5150_FULL_FIELD_ENA, 0); - /* Disable Line modes */ - for (i=TVP5150_LINE_MODE_INI; i<=TVP5150_LINE_MODE_END; i++) - tvp5150_write(c, i, 0xff); - } - break; + /* Disable Line modes */ + for (i = TVP5150_LINE_MODE_INI; i <= TVP5150_LINE_MODE_END; i++) + tvp5150_write(sd, i, 0xff); } - case VIDIOC_G_FMT: - { - struct v4l2_format *fmt; - struct v4l2_sliced_vbi_format *svbi; + return 0; +} - int i, mask=0; +static int tvp5150_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) +{ + struct v4l2_sliced_vbi_format *svbi; + int i, mask = 0; - fmt = arg; - if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) - return -EINVAL; - svbi = &fmt->fmt.sliced; - memset(svbi, 0, sizeof(*svbi)); + if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) + return -EINVAL; + svbi = &fmt->fmt.sliced; + memset(svbi, 0, sizeof(*svbi)); - for (i = 0; i <= 23; i++) { - svbi->service_lines[0][i]=tvp5150_get_vbi(c, - vbi_ram_default,i); - mask|=svbi->service_lines[0][i]; - } - svbi->service_set=mask; - break; + for (i = 0; i <= 23; i++) { + svbi->service_lines[0][i] = + tvp5150_get_vbi(sd, vbi_ram_default, i); + mask |= svbi->service_lines[0][i]; } + svbi->service_set = mask; + return 0; +} + #if 0 - /* This will not work for USB devices */ - case VIDIOC_INT_G_VBI_DATA: - { - struct v4l2_sliced_vbi_data *data = arg; - u8 status; - - status=tvp5150_read(c, TVP5150_VDP_STATUS_REG); - - if (data->id & V4L2_SLICED_CAPTION) { - if (!field && (status&0x10)) { - data->data[0]=tvp5150_read(c, TVP5150_CC_DATA_INI); - data->data[1]=tvp5150_read(c, TVP5150_CC_DATA_INI+1); - } if (field && (status&0x8)) { - data->data[0]=tvp5150_read(c, TVP5150_CC_DATA_INI+2); - data->data[1]=tvp5150_read(c, TVP5150_CC_DATA_INI+3); - } else data->id=0; - return 0; - } else if (data->id & V4L2_SLICED_WSS) { - } else if (data->id & V4L2_SLICED_VPS) { +/* This will not work for USB devices */ +static int tvp5150_g_vbi_data(struct v4l2_subdev *sd, + struct v4l2_sliced_vbi_data *data) +{ + u8 status = tvp5150_read(sd, TVP5150_VDP_STATUS_REG); + + if (data->id & V4L2_SLICED_CAPTION) { + if (!field && (status & 0x10)) { + data->data[0] = tvp5150_read(sd, TVP5150_CC_DATA_INI); + data->data[1] = tvp5150_read(sd, TVP5150_CC_DATA_INI+1); + } if (field && (status & 0x8)) { + data->data[0] = tvp5150_read(sd, TVP5150_CC_DATA_INI+2); + data->data[1] = tvp5150_read(sd, TVP5150_CC_DATA_INI+3); + } else { + data->id = 0; } - break; + return 0; + } else if (data->id & V4L2_SLICED_WSS) { + } else if (data->id & V4L2_SLICED_VPS) { } - case VIDIOC_INT_DECODE_VBI_LINE: - { - struct v4l2_decode_vbi_line *vbi = arg; - u8 status; + return 0; +} - status=tvp5150_read(c, TVP5150_VDP_STATUS_REG); +static int decode_vbi_data(struct v4l2_subdev *sd, vbi) +{ + count = tvp5150_read(sd, TVP5150_FIFO_WORD_COUNT); - if (status&0x80) { - tvp5150_err("Full field error"); - status&=0x7f; - tvp5150_write(c, TVP5150_VDP_STATUS_REG,status); - } + for (i = 0; i < count; i++) { + *p = tvp5150_read(sd, TVP5150_VBI_FIFO_READ_DATA); + p++; + } +} - /* FIFO */ - /* Current V4L2 API allows sliced VBI only with fifo mode, - since line and types are not provided on other means - on tvp5150. - */ - if (!(status&0x40)) /* Has FIFO data */ - decode_vbi_data(c,vbi); +static int tvp5150_decode_vbi_line(struct v4l2_subdev *sd, + struct v4l2_decode_vbi_line *vbi) +{ + u8 status = tvp5150_read(sd, TVP5150_VDP_STATUS_REG); - break; + if (status & 0x80) { + v4l2_err(sd, "Full field error"); + status &= 0x7f; + tvp5150_write(sd, TVP5150_VDP_STATUS_REG, status); } + + /* FIFO */ + /* Current V4L2 API allows sliced VBI only with fifo mode, + since line and types are not provided on other means + on tvp5150. + */ + if (!(status & 0x40)) /* Has FIFO data */ + decode_vbi_data(sd, vbi); + return 0; +} #endif #ifdef CONFIG_VIDEO_ADV_DEBUG - case VIDIOC_DBG_G_REGISTER: - case VIDIOC_DBG_S_REGISTER: - { - struct v4l2_register *reg = arg; - - if (!v4l2_chip_match_i2c_client(c, reg->match_type, reg->match_chip)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - if (cmd == VIDIOC_DBG_G_REGISTER) - reg->val = tvp5150_read(c, reg->reg & 0xff); - else - tvp5150_write(c, reg->reg & 0xff, reg->val & 0xff); - break; - } -#endif +static int tvp5150_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); - case VIDIOC_LOG_STATUS: - dump_reg(c); - break; + if (!v4l2_chip_match_i2c_client(client, + reg->match_type, reg->match_chip)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reg->val = tvp5150_read(sd, reg->reg & 0xff); + return 0; +} - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *vt = arg; - int status = tvp5150_read(c, 0x88); +static int tvp5150_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); - vt->signal = ((status & 0x04) && (status & 0x02)) ? 0xffff : 0x0; - break; - } - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; - int i; + if (!v4l2_chip_match_i2c_client(client, + reg->match_type, reg->match_chip)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + tvp5150_write(sd, reg->reg & 0xff, reg->val & 0xff); + return 0; +} +#endif - tvp5150_dbg(1, "VIDIOC_QUERYCTRL called\n"); +static int tvp5150_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + int status = tvp5150_read(sd, 0x88); + + vt->signal = ((status & 0x04) && (status & 0x02)) ? 0xffff : 0x0; + return 0; +} - for (i = 0; i < ARRAY_SIZE(tvp5150_qctrl); i++) - if (qc->id && qc->id == tvp5150_qctrl[i].id) { - memcpy(qc, &(tvp5150_qctrl[i]), - sizeof(*qc)); - return 0; - } +static int tvp5150_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) +{ + int i; - return -EINVAL; - } - case VIDIOC_G_CTRL: - { - struct v4l2_control *ctrl = arg; - tvp5150_dbg(1, "VIDIOC_G_CTRL called\n"); + v4l2_dbg(1, debug, sd, "VIDIOC_QUERYCTRL called\n"); - return tvp5150_get_ctrl(c, ctrl); - } - case VIDIOC_S_CTRL: - { - struct v4l2_control *ctrl = arg; - u8 i, n; - n = ARRAY_SIZE(tvp5150_qctrl); - for (i = 0; i < n; i++) - if (ctrl->id == tvp5150_qctrl[i].id) { - if (ctrl->value < - tvp5150_qctrl[i].minimum - || ctrl->value > - tvp5150_qctrl[i].maximum) - return -ERANGE; - tvp5150_dbg(1, - "VIDIOC_S_CTRL: id=%d, value=%d\n", - ctrl->id, ctrl->value); - return tvp5150_set_ctrl(c, ctrl); - } - return -EINVAL; + for (i = 0; i < ARRAY_SIZE(tvp5150_qctrl); i++) + if (qc->id && qc->id == tvp5150_qctrl[i].id) { + memcpy(qc, &(tvp5150_qctrl[i]), + sizeof(*qc)); + return 0; } - default: - return -EINVAL; - } + return -EINVAL; +} - return 0; +static int tvp5150_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); } +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops tvp5150_core_ops = { + .log_status = tvp5150_log_status, + .g_ctrl = tvp5150_g_ctrl, + .s_ctrl = tvp5150_s_ctrl, + .queryctrl = tvp5150_queryctrl, + .reset = tvp5150_reset, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = tvp5150_g_register, + .s_register = tvp5150_s_register, +#endif +}; + +static const struct v4l2_subdev_tuner_ops tvp5150_tuner_ops = { + .s_std = tvp5150_s_std, + .g_tuner = tvp5150_g_tuner, +}; + +static const struct v4l2_subdev_video_ops tvp5150_video_ops = { + .s_routing = tvp5150_s_routing, + .g_fmt = tvp5150_g_fmt, + .s_fmt = tvp5150_s_fmt, + .g_sliced_vbi_cap = tvp5150_g_sliced_vbi_cap, +}; + +static const struct v4l2_subdev_ops tvp5150_ops = { + .core = &tvp5150_core_ops, + .tuner = &tvp5150_tuner_ops, + .video = &tvp5150_video_ops, +}; + + /**************************************************************************** I2C Client & Driver ****************************************************************************/ -static struct i2c_driver driver; - -static struct i2c_client client_template = { - .name = "(unset)", - .driver = &driver, -}; -static int tvp5150_detect_client(struct i2c_adapter *adapter, - int address, int kind) +static int tvp5150_probe(struct i2c_client *c, + const struct i2c_device_id *id) { - struct i2c_client *c; struct tvp5150 *core; - int rv; - - if (debug) - printk( KERN_INFO - "tvp5150.c: detecting tvp5150 client on address 0x%x\n", - address << 1); - - client_template.adapter = adapter; - client_template.addr = address; + struct v4l2_subdev *sd; /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality - (adapter, + if (!i2c_check_functionality(c->adapter, I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) - return 0; - - c = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); - if (!c) - return -ENOMEM; - memcpy(c, &client_template, sizeof(struct i2c_client)); + return -EIO; core = kzalloc(sizeof(struct tvp5150), GFP_KERNEL); if (!core) { - kfree(c); return -ENOMEM; } - i2c_set_clientdata(c, core); - - rv = i2c_attach_client(c); + sd = &core->sd; + v4l2_i2c_subdev_init(sd, c, &tvp5150_ops); + v4l_info(c, "chip found @ 0x%02x (%s)\n", + c->addr << 1, c->adapter->name); core->norm = V4L2_STD_ALL; /* Default is autodetect */ core->route.input = TVP5150_COMPOSITE1; @@ -1178,75 +1150,42 @@ static int tvp5150_detect_client(struct i2c_adapter *adapter, core->hue = 0; core->sat = 128; - if (rv) { - kfree(c); - kfree(core); - return rv; - } - if (debug > 1) - dump_reg(c); + tvp5150_log_status(sd); return 0; } -static int tvp5150_attach_adapter(struct i2c_adapter *adapter) +static int tvp5150_remove(struct i2c_client *c) { - if (debug) - printk( KERN_INFO - "tvp5150.c: starting probe for adapter %s (0x%x)\n", - adapter->name, adapter->id); - return i2c_probe(adapter, &addr_data, &tvp5150_detect_client); -} + struct v4l2_subdev *sd = i2c_get_clientdata(c); -static int tvp5150_detach_client(struct i2c_client *c) -{ - struct tvp5150 *decoder = i2c_get_clientdata(c); - int err; - - tvp5150_dbg(1, + v4l2_dbg(1, debug, sd, "tvp5150.c: removing tvp5150 adapter on address 0x%x\n", c->addr << 1); - err = i2c_detach_client(c); - if (err) { - return err; - } - - kfree(decoder); - kfree(c); - + v4l2_device_unregister_subdev(sd); + kfree(to_tvp5150(sd)); return 0; } /* ----------------------------------------------------------------------- */ -static struct i2c_driver driver = { - .driver = { - .name = "tvp5150", - }, - .id = I2C_DRIVERID_TVP5150, - - .attach_adapter = tvp5150_attach_adapter, - .detach_client = tvp5150_detach_client, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) +static const struct i2c_device_id tvp5150_id[] = { + { "tvp5150", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tvp5150_id); +#endif +static struct v4l2_i2c_driver_data v4l2_i2c_data = { + .name = "tvp5150", + .driverid = I2C_DRIVERID_TVP5150, .command = tvp5150_command, -#if 0 - .driver = { - .suspend = tvp5150_suspend, - .resume = tvp5150_resume, - }, + .probe = tvp5150_probe, + .remove = tvp5150_remove, + .legacy_class = I2C_CLASS_TV_ANALOG | I2C_CLASS_TV_DIGITAL, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + .id_table = tvp5150_id, #endif }; - -static int __init tvp5150_init(void) -{ - return i2c_add_driver(&driver); -} - -static void __exit tvp5150_exit(void) -{ - i2c_del_driver(&driver); -} - -module_init(tvp5150_init); -module_exit(tvp5150_exit); -- cgit v1.2.3 From 51a0d7da82dc5d293c59c43e1e912f1c673a9849 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 18 Dec 2008 16:11:32 +0100 Subject: tvaudio: convert to v4l2_subdev. From: Hans Verkuil Priority: normal Signed-off-by: Hans Verkuil --- linux/drivers/media/video/tvaudio.c | 715 +++++++++++++++++++----------------- 1 file changed, 387 insertions(+), 328 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/tvaudio.c b/linux/drivers/media/video/tvaudio.c index 0a86c80a2..492da4d8e 100644 --- a/linux/drivers/media/video/tvaudio.c +++ b/linux/drivers/media/video/tvaudio.c @@ -36,8 +36,7 @@ #include "compat.h" #include -#include -#include +#include #include #include @@ -113,7 +112,7 @@ struct CHIPDESC { /* current state of the chip */ struct CHIPSTATE { - struct i2c_client *c; + struct v4l2_subdev sd; /* chip-specific description - should point to an entry at CHIPDESC table */ @@ -135,6 +134,11 @@ struct CHIPSTATE { int audmode; }; +static inline struct CHIPSTATE *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct CHIPSTATE, sd); +} + /* ---------------------------------------------------------------------- */ /* i2c addresses */ @@ -155,34 +159,34 @@ I2C_CLIENT_INSMOD; static int chip_write(struct CHIPSTATE *chip, int subaddr, int val) { + struct v4l2_subdev *sd = &chip->sd; + struct i2c_client *c = v4l2_get_subdevdata(sd); unsigned char buffer[2]; if (subaddr < 0) { - v4l_dbg(1, debug, chip->c, "%s: chip_write: 0x%x\n", - chip->c->name, val); + v4l2_dbg(1, debug, sd, "chip_write: 0x%x\n", val); chip->shadow.bytes[1] = val; buffer[0] = val; - if (1 != i2c_master_send(chip->c,buffer,1)) { - v4l_warn(chip->c, "%s: I/O error (write 0x%x)\n", - chip->c->name, val); + if (1 != i2c_master_send(c, buffer, 1)) { + v4l2_warn(sd, "I/O error (write 0x%x)\n", val); return -1; } } else { if (subaddr + 1 >= ARRAY_SIZE(chip->shadow.bytes)) { - v4l_info(chip->c, + v4l2_info(sd, "Tried to access a non-existent register: %d\n", subaddr); return -EINVAL; } - v4l_dbg(1, debug, chip->c, "%s: chip_write: reg%d=0x%x\n", - chip->c->name, subaddr, val); + v4l2_dbg(1, debug, sd, "chip_write: reg%d=0x%x\n", + subaddr, val); chip->shadow.bytes[subaddr+1] = val; buffer[0] = subaddr; buffer[1] = val; - if (2 != i2c_master_send(chip->c,buffer,2)) { - v4l_warn(chip->c, "%s: I/O error (write reg%d=0x%x)\n", - chip->c->name, subaddr, val); + if (2 != i2c_master_send(c, buffer, 2)) { + v4l2_warn(sd, "I/O error (write reg%d=0x%x)\n", + subaddr, val); return -1; } } @@ -192,12 +196,14 @@ static int chip_write(struct CHIPSTATE *chip, int subaddr, int val) static int chip_write_masked(struct CHIPSTATE *chip, int subaddr, int val, int mask) { + struct v4l2_subdev *sd = &chip->sd; + if (mask != 0) { if (subaddr < 0) { val = (chip->shadow.bytes[1] & ~mask) | (val & mask); } else { if (subaddr + 1 >= ARRAY_SIZE(chip->shadow.bytes)) { - v4l_info(chip->c, + v4l2_info(sd, "Tried to access a non-existent register: %d\n", subaddr); return -EINVAL; @@ -211,45 +217,51 @@ static int chip_write_masked(struct CHIPSTATE *chip, static int chip_read(struct CHIPSTATE *chip) { + struct v4l2_subdev *sd = &chip->sd; + struct i2c_client *c = v4l2_get_subdevdata(sd); unsigned char buffer; - if (1 != i2c_master_recv(chip->c,&buffer,1)) { - v4l_warn(chip->c, "%s: I/O error (read)\n", - chip->c->name); + if (1 != i2c_master_recv(c, &buffer, 1)) { + v4l2_warn(sd, "I/O error (read)\n"); return -1; } - v4l_dbg(1, debug, chip->c, "%s: chip_read: 0x%x\n",chip->c->name, buffer); + v4l2_dbg(1, debug, sd, "chip_read: 0x%x\n", buffer); return buffer; } static int chip_read2(struct CHIPSTATE *chip, int subaddr) { + struct v4l2_subdev *sd = &chip->sd; + struct i2c_client *c = v4l2_get_subdevdata(sd); unsigned char write[1]; unsigned char read[1]; struct i2c_msg msgs[2] = { - { chip->c->addr, 0, 1, write }, - { chip->c->addr, I2C_M_RD, 1, read } + { c->addr, 0, 1, write }, + { c->addr, I2C_M_RD, 1, read } }; + write[0] = subaddr; - if (2 != i2c_transfer(chip->c->adapter,msgs,2)) { - v4l_warn(chip->c, "%s: I/O error (read2)\n", chip->c->name); + if (2 != i2c_transfer(c->adapter, msgs, 2)) { + v4l2_warn(sd, "I/O error (read2)\n"); return -1; } - v4l_dbg(1, debug, chip->c, "%s: chip_read2: reg%d=0x%x\n", - chip->c->name, subaddr,read[0]); + v4l2_dbg(1, debug, sd, "chip_read2: reg%d=0x%x\n", + subaddr, read[0]); return read[0]; } static int chip_cmd(struct CHIPSTATE *chip, char *name, audiocmd *cmd) { + struct v4l2_subdev *sd = &chip->sd; + struct i2c_client *c = v4l2_get_subdevdata(sd); int i; if (0 == cmd->count) return 0; if (cmd->count + cmd->bytes[0] - 1 >= ARRAY_SIZE(chip->shadow.bytes)) { - v4l_info(chip->c, + v4l2_info(sd, "Tried to access a non-existent register range: %d to %d\n", cmd->bytes[0] + 1, cmd->bytes[0] + cmd->count - 1); return -EINVAL; @@ -258,19 +270,19 @@ static int chip_cmd(struct CHIPSTATE *chip, char *name, audiocmd *cmd) /* FIXME: it seems that the shadow bytes are wrong bellow !*/ /* update our shadow register set; print bytes if (debug > 0) */ - v4l_dbg(1, debug, chip->c, "%s: chip_cmd(%s): reg=%d, data:", - chip->c->name, name,cmd->bytes[0]); + v4l2_dbg(1, debug, sd, "chip_cmd(%s): reg=%d, data:", + name, cmd->bytes[0]); for (i = 1; i < cmd->count; i++) { if (debug) - printk(" 0x%x",cmd->bytes[i]); + printk(KERN_CONT " 0x%x", cmd->bytes[i]); chip->shadow.bytes[i+cmd->bytes[0]] = cmd->bytes[i]; } if (debug) - printk("\n"); + printk(KERN_CONT "\n"); /* send data to the chip */ - if (cmd->count != i2c_master_send(chip->c,cmd->bytes,cmd->count)) { - v4l_warn(chip->c, "%s: I/O error (%s)\n", chip->c->name, name); + if (cmd->count != i2c_master_send(c, cmd->bytes, cmd->count)) { + v4l2_warn(sd, "I/O error (%s)\n", name); return -1; } return 0; @@ -293,9 +305,10 @@ static int chip_thread(void *data) { struct CHIPSTATE *chip = data; struct CHIPDESC *desc = chip->desc; + struct v4l2_subdev *sd = &chip->sd; int mode; - v4l_dbg(1, debug, chip->c, "%s: thread started\n", chip->c->name); + v4l2_dbg(1, debug, sd, "thread started\n"); set_freezable(); for (;;) { set_current_state(TASK_INTERRUPTIBLE); @@ -305,7 +318,7 @@ static int chip_thread(void *data) try_to_freeze(); if (kthread_should_stop()) break; - v4l_dbg(1, debug, chip->c, "%s: thread wakeup\n", chip->c->name); + v4l2_dbg(1, debug, sd, "thread wakeup\n"); /* don't do anything for radio or if mode != auto */ if (chip->radio || chip->mode != 0) @@ -317,8 +330,7 @@ static int chip_thread(void *data) continue; /* chip detected a new audio mode - set it */ - v4l_dbg(1, debug, chip->c, "%s: thread checkmode\n", - chip->c->name); + v4l2_dbg(1, debug, sd, "thread checkmode\n"); chip->prevmode = mode; @@ -337,7 +349,7 @@ static int chip_thread(void *data) mod_timer(&chip->wt, jiffies+msecs_to_jiffies(2000)); } - v4l_dbg(1, debug, chip->c, "%s: thread exiting\n", chip->c->name); + v4l2_dbg(1, debug, sd, "thread exiting\n"); return 0; } @@ -366,6 +378,7 @@ static int chip_thread(void *data) static int tda9840_getmode(struct CHIPSTATE *chip) { + struct v4l2_subdev *sd = &chip->sd; int val, mode; val = chip_read(chip); @@ -375,7 +388,7 @@ static int tda9840_getmode(struct CHIPSTATE *chip) if (val & TDA9840_ST_STEREO) mode |= V4L2_TUNER_MODE_STEREO; - v4l_dbg(1, debug, chip->c, "tda9840_getmode(): raw chip read: %d, return: %d\n", + v4l2_dbg(1, debug, sd, "tda9840_getmode(): raw chip read: %d, return: %d\n", val, mode); return mode; } @@ -671,6 +684,7 @@ static void tda985x_setmode(struct CHIPSTATE *chip, int mode) static int tda9873_getmode(struct CHIPSTATE *chip) { + struct v4l2_subdev *sd = &chip->sd; int val,mode; val = chip_read(chip); @@ -679,23 +693,24 @@ static int tda9873_getmode(struct CHIPSTATE *chip) mode |= V4L2_TUNER_MODE_STEREO; if (val & TDA9873_DUAL) mode |= V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2; - v4l_dbg(1, debug, chip->c, "tda9873_getmode(): raw chip read: %d, return: %d\n", + v4l2_dbg(1, debug, sd, "tda9873_getmode(): raw chip read: %d, return: %d\n", val, mode); return mode; } static void tda9873_setmode(struct CHIPSTATE *chip, int mode) { + struct v4l2_subdev *sd = &chip->sd; int sw_data = chip->shadow.bytes[TDA9873_SW+1] & ~ TDA9873_TR_MASK; /* int adj_data = chip->shadow.bytes[TDA9873_AD+1] ; */ if ((sw_data & TDA9873_INP_MASK) != TDA9873_INTERNAL) { - v4l_dbg(1, debug, chip->c, "tda9873_setmode(): external input\n"); + v4l2_dbg(1, debug, sd, "tda9873_setmode(): external input\n"); return; } - v4l_dbg(1, debug, chip->c, "tda9873_setmode(): chip->shadow.bytes[%d] = %d\n", TDA9873_SW+1, chip->shadow.bytes[TDA9873_SW+1]); - v4l_dbg(1, debug, chip->c, "tda9873_setmode(): sw_data = %d\n", sw_data); + v4l2_dbg(1, debug, sd, "tda9873_setmode(): chip->shadow.bytes[%d] = %d\n", TDA9873_SW+1, chip->shadow.bytes[TDA9873_SW+1]); + v4l2_dbg(1, debug, sd, "tda9873_setmode(): sw_data = %d\n", sw_data); switch (mode) { case V4L2_TUNER_MODE_MONO: @@ -716,7 +731,7 @@ static void tda9873_setmode(struct CHIPSTATE *chip, int mode) } chip_write(chip, TDA9873_SW, sw_data); - v4l_dbg(1, debug, chip->c, "tda9873_setmode(): req. mode %d; chip_write: %d\n", + v4l2_dbg(1, debug, sd, "tda9873_setmode(): req. mode %d; chip_write: %d\n", mode, sw_data); } @@ -825,6 +840,8 @@ static struct tda9874a_MODES { static int tda9874a_setup(struct CHIPSTATE *chip) { + struct v4l2_subdev *sd = &chip->sd; + chip_write(chip, TDA9874A_AGCGR, 0x00); /* 0 dB */ chip_write(chip, TDA9874A_GCONR, tda9874a_GCONR); chip_write(chip, TDA9874A_MSR, (tda9874a_mode) ? 0x03:0x02); @@ -855,13 +872,14 @@ static int tda9874a_setup(struct CHIPSTATE *chip) chip_write(chip, TDA9874A_SDACOSR, (tda9874a_mode) ? 0x81:0x80); chip_write(chip, TDA9874A_AOSR, 0x00); /* or 0x10 */ } - v4l_dbg(1, debug, chip->c, "tda9874a_setup(): %s [0x%02X].\n", + v4l2_dbg(1, debug, sd, "tda9874a_setup(): %s [0x%02X].\n", tda9874a_modelist[tda9874a_STD].name,tda9874a_STD); return 1; } static int tda9874a_getmode(struct CHIPSTATE *chip) { + struct v4l2_subdev *sd = &chip->sd; int dsr,nsr,mode; int necr; /* just for debugging */ @@ -903,16 +921,18 @@ static int tda9874a_getmode(struct CHIPSTATE *chip) mode |= V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2; } - v4l_dbg(1, debug, chip->c, "tda9874a_getmode(): DSR=0x%X, NSR=0x%X, NECR=0x%X, return: %d.\n", + v4l2_dbg(1, debug, sd, "tda9874a_getmode(): DSR=0x%X, NSR=0x%X, NECR=0x%X, return: %d.\n", dsr, nsr, necr, mode); return mode; } static void tda9874a_setmode(struct CHIPSTATE *chip, int mode) { + struct v4l2_subdev *sd = &chip->sd; + /* Disable/enable NICAM auto-muting (based on DSR.RSSF status bit). */ /* If auto-muting is disabled, we can hear a signal of degrading quality. */ - if(tda9874a_mode) { + if (tda9874a_mode) { if(chip->shadow.bytes[MAXREGS-2] & 0x20) /* DSR.RSSF=1 */ tda9874a_NCONR &= 0xfe; /* enable */ else @@ -949,7 +969,7 @@ static void tda9874a_setmode(struct CHIPSTATE *chip, int mode) chip_write(chip, TDA9874A_AOSR, aosr); chip_write(chip, TDA9874A_MDACOSR, mdacosr); - v4l_dbg(1, debug, chip->c, "tda9874a_setmode(): req. mode %d; AOSR=0x%X, MDACOSR=0x%X.\n", + v4l2_dbg(1, debug, sd, "tda9874a_setmode(): req. mode %d; AOSR=0x%X, MDACOSR=0x%X.\n", mode, aosr, mdacosr); } else { /* dic == 0x07 */ @@ -984,13 +1004,14 @@ static void tda9874a_setmode(struct CHIPSTATE *chip, int mode) chip_write(chip, TDA9874A_FMMR, fmmr); chip_write(chip, TDA9874A_AOSR, aosr); - v4l_dbg(1, debug, chip->c, "tda9874a_setmode(): req. mode %d; FMMR=0x%X, AOSR=0x%X.\n", + v4l2_dbg(1, debug, sd, "tda9874a_setmode(): req. mode %d; FMMR=0x%X, AOSR=0x%X.\n", mode, fmmr, aosr); } } static int tda9874a_checkit(struct CHIPSTATE *chip) { + struct v4l2_subdev *sd = &chip->sd; int dic,sic; /* device id. and software id. codes */ if(-1 == (dic = chip_read2(chip,TDA9874A_DIC))) @@ -998,10 +1019,10 @@ static int tda9874a_checkit(struct CHIPSTATE *chip) if(-1 == (sic = chip_read2(chip,TDA9874A_SIC))) return 0; - v4l_dbg(1, debug, chip->c, "tda9874a_checkit(): DIC=0x%X, SIC=0x%X.\n", dic, sic); + v4l2_dbg(1, debug, sd, "tda9874a_checkit(): DIC=0x%X, SIC=0x%X.\n", dic, sic); if((dic == 0x11)||(dic == 0x07)) { - v4l_info(chip->c, "found tda9874%s.\n", (dic == 0x11) ? "a":"h"); + v4l2_info(sd, "found tda9874%s.\n", (dic == 0x11) ? "a" : "h"); tda9874a_dic = dic; /* remember device id. */ return 1; } @@ -1121,12 +1142,12 @@ static int tda8425_shift12(int val) { return (val >> 12) | 0xf0; } static int tda8425_initialize(struct CHIPSTATE *chip) { struct CHIPDESC *desc = chip->desc; + struct i2c_client *c = v4l2_get_subdevdata(&chip->sd); int inputmap[4] = { /* tuner */ TDA8425_S1_CH2, /* radio */ TDA8425_S1_CH1, /* extern */ TDA8425_S1_CH1, /* intern */ TDA8425_S1_OFF}; - if (chip->c->adapter->id == I2C_HW_B_RIVA) { - memcpy (desc->inputmap, inputmap, sizeof (inputmap)); - } + if (c->adapter->id == I2C_HW_B_RIVA) + memcpy(desc->inputmap, inputmap, sizeof(inputmap)); return 0; } @@ -1223,9 +1244,11 @@ static audiocmd ta8874z_sub = {2, { TA8874Z_MODE_SUB, TA8874Z_SEPARATION_DEFAULT static void ta8874z_setmode(struct CHIPSTATE *chip, int mode) { + struct v4l2_subdev *sd = &chip->sd; int update = 1; audiocmd *t = NULL; - v4l_dbg(1, debug, chip->c, "ta8874z_setmode(): mode: 0x%02x\n", mode); + + v4l2_dbg(1, debug, sd, "ta8874z_setmode(): mode: 0x%02x\n", mode); switch(mode){ case V4L2_TUNER_MODE_MONO: @@ -1487,146 +1510,11 @@ static struct CHIPDESC chiplist[] = { /* ---------------------------------------------------------------------- */ -/* i2c registration */ -static int chip_probe(struct i2c_client *client, const struct i2c_device_id *id) -{ - struct CHIPSTATE *chip; - struct CHIPDESC *desc; - - if (debug) { - printk(KERN_INFO "tvaudio: TV audio decoder + audio/video mux driver\n"); - printk(KERN_INFO "tvaudio: known chips: "); - for (desc = chiplist; desc->name != NULL; desc++) - printk("%s%s", (desc == chiplist) ? "" : ", ", desc->name); - printk("\n"); - } - - chip = kzalloc(sizeof(*chip),GFP_KERNEL); - if (!chip) - return -ENOMEM; - chip->c = client; - i2c_set_clientdata(client, chip); - - /* find description for the chip */ - v4l_dbg(1, debug, client, "chip found @ 0x%x\n", client->addr<<1); - for (desc = chiplist; desc->name != NULL; desc++) { - if (0 == *(desc->insmodopt)) - continue; - if (client->addr < desc->addr_lo || - client->addr > desc->addr_hi) - continue; - if (desc->checkit && !desc->checkit(chip)) - continue; - break; - } - if (desc->name == NULL) { - v4l_dbg(1, debug, client, "no matching chip description found\n"); - kfree(chip); - return -EIO; - } - v4l_info(client, "%s found @ 0x%x (%s)\n", desc->name, client->addr<<1, client->adapter->name); - if (desc->flags) { - v4l_dbg(1, debug, client, "matches:%s%s%s.\n", - (desc->flags & CHIP_HAS_VOLUME) ? " volume" : "", - (desc->flags & CHIP_HAS_BASSTREBLE) ? " bass/treble" : "", - (desc->flags & CHIP_HAS_INPUTSEL) ? " audiomux" : ""); - } - - /* fill required data structures */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) - strlcpy(client->name, desc->name, I2C_NAME_SIZE); -#else - if (!id) - strlcpy(client->name, desc->name, I2C_NAME_SIZE); -#endif - chip->desc = desc; - chip->shadow.count = desc->registers+1; - chip->prevmode = -1; - chip->audmode = V4L2_TUNER_MODE_LANG1; - - /* initialization */ - if (desc->initialize != NULL) - desc->initialize(chip); - else - chip_cmd(chip,"init",&desc->init); - - if (desc->flags & CHIP_HAS_VOLUME) { - if (!desc->volfunc) { - /* This shouldn't be happen. Warn user, but keep working - without volume controls - */ - v4l_info(chip->c, "volume callback undefined!\n"); - desc->flags &= ~CHIP_HAS_VOLUME; - } else { - chip->left = desc->leftinit ? desc->leftinit : 65535; - chip->right = desc->rightinit ? desc->rightinit : 65535; - chip_write(chip, desc->leftreg, - desc->volfunc(chip->left)); - chip_write(chip, desc->rightreg, - desc->volfunc(chip->right)); - } - } - if (desc->flags & CHIP_HAS_BASSTREBLE) { - if (!desc->bassfunc || !desc->treblefunc) { - /* This shouldn't be happen. Warn user, but keep working - without bass/treble controls - */ - v4l_info(chip->c, "bass/treble callbacks undefined!\n"); - desc->flags &= ~CHIP_HAS_BASSTREBLE; - } else { - chip->treble = desc->trebleinit ? - desc->trebleinit : 32768; - chip->bass = desc->bassinit ? - desc->bassinit : 32768; - chip_write(chip, desc->bassreg, - desc->bassfunc(chip->bass)); - chip_write(chip, desc->treblereg, - desc->treblefunc(chip->treble)); - } - } - - chip->thread = NULL; - if (desc->flags & CHIP_NEED_CHECKMODE) { - if (!desc->getmode || !desc->setmode) { - /* This shouldn't be happen. Warn user, but keep working - without kthread - */ - v4l_info(chip->c, "set/get mode callbacks undefined!\n"); - return 0; - } - /* start async thread */ - init_timer(&chip->wt); - chip->wt.function = chip_thread_wake; - chip->wt.data = (unsigned long)chip; - chip->thread = kthread_run(chip_thread, chip, chip->c->name); - if (IS_ERR(chip->thread)) { - v4l_warn(chip->c, "%s: failed to create kthread\n", - chip->c->name); - chip->thread = NULL; - } - } - return 0; -} - -static int chip_remove(struct i2c_client *client) -{ - struct CHIPSTATE *chip = i2c_get_clientdata(client); - - del_timer_sync(&chip->wt); - if (chip->thread) { - /* shutdown async thread */ - kthread_stop(chip->thread); - chip->thread = NULL; - } - - kfree(chip); - return 0; -} - -static int tvaudio_get_ctrl(struct CHIPSTATE *chip, +static int tvaudio_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { + struct CHIPSTATE *chip = to_state(sd); struct CHIPDESC *desc = chip->desc; switch (ctrl->id) { @@ -1664,9 +1552,10 @@ static int tvaudio_get_ctrl(struct CHIPSTATE *chip, return -EINVAL; } -static int tvaudio_set_ctrl(struct CHIPSTATE *chip, +static int tvaudio_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { + struct CHIPSTATE *chip = to_state(sd); struct CHIPDESC *desc = chip->desc; switch (ctrl->id) { @@ -1738,161 +1627,331 @@ static int tvaudio_set_ctrl(struct CHIPSTATE *chip, /* ---------------------------------------------------------------------- */ /* video4linux interface */ -static int chip_command(struct i2c_client *client, - unsigned int cmd, void *arg) +static int tvaudio_s_radio(struct v4l2_subdev *sd) { - struct CHIPSTATE *chip = i2c_get_clientdata(client); - struct CHIPDESC *desc = chip->desc; + struct CHIPSTATE *chip = to_state(sd); - if (debug > 0) { - v4l_i2c_print_ioctl(chip->c, cmd); - printk("\n"); + chip->radio = 1; + chip->watch_stereo = 0; + /* del_timer(&chip->wt); */ + return 0; +} + +static int tvaudio_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) +{ + struct CHIPSTATE *chip = to_state(sd); + struct CHIPDESC *desc = chip->desc; + + switch (qc->id) { + case V4L2_CID_AUDIO_MUTE: + break; + case V4L2_CID_AUDIO_VOLUME: + case V4L2_CID_AUDIO_BALANCE: + if (!(desc->flags & CHIP_HAS_VOLUME)) + return -EINVAL; + break; + case V4L2_CID_AUDIO_BASS: + case V4L2_CID_AUDIO_TREBLE: + if (!(desc->flags & CHIP_HAS_BASSTREBLE)) + return -EINVAL; + break; + default: + return -EINVAL; + } + return v4l2_ctrl_query_fill_std(qc); +} + +static int tvaudio_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *rt) +{ + struct CHIPSTATE *chip = to_state(sd); + struct CHIPDESC *desc = chip->desc; + + if (!(desc->flags & CHIP_HAS_INPUTSEL) || rt->input >= 4) + return -EINVAL; + /* There are four inputs: tuner, radio, extern and intern. */ + chip->input = rt->input; + if (chip->muted) + return 0; + chip_write_masked(chip, desc->inputreg, + desc->inputmap[chip->input], desc->inputmask); + return 0; +} + +static int tvaudio_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct CHIPSTATE *chip = to_state(sd); + struct CHIPDESC *desc = chip->desc; + int mode = 0; + + if (chip->radio) + return 0; + switch (vt->audmode) { + case V4L2_TUNER_MODE_MONO: + case V4L2_TUNER_MODE_STEREO: + case V4L2_TUNER_MODE_LANG1: + case V4L2_TUNER_MODE_LANG2: + mode = vt->audmode; + break; + case V4L2_TUNER_MODE_LANG1_LANG2: + mode = V4L2_TUNER_MODE_STEREO; + break; + default: + return -EINVAL; } + chip->audmode = vt->audmode; - switch (cmd) { - case AUDC_SET_RADIO: - chip->radio = 1; + if (desc->setmode && mode) { chip->watch_stereo = 0; /* del_timer(&chip->wt); */ - break; - /* --- v4l ioctls --- */ - /* take care: bttv does userspace copying, we'll get a - kernel pointer here... */ - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; - - switch (qc->id) { - case V4L2_CID_AUDIO_MUTE: - break; - case V4L2_CID_AUDIO_VOLUME: - case V4L2_CID_AUDIO_BALANCE: - if (!(desc->flags & CHIP_HAS_VOLUME)) - return -EINVAL; - break; - case V4L2_CID_AUDIO_BASS: - case V4L2_CID_AUDIO_TREBLE: - if (!(desc->flags & CHIP_HAS_BASSTREBLE)) - return -EINVAL; - break; - default: - return -EINVAL; - } - return v4l2_ctrl_query_fill_std(qc); + chip->mode = mode; + desc->setmode(chip, mode); } - case VIDIOC_S_CTRL: - return tvaudio_set_ctrl(chip, arg); + return 0; +} - case VIDIOC_G_CTRL: - return tvaudio_get_ctrl(chip, arg); - case VIDIOC_INT_G_AUDIO_ROUTING: - { - struct v4l2_routing *rt = arg; +static int tvaudio_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct CHIPSTATE *chip = to_state(sd); + struct CHIPDESC *desc = chip->desc; + int mode = V4L2_TUNER_MODE_MONO; - rt->input = chip->input; - rt->output = 0; - break; + if (chip->radio) + return 0; + vt->audmode = chip->audmode; + vt->rxsubchans = 0; + vt->capability = V4L2_TUNER_CAP_STEREO | + V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; + + if (desc->getmode) + mode = desc->getmode(chip); + + if (mode & V4L2_TUNER_MODE_MONO) + vt->rxsubchans |= V4L2_TUNER_SUB_MONO; + if (mode & V4L2_TUNER_MODE_STEREO) + vt->rxsubchans |= V4L2_TUNER_SUB_STEREO; + /* Note: for SAP it should be mono/lang2 or stereo/lang2. + When this module is converted fully to v4l2, then this + should change for those chips that can detect SAP. */ + if (mode & V4L2_TUNER_MODE_LANG1) + vt->rxsubchans = V4L2_TUNER_SUB_LANG1 | + V4L2_TUNER_SUB_LANG2; + return 0; +} + +static int tvaudio_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct CHIPSTATE *chip = to_state(sd); + + chip->radio = 0; + return 0; +} + +static int tvaudio_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) +{ + struct CHIPSTATE *chip = to_state(sd); + struct CHIPDESC *desc = chip->desc; + + chip->mode = 0; /* automatic */ + + /* For chips that provide getmode and setmode, and doesn't + automatically follows the stereo carrier, a kthread is + created to set the audio standard. In this case, when then + the video channel is changed, tvaudio starts on MONO mode. + After waiting for 2 seconds, the kernel thread is called, + to follow whatever audio standard is pointed by the + audio carrier. + */ + if (chip->thread) { + desc->setmode(chip, V4L2_TUNER_MODE_MONO); + if (chip->prevmode != V4L2_TUNER_MODE_MONO) + chip->prevmode = -1; /* reset previous mode */ + mod_timer(&chip->wt, jiffies+msecs_to_jiffies(2000)); } - case VIDIOC_INT_S_AUDIO_ROUTING: - { - struct v4l2_routing *rt = arg; + return 0; +} - if (!(desc->flags & CHIP_HAS_INPUTSEL) || rt->input >= 4) - return -EINVAL; - /* There are four inputs: tuner, radio, extern and intern. */ - chip->input = rt->input; - if (chip->muted) - break; - chip_write_masked(chip, desc->inputreg, - desc->inputmap[chip->input], desc->inputmask); - break; +static int tvaudio_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TVAUDIO, 0); +} + +static int tvaudio_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops tvaudio_core_ops = { + .g_chip_ident = tvaudio_g_chip_ident, + .queryctrl = tvaudio_queryctrl, + .g_ctrl = tvaudio_g_ctrl, + .s_ctrl = tvaudio_s_ctrl, +}; + +static const struct v4l2_subdev_tuner_ops tvaudio_tuner_ops = { + .s_radio = tvaudio_s_radio, + .s_frequency = tvaudio_s_frequency, + .s_std = tvaudio_s_std, + .s_tuner = tvaudio_s_tuner, + .s_tuner = tvaudio_g_tuner, +}; + +static const struct v4l2_subdev_audio_ops tvaudio_audio_ops = { + .s_routing = tvaudio_s_routing, +}; + +static const struct v4l2_subdev_ops tvaudio_ops = { + .core = &tvaudio_core_ops, + .tuner = &tvaudio_tuner_ops, + .audio = &tvaudio_audio_ops, +}; + +/* ----------------------------------------------------------------------- */ + + +/* i2c registration */ + +static int tvaudio_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct CHIPSTATE *chip; + struct CHIPDESC *desc; + struct v4l2_subdev *sd; + + if (debug) { + printk(KERN_INFO "tvaudio: TV audio decoder + audio/video mux driver\n"); + printk(KERN_INFO "tvaudio: known chips: "); + for (desc = chiplist; desc->name != NULL; desc++) + printk("%s%s", (desc == chiplist) ? "" : ", ", desc->name); + printk("\n"); } - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *vt = arg; - int mode = 0; - if (chip->radio) - break; - switch (vt->audmode) { - case V4L2_TUNER_MODE_MONO: - case V4L2_TUNER_MODE_STEREO: - case V4L2_TUNER_MODE_LANG1: - case V4L2_TUNER_MODE_LANG2: - mode = vt->audmode; - break; - case V4L2_TUNER_MODE_LANG1_LANG2: - mode = V4L2_TUNER_MODE_STEREO; - break; - default: - return -EINVAL; - } - chip->audmode = vt->audmode; + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + sd = &chip->sd; + v4l2_i2c_subdev_init(sd, client, &tvaudio_ops); - if (desc->setmode && mode) { - chip->watch_stereo = 0; - /* del_timer(&chip->wt); */ - chip->mode = mode; - desc->setmode(chip, mode); - } + /* find description for the chip */ + v4l2_dbg(1, debug, sd, "chip found @ 0x%x\n", client->addr<<1); + for (desc = chiplist; desc->name != NULL; desc++) { + if (0 == *(desc->insmodopt)) + continue; + if (client->addr < desc->addr_lo || + client->addr > desc->addr_hi) + continue; + if (desc->checkit && !desc->checkit(chip)) + continue; break; } - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *vt = arg; - int mode = V4L2_TUNER_MODE_MONO; + if (desc->name == NULL) { + v4l2_dbg(1, debug, sd, "no matching chip description found\n"); + kfree(chip); + return -EIO; + } + v4l2_info(sd, "%s found @ 0x%x (%s)\n", desc->name, client->addr<<1, client->adapter->name); + if (desc->flags) { + v4l2_dbg(1, debug, sd, "matches:%s%s%s.\n", + (desc->flags & CHIP_HAS_VOLUME) ? " volume" : "", + (desc->flags & CHIP_HAS_BASSTREBLE) ? " bass/treble" : "", + (desc->flags & CHIP_HAS_INPUTSEL) ? " audiomux" : ""); + } - if (chip->radio) - break; - vt->audmode = chip->audmode; - vt->rxsubchans = 0; - vt->capability = V4L2_TUNER_CAP_STEREO | - V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; + /* fill required data structures */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) + strlcpy(client->name, desc->name, I2C_NAME_SIZE); +#else + if (!id) + strlcpy(client->name, desc->name, I2C_NAME_SIZE); +#endif + chip->desc = desc; + chip->shadow.count = desc->registers+1; + chip->prevmode = -1; + chip->audmode = V4L2_TUNER_MODE_LANG1; - if (desc->getmode) - mode = desc->getmode(chip); + /* initialization */ + if (desc->initialize != NULL) + desc->initialize(chip); + else + chip_cmd(chip, "init", &desc->init); - if (mode & V4L2_TUNER_MODE_MONO) - vt->rxsubchans |= V4L2_TUNER_SUB_MONO; - if (mode & V4L2_TUNER_MODE_STEREO) - vt->rxsubchans |= V4L2_TUNER_SUB_STEREO; - /* Note: for SAP it should be mono/lang2 or stereo/lang2. - When this module is converted fully to v4l2, then this - should change for those chips that can detect SAP. */ - if (mode & V4L2_TUNER_MODE_LANG1) - vt->rxsubchans = V4L2_TUNER_SUB_LANG1 | - V4L2_TUNER_SUB_LANG2; - break; + if (desc->flags & CHIP_HAS_VOLUME) { + if (!desc->volfunc) { + /* This shouldn't be happen. Warn user, but keep working + without volume controls + */ + v4l2_info(sd, "volume callback undefined!\n"); + desc->flags &= ~CHIP_HAS_VOLUME; + } else { + chip->left = desc->leftinit ? desc->leftinit : 65535; + chip->right = desc->rightinit ? desc->rightinit : 65535; + chip_write(chip, desc->leftreg, + desc->volfunc(chip->left)); + chip_write(chip, desc->rightreg, + desc->volfunc(chip->right)); + } } - case VIDIOC_S_STD: - chip->radio = 0; - break; - case VIDIOC_S_FREQUENCY: - chip->mode = 0; /* automatic */ - - /* For chips that provide getmode and setmode, and doesn't - automatically follows the stereo carrier, a kthread is - created to set the audio standard. In this case, when then - the video channel is changed, tvaudio starts on MONO mode. - After waiting for 2 seconds, the kernel thread is called, - to follow whatever audio standard is pointed by the - audio carrier. - */ - if (chip->thread) { - desc->setmode(chip,V4L2_TUNER_MODE_MONO); - if (chip->prevmode != V4L2_TUNER_MODE_MONO) - chip->prevmode = -1; /* reset previous mode */ - mod_timer(&chip->wt, jiffies+msecs_to_jiffies(2000)); + if (desc->flags & CHIP_HAS_BASSTREBLE) { + if (!desc->bassfunc || !desc->treblefunc) { + /* This shouldn't be happen. Warn user, but keep working + without bass/treble controls + */ + v4l2_info(sd, "bass/treble callbacks undefined!\n"); + desc->flags &= ~CHIP_HAS_BASSTREBLE; + } else { + chip->treble = desc->trebleinit ? + desc->trebleinit : 32768; + chip->bass = desc->bassinit ? + desc->bassinit : 32768; + chip_write(chip, desc->bassreg, + desc->bassfunc(chip->bass)); + chip_write(chip, desc->treblereg, + desc->treblefunc(chip->treble)); } - break; + } + + chip->thread = NULL; + if (desc->flags & CHIP_NEED_CHECKMODE) { + if (!desc->getmode || !desc->setmode) { + /* This shouldn't be happen. Warn user, but keep working + without kthread + */ + v4l2_info(sd, "set/get mode callbacks undefined!\n"); + return 0; + } + /* start async thread */ + init_timer(&chip->wt); + chip->wt.function = chip_thread_wake; + chip->wt.data = (unsigned long)chip; + chip->thread = kthread_run(chip_thread, chip, client->name); + if (IS_ERR(chip->thread)) { + v4l2_warn(sd, "failed to create kthread\n"); + chip->thread = NULL; + } + } + return 0; +} + +static int tvaudio_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct CHIPSTATE *chip = to_state(sd); - case VIDIOC_G_CHIP_IDENT: - return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_TVAUDIO, 0); + del_timer_sync(&chip->wt); + if (chip->thread) { + /* shutdown async thread */ + kthread_stop(chip->thread); + chip->thread = NULL; } + + v4l2_device_unregister_subdev(sd); + kfree(chip); return 0; } -static int chip_legacy_probe(struct i2c_adapter *adap) +static int tvaudio_legacy_probe(struct i2c_adapter *adap) { /* don't attach on saa7146 based cards, because dedicated drivers are used */ @@ -1907,21 +1966,21 @@ static int chip_legacy_probe(struct i2c_adapter *adap) /* This driver supports many devices and the idea is to let the driver detect which device is present. So rather than listing all supported devices here, we pretend to support a single, fake device type. */ -static const struct i2c_device_id chip_id[] = { +static const struct i2c_device_id tvaudio_id[] = { { "tvaudio", 0 }, { } }; -MODULE_DEVICE_TABLE(i2c, chip_id); +MODULE_DEVICE_TABLE(i2c, tvaudio_id); #endif static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "tvaudio", .driverid = I2C_DRIVERID_TVAUDIO, - .command = chip_command, - .probe = chip_probe, - .remove = chip_remove, - .legacy_probe = chip_legacy_probe, + .command = tvaudio_command, + .probe = tvaudio_probe, + .remove = tvaudio_remove, + .legacy_probe = tvaudio_legacy_probe, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) - .id_table = chip_id, + .id_table = tvaudio_id, #endif }; -- cgit v1.2.3 From 3fa4127b747284ddb0986e285315094ffb1362c5 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 18 Dec 2008 16:27:28 +0100 Subject: v4l2-subdev: ioctl ops should use unsigned for cmd arg. From: Hans Verkuil Priority: normal Signed-off-by: Hans Verkuil --- linux/drivers/media/video/msp3400-driver.c | 2 +- linux/drivers/media/video/tuner-core.c | 2 +- linux/include/media/v4l2-subdev.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/msp3400-driver.c b/linux/drivers/media/video/msp3400-driver.c index def15e381..9d2c98b82 100644 --- a/linux/drivers/media/video/msp3400-driver.c +++ b/linux/drivers/media/video/msp3400-driver.c @@ -488,7 +488,7 @@ static int msp_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) } #ifdef CONFIG_VIDEO_ALLOW_V4L1 -static int msp_ioctl(struct v4l2_subdev *sd, int cmd, void *arg) +static int msp_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) { struct msp_state *state = to_state(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/linux/drivers/media/video/tuner-core.c b/linux/drivers/media/video/tuner-core.c index e649f2eb7..3e674ad0e 100644 --- a/linux/drivers/media/video/tuner-core.c +++ b/linux/drivers/media/video/tuner-core.c @@ -806,7 +806,7 @@ static int tuner_s_standby(struct v4l2_subdev *sd, u32 standby) } #ifdef CONFIG_VIDEO_ALLOW_V4L1 -static int tuner_ioctl(struct v4l2_subdev *sd, int cmd, void *arg) +static int tuner_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) { struct tuner *t = to_tuner(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); diff --git a/linux/include/media/v4l2-subdev.h b/linux/include/media/v4l2-subdev.h index bca25e8ea..ceef016bb 100644 --- a/linux/include/media/v4l2-subdev.h +++ b/linux/include/media/v4l2-subdev.h @@ -79,7 +79,7 @@ struct v4l2_subdev_core_ops { int (*g_ctrl)(struct v4l2_subdev *sd, struct v4l2_control *ctrl); int (*s_ctrl)(struct v4l2_subdev *sd, struct v4l2_control *ctrl); int (*querymenu)(struct v4l2_subdev *sd, struct v4l2_querymenu *qm); - int (*ioctl)(struct v4l2_subdev *sd, int cmd, void *arg); + int (*ioctl)(struct v4l2_subdev *sd, unsigned int cmd, void *arg); #ifdef CONFIG_VIDEO_ADV_DEBUG int (*g_register)(struct v4l2_subdev *sd, struct v4l2_register *reg); int (*s_register)(struct v4l2_subdev *sd, struct v4l2_register *reg); -- cgit v1.2.3 From dc5868e91136a22b5899beae9487121c964a3666 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 18 Dec 2008 16:29:07 +0100 Subject: tea6415c: convert to v4l2_subdev. From: Hans Verkuil Priority: normal Signed-off-by: Hans Verkuil --- linux/drivers/media/video/tea6415c.c | 49 ++++++++++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 11 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/tea6415c.c b/linux/drivers/media/video/tea6415c.c index 073f020fa..21900def4 100644 --- a/linux/drivers/media/video/tea6415c.c +++ b/linux/drivers/media/video/tea6415c.c @@ -31,7 +31,7 @@ #include #include #include -#include +#include #include #include "tea6415c.h" #include "compat.h" @@ -123,31 +123,57 @@ static int switch_matrix(struct i2c_client *client, int i, int o) return ret; } -static int tea6415c_command(struct i2c_client *client, unsigned cmd, void *arg) +static int tea6415c_ioctl(struct v4l2_subdev *sd, unsigned cmd, void *arg) { - struct tea6415c_multiplex *v = (struct tea6415c_multiplex *)arg; - int result = 0; + if (cmd == TEA6415C_SWITCH) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct tea6415c_multiplex *v = (struct tea6415c_multiplex *)arg; - switch (cmd) { - case TEA6415C_SWITCH: - result = switch_matrix(client, v->in, v->out); - break; - default: - return -ENOIOCTLCMD; + return switch_matrix(client, v->in, v->out); } - return result; + return -ENOIOCTLCMD; } +static int tea6415c_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops tea6415c_core_ops = { + .ioctl = tea6415c_ioctl, +}; + +static const struct v4l2_subdev_ops tea6415c_ops = { + .core = &tea6415c_core_ops, +}; + /* this function is called by i2c_probe */ static int tea6415c_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct v4l2_subdev *sd; + /* let's see whether this adapter can support what we need */ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE)) return 0; v4l_info(client, "chip found @ 0x%x (%s)\n", client->addr << 1, client->adapter->name); + sd = kmalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); + if (sd == NULL) + return -ENOMEM; + v4l2_i2c_subdev_init(sd, client, &tea6415c_ops); + return 0; +} + +static int tea6415c_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(sd); return 0; } @@ -171,6 +197,7 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = { .driverid = I2C_DRIVERID_TEA6415C, .command = tea6415c_command, .probe = tea6415c_probe, + .remove = tea6415c_remove, .legacy_probe = tea6415c_legacy_probe, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) .id_table = tea6415c_id, -- cgit v1.2.3 From 2a05d665a9177e46028b495558a77321a718a19d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 18 Dec 2008 16:33:45 +0100 Subject: tea6420: convert to v4l2_subdev. From: Hans Verkuil Priority: normal Signed-off-by: Hans Verkuil --- linux/drivers/media/video/tea6420.c | 49 ++++++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 12 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/tea6420.c b/linux/drivers/media/video/tea6420.c index 8ded72ade..aee419402 100644 --- a/linux/drivers/media/video/tea6420.c +++ b/linux/drivers/media/video/tea6420.c @@ -31,7 +31,7 @@ #include #include #include -#include +#include #include #include "tea6420.h" #include "compat.h" @@ -91,26 +91,37 @@ static int tea6420_switch(struct i2c_client *client, int i, int o, int g) return 0; } -static int tea6420_command(struct i2c_client *client, unsigned cmd, void *arg) +static int tea6420_ioctl(struct v4l2_subdev *sd, unsigned cmd, void *arg) { - struct tea6420_multiplex *a = (struct tea6420_multiplex *)arg; - int result = 0; + if (cmd == TEA6420_SWITCH) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct tea6420_multiplex *a = (struct tea6420_multiplex *)arg; - switch (cmd) { - case TEA6420_SWITCH: - result = tea6420_switch(client, a->in, a->out, a->gain); - break; - default: - return -ENOIOCTLCMD; + return tea6420_switch(client, a->in, a->out, a->gain); } + return -ENOIOCTLCMD; +} - return result; +static int tea6420_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); } +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops tea6420_core_ops = { + .ioctl = tea6420_ioctl, +}; + +static const struct v4l2_subdev_ops tea6420_ops = { + .core = &tea6420_core_ops, +}; + /* this function is called by i2c_probe */ static int tea6420_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct v4l2_subdev *sd; int err, i; /* let's see whether this adapter can support what we need */ @@ -127,9 +138,22 @@ static int tea6420_probe(struct i2c_client *client, } if (err) { v4l_dbg(1, debug, client, "could not initialize tea6420\n"); - kfree(client); return -ENODEV; } + + sd = kmalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); + if (sd == NULL) + return -ENOMEM; + v4l2_i2c_subdev_init(sd, client, &tea6420_ops); + return 0; +} + +static int tea6420_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(sd); return 0; } @@ -153,6 +177,7 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = { .driverid = I2C_DRIVERID_TEA6420, .command = tea6420_command, .probe = tea6420_probe, + .remove = tea6420_remove, .legacy_probe = tea6420_legacy_probe, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) .id_table = tea6420_id, -- cgit v1.2.3 From 4578c8c9e7298823a34019e02afd73d971971a37 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Thu, 18 Dec 2008 16:38:15 +0100 Subject: gspca - spca561: Control changes for Rev72a. From: Jean-Francois Moine - move the rev12a sequence from setcontrast to end of start - add the white balance control for rev72a - adjust the contrast formula Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/spca561.c | 141 ++++++++++++++++++------------ 1 file changed, 85 insertions(+), 56 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/spca561.c b/linux/drivers/media/video/gspca/spca561.c index ee24629f0..6114ac5f7 100644 --- a/linux/drivers/media/video/gspca/spca561.c +++ b/linux/drivers/media/video/gspca/spca561.c @@ -32,22 +32,22 @@ MODULE_LICENSE("GPL"); struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - __u16 contrast; /* rev72a only */ -#define CONTRAST_MIN 0x0000 -#define CONTRAST_DEF 0x2000 -#define CONTRAST_MAX 0x3fff - __u16 exposure; /* rev12a only */ #define EXPOSURE_MIN 1 #define EXPOSURE_DEF 200 #define EXPOSURE_MAX (4095 - 900) /* see set_exposure */ + __u8 contrast; /* rev72a only */ +#define CONTRAST_MIN 0x00 +#define CONTRAST_DEF 0x20 +#define CONTRAST_MAX 0x3f + __u8 brightness; /* rev72a only */ #define BRIGHTNESS_MIN 0 -#define BRIGHTNESS_DEF 32 -#define BRIGHTNESS_MAX 63 +#define BRIGHTNESS_DEF 0x20 +#define BRIGHTNESS_MAX 0x3f - __u8 white; /* rev12a only */ + __u8 white; #define WHITE_MIN 1 #define WHITE_DEF 0x40 #define WHITE_MAX 0x7f @@ -218,10 +218,18 @@ static const __u16 rev72a_init_data[][2] = { {0x0004, 0x8612}, /* Gr offset for white balance */ {0x0007, 0x8613}, /* B offset for white balance */ {0x0000, 0x8614}, /* Gb offset for white balance */ +#if 1 +/* from ms-win */ + {0x0035, 0x8651}, /* R gain for white balance */ + {0x0040, 0x8652}, /* Gr gain for white balance */ + {0x005f, 0x8653}, /* B gain for white balance */ + {0x0040, 0x8654}, /* Gb gain for white balance */ +#else {0x008c, 0x8651}, /* R gain for white balance */ {0x008c, 0x8652}, /* Gr gain for white balance */ {0x00b5, 0x8653}, /* B gain for white balance */ {0x008c, 0x8654}, /* Gb gain for white balance */ +#endif {0x0002, 0x8502}, /* Maximum average bit rate stuff */ {0x0011, 0x8802}, @@ -591,46 +599,60 @@ static int sd_init_72a(struct gspca_dev *gspca_dev) return 0; } -static void setcontrast(struct gspca_dev *gspca_dev) +/* rev 72a only */ +static void setbrightness(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; struct usb_device *dev = gspca_dev->dev; - __u8 lowb; + __u8 value; - switch (sd->chip_revision) { - case Rev072A: - lowb = sd->contrast >> 8; - reg_w_val(dev, 0x8651, lowb); - reg_w_val(dev, 0x8652, lowb); - reg_w_val(dev, 0x8653, lowb); - reg_w_val(dev, 0x8654, lowb); - break; - default: { -/* case Rev012A: { */ - static const __u8 Reg8391[] = - { 0x92, 0x30, 0x20, 0x00, 0x0c, 0x00, 0x00, 0x00 }; - - memcpy(gspca_dev->usb_buf, Reg8391, 8); - reg_w_buf(gspca_dev, 0x8391, 8); - reg_w_buf(gspca_dev, 0x8390, 8); - break; - } - } + value = sd->brightness; + + /* offsets for white balance */ + reg_w_val(dev, 0x8611, value); /* R */ + reg_w_val(dev, 0x8612, value); /* Gr */ + reg_w_val(dev, 0x8613, value); /* B */ + reg_w_val(dev, 0x8614, value); /* Gb */ } -/* rev12a only */ static void setwhite(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; __u16 white; - __u8 reg8614, reg8616; + __u8 blue, red; + __u16 reg; - white = sd->white; /* try to emulate MS-win as possible */ - reg8616 = 0x90 - white * 5 / 8; - reg_w_val(gspca_dev->dev, 0x8616, reg8616); - reg8614 = 0x20 + white * 3 / 8; - reg_w_val(gspca_dev->dev, 0x8614, reg8614); + white = sd->white; + red = 0x20 + white * 3 / 8; + blue = 0x90 - white * 5 / 8; + if (sd->chip_revision == Rev012A) { + reg = 0x8614; + } else { + reg = 0x8651; + red += sd->contrast - 0x20; + blue += sd->contrast - 0x20; + } + reg_w_val(gspca_dev->dev, reg, red); + reg_w_val(gspca_dev->dev, reg + 2, blue); +} + +static void setcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + __u8 value; + + if (sd->chip_revision != Rev072A) + return; + value = sd->contrast + 0x20; + + /* gains for white balance */ + setwhite(gspca_dev); +/* reg_w_val(dev, 0x8651, value); * R - done by setwhite */ + reg_w_val(dev, 0x8652, value); /* Gr */ +/* reg_w_val(dev, 0x8653, value); * B - done by setwhite */ + reg_w_val(dev, 0x8654, value); /* Gb */ } /* rev 12a only */ @@ -690,9 +712,9 @@ static void setautogain(struct gspca_dev *gspca_dev) static int sd_start_12a(struct gspca_dev *gspca_dev) { struct usb_device *dev = gspca_dev->dev; - int Clck = 0x8a; /* lower 0x8X values lead to fps > 30 */ - __u8 Reg8307[] = { 0xaa, 0x00 }; int mode; + static const __u8 Reg8391[8] = + {0x92, 0x30, 0x20, 0x00, 0x0c, 0x00, 0x00, 0x00}; mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; if (mode <= 1) { @@ -705,15 +727,20 @@ static int sd_start_12a(struct gspca_dev *gspca_dev) reg_w_val(dev, 0x8500, mode); } /* -- qq@kuku.eu.org */ - memcpy(gspca_dev->usb_buf, Reg8307, sizeof Reg8307); - reg_w_buf(gspca_dev, 0x8307, sizeof Reg8307); - reg_w_val(gspca_dev->dev, 0x8700, Clck); + gspca_dev->usb_buf[0] = 0xaa; + gspca_dev->usb_buf[1] = 0x00; + reg_w_buf(gspca_dev, 0x8307, 2); + /* clock - lower 0x8X values lead to fps > 30 */ + reg_w_val(gspca_dev->dev, 0x8700, 0x8a); /* 0x8f 0x85 0x27 clock */ reg_w_val(gspca_dev->dev, 0x8112, 0x1e | 0x20); reg_w_val(gspca_dev->dev, 0x850b, 0x03); - setcontrast(gspca_dev); + memcpy(gspca_dev->usb_buf, Reg8391, 8); + reg_w_buf(gspca_dev, 0x8391, 8); + reg_w_buf(gspca_dev, 0x8390, 8); setwhite(gspca_dev); setautogain(gspca_dev); +/* setgain(gspca_dev); */ setexposure(gspca_dev); return 0; } @@ -740,6 +767,9 @@ static int sd_start_72a(struct gspca_dev *gspca_dev) reg_w_val(dev, 0x8500, mode); /* mode */ reg_w_val(dev, 0x8700, Clck); /* 0x27 clock */ reg_w_val(dev, 0x8112, 0x10 | 0x20); + setcontrast(gspca_dev); +/* setbrightness(gspca_dev); * fixme: bad values */ + setwhite(gspca_dev); setautogain(gspca_dev); return 0; } @@ -887,19 +917,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); } -/* rev 72a only */ -static void setbrightness(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - __u8 value; - - value = sd->brightness; - reg_w_val(gspca_dev->dev, 0x8611, value); - reg_w_val(gspca_dev->dev, 0x8612, value); - reg_w_val(gspca_dev->dev, 0x8613, value); - reg_w_val(gspca_dev->dev, 0x8614, value); -} - /* rev 72a only */ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) { @@ -956,7 +973,6 @@ static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) return 0; } -/* rev12a only */ static int sd_setwhite(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; @@ -1070,6 +1086,19 @@ static struct ctrl sd_ctrls_12a[] = { }; static struct ctrl sd_ctrls_72a[] = { + { + { + .id = V4L2_CID_DO_WHITE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "White Balance", + .minimum = WHITE_MIN, + .maximum = WHITE_MAX, + .step = 1, + .default_value = WHITE_DEF, + }, + .set = sd_setwhite, + .get = sd_getwhite, + }, { { .id = V4L2_CID_BRIGHTNESS, -- cgit v1.2.3 From e8f15f9a8804dc9ff8ae2642c12cd5fbcaba4655 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 18 Dec 2008 16:43:56 +0100 Subject: tlv320aic23b: convert to v4l2_subdev. From: Hans Verkuil Priority: normal Signed-off-by: Hans Verkuil --- linux/drivers/media/video/tlv320aic23b.c | 141 +++++++++++++++++++------------ 1 file changed, 87 insertions(+), 54 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/tlv320aic23b.c b/linux/drivers/media/video/tlv320aic23b.c index a531a46d1..ecc3a4ef6 100644 --- a/linux/drivers/media/video/tlv320aic23b.c +++ b/linux/drivers/media/video/tlv320aic23b.c @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include #include "compat.h" @@ -45,15 +45,22 @@ I2C_CLIENT_INSMOD; /* ----------------------------------------------------------------------- */ struct tlv320aic23b_state { + struct v4l2_subdev sd; u8 muted; }; -static int tlv320aic23b_write(struct i2c_client *client, int reg, u16 val) +static inline struct tlv320aic23b_state *to_state(struct v4l2_subdev *sd) { + return container_of(sd, struct tlv320aic23b_state, sd); +} + +static int tlv320aic23b_write(struct v4l2_subdev *sd, int reg, u16 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); int i; if ((reg < 0 || reg > 9) && (reg != 15)) { - v4l_err(client, "Invalid register R%d\n", reg); + v4l2_err(sd, "Invalid register R%d\n", reg); return -1; } @@ -61,61 +68,82 @@ static int tlv320aic23b_write(struct i2c_client *client, int reg, u16 val) if (i2c_smbus_write_byte_data(client, (reg << 1) | (val >> 8), val & 0xff) == 0) return 0; - v4l_err(client, "I2C: cannot write %03x to register R%d\n", val, reg); + v4l2_err(sd, "I2C: cannot write %03x to register R%d\n", val, reg); return -1; } -static int tlv320aic23b_command(struct i2c_client *client, - unsigned int cmd, void *arg) +static int tlv320aic23b_s_clock_freq(struct v4l2_subdev *sd, u32 freq) { - struct tlv320aic23b_state *state = i2c_get_clientdata(client); - struct v4l2_control *ctrl = arg; - u32 *freq = arg; - - switch (cmd) { - case VIDIOC_INT_AUDIO_CLOCK_FREQ: - switch (*freq) { - case 32000: /* set sample rate to 32 kHz */ - tlv320aic23b_write(client, 8, 0x018); - break; - case 44100: /* set sample rate to 44.1 kHz */ - tlv320aic23b_write(client, 8, 0x022); - break; - case 48000: /* set sample rate to 48 kHz */ - tlv320aic23b_write(client, 8, 0x000); - break; - default: - return -EINVAL; - } - break; - - case VIDIOC_G_CTRL: - if (ctrl->id != V4L2_CID_AUDIO_MUTE) - return -EINVAL; - ctrl->value = state->muted; + switch (freq) { + case 32000: /* set sample rate to 32 kHz */ + tlv320aic23b_write(sd, 8, 0x018); break; - - case VIDIOC_S_CTRL: - if (ctrl->id != V4L2_CID_AUDIO_MUTE) - return -EINVAL; - state->muted = ctrl->value; - tlv320aic23b_write(client, 0, 0x180); /* mute both channels */ - /* set gain on both channels to +3.0 dB */ - if (!state->muted) - tlv320aic23b_write(client, 0, 0x119); + case 44100: /* set sample rate to 44.1 kHz */ + tlv320aic23b_write(sd, 8, 0x022); break; - - case VIDIOC_LOG_STATUS: - v4l_info(client, "Input: %s\n", - state->muted ? "muted" : "active"); + case 48000: /* set sample rate to 48 kHz */ + tlv320aic23b_write(sd, 8, 0x000); break; - default: return -EINVAL; } return 0; } +static int tlv320aic23b_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct tlv320aic23b_state *state = to_state(sd); + + if (ctrl->id != V4L2_CID_AUDIO_MUTE) + return -EINVAL; + ctrl->value = state->muted; + return 0; +} + +static int tlv320aic23b_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct tlv320aic23b_state *state = to_state(sd); + + if (ctrl->id != V4L2_CID_AUDIO_MUTE) + return -EINVAL; + state->muted = ctrl->value; + tlv320aic23b_write(sd, 0, 0x180); /* mute both channels */ + /* set gain on both channels to +3.0 dB */ + if (!state->muted) + tlv320aic23b_write(sd, 0, 0x119); + return 0; +} + +static int tlv320aic23b_log_status(struct v4l2_subdev *sd) +{ + struct tlv320aic23b_state *state = to_state(sd); + + v4l2_info(sd, "Input: %s\n", state->muted ? "muted" : "active"); + return 0; +} + +static int tlv320aic23b_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops tlv320aic23b_core_ops = { + .log_status = tlv320aic23b_log_status, + .g_ctrl = tlv320aic23b_g_ctrl, + .s_ctrl = tlv320aic23b_s_ctrl, +}; + +static const struct v4l2_subdev_audio_ops tlv320aic23b_audio_ops = { + .s_clock_freq = tlv320aic23b_s_clock_freq, +}; + +static const struct v4l2_subdev_ops tlv320aic23b_ops = { + .core = &tlv320aic23b_core_ops, + .audio = &tlv320aic23b_audio_ops, +}; + /* ----------------------------------------------------------------------- */ /* i2c implementation */ @@ -129,6 +157,7 @@ static int tlv320aic23b_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct tlv320aic23b_state *state; + struct v4l2_subdev *sd; /* Check if the adapter supports the needed features */ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) @@ -137,32 +166,36 @@ static int tlv320aic23b_probe(struct i2c_client *client, v4l_info(client, "chip found @ 0x%x (%s)\n", client->addr << 1, client->adapter->name); - state = kmalloc(sizeof(struct tlv320aic23b_state), GFP_KERNEL); + state = kzalloc(sizeof(struct tlv320aic23b_state), GFP_KERNEL); if (state == NULL) return -ENOMEM; + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &tlv320aic23b_ops); state->muted = 0; - i2c_set_clientdata(client, state); /* Initialize tlv320aic23b */ /* RESET */ - tlv320aic23b_write(client, 15, 0x000); + tlv320aic23b_write(sd, 15, 0x000); /* turn off DAC & mic input */ - tlv320aic23b_write(client, 6, 0x00A); + tlv320aic23b_write(sd, 6, 0x00A); /* left-justified, 24-bit, master mode */ - tlv320aic23b_write(client, 7, 0x049); + tlv320aic23b_write(sd, 7, 0x049); /* set gain on both channels to +3.0 dB */ - tlv320aic23b_write(client, 0, 0x119); + tlv320aic23b_write(sd, 0, 0x119); /* set sample rate to 48 kHz */ - tlv320aic23b_write(client, 8, 0x000); + tlv320aic23b_write(sd, 8, 0x000); /* activate digital interface */ - tlv320aic23b_write(client, 9, 0x001); + tlv320aic23b_write(sd, 9, 0x001); return 0; } static int tlv320aic23b_remove(struct i2c_client *client) { - kfree(i2c_get_clientdata(client)); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); return 0; } -- cgit v1.2.3 From 22fd18c49b72180be0a26826a73d0db1c0b3e272 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 18 Dec 2008 17:04:05 +0100 Subject: tda7432: convert to v4l2_subdev. From: Hans Verkuil Priority: normal Signed-off-by: Hans Verkuil --- linux/drivers/media/video/tda7432.c | 267 +++++++++++++++--------------------- 1 file changed, 114 insertions(+), 153 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/tda7432.c b/linux/drivers/media/video/tda7432.c index 375b8d6d2..64a90a71b 100644 --- a/linux/drivers/media/video/tda7432.c +++ b/linux/drivers/media/video/tda7432.c @@ -47,9 +47,10 @@ #include #include -#include +#include #include #include +#include #include "compat.h" #ifndef VIDEO_AUDIO_BALANCE @@ -80,6 +81,7 @@ I2C_CLIENT_INSMOD; /* Structure of address and subaddresses for the tda7432 */ struct tda7432 { + struct v4l2_subdev sd; int addr; int input; int volume; @@ -87,10 +89,12 @@ struct tda7432 { int bass, treble; int lf, lr, rf, rr; int loud; - struct i2c_client c; }; -static struct i2c_driver driver; -static struct i2c_client client_template; + +static inline struct tda7432 *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct tda7432, sd); +} /* The TDA7432 is made by STS-Thompson * http://www.st.com @@ -225,45 +229,33 @@ static struct i2c_client client_template; /* Begin code */ -static int tda7432_write(struct i2c_client *client, int subaddr, int val) +static int tda7432_write(struct v4l2_subdev *sd, int subaddr, int val) { + struct i2c_client *client = v4l2_get_subdevdata(sd); unsigned char buffer[2]; - v4l_dbg(2, debug,client,"In tda7432_write\n"); - v4l_dbg(1, debug,client,"Writing %d 0x%x\n", subaddr, val); + + v4l2_dbg(2, debug, sd, "In tda7432_write\n"); + v4l2_dbg(1, debug, sd, "Writing %d 0x%x\n", subaddr, val); buffer[0] = subaddr; buffer[1] = val; - if (2 != i2c_master_send(client,buffer,2)) { - v4l_err(client,"I/O error, trying (write %d 0x%x)\n", + if (2 != i2c_master_send(client, buffer, 2)) { + v4l2_err(sd, "I/O error, trying (write %d 0x%x)\n", subaddr, val); return -1; } return 0; } -/* I don't think we ever actually _read_ the chip... */ -#if 0 -static int tda7432_read(struct i2c_client *client) -{ - unsigned char buffer; - v4l_dbg(2, debug,client,"In tda7432_read\n"); - if (1 != i2c_master_recv(client,&buffer,1)) { - v4l_err(client,"I/O error, trying (read)\n"); - return -1; - } - v4l_dbg(1, debug,client,"Read 0x%02x\n", buffer); - return buffer; -} -#endif - -static int tda7432_set(struct i2c_client *client) +static int tda7432_set(struct v4l2_subdev *sd) { - struct tda7432 *t = i2c_get_clientdata(client); + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct tda7432 *t = to_state(sd); unsigned char buf[16]; - v4l_dbg(2, debug,client,"In tda7432_set\n"); - v4l_dbg(1, debug,client, + v4l2_dbg(1, debug, sd, "tda7432: 7432_set(0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x)\n", - t->input,t->volume,t->bass,t->treble,t->lf,t->lr,t->rf,t->rr,t->loud); + t->input, t->volume, t->bass, t->treble, t->lf, t->lr, + t->rf, t->rr, t->loud); buf[0] = TDA7432_IN; buf[1] = t->input; buf[2] = t->volume; @@ -274,18 +266,19 @@ static int tda7432_set(struct i2c_client *client) buf[7] = t->rf; buf[8] = t->rr; buf[9] = t->loud; - if (10 != i2c_master_send(client,buf,10)) { - v4l_err(client,"I/O error, trying tda7432_set\n"); + if (10 != i2c_master_send(client, buf, 10)) { + v4l2_err(sd, "I/O error, trying tda7432_set\n"); return -1; } return 0; } -static void do_tda7432_init(struct i2c_client *client) +static void do_tda7432_init(struct v4l2_subdev *sd) { - struct tda7432 *t = i2c_get_clientdata(client); - v4l_dbg(2, debug,client,"In tda7432_init\n"); + struct tda7432 *t = to_state(sd); + + v4l2_dbg(2, debug, sd, "In tda7432_init\n"); t->input = TDA7432_STEREO_IN | /* Main (stereo) input */ TDA7432_BASS_SYM | /* Symmetric bass cut */ @@ -302,57 +295,12 @@ static void do_tda7432_init(struct i2c_client *client) t->rr = TDA7432_ATTEN_0DB; /* 0dB attenuation */ t->loud = loudness; /* insmod parameter */ - tda7432_set(client); -} - -/* *********************** * - * i2c interface functions * - * *********************** */ - -static int tda7432_attach(struct i2c_adapter *adap, int addr, int kind) -{ - struct tda7432 *t; - struct i2c_client *client; - - t = kzalloc(sizeof *t,GFP_KERNEL); - if (!t) - return -ENOMEM; - - client = &t->c; - memcpy(client,&client_template,sizeof(struct i2c_client)); - client->adapter = adap; - client->addr = addr; - i2c_set_clientdata(client, t); - - do_tda7432_init(client); - i2c_attach_client(client); - - v4l_info(client, "chip found @ 0x%x (%s)\n", addr << 1, adap->name); - return 0; -} - -static int tda7432_probe(struct i2c_adapter *adap) -{ - if (adap->class & I2C_CLASS_TV_ANALOG) - return i2c_probe(adap, &addr_data, tda7432_attach); - return 0; -} - -static int tda7432_detach(struct i2c_client *client) -{ - struct tda7432 *t = i2c_get_clientdata(client); - - do_tda7432_init(client); - i2c_detach_client(client); - - kfree(t); - return 0; + tda7432_set(sd); } -static int tda7432_get_ctrl(struct i2c_client *client, - struct v4l2_control *ctrl) +static int tda7432_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct tda7432 *t = i2c_get_clientdata(client); + struct tda7432 *t = to_state(sd); switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: @@ -396,10 +344,9 @@ static int tda7432_get_ctrl(struct i2c_client *client, return -EINVAL; } -static int tda7432_set_ctrl(struct i2c_client *client, - struct v4l2_control *ctrl) +static int tda7432_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct tda7432 *t = i2c_get_clientdata(client); + struct tda7432 *t = to_state(sd); switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: @@ -414,7 +361,7 @@ static int tda7432_set_ctrl(struct i2c_client *client, if (loudness) /* Turn on the loudness bit */ t->volume |= TDA7432_LD_ON; - tda7432_write(client,TDA7432_VL, t->volume); + tda7432_write(sd, TDA7432_VL, t->volume); return 0; case V4L2_CID_AUDIO_BALANCE: if (ctrl->value < 32768) { @@ -442,14 +389,14 @@ static int tda7432_set_ctrl(struct i2c_client *client, if(t->bass>= 0x8) t->bass = (~t->bass & 0xf) + 0x8 ; - tda7432_write(client,TDA7432_TN, 0x10 | (t->bass << 4) | t->treble ); + tda7432_write(sd, TDA7432_TN, 0x10 | (t->bass << 4) | t->treble); return 0; case V4L2_CID_AUDIO_TREBLE: t->treble= ctrl->value >> 12; if(t->treble>= 0x8) t->treble = (~t->treble & 0xf) + 0x8 ; - tda7432_write(client,TDA7432_TN, 0x10 | (t->bass << 4) | t->treble ); + tda7432_write(sd, TDA7432_TN, 0x10 | (t->bass << 4) | t->treble); return 0; default: return -EINVAL; @@ -459,92 +406,106 @@ static int tda7432_set_ctrl(struct i2c_client *client, if (t->muted) { /* Mute & update balance*/ - tda7432_write(client,TDA7432_LF, t->lf | TDA7432_MUTE); - tda7432_write(client,TDA7432_LR, t->lr | TDA7432_MUTE); - tda7432_write(client,TDA7432_RF, t->rf | TDA7432_MUTE); - tda7432_write(client,TDA7432_RR, t->rr | TDA7432_MUTE); + tda7432_write(sd, TDA7432_LF, t->lf | TDA7432_MUTE); + tda7432_write(sd, TDA7432_LR, t->lr | TDA7432_MUTE); + tda7432_write(sd, TDA7432_RF, t->rf | TDA7432_MUTE); + tda7432_write(sd, TDA7432_RR, t->rr | TDA7432_MUTE); } else { - tda7432_write(client,TDA7432_LF, t->lf); - tda7432_write(client,TDA7432_LR, t->lr); - tda7432_write(client,TDA7432_RF, t->rf); - tda7432_write(client,TDA7432_RR, t->rr); + tda7432_write(sd, TDA7432_LF, t->lf); + tda7432_write(sd, TDA7432_LR, t->lr); + tda7432_write(sd, TDA7432_RF, t->rf); + tda7432_write(sd, TDA7432_RR, t->rr); } return 0; } -static int tda7432_command(struct i2c_client *client, - unsigned int cmd, void *arg) +static int tda7432_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) { - v4l_dbg(2, debug,client,"In tda7432_command\n"); - if (debug>1) - v4l_i2c_print_ioctl(client,cmd); - - switch (cmd) { - /* --- v4l ioctls --- */ - /* take care: bttv does userspace copying, we'll get a - kernel pointer here... */ - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; - - switch (qc->id) { - case V4L2_CID_AUDIO_MUTE: - case V4L2_CID_AUDIO_VOLUME: - case V4L2_CID_AUDIO_BALANCE: - case V4L2_CID_AUDIO_BASS: - case V4L2_CID_AUDIO_TREBLE: - default: - return -EINVAL; - } + switch (qc->id) { + case V4L2_CID_AUDIO_MUTE: + case V4L2_CID_AUDIO_VOLUME: + case V4L2_CID_AUDIO_BALANCE: + case V4L2_CID_AUDIO_BASS: + case V4L2_CID_AUDIO_TREBLE: return v4l2_ctrl_query_fill_std(qc); } - case VIDIOC_S_CTRL: - return tda7432_set_ctrl(client, arg); - - case VIDIOC_G_CTRL: - return tda7432_get_ctrl(client, arg); - - } /* end of (cmd) switch */ + return -EINVAL; +} - return 0; +static int tda7432_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); } -static struct i2c_driver driver = { - .driver = { - .name = "tda7432", - }, - .id = I2C_DRIVERID_TDA7432, - .attach_adapter = tda7432_probe, - .detach_client = tda7432_detach, - .command = tda7432_command, +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops tda7432_core_ops = { + .queryctrl = tda7432_queryctrl, + .g_ctrl = tda7432_g_ctrl, + .s_ctrl = tda7432_s_ctrl, }; -static struct i2c_client client_template = -{ - .name = "tda7432", - .driver = &driver, +static const struct v4l2_subdev_ops tda7432_ops = { + .core = &tda7432_core_ops, }; -static int __init tda7432_init(void) +/* ----------------------------------------------------------------------- */ + +/* *********************** * + * i2c interface functions * + * *********************** */ + +static int tda7432_probe(struct i2c_client *client, + const struct i2c_device_id *id) { - if ( (loudness < 0) || (loudness > 15) ) { - printk(KERN_ERR "loudness parameter must be between 0 and 15\n"); - return -EINVAL; + struct tda7432 *t; + struct v4l2_subdev *sd; + + v4l_info(client, "chip found @ 0x%02x (%s)\n", + client->addr << 1, client->adapter->name); + + t = kzalloc(sizeof(*t), GFP_KERNEL); + if (!t) + return -ENOMEM; + sd = &t->sd; + v4l2_i2c_subdev_init(sd, client, &tda7432_ops); + if (loudness < 0 || loudness > 15) { + v4l2_warn(sd, "loudness parameter must be between 0 and 15\n"); + if (loudness < 0) + loudness = 0; + if (loudness > 15) + loudness = 15; } - return i2c_add_driver(&driver); + do_tda7432_init(sd); + return 0; } -static void __exit tda7432_fini(void) +static int tda7432_remove(struct i2c_client *client) { - i2c_del_driver(&driver); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + do_tda7432_init(sd); + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); + return 0; } -module_init(tda7432_init); -module_exit(tda7432_fini); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) +static const struct i2c_device_id tda7432_id[] = { + { "tda7432", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tda7432_id); -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ +#endif +static struct v4l2_i2c_driver_data v4l2_i2c_data = { + .name = "tda7432", + .driverid = I2C_DRIVERID_TDA7432, + .command = tda7432_command, + .probe = tda7432_probe, + .remove = tda7432_remove, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + .id_table = tda7432_id, +#endif +}; -- cgit v1.2.3 From 104b4b757e631192d8c88e2682c0f23444522085 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 18 Dec 2008 17:21:57 +0100 Subject: tda9840: convert to v4l2_subdev. From: Hans Verkuil Priority: normal Signed-off-by: Hans Verkuil --- linux/drivers/media/video/tda9840.c | 188 ++++++++++++++++++++++-------------- 1 file changed, 116 insertions(+), 72 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/tda9840.c b/linux/drivers/media/video/tda9840.c index 31dde7516..7b749e4bb 100644 --- a/linux/drivers/media/video/tda9840.c +++ b/linux/drivers/media/video/tda9840.c @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include "tda9840.h" #include "compat.h" @@ -63,85 +63,89 @@ static unsigned short normal_i2c[] = { I2C_ADDR_TDA9840, I2C_CLIENT_END }; /* magic definition of all other variables and things */ I2C_CLIENT_INSMOD; -static void tda9840_write(struct i2c_client *client, u8 reg, u8 val) +static void tda9840_write(struct v4l2_subdev *sd, u8 reg, u8 val) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + if (i2c_smbus_write_byte_data(client, reg, val)) - v4l_dbg(1, debug, client, "error writing %02x to %02x\n", + v4l2_dbg(1, debug, sd, "error writing %02x to %02x\n", val, reg); } -static int tda9840_command(struct i2c_client *client, unsigned cmd, void *arg) +static int tda9840_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *t) { - int byte = *(int *)arg; - - switch (cmd) { - case VIDIOC_S_TUNER: { - struct v4l2_tuner *t = arg; - int byte; + int byte; - if (t->index) - return -EINVAL; + if (t->index) + return -EINVAL; - switch (t->audmode) { - case V4L2_TUNER_MODE_STEREO: - byte = TDA9840_SET_STEREO; - break; - case V4L2_TUNER_MODE_LANG1_LANG2: - byte = TDA9840_SET_BOTH; - break; - case V4L2_TUNER_MODE_LANG1: - byte = TDA9840_SET_LANG1; - break; - case V4L2_TUNER_MODE_LANG2: - byte = TDA9840_SET_LANG2; - break; - default: - byte = TDA9840_SET_MONO; - break; - } - v4l_dbg(1, debug, client, "TDA9840_SWITCH: 0x%02x\n", byte); - tda9840_write(client, SWITCH, byte); + switch (t->audmode) { + case V4L2_TUNER_MODE_STEREO: + byte = TDA9840_SET_STEREO; break; + case V4L2_TUNER_MODE_LANG1_LANG2: + byte = TDA9840_SET_BOTH; + break; + case V4L2_TUNER_MODE_LANG1: + byte = TDA9840_SET_LANG1; + break; + case V4L2_TUNER_MODE_LANG2: + byte = TDA9840_SET_LANG2; + break; + default: + byte = TDA9840_SET_MONO; + break; + } + v4l2_dbg(1, debug, sd, "TDA9840_SWITCH: 0x%02x\n", byte); + tda9840_write(sd, SWITCH, byte); + return 0; +} + +static int tda9840_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *t) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 byte; + + t->rxsubchans = V4L2_TUNER_SUB_MONO; + if (1 != i2c_master_recv(client, &byte, 1)) { + v4l2_dbg(1, debug, sd, + "i2c_master_recv() failed\n"); + return -EIO; + } + + if (byte & 0x80) { + v4l2_dbg(1, debug, sd, + "TDA9840_DETECT: register contents invalid\n"); + return -EINVAL; } - case VIDIOC_G_TUNER: { - struct v4l2_tuner *t = arg; - u8 byte; + v4l2_dbg(1, debug, sd, "TDA9840_DETECT: byte: 0x%02x\n", byte); + switch (byte & 0x60) { + case 0x00: t->rxsubchans = V4L2_TUNER_SUB_MONO; - if (1 != i2c_master_recv(client, &byte, 1)) { - v4l_dbg(1, debug, client, - "i2c_master_recv() failed\n"); - return -EIO; - } - - if (byte & 0x80) { - v4l_dbg(1, debug, client, - "TDA9840_DETECT: register contents invalid\n"); - return -EINVAL; - } - - v4l_dbg(1, debug, client, "TDA9840_DETECT: byte: 0x%02x\n", byte); - - switch (byte & 0x60) { - case 0x00: - t->rxsubchans = V4L2_TUNER_SUB_MONO; - break; - case 0x20: - t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; - break; - case 0x40: - t->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO; - break; - default: /* Incorrect detect */ - t->rxsubchans = V4L2_TUNER_MODE_MONO; - break; - } + break; + case 0x20: + t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; + break; + case 0x40: + t->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO; + break; + default: /* Incorrect detect */ + t->rxsubchans = V4L2_TUNER_MODE_MONO; break; } + return 0; +} +static int tda9840_ioctl(struct v4l2_subdev *sd, unsigned cmd, void *arg) +{ + int byte; + + switch (cmd) { case TDA9840_LEVEL_ADJUST: - v4l_dbg(1, debug, client, "TDA9840_LEVEL_ADJUST: %d\n", byte); + byte = *(int *)arg; + v4l2_dbg(1, debug, sd, "TDA9840_LEVEL_ADJUST: %d\n", byte); /* check for correct range */ if (byte > 25 || byte < -20) @@ -153,11 +157,12 @@ static int tda9840_command(struct i2c_client *client, unsigned cmd, void *arg) byte += 0x8; else byte = -byte; - tda9840_write(client, LEVEL_ADJUST, byte); + tda9840_write(sd, LEVEL_ADJUST, byte); break; case TDA9840_STEREO_ADJUST: - v4l_dbg(1, debug, client, "TDA9840_STEREO_ADJUST: %d\n", byte); + byte = *(int *)arg; + v4l2_dbg(1, debug, sd, "TDA9840_STEREO_ADJUST: %d\n", byte); /* check for correct range */ if (byte > 25 || byte < -24) @@ -170,18 +175,41 @@ static int tda9840_command(struct i2c_client *client, unsigned cmd, void *arg) else byte = -byte; - tda9840_write(client, STEREO_ADJUST, byte); + tda9840_write(sd, STEREO_ADJUST, byte); break; default: return -ENOIOCTLCMD; } - return 0; } +static int tda9840_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops tda9840_core_ops = { + .ioctl = tda9840_ioctl, +}; + +static const struct v4l2_subdev_tuner_ops tda9840_tuner_ops = { + .s_tuner = tda9840_s_tuner, + .g_tuner = tda9840_g_tuner, +}; + +static const struct v4l2_subdev_ops tda9840_ops = { + .core = &tda9840_core_ops, + .tuner = &tda9840_tuner_ops, +}; + +/* ----------------------------------------------------------------------- */ + static int tda9840_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct v4l2_subdev *sd; int result; int byte; @@ -189,23 +217,38 @@ static int tda9840_probe(struct i2c_client *client, if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) - return 0; + return -EIO; v4l_info(client, "chip found @ 0x%x (%s)\n", client->addr << 1, client->adapter->name); + sd = kmalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); + if (sd == NULL) + return -ENOMEM; + v4l2_i2c_subdev_init(sd, client, &tda9840_ops); + /* set initial values for level & stereo - adjustment, mode */ byte = 0; - result = tda9840_command(client, TDA9840_LEVEL_ADJUST, &byte); - result += tda9840_command(client, TDA9840_STEREO_ADJUST, &byte); - tda9840_write(client, SWITCH, TDA9840_SET_STEREO); + result = tda9840_ioctl(sd, TDA9840_LEVEL_ADJUST, &byte); + result |= tda9840_ioctl(sd, TDA9840_STEREO_ADJUST, &byte); + tda9840_write(sd, SWITCH, TDA9840_SET_STEREO); if (result) { - v4l_dbg(1, debug, client, "could not initialize tda9840\n"); + v4l2_dbg(1, debug, sd, "could not initialize tda9840\n"); + kfree(sd); return -ENODEV; } return 0; } +static int tda9840_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(sd); + return 0; +} + static int tda9840_legacy_probe(struct i2c_adapter *adapter) { /* Let's see whether this is a known adapter we can attach to. @@ -225,6 +268,7 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = { .driverid = I2C_DRIVERID_TDA9840, .command = tda9840_command, .probe = tda9840_probe, + .remove = tda9840_remove, .legacy_probe = tda9840_legacy_probe, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) .id_table = tda9840_id, -- cgit v1.2.3 From e3ee9e96571066103d1768d9ab377dbd9c103f89 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 18 Dec 2008 17:43:45 +0100 Subject: tda9875: convert to v4l2_subdev. From: Hans Verkuil Priority: normal Signed-off-by: Hans Verkuil --- linux/drivers/media/video/tda9875.c | 354 ++++++++++++++++-------------------- 1 file changed, 156 insertions(+), 198 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/tda9875.c b/linux/drivers/media/video/tda9875.c index 20e738264..2e72c36a9 100644 --- a/linux/drivers/media/video/tda9875.c +++ b/linux/drivers/media/video/tda9875.c @@ -25,13 +25,12 @@ #include #include #include -#include -#include #include -#include -#include "compat.h" - +#include +#include +#include #include +#include "compat.h" static int debug; /* insmod parameter */ module_param(debug, int, S_IRUGO | S_IWUSR); @@ -47,13 +46,15 @@ I2C_CLIENT_INSMOD; /* This is a superset of the TDA9875 */ struct tda9875 { + struct v4l2_subdev sd; int rvol, lvol; int bass, treble; - struct i2c_client c; }; -static struct i2c_driver driver; -static struct i2c_client client_template; +static inline struct tda9875 *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct tda9875, sd); +} #define dprintk if (debug) printk @@ -106,15 +107,16 @@ static struct i2c_client client_template; /* Begin code */ -static int tda9875_write(struct i2c_client *client, int subaddr, unsigned char val) +static int tda9875_write(struct v4l2_subdev *sd, int subaddr, unsigned char val) { + struct i2c_client *client = v4l2_get_subdevdata(sd); unsigned char buffer[2]; - dprintk("In tda9875_write\n"); - dprintk("Writing %d 0x%x\n", subaddr, val); + + v4l2_dbg(1, debug, sd, "Writing %d 0x%x\n", subaddr, val); buffer[0] = subaddr; buffer[1] = val; - if (2 != i2c_master_send(client,buffer,2)) { - printk(KERN_WARNING "tda9875: I/O error, trying (write %d 0x%x)\n", + if (2 != i2c_master_send(client, buffer, 2)) { + v4l2_warn(sd, "I/O error, trying (write %d 0x%x)\n", subaddr, val); return -1; } @@ -135,7 +137,7 @@ static int tda9875_read(struct i2c_client *client) } #endif -static int i2c_read_register(struct i2c_adapter *adap, int addr, int reg) +static int i2c_read_register(struct i2c_client *client, int addr, int reg) { unsigned char write[1]; unsigned char read[1]; @@ -143,150 +145,83 @@ static int i2c_read_register(struct i2c_adapter *adap, int addr, int reg) { addr, 0, 1, write }, { addr, I2C_M_RD, 1, read } }; + write[0] = reg; - if (2 != i2c_transfer(adap,msgs,2)) { - printk(KERN_WARNING "tda9875: I/O error (read2)\n"); + if (2 != i2c_transfer(client->adapter, msgs, 2)) { + v4l_warn(client, "I/O error (read2)\n"); return -1; } - dprintk("tda9875: chip_read2: reg%d=0x%x\n",reg,read[0]); + v4l_dbg(1, debug, client, "chip_read2: reg%d=0x%x\n", reg, read[0]); return read[0]; } -static void tda9875_set(struct i2c_client *client) +static void tda9875_set(struct v4l2_subdev *sd) { - struct tda9875 *tda = i2c_get_clientdata(client); + struct tda9875 *tda = to_state(sd); unsigned char a; - dprintk(KERN_DEBUG "tda9875_set(%04x,%04x,%04x,%04x)\n", - tda->lvol,tda->rvol,tda->bass,tda->treble); - + v4l2_dbg(1, debug, sd, "tda9875_set(%04x,%04x,%04x,%04x)\n", + tda->lvol, tda->rvol, tda->bass, tda->treble); a = tda->lvol & 0xff; - tda9875_write(client, TDA9875_MVL, a); + tda9875_write(sd, TDA9875_MVL, a); a =tda->rvol & 0xff; - tda9875_write(client, TDA9875_MVR, a); + tda9875_write(sd, TDA9875_MVR, a); a =tda->bass & 0xff; - tda9875_write(client, TDA9875_MBA, a); + tda9875_write(sd, TDA9875_MBA, a); a =tda->treble & 0xff; - tda9875_write(client, TDA9875_MTR, a); + tda9875_write(sd, TDA9875_MTR, a); } -static void do_tda9875_init(struct i2c_client *client) +static void do_tda9875_init(struct v4l2_subdev *sd) { - struct tda9875 *t = i2c_get_clientdata(client); - dprintk("In tda9875_init\n"); - tda9875_write(client, TDA9875_CFG, 0xd0 ); /*reg de config 0 (reset)*/ - tda9875_write(client, TDA9875_MSR, 0x03 ); /* Monitor 0b00000XXX*/ - tda9875_write(client, TDA9875_C1MSB, 0x00 ); /*Car1(FM) MSB XMHz*/ - tda9875_write(client, TDA9875_C1MIB, 0x00 ); /*Car1(FM) MIB XMHz*/ - tda9875_write(client, TDA9875_C1LSB, 0x00 ); /*Car1(FM) LSB XMHz*/ - tda9875_write(client, TDA9875_C2MSB, 0x00 ); /*Car2(NICAM) MSB XMHz*/ - tda9875_write(client, TDA9875_C2MIB, 0x00 ); /*Car2(NICAM) MIB XMHz*/ - tda9875_write(client, TDA9875_C2LSB, 0x00 ); /*Car2(NICAM) LSB XMHz*/ - tda9875_write(client, TDA9875_DCR, 0x00 ); /*Demod config 0x00*/ - tda9875_write(client, TDA9875_DEEM, 0x44 ); /*DE-Emph 0b0100 0100*/ - tda9875_write(client, TDA9875_FMAT, 0x00 ); /*FM Matrix reg 0x00*/ - tda9875_write(client, TDA9875_SC1, 0x00 ); /* SCART 1 (SC1)*/ - tda9875_write(client, TDA9875_SC2, 0x01 ); /* SCART 2 (sc2)*/ - - tda9875_write(client, TDA9875_CH1V, 0x10 ); /* Channel volume 1 mute*/ - tda9875_write(client, TDA9875_CH2V, 0x10 ); /* Channel volume 2 mute */ - tda9875_write(client, TDA9875_DACOS, 0x02 ); /* sig DAC i/o(in:nicam)*/ - tda9875_write(client, TDA9875_ADCIS, 0x6f ); /* sig ADC input(in:mono)*/ - tda9875_write(client, TDA9875_LOSR, 0x00 ); /* line out (in:mono)*/ - tda9875_write(client, TDA9875_AER, 0x00 ); /*06 Effect (AVL+PSEUDO) */ - tda9875_write(client, TDA9875_MCS, 0x44 ); /* Main ch select (DAC) */ - tda9875_write(client, TDA9875_MVL, 0x03 ); /* Vol Main left 10dB */ - tda9875_write(client, TDA9875_MVR, 0x03 ); /* Vol Main right 10dB*/ - tda9875_write(client, TDA9875_MBA, 0x00 ); /* Main Bass Main 0dB*/ - tda9875_write(client, TDA9875_MTR, 0x00 ); /* Main Treble Main 0dB*/ - tda9875_write(client, TDA9875_ACS, 0x44 ); /* Aux chan select (dac)*/ - tda9875_write(client, TDA9875_AVL, 0x00 ); /* Vol Aux left 0dB*/ - tda9875_write(client, TDA9875_AVR, 0x00 ); /* Vol Aux right 0dB*/ - tda9875_write(client, TDA9875_ABA, 0x00 ); /* Aux Bass Main 0dB*/ - tda9875_write(client, TDA9875_ATR, 0x00 ); /* Aux Aigus Main 0dB*/ - - tda9875_write(client, TDA9875_MUT, 0xcc ); /* General mute */ - - t->lvol=t->rvol =0; /* 0dB */ - t->bass=0; /* 0dB */ - t->treble=0; /* 0dB */ - tda9875_set(client); - -} - - -/* *********************** * - * i2c interface functions * - * *********************** */ - -static int tda9875_checkit(struct i2c_adapter *adap, int addr) -{ - int dic,rev; - - dic=i2c_read_register(adap,addr,254); - rev=i2c_read_register(adap,addr,255); - - if(dic==0 || dic==2) { // tda9875 and tda9875A - printk("tda9875: TDA9875%s Rev.%d detected at 0x%x\n", - dic==0?"":"A", rev,addr<<1); - return 1; - } - printk("tda9875: no such chip at 0x%x (dic=0x%x rev=0x%x)\n",addr<<1,dic,rev); - return(0); + struct tda9875 *t = to_state(sd); + + v4l2_dbg(1, debug, sd, "In tda9875_init\n"); + tda9875_write(sd, TDA9875_CFG, 0xd0); /*reg de config 0 (reset)*/ + tda9875_write(sd, TDA9875_MSR, 0x03); /* Monitor 0b00000XXX*/ + tda9875_write(sd, TDA9875_C1MSB, 0x00); /*Car1(FM) MSB XMHz*/ + tda9875_write(sd, TDA9875_C1MIB, 0x00); /*Car1(FM) MIB XMHz*/ + tda9875_write(sd, TDA9875_C1LSB, 0x00); /*Car1(FM) LSB XMHz*/ + tda9875_write(sd, TDA9875_C2MSB, 0x00); /*Car2(NICAM) MSB XMHz*/ + tda9875_write(sd, TDA9875_C2MIB, 0x00); /*Car2(NICAM) MIB XMHz*/ + tda9875_write(sd, TDA9875_C2LSB, 0x00); /*Car2(NICAM) LSB XMHz*/ + tda9875_write(sd, TDA9875_DCR, 0x00); /*Demod config 0x00*/ + tda9875_write(sd, TDA9875_DEEM, 0x44); /*DE-Emph 0b0100 0100*/ + tda9875_write(sd, TDA9875_FMAT, 0x00); /*FM Matrix reg 0x00*/ + tda9875_write(sd, TDA9875_SC1, 0x00); /* SCART 1 (SC1)*/ + tda9875_write(sd, TDA9875_SC2, 0x01); /* SCART 2 (sc2)*/ + + tda9875_write(sd, TDA9875_CH1V, 0x10); /* Channel volume 1 mute*/ + tda9875_write(sd, TDA9875_CH2V, 0x10); /* Channel volume 2 mute */ + tda9875_write(sd, TDA9875_DACOS, 0x02); /* sig DAC i/o(in:nicam)*/ + tda9875_write(sd, TDA9875_ADCIS, 0x6f); /* sig ADC input(in:mono)*/ + tda9875_write(sd, TDA9875_LOSR, 0x00); /* line out (in:mono)*/ + tda9875_write(sd, TDA9875_AER, 0x00); /*06 Effect (AVL+PSEUDO) */ + tda9875_write(sd, TDA9875_MCS, 0x44); /* Main ch select (DAC) */ + tda9875_write(sd, TDA9875_MVL, 0x03); /* Vol Main left 10dB */ + tda9875_write(sd, TDA9875_MVR, 0x03); /* Vol Main right 10dB*/ + tda9875_write(sd, TDA9875_MBA, 0x00); /* Main Bass Main 0dB*/ + tda9875_write(sd, TDA9875_MTR, 0x00); /* Main Treble Main 0dB*/ + tda9875_write(sd, TDA9875_ACS, 0x44); /* Aux chan select (dac)*/ + tda9875_write(sd, TDA9875_AVL, 0x00); /* Vol Aux left 0dB*/ + tda9875_write(sd, TDA9875_AVR, 0x00); /* Vol Aux right 0dB*/ + tda9875_write(sd, TDA9875_ABA, 0x00); /* Aux Bass Main 0dB*/ + tda9875_write(sd, TDA9875_ATR, 0x00); /* Aux Aigus Main 0dB*/ + + tda9875_write(sd, TDA9875_MUT, 0xcc); /* General mute */ + + t->lvol = t->rvol = 0; /* 0dB */ + t->bass = 0; /* 0dB */ + t->treble = 0; /* 0dB */ + tda9875_set(sd); } -static int tda9875_attach(struct i2c_adapter *adap, int addr, int kind) -{ - struct tda9875 *t; - struct i2c_client *client; - dprintk("In tda9875_attach\n"); - - t = kzalloc(sizeof *t,GFP_KERNEL); - if (!t) - return -ENOMEM; - - client = &t->c; - memcpy(client,&client_template,sizeof(struct i2c_client)); - client->adapter = adap; - client->addr = addr; - i2c_set_clientdata(client, t); - - if(!tda9875_checkit(adap,addr)) { - kfree(t); - return 1; - } - - do_tda9875_init(client); - printk(KERN_INFO "tda9875: init\n"); - - i2c_attach_client(client); - return 0; -} - -static int tda9875_probe(struct i2c_adapter *adap) -{ - if (adap->class & I2C_CLASS_TV_ANALOG) - return i2c_probe(adap, &addr_data, tda9875_attach); - return 0; -} - -static int tda9875_detach(struct i2c_client *client) -{ - struct tda9875 *t = i2c_get_clientdata(client); - - do_tda9875_init(client); - i2c_detach_client(client); - - kfree(t); - return 0; -} -static int tda9875_get_ctrl(struct i2c_client *client, - struct v4l2_control *ctrl) +static int tda9875_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct tda9875 *t = i2c_get_clientdata(client); + struct tda9875 *t = to_state(sd); switch (ctrl->id) { case V4L2_CID_AUDIO_VOLUME: @@ -318,10 +253,9 @@ static int tda9875_get_ctrl(struct i2c_client *client, return -EINVAL; } -static int tda9875_set_ctrl(struct i2c_client *client, - struct v4l2_control *ctrl) +static int tda9875_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct tda9875 *t = i2c_get_clientdata(client); + struct tda9875 *t = to_state(sd); int chvol=0, volume, balance, left, right; switch (ctrl->id) { @@ -385,85 +319,109 @@ static int tda9875_set_ctrl(struct i2c_client *client, t->rvol = -84 & 0xff; } -//printk("tda9875 bal:%04x vol:%04x bass:%04x treble:%04x\n",va->balance,va->volume,va->bass,va->treble); - - tda9875_set(client); - + tda9875_set(sd); return 0; } - -static int tda9875_command(struct i2c_client *client, - unsigned int cmd, void *arg) +static int tda9875_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) { - dprintk("In tda9875_command...\n"); - - switch (cmd) { - /* --- v4l ioctls --- */ - /* take care: bttv does userspace copying, we'll get a - kernel pointer here... */ - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; - - switch (qc->id) { - case V4L2_CID_AUDIO_VOLUME: - case V4L2_CID_AUDIO_BASS: - case V4L2_CID_AUDIO_TREBLE: - default: - return -EINVAL; - } + switch (qc->id) { + case V4L2_CID_AUDIO_VOLUME: + case V4L2_CID_AUDIO_BASS: + case V4L2_CID_AUDIO_TREBLE: return v4l2_ctrl_query_fill_std(qc); } - case VIDIOC_S_CTRL: - return tda9875_set_ctrl(client, arg); + return -EINVAL; +} - case VIDIOC_G_CTRL: - return tda9875_get_ctrl(client, arg); +static int tda9875_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} - default: /* Not VIDEOCGAUDIO or VIDEOCSAUDIO */ +/* ----------------------------------------------------------------------- */ - /* nothing */ - dprintk("Default\n"); +static const struct v4l2_subdev_core_ops tda9875_core_ops = { + .queryctrl = tda9875_queryctrl, + .g_ctrl = tda9875_g_ctrl, + .s_ctrl = tda9875_s_ctrl, +}; - } /* end of (cmd) switch */ +static const struct v4l2_subdev_ops tda9875_ops = { + .core = &tda9875_core_ops, +}; - return 0; -} +/* ----------------------------------------------------------------------- */ -static struct i2c_driver driver = { - .driver = { - .name = "tda9875", - }, - .id = I2C_DRIVERID_TDA9875, - .attach_adapter = tda9875_probe, - .detach_client = tda9875_detach, - .command = tda9875_command, -}; +/* *********************** * + * i2c interface functions * + * *********************** */ -static struct i2c_client client_template = +static int tda9875_checkit(struct i2c_client *client, int addr) { - .name = "tda9875", - .driver = &driver, -}; + int dic, rev; -static int __init tda9875_init(void) -{ - return i2c_add_driver(&driver); + dic = i2c_read_register(client, addr, 254); + rev = i2c_read_register(client, addr, 255); + + if (dic == 0 || dic == 2) { /* tda9875 and tda9875A */ + v4l_info(client, "tda9875%s rev. %d detected at 0x%02x\n", + dic == 0 ? "" : "A", rev, addr << 1); + return 1; + } + v4l_info(client, "no such chip at 0x%02x (dic=0x%x rev=0x%x)\n", + addr << 1, dic, rev); + return 0; } -static void __exit tda9875_fini(void) +static int tda9875_probe(struct i2c_client *client, + const struct i2c_device_id *id) { - i2c_del_driver(&driver); + struct tda9875 *t; + struct v4l2_subdev *sd; + + v4l_info(client, "chip found @ 0x%02x (%s)\n", + client->addr << 1, client->adapter->name); + + if (!tda9875_checkit(client, client->addr)) + return -ENODEV; + + t = kzalloc(sizeof(*t), GFP_KERNEL); + if (!t) + return -ENOMEM; + sd = &t->sd; + v4l2_i2c_subdev_init(sd, client, &tda9875_ops); + + do_tda9875_init(sd); + return 0; } -module_init(tda9875_init); -module_exit(tda9875_fini); +static int tda9875_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ + do_tda9875_init(sd); + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); + return 0; +} +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) +static const struct i2c_device_id tda9875_id[] = { + { "tda9875", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tda9875_id); + +#endif +static struct v4l2_i2c_driver_data v4l2_i2c_data = { + .name = "tda9875", + .driverid = I2C_DRIVERID_TDA9875, + .command = tda9875_command, + .probe = tda9875_probe, + .remove = tda9875_remove, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + .id_table = tda9875_id, +#endif +}; -- cgit v1.2.3 From 5001dbc132aae3d583ec070cfd2f8382f383cec5 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 19 Dec 2008 11:07:56 +0100 Subject: tuner: fix tuner_ioctl compile error if V4L1 ioctls are disabled. From: Hans Verkuil Priority: normal Signed-off-by: Hans Verkuil --- linux/drivers/media/video/tuner-core.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'linux') diff --git a/linux/drivers/media/video/tuner-core.c b/linux/drivers/media/video/tuner-core.c index e649f2eb7..74a7402f2 100644 --- a/linux/drivers/media/video/tuner-core.c +++ b/linux/drivers/media/video/tuner-core.c @@ -1118,7 +1118,9 @@ static int tuner_resume(struct i2c_client *c) static const struct v4l2_subdev_core_ops tuner_core_ops = { .log_status = tuner_log_status, .s_standby = tuner_s_standby, +#ifdef CONFIG_VIDEO_ALLOW_V4L1 .ioctl = tuner_ioctl, +#endif }; static const struct v4l2_subdev_tuner_ops tuner_tuner_ops = { -- cgit v1.2.3 From cdd1295a9bfd3f1f39ba27702a1428a2a4691b19 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 19 Dec 2008 11:18:38 +0100 Subject: bt832: remove this driver From: Hans Verkuil The bt832 i2c driver was never used or even compiled and is no longer maintained. It is now removed completely. Priority: normal Signed-off-by: Hans Verkuil --- linux/drivers/media/video/bt8xx/bt832.c | 287 ------------------------- linux/drivers/media/video/bt8xx/bt832.h | 305 --------------------------- linux/drivers/media/video/bt8xx/bttv-cards.c | 45 ---- linux/include/media/i2c-addr.h | 2 - 4 files changed, 639 deletions(-) delete mode 100644 linux/drivers/media/video/bt8xx/bt832.c delete mode 100644 linux/drivers/media/video/bt8xx/bt832.h (limited to 'linux') diff --git a/linux/drivers/media/video/bt8xx/bt832.c b/linux/drivers/media/video/bt8xx/bt832.c deleted file mode 100644 index b677916bf..000000000 --- a/linux/drivers/media/video/bt8xx/bt832.c +++ /dev/null @@ -1,287 +0,0 @@ -/* Driver for Bt832 CMOS Camera Video Processor - i2c-addresses: 0x88 or 0x8a - - The BT832 interfaces to a Quartzsight Digital Camera (352x288, 25 or 30 fps) - via a 9 pin connector ( 4-wire SDATA, 2-wire i2c, SCLK, VCC, GND). - It outputs an 8-bit 4:2:2 YUV or YCrCb video signal which can be directly - connected to bt848/bt878 GPIO pins on this purpose. - (see: VLSI Vision Ltd. www.vvl.co.uk for camera datasheets) - - Supported Cards: - - Pixelview Rev.4E: 0x8a - GPIO 0x400000 toggles Bt832 RESET, and the chip changes to i2c 0x88 ! - - (c) Gunther Mayer, 2002 - - STATUS: - - detect chip and hexdump - - reset chip and leave low power mode - - detect camera present - - TODO: - - make it work (find correct setup for Bt832 and Bt878) -*/ - -#include -#include -#include -#include -#include "compat.h" -#include -#include -#include -#include -#include - -#include "bttv.h" -#include "bt832.h" - -MODULE_LICENSE("GPL"); - -/* Addresses to scan */ -static unsigned short normal_i2c[] = { I2C_ADDR_BT832_ALT1>>1, I2C_ADDR_BT832_ALT2>>1, - I2C_CLIENT_END }; -I2C_CLIENT_INSMOD; - -int debug; /* debug output */ -module_param(debug, int, 0644); - -/* ---------------------------------------------------------------------- */ - -static int bt832_detach(struct i2c_client *client); - - -static struct i2c_driver driver; -static struct i2c_client client_template; - -struct bt832 { - struct i2c_client client; -}; - -int bt832_hexdump(struct i2c_client *i2c_client_s, unsigned char *buf) -{ - int i,rc; - buf[0]=0x80; // start at register 0 with auto-increment - if (1 != (rc = i2c_master_send(i2c_client_s,buf,1))) - v4l_err(i2c_client_s,"i2c i/o error: rc == %d (should be 1)\n",rc); - - for(i=0;i<65;i++) - buf[i]=0; - if (65 != (rc=i2c_master_recv(i2c_client_s,buf,65))) - v4l_err(i2c_client_s,"i2c i/o error: rc == %d (should be 65)\n",rc); - - // Note: On READ the first byte is the current index - // (e.g. 0x80, what we just wrote) - - if(debug>1) { - int i; - v4l_dbg(2, debug,i2c_client_s,"hexdump:"); - for(i=1;i<65;i++) { - if(i!=1) { - if(((i-1)%8)==0) printk(" "); - if(((i-1)%16)==0) { - printk("\n"); - v4l_dbg(2, debug,i2c_client_s,"hexdump:"); - } - } - printk(" %02x",buf[i]); - } - printk("\n"); - } - return 0; -} - -// Return: 1 (is a bt832), 0 (No bt832 here) -int bt832_init(struct i2c_client *i2c_client_s) -{ - unsigned char *buf; - int rc; - - buf=kmalloc(65,GFP_KERNEL); - if (!buf) { - v4l_err(&t->client, - "Unable to allocate memory. Detaching.\n"); - return 0; - } - bt832_hexdump(i2c_client_s,buf); - - if(buf[0x40] != 0x31) { - v4l_err(i2c_client_s,"This i2c chip is no bt832 (id=%02x). Detaching.\n",buf[0x40]); - kfree(buf); - return 0; - } - - v4l_err(i2c_client_s,"Write 0 tp VPSTATUS\n"); - buf[0]=BT832_VP_STATUS; // Reg.52 - buf[1]= 0x00; - if (2 != (rc = i2c_master_send(i2c_client_s,buf,2))) - v4l_err(i2c_client_s,"i2c i/o error VPS: rc == %d (should be 2)\n",rc); - - bt832_hexdump(i2c_client_s,buf); - - - // Leave low power mode: - v4l_err(i2c_client_s,"leave low power mode.\n"); - buf[0]=BT832_CAM_SETUP0; //0x39 57 - buf[1]=0x08; - if (2 != (rc = i2c_master_send(i2c_client_s,buf,2))) - v4l_err(i2c_client_s,"i2c i/o error LLPM: rc == %d (should be 2)\n",rc); - - bt832_hexdump(i2c_client_s,buf); - - v4l_info(i2c_client_s,"Write 0 tp VPSTATUS\n"); - buf[0]=BT832_VP_STATUS; // Reg.52 - buf[1]= 0x00; - if (2 != (rc = i2c_master_send(i2c_client_s,buf,2))) - v4l_err(i2c_client_s,"i2c i/o error VPS: rc == %d (should be 2)\n",rc); - - bt832_hexdump(i2c_client_s,buf); - - - // Enable Output - v4l_info(i2c_client_s,"Enable Output\n"); - buf[0]=BT832_VP_CONTROL1; // Reg.40 - buf[1]= 0x27 & (~0x01); // Default | !skip - if (2 != (rc = i2c_master_send(i2c_client_s,buf,2))) - v4l_err(i2c_client_s,"i2c i/o error EO: rc == %d (should be 2)\n",rc); - - bt832_hexdump(i2c_client_s,buf); - -#if 0 - // Full 30/25 Frame rate - v4l_err(i2c_client_s,"Full 30/25 Frame rate\n"); - buf[0]=BT832_VP_CONTROL0; // Reg.39 - buf[1]= 0x00; - if (2 != (rc = i2c_master_send(i2c_client_s,buf,2))) - v4l_err(i2c_client_s,"i2c i/o error FFR: rc == %d (should be 2)\n",rc); - - bt832_hexdump(i2c_client_s,buf); -#endif - -#if 1 - // for testing (even works when no camera attached) - v4l_info(i2c_client_s,"*** Generate NTSC M Bars *****\n"); - buf[0]=BT832_VP_TESTCONTROL0; // Reg. 42 - buf[1]=3; // Generate NTSC System M bars, Generate Frame timing internally - if (2 != (rc = i2c_master_send(i2c_client_s,buf,2))) - v4l_info(i2c_client_s,"i2c i/o error MBAR: rc == %d (should be 2)\n",rc); -#endif - - v4l_info(i2c_client_s,"Camera Present: %s\n", - (buf[1+BT832_CAM_STATUS] & BT832_56_CAMERA_PRESENT) ? "yes":"no"); - - bt832_hexdump(i2c_client_s,buf); - kfree(buf); - return 1; -} - - - -static int bt832_attach(struct i2c_adapter *adap, int addr, int kind) -{ - struct bt832 *t; - - client_template.adapter = adap; - client_template.addr = addr; - - if (NULL == (t = kzalloc(sizeof(*t), GFP_KERNEL))) - return -ENOMEM; - t->client = client_template; - i2c_set_clientdata(&t->client, t); - i2c_attach_client(&t->client); - - v4l_info(&t->client,"chip found @ 0x%x\n", addr<<1); - - if(! bt832_init(&t->client)) { - bt832_detach(&t->client); - return -1; - } - - return 0; -} - -static int bt832_probe(struct i2c_adapter *adap) -{ - if (adap->class & I2C_CLASS_TV_ANALOG) - return i2c_probe(adap, &addr_data, bt832_attach); - return 0; -} - -static int bt832_detach(struct i2c_client *client) -{ - struct bt832 *t = i2c_get_clientdata(client); - - v4l_info(&t->client,"dettach\n"); - i2c_detach_client(client); - kfree(t); - return 0; -} - -static int -bt832_command(struct i2c_client *client, unsigned int cmd, void *arg) -{ - struct bt832 *t = i2c_get_clientdata(client); - - if (debug>1) - v4l_i2c_print_ioctl(&t->client,cmd); - - switch (cmd) { - case BT832_HEXDUMP: { - unsigned char *buf; - buf = kmalloc(65, GFP_KERNEL); - if (!buf) { - v4l_err(&t->client, - "Unable to allocate memory\n"); - break; - } - bt832_hexdump(&t->client,buf); - kfree(buf); - } - break; - case BT832_REATTACH: - v4l_info(&t->client,"re-attach\n"); - i2c_del_driver(&driver); - i2c_add_driver(&driver); - break; - } - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static struct i2c_driver driver = { - .driver = { - .name = "bt832", - }, - .id = 0, /* FIXME */ - .attach_adapter = bt832_probe, - .detach_client = bt832_detach, - .command = bt832_command, -}; -static struct i2c_client client_template = -{ - .name = "bt832", - .driver = &driver, -}; - - -static int __init bt832_init_module(void) -{ - return i2c_add_driver(&driver); -} - -static void __exit bt832_cleanup_module(void) -{ - i2c_del_driver(&driver); -} - -module_init(bt832_init_module); -module_exit(bt832_cleanup_module); - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * --------------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/linux/drivers/media/video/bt8xx/bt832.h b/linux/drivers/media/video/bt8xx/bt832.h deleted file mode 100644 index 1ce8fa71f..000000000 --- a/linux/drivers/media/video/bt8xx/bt832.h +++ /dev/null @@ -1,305 +0,0 @@ -/* Bt832 CMOS Camera Video Processor (VP) - - The Bt832 CMOS Camera Video Processor chip connects a Quartsight CMOS - color digital camera directly to video capture devices via an 8-bit, - 4:2:2 YUV or YCrCb video interface. - - i2c addresses: 0x88 or 0x8a - */ - -/* The 64 registers: */ - -// Input Processor -#define BT832_OFFSET 0 -#define BT832_RCOMP 1 -#define BT832_G1COMP 2 -#define BT832_G2COMP 3 -#define BT832_BCOMP 4 -// Exposures: -#define BT832_FINEH 5 -#define BT832_FINEL 6 -#define BT832_COARSEH 7 -#define BT832_COARSEL 8 -#define BT832_CAMGAIN 9 -// Main Processor: -#define BT832_M00 10 -#define BT832_M01 11 -#define BT832_M02 12 -#define BT832_M10 13 -#define BT832_M11 14 -#define BT832_M12 15 -#define BT832_M20 16 -#define BT832_M21 17 -#define BT832_M22 18 -#define BT832_APCOR 19 -#define BT832_GAMCOR 20 -// Level Accumulator Inputs -#define BT832_VPCONTROL2 21 -#define BT832_ZONECODE0 22 -#define BT832_ZONECODE1 23 -#define BT832_ZONECODE2 24 -#define BT832_ZONECODE3 25 -// Level Accumulator Outputs: -#define BT832_RACC 26 -#define BT832_GACC 27 -#define BT832_BACC 28 -#define BT832_BLACKACC 29 -#define BT832_EXP_AGC 30 -#define BT832_LACC0 31 -#define BT832_LACC1 32 -#define BT832_LACC2 33 -#define BT832_LACC3 34 -#define BT832_LACC4 35 -#define BT832_LACC5 36 -#define BT832_LACC6 37 -#define BT832_LACC7 38 -// System: -#define BT832_VP_CONTROL0 39 -#define BT832_VP_CONTROL1 40 -#define BT832_THRESH 41 -#define BT832_VP_TESTCONTROL0 42 -#define BT832_VP_DMCODE 43 -#define BT832_ACB_CONFIG 44 -#define BT832_ACB_GNBASE 45 -#define BT832_ACB_MU 46 -#define BT832_CAM_TEST0 47 -#define BT832_AEC_CONFIG 48 -#define BT832_AEC_TL 49 -#define BT832_AEC_TC 50 -#define BT832_AEC_TH 51 -// Status: -#define BT832_VP_STATUS 52 -#define BT832_VP_LINECOUNT 53 -#define BT832_CAM_DEVICEL 54 // e.g. 0x19 -#define BT832_CAM_DEVICEH 55 // e.g. 0x40 == 0x194 Mask0, 0x194 = 404 decimal (VVL-404 camera) -#define BT832_CAM_STATUS 56 - #define BT832_56_CAMERA_PRESENT 0x20 -//Camera Setups: -#define BT832_CAM_SETUP0 57 -#define BT832_CAM_SETUP1 58 -#define BT832_CAM_SETUP2 59 -#define BT832_CAM_SETUP3 60 -// System: -#define BT832_DEFCOR 61 -#define BT832_VP_TESTCONTROL1 62 -#define BT832_DEVICE_ID 63 -# define BT832_DEVICE_ID__31 0x31 // Bt832 has ID 0x31 - -/* STMicroelectronivcs VV5404 camera module - i2c: 0x20: sensor address - i2c: 0xa0: eeprom for ccd defect map - */ -#define VV5404_device_h 0x00 // 0x19 -#define VV5404_device_l 0x01 // 0x40 -#define VV5404_status0 0x02 -#define VV5404_linecountc 0x03 // current line counter -#define VV5404_linecountl 0x04 -#define VV5404_setup0 0x10 -#define VV5404_setup1 0x11 -#define VV5404_setup2 0x12 -#define VV5404_setup4 0x14 -#define VV5404_setup5 0x15 -#define VV5404_fine_h 0x20 // fine exposure -#define VV5404_fine_l 0x21 -#define VV5404_coarse_h 0x22 //coarse exposure -#define VV5404_coarse_l 0x23 -#define VV5404_gain 0x24 // ADC pre-amp gain setting -#define VV5404_clk_div 0x25 -#define VV5404_cr 0x76 // control register -#define VV5404_as0 0x77 // ADC setup register - - -// IOCTL -#define BT832_HEXDUMP _IOR('b',1,int) -#define BT832_REATTACH _IOR('b',2,int) - -/* from BT8x8VXD/capdrv/dialogs.cpp */ - -/* -typedef enum { SVI, Logitech, Rockwell } CAMERA; - -static COMBOBOX_ENTRY gwCameraOptions[] = -{ - { SVI, "Silicon Vision 512N" }, - { Logitech, "Logitech VideoMan 1.3" }, - { Rockwell, "Rockwell QuartzSight PCI 1.0" } -}; - -// SRAM table values -//=========================================================================== -typedef enum { TGB_NTSC624, TGB_NTSC780, TGB_NTSC858, TGB_NTSC392 } TimeGenByte; - -BYTE SRAMTable[][ 60 ] = -{ - // TGB_NTSC624 - { - 0x33, // size of table = 51 - 0x0E, 0xC0, 0x00, 0x00, 0x90, 0x02, 0x03, 0x10, 0x03, 0x06, - 0x10, 0x04, 0x12, 0x12, 0x05, 0x02, 0x13, 0x04, 0x19, 0x00, - 0x04, 0x39, 0x00, 0x06, 0x59, 0x08, 0x03, 0x85, 0x08, 0x07, - 0x03, 0x50, 0x00, 0x91, 0x40, 0x00, 0x11, 0x01, 0x01, 0x4D, - 0x0D, 0x02, 0x03, 0x11, 0x01, 0x05, 0x37, 0x00, 0x37, 0x21, 0x00 - }, - // TGB_NTSC780 - { - 0x33, // size of table = 51 - 0x0e, 0xc0, 0x00, 0x00, 0x90, 0xe2, 0x03, 0x10, 0x03, 0x06, - 0x10, 0x34, 0x12, 0x12, 0x65, 0x02, 0x13, 0x24, 0x19, 0x00, - 0x24, 0x39, 0x00, 0x96, 0x59, 0x08, 0x93, 0x85, 0x08, 0x97, - 0x03, 0x50, 0x50, 0xaf, 0x40, 0x30, 0x5f, 0x01, 0xf1, 0x7f, - 0x0d, 0xf2, 0x03, 0x11, 0xf1, 0x05, 0x37, 0x30, 0x85, 0x21, 0x50 - }, - // TGB_NTSC858 - { - 0x33, // size of table = 51 - 0x0c, 0xc0, 0x00, 0x00, 0x90, 0xc2, 0x03, 0x10, 0x03, 0x06, - 0x10, 0x34, 0x12, 0x12, 0x65, 0x02, 0x13, 0x24, 0x19, 0x00, - 0x24, 0x39, 0x00, 0x96, 0x59, 0x08, 0x93, 0x83, 0x08, 0x97, - 0x03, 0x50, 0x30, 0xc0, 0x40, 0x30, 0x86, 0x01, 0x01, 0xa6, - 0x0d, 0x62, 0x03, 0x11, 0x61, 0x05, 0x37, 0x30, 0xac, 0x21, 0x50 - }, - // TGB_NTSC392 - // This table has been modified to be used for Fusion Rev D - { - 0x2A, // size of table = 42 - 0x06, 0x08, 0x04, 0x0a, 0xc0, 0x00, 0x18, 0x08, 0x03, 0x24, - 0x08, 0x07, 0x02, 0x90, 0x02, 0x08, 0x10, 0x04, 0x0c, 0x10, - 0x05, 0x2c, 0x11, 0x04, 0x55, 0x48, 0x00, 0x05, 0x50, 0x00, - 0xbf, 0x0c, 0x02, 0x2f, 0x3d, 0x00, 0x2f, 0x3f, 0x00, 0xc3, - 0x20, 0x00 - } -}; - -//=========================================================================== -// This is the structure of the camera specifications -//=========================================================================== -typedef struct tag_cameraSpec -{ - SignalFormat signal; // which digital signal format the camera has - VideoFormat vidFormat; // video standard - SyncVideoRef syncRef; // which sync video reference is used - State syncOutput; // enable sync output for sync video input? - DecInputClk iClk; // which input clock is used - TimeGenByte tgb; // which timing generator byte does the camera use - int HReset; // select 64, 48, 32, or 16 CLKx1 for HReset - PLLFreq pllFreq; // what synthesized frequency to set PLL to - VSIZEPARMS vSize; // video size the camera produces - int lineCount; // expected total number of half-line per frame - 1 - BOOL interlace; // interlace signal? -} CameraSpec; - -//=========================================================================== -// -// Camera specifications database. Update this table whenever camera spec -// has been changed or added/deleted supported camera models -//=========================================================================== -static CameraSpec dbCameraSpec[ N_CAMERAOPTIONS ] = -{ // Silicon Vision 512N - { Signal_CCIR656, VFormat_NTSC, VRef_alignedCb, Off, DecClk_GPCLK, TGB_NTSC624, 64, KHz19636, - // Clkx1_HACTIVE, Clkx1_HDELAY, VActive, VDelay, linesPerField; lineCount, Interlace - { 512, 0x64, 480, 0x13, 240 }, 0, TRUE - }, - // Logitech VideoMan 1.3 - { Signal_CCIR656, VFormat_NTSC, VRef_alignedCb, Off, DecClk_GPCLK, TGB_NTSC780, 64, KHz24545, - // Clkx1_HACTIVE, Clkx1_HDELAY, VActive, VDelay, linesPerField; lineCount, Interlace - { 640, 0x80, 480, 0x1A, 240 }, 0, TRUE - }, - // Rockwell QuartzSight - // Note: Fusion Rev D (rev ID 0x02) and later supports 16 pixels for HReset which is preferable. - // Use 32 for earlier version of hardware. Clkx1_HDELAY also changed from 0x27 to 0x20. - { Signal_CCIR656, VFormat_NTSC, VRef_alignedCb, Off, DecClk_GPCLK, TGB_NTSC392, 16, KHz28636, - // Clkx1_HACTIVE, Clkx1_HDELAY, VActive, VDelay, linesPerField; lineCount, Interlace - { 352, 0x20, 576, 0x08, 288 }, 607, FALSE - } -}; -*/ - -/* -The corresponding APIs required to be invoked are: -SetConnector( ConCamera, TRUE/FALSE ); -SetSignalFormat( spec.signal ); -SetVideoFormat( spec.vidFormat ); -SetSyncVideoRef( spec.syncRef ); -SetEnableSyncOutput( spec.syncOutput ); -SetTimGenByte( SRAMTable[ spec.tgb ], SRAMTableSize[ spec.tgb ] ); -SetHReset( spec.HReset ); -SetPLL( spec.pllFreq ); -SetDecInputClock( spec.iClk ); -SetVideoInfo( spec.vSize ); -SetTotalLineCount( spec.lineCount ); -SetInterlaceMode( spec.interlace ); -*/ - -/* from web: - Video Sampling -Digital video is a sampled form of analog video. The most common sampling schemes in use today are: - Pixel Clock Horiz Horiz Vert - Rate Total Active -NTSC square pixel 12.27 MHz 780 640 525 -NTSC CCIR-601 13.5 MHz 858 720 525 -NTSC 4FSc 14.32 MHz 910 768 525 -PAL square pixel 14.75 MHz 944 768 625 -PAL CCIR-601 13.5 MHz 864 720 625 -PAL 4FSc 17.72 MHz 1135 948 625 - -For the CCIR-601 standards, the sampling is based on a static orthogonal sampling grid. The luminance component (Y) is sampled at 13.5 MHz, while the two color difference signals, Cr and Cb are sampled at half that, or 6.75 MHz. The Cr and Cb samples are colocated with alternate Y samples, and they are taken at the same position on each line, such that one sample is coincident with the 50% point of the falling edge of analog sync. The samples are coded to either 8 or 10 bits per component. -*/ - -/* from DScaler:*/ -/* -//=========================================================================== -// CCIR656 Digital Input Support: The tables were taken from DScaler proyect -// -// 13 Dec 2000 - Michael Eskin, Conexant Systems - Initial version -// - -//=========================================================================== -// Timing generator SRAM table values for CCIR601 720x480 NTSC -//=========================================================================== -// For NTSC CCIR656 -BYTE BtCard::SRAMTable_NTSC[] = -{ - // SRAM Timing Table for NTSC - 0x0c, 0xc0, 0x00, - 0x00, 0x90, 0xc2, - 0x03, 0x10, 0x03, - 0x06, 0x10, 0x34, - 0x12, 0x12, 0x65, - 0x02, 0x13, 0x24, - 0x19, 0x00, 0x24, - 0x39, 0x00, 0x96, - 0x59, 0x08, 0x93, - 0x83, 0x08, 0x97, - 0x03, 0x50, 0x30, - 0xc0, 0x40, 0x30, - 0x86, 0x01, 0x01, - 0xa6, 0x0d, 0x62, - 0x03, 0x11, 0x61, - 0x05, 0x37, 0x30, - 0xac, 0x21, 0x50 -}; - -//=========================================================================== -// Timing generator SRAM table values for CCIR601 720x576 NTSC -//=========================================================================== -// For PAL CCIR656 -BYTE BtCard::SRAMTable_PAL[] = -{ - // SRAM Timing Table for PAL - 0x36, 0x11, 0x01, - 0x00, 0x90, 0x02, - 0x05, 0x10, 0x04, - 0x16, 0x14, 0x05, - 0x11, 0x00, 0x04, - 0x12, 0xc0, 0x00, - 0x31, 0x00, 0x06, - 0x51, 0x08, 0x03, - 0x89, 0x08, 0x07, - 0xc0, 0x44, 0x00, - 0x81, 0x01, 0x01, - 0xa9, 0x0d, 0x02, - 0x02, 0x50, 0x03, - 0x37, 0x3d, 0x00, - 0xaf, 0x21, 0x00, -}; -*/ diff --git a/linux/drivers/media/video/bt8xx/bttv-cards.c b/linux/drivers/media/video/bt8xx/bttv-cards.c index a00228893..aea093419 100644 --- a/linux/drivers/media/video/bt8xx/bttv-cards.c +++ b/linux/drivers/media/video/bt8xx/bttv-cards.c @@ -41,14 +41,10 @@ #include "bttvp.h" #include #include -#if 0 /* not working yet */ -#include "bt832.h" -#endif #include "bttv-audio-hook.h" /* fwd decl */ static void boot_msp34xx(struct bttv *btv, int pin); -static void boot_bt832(struct bttv *btv); static void hauppauge_eeprom(struct bttv *btv); static void avermedia_eeprom(struct bttv *btv); static void osprey_eeprom(struct bttv *btv, const u8 ee[256]); @@ -3762,13 +3758,6 @@ void __devinit bttv_init_card2(struct bttv *btv) if (bttv_tvcards[btv->c.type].audio_mode_gpio) btv->audio_mode_gpio=bttv_tvcards[btv->c.type].audio_mode_gpio; - if (bttv_tvcards[btv->c.type].digital_mode == DIGITAL_MODE_CAMERA) { - /* detect Bt832 chip for quartzsight digital camera */ - if ((bttv_I2CRead(btv, I2C_ADDR_BT832_ALT1, "Bt832") >=0) || - (bttv_I2CRead(btv, I2C_ADDR_BT832_ALT2, "Bt832") >=0)) - boot_bt832(btv); - } - if (!autoload) return; @@ -4172,40 +4161,6 @@ static void __devinit boot_msp34xx(struct bttv *btv, int pin) "init [%d]\n", btv->c.nr, pin); } -static void __devinit boot_bt832(struct bttv *btv) -{ -#if 0 /* not working yet */ - int resetbit=0; - - switch (btv->c.type) { - case BTTV_BOARD_PXELVWPLTVPAK: - resetbit = 0x400000; - break; - case BTTV_BOARD_MODTEC_205: - resetbit = 1<<9; - break; - default: - BUG(); - } - - request_module("bt832"); - bttv_call_i2c_clients(btv, BT832_HEXDUMP, NULL); - - printk("bttv%d: Reset Bt832 [line=0x%x]\n",btv->c.nr,resetbit); - gpio_write(0); - gpio_inout(resetbit, resetbit); - udelay(5); - gpio_bits(resetbit, resetbit); - udelay(5); - gpio_bits(resetbit, 0); - udelay(5); - - /* bt832 on pixelview changes from i2c 0x8a to 0x88 after - * being reset as above. So we must follow by this: */ - bttv_call_i2c_clients(btv, BT832_REATTACH, NULL); -#endif -} - /* ----------------------------------------------------------------------- */ /* Imagenation L-Model PXC200 Framegrabber */ /* This is basically the same procedure as diff --git a/linux/include/media/i2c-addr.h b/linux/include/media/i2c-addr.h index e7ff44a35..5d0f56054 100644 --- a/linux/include/media/i2c-addr.h +++ b/linux/include/media/i2c-addr.h @@ -12,8 +12,6 @@ /* bttv address list */ #define I2C_ADDR_TSA5522 0xc2 #define I2C_ADDR_TDA7432 0x8a -#define I2C_ADDR_BT832_ALT1 0x88 -#define I2C_ADDR_BT832_ALT2 0x8a // alternate setting #define I2C_ADDR_TDA8425 0x82 #define I2C_ADDR_TDA9840 0x84 #define I2C_ADDR_TDA9850 0xb6 /* also used by 9855,9873 */ -- cgit v1.2.3 From eebdebc91edbad9e91a7ac0b8762b12fd4160b0b Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Fri, 19 Dec 2008 11:21:26 +0100 Subject: gspca - spca561: Separate the bridge and sensor tables of Rev72a. From: Jean-Francois Moine Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/spca561.c | 110 +++++++++++++----------------- 1 file changed, 49 insertions(+), 61 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/spca561.c b/linux/drivers/media/video/gspca/spca561.c index 6114ac5f7..f518bb02f 100644 --- a/linux/drivers/media/video/gspca/spca561.c +++ b/linux/drivers/media/video/gspca/spca561.c @@ -146,7 +146,7 @@ static struct v4l2_pix_format sif_072a_mode[] = { #define SPCA561_SNAPBIT 0x20 #define SPCA561_SNAPCTRL 0x40 -static const __u16 rev72a_init_data[][2] = { +static const __u16 rev72a_init_data1[][2] = { {0x0000, 0x8114}, /* Software GPIO output data */ {0x0001, 0x8114}, /* Software GPIO output data */ {0x0000, 0x8112}, /* Some kind of reset */ @@ -156,44 +156,26 @@ static const __u16 rev72a_init_data[][2] = { {0x0001, 0x8118}, /* Conf sensor */ {0x0092, 0x8804}, /* I know nothing about these */ {0x0010, 0x8802}, /* 0x88xx registers, so I won't */ - /***************/ {0x000d, 0x8805}, /* sensor default setting */ - {0x0001, 0x8801}, /* 1 <- 0x0d */ - {0x0000, 0x8800}, - {0x0018, 0x8805}, - {0x0002, 0x8801}, /* 2 <- 0x18 */ - {0x0000, 0x8800}, - {0x0065, 0x8805}, - {0x0004, 0x8801}, /* 4 <- 0x01 0x65 */ - {0x0001, 0x8800}, - {0x0021, 0x8805}, - {0x0005, 0x8801}, /* 5 <- 0x21 */ - {0x0000, 0x8800}, - {0x00aa, 0x8805}, - {0x0007, 0x8801}, /* 7 <- 0xaa */ - {0x0000, 0x8800}, - {0x0004, 0x8805}, - {0x0020, 0x8801}, /* 0x20 <- 0x15 0x04 */ - {0x0015, 0x8800}, - {0x0002, 0x8805}, - {0x0039, 0x8801}, /* 0x39 <- 0x02 */ - {0x0000, 0x8800}, - {0x0010, 0x8805}, - {0x0035, 0x8801}, /* 0x35 <- 0x10 */ - {0x0000, 0x8800}, - {0x0049, 0x8805}, - {0x0009, 0x8801}, /* 0x09 <- 0x10 0x49 */ - {0x0010, 0x8800}, - {0x000b, 0x8805}, - {0x0028, 0x8801}, /* 0x28 <- 0x0b */ - {0x0000, 0x8800}, - {0x000f, 0x8805}, - {0x003b, 0x8801}, /* 0x3b <- 0x0f */ - {0x0000, 0x8800}, - {0x0000, 0x8805}, - {0x003c, 0x8801}, /* 0x3c <- 0x00 */ - {0x0000, 0x8800}, - /***************/ + {} +}; +static const __u16 rev72a_init_sensor1[][2] = { + /* ms-win values */ + {0x0001, 0x0018}, /* 0x01 <- 0x0d */ + {0x0002, 0x0065}, /* 0x02 <- 0x18 */ + {0x0004, 0x0121}, /* 0x04 <- 0x0165 */ + {0x0005, 0x00aa}, /* 0x05 <- 0x21 */ + {0x0007, 0x0004}, /* 0x07 <- 0xaa */ + {0x0020, 0x1502}, /* 0x20 <- 0x1504 */ + {0x0039, 0x0010}, /* 0x39 <- 0x02 */ + {0x0035, 0x0049}, /* 0x35 <- 0x10 */ + {0x0009, 0x100b}, /* 0x09 <- 0x1049 */ + {0x0028, 0x000f}, /* 0x28 <- 0x0b */ + {0x003b, 0x003c}, /* 0x3b <- 0x0f */ + {0x003c, 0x0000}, /* 0x3c <- 0x00 */ + {} +}; +static const __u16 rev72a_init_data2[][2] = { {0x0018, 0x8601}, /* Pixel/line selection for color separation */ {0x0000, 0x8602}, /* Optical black level for user setting */ {0x0060, 0x8604}, /* Optical black horizontal offset */ @@ -241,29 +223,22 @@ static const __u16 rev72a_init_data[][2] = { {0x0002, 0x865b}, /* Horizontal offset for valid pixels */ {0x0003, 0x865c}, /* Vertical offset for valid lines */ - /***************/ /* sensor active */ - {0x0003, 0x8801}, /* 0x03 <- 0x01 0x21 //289 */ - {0x0021, 0x8805}, - {0x0001, 0x8800}, - {0x0004, 0x8801}, /* 0x04 <- 0x01 0x65 //357 */ - {0x0065, 0x8805}, - {0x0001, 0x8800}, - {0x0005, 0x8801}, /* 0x05 <- 0x2f */ - {0x002f, 0x8805}, - {0x0000, 0x8800}, - {0x0006, 0x8801}, /* 0x06 <- 0 */ - {0x0000, 0x8805}, - {0x0000, 0x8800}, - {0x000a, 0x8801}, /* 0x0a <- 2 */ - {0x0002, 0x8805}, - {0x0000, 0x8800}, - {0x0009, 0x8801}, /* 0x09 <- 0x1061 */ - {0x0061, 0x8805}, - {0x0010, 0x8800}, - {0x0035, 0x8801}, /* 0x35 <-0x14 */ - {0x0014, 0x8805}, - {0x0000, 0x8800}, + {} +}; +static const __u16 rev72a_init_sensor2[][2] = { + /* ms-win values */ + {0x0003, 0x0121}, /* 0x03 <- 0x01 0x21 //289 */ + {0x0004, 0x0165}, /* 0x04 <- 0x01 0x65 //357 */ + {0x0005, 0x002f}, /* 0x05 <- 0x2f */ + {0x0006, 0x0000}, /* 0x06 <- 0 */ + {0x000a, 0x0002}, /* 0x0a <- 2 */ + {0x0009, 0x1061}, /* 0x09 <- 0x1061 */ + {0x0035, 0x0014}, /* 0x35 <- 0x14 */ + {} +}; +static const __u16 rev72a_init_data3[][2] = { {0x0030, 0x8112}, /* ISO and drop packet enable */ +/*fixme: should stop here*/ {0x0000, 0x8112}, /* Some kind of reset ???? */ {0x0009, 0x8118}, /* Enable sensor and set standby */ {0x0000, 0x8114}, /* Software GPIO output data */ @@ -526,6 +501,15 @@ static void sensor_mapwrite(struct gspca_dev *gspca_dev, } } +static void write_sensor_72a(struct gspca_dev *gspca_dev, + const __u16 (*sensor)[2]) +{ + while ((*sensor)[0]) { + i2c_write(gspca_dev, (*sensor)[1], (*sensor)[0]); + sensor++; + } +} + static void init_161rev12A(struct gspca_dev *gspca_dev) { write_vector(gspca_dev, spca561_161rev12A_data1); @@ -595,7 +579,11 @@ static int sd_init_12a(struct gspca_dev *gspca_dev) static int sd_init_72a(struct gspca_dev *gspca_dev) { PDEBUG(D_STREAM, "Chip revision: 072a"); - write_vector(gspca_dev, rev72a_init_data); + write_vector(gspca_dev, rev72a_init_data1); + write_sensor_72a(gspca_dev, rev72a_init_sensor1); + write_vector(gspca_dev, rev72a_init_data2); + write_sensor_72a(gspca_dev, rev72a_init_sensor2); + write_vector(gspca_dev, rev72a_init_data3); return 0; } -- cgit v1.2.3 From 4a1ce8a5db70639704279510654844e70138807e Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 19 Dec 2008 11:36:46 +0100 Subject: cx24113: fix compile warnings From: Hans Verkuil Priority: normal Signed-off-by: Hans Verkuil --- linux/drivers/media/dvb/frontends/cx24113.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/dvb/frontends/cx24113.c b/linux/drivers/media/dvb/frontends/cx24113.c index 93f35941a..f6e7b0380 100644 --- a/linux/drivers/media/dvb/frontends/cx24113.c +++ b/linux/drivers/media/dvb/frontends/cx24113.c @@ -345,12 +345,12 @@ static void cx24113_calc_pll_nf(struct cx24113_state *state, u16 *n, s32 *f) } F = freq_hz; F *= (u64) (R * vcodiv * 262144); - dprintk("1 N: %d, F: %lld, R: %d\n", N, F, R); + dprintk("1 N: %d, F: %lld, R: %d\n", N, (long long)F, R); do_div(F, state->config->xtal_khz*1000 * factor * 2); - dprintk("2 N: %d, F: %lld, R: %d\n", N, F, R); + dprintk("2 N: %d, F: %lld, R: %d\n", N, (long long)F, R); F -= (N + 32) * 262144; - dprintk("3 N: %d, F: %lld, R: %d\n", N, F, R); + dprintk("3 N: %d, F: %lld, R: %d\n", N, (long long)F, R); if (state->Fwindow_enabled) { if (F > (262144 / 2 - 1638)) @@ -363,7 +363,7 @@ static void cx24113_calc_pll_nf(struct cx24113_state *state, u16 *n, s32 *f) cx24113_writereg(state, 0x10, r | (1 << 6)); } } - dprintk("4 N: %d, F: %lld, R: %d\n", N, F, R); + dprintk("4 N: %d, F: %lld, R: %d\n", N, (long long)F, R); *n = (u16) N; *f = (s32) F; -- cgit v1.2.3 From 2db770287e0d030f614cc7c312c7e5e96041b672 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 19 Dec 2008 13:10:56 +0100 Subject: v4l2-dev: check for parent device in get_index. From: Hans Verkuil get_index requires a valid parent device in order to discover which indices are in use. Some drivers (e.g. pvrusb2) do not set the parent device. In that case just return 0. Priority: normal Signed-off-by: Hans Verkuil --- linux/drivers/media/video/v4l2-dev.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'linux') diff --git a/linux/drivers/media/video/v4l2-dev.c b/linux/drivers/media/video/v4l2-dev.c index 3b8f33c8d..91849084f 100644 --- a/linux/drivers/media/video/v4l2-dev.c +++ b/linux/drivers/media/video/v4l2-dev.c @@ -194,6 +194,10 @@ static int get_index(struct video_device *vdev, int num) return -EINVAL; } + /* Some drivers do not set the parent. In that case always return 0. */ + if (vdev->parent == NULL) + return 0; + for (i = 0; i < VIDEO_NUM_DEVICES; i++) { if (video_device[i] != NULL && video_device[i] != vdev && -- cgit v1.2.3 From 1260044709b00c199d5cc6d8afce99b6aeea019b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 19 Dec 2008 14:20:22 +0100 Subject: v4l2: document video_device. From: Hans Verkuil Add the missing video_device documentation to v4l2-framework.txt. Priority: normal Signed-off-by: Hans Verkuil --- linux/Documentation/video4linux/v4l2-framework.txt | 160 ++++++++++++++++++++- 1 file changed, 159 insertions(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/Documentation/video4linux/v4l2-framework.txt b/linux/Documentation/video4linux/v4l2-framework.txt index 60eaf54e7..eeae76c22 100644 --- a/linux/Documentation/video4linux/v4l2-framework.txt +++ b/linux/Documentation/video4linux/v4l2-framework.txt @@ -86,6 +86,9 @@ to v4l2_dev. Registration will also set v4l2_dev->name to a value derived from dev (driver name followed by the bus_id, to be precise). You may change the name after registration if you want. +The first 'dev' argument is normally the struct device pointer of a pci_dev, +usb_device or platform_device. + You unregister with: v4l2_device_unregister(struct v4l2_device *v4l2_dev); @@ -359,4 +362,159 @@ Both functions return NULL if something went wrong. struct video_device ------------------- -Not yet documented. +The actual device nodes in the /dev directory are created using the +video_device struct (v4l2-dev.h). This struct can either be allocated +dynamically or embedded in a larger struct. + +To allocate it dynamically use: + + struct video_device *vdev = video_device_alloc(); + + if (vdev == NULL) + return -ENOMEM; + + vdev->release = video_device_release; + +If you embed it in a larger struct, then you must set the release() +callback to your own function: + + struct video_device *vdev = &my_vdev->vdev; + + vdev->release = my_vdev_release; + +The release callback must be set and it is called when the last user +of the video device exits. + +The default video_device_release() callback just calls kfree to free the +allocated memory. + +You should also set these fields: + +- parent: set to the parent device (same device as was used to register + v4l2_device). +- name: set to something descriptive and unique. +- fops: set to the file_operations struct. +- ioctl_ops: if you use the v4l2_ioctl_ops to simplify ioctl maintenance + (highly recommended to use this and it might become compulsory in the + future!), then set this to your v4l2_ioctl_ops struct. + +If you use v4l2_ioctl_ops, then you should set .unlocked_ioctl to +__video_ioctl2 or .ioctl to video_ioctl2 in your file_operations struct. + + +video_device registration +------------------------- + +Next you register the video device: this will create the character device +for you. + + err = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + if (err) { + video_device_release(vdev); // or kfree(my_vdev); + return err; + } + +Which device is registered depends on the type argument. The following +types exist: + +VFL_TYPE_GRABBER: videoX for video input/output devices +VFL_TYPE_VBI: vbiX for vertical blank data (i.e. closed captions, teletext) +VFL_TYPE_RADIO: radioX for radio tuners +VFL_TYPE_VTX: vtxX for teletext devices (deprecated, don't use) + +The last argument gives you a certain amount of control over the device +kernel number used (i.e. the X in videoX). Normally you will pass -1 to +let the v4l2 framework pick the first free number. But if a driver creates +many devices, then it can be useful to have different video devices in +separate ranges. For example, video capture devices start at 0, video +output devices start at 16. + +So you can use the last argument to specify a minimum kernel number and +the v4l2 framework will try to pick the first free number that is equal +or higher to what you passed. If that fails, then it will just pick the +first free number. + +Whenever a device node is created some attributes are also created for you. +If you look in /sys/class/video4linux you see the devices. Go into e.g. +video0 and you will see 'name' and 'index' attributes. The 'name' attribute +is the 'name' field of the video_device struct. The 'index' attribute is +a device node index that can be assigned by the driver, or that is calculated +for you. + +If you call video_register_device(), then the index is just increased by +1 for each device node you register. The first video device node you register +always starts off with 0. + +Alternatively you can call video_register_device_index() which is identical +to video_register_device(), but with an extra index argument. Here you can +pass a specific index value (between 0 and 31) that should be used. + +Users can setup udev rules that utilize the index attribute to make fancy +device names (e.g. 'mpegX' for MPEG video capture device nodes). + +After the device was successfully registered, then you can use these fields: + +- vfl_type: the device type passed to video_register_device. +- minor: the assigned device minor number. +- num: the device kernel number (i.e. the X in videoX). +- index: the device index number (calculated or set explicitly using + video_register_device_index). + +If the registration failed, then you need to call video_device_release() +to free the allocated video_device struct, or free your own struct if the +video_device was embedded in it. The vdev->release() callback will never +be called if the registration failed, nor should you ever attempt to +unregister the device if the registration failed. + + +video_device cleanup +-------------------- + +When the video device nodes have to be removed, either during the unload +of the driver or because the USB device was disconnected, then you should +unregister them: + + video_unregister_device(vdev); + +This will remove the device nodes from sysfs (causing udev to remove them +from /dev). + +After video_unregister_device() returns no new opens can be done. + +However, in the case of USB devices some application might still have one +of these device nodes open. You should block all new accesses to read, +write, poll, etc. except possibly for certain ioctl operations like +queueing buffers. + +When the last user of the video device node exits, then the vdev->release() +callback is called and you can do the final cleanup there. + + +video_device helper functions +----------------------------- + +There are a few useful helper functions: + +You can set/get driver private data in the video_device struct using: + +void *video_get_drvdata(struct video_device *dev); +void video_set_drvdata(struct video_device *dev, void *data); + +Note that you can safely call video_set_drvdata() before calling +video_register_device(). + +And this function: + +struct video_device *video_devdata(struct file *file); + +returns the video_device belonging to the file struct. + +The final helper function combines video_get_drvdata with +video_devdata: + +void *video_drvdata(struct file *file); + +You can go from a video_device struct to the v4l2_device struct using: + +struct v4l2_device *v4l2_dev = dev_get_drvdata(vdev->parent); + -- cgit v1.2.3 From ff63d4ba6fa540c90a770d99026921965ab56c80 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 19 Dec 2008 14:34:22 +0100 Subject: cs5345: convert to v4l2_subdev and fix broken s_ctrl. From: Hans Verkuil Converted to v4l2_subdev. While doing that I also discovered a stray 'break' that broke the S_CTRL handling. Fixed that as well. Priority: normal Signed-off-by: Hans Verkuil --- linux/drivers/media/video/cs5345.c | 227 ++++++++++++++++++++++--------------- 1 file changed, 138 insertions(+), 89 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/cs5345.c b/linux/drivers/media/video/cs5345.c index decbc5f1f..1af0f53dd 100644 --- a/linux/drivers/media/video/cs5345.c +++ b/linux/drivers/media/video/cs5345.c @@ -23,9 +23,9 @@ #include #include #include -#include -#include +#include #include +#include #include "compat.h" MODULE_DESCRIPTION("i2c device driver for cs5345 Audio ADC"); @@ -46,111 +46,143 @@ I2C_CLIENT_INSMOD; /* ----------------------------------------------------------------------- */ -static inline int cs5345_write(struct i2c_client *client, u8 reg, u8 value) +static inline int cs5345_write(struct v4l2_subdev *sd, u8 reg, u8 value) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + return i2c_smbus_write_byte_data(client, reg, value); } -static inline int cs5345_read(struct i2c_client *client, u8 reg) +static inline int cs5345_read(struct v4l2_subdev *sd, u8 reg) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + return i2c_smbus_read_byte_data(client, reg); } -static int cs5345_command(struct i2c_client *client, unsigned cmd, void *arg) +static int cs5345_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) { - struct v4l2_routing *route = arg; - struct v4l2_control *ctrl = arg; - - switch (cmd) { - case VIDIOC_INT_G_AUDIO_ROUTING: - route->input = cs5345_read(client, 0x09) & 7; - route->input |= cs5345_read(client, 0x05) & 0x70; - route->output = 0; - break; - - case VIDIOC_INT_S_AUDIO_ROUTING: - if ((route->input & 0xf) > 6) { - v4l_err(client, "Invalid input %d.\n", route->input); - return -EINVAL; - } - cs5345_write(client, 0x09, route->input & 0xf); - cs5345_write(client, 0x05, route->input & 0xf0); - break; - - case VIDIOC_G_CTRL: - if (ctrl->id == V4L2_CID_AUDIO_MUTE) { - ctrl->value = (cs5345_read(client, 0x04) & 0x08) != 0; - break; - } - if (ctrl->id != V4L2_CID_AUDIO_VOLUME) - return -EINVAL; - ctrl->value = cs5345_read(client, 0x07) & 0x3f; - if (ctrl->value >= 32) - ctrl->value = ctrl->value - 64; - break; - - case VIDIOC_S_CTRL: - break; - if (ctrl->id == V4L2_CID_AUDIO_MUTE) { - cs5345_write(client, 0x04, ctrl->value ? 0x80 : 0); - break; - } - if (ctrl->id != V4L2_CID_AUDIO_VOLUME) - return -EINVAL; - if (ctrl->value > 24 || ctrl->value < -24) - return -EINVAL; - cs5345_write(client, 0x07, ((u8)ctrl->value) & 0x3f); - cs5345_write(client, 0x08, ((u8)ctrl->value) & 0x3f); - break; - -#ifdef CONFIG_VIDEO_ADV_DEBUG - case VIDIOC_DBG_G_REGISTER: - case VIDIOC_DBG_S_REGISTER: - { - struct v4l2_register *reg = arg; - - if (!v4l2_chip_match_i2c_client(client, - reg->match_type, reg->match_chip)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - if (cmd == VIDIOC_DBG_G_REGISTER) - reg->val = cs5345_read(client, reg->reg & 0x1f); - else - cs5345_write(client, reg->reg & 0x1f, reg->val & 0xff); - break; + if ((route->input & 0xf) > 6) { + v4l2_err(sd, "Invalid input %d.\n", route->input); + return -EINVAL; } -#endif + cs5345_write(sd, 0x09, route->input & 0xf); + cs5345_write(sd, 0x05, route->input & 0xf0); + return 0; +} - case VIDIOC_G_CHIP_IDENT: - return v4l2_chip_ident_i2c_client(client, - arg, V4L2_IDENT_CS5345, 0); - - case VIDIOC_LOG_STATUS: - { - u8 v = cs5345_read(client, 0x09) & 7; - u8 m = cs5345_read(client, 0x04); - int vol = cs5345_read(client, 0x08) & 0x3f; - - v4l_info(client, "Input: %d%s\n", v, - (m & 0x80) ? " (muted)" : ""); - if (vol >= 32) - vol = vol - 64; - v4l_info(client, "Volume: %d dB\n", vol); - break; - } - - default: +static int cs5345_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + if (ctrl->id == V4L2_CID_AUDIO_MUTE) { + ctrl->value = (cs5345_read(sd, 0x04) & 0x08) != 0; + return 0; + } + if (ctrl->id != V4L2_CID_AUDIO_VOLUME) return -EINVAL; + ctrl->value = cs5345_read(sd, 0x07) & 0x3f; + if (ctrl->value >= 32) + ctrl->value = ctrl->value - 64; + return 0; +} + +static int cs5345_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + if (ctrl->id == V4L2_CID_AUDIO_MUTE) { + cs5345_write(sd, 0x04, ctrl->value ? 0x80 : 0); + return 0; } + if (ctrl->id != V4L2_CID_AUDIO_VOLUME) + return -EINVAL; + if (ctrl->value > 24 || ctrl->value < -24) + return -EINVAL; + cs5345_write(sd, 0x07, ((u8)ctrl->value) & 0x3f); + cs5345_write(sd, 0x08, ((u8)ctrl->value) & 0x3f); + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int cs5345_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, + reg->match_type, reg->match_chip)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reg->val = cs5345_read(sd, reg->reg & 0x1f); + return 0; +} + +static int cs5345_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, + reg->match_type, reg->match_chip)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + cs5345_write(sd, reg->reg & 0x1f, reg->val & 0xff); return 0; } +#endif + +static int cs5345_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_CS5345, 0); +} + +static int cs5345_log_status(struct v4l2_subdev *sd) +{ + u8 v = cs5345_read(sd, 0x09) & 7; + u8 m = cs5345_read(sd, 0x04); + int vol = cs5345_read(sd, 0x08) & 0x3f; + + v4l2_info(sd, "Input: %d%s\n", v, + (m & 0x80) ? " (muted)" : ""); + if (vol >= 32) + vol = vol - 64; + v4l2_info(sd, "Volume: %d dB\n", vol); + return 0; +} + +static int cs5345_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops cs5345_core_ops = { + .log_status = cs5345_log_status, + .g_chip_ident = cs5345_g_chip_ident, + .g_ctrl = cs5345_g_ctrl, + .s_ctrl = cs5345_s_ctrl, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = cs5345_g_register, + .s_register = cs5345_s_register, +#endif +}; + +static const struct v4l2_subdev_audio_ops cs5345_audio_ops = { + .s_routing = cs5345_s_routing, +}; + +static const struct v4l2_subdev_ops cs5345_ops = { + .core = &cs5345_core_ops, + .audio = &cs5345_audio_ops, +}; /* ----------------------------------------------------------------------- */ static int cs5345_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct v4l2_subdev *sd; + /* Check if the adapter supports the needed features */ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -EIO; @@ -158,9 +190,25 @@ static int cs5345_probe(struct i2c_client *client, v4l_info(client, "chip found @ 0x%x (%s)\n", client->addr << 1, client->adapter->name); - cs5345_write(client, 0x02, 0x00); - cs5345_write(client, 0x04, 0x01); - cs5345_write(client, 0x09, 0x01); + sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); + if (sd == NULL) + return -ENOMEM; + v4l2_i2c_subdev_init(sd, client, &cs5345_ops); + + cs5345_write(sd, 0x02, 0x00); + cs5345_write(sd, 0x04, 0x01); + cs5345_write(sd, 0x09, 0x01); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int cs5345_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(sd); return 0; } @@ -179,6 +227,7 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = { .driverid = I2C_DRIVERID_CS5345, .command = cs5345_command, .probe = cs5345_probe, + .remove = cs5345_remove, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) .id_table = cs5345_id, #endif -- cgit v1.2.3 From 1d6cf736e1fc3df22e0f38fa20894cb96e1d32fc Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 19 Dec 2008 15:17:56 +0100 Subject: videodev2.h: fix typo. From: Hans Verkuil The comment said CX2584X instead of CX2341X. Priority: normal Signed-off-by: Hans Verkuil --- linux/include/linux/videodev2.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/include/linux/videodev2.h b/linux/include/linux/videodev2.h index 6ee771108..96624e29f 100644 --- a/linux/include/linux/videodev2.h +++ b/linux/include/linux/videodev2.h @@ -1051,7 +1051,7 @@ enum v4l2_mpeg_video_bitrate_mode { #define V4L2_CID_MPEG_VIDEO_MUTE (V4L2_CID_MPEG_BASE+210) #define V4L2_CID_MPEG_VIDEO_MUTE_YUV (V4L2_CID_MPEG_BASE+211) -/* MPEG-class control IDs specific to the CX2584x driver as defined by V4L2 */ +/* MPEG-class control IDs specific to the CX2341x driver as defined by V4L2 */ #define V4L2_CID_MPEG_CX2341X_BASE (V4L2_CTRL_CLASS_MPEG | 0x1000) #define V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE (V4L2_CID_MPEG_CX2341X_BASE+0) enum v4l2_mpeg_cx2341x_video_spatial_filter_mode { -- cgit v1.2.3 From 6880329c518c82eee1f0cbae9d1710285b8a1b3e Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Fri, 19 Dec 2008 19:08:51 +0100 Subject: gspca - main: Remove useless tests of the buffer type. From: Jean-Francois Moine The buffer type is already checked in v4l2-ioctl. Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/gspca.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/gspca.c b/linux/drivers/media/video/gspca/gspca.c index 71e7afe1b..920e01c0d 100644 --- a/linux/drivers/media/video/gspca/gspca.c +++ b/linux/drivers/media/video/gspca/gspca.c @@ -785,8 +785,6 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct gspca_dev *gspca_dev = priv; int mode; - if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; mode = gspca_dev->curr_mode; memcpy(&fmt->fmt.pix, &gspca_dev->cam.cam_mode[mode], sizeof fmt->fmt.pix); @@ -798,8 +796,6 @@ static int try_fmt_vid_cap(struct gspca_dev *gspca_dev, { int w, h, mode, mode2; - if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; w = fmt->fmt.pix.width; h = fmt->fmt.pix.height; @@ -1153,8 +1149,6 @@ static int vidioc_reqbufs(struct file *file, void *priv, struct gspca_dev *gspca_dev = priv; int i, ret = 0; - if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; switch (rb->memory) { case GSPCA_MEMORY_READ: /* (internal call) */ case V4L2_MEMORY_MMAP: @@ -1219,8 +1213,7 @@ static int vidioc_querybuf(struct file *file, void *priv, struct gspca_dev *gspca_dev = priv; struct gspca_frame *frame; - if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE - || v4l2_buf->index < 0 + if (v4l2_buf->index < 0 || v4l2_buf->index >= gspca_dev->nframes) return -EINVAL; @@ -1561,8 +1554,6 @@ static int vidioc_dqbuf(struct file *file, void *priv, int i, ret; PDEBUG(D_FRAM, "dqbuf"); - if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; if (v4l2_buf->memory != gspca_dev->memory) return -EINVAL; @@ -1617,8 +1608,6 @@ static int vidioc_qbuf(struct file *file, void *priv, int i, index, ret; PDEBUG(D_FRAM, "qbuf %d", v4l2_buf->index); - if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; if (mutex_lock_interruptible(&gspca_dev->queue_lock)) return -ERESTARTSYS; -- cgit v1.2.3 From 21d405e38a20de8a1d21656ffb140a84ffad72be Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Fri, 19 Dec 2008 19:17:25 +0100 Subject: gspca - main: Check if a buffer has been queued on streamon. From: Jean-Francois Moine Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/gspca.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/gspca.c b/linux/drivers/media/video/gspca/gspca.c index 920e01c0d..5d2f0ab93 100644 --- a/linux/drivers/media/video/gspca/gspca.c +++ b/linux/drivers/media/video/gspca/gspca.c @@ -1236,7 +1236,8 @@ static int vidioc_streamon(struct file *file, void *priv, ret = -ENODEV; goto out; } - if (gspca_dev->nframes == 0) { + if (gspca_dev->nframes == 0 + || !(gspca_dev->frame[0].v4l2_buf.flags & V4L2_BUF_FLAG_QUEUED)) { ret = -EINVAL; goto out; } -- cgit v1.2.3 From a012e750305d20ec8b2f79b6a32c5e7920096fd3 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 20 Dec 2008 01:28:27 +0100 Subject: v4l2-dev: use the release callback from device instead of cdev From: Hans Verkuil Instead of relying on the cdev release callback we should rely on the release callback from the device struct. This requires that we use get_device/put_device to do proper refcounting. In order to do this safely v4l2-dev.c now sets up its own file_operations that call out to the driver's ops. Priority: normal Signed-off-by: Hans Verkuil --- linux/drivers/media/video/v4l2-dev.c | 417 +++++++++++++++++++++++++---------- linux/include/media/v4l2-dev.h | 51 +++-- 2 files changed, 332 insertions(+), 136 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/v4l2-dev.c b/linux/drivers/media/video/v4l2-dev.c index 91849084f..f877d3f86 100644 --- a/linux/drivers/media/video/v4l2-dev.c +++ b/linux/drivers/media/video/v4l2-dev.c @@ -43,17 +43,17 @@ static ssize_t show_index(struct device *cd, struct device_attribute *attr, char *buf) { - struct video_device *vfd = container_of(cd, struct video_device, dev); + struct video_device *vdev = to_video_device(cd); - return sprintf(buf, "%i\n", vfd->index); + return sprintf(buf, "%i\n", vdev->index); } static ssize_t show_name(struct device *cd, struct device_attribute *attr, char *buf) { - struct video_device *vfd = container_of(cd, struct video_device, dev); + struct video_device *vdev = to_video_device(cd); - return sprintf(buf, "%.*s\n", (int)sizeof(vfd->name), vfd->name); + return sprintf(buf, "%.*s\n", (int)sizeof(vdev->name), vdev->name); } static struct device_attribute video_device_attrs[] = { @@ -64,16 +64,16 @@ static struct device_attribute video_device_attrs[] = { #else static ssize_t show_index(struct class_device *cd, char *buf) { - struct video_device *vfd = container_of(cd, struct video_device, dev); + struct video_device *vdev = to_video_device(cd); - return sprintf(buf, "%i\n", vfd->index); + return sprintf(buf, "%i\n", vdev->index); } static ssize_t show_name(struct class_device *cd, char *buf) { - struct video_device *vfd = container_of(cd, struct video_device, dev); + struct video_device *vdev = to_video_device(cd); - return sprintf(buf, "%.*s\n", (int)sizeof(vfd->name), vfd->name); + return sprintf(buf, "%.*s\n", (int)sizeof(vdev->name), vdev->name); } static CLASS_DEVICE_ATTR(index, S_IRUGO, show_index, NULL); @@ -93,71 +93,77 @@ struct video_device *video_device_alloc(void) } EXPORT_SYMBOL(video_device_alloc); -void video_device_release(struct video_device *vfd) +void video_device_release(struct video_device *vdev) { - kfree(vfd); + kfree(vdev); } EXPORT_SYMBOL(video_device_release); -void video_device_release_empty(struct video_device *vfd) +void video_device_release_empty(struct video_device *vdev) { /* Do nothing */ /* Only valid when the video_device struct is a static. */ } EXPORT_SYMBOL(video_device_release_empty); -/* Called when the last user of the character device is gone. */ -static void v4l2_chardev_release(struct kobject *kobj) +static inline void video_get(struct video_device *vdev) { - struct video_device *vfd = container_of(kobj, struct video_device, cdev.kobj); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) + class_device_get(&vdev->dev); +#else + get_device(&vdev->dev); +#endif +} + +static inline void video_put(struct video_device *vdev) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) + class_device_put(&vdev->dev); +#else + put_device(&vdev->dev); +#endif +} + +/* Called when the last user of the video device exits. */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) +static void v4l2_device_release(struct class_device *cd) +#else +static void v4l2_device_release(struct device *cd) +#endif +{ + struct video_device *vdev = to_video_device(cd); mutex_lock(&videodev_lock); - if (video_device[vfd->minor] != vfd) { + if (video_device[vdev->minor] != vdev) { mutex_unlock(&videodev_lock); - BUG(); + /* should not happen */ + WARN_ON(1); return; } /* Free up this device for reuse */ - video_device[vfd->minor] = NULL; - clear_bit(vfd->num, video_nums[vfd->vfl_type]); - mutex_unlock(&videodev_lock); + video_device[vdev->minor] = NULL; - /* Release the character device */ - vfd->cdev_release(kobj); - /* Release video_device and perform other - cleanups as needed. */ - if (vfd->release) - vfd->release(vfd); -} + /* Delete the cdev on this minor as well */ + cdev_del(vdev->cdev); + /* Just in case some driver tries to access this from + the release() callback. */ + vdev->cdev = NULL; -/* The new kobj_type for the character device */ -static struct kobj_type v4l2_ktype_cdev_default = { - .release = v4l2_chardev_release, -}; + /* Mark minor as free */ + clear_bit(vdev->num, video_nums[vdev->vfl_type]); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) -static void video_release(struct class_device *cd) -#else -static void video_release(struct device *cd) -#endif -{ - struct video_device *vfd = container_of(cd, struct video_device, dev); + mutex_unlock(&videodev_lock); - /* It's now safe to delete the char device. - This will either trigger the v4l2_chardev_release immediately (if - the refcount goes to 0) or later when the last user of the - character device closes it. */ - cdev_del(&vfd->cdev); + /* Release video_device and perform other + cleanups as needed. */ + vdev->release(vdev); } static struct class video_class = { .name = VIDEO_NAME, -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) - .release = video_release, -#else +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) .dev_attrs = video_device_attrs, - .dev_release = video_release, #endif }; @@ -171,13 +177,163 @@ struct video_device *video_devdata(struct file *file) } EXPORT_SYMBOL(video_devdata); +static ssize_t v4l2_read(struct file *filp, char __user *buf, + size_t sz, loff_t *off) +{ + struct video_device *vdev = video_devdata(filp); + + if (!vdev->fops->read) + return -EINVAL; + if (video_is_unregistered(vdev)) + return -EIO; + return vdev->fops->read(filp, buf, sz, off); +} + +static ssize_t v4l2_write(struct file *filp, const char __user *buf, + size_t sz, loff_t *off) +{ + struct video_device *vdev = video_devdata(filp); + + if (!vdev->fops->write) + return -EINVAL; + if (video_is_unregistered(vdev)) + return -EIO; + return vdev->fops->write(filp, buf, sz, off); +} + +static unsigned int v4l2_poll(struct file *filp, struct poll_table_struct *poll) +{ + struct video_device *vdev = video_devdata(filp); + + if (!vdev->fops->poll || video_is_unregistered(vdev)) + return DEFAULT_POLLMASK; + return vdev->fops->poll(filp, poll); +} + +static int v4l2_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + struct video_device *vdev = video_devdata(filp); + + if (!vdev->fops->ioctl) + return -ENOTTY; + /* Allow ioctl to continue even if the device was unregistered. + Things like dequeueing buffers might still be useful. */ + return vdev->fops->ioctl(inode, filp, cmd, arg); +} + +static long v4l2_unlocked_ioctl(struct file *filp, + unsigned int cmd, unsigned long arg) +{ + struct video_device *vdev = video_devdata(filp); + + if (!vdev->fops->unlocked_ioctl) + return -ENOTTY; + /* Allow ioctl to continue even if the device was unregistered. + Things like dequeueing buffers might still be useful. */ + return vdev->fops->unlocked_ioctl(filp, cmd, arg); +} + +#ifdef CONFIG_COMPAT +static long v4l2_compat_ioctl(struct file *filp, + unsigned int cmd, unsigned long arg) +{ + struct video_device *vdev = video_devdata(filp); + + if (!vdev->fops->compat_ioctl) + return -ENOIOCTLCMD; + /* Allow ioctl to continue even if the device was unregistered. + Things like dequeueing buffers might still be useful. */ + return vdev->fops->compat_ioctl(filp, cmd, arg); +} +#endif + +static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm) +{ + struct video_device *vdev = video_devdata(filp); + + if (!vdev->fops->mmap || + video_is_unregistered(vdev)) + return -ENODEV; + return vdev->fops->mmap(filp, vm); +} + +/* Override for the open function */ +static int v4l2_open(struct inode *inode, struct file *filp) +{ + struct video_device *vdev; + int ret; + + /* Check if the video device is available */ + mutex_lock(&videodev_lock); + vdev = video_devdata(filp); + /* return ENODEV if the video device has been removed + already or if it is not registered anymore. */ + if (vdev == NULL || video_is_unregistered(vdev)) { + mutex_unlock(&videodev_lock); + return -ENODEV; + } + /* and increase the device refcount */ + video_get(vdev); + mutex_unlock(&videodev_lock); + ret = vdev->fops->open(inode, filp); + /* decrease the refcount in case of an error */ + if (ret) + video_put(vdev); + return ret; +} + +/* Override for the release function */ +static int v4l2_release(struct inode *inode, struct file *filp) +{ + struct video_device *vdev = video_devdata(filp); + int ret = vdev->fops->release(inode, filp); + + /* decrease the refcount unconditionally since the release() + return value is ignored. */ + video_put(vdev); + return ret; +} + +static const struct file_operations v4l2_unlocked_fops = { + .owner = THIS_MODULE, + .read = v4l2_read, + .write = v4l2_write, + .open = v4l2_open, + .mmap = v4l2_mmap, + .unlocked_ioctl = v4l2_unlocked_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = v4l2_compat_ioctl, +#endif + .release = v4l2_release, + .poll = v4l2_poll, + .llseek = no_llseek, +}; + +static const struct file_operations v4l2_fops = { + .owner = THIS_MODULE, + .read = v4l2_read, + .write = v4l2_write, + .open = v4l2_open, + .mmap = v4l2_mmap, + .ioctl = v4l2_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = v4l2_compat_ioctl, +#endif + .release = v4l2_release, + .poll = v4l2_poll, + .llseek = no_llseek, +}; + /** * get_index - assign stream number based on parent device - * @vdev: video_device to assign index number to, vdev->dev should be assigned - * @num: -1 if auto assign, requested number otherwise + * @vdev: video_device to assign index number to, vdev->parent should be assigned + * @num: -1 if auto assign, requested number otherwise * + * Note that when this is called the new device has not yet been registered + * in the video_device array. * - * returns -ENFILE if num is already in use, a free index number if + * Returns -ENFILE if num is already in use, a free index number if * successful. */ static int get_index(struct video_device *vdev, int num) @@ -200,7 +356,6 @@ static int get_index(struct video_device *vdev, int num) for (i = 0; i < VIDEO_NUM_DEVICES; i++) { if (video_device[i] != NULL && - video_device[i] != vdev && video_device[i]->parent == vdev->parent) { used |= 1 << video_device[i]->index; } @@ -216,17 +371,15 @@ static int get_index(struct video_device *vdev, int num) return i > max_index ? -ENFILE : i; } -static const struct file_operations video_fops; - -int video_register_device(struct video_device *vfd, int type, int nr) +int video_register_device(struct video_device *vdev, int type, int nr) { - return video_register_device_index(vfd, type, nr, -1); + return video_register_device_index(vdev, type, nr, -1); } EXPORT_SYMBOL(video_register_device); /** * video_register_device_index - register video4linux devices - * @vfd: video device structure we want to register + * @vdev: video device structure we want to register * @type: type of device to register * @nr: which device number (0 == /dev/video0, 1 == /dev/video1, ... * -1 == first free) @@ -250,8 +403,7 @@ EXPORT_SYMBOL(video_register_device); * * %VFL_TYPE_RADIO - A radio card */ - -int video_register_device_index(struct video_device *vfd, int type, int nr, +int video_register_device_index(struct video_device *vdev, int type, int nr, int index) { int i = 0; @@ -259,14 +411,19 @@ int video_register_device_index(struct video_device *vfd, int type, int nr, int minor_offset = 0; int minor_cnt = VIDEO_NUM_DEVICES; const char *name_base; - void *priv = video_get_drvdata(vfd); + void *priv = video_get_drvdata(vdev); - /* the release callback MUST be present */ - BUG_ON(!vfd->release); + /* A minor value of -1 marks this video device as never + having been registered */ + if (vdev) + vdev->minor = -1; - if (vfd == NULL) + /* the release callback MUST be present */ + WARN_ON(!vdev || !vdev->release); + if (!vdev || !vdev->release) return -EINVAL; + /* Part 1: check device type */ switch (type) { case VFL_TYPE_GRABBER: name_base = "video"; @@ -286,8 +443,10 @@ int video_register_device_index(struct video_device *vfd, int type, int nr, return -EINVAL; } - vfd->vfl_type = type; + vdev->vfl_type = type; + vdev->cdev = NULL; + /* Part 2: find a free minor, kernel number and device index. */ #ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES /* Keep the ranges for the first four types for historical * reasons. @@ -318,14 +477,7 @@ int video_register_device_index(struct video_device *vfd, int type, int nr, } #endif - /* Initialize the character device */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 17) - cdev_init(&vfd->cdev, vfd->fops); -#else - cdev_init(&vfd->cdev, (struct file_operations *)vfd->fops); -#endif - vfd->cdev.owner = vfd->fops->owner; - /* pick a minor number */ + /* Pick a minor number */ mutex_lock(&videodev_lock); nr = find_next_zero_bit(video_nums[type], minor_cnt, nr == -1 ? 0 : nr); if (nr == minor_cnt) @@ -349,98 +501,125 @@ int video_register_device_index(struct video_device *vfd, int type, int nr, return -ENFILE; } #endif - vfd->minor = i + minor_offset; - vfd->num = nr; + vdev->minor = i + minor_offset; + vdev->num = nr; set_bit(nr, video_nums[type]); - BUG_ON(video_device[vfd->minor]); - video_device[vfd->minor] = vfd; - - ret = get_index(vfd, index); - vfd->index = ret; - + /* Should not happen since we thought this minor was free */ + WARN_ON(video_device[vdev->minor] != NULL); + ret = vdev->index = get_index(vdev, index); mutex_unlock(&videodev_lock); if (ret < 0) { printk(KERN_ERR "%s: get_index failed\n", __func__); - goto fail_minor; + goto cleanup; } - ret = cdev_add(&vfd->cdev, MKDEV(VIDEO_MAJOR, vfd->minor), 1); + /* Part 3: Initialize the character device */ + vdev->cdev = cdev_alloc(); + if (vdev->cdev == NULL) { + ret = -ENOMEM; + goto cleanup; + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 17) + if (vdev->fops->unlocked_ioctl) + vdev->cdev->ops = &v4l2_unlocked_fops; + else + vdev->cdev->ops = &v4l2_fops; +#else + if (vdev->fops->unlocked_ioctl) + vdev->cdev->ops = (struct file_operations *)&v4l2_unlocked_fops; + else + vdev->cdev->ops = (struct file_operations *)&v4l2_fops; +#endif + vdev->cdev->owner = vdev->fops->owner; + ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1); if (ret < 0) { printk(KERN_ERR "%s: cdev_add failed\n", __func__); - goto fail_minor; + kfree(vdev->cdev); + vdev->cdev = NULL; + goto cleanup; } - /* sysfs class */ - memset(&vfd->dev, 0, sizeof(vfd->dev)); + + /* Part 4: register the device with sysfs */ + memset(&vdev->dev, 0, sizeof(vdev->dev)); /* The memset above cleared the device's drvdata, so put back the copy we made earlier. */ - video_set_drvdata(vfd, priv); - vfd->dev.class = &video_class; - vfd->dev.devt = MKDEV(VIDEO_MAJOR, vfd->minor); + video_set_drvdata(vdev, priv); + vdev->dev.class = &video_class; + vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor); #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) - if (vfd->parent) - vfd->dev.dev = vfd->parent; - sprintf(vfd->dev.class_id, "%s%d", name_base, nr); - ret = class_device_register(&vfd->dev); + if (vdev->parent) + vdev->dev.dev = vdev->parent; + sprintf(vdev->dev.class_id, "%s%d", name_base, nr); + ret = class_device_register(&vdev->dev); #else - if (vfd->parent) - vfd->dev.parent = vfd->parent; - dev_set_name(&vfd->dev, "%s%d", name_base, nr); - ret = device_register(&vfd->dev); + if (vdev->parent) + vdev->dev.parent = vdev->parent; + dev_set_name(&vdev->dev, "%s%d", name_base, nr); + ret = device_register(&vdev->dev); #endif if (ret < 0) { printk(KERN_ERR "%s: device_register failed\n", __func__); - goto del_cdev; + goto cleanup; } #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) - ret = class_device_create_file(&vfd->dev, &class_device_attr_name); + ret = class_device_create_file(&vdev->dev, &class_device_attr_name); if (ret < 0) { printk(KERN_ERR "%s: class_device_create_file 'name' failed\n", __func__); - class_device_unregister(&vfd->dev); - goto del_cdev; + class_device_unregister(&vdev->dev); + goto cleanup; } - ret = class_device_create_file(&vfd->dev, &class_device_attr_index); + ret = class_device_create_file(&vdev->dev, &class_device_attr_index); if (ret < 0) { printk(KERN_ERR "%s: class_device_create_file 'index' failed\n", __func__); - class_device_unregister(&vfd->dev); - goto del_cdev; + class_device_unregister(&vdev->dev); + goto cleanup; } #endif - /* Remember the cdev's release function */ - vfd->cdev_release = vfd->cdev.kobj.ktype->release; - /* Install our own */ - vfd->cdev.kobj.ktype = &v4l2_ktype_cdev_default; - return 0; + /* Register the release callback that will be called when the last + reference to the device goes away. */ + vdev->dev.release = v4l2_device_release; -del_cdev: - cdev_del(&vfd->cdev); + /* Part 5: Activate this minor. The char device can now be used. */ + mutex_lock(&videodev_lock); + video_device[vdev->minor] = vdev; + mutex_unlock(&videodev_lock); + return 0; -fail_minor: +cleanup: mutex_lock(&videodev_lock); - video_device[vfd->minor] = NULL; - clear_bit(vfd->num, video_nums[type]); + if (vdev->cdev) + cdev_del(vdev->cdev); + clear_bit(vdev->num, video_nums[type]); mutex_unlock(&videodev_lock); - vfd->minor = -1; + /* Mark this video device as never having been registered. */ + vdev->minor = -1; return ret; } EXPORT_SYMBOL(video_register_device_index); /** * video_unregister_device - unregister a video4linux device - * @vfd: the device to unregister + * @vdev: the device to unregister * - * This unregisters the passed device and deassigns the minor - * number. Future open calls will be met with errors. + * This unregisters the passed device. Future open calls will + * be met with errors. */ - -void video_unregister_device(struct video_device *vfd) +void video_unregister_device(struct video_device *vdev) { + /* Check if vdev was ever registered at all */ + if (!vdev || vdev->minor < 0) + return; + + mutex_lock(&videodev_lock); + set_bit(V4L2_FL_UNREGISTERED, &vdev->flags); + mutex_unlock(&videodev_lock); #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) - class_device_unregister(&vfd->dev); + class_device_unregister(&vdev->dev); #else - device_unregister(&vfd->dev); + device_unregister(&vdev->dev); #endif } EXPORT_SYMBOL(video_unregister_device); diff --git a/linux/include/media/v4l2-dev.h b/linux/include/media/v4l2-dev.h index bd59577d7..6d76f0bce 100644 --- a/linux/include/media/v4l2-dev.h +++ b/linux/include/media/v4l2-dev.h @@ -26,6 +26,11 @@ struct v4l2_ioctl_callbacks; +/* Flag to mark the video_device struct as unregistered. + Drivers can set this flag if they want to block all future + device access. It is set by video_unregister_device. */ +#define V4L2_FL_UNREGISTERED (0) + /* * Newer version of video_device, handled by videodev2.c * This version moves redundant code from video device code to @@ -43,15 +48,17 @@ struct video_device #else struct class_device dev; #endif - struct cdev cdev; /* character device */ - void (*cdev_release)(struct kobject *kobj); + struct cdev *cdev; /* character device */ struct device *parent; /* device parent */ /* device info */ char name[32]; int vfl_type; + /* 'minor' is set to -1 if the registration failed */ int minor; u16 num; + /* use bitops to set/clear/test flags */ + unsigned long flags; /* attribute to differentiate multiple indices on one physical device */ int index; @@ -62,7 +69,7 @@ struct video_device v4l2_std_id current_norm; /* Current tvnorm */ /* callbacks */ - void (*release)(struct video_device *vfd); + void (*release)(struct video_device *vdev); /* ioctl callbacks */ const struct v4l2_ioctl_ops *ioctl_ops; @@ -71,43 +78,48 @@ struct video_device /* dev to video-device */ #define to_video_device(cd) container_of(cd, struct video_device, dev) -/* Register and unregister devices. Note that if video_register_device fails, +/* Register video devices. Note that if video_register_device fails, the release() callback of the video_device structure is *not* called, so the caller is responsible for freeing any data. Usually that means that - you call video_device_release() on failure. */ -int __must_check video_register_device(struct video_device *vfd, int type, int nr); -int __must_check video_register_device_index(struct video_device *vfd, + you call video_device_release() on failure. + + Also note that vdev->minor is set to -1 if the registration failed. */ +int __must_check video_register_device(struct video_device *vdev, int type, int nr); +int __must_check video_register_device_index(struct video_device *vdev, int type, int nr, int index); -void video_unregister_device(struct video_device *vfd); + +/* Unregister video devices. Will do nothing if vdev == NULL or + vdev->minor < 0. */ +void video_unregister_device(struct video_device *vdev); /* helper functions to alloc/release struct video_device, the latter can also be used for video_device->release(). */ struct video_device * __must_check video_device_alloc(void); -/* this release function frees the vfd pointer */ -void video_device_release(struct video_device *vfd); +/* this release function frees the vdev pointer */ +void video_device_release(struct video_device *vdev); /* this release function does nothing, use when the video_device is a static global struct. Note that having a static video_device is a dubious construction at best. */ -void video_device_release_empty(struct video_device *vfd); +void video_device_release_empty(struct video_device *vdev); /* helper functions to access driver private data. */ -static inline void *video_get_drvdata(struct video_device *dev) +static inline void *video_get_drvdata(struct video_device *vdev) { #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) - return class_get_devdata(&dev->dev); + return class_get_devdata(&vdev->dev); #else - return dev_get_drvdata(&dev->dev); + return dev_get_drvdata(&vdev->dev); #endif } -static inline void video_set_drvdata(struct video_device *dev, void *data) +static inline void video_set_drvdata(struct video_device *vdev, void *data) { #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) - class_set_devdata(&dev->dev, data); + class_set_devdata(&vdev->dev, data); #else - dev_set_drvdata(&dev->dev, data); + dev_set_drvdata(&vdev->dev, data); #endif } @@ -120,4 +132,9 @@ static inline void *video_drvdata(struct file *file) return video_get_drvdata(video_devdata(file)); } +static inline int video_is_unregistered(struct video_device *vdev) +{ + return test_bit(V4L2_FL_UNREGISTERED, &vdev->flags); +} + #endif /* _V4L2_DEV_H */ -- cgit v1.2.3 From 1e0f865674f729f9f5e346a2f0f77bc3b297c51b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 20 Dec 2008 08:17:10 -0200 Subject: tuner-core: add debug msg's when asking tuner to sleep From: Mauro Carvalho Chehab Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/tuner-core.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'linux') diff --git a/linux/drivers/media/video/tuner-core.c b/linux/drivers/media/video/tuner-core.c index e649f2eb7..47b58fe98 100644 --- a/linux/drivers/media/video/tuner-core.c +++ b/linux/drivers/media/video/tuner-core.c @@ -737,6 +737,8 @@ static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, t->mode = mode; if (check_mode(t, cmd) == -EINVAL) { + tuner_dbg("Tuner doesn't support this mode. " + "Putting tuner to sleep\n"); t->mode = T_STANDBY; if (analog_ops->standby) analog_ops->standby(&t->fe); @@ -797,6 +799,8 @@ static int tuner_s_standby(struct v4l2_subdev *sd, u32 standby) struct tuner *t = to_tuner(sd); struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; + tuner_dbg("Putting tuner to sleep\n"); + if (check_mode(t, "TUNER_SET_STANDBY") == -EINVAL) return 0; t->mode = T_STANDBY; -- cgit v1.2.3 From 86e292e82d505fb16fd1e283905a65c5c603ec16 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 20 Dec 2008 08:43:34 -0200 Subject: em28xx: Fix a bug that were putting xc2028/3028 tuner to sleep From: Mauro Carvalho Chehab The changeset 78aa52a159cf introduced a bug on em28xx: buffer setup should be awaking xc3028. Instead, since we didn't specify the tuner mode, the device were going to sleep, due to the lack of tuner mode when asking tuner to handle VIDIOC_S_FREQUENCY: xc2028 0-0061: Device is Xceive 3028 version 1.0, firmware version 2.7 xc2028 0-0061: divisor= 00 00 14 d0 (freq=83.250) xc2028 0-0061: Putting xc2028/3028 into poweroff mode. xc2028 0-0061: Printing sleep stack trace: Pid: 10936, comm: mplayer Tainted: P M 2.6.27.8 #1 Call Trace: [] xc2028_sleep+0x89/0x1ab [tuner_xc2028] [] tuner_s_frequency+0xf5/0x165 [tuner] Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-video.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index 78eb335a5..6e4ee2d96 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -397,9 +397,10 @@ buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) if (*count < EM28XX_MIN_BUF) *count = EM28XX_MIN_BUF; - /* Ask tuner to go to analog mode */ + /* Ask tuner to go to analog or radio mode */ memset(&f, 0, sizeof(f)); f.frequency = dev->ctl_freq; + f.type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; em28xx_i2c_call_clients(dev, VIDIOC_S_FREQUENCY, &f); -- cgit v1.2.3 From 1990a8c65fe2820d7426c83c1b0fe0cf13a1c4ac Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 20 Dec 2008 10:06:37 -0200 Subject: em28xx: use a more standard way to specify video formats From: Mauro Carvalho Chehab This patch uses the same code for enumberating video formats that are present on cx88, bttv and saa7134 drivers. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-core.c | 17 +++++-- linux/drivers/media/video/em28xx/em28xx-video.c | 66 +++++++++++++++++++------ linux/drivers/media/video/em28xx/em28xx.h | 7 ++- 3 files changed, 69 insertions(+), 21 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-core.c b/linux/drivers/media/video/em28xx/em28xx-core.c index f62b05eae..2c975ffa4 100644 --- a/linux/drivers/media/video/em28xx/em28xx-core.c +++ b/linux/drivers/media/video/em28xx/em28xx-core.c @@ -621,10 +621,19 @@ int em28xx_capture_start(struct em28xx *dev, int start) return rc; } -int em28xx_outfmt_set_yuv422(struct em28xx *dev) +int em28xx_set_outfmt(struct em28xx *dev) { - em28xx_write_reg(dev, EM28XX_R27_OUTFMT, 0x34); - em28xx_write_reg(dev, EM28XX_R10_VINMODE, 0x10); + int ret; + + ret = em28xx_write_reg_bits(dev, EM28XX_R27_OUTFMT, + dev->format->reg | 0x20, 0x3f); + if (ret < 0) + return ret; + + ret = em28xx_write_reg(dev, EM28XX_R10_VINMODE, 0x10); + if (ret < 0) + return ret; + return em28xx_write_reg(dev, EM28XX_R11_VINCTRL, 0x11); } @@ -686,7 +695,7 @@ int em28xx_resolution_set(struct em28xx *dev) width = norm_maxw(dev); height = norm_maxh(dev) >> 1; - em28xx_outfmt_set_yuv422(dev); + em28xx_set_outfmt(dev); em28xx_accumulator_set(dev, 1, (width - 4) >> 2, 1, (height - 4) >> 2); em28xx_capture_area_set(dev, 0, 0, width >> 2, height >> 2); return em28xx_scaler_set(dev, dev->hscale, dev->vscale); diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index 6e4ee2d96..9fb9363dc 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -96,6 +96,16 @@ MODULE_PARM_DESC(video_debug, "enable debug messages [video]"); /* Bitmask marking allocated devices from 0 to EM28XX_MAXBOARDS */ static unsigned long em28xx_devused; +/* supported video standards */ +static struct em28xx_fmt format[] = { + { + .name = "16bpp YUY2, 4:2:2, packed", + .fourcc = V4L2_PIX_FMT_YUYV, + .depth = 16, + .reg = 0x14, + }, +}; + /* supported controls */ /* Common to all boards */ static struct v4l2_queryctrl em28xx_qctrl[] = { @@ -390,7 +400,8 @@ buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) struct em28xx *dev = fh->dev; struct v4l2_frequency f; - *size = 16 * fh->dev->width * fh->dev->height >> 3; + *size = (fh->dev->width * fh->dev->height * dev->format->depth + 7) >> 3; + if (0 == *count) *count = EM28XX_DEF_BUF; @@ -443,9 +454,7 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, struct em28xx *dev = fh->dev; int rc = 0, urb_init = 0; - /* FIXME: It assumes depth = 16 */ - /* The only currently supported format is 16 bits/pixel */ - buf->vb.size = 16 * dev->width * dev->height >> 3; + buf->vb.size = (fh->dev->width * fh->dev->height * dev->format->depth + 7) >> 3; if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) return -EINVAL; @@ -542,7 +551,7 @@ static int em28xx_config(struct em28xx *dev) dev->mute = 1; /* maybe not the right place... */ dev->volume = 0x1f; - em28xx_outfmt_set_yuv422(dev); + em28xx_set_outfmt(dev); em28xx_colorlevels_set_default(dev); em28xx_compression_disable(dev); @@ -710,8 +719,8 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.width = dev->width; f->fmt.pix.height = dev->height; - f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; - f->fmt.pix.bytesperline = dev->width * 2; + f->fmt.pix.pixelformat = dev->format->fourcc; + f->fmt.pix.bytesperline = (dev->width * dev->format->depth + 7) >> 3; f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * dev->height; f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; @@ -723,6 +732,17 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, return 0; } +static struct em28xx_fmt *format_by_fourcc(unsigned int fourcc) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(format); i++) + if (format[i].fourcc == fourcc) + return &format[i]; + + return NULL; +} + static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { @@ -733,6 +753,14 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, unsigned int maxw = norm_maxw(dev); unsigned int maxh = norm_maxh(dev); unsigned int hscale, vscale; + struct em28xx_fmt *fmt; + + fmt = format_by_fourcc(f->fmt.pix.pixelformat); + if (!fmt) { + em28xx_videodbg("Fourcc format (%08x) invalid.\n", + f->fmt.pix.pixelformat); + return -EINVAL; + } /* width must even because of the YUYV format height must be even because of interlacing */ @@ -771,9 +799,9 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.width = width; f->fmt.pix.height = height; - f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; - f->fmt.pix.bytesperline = width * 2; - f->fmt.pix.sizeimage = width * 2 * height; + f->fmt.pix.pixelformat = fmt->fourcc; + f->fmt.pix.bytesperline = (dev->width * fmt->depth + 7) >> 3; + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * height; f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; f->fmt.pix.field = V4L2_FIELD_INTERLACED; @@ -786,6 +814,7 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; int rc; + struct em28xx_fmt *fmt; rc = check_dev(dev); if (rc < 0) @@ -795,6 +824,10 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, vidioc_try_fmt_vid_cap(file, priv, f); + fmt = format_by_fourcc(f->fmt.pix.pixelformat); + if (!fmt) + return -EINVAL; + if (videobuf_queue_is_busy(&fh->vb_vidq)) { em28xx_errdev("%s queue busy\n", __func__); rc = -EBUSY; @@ -810,6 +843,7 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, /* set new image size */ dev->width = f->fmt.pix.width; dev->height = f->fmt.pix.height; + dev->format = fmt; get_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale); em28xx_set_alternate(dev); @@ -1347,15 +1381,13 @@ static int vidioc_querycap(struct file *file, void *priv, } static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *fmtd) + struct v4l2_fmtdesc *f) { - if (fmtd->index != 0) + if (unlikely(f->index >= ARRAY_SIZE(format))) return -EINVAL; - fmtd->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - strcpy(fmtd->description, "Packed YUY2"); - fmtd->pixelformat = V4L2_PIX_FMT_YUYV; - memset(fmtd->reserved, 0, sizeof(fmtd->reserved)); + strlcpy(f->description, format[f->index].name, sizeof(f->description)); + f->pixelformat = format[f->index].fourcc; return 0; } @@ -2144,6 +2176,7 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, dev->em28xx_write_regs_req = em28xx_write_regs_req; dev->em28xx_read_reg_req = em28xx_read_reg_req; dev->board.is_em2800 = em28xx_boards[dev->model].is_em2800; + dev->format = &format[0]; em28xx_pre_card_setup(dev); @@ -2565,3 +2598,4 @@ static void __exit em28xx_module_exit(void) module_init(em28xx_module_init); module_exit(em28xx_module_exit); + diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index 4de9a8190..ae511ed7e 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -210,9 +210,12 @@ struct em28xx_usb_isoc_ctl { }; +/* Struct to enumberate video formats */ struct em28xx_fmt { char *name; u32 fourcc; /* v4l2 format id */ + int depth; + int reg; }; /* buffer for one video frame */ @@ -439,6 +442,8 @@ struct em28xx { unsigned int has_audio_class:1; unsigned int has_alsa_audio:1; + struct em28xx_fmt *format; + struct em28xx_IR *ir; /* Some older em28xx chips needs a waiting time after writing */ @@ -577,7 +582,7 @@ int em28xx_audio_setup(struct em28xx *dev); int em28xx_colorlevels_set_default(struct em28xx *dev); int em28xx_capture_start(struct em28xx *dev, int start); -int em28xx_outfmt_set_yuv422(struct em28xx *dev); +int em28xx_set_outfmt(struct em28xx *dev); int em28xx_resolution_set(struct em28xx *dev); int em28xx_set_alternate(struct em28xx *dev); int em28xx_init_isoc(struct em28xx *dev, int max_packets, -- cgit v1.2.3 From 162b73a47b547cad436989665074bcb093d71c57 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 20 Dec 2008 07:03:06 -0800 Subject: Convert GP8PSK module to use S2API From: Alan Nisota This patch converts the gp8psk module to use the S2API. It pretends to be DVB-S2 capable in order to allow the various supported modulations (8PSK, QPSK-Turbo, etc), and keep software compatibility with the S2API patches for Mythtv and VDR. Signed-off by: Alan Nisota Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/dvb/dvb-usb/gp8psk-fe.c | 140 ++++++++++++++++++++++------ linux/drivers/media/dvb/dvb-usb/gp8psk.c | 2 - linux/drivers/media/dvb/dvb-usb/gp8psk.h | 1 + 3 files changed, 115 insertions(+), 28 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/dvb/dvb-usb/gp8psk-fe.c b/linux/drivers/media/dvb/dvb-usb/gp8psk-fe.c index 262a858c3..20eadf931 100644 --- a/linux/drivers/media/dvb/dvb-usb/gp8psk-fe.c +++ b/linux/drivers/media/dvb/dvb-usb/gp8psk-fe.c @@ -25,6 +25,20 @@ struct gp8psk_fe_state { unsigned long status_check_interval; }; +static int gp8psk_tuned_to_DCII(struct dvb_frontend *fe) +{ + struct gp8psk_fe_state *st = fe->demodulator_priv; + u8 status; + gp8psk_usb_in_op(st->d, GET_8PSK_CONFIG, 0, 0, &status, 1); + return status & bmDCtuned; +} + +static int gp8psk_set_tuner_mode(struct dvb_frontend *fe, int mode) +{ + struct gp8psk_fe_state *state = fe->demodulator_priv; + return gp8psk_usb_out_op(state->d, SET_8PSK_CONFIG, mode, 0, NULL, 0); +} + static int gp8psk_fe_update_status(struct gp8psk_fe_state *st) { u8 buf[6]; @@ -99,39 +113,114 @@ static int gp8psk_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_front return 0; } +static int gp8psk_fe_set_property(struct dvb_frontend *fe, + struct dtv_property *tvp) +{ + deb_fe("%s(..)\n", __func__); + return 0; +} + +static int gp8psk_fe_get_property(struct dvb_frontend *fe, + struct dtv_property *tvp) +{ + deb_fe("%s(..)\n", __func__); + return 0; +} + + static int gp8psk_fe_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *fep) { struct gp8psk_fe_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; u8 cmd[10]; u32 freq = fep->frequency * 1000; + int gp_product_id = le16_to_cpu(state->d->udev->descriptor.idProduct); + + deb_fe("%s()\n", __func__); cmd[4] = freq & 0xff; cmd[5] = (freq >> 8) & 0xff; cmd[6] = (freq >> 16) & 0xff; cmd[7] = (freq >> 24) & 0xff; - switch(fe->ops.info.type) { - case FE_QPSK: - cmd[0] = fep->u.qpsk.symbol_rate & 0xff; - cmd[1] = (fep->u.qpsk.symbol_rate >> 8) & 0xff; - cmd[2] = (fep->u.qpsk.symbol_rate >> 16) & 0xff; - cmd[3] = (fep->u.qpsk.symbol_rate >> 24) & 0xff; - cmd[8] = ADV_MOD_DVB_QPSK; - cmd[9] = 0x03; /*ADV_MOD_FEC_XXX*/ + switch (c->delivery_system) { + case SYS_DVBS: + /* Only QPSK is supported for DVB-S */ + if (c->modulation != QPSK) { + deb_fe("%s: unsupported modulation selected (%d)\n", + __func__, c->modulation); + return -EOPNOTSUPP; + } + c->fec_inner = FEC_AUTO; break; + case SYS_DVBS2: + deb_fe("%s: DVB-S2 delivery system selected\n", __func__); + break; + default: - // other modes are unsuported right now - cmd[0] = 0; - cmd[1] = 0; - cmd[2] = 0; - cmd[3] = 0; - cmd[8] = 0; + deb_fe("%s: unsupported delivery system selected (%d)\n", + __func__, c->delivery_system); + return -EOPNOTSUPP; + } + + cmd[0] = c->symbol_rate & 0xff; + cmd[1] = (c->symbol_rate >> 8) & 0xff; + cmd[2] = (c->symbol_rate >> 16) & 0xff; + cmd[3] = (c->symbol_rate >> 24) & 0xff; + switch (c->modulation) { + case QPSK: + if (gp_product_id == USB_PID_GENPIX_8PSK_REV_1_WARM) + if (gp8psk_tuned_to_DCII(fe)) + gp8psk_bcm4500_reload(state->d); + switch (c->fec_inner) { + case FEC_1_2: + cmd[9] = 0; break; + case FEC_2_3: + cmd[9] = 1; break; + case FEC_3_4: + cmd[9] = 2; break; + case FEC_5_6: + cmd[9] = 3; break; + case FEC_7_8: + cmd[9] = 4; break; + case FEC_AUTO: + cmd[9] = 5; break; + default: + cmd[9] = 5; break; + } + cmd[8] = ADV_MOD_DVB_QPSK; + break; + case PSK_8: /* PSK_8 is for compatibility with DN */ + cmd[8] = ADV_MOD_TURBO_8PSK; + switch (c->fec_inner) { + case FEC_2_3: + cmd[9] = 0; break; + case FEC_3_4: + cmd[9] = 1; break; + case FEC_3_5: + cmd[9] = 2; break; + case FEC_5_6: + cmd[9] = 3; break; + case FEC_8_9: + cmd[9] = 4; break; + default: + cmd[9] = 0; break; + } + break; + case QAM_16: /* QAM_16 is for compatibility with DN */ + cmd[8] = ADV_MOD_TURBO_16QAM; cmd[9] = 0; break; + default: /* Unknown modulation */ + deb_fe("%s: unsupported modulation selected (%d)\n", + __func__, c->modulation); + return -EOPNOTSUPP; } - gp8psk_usb_out_op(state->d,TUNE_8PSK,0,0,cmd,10); + if (gp_product_id == USB_PID_GENPIX_8PSK_REV_1_WARM) + gp8psk_set_tuner_mode(fe, 0); + gp8psk_usb_out_op(state->d, TUNE_8PSK, 0, 0, cmd, 10); state->lock = 0; state->next_status_check = jiffies; @@ -140,13 +229,6 @@ static int gp8psk_fe_set_frontend(struct dvb_frontend* fe, return 0; } -static int gp8psk_fe_get_frontend(struct dvb_frontend* fe, - struct dvb_frontend_parameters *fep) -{ - return 0; -} - - static int gp8psk_fe_send_diseqc_msg (struct dvb_frontend* fe, struct dvb_diseqc_master_cmd *m) { @@ -261,9 +343,13 @@ static struct dvb_frontend_ops gp8psk_fe_ops = { .symbol_rate_max = 45000000, .symbol_rate_tolerance = 500, /* ppm */ .caps = FE_CAN_INVERSION_AUTO | - FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | - FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | - FE_CAN_QPSK + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + /* + * FE_CAN_QAM_16 is for compatibility + * (Myth incorrectly detects Turbo-QPSK as plain QAM-16) + */ + FE_CAN_QPSK | FE_CAN_QAM_16 }, .release = gp8psk_fe_release, @@ -271,8 +357,10 @@ static struct dvb_frontend_ops gp8psk_fe_ops = { .init = NULL, .sleep = NULL, + .set_property = gp8psk_fe_set_property, + .get_property = gp8psk_fe_get_property, .set_frontend = gp8psk_fe_set_frontend, - .get_frontend = gp8psk_fe_get_frontend, + .get_tune_settings = gp8psk_fe_get_tune_settings, .read_status = gp8psk_fe_read_status, diff --git a/linux/drivers/media/dvb/dvb-usb/gp8psk.c b/linux/drivers/media/dvb/dvb-usb/gp8psk.c index 1b002909b..c1da962cc 100644 --- a/linux/drivers/media/dvb/dvb-usb/gp8psk.c +++ b/linux/drivers/media/dvb/dvb-usb/gp8psk.c @@ -174,7 +174,6 @@ static int gp8psk_power_ctrl(struct dvb_usb_device *d, int onoff) return 0; } -#if 0 int gp8psk_bcm4500_reload(struct dvb_usb_device *d) { u8 buf; @@ -191,7 +190,6 @@ int gp8psk_bcm4500_reload(struct dvb_usb_device *d) return EINVAL; return 0; } -#endif /* 0 */ static int gp8psk_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) { diff --git a/linux/drivers/media/dvb/dvb-usb/gp8psk.h b/linux/drivers/media/dvb/dvb-usb/gp8psk.h index e5cd8149c..e83a57506 100644 --- a/linux/drivers/media/dvb/dvb-usb/gp8psk.h +++ b/linux/drivers/media/dvb/dvb-usb/gp8psk.h @@ -92,5 +92,6 @@ extern struct dvb_frontend * gp8psk_fe_attach(struct dvb_usb_device *d); extern int gp8psk_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8 *b, int blen); extern int gp8psk_usb_out_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8 *b, int blen); +extern int gp8psk_bcm4500_reload(struct dvb_usb_device *d); #endif -- cgit v1.2.3 From 45232ac0864b8ed7b275e20303a8175d18b1f3d4 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 19 Dec 2008 16:36:48 -0800 Subject: tuner: fix tuner_ioctl build error From: Randy Dunlap Fix drivers/media/video/tuner-core.c so that it will build when CONFIG_VIDEO_ALLOW_V4L1=n: drivers/media/video/tuner-core.c:1111: error: 'tuner_ioctl' undeclared here (not in a function) Signed-off-by: Randy Dunlap Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/tuner-core.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'linux') diff --git a/linux/drivers/media/video/tuner-core.c b/linux/drivers/media/video/tuner-core.c index 47b58fe98..fd62aa6bd 100644 --- a/linux/drivers/media/video/tuner-core.c +++ b/linux/drivers/media/video/tuner-core.c @@ -933,6 +933,11 @@ static int tuner_ioctl(struct v4l2_subdev *sd, int cmd, void *arg) } return -ENOIOCTLCMD; } +#else +static int tuner_ioctl(struct v4l2_subdev *sd, int cmd, void *arg) +{ + return -ENOIOCTLCMD; +} #endif static int tuner_s_config(struct v4l2_subdev *sd, const struct v4l2_priv_tun_config *cfg) -- cgit v1.2.3 From f8c54503d6d27d303f307f24a889b96bc1ed6236 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Sat, 20 Dec 2008 18:30:58 +0100 Subject: gspca - pac207: Webcam 093a:2461 added. From: Hans de Goede Priority: normal Signed-off-by: Hans de Goede Signed-off-by: Jean-Francois Moine --- linux/Documentation/video4linux/gspca.txt | 3 ++- linux/drivers/media/video/gspca/pac207.c | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/Documentation/video4linux/gspca.txt b/linux/Documentation/video4linux/gspca.txt index a55459456..ec00740e6 100644 --- a/linux/Documentation/video4linux/gspca.txt +++ b/linux/Documentation/video4linux/gspca.txt @@ -202,7 +202,8 @@ sunplus 08ca:2050 Medion MD 41437 sunplus 08ca:2060 Aiptek PocketDV5300 tv8532 0923:010f ICM532 cams mars 093a:050f Mars-Semi Pc-Camera -pac207 093a:2460 PAC207 Qtec Webcam 100 +pac207 093a:2460 Qtec Webcam 100 +pac207 093a:2461 HP Webcam pac207 093a:2463 Philips SPC 220 NC pac207 093a:2464 Labtec Webcam 1200 pac207 093a:2468 PAC207 diff --git a/linux/drivers/media/video/gspca/pac207.c b/linux/drivers/media/video/gspca/pac207.c index 84bcaaf4d..e46b8e8f0 100644 --- a/linux/drivers/media/video/gspca/pac207.c +++ b/linux/drivers/media/video/gspca/pac207.c @@ -529,6 +529,7 @@ static const struct sd_desc sd_desc = { static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x041e, 0x4028)}, {USB_DEVICE(0x093a, 0x2460)}, + {USB_DEVICE(0x093a, 0x2461)}, {USB_DEVICE(0x093a, 0x2463)}, {USB_DEVICE(0x093a, 0x2464)}, {USB_DEVICE(0x093a, 0x2468)}, -- cgit v1.2.3 From 51b7994a471b5bdb5d68cfd1af7743c3faa91268 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sat, 20 Dec 2008 21:26:38 -0500 Subject: cx18: Disable locking of Video and Audio PLL for analog captures From: Andy Walls 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 Priority: normal Signed-off-by: Andy Walls --- linux/drivers/media/video/cx18/cx18-av-audio.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/cx18/cx18-av-audio.c b/linux/drivers/media/video/cx18/cx18-av-audio.c index fd85b9b2d..148f71097 100644 --- a/linux/drivers/media/video/cx18/cx18-av-audio.c +++ b/linux/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 bce583f51a7c7b70a1d1f922d5bf816df4f5766b Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sat, 20 Dec 2008 21:48:57 -0500 Subject: cx18: Use a consistent crystal value for computing all PLL parameters From: Andy Walls 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 Priority: normal Signed-off-by: Andy Walls --- linux/drivers/media/video/cx18/cx18-av-audio.c | 165 +++++++++++++++++++------ linux/drivers/media/video/cx18/cx18-av-core.c | 2 +- linux/drivers/media/video/cx18/cx18-firmware.c | 69 ++++++++++- 3 files changed, 191 insertions(+), 45 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/cx18/cx18-av-audio.c b/linux/drivers/media/video/cx18/cx18-av-audio.c index 148f71097..a2f0ad570 100644 --- a/linux/drivers/media/video/cx18/cx18-av-audio.c +++ b/linux/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/linux/drivers/media/video/cx18/cx18-av-core.c b/linux/drivers/media/video/cx18/cx18-av-core.c index 40ea6fde6..0b1c84b4d 100644 --- a/linux/drivers/media/video/cx18/cx18-av-core.c +++ b/linux/drivers/media/video/cx18/cx18-av-core.c @@ -271,7 +271,7 @@ void cx18_av_std_setup(struct cx18 *cx) if (pll_post) { int fin, fsc, pll; - pll = (28636364L * ((((u64)pll_int) << 25) + pll_frac)) >> 25; + pll = (28636360L * ((((u64)pll_int) << 25) + pll_frac)) >> 25; pll /= pll_post; CX18_DEBUG_INFO("PLL = %d.%06d MHz\n", pll / 1000000, pll % 1000000); diff --git a/linux/drivers/media/video/cx18/cx18-firmware.c b/linux/drivers/media/video/cx18/cx18-firmware.c index e74f76d47..1fa95da15 100644 --- a/linux/drivers/media/video/cx18/cx18-firmware.c +++ b/linux/drivers/media/video/cx18/cx18-firmware.c @@ -26,6 +26,7 @@ #include "cx18-irq.h" #include "cx18-firmware.h" #include "cx18-cards.h" +#include "cx18-av-core.h" #include #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 From 50fbd8c40d2ae7f63c97281f404b9d3870af89a0 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 21 Dec 2008 14:35:25 +0100 Subject: v4l2-compat32: fix 32-64 compatibility module From: Hans Verkuil Added all missing v4l1/2 ioctls and fix several broken conversions. Partially based on work done by Cody Pisto . Priority: normal Signed-off-by: Hans Verkuil Tested-by: Brandon Jenkins --- linux/drivers/media/video/v4l2-compat-ioctl32.c | 848 +++++++++++++----------- linux/include/linux/videodev2.h | 2 + 2 files changed, 471 insertions(+), 379 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/v4l2-compat-ioctl32.c b/linux/drivers/media/video/v4l2-compat-ioctl32.c index 171f1ccd1..7a0c6b6a4 100644 --- a/linux/drivers/media/video/v4l2-compat-ioctl32.c +++ b/linux/drivers/media/video/v4l2-compat-ioctl32.c @@ -7,12 +7,14 @@ * Copyright (C) 2001,2002 Andi Kleen, SuSE Labs * Copyright (C) 2003 Pavel Machek (pavel@suse.cz) * Copyright (C) 2005 Philippe De Muyter (phdm@macqel.be) + * Copyright (C) 2008 Hans Verkuil * * These routines maintain argument size conversion between 32bit and 64bit * ioctls. */ #include +#define __OLD_VIDIOC_ /* To allow fixing old calls*/ #include #include #include @@ -59,55 +61,6 @@ static int put_video_tuner32(struct video_tuner *kp, struct video_tuner32 __user return 0; } -#if 0 /*FIXME */ -struct video_audio32 { - compat_int_t audio; - __u16 volume; - __u16 bass, treble; - __u32 flags; - __u8 name[16]; - __u16 mode; - __u16 balance; - __u16 step; -}; - -static int get_video_audio32(struct video_audio *kp, - struct video_audio32 __user *up) -{ - if (!access_ok(VERIFY_READ, up, sizeof(struct video_audio32)) || - get_user(kp->audio, &up->audio) || - get_user(kp->volume, &up->volume) || - get_user(kp->bass, &up->bass) || - get_user(kp->treble, &up->treble) || - get_user(kp->flags, &up->flags) || - copy_from_user(kp->name, up->name, sizeof(up->name)) || - get_user(kp->mode, &up->mode) || - get_user(kp->balance, &up->balance) || - get_user(kp->step, &up->step)) - return -EFAULT; - - return 0; -} - -static int put_video_audio32(struct video_audio *kp, - struct video_audio32 __user *up) -{ - if (!access_ok(VERIFY_WRITE, up, sizeof(struct video_audio32)) || - put_user(kp->audio, &up->audio) || - put_user(kp->volume, &up->volume) || - put_user(kp->bass, &up->bass) || - put_user(kp->treble, &up->treble) || - put_user(kp->flags, &up->flags) || - copy_to_user(kp->name, up->name, sizeof(up->name)) || - put_user(kp->mode, &up->mode) || - put_user(kp->balance, &up->balance) || - put_user(kp->step, &up->step)) - return -EFAULT; - - return 0; -} -#endif - struct video_buffer32 { compat_caddr_t base; compat_int_t height, width, depth, bytesperline; @@ -148,7 +101,7 @@ static int put_video_buffer32(struct video_buffer *kp, struct video_buffer32 __u } struct video_clip32 { - s32 x, y, width, height; /* Its really s32 in videodev.h */ + s32 x, y, width, height; /* It's really s32 in videodev.h */ compat_caddr_t next; }; @@ -157,29 +110,72 @@ struct video_window32 { compat_caddr_t clips; compat_int_t clipcount; }; -#endif -static int native_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +static int get_video_window32(struct video_window *kp, struct video_window32 __user *up) { - int ret = -ENOIOCTLCMD; + struct video_clip __user *uclips; + struct video_clip __user *kclips; + compat_caddr_t p; + int nclips; - if (file->f_op->unlocked_ioctl) - ret = file->f_op->unlocked_ioctl(file, cmd, arg); - else if (file->f_op->ioctl) { - lock_kernel(); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) - ret = file->f_op->ioctl(file->f_dentry->d_inode, file, cmd, arg); -#else - ret = file->f_op->ioctl(file->f_path.dentry->d_inode, file, cmd, arg); -#endif - unlock_kernel(); + if (!access_ok(VERIFY_READ, up, sizeof(struct video_window32))) + return -EFAULT; + + if (get_user(nclips, &up->clipcount)) + return -EFAULT; + + if (!access_ok(VERIFY_READ, up, sizeof(struct video_window32)) || + get_user(kp->x, &up->x) || + get_user(kp->y, &up->y) || + get_user(kp->width, &up->width) || + get_user(kp->height, &up->height) || + get_user(kp->chromakey, &up->chromakey) || + get_user(kp->flags, &up->flags) || + get_user(kp->clipcount, &up->clipcount)) + return -EFAULT; + + nclips = kp->clipcount; + kp->clips = NULL; + + if (nclips == 0) + return 0; + if (get_user(p, &up->clips)) + return -EFAULT; + uclips = compat_ptr(p); + + /* If nclips < 0, then it is a clipping bitmap of size + VIDEO_CLIPMAP_SIZE */ + if (nclips < 0) { + if (!access_ok(VERIFY_READ, uclips, VIDEO_CLIPMAP_SIZE)) + return -EFAULT; + kp->clips = compat_alloc_user_space(VIDEO_CLIPMAP_SIZE); + if (copy_in_user(kp->clips, uclips, VIDEO_CLIPMAP_SIZE)) + return -EFAULT; + return 0; } - return ret; -} + /* Otherwise it is an array of video_clip structs. */ + if (!access_ok(VERIFY_READ, uclips, nclips * sizeof(struct video_clip))) + return -EFAULT; + kp->clips = compat_alloc_user_space(nclips * sizeof(struct video_clip)); + kclips = kp->clips; + while (nclips--) { + int err; + + err = copy_in_user(&kclips->x, &uclips->x, sizeof(kclips->x)); + err |= copy_in_user(&kclips->y, &uclips->y, sizeof(kclips->y)); + err |= copy_in_user(&kclips->width, &uclips->width, sizeof(kclips->width)); + err |= copy_in_user(&kclips->height, &uclips->height, sizeof(kclips->height)); + kclips->next = NULL; + if (err) + return -EFAULT; + kclips++; + uclips++; + } + return 0; +} -#ifdef CONFIG_VIDEO_V4L1_COMPAT /* You get back everything except the clips... */ static int put_video_window32(struct video_window *kp, struct video_window32 __user *up) { @@ -194,8 +190,59 @@ static int put_video_window32(struct video_window *kp, struct video_window32 __u return -EFAULT; return 0; } + +struct video_code32 { + char loadwhat[16]; /* name or tag of file being passed */ + compat_int_t datasize; + unsigned char *data; +}; + +static int get_microcode32(struct video_code *kp, struct video_code32 __user *up) +{ + if (!access_ok(VERIFY_READ, up, sizeof(struct video_code32)) || + copy_from_user(kp->loadwhat, up->loadwhat, sizeof(up->loadwhat)) || + get_user(kp->datasize, &up->datasize) || + copy_from_user(kp->data, up->data, up->datasize)) + return -EFAULT; + return 0; +} + +#define VIDIOCGTUNER32 _IOWR('v', 4, struct video_tuner32) +#define VIDIOCSTUNER32 _IOW('v', 5, struct video_tuner32) +#define VIDIOCGWIN32 _IOR('v', 9, struct video_window32) +#define VIDIOCSWIN32 _IOW('v', 10, struct video_window32) +#define VIDIOCGFBUF32 _IOR('v', 11, struct video_buffer32) +#define VIDIOCSFBUF32 _IOW('v', 12, struct video_buffer32) +#define VIDIOCGFREQ32 _IOR('v', 14, u32) +#define VIDIOCSFREQ32 _IOW('v', 15, u32) +#define VIDIOCSMICROCODE32 _IOW('v', 27, struct video_code32) + +#define VIDIOCCAPTURE32 _IOW('v', 8, s32) +#define VIDIOCSYNC32 _IOW('v', 18, s32) +#define VIDIOCSWRITEMODE32 _IOW('v', 25, s32) + #endif +static int native_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int ret = -ENOIOCTLCMD; + + if (file->f_op->unlocked_ioctl) + ret = file->f_op->unlocked_ioctl(file, cmd, arg); + else if (file->f_op->ioctl) { + lock_kernel(); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) + ret = file->f_op->ioctl(file->f_dentry->d_inode, file, cmd, arg); +#else + ret = file->f_op->ioctl(file->f_path.dentry->d_inode, file, cmd, arg); +#endif + unlock_kernel(); + } + + return ret; +} + + struct v4l2_clip32 { struct v4l2_rect c; compat_caddr_t next; @@ -282,12 +329,27 @@ static inline int put_v4l2_vbi_format(struct v4l2_vbi_format *kp, struct v4l2_vb return 0; } +static inline int get_v4l2_sliced_vbi_format(struct v4l2_sliced_vbi_format *kp, struct v4l2_sliced_vbi_format __user *up) +{ + if (copy_from_user(kp, up, sizeof(struct v4l2_sliced_vbi_format))) + return -EFAULT; + return 0; +} + +static inline int put_v4l2_sliced_vbi_format(struct v4l2_sliced_vbi_format *kp, struct v4l2_sliced_vbi_format __user *up) +{ + if (copy_to_user(up, kp, sizeof(struct v4l2_sliced_vbi_format))) + return -EFAULT; + return 0; +} + struct v4l2_format32 { enum v4l2_buf_type type; union { - struct v4l2_pix_format pix; /* V4L2_BUF_TYPE_VIDEO_CAPTURE */ - struct v4l2_window32 win; /* V4L2_BUF_TYPE_VIDEO_OVERLAY */ - struct v4l2_vbi_format vbi; /* V4L2_BUF_TYPE_VBI_CAPTURE */ + struct v4l2_pix_format pix; + struct v4l2_window32 win; + struct v4l2_vbi_format vbi; + struct v4l2_sliced_vbi_format sliced; __u8 raw_data[200]; /* user-defined */ } fmt; }; @@ -299,15 +361,27 @@ static int get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user return -EFAULT; switch (kp->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: + case V4L2_BUF_TYPE_VIDEO_OUTPUT: return get_v4l2_pix_format(&kp->fmt.pix, &up->fmt.pix); case V4L2_BUF_TYPE_VIDEO_OVERLAY: + case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: return get_v4l2_window32(&kp->fmt.win, &up->fmt.win); case V4L2_BUF_TYPE_VBI_CAPTURE: + case V4L2_BUF_TYPE_VBI_OUTPUT: return get_v4l2_vbi_format(&kp->fmt.vbi, &up->fmt.vbi); + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + return get_v4l2_sliced_vbi_format(&kp->fmt.sliced, &up->fmt.sliced); + case V4L2_BUF_TYPE_PRIVATE: + if (copy_from_user(kp, up, sizeof(kp->fmt.raw_data))) + return -EFAULT; + return 0; + case 0: + return -EINVAL; default: - printk(KERN_INFO "compat_ioctl: unexpected VIDIOC_FMT type %d\n", + printk(KERN_INFO "compat_ioctl32: unexpected VIDIOC_FMT type %d\n", kp->type); - return -ENXIO; + return -EINVAL; } } @@ -318,31 +392,30 @@ static int put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user return -EFAULT; switch (kp->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: + case V4L2_BUF_TYPE_VIDEO_OUTPUT: return put_v4l2_pix_format(&kp->fmt.pix, &up->fmt.pix); case V4L2_BUF_TYPE_VIDEO_OVERLAY: + case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: return put_v4l2_window32(&kp->fmt.win, &up->fmt.win); case V4L2_BUF_TYPE_VBI_CAPTURE: + case V4L2_BUF_TYPE_VBI_OUTPUT: return put_v4l2_vbi_format(&kp->fmt.vbi, &up->fmt.vbi); + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + return put_v4l2_sliced_vbi_format(&kp->fmt.sliced, &up->fmt.sliced); + case V4L2_BUF_TYPE_PRIVATE: + if (copy_to_user(up, kp, sizeof(up->fmt.raw_data))) + return -EFAULT; + return 0; + case 0: + return -EINVAL; default: - return -ENXIO; + printk(KERN_INFO "compat_ioctl32: unexpected VIDIOC_FMT type %d\n", + kp->type); + return -EINVAL; } } -static inline int get_v4l2_standard(struct v4l2_standard *kp, struct v4l2_standard __user *up) -{ - if (copy_from_user(kp, up, sizeof(struct v4l2_standard))) - return -EFAULT; - return 0; - -} - -static inline int put_v4l2_standard(struct v4l2_standard *kp, struct v4l2_standard __user *up) -{ - if (copy_to_user(up, kp, sizeof(struct v4l2_standard))) - return -EFAULT; - return 0; -} - struct v4l2_standard32 { __u32 index; __u32 id[2]; /* __u64 would get the alignment wrong */ @@ -374,21 +447,6 @@ static int put_v4l2_standard32(struct v4l2_standard *kp, struct v4l2_standard32 return 0; } -static inline int get_v4l2_tuner(struct v4l2_tuner *kp, struct v4l2_tuner __user *up) -{ - if (copy_from_user(kp, up, sizeof(struct v4l2_tuner))) - return -EFAULT; - return 0; - -} - -static inline int put_v4l2_tuner(struct v4l2_tuner *kp, struct v4l2_tuner __user *up) -{ - if (copy_to_user(up, kp, sizeof(struct v4l2_tuner))) - return -EFAULT; - return 0; -} - struct v4l2_buffer32 { __u32 index; enum v4l2_buf_type type; @@ -512,149 +570,143 @@ static int put_v4l2_framebuffer32(struct v4l2_framebuffer *kp, struct v4l2_frame return 0; } -static inline int get_v4l2_input32(struct v4l2_input *kp, struct v4l2_input __user *up) -{ - if (copy_from_user(kp, up, sizeof(struct v4l2_input) - 4)) - return -EFAULT; - return 0; -} - -static inline int put_v4l2_input32(struct v4l2_input *kp, struct v4l2_input __user *up) +struct v4l2_input32 { + __u32 index; /* Which input */ + __u8 name[32]; /* Label */ + __u32 type; /* Type of input */ + __u32 audioset; /* Associated audios (bitfield) */ + __u32 tuner; /* Associated tuner */ + v4l2_std_id std; + __u32 status; + __u32 reserved[4]; +} __attribute__ ((packed)); + +/* The 64-bit v4l2_input struct has extra padding at the end of the struct. + Otherwise it is identical to the 32-bit version. */ +static inline int get_v4l2_input32(struct v4l2_input *kp, struct v4l2_input32 __user *up) { - if (copy_to_user(up, kp, sizeof(struct v4l2_input) - 4)) + if (copy_from_user(kp, up, sizeof(struct v4l2_input32))) return -EFAULT; return 0; } -static inline int get_v4l2_input(struct v4l2_input *kp, struct v4l2_input __user *up) +static inline int put_v4l2_input32(struct v4l2_input *kp, struct v4l2_input32 __user *up) { - if (copy_from_user(kp, up, sizeof(struct v4l2_input))) + if (copy_to_user(up, kp, sizeof(struct v4l2_input32))) return -EFAULT; return 0; } -static inline int put_v4l2_input(struct v4l2_input *kp, struct v4l2_input __user *up) -{ - if (copy_to_user(up, kp, sizeof(struct v4l2_input))) - return -EFAULT; - return 0; -} - -#ifdef CONFIG_VIDEO_V4L1_COMPAT -struct video_code32 { - char loadwhat[16]; /* name or tag of file being passed */ - compat_int_t datasize; - unsigned char *data; +struct v4l2_ext_controls32 { + __u32 ctrl_class; + __u32 count; + __u32 error_idx; + __u32 reserved[2]; + compat_caddr_t controls; /* actually struct v4l2_ext_control32 * */ }; -static inline int microcode32(struct video_code *kp, struct video_code32 __user *up) +static int get_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext_controls32 __user *up) { - if (!access_ok(VERIFY_READ, up, sizeof(struct video_code32)) || - copy_from_user(kp->loadwhat, up->loadwhat, sizeof(up->loadwhat)) || - get_user(kp->datasize, &up->datasize) || - copy_from_user(kp->data, up->data, up->datasize)) + struct v4l2_ext_control __user *ucontrols; + struct v4l2_ext_control __user *kcontrols; + int n; + compat_caddr_t p; + + if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_ext_controls32)) || + get_user(kp->ctrl_class, &up->ctrl_class) || + get_user(kp->count, &up->count) || + get_user(kp->error_idx, &up->error_idx) || + copy_from_user(kp->reserved, up->reserved, sizeof(kp->reserved))) + return -EFAULT; + n = kp->count; + if (n == 0) { + kp->controls = NULL; + return 0; + } + if (get_user(p, &up->controls)) + return -EFAULT; + ucontrols = compat_ptr(p); + if (!access_ok(VERIFY_READ, ucontrols, n * sizeof(struct v4l2_ext_control))) + return -EFAULT; + kcontrols = compat_alloc_user_space(n * sizeof(struct v4l2_ext_control)); + kp->controls = kcontrols; + while (--n >= 0) { + if (copy_in_user(&kcontrols->id, &ucontrols->id, sizeof(__u32))) return -EFAULT; + if (copy_in_user(&kcontrols->reserved2, &ucontrols->reserved2, sizeof(ucontrols->reserved2))) + return -EFAULT; + /* Note: if the void * part of the union ever becomes relevant + then we need to know the type of the control in order to do + the right thing here. Luckily, that is not yet an issue. */ + if (copy_in_user(&kcontrols->value, &ucontrols->value, sizeof(ucontrols->value))) + return -EFAULT; + ucontrols++; + kcontrols++; + } return 0; } -#define VIDIOCGTUNER32 _IOWR('v', 4, struct video_tuner32) -#define VIDIOCSTUNER32 _IOW('v', 5, struct video_tuner32) -#define VIDIOCGWIN32 _IOR('v', 9, struct video_window32) -#define VIDIOCSWIN32 _IOW('v', 10, struct video_window32) -#define VIDIOCGFBUF32 _IOR('v', 11, struct video_buffer32) -#define VIDIOCSFBUF32 _IOW('v', 12, struct video_buffer32) -#define VIDIOCGFREQ32 _IOR('v', 14, u32) -#define VIDIOCSFREQ32 _IOW('v', 15, u32) -#define VIDIOCSMICROCODE32 _IOW('v', 27, struct video_code32) - -#endif - -/* VIDIOC_ENUMINPUT32 is VIDIOC_ENUMINPUT minus 4 bytes of padding alignement */ -#define VIDIOC_ENUMINPUT32 (VIDIOC_ENUMINPUT - _IOC(0, 0, 0, 4)) -#define VIDIOC_G_FMT32 _IOWR ('V', 4, struct v4l2_format32) -#define VIDIOC_S_FMT32 _IOWR ('V', 5, struct v4l2_format32) -#define VIDIOC_QUERYBUF32 _IOWR ('V', 9, struct v4l2_buffer32) -#define VIDIOC_G_FBUF32 _IOR ('V', 10, struct v4l2_framebuffer32) -#define VIDIOC_S_FBUF32 _IOW ('V', 11, struct v4l2_framebuffer32) -/* VIDIOC_OVERLAY is now _IOW, but was _IOWR */ -#define VIDIOC_OVERLAY32 _IOWR ('V', 14, compat_int_t) -#define VIDIOC_QBUF32 _IOWR ('V', 15, struct v4l2_buffer32) -#define VIDIOC_DQBUF32 _IOWR ('V', 17, struct v4l2_buffer32) -#define VIDIOC_STREAMON32 _IOW ('V', 18, compat_int_t) -#define VIDIOC_STREAMOFF32 _IOW ('V', 19, compat_int_t) -#define VIDIOC_ENUMSTD32 _IOWR ('V', 25, struct v4l2_standard32) -/* VIDIOC_S_CTRL is now _IOWR, but was _IOW */ -#define VIDIOC_S_CTRL32 _IOW ('V', 28, struct v4l2_control) -#define VIDIOC_G_INPUT32 _IOR ('V', 38, compat_int_t) -#define VIDIOC_S_INPUT32 _IOWR ('V', 39, compat_int_t) -#define VIDIOC_TRY_FMT32 _IOWR ('V', 64, struct v4l2_format32) - -#ifdef CONFIG_VIDEO_V4L1_COMPAT -enum { - MaxClips = (~0U-sizeof(struct video_window))/sizeof(struct video_clip) -}; - -static int do_set_window(struct file *file, unsigned int cmd, unsigned long arg) +static int put_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext_controls32 __user *up) { - struct video_window32 __user *up = compat_ptr(arg); - struct video_window __user *vw; - struct video_clip __user *p; - int nclips; - u32 n; - - if (!access_ok(VERIFY_READ, up, sizeof(struct video_window32))) - return -EFAULT; + struct v4l2_ext_control __user *ucontrols; + struct v4l2_ext_control __user *kcontrols = kp->controls; + int n = kp->count; + compat_caddr_t p; + + if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_ext_controls32)) || + put_user(kp->ctrl_class, &up->ctrl_class) || + put_user(kp->count, &up->count) || + put_user(kp->error_idx, &up->error_idx) || + copy_to_user(up->reserved, kp->reserved, sizeof(up->reserved))) + return -EFAULT; + if (!kp->count) + return 0; - if (get_user(nclips, &up->clipcount)) + if (get_user(p, &up->controls)) return -EFAULT; - - /* Peculiar interface... */ - if (nclips < 0) - nclips = VIDEO_CLIPMAP_SIZE; - - if (nclips > MaxClips) - return -ENOMEM; - - vw = compat_alloc_user_space(sizeof(struct video_window) + - nclips * sizeof(struct video_clip)); - - p = nclips ? (struct video_clip __user *)(vw + 1) : NULL; - - if (get_user(n, &up->x) || put_user(n, &vw->x) || - get_user(n, &up->y) || put_user(n, &vw->y) || - get_user(n, &up->width) || put_user(n, &vw->width) || - get_user(n, &up->height) || put_user(n, &vw->height) || - get_user(n, &up->chromakey) || put_user(n, &vw->chromakey) || - get_user(n, &up->flags) || put_user(n, &vw->flags) || - get_user(n, &up->clipcount) || put_user(n, &vw->clipcount) || - get_user(n, &up->clips) || put_user(p, &vw->clips)) + ucontrols = compat_ptr(p); + if (!access_ok(VERIFY_WRITE, ucontrols, n * sizeof(struct v4l2_ext_control))) return -EFAULT; - if (nclips) { - struct video_clip32 __user *u = compat_ptr(n); - int i; - if (!u) - return -EINVAL; - for (i = 0; i < nclips; i++, u++, p++) { - s32 v; - if (!access_ok(VERIFY_READ, u, sizeof(struct video_clip32)) || - !access_ok(VERIFY_WRITE, p, sizeof(struct video_clip32)) || - get_user(v, &u->x) || - put_user(v, &p->x) || - get_user(v, &u->y) || - put_user(v, &p->y) || - get_user(v, &u->width) || - put_user(v, &p->width) || - get_user(v, &u->height) || - put_user(v, &p->height) || - put_user(NULL, &p->next)) - return -EFAULT; - } + while (--n >= 0) { + if (copy_in_user(&ucontrols->id, &kcontrols->id, sizeof(__u32))) + return -EFAULT; + if (copy_in_user(&ucontrols->reserved2, &kcontrols->reserved2, + sizeof(ucontrols->reserved2))) + return -EFAULT; + /* Note: if the void * part of the union ever becomes relevant + then we need to know the type of the control in order to do + the right thing here. Luckily, that is not yet an issue. */ + if (copy_in_user(&ucontrols->value, &kcontrols->value, sizeof(ucontrols->value))) + return -EFAULT; + ucontrols++; + kcontrols++; } - - return native_ioctl(file, VIDIOCSWIN, (unsigned long)vw); + return 0; } -#endif + +#define VIDIOC_G_FMT32 _IOWR('V', 4, struct v4l2_format32) +#define VIDIOC_S_FMT32 _IOWR('V', 5, struct v4l2_format32) +#define VIDIOC_QUERYBUF32 _IOWR('V', 9, struct v4l2_buffer32) +#define VIDIOC_G_FBUF32 _IOR ('V', 10, struct v4l2_framebuffer32) +#define VIDIOC_S_FBUF32 _IOW ('V', 11, struct v4l2_framebuffer32) +#define VIDIOC_QBUF32 _IOWR('V', 15, struct v4l2_buffer32) +#define VIDIOC_DQBUF32 _IOWR('V', 17, struct v4l2_buffer32) +#define VIDIOC_ENUMSTD32 _IOWR('V', 25, struct v4l2_standard32) +#define VIDIOC_ENUMINPUT32 _IOWR('V', 26, struct v4l2_input32) +#define VIDIOC_TRY_FMT32 _IOWR('V', 64, struct v4l2_format32) +#define VIDIOC_G_EXT_CTRLS32 _IOWR('V', 71, struct v4l2_ext_controls32) +#define VIDIOC_S_EXT_CTRLS32 _IOWR('V', 72, struct v4l2_ext_controls32) +#define VIDIOC_TRY_EXT_CTRLS32 _IOWR('V', 73, struct v4l2_ext_controls32) + +#define VIDIOC_OVERLAY32 _IOW ('V', 14, s32) +#define VIDIOC_OVERLAY32_OLD _IOWR('V', 14, s32) +#define VIDIOC_STREAMON32 _IOW ('V', 18, s32) +#define VIDIOC_STREAMOFF32 _IOW ('V', 19, s32) +#define VIDIOC_G_INPUT32 _IOR ('V', 38, s32) +#define VIDIOC_S_INPUT32 _IOWR('V', 39, s32) +#define VIDIOC_G_OUTPUT32 _IOR ('V', 46, s32) +#define VIDIOC_S_OUTPUT32 _IOWR('V', 47, s32) static int do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { @@ -669,45 +721,51 @@ static int do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg struct v4l2_format v2f; struct v4l2_buffer v2b; struct v4l2_framebuffer v2fb; - struct v4l2_standard v2s; struct v4l2_input v2i; - struct v4l2_tuner v2t; + struct v4l2_standard v2s; + struct v4l2_ext_controls v2ecs; unsigned long vx; + int vi; } karg; void __user *up = compat_ptr(arg); int compatible_arg = 1; int err = 0; - int realcmd = cmd; /* First, convert the command. */ switch (cmd) { #ifdef CONFIG_VIDEO_V4L1_COMPAT - case VIDIOCGTUNER32: realcmd = cmd = VIDIOCGTUNER; break; - case VIDIOCSTUNER32: realcmd = cmd = VIDIOCSTUNER; break; - case VIDIOCGWIN32: realcmd = cmd = VIDIOCGWIN; break; - case VIDIOCGFBUF32: realcmd = cmd = VIDIOCGFBUF; break; - case VIDIOCSFBUF32: realcmd = cmd = VIDIOCSFBUF; break; - case VIDIOCGFREQ32: realcmd = cmd = VIDIOCGFREQ; break; - case VIDIOCSFREQ32: realcmd = cmd = VIDIOCSFREQ; break; - case VIDIOCSMICROCODE32: realcmd = cmd = VIDIOCSMICROCODE; break; + case VIDIOCGTUNER32: cmd = VIDIOCGTUNER; break; + case VIDIOCSTUNER32: cmd = VIDIOCSTUNER; break; + case VIDIOCGWIN32: cmd = VIDIOCGWIN; break; + case VIDIOCSWIN32: cmd = VIDIOCSWIN; break; + case VIDIOCGFBUF32: cmd = VIDIOCGFBUF; break; + case VIDIOCSFBUF32: cmd = VIDIOCSFBUF; break; + case VIDIOCGFREQ32: cmd = VIDIOCGFREQ; break; + case VIDIOCSFREQ32: cmd = VIDIOCSFREQ; break; + case VIDIOCSMICROCODE32: cmd = VIDIOCSMICROCODE; break; #endif - case VIDIOC_G_FMT32: realcmd = cmd = VIDIOC_G_FMT; break; - case VIDIOC_S_FMT32: realcmd = cmd = VIDIOC_S_FMT; break; - case VIDIOC_QUERYBUF32: realcmd = cmd = VIDIOC_QUERYBUF; break; - case VIDIOC_QBUF32: realcmd = cmd = VIDIOC_QBUF; break; - case VIDIOC_DQBUF32: realcmd = cmd = VIDIOC_DQBUF; break; - case VIDIOC_STREAMON32: realcmd = cmd = VIDIOC_STREAMON; break; - case VIDIOC_STREAMOFF32: realcmd = cmd = VIDIOC_STREAMOFF; break; - case VIDIOC_G_FBUF32: realcmd = cmd = VIDIOC_G_FBUF; break; - case VIDIOC_S_FBUF32: realcmd = cmd = VIDIOC_S_FBUF; break; - case VIDIOC_OVERLAY32: realcmd = cmd = VIDIOC_OVERLAY; break; - case VIDIOC_ENUMSTD32: realcmd = VIDIOC_ENUMSTD; break; - case VIDIOC_ENUMINPUT32: realcmd = VIDIOC_ENUMINPUT; break; - case VIDIOC_S_CTRL32: realcmd = cmd = VIDIOC_S_CTRL; break; - case VIDIOC_G_INPUT32: realcmd = cmd = VIDIOC_G_INPUT; break; - case VIDIOC_S_INPUT32: realcmd = cmd = VIDIOC_S_INPUT; break; - case VIDIOC_TRY_FMT32: realcmd = cmd = VIDIOC_TRY_FMT; break; - }; + case VIDIOC_G_FMT32: cmd = VIDIOC_G_FMT; break; + case VIDIOC_S_FMT32: cmd = VIDIOC_S_FMT; break; + case VIDIOC_QUERYBUF32: cmd = VIDIOC_QUERYBUF; break; + case VIDIOC_G_FBUF32: cmd = VIDIOC_G_FBUF; break; + case VIDIOC_S_FBUF32: cmd = VIDIOC_S_FBUF; break; + case VIDIOC_QBUF32: cmd = VIDIOC_QBUF; break; + case VIDIOC_DQBUF32: cmd = VIDIOC_DQBUF; break; + case VIDIOC_ENUMSTD32: cmd = VIDIOC_ENUMSTD; break; + case VIDIOC_ENUMINPUT32: cmd = VIDIOC_ENUMINPUT; break; + case VIDIOC_TRY_FMT32: cmd = VIDIOC_TRY_FMT; break; + case VIDIOC_G_EXT_CTRLS32: cmd = VIDIOC_G_EXT_CTRLS; break; + case VIDIOC_S_EXT_CTRLS32: cmd = VIDIOC_S_EXT_CTRLS; break; + case VIDIOC_TRY_EXT_CTRLS32: cmd = VIDIOC_TRY_EXT_CTRLS; break; + case VIDIOC_OVERLAY32: cmd = VIDIOC_OVERLAY; break; + case VIDIOC_OVERLAY32_OLD: cmd = VIDIOC_OVERLAY; break; + case VIDIOC_STREAMON32: cmd = VIDIOC_STREAMON; break; + case VIDIOC_STREAMOFF32: cmd = VIDIOC_STREAMOFF; break; + case VIDIOC_G_INPUT32: cmd = VIDIOC_G_INPUT; break; + case VIDIOC_S_INPUT32: cmd = VIDIOC_S_INPUT; break; + case VIDIOC_G_OUTPUT32: cmd = VIDIOC_G_OUTPUT; break; + case VIDIOC_S_OUTPUT32: cmd = VIDIOC_S_OUTPUT; break; + } switch (cmd) { #ifdef CONFIG_VIDEO_V4L1_COMPAT @@ -715,7 +773,6 @@ static int do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg case VIDIOCGTUNER: err = get_video_tuner32(&karg.vt, up); compatible_arg = 0; - break; case VIDIOCSFBUF: @@ -723,25 +780,42 @@ static int do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg compatible_arg = 0; break; -#if 0 /*FIXME*/ - case VIDIOCSAUDIO: - err = get_video_audio32(&karg.va, up); + case VIDIOCSWIN: + err = get_video_window32(&karg.vw, up); + compatible_arg = 0; + break; + + case VIDIOCGWIN: + case VIDIOCGFBUF: + case VIDIOCGFREQ: + compatible_arg = 0; + break; + + case VIDIOCSMICROCODE: + err = get_microcode32(&karg.vc, up); compatible_arg = 0; break; -#endif case VIDIOCSFREQ: + err = get_user(karg.vx, (u32 __user *)up); + compatible_arg = 0; + break; + + case VIDIOCCAPTURE: + case VIDIOCSYNC: + case VIDIOCSWRITEMODE: #endif - case VIDIOC_S_INPUT: case VIDIOC_OVERLAY: case VIDIOC_STREAMON: case VIDIOC_STREAMOFF: - err = get_user(karg.vx, (u32 __user *)up); - compatible_arg = 1; + case VIDIOC_S_INPUT: + case VIDIOC_S_OUTPUT: + err = get_user(karg.vi, (s32 __user *)up); + compatible_arg = 0; break; - case VIDIOC_S_FBUF: - err = get_v4l2_framebuffer32(&karg.v2fb, up); + case VIDIOC_G_INPUT: + case VIDIOC_G_OUTPUT: compatible_arg = 0; break; @@ -759,127 +833,108 @@ static int do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg compatible_arg = 0; break; - case VIDIOC_ENUMSTD: - err = get_v4l2_standard(&karg.v2s, up); + case VIDIOC_S_FBUF: + err = get_v4l2_framebuffer32(&karg.v2fb, up); compatible_arg = 0; break; - case VIDIOC_ENUMSTD32: - err = get_v4l2_standard32(&karg.v2s, up); + case VIDIOC_G_FBUF: compatible_arg = 0; break; - case VIDIOC_ENUMINPUT: - err = get_v4l2_input(&karg.v2i, up); + case VIDIOC_ENUMSTD: + err = get_v4l2_standard32(&karg.v2s, up); compatible_arg = 0; break; - case VIDIOC_ENUMINPUT32: + case VIDIOC_ENUMINPUT: err = get_v4l2_input32(&karg.v2i, up); compatible_arg = 0; break; - case VIDIOC_G_TUNER: - case VIDIOC_S_TUNER: - err = get_v4l2_tuner(&karg.v2t, up); + case VIDIOC_G_EXT_CTRLS: + case VIDIOC_S_EXT_CTRLS: + case VIDIOC_TRY_EXT_CTRLS: + err = get_v4l2_ext_controls32(&karg.v2ecs, up); compatible_arg = 0; break; - -#ifdef CONFIG_VIDEO_V4L1_COMPAT - case VIDIOCGWIN: - case VIDIOCGFBUF: - case VIDIOCGFREQ: -#endif - case VIDIOC_G_FBUF: - case VIDIOC_G_INPUT: - compatible_arg = 0; - break; -#ifdef CONFIG_VIDEO_V4L1_COMPAT - case VIDIOCSMICROCODE: - err = microcode32(&karg.vc, up); - compatible_arg = 0; - break; -#endif - }; + } if (err) - goto out; + return err; if (compatible_arg) - err = native_ioctl(file, realcmd, (unsigned long)up); + err = native_ioctl(file, cmd, (unsigned long)up); else { mm_segment_t old_fs = get_fs(); set_fs(KERNEL_DS); - err = native_ioctl(file, realcmd, (unsigned long)&karg); + err = native_ioctl(file, cmd, (unsigned long)&karg); set_fs(old_fs); } - if (err == 0) { - switch (cmd) { -#ifdef CONFIG_VIDEO_V4L1_COMPAT - case VIDIOCGTUNER: - err = put_video_tuner32(&karg.vt, up); - break; - - case VIDIOCGWIN: - err = put_video_window32(&karg.vw, up); - break; - - case VIDIOCGFBUF: - err = put_video_buffer32(&karg.vb, up); - break; -#if 0 /*FIXME*/ - case VIDIOCGAUDIO: - err = put_video_audio32(&karg.va, up); - break; -#endif -#endif - case VIDIOC_G_FBUF: - err = put_v4l2_framebuffer32(&karg.v2fb, up); - break; - - case VIDIOC_G_FMT: - case VIDIOC_S_FMT: - case VIDIOC_TRY_FMT: - err = put_v4l2_format32(&karg.v2f, up); - break; - - case VIDIOC_QUERYBUF: - case VIDIOC_QBUF: - case VIDIOC_DQBUF: - err = put_v4l2_buffer32(&karg.v2b, up); - break; - - case VIDIOC_ENUMSTD: - err = put_v4l2_standard(&karg.v2s, up); - break; - - case VIDIOC_ENUMSTD32: - err = put_v4l2_standard32(&karg.v2s, up); - break; - - case VIDIOC_G_TUNER: - case VIDIOC_S_TUNER: - err = put_v4l2_tuner(&karg.v2t, up); - break; - - case VIDIOC_ENUMINPUT: - err = put_v4l2_input(&karg.v2i, up); - break; - - case VIDIOC_ENUMINPUT32: - err = put_v4l2_input32(&karg.v2i, up); - break; + /* Special case: even after an error we need to put the + results back for these ioctls since the error_idx will + contain information on which control failed. */ + switch (cmd) { + case VIDIOC_G_EXT_CTRLS: + case VIDIOC_S_EXT_CTRLS: + case VIDIOC_TRY_EXT_CTRLS: + if (put_v4l2_ext_controls32(&karg.v2ecs, up)) + err = -EFAULT; + break; + } + if (err) + return err; + switch (cmd) { #ifdef CONFIG_VIDEO_V4L1_COMPAT - case VIDIOCGFREQ: + case VIDIOCGTUNER: + err = put_video_tuner32(&karg.vt, up); + break; + + case VIDIOCGWIN: + err = put_video_window32(&karg.vw, up); + break; + + case VIDIOCGFBUF: + err = put_video_buffer32(&karg.vb, up); + break; + + case VIDIOCGFREQ: + err = put_user(((u32)karg.vx), (u32 __user *)up); + break; #endif - case VIDIOC_G_INPUT: - err = put_user(((u32)karg.vx), (u32 __user *)up); - break; - }; + case VIDIOC_S_INPUT: + case VIDIOC_S_OUTPUT: + case VIDIOC_G_INPUT: + case VIDIOC_G_OUTPUT: + err = put_user(((s32)karg.vi), (s32 __user *)up); + break; + + case VIDIOC_G_FBUF: + err = put_v4l2_framebuffer32(&karg.v2fb, up); + break; + + case VIDIOC_G_FMT: + case VIDIOC_S_FMT: + case VIDIOC_TRY_FMT: + err = put_v4l2_format32(&karg.v2f, up); + break; + + case VIDIOC_QUERYBUF: + case VIDIOC_QBUF: + case VIDIOC_DQBUF: + err = put_v4l2_buffer32(&karg.v2b, up); + break; + + case VIDIOC_ENUMSTD: + err = put_v4l2_standard32(&karg.v2s, up); + break; + + case VIDIOC_ENUMINPUT: + err = put_v4l2_input32(&karg.v2i, up); + break; } -out: return err; } @@ -892,26 +947,48 @@ long v4l_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) switch (cmd) { #ifdef CONFIG_VIDEO_V4L1_COMPAT - case VIDIOCSWIN32: - ret = do_set_window(file, cmd, arg); - break; + case VIDIOCGCAP: + case VIDIOCGCHAN: + case VIDIOCSCHAN: case VIDIOCGTUNER32: case VIDIOCSTUNER32: + case VIDIOCGPICT: + case VIDIOCSPICT: + case VIDIOCCAPTURE32: case VIDIOCGWIN32: + case VIDIOCSWIN32: case VIDIOCGFBUF32: case VIDIOCSFBUF32: + case VIDIOCKEY: case VIDIOCGFREQ32: case VIDIOCSFREQ32: case VIDIOCGAUDIO: case VIDIOCSAUDIO: + case VIDIOCSYNC32: + case VIDIOCMCAPTURE: + case VIDIOCGMBUF: + case VIDIOCGUNIT: + case VIDIOCGCAPTURE: + case VIDIOCSCAPTURE: + case VIDIOCSPLAYMODE: + case VIDIOCSWRITEMODE32: + case VIDIOCGPLAYINFO: + case VIDIOCSMICROCODE32: case VIDIOCGVBIFMT: case VIDIOCSVBIFMT: +#endif +#ifdef __OLD_VIDIOC_ + case VIDIOC_OVERLAY32_OLD: + case VIDIOC_S_PARM_OLD: + case VIDIOC_S_CTRL_OLD: + case VIDIOC_G_AUDIO_OLD: + case VIDIOC_G_AUDOUT_OLD: + case VIDIOC_CROPCAP_OLD: #endif case VIDIOC_QUERYCAP: + case VIDIOC_RESERVED: case VIDIOC_ENUM_FMT: case VIDIOC_G_FMT32: - case VIDIOC_CROPCAP: - case VIDIOC_S_CROP: case VIDIOC_S_FMT32: case VIDIOC_REQBUFS: case VIDIOC_QUERYBUF32: @@ -926,43 +1003,56 @@ long v4l_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) case VIDIOC_S_PARM: case VIDIOC_G_STD: case VIDIOC_S_STD: - case VIDIOC_G_TUNER: - case VIDIOC_S_TUNER: - case VIDIOC_ENUMSTD: case VIDIOC_ENUMSTD32: - case VIDIOC_ENUMINPUT: case VIDIOC_ENUMINPUT32: case VIDIOC_G_CTRL: case VIDIOC_S_CTRL: - case VIDIOC_S_CTRL32: - case VIDIOC_S_FREQUENCY: - case VIDIOC_G_FREQUENCY: + case VIDIOC_G_TUNER: + case VIDIOC_S_TUNER: + case VIDIOC_G_AUDIO: + case VIDIOC_S_AUDIO: case VIDIOC_QUERYCTRL: + case VIDIOC_QUERYMENU: case VIDIOC_G_INPUT32: case VIDIOC_S_INPUT32: + case VIDIOC_G_OUTPUT32: + case VIDIOC_S_OUTPUT32: + case VIDIOC_ENUMOUTPUT: + case VIDIOC_G_AUDOUT: + case VIDIOC_S_AUDOUT: + case VIDIOC_G_MODULATOR: + case VIDIOC_S_MODULATOR: + case VIDIOC_S_FREQUENCY: + case VIDIOC_G_FREQUENCY: + case VIDIOC_CROPCAP: + case VIDIOC_G_CROP: + case VIDIOC_S_CROP: + case VIDIOC_G_JPEGCOMP: + case VIDIOC_S_JPEGCOMP: + case VIDIOC_QUERYSTD: case VIDIOC_TRY_FMT32: - case VIDIOC_S_HW_FREQ_SEEK: + case VIDIOC_ENUMAUDIO: + case VIDIOC_ENUMAUDOUT: + case VIDIOC_G_PRIORITY: + case VIDIOC_S_PRIORITY: + case VIDIOC_G_SLICED_VBI_CAP: + case VIDIOC_LOG_STATUS: + case VIDIOC_G_EXT_CTRLS32: + case VIDIOC_S_EXT_CTRLS32: + case VIDIOC_TRY_EXT_CTRLS32: case VIDIOC_ENUM_FRAMESIZES: case VIDIOC_ENUM_FRAMEINTERVALS: + case VIDIOC_G_ENC_INDEX: + case VIDIOC_ENCODER_CMD: + case VIDIOC_TRY_ENCODER_CMD: + case VIDIOC_DBG_S_REGISTER: + case VIDIOC_DBG_G_REGISTER: + case VIDIOC_G_CHIP_IDENT: + case VIDIOC_S_HW_FREQ_SEEK: ret = do_video_ioctl(file, cmd, arg); break; #ifdef CONFIG_VIDEO_V4L1_COMPAT - /* Little v, the video4linux ioctls (conflict?) */ - case VIDIOCGCAP: - case VIDIOCGCHAN: - case VIDIOCSCHAN: - case VIDIOCGPICT: - case VIDIOCSPICT: - case VIDIOCCAPTURE: - case VIDIOCKEY: - case VIDIOCSYNC: - case VIDIOCMCAPTURE: - case VIDIOCGMBUF: - case VIDIOCGUNIT: - case VIDIOCGCAPTURE: - case VIDIOCSCAPTURE: - /* BTTV specific... */ case _IOW('v', BASE_VIDIOCPRIVATE+0, char [256]): case _IOR('v', BASE_VIDIOCPRIVATE+1, char [256]): diff --git a/linux/include/linux/videodev2.h b/linux/include/linux/videodev2.h index 6ee771108..09cfcc43c 100644 --- a/linux/include/linux/videodev2.h +++ b/linux/include/linux/videodev2.h @@ -1465,6 +1465,8 @@ struct v4l2_chip_ident { #define VIDIOC_G_CHIP_IDENT _IOWR('V', 81, struct v4l2_chip_ident) #endif #define VIDIOC_S_HW_FREQ_SEEK _IOW('V', 82, struct v4l2_hw_freq_seek) +/* Reminder: when adding new ioctls please add support for them to + drivers/media/video/v4l2-compat-ioctl32.c as well! */ #ifdef __OLD_VIDIOC_ /* for compatibility, will go away some day */ -- cgit v1.2.3 From 1fe56e49c1de689d7f2bcc334ff2eea0f4d794bd Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 21 Dec 2008 14:44:24 +0100 Subject: v4l2-compat32: add two additional #ifdef __OLD_VIDIOC_ lines From: Hans Verkuil Priority: normal Signed-off-by: Hans Verkuil --- linux/drivers/media/video/v4l2-compat-ioctl32.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'linux') diff --git a/linux/drivers/media/video/v4l2-compat-ioctl32.c b/linux/drivers/media/video/v4l2-compat-ioctl32.c index 7a0c6b6a4..c9cb73a06 100644 --- a/linux/drivers/media/video/v4l2-compat-ioctl32.c +++ b/linux/drivers/media/video/v4l2-compat-ioctl32.c @@ -700,7 +700,9 @@ static int put_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext #define VIDIOC_TRY_EXT_CTRLS32 _IOWR('V', 73, struct v4l2_ext_controls32) #define VIDIOC_OVERLAY32 _IOW ('V', 14, s32) +#ifdef __OLD_VIDIOC_ #define VIDIOC_OVERLAY32_OLD _IOWR('V', 14, s32) +#endif #define VIDIOC_STREAMON32 _IOW ('V', 18, s32) #define VIDIOC_STREAMOFF32 _IOW ('V', 19, s32) #define VIDIOC_G_INPUT32 _IOR ('V', 38, s32) @@ -758,7 +760,9 @@ static int do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg case VIDIOC_S_EXT_CTRLS32: cmd = VIDIOC_S_EXT_CTRLS; break; case VIDIOC_TRY_EXT_CTRLS32: cmd = VIDIOC_TRY_EXT_CTRLS; break; case VIDIOC_OVERLAY32: cmd = VIDIOC_OVERLAY; break; +#ifdef __OLD_VIDIOC_ case VIDIOC_OVERLAY32_OLD: cmd = VIDIOC_OVERLAY; break; +#endif case VIDIOC_STREAMON32: cmd = VIDIOC_STREAMON; break; case VIDIOC_STREAMOFF32: cmd = VIDIOC_STREAMOFF; break; case VIDIOC_G_INPUT32: cmd = VIDIOC_G_INPUT; break; -- cgit v1.2.3 From a48a5ef826d2948a3b2a17d024cf853f1ed3b931 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Sun, 21 Dec 2008 15:58:05 +0100 Subject: gspca: t613: Bad loop in om6802 reset. From: Roel Kluin Priority: normal Signed-off-by: Roel Kluin Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/t613.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/t613.c b/linux/drivers/media/video/gspca/t613.c index d2d6ad9d1..452ca1d16 100644 --- a/linux/drivers/media/video/gspca/t613.c +++ b/linux/drivers/media/video/gspca/t613.c @@ -499,7 +499,7 @@ static void om6802_sensor_init(struct gspca_dev *gspca_dev) reg_w_buf(gspca_dev, sensor_reset, sizeof sensor_reset); msleep(5); i = 4; - while (--i < 0) { + while (--i > 0) { byte = reg_r(gspca_dev, 0x0060); if (!(byte & 0x01)) break; -- cgit v1.2.3 From e3ed9d5ccedc1a7a60c7cf106fbfb795448b32ea Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Sun, 21 Dec 2008 19:02:54 +0100 Subject: gspca - ov534 and m5602: Set static some functions/variables. From: Jean-Francois Moine Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/m5602/m5602_core.c | 2 +- linux/drivers/media/video/gspca/ov534.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/m5602/m5602_core.c b/linux/drivers/media/video/gspca/m5602/m5602_core.c index fd6ce384b..059c4d2ff 100644 --- a/linux/drivers/media/video/gspca/m5602/m5602_core.c +++ b/linux/drivers/media/video/gspca/m5602/m5602_core.c @@ -24,7 +24,7 @@ /* Kernel module parameters */ int force_sensor; -int dump_bridge; +static int dump_bridge; int dump_sensor; static const __devinitdata struct usb_device_id m5602_table[] = { diff --git a/linux/drivers/media/video/gspca/ov534.c b/linux/drivers/media/video/gspca/ov534.c index 7332947cb..0eeb417d4 100644 --- a/linux/drivers/media/video/gspca/ov534.c +++ b/linux/drivers/media/video/gspca/ov534.c @@ -499,8 +499,8 @@ discard: } /* get stream parameters (framerate) */ -int sd_get_streamparm(struct gspca_dev *gspca_dev, - struct v4l2_streamparm *parm) +static int sd_get_streamparm(struct gspca_dev *gspca_dev, + struct v4l2_streamparm *parm) { struct v4l2_captureparm *cp = &parm->parm.capture; struct v4l2_fract *tpf = &cp->timeperframe; @@ -517,8 +517,8 @@ int sd_get_streamparm(struct gspca_dev *gspca_dev, } /* set stream parameters (framerate) */ -int sd_set_streamparm(struct gspca_dev *gspca_dev, - struct v4l2_streamparm *parm) +static int sd_set_streamparm(struct gspca_dev *gspca_dev, + struct v4l2_streamparm *parm) { struct v4l2_captureparm *cp = &parm->parm.capture; struct v4l2_fract *tpf = &cp->timeperframe; -- cgit v1.2.3 From a47d14089127cf096b56de570d96ac0d2611f679 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 22 Dec 2008 07:14:31 -0200 Subject: em28xx: Fix bad locks on error condition From: Mauro Carvalho Chehab Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-video.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index 9fb9363dc..de4065bd7 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -825,8 +825,10 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, vidioc_try_fmt_vid_cap(file, priv, f); fmt = format_by_fourcc(f->fmt.pix.pixelformat); - if (!fmt) - return -EINVAL; + if (!fmt) { + rc = -EINVAL; + goto out; + } if (videobuf_queue_is_busy(&fh->vb_vidq)) { em28xx_errdev("%s queue busy\n", __func__); @@ -1317,10 +1319,8 @@ static int vidioc_streamon(struct file *file, void *priv, mutex_lock(&dev->lock); rc = res_get(fh); - if (unlikely(rc < 0)) - return rc; - - rc = videobuf_streamon(&fh->vb_vidq); + if (likely(rc >= 0)) + rc = videobuf_streamon(&fh->vb_vidq); mutex_unlock(&dev->lock); -- cgit v1.2.3 From 290fdd37a2914bb6aa8e67179c1d539c5cddd81c Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 22 Dec 2008 07:20:32 -0200 Subject: em28xx: de-obfuscate vidioc_g_ctrl logic From: Mauro Carvalho Chehab vidioc_g_ctrl() were using an uneeded confusing logic. Instead, use the direct approach. Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-video.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index de4065bd7..7531a20f7 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -1069,18 +1069,14 @@ static int vidioc_g_ctrl(struct file *file, void *priv, rc = check_dev(dev); if (rc < 0) return rc; + rc = 0; mutex_lock(&dev->lock); - if (!dev->board.has_msp34xx) - rc = em28xx_get_ctrl(dev, ctrl); - else - rc = -EINVAL; - - if (rc == -EINVAL) { + if (dev->board.has_msp34xx) em28xx_i2c_call_clients(dev, VIDIOC_G_CTRL, ctrl); - rc = 0; - } + else + rc = em28xx_get_ctrl(dev, ctrl); mutex_unlock(&dev->lock); return rc; -- cgit v1.2.3 From cdf96638de5ab84aa3c30176add8d458d4344014 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 22 Dec 2008 08:54:05 -0200 Subject: tuner-core: remove a no longer needed hack From: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/tuner-core.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/tuner-core.c b/linux/drivers/media/video/tuner-core.c index 8d1f8bebf..8f1ca6567 100644 --- a/linux/drivers/media/video/tuner-core.c +++ b/linux/drivers/media/video/tuner-core.c @@ -933,11 +933,6 @@ static int tuner_ioctl(struct v4l2_subdev *sd, int cmd, void *arg) } return -ENOIOCTLCMD; } -#else -static int tuner_ioctl(struct v4l2_subdev *sd, int cmd, void *arg) -{ - return -ENOIOCTLCMD; -} #endif static int tuner_s_config(struct v4l2_subdev *sd, const struct v4l2_priv_tun_config *cfg) -- cgit v1.2.3 From 786d73eb9aad6efa7ddd89150d18a689d08f4e07 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 22 Dec 2008 14:18:27 -0200 Subject: em28xx: Add suport for debugging AC97 anciliary chips From: Mauro Carvalho Chehab The em28xx driver can be coupled to an anciliary AC97 chip. This patch allows read/write AC97 registers directly. Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-core.c | 4 ++-- linux/drivers/media/video/em28xx/em28xx-video.c | 19 +++++++++++++++++++ linux/drivers/media/video/em28xx/em28xx.h | 3 +++ linux/include/linux/videodev2.h | 1 + 4 files changed, 25 insertions(+), 2 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-core.c b/linux/drivers/media/video/em28xx/em28xx-core.c index 2c975ffa4..025a54bb7 100644 --- a/linux/drivers/media/video/em28xx/em28xx-core.c +++ b/linux/drivers/media/video/em28xx/em28xx-core.c @@ -259,7 +259,7 @@ static int em28xx_is_ac97_ready(struct em28xx *dev) * em28xx_read_ac97() * write a 16 bit value to the specified AC97 address (LSB first!) */ -static int em28xx_read_ac97(struct em28xx *dev, u8 reg) +int em28xx_read_ac97(struct em28xx *dev, u8 reg) { int ret; u8 addr = (reg & 0x7f) | 0x80; @@ -285,7 +285,7 @@ static int em28xx_read_ac97(struct em28xx *dev, u8 reg) * em28xx_write_ac97() * write a 16 bit value to the specified AC97 address (LSB first!) */ -static int em28xx_write_ac97(struct em28xx *dev, u8 reg, u16 val) +int em28xx_write_ac97(struct em28xx *dev, u8 reg, u16 val) { int ret; u8 addr = reg & 0x7f; diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index 7531a20f7..4d9b167e6 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -1233,6 +1233,17 @@ static int vidioc_g_register(struct file *file, void *priv, struct em28xx *dev = fh->dev; int ret; + if (reg->match_type == V4L2_CHIP_MATCH_AC97) { + mutex_lock(&dev->lock); + ret = em28xx_read_ac97(dev, reg->reg); + mutex_unlock(&dev->lock); + if (ret < 0) + return ret; + + reg->val = ret; + return 0; + } + if (!v4l2_chip_match_host(reg->match_type, reg->match_chip)) return -EINVAL; @@ -1268,6 +1279,14 @@ static int vidioc_s_register(struct file *file, void *priv, __le64 buf; int rc; + if (reg->match_type == V4L2_CHIP_MATCH_AC97) { + mutex_lock(&dev->lock); + rc = em28xx_write_ac97(dev, reg->reg, reg->val); + mutex_unlock(&dev->lock); + + return rc; + } + buf = cpu_to_le64(reg->val); mutex_lock(&dev->lock); diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index ae511ed7e..84f49934a 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -577,6 +577,9 @@ int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf, int em28xx_write_regs(struct em28xx *dev, u16 reg, char *buf, int len); int em28xx_write_reg(struct em28xx *dev, u16 reg, u8 val); +int em28xx_read_ac97(struct em28xx *dev, u8 reg); +int em28xx_write_ac97(struct em28xx *dev, u8 reg, u16 val); + int em28xx_audio_analog_set(struct em28xx *dev); int em28xx_audio_setup(struct em28xx *dev); diff --git a/linux/include/linux/videodev2.h b/linux/include/linux/videodev2.h index f64a3f780..018894814 100644 --- a/linux/include/linux/videodev2.h +++ b/linux/include/linux/videodev2.h @@ -1376,6 +1376,7 @@ struct v4l2_streamparm { #define V4L2_CHIP_MATCH_HOST 0 /* Match against chip ID on host (0 for the host) */ #define V4L2_CHIP_MATCH_I2C_DRIVER 1 /* Match against I2C driver ID */ #define V4L2_CHIP_MATCH_I2C_ADDR 2 /* Match against I2C 7-bit address */ +#define V4L2_CHIP_MATCH_AC97 3 /* Match against anciliary AC97 chip */ struct v4l2_register { __u32 match_type; /* Match type */ -- cgit v1.2.3 From 64fe35cc0bcee9c48cfab4d8f813996d8c2c0369 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 22 Dec 2008 21:34:18 -0200 Subject: tvp5150: add support for VIDIOC_G_CHIP_IDENT ioctl From: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/tvp5150.c | 16 ++++++++++++++++ linux/include/media/v4l2-chip-ident.h | 3 +++ 2 files changed, 19 insertions(+) (limited to 'linux') diff --git a/linux/drivers/media/video/tvp5150.c b/linux/drivers/media/video/tvp5150.c index 37421fb03..404df4aa2 100644 --- a/linux/drivers/media/video/tvp5150.c +++ b/linux/drivers/media/video/tvp5150.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "tvp5150_reg.h" @@ -1028,6 +1029,20 @@ static int tvp5150_decode_vbi_line(struct v4l2_subdev *sd, } #endif +static int tvp5150_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_chip_ident *chip) +{ + int rev; + struct i2c_client *client = v4l2_get_subdevdata(sd); + + rev = tvp5150_read(sd, TVP5150_ROM_MAJOR_VER) << 8 | + tvp5150_read(sd, TVP5150_ROM_MINOR_VER); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TVP5150, + rev); +} + + #ifdef CONFIG_VIDEO_ADV_DEBUG static int tvp5150_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg) { @@ -1093,6 +1108,7 @@ static const struct v4l2_subdev_core_ops tvp5150_core_ops = { .s_ctrl = tvp5150_s_ctrl, .queryctrl = tvp5150_queryctrl, .reset = tvp5150_reset, + .g_chip_ident = tvp5150_g_chip_ident, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = tvp5150_g_register, .s_register = tvp5150_s_register, diff --git a/linux/include/media/v4l2-chip-ident.h b/linux/include/media/v4l2-chip-ident.h index bfe5142e6..e3e5b5393 100644 --- a/linux/include/media/v4l2-chip-ident.h +++ b/linux/include/media/v4l2-chip-ident.h @@ -70,6 +70,9 @@ enum { /* module vp27smpx: just ident 2700 */ V4L2_IDENT_VP27SMPX = 2700, + /* module tvp5150 */ + V4L2_IDENT_TVP5150 = 5150, + /* module cs5345: just ident 5345 */ V4L2_IDENT_CS5345 = 5345, -- cgit v1.2.3 From 57f1a4de9c25fb0d892c1dfd2369b99fa84b1406 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 22 Dec 2008 21:58:41 -0200 Subject: em28xx: Allow get/set registers for debug on i2c slave chips From: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/em28xx/em28xx-video.c | 46 ++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 5 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index 4d9b167e6..9a7f57c8b 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -39,6 +39,7 @@ #include "em28xx.h" #include #include +#include #include #include @@ -1226,6 +1227,21 @@ static int em28xx_reg_len(int reg) } } +static int vidioc_g_chip_ident(struct file *file, void *priv, + struct v4l2_chip_ident *chip) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + + chip->ident = V4L2_IDENT_NONE; + chip->revision = 0; + + em28xx_i2c_call_clients(dev, VIDIOC_G_CHIP_IDENT, chip); + + return 0; +} + + static int vidioc_g_register(struct file *file, void *priv, struct v4l2_register *reg) { @@ -1233,7 +1249,8 @@ static int vidioc_g_register(struct file *file, void *priv, struct em28xx *dev = fh->dev; int ret; - if (reg->match_type == V4L2_CHIP_MATCH_AC97) { + switch (reg->match_type) { + case V4L2_CHIP_MATCH_AC97: mutex_lock(&dev->lock); ret = em28xx_read_ac97(dev, reg->reg); mutex_unlock(&dev->lock); @@ -1242,11 +1259,18 @@ static int vidioc_g_register(struct file *file, void *priv, reg->val = ret; return 0; - } - - if (!v4l2_chip_match_host(reg->match_type, reg->match_chip)) + case V4L2_CHIP_MATCH_I2C_DRIVER: + em28xx_i2c_call_clients(dev, VIDIOC_DBG_G_REGISTER, reg); + return 0; + case V4L2_CHIP_MATCH_I2C_ADDR: + /* Not supported yet */ return -EINVAL; + default: + if (!v4l2_chip_match_host(reg->match_type, reg->match_chip)) + return -EINVAL; + } + /* Match host */ if (em28xx_reg_len(reg->reg) == 1) { mutex_lock(&dev->lock); ret = em28xx_read_reg(dev, reg->reg); @@ -1279,14 +1303,25 @@ static int vidioc_s_register(struct file *file, void *priv, __le64 buf; int rc; - if (reg->match_type == V4L2_CHIP_MATCH_AC97) { + switch (reg->match_type) { + case V4L2_CHIP_MATCH_AC97: mutex_lock(&dev->lock); rc = em28xx_write_ac97(dev, reg->reg, reg->val); mutex_unlock(&dev->lock); return rc; + case V4L2_CHIP_MATCH_I2C_DRIVER: + em28xx_i2c_call_clients(dev, VIDIOC_DBG_S_REGISTER, reg); + return 0; + case V4L2_CHIP_MATCH_I2C_ADDR: + /* Not supported yet */ + return -EINVAL; + default: + if (!v4l2_chip_match_host(reg->match_type, reg->match_chip)) + return -EINVAL; } + /* Match host */ buf = cpu_to_le64(reg->val); mutex_lock(&dev->lock); @@ -1996,6 +2031,7 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { #ifdef CONFIG_VIDEO_ADV_DEBUG .vidioc_g_register = vidioc_g_register, .vidioc_s_register = vidioc_s_register, + .vidioc_g_chip_ident = vidioc_g_chip_ident, #endif #ifdef CONFIG_VIDEO_V4L1_COMPAT .vidiocgmbuf = vidiocgmbuf, -- cgit v1.2.3 From bba7ba66b44e9f98fbf2c6019be85b8448f0a3a7 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 23 Dec 2008 01:38:23 +0000 Subject: v4l: usbvideo, fix module ref count check From: Jiri Slaby usbvideo_ClientIncModCount may return value < 0 in the case of error, not > 0. Signed-off-by: Jiri Slaby Signed-off-by: Andrew Morton Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/usbvideo/usbvideo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/video/usbvideo/usbvideo.c b/linux/drivers/media/video/usbvideo/usbvideo.c index 332929bb2..85409c624 100644 --- a/linux/drivers/media/video/usbvideo/usbvideo.c +++ b/linux/drivers/media/video/usbvideo/usbvideo.c @@ -1123,7 +1123,7 @@ static int usbvideo_v4l_open(struct inode *inode, struct file *file) if (uvd->debug > 1) dev_info(&uvd->dev->dev, "%s($%p)\n", __func__, dev); - if (0 < usbvideo_ClientIncModCount(uvd)) + if (usbvideo_ClientIncModCount(uvd) < 0) return -ENODEV; mutex_lock(&uvd->lock); -- cgit v1.2.3 From 85eea3e7c4e6c0b3196466d9b14a8965cc751003 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 23 Dec 2008 11:35:17 +0100 Subject: v4l2-dev: allow drivers to pass v4l2_device as parent From: Hans Verkuil Drivers that use v4l2_device can set that as parent pointer in the v4l2_dev field instead of using the struct device parent field. This allows v4l2-dev.c to check whether this driver is v4l2_device based, and if so then it can offer additional services. Priority: normal Signed-off-by: Hans Verkuil --- linux/drivers/media/video/v4l2-dev.c | 3 +++ linux/include/media/v4l2-dev.h | 4 ++++ 2 files changed, 7 insertions(+) (limited to 'linux') diff --git a/linux/drivers/media/video/v4l2-dev.c b/linux/drivers/media/video/v4l2-dev.c index f877d3f86..0b832f210 100644 --- a/linux/drivers/media/video/v4l2-dev.c +++ b/linux/drivers/media/video/v4l2-dev.c @@ -30,6 +30,7 @@ #include #include +#include #include "compat.h" #define VIDEO_NUM_DEVICES 256 @@ -445,6 +446,8 @@ int video_register_device_index(struct video_device *vdev, int type, int nr, vdev->vfl_type = type; vdev->cdev = NULL; + if (vdev->v4l2_dev) + vdev->parent = vdev->v4l2_dev->dev; /* Part 2: find a free minor, kernel number and device index. */ #ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES diff --git a/linux/include/media/v4l2-dev.h b/linux/include/media/v4l2-dev.h index 6d76f0bce..c6cf5b1b2 100644 --- a/linux/include/media/v4l2-dev.h +++ b/linux/include/media/v4l2-dev.h @@ -25,6 +25,7 @@ #define VFL_TYPE_MAX 4 struct v4l2_ioctl_callbacks; +struct v4l2_device; /* Flag to mark the video_device struct as unregistered. Drivers can set this flag if they want to block all future @@ -49,7 +50,10 @@ struct video_device struct class_device dev; #endif struct cdev *cdev; /* character device */ + + /* Set either parent or v4l2_dev if your driver uses v4l2_device */ struct device *parent; /* device parent */ + struct v4l2_device *v4l2_dev; /* v4l2_device parent */ /* device info */ char name[32]; -- cgit v1.2.3 From 43cea8a5c34e5756adad03ee6522853203b48433 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 23 Dec 2008 11:35:45 +0100 Subject: ivtv: set v4l2_dev instead of parent. From: Hans Verkuil Priority: normal Signed-off-by: Hans Verkuil --- linux/drivers/media/video/ivtv/ivtv-streams.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/video/ivtv/ivtv-streams.c b/linux/drivers/media/video/ivtv/ivtv-streams.c index 76279ed30..f77d76470 100644 --- a/linux/drivers/media/video/ivtv/ivtv-streams.c +++ b/linux/drivers/media/video/ivtv/ivtv-streams.c @@ -209,7 +209,7 @@ static int ivtv_prep_dev(struct ivtv *itv, int type) itv->device.name, s->name); s->v4l2dev->num = num; - s->v4l2dev->parent = &itv->dev->dev; + s->v4l2dev->v4l2_dev = &itv->device; s->v4l2dev->fops = ivtv_stream_info[type].fops; s->v4l2dev->release = video_device_release; s->v4l2dev->tvnorms = V4L2_STD_ALL; -- cgit v1.2.3