Commit b1526421eac9a912b2cda7e147f1da2aa31be278
Committed by
Mauro Carvalho Chehab
1 parent
4519064c1c
Exists in
master
and in
4 other branches
V4L/DVB (8913): cx18: Create cx18_ specific wrappers for all pci mmio accessesors.
cx18: Create cx18_ specific wrappers for all pci mmio accessesors. This is a first step in instrumenting all CX23418 PCI bus IO, to debug problems with accessing the CX23418's PCI memory mapped IO. Signed-off-by: Andy Walls <awalls@radix.net> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Showing 19 changed files with 468 additions and 263 deletions Inline Diff
- drivers/media/video/cx18/Makefile
- drivers/media/video/cx18/cx18-audio.c
- drivers/media/video/cx18/cx18-av-core.c
- drivers/media/video/cx18/cx18-av-firmware.c
- drivers/media/video/cx18/cx18-driver.c
- drivers/media/video/cx18/cx18-driver.h
- drivers/media/video/cx18/cx18-dvb.c
- drivers/media/video/cx18/cx18-firmware.c
- drivers/media/video/cx18/cx18-gpio.c
- drivers/media/video/cx18/cx18-i2c.c
- drivers/media/video/cx18/cx18-io.c
- drivers/media/video/cx18/cx18-io.h
- drivers/media/video/cx18/cx18-ioctl.c
- drivers/media/video/cx18/cx18-irq.c
- drivers/media/video/cx18/cx18-mailbox.c
- drivers/media/video/cx18/cx18-queue.c
- drivers/media/video/cx18/cx18-queue.h
- drivers/media/video/cx18/cx18-scb.c
- drivers/media/video/cx18/cx18-streams.c
drivers/media/video/cx18/Makefile
1 | cx18-objs := cx18-driver.o cx18-cards.o cx18-i2c.o cx18-firmware.o cx18-gpio.o \ | 1 | cx18-objs := cx18-driver.o cx18-cards.o cx18-i2c.o cx18-firmware.o cx18-gpio.o \ |
2 | cx18-queue.o cx18-streams.o cx18-fileops.o cx18-ioctl.o cx18-controls.o \ | 2 | cx18-queue.o cx18-streams.o cx18-fileops.o cx18-ioctl.o cx18-controls.o \ |
3 | cx18-mailbox.o cx18-vbi.o cx18-audio.o cx18-video.o cx18-irq.o \ | 3 | cx18-mailbox.o cx18-vbi.o cx18-audio.o cx18-video.o cx18-irq.o \ |
4 | cx18-av-core.o cx18-av-audio.o cx18-av-firmware.o cx18-av-vbi.o cx18-scb.o \ | 4 | cx18-av-core.o cx18-av-audio.o cx18-av-firmware.o cx18-av-vbi.o cx18-scb.o \ |
5 | cx18-dvb.o | 5 | cx18-dvb.o cx18-io.o |
6 | 6 | ||
7 | obj-$(CONFIG_VIDEO_CX18) += cx18.o | 7 | obj-$(CONFIG_VIDEO_CX18) += cx18.o |
8 | 8 | ||
9 | EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core | 9 | EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core |
10 | EXTRA_CFLAGS += -Idrivers/media/dvb/frontends | 10 | EXTRA_CFLAGS += -Idrivers/media/dvb/frontends |
11 | EXTRA_CFLAGS += -Idrivers/media/common/tuners | 11 | EXTRA_CFLAGS += -Idrivers/media/common/tuners |
12 | 12 |
drivers/media/video/cx18/cx18-audio.c
1 | /* | 1 | /* |
2 | * cx18 audio-related functions | 2 | * cx18 audio-related functions |
3 | * | 3 | * |
4 | * Derived from ivtv-audio.c | 4 | * Derived from ivtv-audio.c |
5 | * | 5 | * |
6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | 6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> |
7 | * | 7 | * |
8 | * This program is free software; you can redistribute it and/or modify | 8 | * This program is free software; you can redistribute it and/or modify |
9 | * it under the terms of the GNU General Public License as published by | 9 | * it under the terms of the GNU General Public License as published by |
10 | * the Free Software Foundation; either version 2 of the License, or | 10 | * the Free Software Foundation; either version 2 of the License, or |
11 | * (at your option) any later version. | 11 | * (at your option) any later version. |
12 | * | 12 | * |
13 | * This program is distributed in the hope that it will be useful, | 13 | * This program is distributed in the hope that it will be useful, |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | * GNU General Public License for more details. | 16 | * GNU General Public License for more details. |
17 | * | 17 | * |
18 | * You should have received a copy of the GNU General Public License | 18 | * You should have received a copy of the GNU General Public License |
19 | * along with this program; if not, write to the Free Software | 19 | * along with this program; if not, write to the Free Software |
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | 20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
21 | * 02111-1307 USA | 21 | * 02111-1307 USA |
22 | */ | 22 | */ |
23 | 23 | ||
24 | #include "cx18-driver.h" | 24 | #include "cx18-driver.h" |
25 | #include "cx18-io.h" | ||
25 | #include "cx18-i2c.h" | 26 | #include "cx18-i2c.h" |
26 | #include "cx18-cards.h" | 27 | #include "cx18-cards.h" |
27 | #include "cx18-audio.h" | 28 | #include "cx18-audio.h" |
28 | 29 | ||
29 | #define CX18_AUDIO_ENABLE 0xc72014 | 30 | #define CX18_AUDIO_ENABLE 0xc72014 |
30 | 31 | ||
31 | /* Selects the audio input and output according to the current | 32 | /* Selects the audio input and output according to the current |
32 | settings. */ | 33 | settings. */ |
33 | int cx18_audio_set_io(struct cx18 *cx) | 34 | int cx18_audio_set_io(struct cx18 *cx) |
34 | { | 35 | { |
35 | struct v4l2_routing route; | 36 | struct v4l2_routing route; |
36 | u32 audio_input; | 37 | u32 audio_input; |
37 | u32 val; | 38 | u32 val; |
38 | int mux_input; | 39 | int mux_input; |
39 | int err; | 40 | int err; |
40 | 41 | ||
41 | /* Determine which input to use */ | 42 | /* Determine which input to use */ |
42 | if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) { | 43 | if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) { |
43 | audio_input = cx->card->radio_input.audio_input; | 44 | audio_input = cx->card->radio_input.audio_input; |
44 | mux_input = cx->card->radio_input.muxer_input; | 45 | mux_input = cx->card->radio_input.muxer_input; |
45 | } else { | 46 | } else { |
46 | audio_input = | 47 | audio_input = |
47 | cx->card->audio_inputs[cx->audio_input].audio_input; | 48 | cx->card->audio_inputs[cx->audio_input].audio_input; |
48 | mux_input = | 49 | mux_input = |
49 | cx->card->audio_inputs[cx->audio_input].muxer_input; | 50 | cx->card->audio_inputs[cx->audio_input].muxer_input; |
50 | } | 51 | } |
51 | 52 | ||
52 | /* handle muxer chips */ | 53 | /* handle muxer chips */ |
53 | route.input = mux_input; | 54 | route.input = mux_input; |
54 | route.output = 0; | 55 | route.output = 0; |
55 | cx18_i2c_hw(cx, cx->card->hw_muxer, VIDIOC_INT_S_AUDIO_ROUTING, &route); | 56 | cx18_i2c_hw(cx, cx->card->hw_muxer, VIDIOC_INT_S_AUDIO_ROUTING, &route); |
56 | 57 | ||
57 | route.input = audio_input; | 58 | route.input = audio_input; |
58 | err = cx18_i2c_hw(cx, cx->card->hw_audio_ctrl, | 59 | err = cx18_i2c_hw(cx, cx->card->hw_audio_ctrl, |
59 | VIDIOC_INT_S_AUDIO_ROUTING, &route); | 60 | VIDIOC_INT_S_AUDIO_ROUTING, &route); |
60 | if (err) | 61 | if (err) |
61 | return err; | 62 | return err; |
62 | 63 | ||
63 | val = read_reg(CX18_AUDIO_ENABLE) & ~0x30; | 64 | val = cx18_read_reg(cx, CX18_AUDIO_ENABLE) & ~0x30; |
64 | val |= (audio_input > CX18_AV_AUDIO_SERIAL2) ? 0x20 : | 65 | val |= (audio_input > CX18_AV_AUDIO_SERIAL2) ? 0x20 : |
65 | (audio_input << 4); | 66 | (audio_input << 4); |
66 | write_reg(val | 0xb00, CX18_AUDIO_ENABLE); | 67 | cx18_write_reg(cx, val | 0xb00, CX18_AUDIO_ENABLE); |
67 | cx18_vapi(cx, CX18_APU_RESETAI, 1, 0); | 68 | cx18_vapi(cx, CX18_APU_RESETAI, 1, 0); |
68 | return 0; | 69 | return 0; |
69 | } | 70 | } |
70 | 71 | ||
71 | void cx18_audio_set_route(struct cx18 *cx, struct v4l2_routing *route) | 72 | void cx18_audio_set_route(struct cx18 *cx, struct v4l2_routing *route) |
72 | { | 73 | { |
73 | cx18_i2c_hw(cx, cx->card->hw_audio_ctrl, | 74 | cx18_i2c_hw(cx, cx->card->hw_audio_ctrl, |
74 | VIDIOC_INT_S_AUDIO_ROUTING, route); | 75 | VIDIOC_INT_S_AUDIO_ROUTING, route); |
75 | } | 76 | } |
76 | 77 | ||
77 | void cx18_audio_set_audio_clock_freq(struct cx18 *cx, u8 freq) | 78 | void cx18_audio_set_audio_clock_freq(struct cx18 *cx, u8 freq) |
78 | { | 79 | { |
79 | static u32 freqs[3] = { 44100, 48000, 32000 }; | 80 | static u32 freqs[3] = { 44100, 48000, 32000 }; |
80 | 81 | ||
81 | /* The audio clock of the digitizer must match the codec sample | 82 | /* The audio clock of the digitizer must match the codec sample |
82 | rate otherwise you get some very strange effects. */ | 83 | rate otherwise you get some very strange effects. */ |
83 | if (freq > 2) | 84 | if (freq > 2) |
84 | return; | 85 | return; |
85 | cx18_call_i2c_clients(cx, VIDIOC_INT_AUDIO_CLOCK_FREQ, &freqs[freq]); | 86 | cx18_call_i2c_clients(cx, VIDIOC_INT_AUDIO_CLOCK_FREQ, &freqs[freq]); |
86 | } | 87 | } |
87 | 88 |
drivers/media/video/cx18/cx18-av-core.c
1 | /* | 1 | /* |
2 | * cx18 ADEC audio functions | 2 | * cx18 ADEC audio functions |
3 | * | 3 | * |
4 | * Derived from cx25840-core.c | 4 | * Derived from cx25840-core.c |
5 | * | 5 | * |
6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | 6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> |
7 | * | 7 | * |
8 | * This program is free software; you can redistribute it and/or | 8 | * This program is free software; you can redistribute it and/or |
9 | * modify it under the terms of the GNU General Public License | 9 | * modify it under the terms of the GNU General Public License |
10 | * as published by the Free Software Foundation; either version 2 | 10 | * as published by the Free Software Foundation; either version 2 |
11 | * of the License, or (at your option) any later version. | 11 | * of the License, or (at your option) any later version. |
12 | * | 12 | * |
13 | * This program is distributed in the hope that it will be useful, | 13 | * This program is distributed in the hope that it will be useful, |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | * GNU General Public License for more details. | 16 | * GNU General Public License for more details. |
17 | * | 17 | * |
18 | * You should have received a copy of the GNU General Public License | 18 | * You should have received a copy of the GNU General Public License |
19 | * along with this program; if not, write to the Free Software | 19 | * along with this program; if not, write to the Free Software |
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
21 | * 02110-1301, USA. | 21 | * 02110-1301, USA. |
22 | */ | 22 | */ |
23 | 23 | ||
24 | #include "cx18-driver.h" | 24 | #include "cx18-driver.h" |
25 | #include "cx18-io.h" | ||
25 | 26 | ||
26 | int cx18_av_write(struct cx18 *cx, u16 addr, u8 value) | 27 | int cx18_av_write(struct cx18 *cx, u16 addr, u8 value) |
27 | { | 28 | { |
28 | u32 x = readl(cx->reg_mem + 0xc40000 + (addr & ~3)); | 29 | u32 reg = 0xc40000 + (addr & ~3); |
29 | u32 mask = 0xff; | 30 | u32 mask = 0xff; |
30 | int shift = (addr & 3) * 8; | 31 | int shift = (addr & 3) * 8; |
32 | u32 x = cx18_read_reg(cx, reg); | ||
31 | 33 | ||
32 | x = (x & ~(mask << shift)) | ((u32)value << shift); | 34 | x = (x & ~(mask << shift)) | ((u32)value << shift); |
33 | writel(x, cx->reg_mem + 0xc40000 + (addr & ~3)); | 35 | cx18_write_reg(cx, x, reg); |
34 | return 0; | 36 | return 0; |
35 | } | 37 | } |
36 | 38 | ||
37 | int cx18_av_write4(struct cx18 *cx, u16 addr, u32 value) | 39 | int cx18_av_write4(struct cx18 *cx, u16 addr, u32 value) |
38 | { | 40 | { |
39 | writel(value, cx->reg_mem + 0xc40000 + addr); | 41 | cx18_write_reg(cx, value, 0xc40000 + addr); |
40 | return 0; | 42 | return 0; |
41 | } | 43 | } |
42 | 44 | ||
43 | u8 cx18_av_read(struct cx18 *cx, u16 addr) | 45 | u8 cx18_av_read(struct cx18 *cx, u16 addr) |
44 | { | 46 | { |
45 | u32 x = readl(cx->reg_mem + 0xc40000 + (addr & ~3)); | 47 | u32 x = cx18_read_reg(cx, 0xc40000 + (addr & ~3)); |
46 | int shift = (addr & 3) * 8; | 48 | int shift = (addr & 3) * 8; |
47 | 49 | ||
48 | return (x >> shift) & 0xff; | 50 | return (x >> shift) & 0xff; |
49 | } | 51 | } |
50 | 52 | ||
51 | u32 cx18_av_read4(struct cx18 *cx, u16 addr) | 53 | u32 cx18_av_read4(struct cx18 *cx, u16 addr) |
52 | { | 54 | { |
53 | return readl(cx->reg_mem + 0xc40000 + addr); | 55 | return cx18_read_reg(cx, 0xc40000 + addr); |
54 | } | 56 | } |
55 | 57 | ||
56 | int cx18_av_and_or(struct cx18 *cx, u16 addr, unsigned and_mask, | 58 | int cx18_av_and_or(struct cx18 *cx, u16 addr, unsigned and_mask, |
57 | u8 or_value) | 59 | u8 or_value) |
58 | { | 60 | { |
59 | return cx18_av_write(cx, addr, | 61 | return cx18_av_write(cx, addr, |
60 | (cx18_av_read(cx, addr) & and_mask) | | 62 | (cx18_av_read(cx, addr) & and_mask) | |
61 | or_value); | 63 | or_value); |
62 | } | 64 | } |
63 | 65 | ||
64 | int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 and_mask, | 66 | int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 and_mask, |
65 | u32 or_value) | 67 | u32 or_value) |
66 | { | 68 | { |
67 | return cx18_av_write4(cx, addr, | 69 | return cx18_av_write4(cx, addr, |
68 | (cx18_av_read4(cx, addr) & and_mask) | | 70 | (cx18_av_read4(cx, addr) & and_mask) | |
69 | or_value); | 71 | or_value); |
70 | } | 72 | } |
71 | 73 | ||
72 | /* ----------------------------------------------------------------------- */ | 74 | /* ----------------------------------------------------------------------- */ |
73 | 75 | ||
74 | static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input, | 76 | static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input, |
75 | enum cx18_av_audio_input aud_input); | 77 | enum cx18_av_audio_input aud_input); |
76 | static void log_audio_status(struct cx18 *cx); | 78 | static void log_audio_status(struct cx18 *cx); |
77 | static void log_video_status(struct cx18 *cx); | 79 | static void log_video_status(struct cx18 *cx); |
78 | 80 | ||
79 | /* ----------------------------------------------------------------------- */ | 81 | /* ----------------------------------------------------------------------- */ |
80 | 82 | ||
81 | static void cx18_av_initialize(struct cx18 *cx) | 83 | static void cx18_av_initialize(struct cx18 *cx) |
82 | { | 84 | { |
83 | struct cx18_av_state *state = &cx->av_state; | 85 | struct cx18_av_state *state = &cx->av_state; |
84 | u32 v; | 86 | u32 v; |
85 | 87 | ||
86 | cx18_av_loadfw(cx); | 88 | cx18_av_loadfw(cx); |
87 | /* Stop 8051 code execution */ | 89 | /* Stop 8051 code execution */ |
88 | cx18_av_write4(cx, CXADEC_DL_CTL, 0x03000000); | 90 | cx18_av_write4(cx, CXADEC_DL_CTL, 0x03000000); |
89 | 91 | ||
90 | /* initallize the PLL by toggling sleep bit */ | 92 | /* initallize the PLL by toggling sleep bit */ |
91 | v = cx18_av_read4(cx, CXADEC_HOST_REG1); | 93 | v = cx18_av_read4(cx, CXADEC_HOST_REG1); |
92 | /* enable sleep mode */ | 94 | /* enable sleep mode */ |
93 | cx18_av_write4(cx, CXADEC_HOST_REG1, v | 1); | 95 | cx18_av_write4(cx, CXADEC_HOST_REG1, v | 1); |
94 | /* disable sleep mode */ | 96 | /* disable sleep mode */ |
95 | cx18_av_write4(cx, CXADEC_HOST_REG1, v & 0xfffe); | 97 | cx18_av_write4(cx, CXADEC_HOST_REG1, v & 0xfffe); |
96 | 98 | ||
97 | /* initialize DLLs */ | 99 | /* initialize DLLs */ |
98 | v = cx18_av_read4(cx, CXADEC_DLL1_DIAG_CTRL) & 0xE1FFFEFF; | 100 | v = cx18_av_read4(cx, CXADEC_DLL1_DIAG_CTRL) & 0xE1FFFEFF; |
99 | /* disable FLD */ | 101 | /* disable FLD */ |
100 | cx18_av_write4(cx, CXADEC_DLL1_DIAG_CTRL, v); | 102 | cx18_av_write4(cx, CXADEC_DLL1_DIAG_CTRL, v); |
101 | /* enable FLD */ | 103 | /* enable FLD */ |
102 | cx18_av_write4(cx, CXADEC_DLL1_DIAG_CTRL, v | 0x10000100); | 104 | cx18_av_write4(cx, CXADEC_DLL1_DIAG_CTRL, v | 0x10000100); |
103 | 105 | ||
104 | v = cx18_av_read4(cx, CXADEC_DLL2_DIAG_CTRL) & 0xE1FFFEFF; | 106 | v = cx18_av_read4(cx, CXADEC_DLL2_DIAG_CTRL) & 0xE1FFFEFF; |
105 | /* disable FLD */ | 107 | /* disable FLD */ |
106 | cx18_av_write4(cx, CXADEC_DLL2_DIAG_CTRL, v); | 108 | cx18_av_write4(cx, CXADEC_DLL2_DIAG_CTRL, v); |
107 | /* enable FLD */ | 109 | /* enable FLD */ |
108 | cx18_av_write4(cx, CXADEC_DLL2_DIAG_CTRL, v | 0x06000100); | 110 | cx18_av_write4(cx, CXADEC_DLL2_DIAG_CTRL, v | 0x06000100); |
109 | 111 | ||
110 | /* set analog bias currents. Set Vreg to 1.20V. */ | 112 | /* set analog bias currents. Set Vreg to 1.20V. */ |
111 | cx18_av_write4(cx, CXADEC_AFE_DIAG_CTRL1, 0x000A1802); | 113 | cx18_av_write4(cx, CXADEC_AFE_DIAG_CTRL1, 0x000A1802); |
112 | 114 | ||
113 | v = cx18_av_read4(cx, CXADEC_AFE_DIAG_CTRL3) | 1; | 115 | v = cx18_av_read4(cx, CXADEC_AFE_DIAG_CTRL3) | 1; |
114 | /* enable TUNE_FIL_RST */ | 116 | /* enable TUNE_FIL_RST */ |
115 | cx18_av_write4(cx, CXADEC_AFE_DIAG_CTRL3, v); | 117 | cx18_av_write4(cx, CXADEC_AFE_DIAG_CTRL3, v); |
116 | /* disable TUNE_FIL_RST */ | 118 | /* disable TUNE_FIL_RST */ |
117 | cx18_av_write4(cx, CXADEC_AFE_DIAG_CTRL3, v & 0xFFFFFFFE); | 119 | cx18_av_write4(cx, CXADEC_AFE_DIAG_CTRL3, v & 0xFFFFFFFE); |
118 | 120 | ||
119 | /* enable 656 output */ | 121 | /* enable 656 output */ |
120 | cx18_av_and_or4(cx, CXADEC_PIN_CTRL1, ~0, 0x040C00); | 122 | cx18_av_and_or4(cx, CXADEC_PIN_CTRL1, ~0, 0x040C00); |
121 | 123 | ||
122 | /* video output drive strength */ | 124 | /* video output drive strength */ |
123 | cx18_av_and_or4(cx, CXADEC_PIN_CTRL2, ~0, 0x2); | 125 | cx18_av_and_or4(cx, CXADEC_PIN_CTRL2, ~0, 0x2); |
124 | 126 | ||
125 | /* reset video */ | 127 | /* reset video */ |
126 | cx18_av_write4(cx, CXADEC_SOFT_RST_CTRL, 0x8000); | 128 | cx18_av_write4(cx, CXADEC_SOFT_RST_CTRL, 0x8000); |
127 | cx18_av_write4(cx, CXADEC_SOFT_RST_CTRL, 0); | 129 | cx18_av_write4(cx, CXADEC_SOFT_RST_CTRL, 0); |
128 | 130 | ||
129 | /* set video to auto-detect */ | 131 | /* set video to auto-detect */ |
130 | /* Clear bits 11-12 to enable slow locking mode. Set autodetect mode */ | 132 | /* Clear bits 11-12 to enable slow locking mode. Set autodetect mode */ |
131 | /* set the comb notch = 1 */ | 133 | /* set the comb notch = 1 */ |
132 | cx18_av_and_or4(cx, CXADEC_MODE_CTRL, 0xFFF7E7F0, 0x02040800); | 134 | cx18_av_and_or4(cx, CXADEC_MODE_CTRL, 0xFFF7E7F0, 0x02040800); |
133 | 135 | ||
134 | /* Enable wtw_en in CRUSH_CTRL (Set bit 22) */ | 136 | /* Enable wtw_en in CRUSH_CTRL (Set bit 22) */ |
135 | /* Enable maj_sel in CRUSH_CTRL (Set bit 20) */ | 137 | /* Enable maj_sel in CRUSH_CTRL (Set bit 20) */ |
136 | cx18_av_and_or4(cx, CXADEC_CRUSH_CTRL, ~0, 0x00500000); | 138 | cx18_av_and_or4(cx, CXADEC_CRUSH_CTRL, ~0, 0x00500000); |
137 | 139 | ||
138 | /* Set VGA_TRACK_RANGE to 0x20 */ | 140 | /* Set VGA_TRACK_RANGE to 0x20 */ |
139 | cx18_av_and_or4(cx, CXADEC_DFE_CTRL2, 0xFFFF00FF, 0x00002000); | 141 | cx18_av_and_or4(cx, CXADEC_DFE_CTRL2, 0xFFFF00FF, 0x00002000); |
140 | 142 | ||
141 | /* Enable VBI capture */ | 143 | /* Enable VBI capture */ |
142 | cx18_av_write4(cx, CXADEC_OUT_CTRL1, 0x4010253F); | 144 | cx18_av_write4(cx, CXADEC_OUT_CTRL1, 0x4010253F); |
143 | /* cx18_av_write4(cx, CXADEC_OUT_CTRL1, 0x4010253E); */ | 145 | /* cx18_av_write4(cx, CXADEC_OUT_CTRL1, 0x4010253E); */ |
144 | 146 | ||
145 | /* Set the video input. | 147 | /* Set the video input. |
146 | The setting in MODE_CTRL gets lost when we do the above setup */ | 148 | The setting in MODE_CTRL gets lost when we do the above setup */ |
147 | /* EncSetSignalStd(dwDevNum, pEnc->dwSigStd); */ | 149 | /* EncSetSignalStd(dwDevNum, pEnc->dwSigStd); */ |
148 | /* EncSetVideoInput(dwDevNum, pEnc->VidIndSelection); */ | 150 | /* EncSetVideoInput(dwDevNum, pEnc->VidIndSelection); */ |
149 | 151 | ||
150 | v = cx18_av_read4(cx, CXADEC_AFE_CTRL); | 152 | v = cx18_av_read4(cx, CXADEC_AFE_CTRL); |
151 | v &= 0xFFFBFFFF; /* turn OFF bit 18 for droop_comp_ch1 */ | 153 | v &= 0xFFFBFFFF; /* turn OFF bit 18 for droop_comp_ch1 */ |
152 | v &= 0xFFFF7FFF; /* turn OFF bit 9 for clamp_sel_ch1 */ | 154 | v &= 0xFFFF7FFF; /* turn OFF bit 9 for clamp_sel_ch1 */ |
153 | v &= 0xFFFFFFFE; /* turn OFF bit 0 for 12db_ch1 */ | 155 | v &= 0xFFFFFFFE; /* turn OFF bit 0 for 12db_ch1 */ |
154 | /* v |= 0x00000001;*/ /* turn ON bit 0 for 12db_ch1 */ | 156 | /* v |= 0x00000001;*/ /* turn ON bit 0 for 12db_ch1 */ |
155 | cx18_av_write4(cx, CXADEC_AFE_CTRL, v); | 157 | cx18_av_write4(cx, CXADEC_AFE_CTRL, v); |
156 | 158 | ||
157 | /* if(dwEnable && dw3DCombAvailable) { */ | 159 | /* if(dwEnable && dw3DCombAvailable) { */ |
158 | /* CxDevWrReg(CXADEC_SRC_COMB_CFG, 0x7728021F); */ | 160 | /* CxDevWrReg(CXADEC_SRC_COMB_CFG, 0x7728021F); */ |
159 | /* } else { */ | 161 | /* } else { */ |
160 | /* CxDevWrReg(CXADEC_SRC_COMB_CFG, 0x6628021F); */ | 162 | /* CxDevWrReg(CXADEC_SRC_COMB_CFG, 0x6628021F); */ |
161 | /* } */ | 163 | /* } */ |
162 | cx18_av_write4(cx, CXADEC_SRC_COMB_CFG, 0x6628021F); | 164 | cx18_av_write4(cx, CXADEC_SRC_COMB_CFG, 0x6628021F); |
163 | state->default_volume = 228 - cx18_av_read(cx, 0x8d4); | 165 | state->default_volume = 228 - cx18_av_read(cx, 0x8d4); |
164 | state->default_volume = ((state->default_volume / 2) + 23) << 9; | 166 | state->default_volume = ((state->default_volume / 2) + 23) << 9; |
165 | } | 167 | } |
166 | 168 | ||
167 | /* ----------------------------------------------------------------------- */ | 169 | /* ----------------------------------------------------------------------- */ |
168 | 170 | ||
169 | void cx18_av_std_setup(struct cx18 *cx) | 171 | void cx18_av_std_setup(struct cx18 *cx) |
170 | { | 172 | { |
171 | struct cx18_av_state *state = &cx->av_state; | 173 | struct cx18_av_state *state = &cx->av_state; |
172 | v4l2_std_id std = state->std; | 174 | v4l2_std_id std = state->std; |
173 | int hblank, hactive, burst, vblank, vactive, sc; | 175 | int hblank, hactive, burst, vblank, vactive, sc; |
174 | int vblank656, src_decimation; | 176 | int vblank656, src_decimation; |
175 | int luma_lpf, uv_lpf, comb; | 177 | int luma_lpf, uv_lpf, comb; |
176 | u32 pll_int, pll_frac, pll_post; | 178 | u32 pll_int, pll_frac, pll_post; |
177 | 179 | ||
178 | /* datasheet startup, step 8d */ | 180 | /* datasheet startup, step 8d */ |
179 | if (std & ~V4L2_STD_NTSC) | 181 | if (std & ~V4L2_STD_NTSC) |
180 | cx18_av_write(cx, 0x49f, 0x11); | 182 | cx18_av_write(cx, 0x49f, 0x11); |
181 | else | 183 | else |
182 | cx18_av_write(cx, 0x49f, 0x14); | 184 | cx18_av_write(cx, 0x49f, 0x14); |
183 | 185 | ||
184 | if (std & V4L2_STD_625_50) { | 186 | if (std & V4L2_STD_625_50) { |
185 | hblank = 132; | 187 | hblank = 132; |
186 | hactive = 720; | 188 | hactive = 720; |
187 | burst = 93; | 189 | burst = 93; |
188 | vblank = 36; | 190 | vblank = 36; |
189 | vactive = 580; | 191 | vactive = 580; |
190 | vblank656 = 40; | 192 | vblank656 = 40; |
191 | src_decimation = 0x21f; | 193 | src_decimation = 0x21f; |
192 | 194 | ||
193 | luma_lpf = 2; | 195 | luma_lpf = 2; |
194 | if (std & V4L2_STD_PAL) { | 196 | if (std & V4L2_STD_PAL) { |
195 | uv_lpf = 1; | 197 | uv_lpf = 1; |
196 | comb = 0x20; | 198 | comb = 0x20; |
197 | sc = 688739; | 199 | sc = 688739; |
198 | } else if (std == V4L2_STD_PAL_Nc) { | 200 | } else if (std == V4L2_STD_PAL_Nc) { |
199 | uv_lpf = 1; | 201 | uv_lpf = 1; |
200 | comb = 0x20; | 202 | comb = 0x20; |
201 | sc = 556453; | 203 | sc = 556453; |
202 | } else { /* SECAM */ | 204 | } else { /* SECAM */ |
203 | uv_lpf = 0; | 205 | uv_lpf = 0; |
204 | comb = 0; | 206 | comb = 0; |
205 | sc = 672351; | 207 | sc = 672351; |
206 | } | 208 | } |
207 | } else { | 209 | } else { |
208 | hactive = 720; | 210 | hactive = 720; |
209 | hblank = 122; | 211 | hblank = 122; |
210 | vactive = 487; | 212 | vactive = 487; |
211 | luma_lpf = 1; | 213 | luma_lpf = 1; |
212 | uv_lpf = 1; | 214 | uv_lpf = 1; |
213 | vblank = 26; | 215 | vblank = 26; |
214 | vblank656 = 26; | 216 | vblank656 = 26; |
215 | 217 | ||
216 | src_decimation = 0x21f; | 218 | src_decimation = 0x21f; |
217 | if (std == V4L2_STD_PAL_60) { | 219 | if (std == V4L2_STD_PAL_60) { |
218 | burst = 0x5b; | 220 | burst = 0x5b; |
219 | luma_lpf = 2; | 221 | luma_lpf = 2; |
220 | comb = 0x20; | 222 | comb = 0x20; |
221 | sc = 688739; | 223 | sc = 688739; |
222 | } else if (std == V4L2_STD_PAL_M) { | 224 | } else if (std == V4L2_STD_PAL_M) { |
223 | burst = 0x61; | 225 | burst = 0x61; |
224 | comb = 0x20; | 226 | comb = 0x20; |
225 | sc = 555452; | 227 | sc = 555452; |
226 | } else { | 228 | } else { |
227 | burst = 0x5b; | 229 | burst = 0x5b; |
228 | comb = 0x66; | 230 | comb = 0x66; |
229 | sc = 556063; | 231 | sc = 556063; |
230 | } | 232 | } |
231 | } | 233 | } |
232 | 234 | ||
233 | /* DEBUG: Displays configured PLL frequency */ | 235 | /* DEBUG: Displays configured PLL frequency */ |
234 | pll_int = cx18_av_read(cx, 0x108); | 236 | pll_int = cx18_av_read(cx, 0x108); |
235 | pll_frac = cx18_av_read4(cx, 0x10c) & 0x1ffffff; | 237 | pll_frac = cx18_av_read4(cx, 0x10c) & 0x1ffffff; |
236 | pll_post = cx18_av_read(cx, 0x109); | 238 | pll_post = cx18_av_read(cx, 0x109); |
237 | CX18_DEBUG_INFO("PLL regs = int: %u, frac: %u, post: %u\n", | 239 | CX18_DEBUG_INFO("PLL regs = int: %u, frac: %u, post: %u\n", |
238 | pll_int, pll_frac, pll_post); | 240 | pll_int, pll_frac, pll_post); |
239 | 241 | ||
240 | if (pll_post) { | 242 | if (pll_post) { |
241 | int fin, fsc; | 243 | int fin, fsc; |
242 | int pll = 28636363L * ((((u64)pll_int) << 25) + pll_frac); | 244 | int pll = 28636363L * ((((u64)pll_int) << 25) + pll_frac); |
243 | 245 | ||
244 | pll >>= 25; | 246 | pll >>= 25; |
245 | pll /= pll_post; | 247 | pll /= pll_post; |
246 | CX18_DEBUG_INFO("PLL = %d.%06d MHz\n", | 248 | CX18_DEBUG_INFO("PLL = %d.%06d MHz\n", |
247 | pll / 1000000, pll % 1000000); | 249 | pll / 1000000, pll % 1000000); |
248 | CX18_DEBUG_INFO("PLL/8 = %d.%06d MHz\n", | 250 | CX18_DEBUG_INFO("PLL/8 = %d.%06d MHz\n", |
249 | pll / 8000000, (pll / 8) % 1000000); | 251 | pll / 8000000, (pll / 8) % 1000000); |
250 | 252 | ||
251 | fin = ((u64)src_decimation * pll) >> 12; | 253 | fin = ((u64)src_decimation * pll) >> 12; |
252 | CX18_DEBUG_INFO("ADC Sampling freq = %d.%06d MHz\n", | 254 | CX18_DEBUG_INFO("ADC Sampling freq = %d.%06d MHz\n", |
253 | fin / 1000000, fin % 1000000); | 255 | fin / 1000000, fin % 1000000); |
254 | 256 | ||
255 | fsc = (((u64)sc) * pll) >> 24L; | 257 | fsc = (((u64)sc) * pll) >> 24L; |
256 | CX18_DEBUG_INFO("Chroma sub-carrier freq = %d.%06d MHz\n", | 258 | CX18_DEBUG_INFO("Chroma sub-carrier freq = %d.%06d MHz\n", |
257 | fsc / 1000000, fsc % 1000000); | 259 | fsc / 1000000, fsc % 1000000); |
258 | 260 | ||
259 | CX18_DEBUG_INFO("hblank %i, hactive %i, " | 261 | CX18_DEBUG_INFO("hblank %i, hactive %i, " |
260 | "vblank %i , vactive %i, vblank656 %i, src_dec %i," | 262 | "vblank %i , vactive %i, vblank656 %i, src_dec %i," |
261 | "burst 0x%02x, luma_lpf %i, uv_lpf %i, comb 0x%02x," | 263 | "burst 0x%02x, luma_lpf %i, uv_lpf %i, comb 0x%02x," |
262 | " sc 0x%06x\n", | 264 | " sc 0x%06x\n", |
263 | hblank, hactive, vblank, vactive, vblank656, | 265 | hblank, hactive, vblank, vactive, vblank656, |
264 | src_decimation, burst, luma_lpf, uv_lpf, comb, sc); | 266 | src_decimation, burst, luma_lpf, uv_lpf, comb, sc); |
265 | } | 267 | } |
266 | 268 | ||
267 | /* Sets horizontal blanking delay and active lines */ | 269 | /* Sets horizontal blanking delay and active lines */ |
268 | cx18_av_write(cx, 0x470, hblank); | 270 | cx18_av_write(cx, 0x470, hblank); |
269 | cx18_av_write(cx, 0x471, 0xff & (((hblank >> 8) & 0x3) | | 271 | cx18_av_write(cx, 0x471, 0xff & (((hblank >> 8) & 0x3) | |
270 | (hactive << 4))); | 272 | (hactive << 4))); |
271 | cx18_av_write(cx, 0x472, hactive >> 4); | 273 | cx18_av_write(cx, 0x472, hactive >> 4); |
272 | 274 | ||
273 | /* Sets burst gate delay */ | 275 | /* Sets burst gate delay */ |
274 | cx18_av_write(cx, 0x473, burst); | 276 | cx18_av_write(cx, 0x473, burst); |
275 | 277 | ||
276 | /* Sets vertical blanking delay and active duration */ | 278 | /* Sets vertical blanking delay and active duration */ |
277 | cx18_av_write(cx, 0x474, vblank); | 279 | cx18_av_write(cx, 0x474, vblank); |
278 | cx18_av_write(cx, 0x475, 0xff & (((vblank >> 8) & 0x3) | | 280 | cx18_av_write(cx, 0x475, 0xff & (((vblank >> 8) & 0x3) | |
279 | (vactive << 4))); | 281 | (vactive << 4))); |
280 | cx18_av_write(cx, 0x476, vactive >> 4); | 282 | cx18_av_write(cx, 0x476, vactive >> 4); |
281 | cx18_av_write(cx, 0x477, vblank656); | 283 | cx18_av_write(cx, 0x477, vblank656); |
282 | 284 | ||
283 | /* Sets src decimation rate */ | 285 | /* Sets src decimation rate */ |
284 | cx18_av_write(cx, 0x478, 0xff & src_decimation); | 286 | cx18_av_write(cx, 0x478, 0xff & src_decimation); |
285 | cx18_av_write(cx, 0x479, 0xff & (src_decimation >> 8)); | 287 | cx18_av_write(cx, 0x479, 0xff & (src_decimation >> 8)); |
286 | 288 | ||
287 | /* Sets Luma and UV Low pass filters */ | 289 | /* Sets Luma and UV Low pass filters */ |
288 | cx18_av_write(cx, 0x47a, luma_lpf << 6 | ((uv_lpf << 4) & 0x30)); | 290 | cx18_av_write(cx, 0x47a, luma_lpf << 6 | ((uv_lpf << 4) & 0x30)); |
289 | 291 | ||
290 | /* Enables comb filters */ | 292 | /* Enables comb filters */ |
291 | cx18_av_write(cx, 0x47b, comb); | 293 | cx18_av_write(cx, 0x47b, comb); |
292 | 294 | ||
293 | /* Sets SC Step*/ | 295 | /* Sets SC Step*/ |
294 | cx18_av_write(cx, 0x47c, sc); | 296 | cx18_av_write(cx, 0x47c, sc); |
295 | cx18_av_write(cx, 0x47d, 0xff & sc >> 8); | 297 | cx18_av_write(cx, 0x47d, 0xff & sc >> 8); |
296 | cx18_av_write(cx, 0x47e, 0xff & sc >> 16); | 298 | cx18_av_write(cx, 0x47e, 0xff & sc >> 16); |
297 | 299 | ||
298 | /* Sets VBI parameters */ | 300 | /* Sets VBI parameters */ |
299 | if (std & V4L2_STD_625_50) { | 301 | if (std & V4L2_STD_625_50) { |
300 | cx18_av_write(cx, 0x47f, 0x01); | 302 | cx18_av_write(cx, 0x47f, 0x01); |
301 | state->vbi_line_offset = 5; | 303 | state->vbi_line_offset = 5; |
302 | } else { | 304 | } else { |
303 | cx18_av_write(cx, 0x47f, 0x00); | 305 | cx18_av_write(cx, 0x47f, 0x00); |
304 | state->vbi_line_offset = 8; | 306 | state->vbi_line_offset = 8; |
305 | } | 307 | } |
306 | } | 308 | } |
307 | 309 | ||
308 | /* ----------------------------------------------------------------------- */ | 310 | /* ----------------------------------------------------------------------- */ |
309 | 311 | ||
310 | static void input_change(struct cx18 *cx) | 312 | static void input_change(struct cx18 *cx) |
311 | { | 313 | { |
312 | struct cx18_av_state *state = &cx->av_state; | 314 | struct cx18_av_state *state = &cx->av_state; |
313 | v4l2_std_id std = state->std; | 315 | v4l2_std_id std = state->std; |
314 | 316 | ||
315 | /* Follow step 8c and 8d of section 3.16 in the cx18_av datasheet */ | 317 | /* Follow step 8c and 8d of section 3.16 in the cx18_av datasheet */ |
316 | cx18_av_write(cx, 0x49f, (std & V4L2_STD_NTSC) ? 0x14 : 0x11); | 318 | cx18_av_write(cx, 0x49f, (std & V4L2_STD_NTSC) ? 0x14 : 0x11); |
317 | cx18_av_and_or(cx, 0x401, ~0x60, 0); | 319 | cx18_av_and_or(cx, 0x401, ~0x60, 0); |
318 | cx18_av_and_or(cx, 0x401, ~0x60, 0x60); | 320 | cx18_av_and_or(cx, 0x401, ~0x60, 0x60); |
319 | 321 | ||
320 | if (std & V4L2_STD_525_60) { | 322 | if (std & V4L2_STD_525_60) { |
321 | if (std == V4L2_STD_NTSC_M_JP) { | 323 | if (std == V4L2_STD_NTSC_M_JP) { |
322 | /* Japan uses EIAJ audio standard */ | 324 | /* Japan uses EIAJ audio standard */ |
323 | cx18_av_write(cx, 0x808, 0xf7); | 325 | cx18_av_write(cx, 0x808, 0xf7); |
324 | cx18_av_write(cx, 0x80b, 0x02); | 326 | cx18_av_write(cx, 0x80b, 0x02); |
325 | } else if (std == V4L2_STD_NTSC_M_KR) { | 327 | } else if (std == V4L2_STD_NTSC_M_KR) { |
326 | /* South Korea uses A2 audio standard */ | 328 | /* South Korea uses A2 audio standard */ |
327 | cx18_av_write(cx, 0x808, 0xf8); | 329 | cx18_av_write(cx, 0x808, 0xf8); |
328 | cx18_av_write(cx, 0x80b, 0x03); | 330 | cx18_av_write(cx, 0x80b, 0x03); |
329 | } else { | 331 | } else { |
330 | /* Others use the BTSC audio standard */ | 332 | /* Others use the BTSC audio standard */ |
331 | cx18_av_write(cx, 0x808, 0xf6); | 333 | cx18_av_write(cx, 0x808, 0xf6); |
332 | cx18_av_write(cx, 0x80b, 0x01); | 334 | cx18_av_write(cx, 0x80b, 0x01); |
333 | } | 335 | } |
334 | } else if (std & V4L2_STD_PAL) { | 336 | } else if (std & V4L2_STD_PAL) { |
335 | /* Follow tuner change procedure for PAL */ | 337 | /* Follow tuner change procedure for PAL */ |
336 | cx18_av_write(cx, 0x808, 0xff); | 338 | cx18_av_write(cx, 0x808, 0xff); |
337 | cx18_av_write(cx, 0x80b, 0x03); | 339 | cx18_av_write(cx, 0x80b, 0x03); |
338 | } else if (std & V4L2_STD_SECAM) { | 340 | } else if (std & V4L2_STD_SECAM) { |
339 | /* Select autodetect for SECAM */ | 341 | /* Select autodetect for SECAM */ |
340 | cx18_av_write(cx, 0x808, 0xff); | 342 | cx18_av_write(cx, 0x808, 0xff); |
341 | cx18_av_write(cx, 0x80b, 0x03); | 343 | cx18_av_write(cx, 0x80b, 0x03); |
342 | } | 344 | } |
343 | 345 | ||
344 | if (cx18_av_read(cx, 0x803) & 0x10) { | 346 | if (cx18_av_read(cx, 0x803) & 0x10) { |
345 | /* restart audio decoder microcontroller */ | 347 | /* restart audio decoder microcontroller */ |
346 | cx18_av_and_or(cx, 0x803, ~0x10, 0x00); | 348 | cx18_av_and_or(cx, 0x803, ~0x10, 0x00); |
347 | cx18_av_and_or(cx, 0x803, ~0x10, 0x10); | 349 | cx18_av_and_or(cx, 0x803, ~0x10, 0x10); |
348 | } | 350 | } |
349 | } | 351 | } |
350 | 352 | ||
351 | static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input, | 353 | static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input, |
352 | enum cx18_av_audio_input aud_input) | 354 | enum cx18_av_audio_input aud_input) |
353 | { | 355 | { |
354 | struct cx18_av_state *state = &cx->av_state; | 356 | struct cx18_av_state *state = &cx->av_state; |
355 | u8 is_composite = (vid_input >= CX18_AV_COMPOSITE1 && | 357 | u8 is_composite = (vid_input >= CX18_AV_COMPOSITE1 && |
356 | vid_input <= CX18_AV_COMPOSITE8); | 358 | vid_input <= CX18_AV_COMPOSITE8); |
357 | u8 reg; | 359 | u8 reg; |
358 | 360 | ||
359 | CX18_DEBUG_INFO("decoder set video input %d, audio input %d\n", | 361 | CX18_DEBUG_INFO("decoder set video input %d, audio input %d\n", |
360 | vid_input, aud_input); | 362 | vid_input, aud_input); |
361 | 363 | ||
362 | if (is_composite) { | 364 | if (is_composite) { |
363 | reg = 0xf0 + (vid_input - CX18_AV_COMPOSITE1); | 365 | reg = 0xf0 + (vid_input - CX18_AV_COMPOSITE1); |
364 | } else { | 366 | } else { |
365 | int luma = vid_input & 0xf0; | 367 | int luma = vid_input & 0xf0; |
366 | int chroma = vid_input & 0xf00; | 368 | int chroma = vid_input & 0xf00; |
367 | 369 | ||
368 | if ((vid_input & ~0xff0) || | 370 | if ((vid_input & ~0xff0) || |
369 | luma < CX18_AV_SVIDEO_LUMA1 || | 371 | luma < CX18_AV_SVIDEO_LUMA1 || |
370 | luma > CX18_AV_SVIDEO_LUMA8 || | 372 | luma > CX18_AV_SVIDEO_LUMA8 || |
371 | chroma < CX18_AV_SVIDEO_CHROMA4 || | 373 | chroma < CX18_AV_SVIDEO_CHROMA4 || |
372 | chroma > CX18_AV_SVIDEO_CHROMA8) { | 374 | chroma > CX18_AV_SVIDEO_CHROMA8) { |
373 | CX18_ERR("0x%04x is not a valid video input!\n", | 375 | CX18_ERR("0x%04x is not a valid video input!\n", |
374 | vid_input); | 376 | vid_input); |
375 | return -EINVAL; | 377 | return -EINVAL; |
376 | } | 378 | } |
377 | reg = 0xf0 + ((luma - CX18_AV_SVIDEO_LUMA1) >> 4); | 379 | reg = 0xf0 + ((luma - CX18_AV_SVIDEO_LUMA1) >> 4); |
378 | if (chroma >= CX18_AV_SVIDEO_CHROMA7) { | 380 | if (chroma >= CX18_AV_SVIDEO_CHROMA7) { |
379 | reg &= 0x3f; | 381 | reg &= 0x3f; |
380 | reg |= (chroma - CX18_AV_SVIDEO_CHROMA7) >> 2; | 382 | reg |= (chroma - CX18_AV_SVIDEO_CHROMA7) >> 2; |
381 | } else { | 383 | } else { |
382 | reg &= 0xcf; | 384 | reg &= 0xcf; |
383 | reg |= (chroma - CX18_AV_SVIDEO_CHROMA4) >> 4; | 385 | reg |= (chroma - CX18_AV_SVIDEO_CHROMA4) >> 4; |
384 | } | 386 | } |
385 | } | 387 | } |
386 | 388 | ||
387 | switch (aud_input) { | 389 | switch (aud_input) { |
388 | case CX18_AV_AUDIO_SERIAL1: | 390 | case CX18_AV_AUDIO_SERIAL1: |
389 | case CX18_AV_AUDIO_SERIAL2: | 391 | case CX18_AV_AUDIO_SERIAL2: |
390 | /* do nothing, use serial audio input */ | 392 | /* do nothing, use serial audio input */ |
391 | break; | 393 | break; |
392 | case CX18_AV_AUDIO4: reg &= ~0x30; break; | 394 | case CX18_AV_AUDIO4: reg &= ~0x30; break; |
393 | case CX18_AV_AUDIO5: reg &= ~0x30; reg |= 0x10; break; | 395 | case CX18_AV_AUDIO5: reg &= ~0x30; reg |= 0x10; break; |
394 | case CX18_AV_AUDIO6: reg &= ~0x30; reg |= 0x20; break; | 396 | case CX18_AV_AUDIO6: reg &= ~0x30; reg |= 0x20; break; |
395 | case CX18_AV_AUDIO7: reg &= ~0xc0; break; | 397 | case CX18_AV_AUDIO7: reg &= ~0xc0; break; |
396 | case CX18_AV_AUDIO8: reg &= ~0xc0; reg |= 0x40; break; | 398 | case CX18_AV_AUDIO8: reg &= ~0xc0; reg |= 0x40; break; |
397 | 399 | ||
398 | default: | 400 | default: |
399 | CX18_ERR("0x%04x is not a valid audio input!\n", aud_input); | 401 | CX18_ERR("0x%04x is not a valid audio input!\n", aud_input); |
400 | return -EINVAL; | 402 | return -EINVAL; |
401 | } | 403 | } |
402 | 404 | ||
403 | cx18_av_write(cx, 0x103, reg); | 405 | cx18_av_write(cx, 0x103, reg); |
404 | /* Set INPUT_MODE to Composite (0) or S-Video (1) */ | 406 | /* Set INPUT_MODE to Composite (0) or S-Video (1) */ |
405 | cx18_av_and_or(cx, 0x401, ~0x6, is_composite ? 0 : 0x02); | 407 | cx18_av_and_or(cx, 0x401, ~0x6, is_composite ? 0 : 0x02); |
406 | /* Set CH_SEL_ADC2 to 1 if input comes from CH3 */ | 408 | /* Set CH_SEL_ADC2 to 1 if input comes from CH3 */ |
407 | cx18_av_and_or(cx, 0x102, ~0x2, (reg & 0x80) == 0 ? 2 : 0); | 409 | cx18_av_and_or(cx, 0x102, ~0x2, (reg & 0x80) == 0 ? 2 : 0); |
408 | /* Set DUAL_MODE_ADC2 to 1 if input comes from both CH2 and CH3 */ | 410 | /* Set DUAL_MODE_ADC2 to 1 if input comes from both CH2 and CH3 */ |
409 | if ((reg & 0xc0) != 0xc0 && (reg & 0x30) != 0x30) | 411 | if ((reg & 0xc0) != 0xc0 && (reg & 0x30) != 0x30) |
410 | cx18_av_and_or(cx, 0x102, ~0x4, 4); | 412 | cx18_av_and_or(cx, 0x102, ~0x4, 4); |
411 | else | 413 | else |
412 | cx18_av_and_or(cx, 0x102, ~0x4, 0); | 414 | cx18_av_and_or(cx, 0x102, ~0x4, 0); |
413 | /*cx18_av_and_or4(cx, 0x104, ~0x001b4180, 0x00004180);*/ | 415 | /*cx18_av_and_or4(cx, 0x104, ~0x001b4180, 0x00004180);*/ |
414 | 416 | ||
415 | state->vid_input = vid_input; | 417 | state->vid_input = vid_input; |
416 | state->aud_input = aud_input; | 418 | state->aud_input = aud_input; |
417 | cx18_av_audio_set_path(cx); | 419 | cx18_av_audio_set_path(cx); |
418 | input_change(cx); | 420 | input_change(cx); |
419 | return 0; | 421 | return 0; |
420 | } | 422 | } |
421 | 423 | ||
422 | /* ----------------------------------------------------------------------- */ | 424 | /* ----------------------------------------------------------------------- */ |
423 | 425 | ||
424 | static int set_v4lstd(struct cx18 *cx) | 426 | static int set_v4lstd(struct cx18 *cx) |
425 | { | 427 | { |
426 | struct cx18_av_state *state = &cx->av_state; | 428 | struct cx18_av_state *state = &cx->av_state; |
427 | u8 fmt = 0; /* zero is autodetect */ | 429 | u8 fmt = 0; /* zero is autodetect */ |
428 | u8 pal_m = 0; | 430 | u8 pal_m = 0; |
429 | 431 | ||
430 | /* First tests should be against specific std */ | 432 | /* First tests should be against specific std */ |
431 | if (state->std == V4L2_STD_NTSC_M_JP) { | 433 | if (state->std == V4L2_STD_NTSC_M_JP) { |
432 | fmt = 0x2; | 434 | fmt = 0x2; |
433 | } else if (state->std == V4L2_STD_NTSC_443) { | 435 | } else if (state->std == V4L2_STD_NTSC_443) { |
434 | fmt = 0x3; | 436 | fmt = 0x3; |
435 | } else if (state->std == V4L2_STD_PAL_M) { | 437 | } else if (state->std == V4L2_STD_PAL_M) { |
436 | pal_m = 1; | 438 | pal_m = 1; |
437 | fmt = 0x5; | 439 | fmt = 0x5; |
438 | } else if (state->std == V4L2_STD_PAL_N) { | 440 | } else if (state->std == V4L2_STD_PAL_N) { |
439 | fmt = 0x6; | 441 | fmt = 0x6; |
440 | } else if (state->std == V4L2_STD_PAL_Nc) { | 442 | } else if (state->std == V4L2_STD_PAL_Nc) { |
441 | fmt = 0x7; | 443 | fmt = 0x7; |
442 | } else if (state->std == V4L2_STD_PAL_60) { | 444 | } else if (state->std == V4L2_STD_PAL_60) { |
443 | fmt = 0x8; | 445 | fmt = 0x8; |
444 | } else { | 446 | } else { |
445 | /* Then, test against generic ones */ | 447 | /* Then, test against generic ones */ |
446 | if (state->std & V4L2_STD_NTSC) | 448 | if (state->std & V4L2_STD_NTSC) |
447 | fmt = 0x1; | 449 | fmt = 0x1; |
448 | else if (state->std & V4L2_STD_PAL) | 450 | else if (state->std & V4L2_STD_PAL) |
449 | fmt = 0x4; | 451 | fmt = 0x4; |
450 | else if (state->std & V4L2_STD_SECAM) | 452 | else if (state->std & V4L2_STD_SECAM) |
451 | fmt = 0xc; | 453 | fmt = 0xc; |
452 | } | 454 | } |
453 | 455 | ||
454 | CX18_DEBUG_INFO("changing video std to fmt %i\n", fmt); | 456 | CX18_DEBUG_INFO("changing video std to fmt %i\n", fmt); |
455 | 457 | ||
456 | /* Follow step 9 of section 3.16 in the cx18_av datasheet. | 458 | /* Follow step 9 of section 3.16 in the cx18_av datasheet. |
457 | Without this PAL may display a vertical ghosting effect. | 459 | Without this PAL may display a vertical ghosting effect. |
458 | This happens for example with the Yuan MPC622. */ | 460 | This happens for example with the Yuan MPC622. */ |
459 | if (fmt >= 4 && fmt < 8) { | 461 | if (fmt >= 4 && fmt < 8) { |
460 | /* Set format to NTSC-M */ | 462 | /* Set format to NTSC-M */ |
461 | cx18_av_and_or(cx, 0x400, ~0xf, 1); | 463 | cx18_av_and_or(cx, 0x400, ~0xf, 1); |
462 | /* Turn off LCOMB */ | 464 | /* Turn off LCOMB */ |
463 | cx18_av_and_or(cx, 0x47b, ~6, 0); | 465 | cx18_av_and_or(cx, 0x47b, ~6, 0); |
464 | } | 466 | } |
465 | cx18_av_and_or(cx, 0x400, ~0x2f, fmt | 0x20); | 467 | cx18_av_and_or(cx, 0x400, ~0x2f, fmt | 0x20); |
466 | cx18_av_and_or(cx, 0x403, ~0x3, pal_m); | 468 | cx18_av_and_or(cx, 0x403, ~0x3, pal_m); |
467 | cx18_av_std_setup(cx); | 469 | cx18_av_std_setup(cx); |
468 | input_change(cx); | 470 | input_change(cx); |
469 | return 0; | 471 | return 0; |
470 | } | 472 | } |
471 | 473 | ||
472 | /* ----------------------------------------------------------------------- */ | 474 | /* ----------------------------------------------------------------------- */ |
473 | 475 | ||
474 | static int set_v4lctrl(struct cx18 *cx, struct v4l2_control *ctrl) | 476 | static int set_v4lctrl(struct cx18 *cx, struct v4l2_control *ctrl) |
475 | { | 477 | { |
476 | switch (ctrl->id) { | 478 | switch (ctrl->id) { |
477 | case V4L2_CID_BRIGHTNESS: | 479 | case V4L2_CID_BRIGHTNESS: |
478 | if (ctrl->value < 0 || ctrl->value > 255) { | 480 | if (ctrl->value < 0 || ctrl->value > 255) { |
479 | CX18_ERR("invalid brightness setting %d\n", | 481 | CX18_ERR("invalid brightness setting %d\n", |
480 | ctrl->value); | 482 | ctrl->value); |
481 | return -ERANGE; | 483 | return -ERANGE; |
482 | } | 484 | } |
483 | 485 | ||
484 | cx18_av_write(cx, 0x414, ctrl->value - 128); | 486 | cx18_av_write(cx, 0x414, ctrl->value - 128); |
485 | break; | 487 | break; |
486 | 488 | ||
487 | case V4L2_CID_CONTRAST: | 489 | case V4L2_CID_CONTRAST: |
488 | if (ctrl->value < 0 || ctrl->value > 127) { | 490 | if (ctrl->value < 0 || ctrl->value > 127) { |
489 | CX18_ERR("invalid contrast setting %d\n", | 491 | CX18_ERR("invalid contrast setting %d\n", |
490 | ctrl->value); | 492 | ctrl->value); |
491 | return -ERANGE; | 493 | return -ERANGE; |
492 | } | 494 | } |
493 | 495 | ||
494 | cx18_av_write(cx, 0x415, ctrl->value << 1); | 496 | cx18_av_write(cx, 0x415, ctrl->value << 1); |
495 | break; | 497 | break; |
496 | 498 | ||
497 | case V4L2_CID_SATURATION: | 499 | case V4L2_CID_SATURATION: |
498 | if (ctrl->value < 0 || ctrl->value > 127) { | 500 | if (ctrl->value < 0 || ctrl->value > 127) { |
499 | CX18_ERR("invalid saturation setting %d\n", | 501 | CX18_ERR("invalid saturation setting %d\n", |
500 | ctrl->value); | 502 | ctrl->value); |
501 | return -ERANGE; | 503 | return -ERANGE; |
502 | } | 504 | } |
503 | 505 | ||
504 | cx18_av_write(cx, 0x420, ctrl->value << 1); | 506 | cx18_av_write(cx, 0x420, ctrl->value << 1); |
505 | cx18_av_write(cx, 0x421, ctrl->value << 1); | 507 | cx18_av_write(cx, 0x421, ctrl->value << 1); |
506 | break; | 508 | break; |
507 | 509 | ||
508 | case V4L2_CID_HUE: | 510 | case V4L2_CID_HUE: |
509 | if (ctrl->value < -127 || ctrl->value > 127) { | 511 | if (ctrl->value < -127 || ctrl->value > 127) { |
510 | CX18_ERR("invalid hue setting %d\n", ctrl->value); | 512 | CX18_ERR("invalid hue setting %d\n", ctrl->value); |
511 | return -ERANGE; | 513 | return -ERANGE; |
512 | } | 514 | } |
513 | 515 | ||
514 | cx18_av_write(cx, 0x422, ctrl->value); | 516 | cx18_av_write(cx, 0x422, ctrl->value); |
515 | break; | 517 | break; |
516 | 518 | ||
517 | case V4L2_CID_AUDIO_VOLUME: | 519 | case V4L2_CID_AUDIO_VOLUME: |
518 | case V4L2_CID_AUDIO_BASS: | 520 | case V4L2_CID_AUDIO_BASS: |
519 | case V4L2_CID_AUDIO_TREBLE: | 521 | case V4L2_CID_AUDIO_TREBLE: |
520 | case V4L2_CID_AUDIO_BALANCE: | 522 | case V4L2_CID_AUDIO_BALANCE: |
521 | case V4L2_CID_AUDIO_MUTE: | 523 | case V4L2_CID_AUDIO_MUTE: |
522 | return cx18_av_audio(cx, VIDIOC_S_CTRL, ctrl); | 524 | return cx18_av_audio(cx, VIDIOC_S_CTRL, ctrl); |
523 | 525 | ||
524 | default: | 526 | default: |
525 | return -EINVAL; | 527 | return -EINVAL; |
526 | } | 528 | } |
527 | 529 | ||
528 | return 0; | 530 | return 0; |
529 | } | 531 | } |
530 | 532 | ||
531 | static int get_v4lctrl(struct cx18 *cx, struct v4l2_control *ctrl) | 533 | static int get_v4lctrl(struct cx18 *cx, struct v4l2_control *ctrl) |
532 | { | 534 | { |
533 | switch (ctrl->id) { | 535 | switch (ctrl->id) { |
534 | case V4L2_CID_BRIGHTNESS: | 536 | case V4L2_CID_BRIGHTNESS: |
535 | ctrl->value = (s8)cx18_av_read(cx, 0x414) + 128; | 537 | ctrl->value = (s8)cx18_av_read(cx, 0x414) + 128; |
536 | break; | 538 | break; |
537 | case V4L2_CID_CONTRAST: | 539 | case V4L2_CID_CONTRAST: |
538 | ctrl->value = cx18_av_read(cx, 0x415) >> 1; | 540 | ctrl->value = cx18_av_read(cx, 0x415) >> 1; |
539 | break; | 541 | break; |
540 | case V4L2_CID_SATURATION: | 542 | case V4L2_CID_SATURATION: |
541 | ctrl->value = cx18_av_read(cx, 0x420) >> 1; | 543 | ctrl->value = cx18_av_read(cx, 0x420) >> 1; |
542 | break; | 544 | break; |
543 | case V4L2_CID_HUE: | 545 | case V4L2_CID_HUE: |
544 | ctrl->value = (s8)cx18_av_read(cx, 0x422); | 546 | ctrl->value = (s8)cx18_av_read(cx, 0x422); |
545 | break; | 547 | break; |
546 | case V4L2_CID_AUDIO_VOLUME: | 548 | case V4L2_CID_AUDIO_VOLUME: |
547 | case V4L2_CID_AUDIO_BASS: | 549 | case V4L2_CID_AUDIO_BASS: |
548 | case V4L2_CID_AUDIO_TREBLE: | 550 | case V4L2_CID_AUDIO_TREBLE: |
549 | case V4L2_CID_AUDIO_BALANCE: | 551 | case V4L2_CID_AUDIO_BALANCE: |
550 | case V4L2_CID_AUDIO_MUTE: | 552 | case V4L2_CID_AUDIO_MUTE: |
551 | return cx18_av_audio(cx, VIDIOC_G_CTRL, ctrl); | 553 | return cx18_av_audio(cx, VIDIOC_G_CTRL, ctrl); |
552 | default: | 554 | default: |
553 | return -EINVAL; | 555 | return -EINVAL; |
554 | } | 556 | } |
555 | 557 | ||
556 | return 0; | 558 | return 0; |
557 | } | 559 | } |
558 | 560 | ||
559 | /* ----------------------------------------------------------------------- */ | 561 | /* ----------------------------------------------------------------------- */ |
560 | 562 | ||
561 | static int get_v4lfmt(struct cx18 *cx, struct v4l2_format *fmt) | 563 | static int get_v4lfmt(struct cx18 *cx, struct v4l2_format *fmt) |
562 | { | 564 | { |
563 | switch (fmt->type) { | 565 | switch (fmt->type) { |
564 | case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: | 566 | case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: |
565 | return cx18_av_vbi(cx, VIDIOC_G_FMT, fmt); | 567 | return cx18_av_vbi(cx, VIDIOC_G_FMT, fmt); |
566 | default: | 568 | default: |
567 | return -EINVAL; | 569 | return -EINVAL; |
568 | } | 570 | } |
569 | 571 | ||
570 | return 0; | 572 | return 0; |
571 | } | 573 | } |
572 | 574 | ||
573 | static int set_v4lfmt(struct cx18 *cx, struct v4l2_format *fmt) | 575 | static int set_v4lfmt(struct cx18 *cx, struct v4l2_format *fmt) |
574 | { | 576 | { |
575 | struct cx18_av_state *state = &cx->av_state; | 577 | struct cx18_av_state *state = &cx->av_state; |
576 | struct v4l2_pix_format *pix; | 578 | struct v4l2_pix_format *pix; |
577 | int HSC, VSC, Vsrc, Hsrc, filter, Vlines; | 579 | int HSC, VSC, Vsrc, Hsrc, filter, Vlines; |
578 | int is_50Hz = !(state->std & V4L2_STD_525_60); | 580 | int is_50Hz = !(state->std & V4L2_STD_525_60); |
579 | 581 | ||
580 | switch (fmt->type) { | 582 | switch (fmt->type) { |
581 | case V4L2_BUF_TYPE_VIDEO_CAPTURE: | 583 | case V4L2_BUF_TYPE_VIDEO_CAPTURE: |
582 | pix = &(fmt->fmt.pix); | 584 | pix = &(fmt->fmt.pix); |
583 | 585 | ||
584 | Vsrc = (cx18_av_read(cx, 0x476) & 0x3f) << 4; | 586 | Vsrc = (cx18_av_read(cx, 0x476) & 0x3f) << 4; |
585 | Vsrc |= (cx18_av_read(cx, 0x475) & 0xf0) >> 4; | 587 | Vsrc |= (cx18_av_read(cx, 0x475) & 0xf0) >> 4; |
586 | 588 | ||
587 | Hsrc = (cx18_av_read(cx, 0x472) & 0x3f) << 4; | 589 | Hsrc = (cx18_av_read(cx, 0x472) & 0x3f) << 4; |
588 | Hsrc |= (cx18_av_read(cx, 0x471) & 0xf0) >> 4; | 590 | Hsrc |= (cx18_av_read(cx, 0x471) & 0xf0) >> 4; |
589 | 591 | ||
590 | Vlines = pix->height + (is_50Hz ? 4 : 7); | 592 | Vlines = pix->height + (is_50Hz ? 4 : 7); |
591 | 593 | ||
592 | if ((pix->width * 16 < Hsrc) || (Hsrc < pix->width) || | 594 | if ((pix->width * 16 < Hsrc) || (Hsrc < pix->width) || |
593 | (Vlines * 8 < Vsrc) || (Vsrc < Vlines)) { | 595 | (Vlines * 8 < Vsrc) || (Vsrc < Vlines)) { |
594 | CX18_ERR("%dx%d is not a valid size!\n", | 596 | CX18_ERR("%dx%d is not a valid size!\n", |
595 | pix->width, pix->height); | 597 | pix->width, pix->height); |
596 | return -ERANGE; | 598 | return -ERANGE; |
597 | } | 599 | } |
598 | 600 | ||
599 | HSC = (Hsrc * (1 << 20)) / pix->width - (1 << 20); | 601 | HSC = (Hsrc * (1 << 20)) / pix->width - (1 << 20); |
600 | VSC = (1 << 16) - (Vsrc * (1 << 9) / Vlines - (1 << 9)); | 602 | VSC = (1 << 16) - (Vsrc * (1 << 9) / Vlines - (1 << 9)); |
601 | VSC &= 0x1fff; | 603 | VSC &= 0x1fff; |
602 | 604 | ||
603 | if (pix->width >= 385) | 605 | if (pix->width >= 385) |
604 | filter = 0; | 606 | filter = 0; |
605 | else if (pix->width > 192) | 607 | else if (pix->width > 192) |
606 | filter = 1; | 608 | filter = 1; |
607 | else if (pix->width > 96) | 609 | else if (pix->width > 96) |
608 | filter = 2; | 610 | filter = 2; |
609 | else | 611 | else |
610 | filter = 3; | 612 | filter = 3; |
611 | 613 | ||
612 | CX18_DEBUG_INFO("decoder set size %dx%d -> scale %ux%u\n", | 614 | CX18_DEBUG_INFO("decoder set size %dx%d -> scale %ux%u\n", |
613 | pix->width, pix->height, HSC, VSC); | 615 | pix->width, pix->height, HSC, VSC); |
614 | 616 | ||
615 | /* HSCALE=HSC */ | 617 | /* HSCALE=HSC */ |
616 | cx18_av_write(cx, 0x418, HSC & 0xff); | 618 | cx18_av_write(cx, 0x418, HSC & 0xff); |
617 | cx18_av_write(cx, 0x419, (HSC >> 8) & 0xff); | 619 | cx18_av_write(cx, 0x419, (HSC >> 8) & 0xff); |
618 | cx18_av_write(cx, 0x41a, HSC >> 16); | 620 | cx18_av_write(cx, 0x41a, HSC >> 16); |
619 | /* VSCALE=VSC */ | 621 | /* VSCALE=VSC */ |
620 | cx18_av_write(cx, 0x41c, VSC & 0xff); | 622 | cx18_av_write(cx, 0x41c, VSC & 0xff); |
621 | cx18_av_write(cx, 0x41d, VSC >> 8); | 623 | cx18_av_write(cx, 0x41d, VSC >> 8); |
622 | /* VS_INTRLACE=1 VFILT=filter */ | 624 | /* VS_INTRLACE=1 VFILT=filter */ |
623 | cx18_av_write(cx, 0x41e, 0x8 | filter); | 625 | cx18_av_write(cx, 0x41e, 0x8 | filter); |
624 | break; | 626 | break; |
625 | 627 | ||
626 | case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: | 628 | case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: |
627 | return cx18_av_vbi(cx, VIDIOC_S_FMT, fmt); | 629 | return cx18_av_vbi(cx, VIDIOC_S_FMT, fmt); |
628 | 630 | ||
629 | case V4L2_BUF_TYPE_VBI_CAPTURE: | 631 | case V4L2_BUF_TYPE_VBI_CAPTURE: |
630 | return cx18_av_vbi(cx, VIDIOC_S_FMT, fmt); | 632 | return cx18_av_vbi(cx, VIDIOC_S_FMT, fmt); |
631 | 633 | ||
632 | default: | 634 | default: |
633 | return -EINVAL; | 635 | return -EINVAL; |
634 | } | 636 | } |
635 | 637 | ||
636 | return 0; | 638 | return 0; |
637 | } | 639 | } |
638 | 640 | ||
639 | /* ----------------------------------------------------------------------- */ | 641 | /* ----------------------------------------------------------------------- */ |
640 | 642 | ||
641 | int cx18_av_cmd(struct cx18 *cx, unsigned int cmd, void *arg) | 643 | int cx18_av_cmd(struct cx18 *cx, unsigned int cmd, void *arg) |
642 | { | 644 | { |
643 | struct cx18_av_state *state = &cx->av_state; | 645 | struct cx18_av_state *state = &cx->av_state; |
644 | struct v4l2_tuner *vt = arg; | 646 | struct v4l2_tuner *vt = arg; |
645 | struct v4l2_routing *route = arg; | 647 | struct v4l2_routing *route = arg; |
646 | 648 | ||
647 | /* ignore these commands */ | 649 | /* ignore these commands */ |
648 | switch (cmd) { | 650 | switch (cmd) { |
649 | case TUNER_SET_TYPE_ADDR: | 651 | case TUNER_SET_TYPE_ADDR: |
650 | return 0; | 652 | return 0; |
651 | } | 653 | } |
652 | 654 | ||
653 | if (!state->is_initialized) { | 655 | if (!state->is_initialized) { |
654 | CX18_DEBUG_INFO("cmd %08x triggered fw load\n", cmd); | 656 | CX18_DEBUG_INFO("cmd %08x triggered fw load\n", cmd); |
655 | /* initialize on first use */ | 657 | /* initialize on first use */ |
656 | state->is_initialized = 1; | 658 | state->is_initialized = 1; |
657 | cx18_av_initialize(cx); | 659 | cx18_av_initialize(cx); |
658 | } | 660 | } |
659 | 661 | ||
660 | switch (cmd) { | 662 | switch (cmd) { |
661 | case VIDIOC_INT_DECODE_VBI_LINE: | 663 | case VIDIOC_INT_DECODE_VBI_LINE: |
662 | return cx18_av_vbi(cx, cmd, arg); | 664 | return cx18_av_vbi(cx, cmd, arg); |
663 | 665 | ||
664 | case VIDIOC_INT_AUDIO_CLOCK_FREQ: | 666 | case VIDIOC_INT_AUDIO_CLOCK_FREQ: |
665 | return cx18_av_audio(cx, cmd, arg); | 667 | return cx18_av_audio(cx, cmd, arg); |
666 | 668 | ||
667 | case VIDIOC_STREAMON: | 669 | case VIDIOC_STREAMON: |
668 | CX18_DEBUG_INFO("enable output\n"); | 670 | CX18_DEBUG_INFO("enable output\n"); |
669 | cx18_av_write(cx, 0x115, 0x8c); | 671 | cx18_av_write(cx, 0x115, 0x8c); |
670 | cx18_av_write(cx, 0x116, 0x07); | 672 | cx18_av_write(cx, 0x116, 0x07); |
671 | break; | 673 | break; |
672 | 674 | ||
673 | case VIDIOC_STREAMOFF: | 675 | case VIDIOC_STREAMOFF: |
674 | CX18_DEBUG_INFO("disable output\n"); | 676 | CX18_DEBUG_INFO("disable output\n"); |
675 | cx18_av_write(cx, 0x115, 0x00); | 677 | cx18_av_write(cx, 0x115, 0x00); |
676 | cx18_av_write(cx, 0x116, 0x00); | 678 | cx18_av_write(cx, 0x116, 0x00); |
677 | break; | 679 | break; |
678 | 680 | ||
679 | case VIDIOC_LOG_STATUS: | 681 | case VIDIOC_LOG_STATUS: |
680 | log_video_status(cx); | 682 | log_video_status(cx); |
681 | log_audio_status(cx); | 683 | log_audio_status(cx); |
682 | break; | 684 | break; |
683 | 685 | ||
684 | case VIDIOC_G_CTRL: | 686 | case VIDIOC_G_CTRL: |
685 | return get_v4lctrl(cx, (struct v4l2_control *)arg); | 687 | return get_v4lctrl(cx, (struct v4l2_control *)arg); |
686 | 688 | ||
687 | case VIDIOC_S_CTRL: | 689 | case VIDIOC_S_CTRL: |
688 | return set_v4lctrl(cx, (struct v4l2_control *)arg); | 690 | return set_v4lctrl(cx, (struct v4l2_control *)arg); |
689 | 691 | ||
690 | case VIDIOC_QUERYCTRL: | 692 | case VIDIOC_QUERYCTRL: |
691 | { | 693 | { |
692 | struct v4l2_queryctrl *qc = arg; | 694 | struct v4l2_queryctrl *qc = arg; |
693 | 695 | ||
694 | switch (qc->id) { | 696 | switch (qc->id) { |
695 | case V4L2_CID_BRIGHTNESS: | 697 | case V4L2_CID_BRIGHTNESS: |
696 | case V4L2_CID_CONTRAST: | 698 | case V4L2_CID_CONTRAST: |
697 | case V4L2_CID_SATURATION: | 699 | case V4L2_CID_SATURATION: |
698 | case V4L2_CID_HUE: | 700 | case V4L2_CID_HUE: |
699 | return v4l2_ctrl_query_fill_std(qc); | 701 | return v4l2_ctrl_query_fill_std(qc); |
700 | default: | 702 | default: |
701 | break; | 703 | break; |
702 | } | 704 | } |
703 | 705 | ||
704 | switch (qc->id) { | 706 | switch (qc->id) { |
705 | case V4L2_CID_AUDIO_VOLUME: | 707 | case V4L2_CID_AUDIO_VOLUME: |
706 | return v4l2_ctrl_query_fill(qc, 0, 65535, | 708 | return v4l2_ctrl_query_fill(qc, 0, 65535, |
707 | 65535 / 100, state->default_volume); | 709 | 65535 / 100, state->default_volume); |
708 | case V4L2_CID_AUDIO_MUTE: | 710 | case V4L2_CID_AUDIO_MUTE: |
709 | case V4L2_CID_AUDIO_BALANCE: | 711 | case V4L2_CID_AUDIO_BALANCE: |
710 | case V4L2_CID_AUDIO_BASS: | 712 | case V4L2_CID_AUDIO_BASS: |
711 | case V4L2_CID_AUDIO_TREBLE: | 713 | case V4L2_CID_AUDIO_TREBLE: |
712 | return v4l2_ctrl_query_fill_std(qc); | 714 | return v4l2_ctrl_query_fill_std(qc); |
713 | default: | 715 | default: |
714 | return -EINVAL; | 716 | return -EINVAL; |
715 | } | 717 | } |
716 | return -EINVAL; | 718 | return -EINVAL; |
717 | } | 719 | } |
718 | 720 | ||
719 | case VIDIOC_G_STD: | 721 | case VIDIOC_G_STD: |
720 | *(v4l2_std_id *)arg = state->std; | 722 | *(v4l2_std_id *)arg = state->std; |
721 | break; | 723 | break; |
722 | 724 | ||
723 | case VIDIOC_S_STD: | 725 | case VIDIOC_S_STD: |
724 | if (state->radio == 0 && state->std == *(v4l2_std_id *)arg) | 726 | if (state->radio == 0 && state->std == *(v4l2_std_id *)arg) |
725 | return 0; | 727 | return 0; |
726 | state->radio = 0; | 728 | state->radio = 0; |
727 | state->std = *(v4l2_std_id *)arg; | 729 | state->std = *(v4l2_std_id *)arg; |
728 | return set_v4lstd(cx); | 730 | return set_v4lstd(cx); |
729 | 731 | ||
730 | case AUDC_SET_RADIO: | 732 | case AUDC_SET_RADIO: |
731 | state->radio = 1; | 733 | state->radio = 1; |
732 | break; | 734 | break; |
733 | 735 | ||
734 | case VIDIOC_INT_G_VIDEO_ROUTING: | 736 | case VIDIOC_INT_G_VIDEO_ROUTING: |
735 | route->input = state->vid_input; | 737 | route->input = state->vid_input; |
736 | route->output = 0; | 738 | route->output = 0; |
737 | break; | 739 | break; |
738 | 740 | ||
739 | case VIDIOC_INT_S_VIDEO_ROUTING: | 741 | case VIDIOC_INT_S_VIDEO_ROUTING: |
740 | return set_input(cx, route->input, state->aud_input); | 742 | return set_input(cx, route->input, state->aud_input); |
741 | 743 | ||
742 | case VIDIOC_INT_G_AUDIO_ROUTING: | 744 | case VIDIOC_INT_G_AUDIO_ROUTING: |
743 | route->input = state->aud_input; | 745 | route->input = state->aud_input; |
744 | route->output = 0; | 746 | route->output = 0; |
745 | break; | 747 | break; |
746 | 748 | ||
747 | case VIDIOC_INT_S_AUDIO_ROUTING: | 749 | case VIDIOC_INT_S_AUDIO_ROUTING: |
748 | return set_input(cx, state->vid_input, route->input); | 750 | return set_input(cx, state->vid_input, route->input); |
749 | 751 | ||
750 | case VIDIOC_S_FREQUENCY: | 752 | case VIDIOC_S_FREQUENCY: |
751 | input_change(cx); | 753 | input_change(cx); |
752 | break; | 754 | break; |
753 | 755 | ||
754 | case VIDIOC_G_TUNER: | 756 | case VIDIOC_G_TUNER: |
755 | { | 757 | { |
756 | u8 vpres = cx18_av_read(cx, 0x40e) & 0x20; | 758 | u8 vpres = cx18_av_read(cx, 0x40e) & 0x20; |
757 | u8 mode; | 759 | u8 mode; |
758 | int val = 0; | 760 | int val = 0; |
759 | 761 | ||
760 | if (state->radio) | 762 | if (state->radio) |
761 | break; | 763 | break; |
762 | 764 | ||
763 | vt->signal = vpres ? 0xffff : 0x0; | 765 | vt->signal = vpres ? 0xffff : 0x0; |
764 | 766 | ||
765 | vt->capability |= | 767 | vt->capability |= |
766 | V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | | 768 | V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | |
767 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; | 769 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; |
768 | 770 | ||
769 | mode = cx18_av_read(cx, 0x804); | 771 | mode = cx18_av_read(cx, 0x804); |
770 | 772 | ||
771 | /* get rxsubchans and audmode */ | 773 | /* get rxsubchans and audmode */ |
772 | if ((mode & 0xf) == 1) | 774 | if ((mode & 0xf) == 1) |
773 | val |= V4L2_TUNER_SUB_STEREO; | 775 | val |= V4L2_TUNER_SUB_STEREO; |
774 | else | 776 | else |
775 | val |= V4L2_TUNER_SUB_MONO; | 777 | val |= V4L2_TUNER_SUB_MONO; |
776 | 778 | ||
777 | if (mode == 2 || mode == 4) | 779 | if (mode == 2 || mode == 4) |
778 | val = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; | 780 | val = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; |
779 | 781 | ||
780 | if (mode & 0x10) | 782 | if (mode & 0x10) |
781 | val |= V4L2_TUNER_SUB_SAP; | 783 | val |= V4L2_TUNER_SUB_SAP; |
782 | 784 | ||
783 | vt->rxsubchans = val; | 785 | vt->rxsubchans = val; |
784 | vt->audmode = state->audmode; | 786 | vt->audmode = state->audmode; |
785 | break; | 787 | break; |
786 | } | 788 | } |
787 | 789 | ||
788 | case VIDIOC_S_TUNER: | 790 | case VIDIOC_S_TUNER: |
789 | if (state->radio) | 791 | if (state->radio) |
790 | break; | 792 | break; |
791 | 793 | ||
792 | switch (vt->audmode) { | 794 | switch (vt->audmode) { |
793 | case V4L2_TUNER_MODE_MONO: | 795 | case V4L2_TUNER_MODE_MONO: |
794 | /* mono -> mono | 796 | /* mono -> mono |
795 | stereo -> mono | 797 | stereo -> mono |
796 | bilingual -> lang1 */ | 798 | bilingual -> lang1 */ |
797 | cx18_av_and_or(cx, 0x809, ~0xf, 0x00); | 799 | cx18_av_and_or(cx, 0x809, ~0xf, 0x00); |
798 | break; | 800 | break; |
799 | case V4L2_TUNER_MODE_STEREO: | 801 | case V4L2_TUNER_MODE_STEREO: |
800 | case V4L2_TUNER_MODE_LANG1: | 802 | case V4L2_TUNER_MODE_LANG1: |
801 | /* mono -> mono | 803 | /* mono -> mono |
802 | stereo -> stereo | 804 | stereo -> stereo |
803 | bilingual -> lang1 */ | 805 | bilingual -> lang1 */ |
804 | cx18_av_and_or(cx, 0x809, ~0xf, 0x04); | 806 | cx18_av_and_or(cx, 0x809, ~0xf, 0x04); |
805 | break; | 807 | break; |
806 | case V4L2_TUNER_MODE_LANG1_LANG2: | 808 | case V4L2_TUNER_MODE_LANG1_LANG2: |
807 | /* mono -> mono | 809 | /* mono -> mono |
808 | stereo -> stereo | 810 | stereo -> stereo |
809 | bilingual -> lang1/lang2 */ | 811 | bilingual -> lang1/lang2 */ |
810 | cx18_av_and_or(cx, 0x809, ~0xf, 0x07); | 812 | cx18_av_and_or(cx, 0x809, ~0xf, 0x07); |
811 | break; | 813 | break; |
812 | case V4L2_TUNER_MODE_LANG2: | 814 | case V4L2_TUNER_MODE_LANG2: |
813 | /* mono -> mono | 815 | /* mono -> mono |
814 | stereo -> stereo | 816 | stereo -> stereo |
815 | bilingual -> lang2 */ | 817 | bilingual -> lang2 */ |
816 | cx18_av_and_or(cx, 0x809, ~0xf, 0x01); | 818 | cx18_av_and_or(cx, 0x809, ~0xf, 0x01); |
817 | break; | 819 | break; |
818 | default: | 820 | default: |
819 | return -EINVAL; | 821 | return -EINVAL; |
820 | } | 822 | } |
821 | state->audmode = vt->audmode; | 823 | state->audmode = vt->audmode; |
822 | break; | 824 | break; |
823 | 825 | ||
824 | case VIDIOC_G_FMT: | 826 | case VIDIOC_G_FMT: |
825 | return get_v4lfmt(cx, (struct v4l2_format *)arg); | 827 | return get_v4lfmt(cx, (struct v4l2_format *)arg); |
826 | 828 | ||
827 | case VIDIOC_S_FMT: | 829 | case VIDIOC_S_FMT: |
828 | return set_v4lfmt(cx, (struct v4l2_format *)arg); | 830 | return set_v4lfmt(cx, (struct v4l2_format *)arg); |
829 | 831 | ||
830 | case VIDIOC_INT_RESET: | 832 | case VIDIOC_INT_RESET: |
831 | cx18_av_initialize(cx); | 833 | cx18_av_initialize(cx); |
832 | break; | 834 | break; |
833 | 835 | ||
834 | default: | 836 | default: |
835 | return -EINVAL; | 837 | return -EINVAL; |
836 | } | 838 | } |
837 | 839 | ||
838 | return 0; | 840 | return 0; |
839 | } | 841 | } |
840 | 842 | ||
841 | /* ----------------------------------------------------------------------- */ | 843 | /* ----------------------------------------------------------------------- */ |
842 | 844 | ||
843 | /* ----------------------------------------------------------------------- */ | 845 | /* ----------------------------------------------------------------------- */ |
844 | 846 | ||
845 | static void log_video_status(struct cx18 *cx) | 847 | static void log_video_status(struct cx18 *cx) |
846 | { | 848 | { |
847 | static const char *const fmt_strs[] = { | 849 | static const char *const fmt_strs[] = { |
848 | "0x0", | 850 | "0x0", |
849 | "NTSC-M", "NTSC-J", "NTSC-4.43", | 851 | "NTSC-M", "NTSC-J", "NTSC-4.43", |
850 | "PAL-BDGHI", "PAL-M", "PAL-N", "PAL-Nc", "PAL-60", | 852 | "PAL-BDGHI", "PAL-M", "PAL-N", "PAL-Nc", "PAL-60", |
851 | "0x9", "0xA", "0xB", | 853 | "0x9", "0xA", "0xB", |
852 | "SECAM", | 854 | "SECAM", |
853 | "0xD", "0xE", "0xF" | 855 | "0xD", "0xE", "0xF" |
854 | }; | 856 | }; |
855 | 857 | ||
856 | struct cx18_av_state *state = &cx->av_state; | 858 | struct cx18_av_state *state = &cx->av_state; |
857 | u8 vidfmt_sel = cx18_av_read(cx, 0x400) & 0xf; | 859 | u8 vidfmt_sel = cx18_av_read(cx, 0x400) & 0xf; |
858 | u8 gen_stat1 = cx18_av_read(cx, 0x40d); | 860 | u8 gen_stat1 = cx18_av_read(cx, 0x40d); |
859 | u8 gen_stat2 = cx18_av_read(cx, 0x40e); | 861 | u8 gen_stat2 = cx18_av_read(cx, 0x40e); |
860 | int vid_input = state->vid_input; | 862 | int vid_input = state->vid_input; |
861 | 863 | ||
862 | CX18_INFO("Video signal: %spresent\n", | 864 | CX18_INFO("Video signal: %spresent\n", |
863 | (gen_stat2 & 0x20) ? "" : "not "); | 865 | (gen_stat2 & 0x20) ? "" : "not "); |
864 | CX18_INFO("Detected format: %s\n", | 866 | CX18_INFO("Detected format: %s\n", |
865 | fmt_strs[gen_stat1 & 0xf]); | 867 | fmt_strs[gen_stat1 & 0xf]); |
866 | 868 | ||
867 | CX18_INFO("Specified standard: %s\n", | 869 | CX18_INFO("Specified standard: %s\n", |
868 | vidfmt_sel ? fmt_strs[vidfmt_sel] : "automatic detection"); | 870 | vidfmt_sel ? fmt_strs[vidfmt_sel] : "automatic detection"); |
869 | 871 | ||
870 | if (vid_input >= CX18_AV_COMPOSITE1 && | 872 | if (vid_input >= CX18_AV_COMPOSITE1 && |
871 | vid_input <= CX18_AV_COMPOSITE8) { | 873 | vid_input <= CX18_AV_COMPOSITE8) { |
872 | CX18_INFO("Specified video input: Composite %d\n", | 874 | CX18_INFO("Specified video input: Composite %d\n", |
873 | vid_input - CX18_AV_COMPOSITE1 + 1); | 875 | vid_input - CX18_AV_COMPOSITE1 + 1); |
874 | } else { | 876 | } else { |
875 | CX18_INFO("Specified video input: S-Video (Luma In%d, Chroma In%d)\n", | 877 | CX18_INFO("Specified video input: S-Video (Luma In%d, Chroma In%d)\n", |
876 | (vid_input & 0xf0) >> 4, (vid_input & 0xf00) >> 8); | 878 | (vid_input & 0xf0) >> 4, (vid_input & 0xf00) >> 8); |
877 | } | 879 | } |
878 | 880 | ||
879 | CX18_INFO("Specified audioclock freq: %d Hz\n", state->audclk_freq); | 881 | CX18_INFO("Specified audioclock freq: %d Hz\n", state->audclk_freq); |
880 | } | 882 | } |
881 | 883 | ||
882 | /* ----------------------------------------------------------------------- */ | 884 | /* ----------------------------------------------------------------------- */ |
883 | 885 | ||
884 | static void log_audio_status(struct cx18 *cx) | 886 | static void log_audio_status(struct cx18 *cx) |
885 | { | 887 | { |
886 | struct cx18_av_state *state = &cx->av_state; | 888 | struct cx18_av_state *state = &cx->av_state; |
887 | u8 download_ctl = cx18_av_read(cx, 0x803); | 889 | u8 download_ctl = cx18_av_read(cx, 0x803); |
888 | u8 mod_det_stat0 = cx18_av_read(cx, 0x804); | 890 | u8 mod_det_stat0 = cx18_av_read(cx, 0x804); |
889 | u8 mod_det_stat1 = cx18_av_read(cx, 0x805); | 891 | u8 mod_det_stat1 = cx18_av_read(cx, 0x805); |
890 | u8 audio_config = cx18_av_read(cx, 0x808); | 892 | u8 audio_config = cx18_av_read(cx, 0x808); |
891 | u8 pref_mode = cx18_av_read(cx, 0x809); | 893 | u8 pref_mode = cx18_av_read(cx, 0x809); |
892 | u8 afc0 = cx18_av_read(cx, 0x80b); | 894 | u8 afc0 = cx18_av_read(cx, 0x80b); |
893 | u8 mute_ctl = cx18_av_read(cx, 0x8d3); | 895 | u8 mute_ctl = cx18_av_read(cx, 0x8d3); |
894 | int aud_input = state->aud_input; | 896 | int aud_input = state->aud_input; |
895 | char *p; | 897 | char *p; |
896 | 898 | ||
897 | switch (mod_det_stat0) { | 899 | switch (mod_det_stat0) { |
898 | case 0x00: p = "mono"; break; | 900 | case 0x00: p = "mono"; break; |
899 | case 0x01: p = "stereo"; break; | 901 | case 0x01: p = "stereo"; break; |
900 | case 0x02: p = "dual"; break; | 902 | case 0x02: p = "dual"; break; |
901 | case 0x04: p = "tri"; break; | 903 | case 0x04: p = "tri"; break; |
902 | case 0x10: p = "mono with SAP"; break; | 904 | case 0x10: p = "mono with SAP"; break; |
903 | case 0x11: p = "stereo with SAP"; break; | 905 | case 0x11: p = "stereo with SAP"; break; |
904 | case 0x12: p = "dual with SAP"; break; | 906 | case 0x12: p = "dual with SAP"; break; |
905 | case 0x14: p = "tri with SAP"; break; | 907 | case 0x14: p = "tri with SAP"; break; |
906 | case 0xfe: p = "forced mode"; break; | 908 | case 0xfe: p = "forced mode"; break; |
907 | default: p = "not defined"; break; | 909 | default: p = "not defined"; break; |
908 | } | 910 | } |
909 | CX18_INFO("Detected audio mode: %s\n", p); | 911 | CX18_INFO("Detected audio mode: %s\n", p); |
910 | 912 | ||
911 | switch (mod_det_stat1) { | 913 | switch (mod_det_stat1) { |
912 | case 0x00: p = "not defined"; break; | 914 | case 0x00: p = "not defined"; break; |
913 | case 0x01: p = "EIAJ"; break; | 915 | case 0x01: p = "EIAJ"; break; |
914 | case 0x02: p = "A2-M"; break; | 916 | case 0x02: p = "A2-M"; break; |
915 | case 0x03: p = "A2-BG"; break; | 917 | case 0x03: p = "A2-BG"; break; |
916 | case 0x04: p = "A2-DK1"; break; | 918 | case 0x04: p = "A2-DK1"; break; |
917 | case 0x05: p = "A2-DK2"; break; | 919 | case 0x05: p = "A2-DK2"; break; |
918 | case 0x06: p = "A2-DK3"; break; | 920 | case 0x06: p = "A2-DK3"; break; |
919 | case 0x07: p = "A1 (6.0 MHz FM Mono)"; break; | 921 | case 0x07: p = "A1 (6.0 MHz FM Mono)"; break; |
920 | case 0x08: p = "AM-L"; break; | 922 | case 0x08: p = "AM-L"; break; |
921 | case 0x09: p = "NICAM-BG"; break; | 923 | case 0x09: p = "NICAM-BG"; break; |
922 | case 0x0a: p = "NICAM-DK"; break; | 924 | case 0x0a: p = "NICAM-DK"; break; |
923 | case 0x0b: p = "NICAM-I"; break; | 925 | case 0x0b: p = "NICAM-I"; break; |
924 | case 0x0c: p = "NICAM-L"; break; | 926 | case 0x0c: p = "NICAM-L"; break; |
925 | case 0x0d: p = "BTSC/EIAJ/A2-M Mono (4.5 MHz FMMono)"; break; | 927 | case 0x0d: p = "BTSC/EIAJ/A2-M Mono (4.5 MHz FMMono)"; break; |
926 | case 0x0e: p = "IF FM Radio"; break; | 928 | case 0x0e: p = "IF FM Radio"; break; |
927 | case 0x0f: p = "BTSC"; break; | 929 | case 0x0f: p = "BTSC"; break; |
928 | case 0x10: p = "detected chrominance"; break; | 930 | case 0x10: p = "detected chrominance"; break; |
929 | case 0xfd: p = "unknown audio standard"; break; | 931 | case 0xfd: p = "unknown audio standard"; break; |
930 | case 0xfe: p = "forced audio standard"; break; | 932 | case 0xfe: p = "forced audio standard"; break; |
931 | case 0xff: p = "no detected audio standard"; break; | 933 | case 0xff: p = "no detected audio standard"; break; |
932 | default: p = "not defined"; break; | 934 | default: p = "not defined"; break; |
933 | } | 935 | } |
934 | CX18_INFO("Detected audio standard: %s\n", p); | 936 | CX18_INFO("Detected audio standard: %s\n", p); |
935 | CX18_INFO("Audio muted: %s\n", | 937 | CX18_INFO("Audio muted: %s\n", |
936 | (mute_ctl & 0x2) ? "yes" : "no"); | 938 | (mute_ctl & 0x2) ? "yes" : "no"); |
937 | CX18_INFO("Audio microcontroller: %s\n", | 939 | CX18_INFO("Audio microcontroller: %s\n", |
938 | (download_ctl & 0x10) ? "running" : "stopped"); | 940 | (download_ctl & 0x10) ? "running" : "stopped"); |
939 | 941 | ||
940 | switch (audio_config >> 4) { | 942 | switch (audio_config >> 4) { |
941 | case 0x00: p = "undefined"; break; | 943 | case 0x00: p = "undefined"; break; |
942 | case 0x01: p = "BTSC"; break; | 944 | case 0x01: p = "BTSC"; break; |
943 | case 0x02: p = "EIAJ"; break; | 945 | case 0x02: p = "EIAJ"; break; |
944 | case 0x03: p = "A2-M"; break; | 946 | case 0x03: p = "A2-M"; break; |
945 | case 0x04: p = "A2-BG"; break; | 947 | case 0x04: p = "A2-BG"; break; |
946 | case 0x05: p = "A2-DK1"; break; | 948 | case 0x05: p = "A2-DK1"; break; |
947 | case 0x06: p = "A2-DK2"; break; | 949 | case 0x06: p = "A2-DK2"; break; |
948 | case 0x07: p = "A2-DK3"; break; | 950 | case 0x07: p = "A2-DK3"; break; |
949 | case 0x08: p = "A1 (6.0 MHz FM Mono)"; break; | 951 | case 0x08: p = "A1 (6.0 MHz FM Mono)"; break; |
950 | case 0x09: p = "AM-L"; break; | 952 | case 0x09: p = "AM-L"; break; |
951 | case 0x0a: p = "NICAM-BG"; break; | 953 | case 0x0a: p = "NICAM-BG"; break; |
952 | case 0x0b: p = "NICAM-DK"; break; | 954 | case 0x0b: p = "NICAM-DK"; break; |
953 | case 0x0c: p = "NICAM-I"; break; | 955 | case 0x0c: p = "NICAM-I"; break; |
954 | case 0x0d: p = "NICAM-L"; break; | 956 | case 0x0d: p = "NICAM-L"; break; |
955 | case 0x0e: p = "FM radio"; break; | 957 | case 0x0e: p = "FM radio"; break; |
956 | case 0x0f: p = "automatic detection"; break; | 958 | case 0x0f: p = "automatic detection"; break; |
957 | default: p = "undefined"; break; | 959 | default: p = "undefined"; break; |
958 | } | 960 | } |
959 | CX18_INFO("Configured audio standard: %s\n", p); | 961 | CX18_INFO("Configured audio standard: %s\n", p); |
960 | 962 | ||
961 | if ((audio_config >> 4) < 0xF) { | 963 | if ((audio_config >> 4) < 0xF) { |
962 | switch (audio_config & 0xF) { | 964 | switch (audio_config & 0xF) { |
963 | case 0x00: p = "MONO1 (LANGUAGE A/Mono L+R channel for BTSC, EIAJ, A2)"; break; | 965 | case 0x00: p = "MONO1 (LANGUAGE A/Mono L+R channel for BTSC, EIAJ, A2)"; break; |
964 | case 0x01: p = "MONO2 (LANGUAGE B)"; break; | 966 | case 0x01: p = "MONO2 (LANGUAGE B)"; break; |
965 | case 0x02: p = "MONO3 (STEREO forced MONO)"; break; | 967 | case 0x02: p = "MONO3 (STEREO forced MONO)"; break; |
966 | case 0x03: p = "MONO4 (NICAM ANALOG-Language C/Analog Fallback)"; break; | 968 | case 0x03: p = "MONO4 (NICAM ANALOG-Language C/Analog Fallback)"; break; |
967 | case 0x04: p = "STEREO"; break; | 969 | case 0x04: p = "STEREO"; break; |
968 | case 0x05: p = "DUAL1 (AC)"; break; | 970 | case 0x05: p = "DUAL1 (AC)"; break; |
969 | case 0x06: p = "DUAL2 (BC)"; break; | 971 | case 0x06: p = "DUAL2 (BC)"; break; |
970 | case 0x07: p = "DUAL3 (AB)"; break; | 972 | case 0x07: p = "DUAL3 (AB)"; break; |
971 | default: p = "undefined"; | 973 | default: p = "undefined"; |
972 | } | 974 | } |
973 | CX18_INFO("Configured audio mode: %s\n", p); | 975 | CX18_INFO("Configured audio mode: %s\n", p); |
974 | } else { | 976 | } else { |
975 | switch (audio_config & 0xF) { | 977 | switch (audio_config & 0xF) { |
976 | case 0x00: p = "BG"; break; | 978 | case 0x00: p = "BG"; break; |
977 | case 0x01: p = "DK1"; break; | 979 | case 0x01: p = "DK1"; break; |
978 | case 0x02: p = "DK2"; break; | 980 | case 0x02: p = "DK2"; break; |
979 | case 0x03: p = "DK3"; break; | 981 | case 0x03: p = "DK3"; break; |
980 | case 0x04: p = "I"; break; | 982 | case 0x04: p = "I"; break; |
981 | case 0x05: p = "L"; break; | 983 | case 0x05: p = "L"; break; |
982 | case 0x06: p = "BTSC"; break; | 984 | case 0x06: p = "BTSC"; break; |
983 | case 0x07: p = "EIAJ"; break; | 985 | case 0x07: p = "EIAJ"; break; |
984 | case 0x08: p = "A2-M"; break; | 986 | case 0x08: p = "A2-M"; break; |
985 | case 0x09: p = "FM Radio (4.5 MHz)"; break; | 987 | case 0x09: p = "FM Radio (4.5 MHz)"; break; |
986 | case 0x0a: p = "FM Radio (5.5 MHz)"; break; | 988 | case 0x0a: p = "FM Radio (5.5 MHz)"; break; |
987 | case 0x0b: p = "S-Video"; break; | 989 | case 0x0b: p = "S-Video"; break; |
988 | case 0x0f: p = "automatic standard and mode detection"; break; | 990 | case 0x0f: p = "automatic standard and mode detection"; break; |
989 | default: p = "undefined"; break; | 991 | default: p = "undefined"; break; |
990 | } | 992 | } |
991 | CX18_INFO("Configured audio system: %s\n", p); | 993 | CX18_INFO("Configured audio system: %s\n", p); |
992 | } | 994 | } |
993 | 995 | ||
994 | if (aud_input) | 996 | if (aud_input) |
995 | CX18_INFO("Specified audio input: Tuner (In%d)\n", | 997 | CX18_INFO("Specified audio input: Tuner (In%d)\n", |
996 | aud_input); | 998 | aud_input); |
997 | else | 999 | else |
998 | CX18_INFO("Specified audio input: External\n"); | 1000 | CX18_INFO("Specified audio input: External\n"); |
999 | 1001 | ||
1000 | switch (pref_mode & 0xf) { | 1002 | switch (pref_mode & 0xf) { |
1001 | case 0: p = "mono/language A"; break; | 1003 | case 0: p = "mono/language A"; break; |
1002 | case 1: p = "language B"; break; | 1004 | case 1: p = "language B"; break; |
1003 | case 2: p = "language C"; break; | 1005 | case 2: p = "language C"; break; |
1004 | case 3: p = "analog fallback"; break; | 1006 | case 3: p = "analog fallback"; break; |
1005 | case 4: p = "stereo"; break; | 1007 | case 4: p = "stereo"; break; |
1006 | case 5: p = "language AC"; break; | 1008 | case 5: p = "language AC"; break; |
1007 | case 6: p = "language BC"; break; | 1009 | case 6: p = "language BC"; break; |
1008 | case 7: p = "language AB"; break; | 1010 | case 7: p = "language AB"; break; |
1009 | default: p = "undefined"; break; | 1011 | default: p = "undefined"; break; |
1010 | } | 1012 | } |
1011 | CX18_INFO("Preferred audio mode: %s\n", p); | 1013 | CX18_INFO("Preferred audio mode: %s\n", p); |
1012 | 1014 | ||
1013 | if ((audio_config & 0xf) == 0xf) { | 1015 | if ((audio_config & 0xf) == 0xf) { |
1014 | switch ((afc0 >> 3) & 0x1) { | 1016 | switch ((afc0 >> 3) & 0x1) { |
1015 | case 0: p = "system DK"; break; | 1017 | case 0: p = "system DK"; break; |
1016 | case 1: p = "system L"; break; | 1018 | case 1: p = "system L"; break; |
1017 | } | 1019 | } |
1018 | CX18_INFO("Selected 65 MHz format: %s\n", p); | 1020 | CX18_INFO("Selected 65 MHz format: %s\n", p); |
1019 | 1021 | ||
1020 | switch (afc0 & 0x7) { | 1022 | switch (afc0 & 0x7) { |
1021 | case 0: p = "Chroma"; break; | 1023 | case 0: p = "Chroma"; break; |
1022 | case 1: p = "BTSC"; break; | 1024 | case 1: p = "BTSC"; break; |
1023 | case 2: p = "EIAJ"; break; | 1025 | case 2: p = "EIAJ"; break; |
1024 | case 3: p = "A2-M"; break; | 1026 | case 3: p = "A2-M"; break; |
1025 | case 4: p = "autodetect"; break; | 1027 | case 4: p = "autodetect"; break; |
1026 | default: p = "undefined"; break; | 1028 | default: p = "undefined"; break; |
1027 | } | 1029 | } |
1028 | CX18_INFO("Selected 45 MHz format: %s\n", p); | 1030 | CX18_INFO("Selected 45 MHz format: %s\n", p); |
1029 | } | 1031 | } |
1030 | } | 1032 | } |
1031 | 1033 |
drivers/media/video/cx18/cx18-av-firmware.c
1 | /* | 1 | /* |
2 | * cx18 ADEC firmware functions | 2 | * cx18 ADEC firmware functions |
3 | * | 3 | * |
4 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | 4 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> |
5 | * | 5 | * |
6 | * This program is free software; you can redistribute it and/or | 6 | * This program is free software; you can redistribute it and/or |
7 | * modify it under the terms of the GNU General Public License | 7 | * modify it under the terms of the GNU General Public License |
8 | * as published by the Free Software Foundation; either version 2 | 8 | * as published by the Free Software Foundation; either version 2 |
9 | * of the License, or (at your option) any later version. | 9 | * of the License, or (at your option) any later version. |
10 | * | 10 | * |
11 | * This program is distributed in the hope that it will be useful, | 11 | * This program is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | * GNU General Public License for more details. | 14 | * GNU General Public License for more details. |
15 | * | 15 | * |
16 | * You should have received a copy of the GNU General Public License | 16 | * You should have received a copy of the GNU General Public License |
17 | * along with this program; if not, write to the Free Software | 17 | * along with this program; if not, write to the Free Software |
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
19 | * 02110-1301, USA. | 19 | * 02110-1301, USA. |
20 | */ | 20 | */ |
21 | 21 | ||
22 | #include "cx18-driver.h" | 22 | #include "cx18-driver.h" |
23 | #include "cx18-io.h" | ||
23 | #include <linux/firmware.h> | 24 | #include <linux/firmware.h> |
24 | 25 | ||
25 | #define CX18_AUDIO_ENABLE 0xc72014 | 26 | #define CX18_AUDIO_ENABLE 0xc72014 |
26 | #define FWFILE "v4l-cx23418-dig.fw" | 27 | #define FWFILE "v4l-cx23418-dig.fw" |
27 | 28 | ||
28 | int cx18_av_loadfw(struct cx18 *cx) | 29 | int cx18_av_loadfw(struct cx18 *cx) |
29 | { | 30 | { |
30 | const struct firmware *fw = NULL; | 31 | const struct firmware *fw = NULL; |
31 | u32 size; | 32 | u32 size; |
32 | u32 v; | 33 | u32 v; |
33 | const u8 *ptr; | 34 | const u8 *ptr; |
34 | int i; | 35 | int i; |
35 | int retries1 = 0; | 36 | int retries1 = 0; |
36 | 37 | ||
37 | if (request_firmware(&fw, FWFILE, &cx->dev->dev) != 0) { | 38 | if (request_firmware(&fw, FWFILE, &cx->dev->dev) != 0) { |
38 | CX18_ERR("unable to open firmware %s\n", FWFILE); | 39 | CX18_ERR("unable to open firmware %s\n", FWFILE); |
39 | return -EINVAL; | 40 | return -EINVAL; |
40 | } | 41 | } |
41 | 42 | ||
42 | /* The firmware load often has byte errors, so allow for several | 43 | /* The firmware load often has byte errors, so allow for several |
43 | retries, both at byte level and at the firmware load level. */ | 44 | retries, both at byte level and at the firmware load level. */ |
44 | while (retries1 < 5) { | 45 | while (retries1 < 5) { |
45 | cx18_av_write4(cx, CXADEC_CHIP_CTRL, 0x00010000); | 46 | cx18_av_write4(cx, CXADEC_CHIP_CTRL, 0x00010000); |
46 | cx18_av_write(cx, CXADEC_STD_DET_CTL, 0xf6); | 47 | cx18_av_write(cx, CXADEC_STD_DET_CTL, 0xf6); |
47 | 48 | ||
48 | /* Reset the Mako core (Register is undocumented.) */ | 49 | /* Reset the Mako core (Register is undocumented.) */ |
49 | cx18_av_write4(cx, 0x8100, 0x00010000); | 50 | cx18_av_write4(cx, 0x8100, 0x00010000); |
50 | 51 | ||
51 | /* Put the 8051 in reset and enable firmware upload */ | 52 | /* Put the 8051 in reset and enable firmware upload */ |
52 | cx18_av_write4(cx, CXADEC_DL_CTL, 0x0F000000); | 53 | cx18_av_write4(cx, CXADEC_DL_CTL, 0x0F000000); |
53 | 54 | ||
54 | ptr = fw->data; | 55 | ptr = fw->data; |
55 | size = fw->size; | 56 | size = fw->size; |
56 | 57 | ||
57 | for (i = 0; i < size; i++) { | 58 | for (i = 0; i < size; i++) { |
58 | u32 dl_control = 0x0F000000 | i | ((u32)ptr[i] << 16); | 59 | u32 dl_control = 0x0F000000 | i | ((u32)ptr[i] << 16); |
59 | u32 value = 0; | 60 | u32 value = 0; |
60 | int retries2; | 61 | int retries2; |
61 | 62 | ||
62 | for (retries2 = 0; retries2 < 5; retries2++) { | 63 | for (retries2 = 0; retries2 < 5; retries2++) { |
63 | cx18_av_write4(cx, CXADEC_DL_CTL, dl_control); | 64 | cx18_av_write4(cx, CXADEC_DL_CTL, dl_control); |
64 | udelay(10); | 65 | udelay(10); |
65 | value = cx18_av_read4(cx, CXADEC_DL_CTL); | 66 | value = cx18_av_read4(cx, CXADEC_DL_CTL); |
66 | if (value == dl_control) | 67 | if (value == dl_control) |
67 | break; | 68 | break; |
68 | /* Check if we can correct the byte by changing | 69 | /* Check if we can correct the byte by changing |
69 | the address. We can only write the lower | 70 | the address. We can only write the lower |
70 | address byte of the address. */ | 71 | address byte of the address. */ |
71 | if ((value & 0x3F00) != (dl_control & 0x3F00)) { | 72 | if ((value & 0x3F00) != (dl_control & 0x3F00)) { |
72 | retries2 = 5; | 73 | retries2 = 5; |
73 | break; | 74 | break; |
74 | } | 75 | } |
75 | } | 76 | } |
76 | if (retries2 >= 5) | 77 | if (retries2 >= 5) |
77 | break; | 78 | break; |
78 | } | 79 | } |
79 | if (i == size) | 80 | if (i == size) |
80 | break; | 81 | break; |
81 | retries1++; | 82 | retries1++; |
82 | } | 83 | } |
83 | if (retries1 >= 5) { | 84 | if (retries1 >= 5) { |
84 | CX18_ERR("unable to load firmware %s\n", FWFILE); | 85 | CX18_ERR("unable to load firmware %s\n", FWFILE); |
85 | release_firmware(fw); | 86 | release_firmware(fw); |
86 | return -EIO; | 87 | return -EIO; |
87 | } | 88 | } |
88 | 89 | ||
89 | cx18_av_write4(cx, CXADEC_DL_CTL, 0x13000000 | fw->size); | 90 | cx18_av_write4(cx, CXADEC_DL_CTL, 0x13000000 | fw->size); |
90 | 91 | ||
91 | /* Output to the 416 */ | 92 | /* Output to the 416 */ |
92 | cx18_av_and_or4(cx, CXADEC_PIN_CTRL1, ~0, 0x78000); | 93 | cx18_av_and_or4(cx, CXADEC_PIN_CTRL1, ~0, 0x78000); |
93 | 94 | ||
94 | /* Audio input control 1 set to Sony mode */ | 95 | /* Audio input control 1 set to Sony mode */ |
95 | /* Audio output input 2 is 0 for slave operation input */ | 96 | /* Audio output input 2 is 0 for slave operation input */ |
96 | /* 0xC4000914[5]: 0 = left sample on WS=0, 1 = left sample on WS=1 */ | 97 | /* 0xC4000914[5]: 0 = left sample on WS=0, 1 = left sample on WS=1 */ |
97 | /* 0xC4000914[7]: 0 = Philips mode, 1 = Sony mode (1st SCK rising edge | 98 | /* 0xC4000914[7]: 0 = Philips mode, 1 = Sony mode (1st SCK rising edge |
98 | after WS transition for first bit of audio word. */ | 99 | after WS transition for first bit of audio word. */ |
99 | cx18_av_write4(cx, CXADEC_I2S_IN_CTL, 0x000000A0); | 100 | cx18_av_write4(cx, CXADEC_I2S_IN_CTL, 0x000000A0); |
100 | 101 | ||
101 | /* Audio output control 1 is set to Sony mode */ | 102 | /* Audio output control 1 is set to Sony mode */ |
102 | /* Audio output control 2 is set to 1 for master mode */ | 103 | /* Audio output control 2 is set to 1 for master mode */ |
103 | /* 0xC4000918[5]: 0 = left sample on WS=0, 1 = left sample on WS=1 */ | 104 | /* 0xC4000918[5]: 0 = left sample on WS=0, 1 = left sample on WS=1 */ |
104 | /* 0xC4000918[7]: 0 = Philips mode, 1 = Sony mode (1st SCK rising edge | 105 | /* 0xC4000918[7]: 0 = Philips mode, 1 = Sony mode (1st SCK rising edge |
105 | after WS transition for first bit of audio word. */ | 106 | after WS transition for first bit of audio word. */ |
106 | /* 0xC4000918[8]: 0 = slave operation, 1 = master (SCK_OUT and WS_OUT | 107 | /* 0xC4000918[8]: 0 = slave operation, 1 = master (SCK_OUT and WS_OUT |
107 | are generated) */ | 108 | are generated) */ |
108 | cx18_av_write4(cx, CXADEC_I2S_OUT_CTL, 0x000001A0); | 109 | cx18_av_write4(cx, CXADEC_I2S_OUT_CTL, 0x000001A0); |
109 | 110 | ||
110 | /* set alt I2s master clock to /16 and enable alt divider i2s | 111 | /* set alt I2s master clock to /16 and enable alt divider i2s |
111 | passthrough */ | 112 | passthrough */ |
112 | cx18_av_write4(cx, CXADEC_PIN_CFG3, 0x5000B687); | 113 | cx18_av_write4(cx, CXADEC_PIN_CFG3, 0x5000B687); |
113 | 114 | ||
114 | cx18_av_write4(cx, CXADEC_STD_DET_CTL, 0x000000F6); | 115 | cx18_av_write4(cx, CXADEC_STD_DET_CTL, 0x000000F6); |
115 | /* CxDevWrReg(CXADEC_STD_DET_CTL, 0x000000FF); */ | 116 | /* CxDevWrReg(CXADEC_STD_DET_CTL, 0x000000FF); */ |
116 | 117 | ||
117 | /* Set bit 0 in register 0x9CC to signify that this is MiniMe. */ | 118 | /* Set bit 0 in register 0x9CC to signify that this is MiniMe. */ |
118 | /* Register 0x09CC is defined by the Merlin firmware, and doesn't | 119 | /* Register 0x09CC is defined by the Merlin firmware, and doesn't |
119 | have a name in the spec. */ | 120 | have a name in the spec. */ |
120 | cx18_av_write4(cx, 0x09CC, 1); | 121 | cx18_av_write4(cx, 0x09CC, 1); |
121 | 122 | ||
122 | v = read_reg(CX18_AUDIO_ENABLE); | 123 | v = cx18_read_reg(cx, CX18_AUDIO_ENABLE); |
123 | /* If bit 11 is 1 */ | 124 | /* If bit 11 is 1, clear bit 10 */ |
124 | if (v & 0x800) | 125 | if (v & 0x800) |
125 | write_reg(v & 0xFFFFFBFF, CX18_AUDIO_ENABLE); /* Clear bit 10 */ | 126 | cx18_write_reg(cx, v & 0xFFFFFBFF, CX18_AUDIO_ENABLE); |
126 | 127 | ||
127 | /* Enable WW auto audio standard detection */ | 128 | /* Enable WW auto audio standard detection */ |
128 | v = cx18_av_read4(cx, CXADEC_STD_DET_CTL); | 129 | v = cx18_av_read4(cx, CXADEC_STD_DET_CTL); |
129 | v |= 0xFF; /* Auto by default */ | 130 | v |= 0xFF; /* Auto by default */ |
130 | v |= 0x400; /* Stereo by default */ | 131 | v |= 0x400; /* Stereo by default */ |
131 | v |= 0x14000000; | 132 | v |= 0x14000000; |
132 | cx18_av_write4(cx, CXADEC_STD_DET_CTL, v); | 133 | cx18_av_write4(cx, CXADEC_STD_DET_CTL, v); |
133 | 134 | ||
134 | release_firmware(fw); | 135 | release_firmware(fw); |
135 | 136 | ||
136 | CX18_INFO("loaded %s firmware (%d bytes)\n", FWFILE, size); | 137 | CX18_INFO("loaded %s firmware (%d bytes)\n", FWFILE, size); |
137 | return 0; | 138 | return 0; |
138 | } | 139 | } |
139 | 140 |
drivers/media/video/cx18/cx18-driver.c
1 | /* | 1 | /* |
2 | * cx18 driver initialization and card probing | 2 | * cx18 driver initialization and card probing |
3 | * | 3 | * |
4 | * Derived from ivtv-driver.c | 4 | * Derived from ivtv-driver.c |
5 | * | 5 | * |
6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | 6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> |
7 | * | 7 | * |
8 | * This program is free software; you can redistribute it and/or modify | 8 | * This program is free software; you can redistribute it and/or modify |
9 | * it under the terms of the GNU General Public License as published by | 9 | * it under the terms of the GNU General Public License as published by |
10 | * the Free Software Foundation; either version 2 of the License, or | 10 | * the Free Software Foundation; either version 2 of the License, or |
11 | * (at your option) any later version. | 11 | * (at your option) any later version. |
12 | * | 12 | * |
13 | * This program is distributed in the hope that it will be useful, | 13 | * This program is distributed in the hope that it will be useful, |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | * GNU General Public License for more details. | 16 | * GNU General Public License for more details. |
17 | * | 17 | * |
18 | * You should have received a copy of the GNU General Public License | 18 | * You should have received a copy of the GNU General Public License |
19 | * along with this program; if not, write to the Free Software | 19 | * along with this program; if not, write to the Free Software |
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | 20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
21 | * 02111-1307 USA | 21 | * 02111-1307 USA |
22 | */ | 22 | */ |
23 | 23 | ||
24 | #include "cx18-driver.h" | 24 | #include "cx18-driver.h" |
25 | #include "cx18-io.h" | ||
25 | #include "cx18-version.h" | 26 | #include "cx18-version.h" |
26 | #include "cx18-cards.h" | 27 | #include "cx18-cards.h" |
27 | #include "cx18-i2c.h" | 28 | #include "cx18-i2c.h" |
28 | #include "cx18-irq.h" | 29 | #include "cx18-irq.h" |
29 | #include "cx18-gpio.h" | 30 | #include "cx18-gpio.h" |
30 | #include "cx18-firmware.h" | 31 | #include "cx18-firmware.h" |
31 | #include "cx18-streams.h" | 32 | #include "cx18-streams.h" |
32 | #include "cx18-av-core.h" | 33 | #include "cx18-av-core.h" |
33 | #include "cx18-scb.h" | 34 | #include "cx18-scb.h" |
34 | #include "cx18-mailbox.h" | 35 | #include "cx18-mailbox.h" |
35 | #include "cx18-ioctl.h" | 36 | #include "cx18-ioctl.h" |
36 | #include "tuner-xc2028.h" | 37 | #include "tuner-xc2028.h" |
37 | 38 | ||
38 | #include <media/tveeprom.h> | 39 | #include <media/tveeprom.h> |
39 | 40 | ||
40 | 41 | ||
41 | /* var to keep track of the number of array elements in use */ | 42 | /* var to keep track of the number of array elements in use */ |
42 | int cx18_cards_active; | 43 | int cx18_cards_active; |
43 | 44 | ||
44 | /* If you have already X v4l cards, then set this to X. This way | 45 | /* If you have already X v4l cards, then set this to X. This way |
45 | the device numbers stay matched. Example: you have a WinTV card | 46 | the device numbers stay matched. Example: you have a WinTV card |
46 | without radio and a Compro H900 with. Normally this would give a | 47 | without radio and a Compro H900 with. Normally this would give a |
47 | video1 device together with a radio0 device for the Compro. By | 48 | video1 device together with a radio0 device for the Compro. By |
48 | setting this to 1 you ensure that radio0 is now also radio1. */ | 49 | setting this to 1 you ensure that radio0 is now also radio1. */ |
49 | int cx18_first_minor; | 50 | int cx18_first_minor; |
50 | 51 | ||
51 | /* Master variable for all cx18 info */ | 52 | /* Master variable for all cx18 info */ |
52 | struct cx18 *cx18_cards[CX18_MAX_CARDS]; | 53 | struct cx18 *cx18_cards[CX18_MAX_CARDS]; |
53 | 54 | ||
54 | /* Protects cx18_cards_active */ | 55 | /* Protects cx18_cards_active */ |
55 | DEFINE_SPINLOCK(cx18_cards_lock); | 56 | DEFINE_SPINLOCK(cx18_cards_lock); |
56 | 57 | ||
57 | /* add your revision and whatnot here */ | 58 | /* add your revision and whatnot here */ |
58 | static struct pci_device_id cx18_pci_tbl[] __devinitdata = { | 59 | static struct pci_device_id cx18_pci_tbl[] __devinitdata = { |
59 | {PCI_VENDOR_ID_CX, PCI_DEVICE_ID_CX23418, | 60 | {PCI_VENDOR_ID_CX, PCI_DEVICE_ID_CX23418, |
60 | PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, | 61 | PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, |
61 | {0,} | 62 | {0,} |
62 | }; | 63 | }; |
63 | 64 | ||
64 | MODULE_DEVICE_TABLE(pci, cx18_pci_tbl); | 65 | MODULE_DEVICE_TABLE(pci, cx18_pci_tbl); |
65 | 66 | ||
66 | /* Parameter declarations */ | 67 | /* Parameter declarations */ |
67 | static int cardtype[CX18_MAX_CARDS]; | 68 | static int cardtype[CX18_MAX_CARDS]; |
68 | static int tuner[CX18_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1, | 69 | static int tuner[CX18_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1, |
69 | -1, -1, -1, -1, -1, -1, -1, -1, | 70 | -1, -1, -1, -1, -1, -1, -1, -1, |
70 | -1, -1, -1, -1, -1, -1, -1, -1, | 71 | -1, -1, -1, -1, -1, -1, -1, -1, |
71 | -1, -1, -1, -1, -1, -1, -1, -1 }; | 72 | -1, -1, -1, -1, -1, -1, -1, -1 }; |
72 | static int radio[CX18_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1, | 73 | static int radio[CX18_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1, |
73 | -1, -1, -1, -1, -1, -1, -1, -1, | 74 | -1, -1, -1, -1, -1, -1, -1, -1, |
74 | -1, -1, -1, -1, -1, -1, -1, -1, | 75 | -1, -1, -1, -1, -1, -1, -1, -1, |
75 | -1, -1, -1, -1, -1, -1, -1, -1 }; | 76 | -1, -1, -1, -1, -1, -1, -1, -1 }; |
76 | 77 | ||
77 | static unsigned cardtype_c = 1; | 78 | static unsigned cardtype_c = 1; |
78 | static unsigned tuner_c = 1; | 79 | static unsigned tuner_c = 1; |
79 | static unsigned radio_c = 1; | 80 | static unsigned radio_c = 1; |
80 | static char pal[] = "--"; | 81 | static char pal[] = "--"; |
81 | static char secam[] = "--"; | 82 | static char secam[] = "--"; |
82 | static char ntsc[] = "-"; | 83 | static char ntsc[] = "-"; |
83 | 84 | ||
84 | /* Buffers */ | 85 | /* Buffers */ |
85 | static int enc_mpg_buffers = CX18_DEFAULT_ENC_MPG_BUFFERS; | 86 | static int enc_mpg_buffers = CX18_DEFAULT_ENC_MPG_BUFFERS; |
86 | static int enc_ts_buffers = CX18_DEFAULT_ENC_TS_BUFFERS; | 87 | static int enc_ts_buffers = CX18_DEFAULT_ENC_TS_BUFFERS; |
87 | static int enc_yuv_buffers = CX18_DEFAULT_ENC_YUV_BUFFERS; | 88 | static int enc_yuv_buffers = CX18_DEFAULT_ENC_YUV_BUFFERS; |
88 | static int enc_vbi_buffers = CX18_DEFAULT_ENC_VBI_BUFFERS; | 89 | static int enc_vbi_buffers = CX18_DEFAULT_ENC_VBI_BUFFERS; |
89 | static int enc_pcm_buffers = CX18_DEFAULT_ENC_PCM_BUFFERS; | 90 | static int enc_pcm_buffers = CX18_DEFAULT_ENC_PCM_BUFFERS; |
90 | 91 | ||
91 | static int cx18_pci_latency = 1; | 92 | static int cx18_pci_latency = 1; |
92 | 93 | ||
93 | int cx18_debug; | 94 | int cx18_debug; |
94 | 95 | ||
95 | module_param_array(tuner, int, &tuner_c, 0644); | 96 | module_param_array(tuner, int, &tuner_c, 0644); |
96 | module_param_array(radio, bool, &radio_c, 0644); | 97 | module_param_array(radio, bool, &radio_c, 0644); |
97 | module_param_array(cardtype, int, &cardtype_c, 0644); | 98 | module_param_array(cardtype, int, &cardtype_c, 0644); |
98 | module_param_string(pal, pal, sizeof(pal), 0644); | 99 | module_param_string(pal, pal, sizeof(pal), 0644); |
99 | module_param_string(secam, secam, sizeof(secam), 0644); | 100 | module_param_string(secam, secam, sizeof(secam), 0644); |
100 | module_param_string(ntsc, ntsc, sizeof(ntsc), 0644); | 101 | module_param_string(ntsc, ntsc, sizeof(ntsc), 0644); |
101 | module_param_named(debug, cx18_debug, int, 0644); | 102 | module_param_named(debug, cx18_debug, int, 0644); |
102 | module_param(cx18_pci_latency, int, 0644); | 103 | module_param(cx18_pci_latency, int, 0644); |
103 | module_param(cx18_first_minor, int, 0644); | 104 | module_param(cx18_first_minor, int, 0644); |
104 | 105 | ||
105 | module_param(enc_mpg_buffers, int, 0644); | 106 | module_param(enc_mpg_buffers, int, 0644); |
106 | module_param(enc_ts_buffers, int, 0644); | 107 | module_param(enc_ts_buffers, int, 0644); |
107 | module_param(enc_yuv_buffers, int, 0644); | 108 | module_param(enc_yuv_buffers, int, 0644); |
108 | module_param(enc_vbi_buffers, int, 0644); | 109 | module_param(enc_vbi_buffers, int, 0644); |
109 | module_param(enc_pcm_buffers, int, 0644); | 110 | module_param(enc_pcm_buffers, int, 0644); |
110 | 111 | ||
111 | MODULE_PARM_DESC(tuner, "Tuner type selection,\n" | 112 | MODULE_PARM_DESC(tuner, "Tuner type selection,\n" |
112 | "\t\t\tsee tuner.h for values"); | 113 | "\t\t\tsee tuner.h for values"); |
113 | MODULE_PARM_DESC(radio, | 114 | MODULE_PARM_DESC(radio, |
114 | "Enable or disable the radio. Use only if autodetection\n" | 115 | "Enable or disable the radio. Use only if autodetection\n" |
115 | "\t\t\tfails. 0 = disable, 1 = enable"); | 116 | "\t\t\tfails. 0 = disable, 1 = enable"); |
116 | MODULE_PARM_DESC(cardtype, | 117 | MODULE_PARM_DESC(cardtype, |
117 | "Only use this option if your card is not detected properly.\n" | 118 | "Only use this option if your card is not detected properly.\n" |
118 | "\t\tSpecify card type:\n" | 119 | "\t\tSpecify card type:\n" |
119 | "\t\t\t 1 = Hauppauge HVR 1600 (ESMT memory)\n" | 120 | "\t\t\t 1 = Hauppauge HVR 1600 (ESMT memory)\n" |
120 | "\t\t\t 2 = Hauppauge HVR 1600 (Samsung memory)\n" | 121 | "\t\t\t 2 = Hauppauge HVR 1600 (Samsung memory)\n" |
121 | "\t\t\t 3 = Compro VideoMate H900\n" | 122 | "\t\t\t 3 = Compro VideoMate H900\n" |
122 | "\t\t\t 4 = Yuan MPC718\n" | 123 | "\t\t\t 4 = Yuan MPC718\n" |
123 | "\t\t\t 5 = Conexant Raptor PAL/SECAM\n" | 124 | "\t\t\t 5 = Conexant Raptor PAL/SECAM\n" |
124 | "\t\t\t 0 = Autodetect (default)\n" | 125 | "\t\t\t 0 = Autodetect (default)\n" |
125 | "\t\t\t-1 = Ignore this card\n\t\t"); | 126 | "\t\t\t-1 = Ignore this card\n\t\t"); |
126 | MODULE_PARM_DESC(pal, "Set PAL standard: B, G, H, D, K, I, M, N, Nc, 60"); | 127 | MODULE_PARM_DESC(pal, "Set PAL standard: B, G, H, D, K, I, M, N, Nc, 60"); |
127 | MODULE_PARM_DESC(secam, "Set SECAM standard: B, G, H, D, K, L, LC"); | 128 | MODULE_PARM_DESC(secam, "Set SECAM standard: B, G, H, D, K, L, LC"); |
128 | MODULE_PARM_DESC(ntsc, "Set NTSC standard: M, J, K"); | 129 | MODULE_PARM_DESC(ntsc, "Set NTSC standard: M, J, K"); |
129 | MODULE_PARM_DESC(debug, | 130 | MODULE_PARM_DESC(debug, |
130 | "Debug level (bitmask). Default: 0\n" | 131 | "Debug level (bitmask). Default: 0\n" |
131 | "\t\t\t 1/0x0001: warning\n" | 132 | "\t\t\t 1/0x0001: warning\n" |
132 | "\t\t\t 2/0x0002: info\n" | 133 | "\t\t\t 2/0x0002: info\n" |
133 | "\t\t\t 4/0x0004: mailbox\n" | 134 | "\t\t\t 4/0x0004: mailbox\n" |
134 | "\t\t\t 8/0x0008: dma\n" | 135 | "\t\t\t 8/0x0008: dma\n" |
135 | "\t\t\t 16/0x0010: ioctl\n" | 136 | "\t\t\t 16/0x0010: ioctl\n" |
136 | "\t\t\t 32/0x0020: file\n" | 137 | "\t\t\t 32/0x0020: file\n" |
137 | "\t\t\t 64/0x0040: i2c\n" | 138 | "\t\t\t 64/0x0040: i2c\n" |
138 | "\t\t\t128/0x0080: irq\n" | 139 | "\t\t\t128/0x0080: irq\n" |
139 | "\t\t\t256/0x0100: high volume\n"); | 140 | "\t\t\t256/0x0100: high volume\n"); |
140 | MODULE_PARM_DESC(cx18_pci_latency, | 141 | MODULE_PARM_DESC(cx18_pci_latency, |
141 | "Change the PCI latency to 64 if lower: 0 = No, 1 = Yes,\n" | 142 | "Change the PCI latency to 64 if lower: 0 = No, 1 = Yes,\n" |
142 | "\t\t\tDefault: Yes"); | 143 | "\t\t\tDefault: Yes"); |
143 | MODULE_PARM_DESC(enc_mpg_buffers, | 144 | MODULE_PARM_DESC(enc_mpg_buffers, |
144 | "Encoder MPG Buffers (in MB)\n" | 145 | "Encoder MPG Buffers (in MB)\n" |
145 | "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_MPG_BUFFERS)); | 146 | "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_MPG_BUFFERS)); |
146 | MODULE_PARM_DESC(enc_ts_buffers, | 147 | MODULE_PARM_DESC(enc_ts_buffers, |
147 | "Encoder TS Buffers (in MB)\n" | 148 | "Encoder TS Buffers (in MB)\n" |
148 | "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_TS_BUFFERS)); | 149 | "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_TS_BUFFERS)); |
149 | MODULE_PARM_DESC(enc_yuv_buffers, | 150 | MODULE_PARM_DESC(enc_yuv_buffers, |
150 | "Encoder YUV Buffers (in MB)\n" | 151 | "Encoder YUV Buffers (in MB)\n" |
151 | "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_YUV_BUFFERS)); | 152 | "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_YUV_BUFFERS)); |
152 | MODULE_PARM_DESC(enc_vbi_buffers, | 153 | MODULE_PARM_DESC(enc_vbi_buffers, |
153 | "Encoder VBI Buffers (in MB)\n" | 154 | "Encoder VBI Buffers (in MB)\n" |
154 | "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_VBI_BUFFERS)); | 155 | "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_VBI_BUFFERS)); |
155 | MODULE_PARM_DESC(enc_pcm_buffers, | 156 | MODULE_PARM_DESC(enc_pcm_buffers, |
156 | "Encoder PCM buffers (in MB)\n" | 157 | "Encoder PCM buffers (in MB)\n" |
157 | "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_PCM_BUFFERS)); | 158 | "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_PCM_BUFFERS)); |
158 | 159 | ||
159 | MODULE_PARM_DESC(cx18_first_minor, "Set minor assigned to first card"); | 160 | MODULE_PARM_DESC(cx18_first_minor, "Set minor assigned to first card"); |
160 | 161 | ||
161 | MODULE_AUTHOR("Hans Verkuil"); | 162 | MODULE_AUTHOR("Hans Verkuil"); |
162 | MODULE_DESCRIPTION("CX23418 driver"); | 163 | MODULE_DESCRIPTION("CX23418 driver"); |
163 | MODULE_SUPPORTED_DEVICE("CX23418 MPEG2 encoder"); | 164 | MODULE_SUPPORTED_DEVICE("CX23418 MPEG2 encoder"); |
164 | MODULE_LICENSE("GPL"); | 165 | MODULE_LICENSE("GPL"); |
165 | 166 | ||
166 | MODULE_VERSION(CX18_VERSION); | 167 | MODULE_VERSION(CX18_VERSION); |
167 | 168 | ||
168 | /* Generic utility functions */ | 169 | /* Generic utility functions */ |
169 | int cx18_msleep_timeout(unsigned int msecs, int intr) | 170 | int cx18_msleep_timeout(unsigned int msecs, int intr) |
170 | { | 171 | { |
171 | int timeout = msecs_to_jiffies(msecs); | 172 | int timeout = msecs_to_jiffies(msecs); |
172 | int sig; | 173 | int sig; |
173 | 174 | ||
174 | do { | 175 | do { |
175 | set_current_state(intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE); | 176 | set_current_state(intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE); |
176 | timeout = schedule_timeout(timeout); | 177 | timeout = schedule_timeout(timeout); |
177 | sig = intr ? signal_pending(current) : 0; | 178 | sig = intr ? signal_pending(current) : 0; |
178 | } while (!sig && timeout); | 179 | } while (!sig && timeout); |
179 | return sig; | 180 | return sig; |
180 | } | 181 | } |
181 | 182 | ||
182 | /* Release ioremapped memory */ | 183 | /* Release ioremapped memory */ |
183 | static void cx18_iounmap(struct cx18 *cx) | 184 | static void cx18_iounmap(struct cx18 *cx) |
184 | { | 185 | { |
185 | if (cx == NULL) | 186 | if (cx == NULL) |
186 | return; | 187 | return; |
187 | 188 | ||
188 | /* Release io memory */ | 189 | /* Release io memory */ |
189 | if (cx->enc_mem != NULL) { | 190 | if (cx->enc_mem != NULL) { |
190 | CX18_DEBUG_INFO("releasing enc_mem\n"); | 191 | CX18_DEBUG_INFO("releasing enc_mem\n"); |
191 | iounmap(cx->enc_mem); | 192 | iounmap(cx->enc_mem); |
192 | cx->enc_mem = NULL; | 193 | cx->enc_mem = NULL; |
193 | } | 194 | } |
194 | } | 195 | } |
195 | 196 | ||
196 | /* Hauppauge card? get values from tveeprom */ | 197 | /* Hauppauge card? get values from tveeprom */ |
197 | void cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv) | 198 | void cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv) |
198 | { | 199 | { |
199 | u8 eedata[256]; | 200 | u8 eedata[256]; |
200 | 201 | ||
201 | cx->i2c_client[0].addr = 0xA0 >> 1; | 202 | cx->i2c_client[0].addr = 0xA0 >> 1; |
202 | tveeprom_read(&cx->i2c_client[0], eedata, sizeof(eedata)); | 203 | tveeprom_read(&cx->i2c_client[0], eedata, sizeof(eedata)); |
203 | tveeprom_hauppauge_analog(&cx->i2c_client[0], tv, eedata); | 204 | tveeprom_hauppauge_analog(&cx->i2c_client[0], tv, eedata); |
204 | } | 205 | } |
205 | 206 | ||
206 | static void cx18_process_eeprom(struct cx18 *cx) | 207 | static void cx18_process_eeprom(struct cx18 *cx) |
207 | { | 208 | { |
208 | struct tveeprom tv; | 209 | struct tveeprom tv; |
209 | 210 | ||
210 | cx18_read_eeprom(cx, &tv); | 211 | cx18_read_eeprom(cx, &tv); |
211 | 212 | ||
212 | /* Many thanks to Steven Toth from Hauppauge for providing the | 213 | /* Many thanks to Steven Toth from Hauppauge for providing the |
213 | model numbers */ | 214 | model numbers */ |
214 | /* Note: the Samsung memory models cannot be reliably determined | 215 | /* Note: the Samsung memory models cannot be reliably determined |
215 | from the model number. Use the cardtype module option if you | 216 | from the model number. Use the cardtype module option if you |
216 | have one of these preproduction models. */ | 217 | have one of these preproduction models. */ |
217 | switch (tv.model) { | 218 | switch (tv.model) { |
218 | case 74000 ... 74999: | 219 | case 74000 ... 74999: |
219 | cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT); | 220 | cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT); |
220 | break; | 221 | break; |
221 | case 0: | 222 | case 0: |
222 | CX18_ERR("Invalid EEPROM\n"); | 223 | CX18_ERR("Invalid EEPROM\n"); |
223 | return; | 224 | return; |
224 | default: | 225 | default: |
225 | CX18_ERR("Unknown model %d, defaulting to HVR-1600\n", tv.model); | 226 | CX18_ERR("Unknown model %d, defaulting to HVR-1600\n", tv.model); |
226 | cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT); | 227 | cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT); |
227 | break; | 228 | break; |
228 | } | 229 | } |
229 | 230 | ||
230 | cx->v4l2_cap = cx->card->v4l2_capabilities; | 231 | cx->v4l2_cap = cx->card->v4l2_capabilities; |
231 | cx->card_name = cx->card->name; | 232 | cx->card_name = cx->card->name; |
232 | cx->card_i2c = cx->card->i2c; | 233 | cx->card_i2c = cx->card->i2c; |
233 | 234 | ||
234 | CX18_INFO("Autodetected %s\n", cx->card_name); | 235 | CX18_INFO("Autodetected %s\n", cx->card_name); |
235 | 236 | ||
236 | if (tv.tuner_type == TUNER_ABSENT) | 237 | if (tv.tuner_type == TUNER_ABSENT) |
237 | CX18_ERR("tveeprom cannot autodetect tuner!"); | 238 | CX18_ERR("tveeprom cannot autodetect tuner!"); |
238 | 239 | ||
239 | if (cx->options.tuner == -1) | 240 | if (cx->options.tuner == -1) |
240 | cx->options.tuner = tv.tuner_type; | 241 | cx->options.tuner = tv.tuner_type; |
241 | if (cx->options.radio == -1) | 242 | if (cx->options.radio == -1) |
242 | cx->options.radio = (tv.has_radio != 0); | 243 | cx->options.radio = (tv.has_radio != 0); |
243 | 244 | ||
244 | if (cx->std != 0) | 245 | if (cx->std != 0) |
245 | /* user specified tuner standard */ | 246 | /* user specified tuner standard */ |
246 | return; | 247 | return; |
247 | 248 | ||
248 | /* autodetect tuner standard */ | 249 | /* autodetect tuner standard */ |
249 | if (tv.tuner_formats & V4L2_STD_PAL) { | 250 | if (tv.tuner_formats & V4L2_STD_PAL) { |
250 | CX18_DEBUG_INFO("PAL tuner detected\n"); | 251 | CX18_DEBUG_INFO("PAL tuner detected\n"); |
251 | cx->std |= V4L2_STD_PAL_BG | V4L2_STD_PAL_H; | 252 | cx->std |= V4L2_STD_PAL_BG | V4L2_STD_PAL_H; |
252 | } else if (tv.tuner_formats & V4L2_STD_NTSC) { | 253 | } else if (tv.tuner_formats & V4L2_STD_NTSC) { |
253 | CX18_DEBUG_INFO("NTSC tuner detected\n"); | 254 | CX18_DEBUG_INFO("NTSC tuner detected\n"); |
254 | cx->std |= V4L2_STD_NTSC_M; | 255 | cx->std |= V4L2_STD_NTSC_M; |
255 | } else if (tv.tuner_formats & V4L2_STD_SECAM) { | 256 | } else if (tv.tuner_formats & V4L2_STD_SECAM) { |
256 | CX18_DEBUG_INFO("SECAM tuner detected\n"); | 257 | CX18_DEBUG_INFO("SECAM tuner detected\n"); |
257 | cx->std |= V4L2_STD_SECAM_L; | 258 | cx->std |= V4L2_STD_SECAM_L; |
258 | } else { | 259 | } else { |
259 | CX18_INFO("No tuner detected, default to NTSC-M\n"); | 260 | CX18_INFO("No tuner detected, default to NTSC-M\n"); |
260 | cx->std |= V4L2_STD_NTSC_M; | 261 | cx->std |= V4L2_STD_NTSC_M; |
261 | } | 262 | } |
262 | } | 263 | } |
263 | 264 | ||
264 | static v4l2_std_id cx18_parse_std(struct cx18 *cx) | 265 | static v4l2_std_id cx18_parse_std(struct cx18 *cx) |
265 | { | 266 | { |
266 | switch (pal[0]) { | 267 | switch (pal[0]) { |
267 | case '6': | 268 | case '6': |
268 | return V4L2_STD_PAL_60; | 269 | return V4L2_STD_PAL_60; |
269 | case 'b': | 270 | case 'b': |
270 | case 'B': | 271 | case 'B': |
271 | case 'g': | 272 | case 'g': |
272 | case 'G': | 273 | case 'G': |
273 | return V4L2_STD_PAL_BG; | 274 | return V4L2_STD_PAL_BG; |
274 | case 'h': | 275 | case 'h': |
275 | case 'H': | 276 | case 'H': |
276 | return V4L2_STD_PAL_H; | 277 | return V4L2_STD_PAL_H; |
277 | case 'n': | 278 | case 'n': |
278 | case 'N': | 279 | case 'N': |
279 | if (pal[1] == 'c' || pal[1] == 'C') | 280 | if (pal[1] == 'c' || pal[1] == 'C') |
280 | return V4L2_STD_PAL_Nc; | 281 | return V4L2_STD_PAL_Nc; |
281 | return V4L2_STD_PAL_N; | 282 | return V4L2_STD_PAL_N; |
282 | case 'i': | 283 | case 'i': |
283 | case 'I': | 284 | case 'I': |
284 | return V4L2_STD_PAL_I; | 285 | return V4L2_STD_PAL_I; |
285 | case 'd': | 286 | case 'd': |
286 | case 'D': | 287 | case 'D': |
287 | case 'k': | 288 | case 'k': |
288 | case 'K': | 289 | case 'K': |
289 | return V4L2_STD_PAL_DK; | 290 | return V4L2_STD_PAL_DK; |
290 | case 'M': | 291 | case 'M': |
291 | case 'm': | 292 | case 'm': |
292 | return V4L2_STD_PAL_M; | 293 | return V4L2_STD_PAL_M; |
293 | case '-': | 294 | case '-': |
294 | break; | 295 | break; |
295 | default: | 296 | default: |
296 | CX18_WARN("pal= argument not recognised\n"); | 297 | CX18_WARN("pal= argument not recognised\n"); |
297 | return 0; | 298 | return 0; |
298 | } | 299 | } |
299 | 300 | ||
300 | switch (secam[0]) { | 301 | switch (secam[0]) { |
301 | case 'b': | 302 | case 'b': |
302 | case 'B': | 303 | case 'B': |
303 | case 'g': | 304 | case 'g': |
304 | case 'G': | 305 | case 'G': |
305 | case 'h': | 306 | case 'h': |
306 | case 'H': | 307 | case 'H': |
307 | return V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H; | 308 | return V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H; |
308 | case 'd': | 309 | case 'd': |
309 | case 'D': | 310 | case 'D': |
310 | case 'k': | 311 | case 'k': |
311 | case 'K': | 312 | case 'K': |
312 | return V4L2_STD_SECAM_DK; | 313 | return V4L2_STD_SECAM_DK; |
313 | case 'l': | 314 | case 'l': |
314 | case 'L': | 315 | case 'L': |
315 | if (secam[1] == 'C' || secam[1] == 'c') | 316 | if (secam[1] == 'C' || secam[1] == 'c') |
316 | return V4L2_STD_SECAM_LC; | 317 | return V4L2_STD_SECAM_LC; |
317 | return V4L2_STD_SECAM_L; | 318 | return V4L2_STD_SECAM_L; |
318 | case '-': | 319 | case '-': |
319 | break; | 320 | break; |
320 | default: | 321 | default: |
321 | CX18_WARN("secam= argument not recognised\n"); | 322 | CX18_WARN("secam= argument not recognised\n"); |
322 | return 0; | 323 | return 0; |
323 | } | 324 | } |
324 | 325 | ||
325 | switch (ntsc[0]) { | 326 | switch (ntsc[0]) { |
326 | case 'm': | 327 | case 'm': |
327 | case 'M': | 328 | case 'M': |
328 | return V4L2_STD_NTSC_M; | 329 | return V4L2_STD_NTSC_M; |
329 | case 'j': | 330 | case 'j': |
330 | case 'J': | 331 | case 'J': |
331 | return V4L2_STD_NTSC_M_JP; | 332 | return V4L2_STD_NTSC_M_JP; |
332 | case 'k': | 333 | case 'k': |
333 | case 'K': | 334 | case 'K': |
334 | return V4L2_STD_NTSC_M_KR; | 335 | return V4L2_STD_NTSC_M_KR; |
335 | case '-': | 336 | case '-': |
336 | break; | 337 | break; |
337 | default: | 338 | default: |
338 | CX18_WARN("ntsc= argument not recognised\n"); | 339 | CX18_WARN("ntsc= argument not recognised\n"); |
339 | return 0; | 340 | return 0; |
340 | } | 341 | } |
341 | 342 | ||
342 | /* no match found */ | 343 | /* no match found */ |
343 | return 0; | 344 | return 0; |
344 | } | 345 | } |
345 | 346 | ||
346 | static void cx18_process_options(struct cx18 *cx) | 347 | static void cx18_process_options(struct cx18 *cx) |
347 | { | 348 | { |
348 | int i, j; | 349 | int i, j; |
349 | 350 | ||
350 | cx->options.megabytes[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_buffers; | 351 | cx->options.megabytes[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_buffers; |
351 | cx->options.megabytes[CX18_ENC_STREAM_TYPE_TS] = enc_ts_buffers; | 352 | cx->options.megabytes[CX18_ENC_STREAM_TYPE_TS] = enc_ts_buffers; |
352 | cx->options.megabytes[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_buffers; | 353 | cx->options.megabytes[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_buffers; |
353 | cx->options.megabytes[CX18_ENC_STREAM_TYPE_VBI] = enc_vbi_buffers; | 354 | cx->options.megabytes[CX18_ENC_STREAM_TYPE_VBI] = enc_vbi_buffers; |
354 | cx->options.megabytes[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_buffers; | 355 | cx->options.megabytes[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_buffers; |
355 | cx->options.cardtype = cardtype[cx->num]; | 356 | cx->options.cardtype = cardtype[cx->num]; |
356 | cx->options.tuner = tuner[cx->num]; | 357 | cx->options.tuner = tuner[cx->num]; |
357 | cx->options.radio = radio[cx->num]; | 358 | cx->options.radio = radio[cx->num]; |
358 | 359 | ||
359 | cx->std = cx18_parse_std(cx); | 360 | cx->std = cx18_parse_std(cx); |
360 | if (cx->options.cardtype == -1) { | 361 | if (cx->options.cardtype == -1) { |
361 | CX18_INFO("Ignore card\n"); | 362 | CX18_INFO("Ignore card\n"); |
362 | return; | 363 | return; |
363 | } | 364 | } |
364 | cx->card = cx18_get_card(cx->options.cardtype - 1); | 365 | cx->card = cx18_get_card(cx->options.cardtype - 1); |
365 | if (cx->card) | 366 | if (cx->card) |
366 | CX18_INFO("User specified %s card\n", cx->card->name); | 367 | CX18_INFO("User specified %s card\n", cx->card->name); |
367 | else if (cx->options.cardtype != 0) | 368 | else if (cx->options.cardtype != 0) |
368 | CX18_ERR("Unknown user specified type, trying to autodetect card\n"); | 369 | CX18_ERR("Unknown user specified type, trying to autodetect card\n"); |
369 | if (cx->card == NULL) { | 370 | if (cx->card == NULL) { |
370 | if (cx->dev->subsystem_vendor == CX18_PCI_ID_HAUPPAUGE) { | 371 | if (cx->dev->subsystem_vendor == CX18_PCI_ID_HAUPPAUGE) { |
371 | cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT); | 372 | cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT); |
372 | CX18_INFO("Autodetected Hauppauge card\n"); | 373 | CX18_INFO("Autodetected Hauppauge card\n"); |
373 | } | 374 | } |
374 | } | 375 | } |
375 | if (cx->card == NULL) { | 376 | if (cx->card == NULL) { |
376 | for (i = 0; (cx->card = cx18_get_card(i)); i++) { | 377 | for (i = 0; (cx->card = cx18_get_card(i)); i++) { |
377 | if (cx->card->pci_list == NULL) | 378 | if (cx->card->pci_list == NULL) |
378 | continue; | 379 | continue; |
379 | for (j = 0; cx->card->pci_list[j].device; j++) { | 380 | for (j = 0; cx->card->pci_list[j].device; j++) { |
380 | if (cx->dev->device != | 381 | if (cx->dev->device != |
381 | cx->card->pci_list[j].device) | 382 | cx->card->pci_list[j].device) |
382 | continue; | 383 | continue; |
383 | if (cx->dev->subsystem_vendor != | 384 | if (cx->dev->subsystem_vendor != |
384 | cx->card->pci_list[j].subsystem_vendor) | 385 | cx->card->pci_list[j].subsystem_vendor) |
385 | continue; | 386 | continue; |
386 | if (cx->dev->subsystem_device != | 387 | if (cx->dev->subsystem_device != |
387 | cx->card->pci_list[j].subsystem_device) | 388 | cx->card->pci_list[j].subsystem_device) |
388 | continue; | 389 | continue; |
389 | CX18_INFO("Autodetected %s card\n", cx->card->name); | 390 | CX18_INFO("Autodetected %s card\n", cx->card->name); |
390 | goto done; | 391 | goto done; |
391 | } | 392 | } |
392 | } | 393 | } |
393 | } | 394 | } |
394 | done: | 395 | done: |
395 | 396 | ||
396 | if (cx->card == NULL) { | 397 | if (cx->card == NULL) { |
397 | cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT); | 398 | cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT); |
398 | CX18_ERR("Unknown card: vendor/device: [%04x:%04x]\n", | 399 | CX18_ERR("Unknown card: vendor/device: [%04x:%04x]\n", |
399 | cx->dev->vendor, cx->dev->device); | 400 | cx->dev->vendor, cx->dev->device); |
400 | CX18_ERR(" subsystem vendor/device: [%04x:%04x]\n", | 401 | CX18_ERR(" subsystem vendor/device: [%04x:%04x]\n", |
401 | cx->dev->subsystem_vendor, cx->dev->subsystem_device); | 402 | cx->dev->subsystem_vendor, cx->dev->subsystem_device); |
402 | CX18_ERR("Defaulting to %s card\n", cx->card->name); | 403 | CX18_ERR("Defaulting to %s card\n", cx->card->name); |
403 | CX18_ERR("Please mail the vendor/device and subsystem vendor/device IDs and what kind of\n"); | 404 | CX18_ERR("Please mail the vendor/device and subsystem vendor/device IDs and what kind of\n"); |
404 | CX18_ERR("card you have to the ivtv-devel mailinglist (www.ivtvdriver.org)\n"); | 405 | CX18_ERR("card you have to the ivtv-devel mailinglist (www.ivtvdriver.org)\n"); |
405 | CX18_ERR("Prefix your subject line with [UNKNOWN CX18 CARD].\n"); | 406 | CX18_ERR("Prefix your subject line with [UNKNOWN CX18 CARD].\n"); |
406 | } | 407 | } |
407 | cx->v4l2_cap = cx->card->v4l2_capabilities; | 408 | cx->v4l2_cap = cx->card->v4l2_capabilities; |
408 | cx->card_name = cx->card->name; | 409 | cx->card_name = cx->card->name; |
409 | cx->card_i2c = cx->card->i2c; | 410 | cx->card_i2c = cx->card->i2c; |
410 | } | 411 | } |
411 | 412 | ||
412 | /* Precondition: the cx18 structure has been memset to 0. Only | 413 | /* Precondition: the cx18 structure has been memset to 0. Only |
413 | the dev and num fields have been filled in. | 414 | the dev and num fields have been filled in. |
414 | No assumptions on the card type may be made here (see cx18_init_struct2 | 415 | No assumptions on the card type may be made here (see cx18_init_struct2 |
415 | for that). | 416 | for that). |
416 | */ | 417 | */ |
417 | static int __devinit cx18_init_struct1(struct cx18 *cx) | 418 | static int __devinit cx18_init_struct1(struct cx18 *cx) |
418 | { | 419 | { |
419 | cx->base_addr = pci_resource_start(cx->dev, 0); | 420 | cx->base_addr = pci_resource_start(cx->dev, 0); |
420 | 421 | ||
421 | mutex_init(&cx->serialize_lock); | 422 | mutex_init(&cx->serialize_lock); |
422 | mutex_init(&cx->i2c_bus_lock[0]); | 423 | mutex_init(&cx->i2c_bus_lock[0]); |
423 | mutex_init(&cx->i2c_bus_lock[1]); | 424 | mutex_init(&cx->i2c_bus_lock[1]); |
424 | mutex_init(&cx->gpio_lock); | 425 | mutex_init(&cx->gpio_lock); |
425 | 426 | ||
426 | spin_lock_init(&cx->lock); | 427 | spin_lock_init(&cx->lock); |
427 | spin_lock_init(&cx->dma_reg_lock); | 428 | spin_lock_init(&cx->dma_reg_lock); |
428 | 429 | ||
429 | /* start counting open_id at 1 */ | 430 | /* start counting open_id at 1 */ |
430 | cx->open_id = 1; | 431 | cx->open_id = 1; |
431 | 432 | ||
432 | /* Initial settings */ | 433 | /* Initial settings */ |
433 | cx2341x_fill_defaults(&cx->params); | 434 | cx2341x_fill_defaults(&cx->params); |
434 | cx->temporal_strength = cx->params.video_temporal_filter; | 435 | cx->temporal_strength = cx->params.video_temporal_filter; |
435 | cx->spatial_strength = cx->params.video_spatial_filter; | 436 | cx->spatial_strength = cx->params.video_spatial_filter; |
436 | cx->filter_mode = cx->params.video_spatial_filter_mode | | 437 | cx->filter_mode = cx->params.video_spatial_filter_mode | |
437 | (cx->params.video_temporal_filter_mode << 1) | | 438 | (cx->params.video_temporal_filter_mode << 1) | |
438 | (cx->params.video_median_filter_type << 2); | 439 | (cx->params.video_median_filter_type << 2); |
439 | cx->params.port = CX2341X_PORT_MEMORY; | 440 | cx->params.port = CX2341X_PORT_MEMORY; |
440 | cx->params.capabilities = CX2341X_CAP_HAS_TS; | 441 | cx->params.capabilities = CX2341X_CAP_HAS_TS; |
441 | init_waitqueue_head(&cx->cap_w); | 442 | init_waitqueue_head(&cx->cap_w); |
442 | init_waitqueue_head(&cx->mb_apu_waitq); | 443 | init_waitqueue_head(&cx->mb_apu_waitq); |
443 | init_waitqueue_head(&cx->mb_cpu_waitq); | 444 | init_waitqueue_head(&cx->mb_cpu_waitq); |
444 | init_waitqueue_head(&cx->mb_epu_waitq); | 445 | init_waitqueue_head(&cx->mb_epu_waitq); |
445 | init_waitqueue_head(&cx->mb_hpu_waitq); | 446 | init_waitqueue_head(&cx->mb_hpu_waitq); |
446 | init_waitqueue_head(&cx->dma_waitq); | 447 | init_waitqueue_head(&cx->dma_waitq); |
447 | 448 | ||
448 | /* VBI */ | 449 | /* VBI */ |
449 | cx->vbi.in.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; | 450 | cx->vbi.in.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; |
450 | cx->vbi.sliced_in = &cx->vbi.in.fmt.sliced; | 451 | cx->vbi.sliced_in = &cx->vbi.in.fmt.sliced; |
451 | cx->vbi.raw_size = 1456; | 452 | cx->vbi.raw_size = 1456; |
452 | cx->vbi.raw_decoder_line_size = 1456; | 453 | cx->vbi.raw_decoder_line_size = 1456; |
453 | cx->vbi.raw_decoder_sav_odd_field = 0x20; | 454 | cx->vbi.raw_decoder_sav_odd_field = 0x20; |
454 | cx->vbi.raw_decoder_sav_even_field = 0x60; | 455 | cx->vbi.raw_decoder_sav_even_field = 0x60; |
455 | cx->vbi.sliced_decoder_line_size = 272; | 456 | cx->vbi.sliced_decoder_line_size = 272; |
456 | cx->vbi.sliced_decoder_sav_odd_field = 0xB0; | 457 | cx->vbi.sliced_decoder_sav_odd_field = 0xB0; |
457 | cx->vbi.sliced_decoder_sav_even_field = 0xF0; | 458 | cx->vbi.sliced_decoder_sav_even_field = 0xF0; |
458 | return 0; | 459 | return 0; |
459 | } | 460 | } |
460 | 461 | ||
461 | /* Second initialization part. Here the card type has been | 462 | /* Second initialization part. Here the card type has been |
462 | autodetected. */ | 463 | autodetected. */ |
463 | static void __devinit cx18_init_struct2(struct cx18 *cx) | 464 | static void __devinit cx18_init_struct2(struct cx18 *cx) |
464 | { | 465 | { |
465 | int i; | 466 | int i; |
466 | 467 | ||
467 | for (i = 0; i < CX18_CARD_MAX_VIDEO_INPUTS; i++) | 468 | for (i = 0; i < CX18_CARD_MAX_VIDEO_INPUTS; i++) |
468 | if (cx->card->video_inputs[i].video_type == 0) | 469 | if (cx->card->video_inputs[i].video_type == 0) |
469 | break; | 470 | break; |
470 | cx->nof_inputs = i; | 471 | cx->nof_inputs = i; |
471 | for (i = 0; i < CX18_CARD_MAX_AUDIO_INPUTS; i++) | 472 | for (i = 0; i < CX18_CARD_MAX_AUDIO_INPUTS; i++) |
472 | if (cx->card->audio_inputs[i].audio_type == 0) | 473 | if (cx->card->audio_inputs[i].audio_type == 0) |
473 | break; | 474 | break; |
474 | cx->nof_audio_inputs = i; | 475 | cx->nof_audio_inputs = i; |
475 | 476 | ||
476 | /* Find tuner input */ | 477 | /* Find tuner input */ |
477 | for (i = 0; i < cx->nof_inputs; i++) { | 478 | for (i = 0; i < cx->nof_inputs; i++) { |
478 | if (cx->card->video_inputs[i].video_type == | 479 | if (cx->card->video_inputs[i].video_type == |
479 | CX18_CARD_INPUT_VID_TUNER) | 480 | CX18_CARD_INPUT_VID_TUNER) |
480 | break; | 481 | break; |
481 | } | 482 | } |
482 | if (i == cx->nof_inputs) | 483 | if (i == cx->nof_inputs) |
483 | i = 0; | 484 | i = 0; |
484 | cx->active_input = i; | 485 | cx->active_input = i; |
485 | cx->audio_input = cx->card->video_inputs[i].audio_index; | 486 | cx->audio_input = cx->card->video_inputs[i].audio_index; |
486 | cx->av_state.vid_input = CX18_AV_COMPOSITE7; | 487 | cx->av_state.vid_input = CX18_AV_COMPOSITE7; |
487 | cx->av_state.aud_input = CX18_AV_AUDIO8; | 488 | cx->av_state.aud_input = CX18_AV_AUDIO8; |
488 | cx->av_state.audclk_freq = 48000; | 489 | cx->av_state.audclk_freq = 48000; |
489 | cx->av_state.audmode = V4L2_TUNER_MODE_LANG1; | 490 | cx->av_state.audmode = V4L2_TUNER_MODE_LANG1; |
490 | cx->av_state.vbi_line_offset = 8; | 491 | cx->av_state.vbi_line_offset = 8; |
491 | } | 492 | } |
492 | 493 | ||
493 | static int cx18_setup_pci(struct cx18 *cx, struct pci_dev *dev, | 494 | static int cx18_setup_pci(struct cx18 *cx, struct pci_dev *dev, |
494 | const struct pci_device_id *pci_id) | 495 | const struct pci_device_id *pci_id) |
495 | { | 496 | { |
496 | u16 cmd; | 497 | u16 cmd; |
497 | unsigned char pci_latency; | 498 | unsigned char pci_latency; |
498 | 499 | ||
499 | CX18_DEBUG_INFO("Enabling pci device\n"); | 500 | CX18_DEBUG_INFO("Enabling pci device\n"); |
500 | 501 | ||
501 | if (pci_enable_device(dev)) { | 502 | if (pci_enable_device(dev)) { |
502 | CX18_ERR("Can't enable device %d!\n", cx->num); | 503 | CX18_ERR("Can't enable device %d!\n", cx->num); |
503 | return -EIO; | 504 | return -EIO; |
504 | } | 505 | } |
505 | if (pci_set_dma_mask(dev, 0xffffffff)) { | 506 | if (pci_set_dma_mask(dev, 0xffffffff)) { |
506 | CX18_ERR("No suitable DMA available on card %d.\n", cx->num); | 507 | CX18_ERR("No suitable DMA available on card %d.\n", cx->num); |
507 | return -EIO; | 508 | return -EIO; |
508 | } | 509 | } |
509 | if (!request_mem_region(cx->base_addr, CX18_MEM_SIZE, "cx18 encoder")) { | 510 | if (!request_mem_region(cx->base_addr, CX18_MEM_SIZE, "cx18 encoder")) { |
510 | CX18_ERR("Cannot request encoder memory region on card %d.\n", cx->num); | 511 | CX18_ERR("Cannot request encoder memory region on card %d.\n", cx->num); |
511 | return -EIO; | 512 | return -EIO; |
512 | } | 513 | } |
513 | 514 | ||
514 | /* Enable bus mastering and memory mapped IO for the CX23418 */ | 515 | /* Enable bus mastering and memory mapped IO for the CX23418 */ |
515 | pci_read_config_word(dev, PCI_COMMAND, &cmd); | 516 | pci_read_config_word(dev, PCI_COMMAND, &cmd); |
516 | cmd |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; | 517 | cmd |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; |
517 | pci_write_config_word(dev, PCI_COMMAND, cmd); | 518 | pci_write_config_word(dev, PCI_COMMAND, cmd); |
518 | 519 | ||
519 | pci_read_config_byte(dev, PCI_CLASS_REVISION, &cx->card_rev); | 520 | pci_read_config_byte(dev, PCI_CLASS_REVISION, &cx->card_rev); |
520 | pci_read_config_byte(dev, PCI_LATENCY_TIMER, &pci_latency); | 521 | pci_read_config_byte(dev, PCI_LATENCY_TIMER, &pci_latency); |
521 | 522 | ||
522 | if (pci_latency < 64 && cx18_pci_latency) { | 523 | if (pci_latency < 64 && cx18_pci_latency) { |
523 | CX18_INFO("Unreasonably low latency timer, " | 524 | CX18_INFO("Unreasonably low latency timer, " |
524 | "setting to 64 (was %d)\n", pci_latency); | 525 | "setting to 64 (was %d)\n", pci_latency); |
525 | pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64); | 526 | pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64); |
526 | pci_read_config_byte(dev, PCI_LATENCY_TIMER, &pci_latency); | 527 | pci_read_config_byte(dev, PCI_LATENCY_TIMER, &pci_latency); |
527 | } | 528 | } |
528 | 529 | ||
529 | CX18_DEBUG_INFO("cx%d (rev %d) at %02x:%02x.%x, " | 530 | CX18_DEBUG_INFO("cx%d (rev %d) at %02x:%02x.%x, " |
530 | "irq: %d, latency: %d, memory: 0x%lx\n", | 531 | "irq: %d, latency: %d, memory: 0x%lx\n", |
531 | cx->dev->device, cx->card_rev, dev->bus->number, | 532 | cx->dev->device, cx->card_rev, dev->bus->number, |
532 | PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), | 533 | PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), |
533 | cx->dev->irq, pci_latency, (unsigned long)cx->base_addr); | 534 | cx->dev->irq, pci_latency, (unsigned long)cx->base_addr); |
534 | 535 | ||
535 | return 0; | 536 | return 0; |
536 | } | 537 | } |
537 | 538 | ||
538 | #ifdef MODULE | 539 | #ifdef MODULE |
539 | static u32 cx18_request_module(struct cx18 *cx, u32 hw, | 540 | static u32 cx18_request_module(struct cx18 *cx, u32 hw, |
540 | const char *name, u32 id) | 541 | const char *name, u32 id) |
541 | { | 542 | { |
542 | if ((hw & id) == 0) | 543 | if ((hw & id) == 0) |
543 | return hw; | 544 | return hw; |
544 | if (request_module(name) != 0) { | 545 | if (request_module(name) != 0) { |
545 | CX18_ERR("Failed to load module %s\n", name); | 546 | CX18_ERR("Failed to load module %s\n", name); |
546 | return hw & ~id; | 547 | return hw & ~id; |
547 | } | 548 | } |
548 | CX18_DEBUG_INFO("Loaded module %s\n", name); | 549 | CX18_DEBUG_INFO("Loaded module %s\n", name); |
549 | return hw; | 550 | return hw; |
550 | } | 551 | } |
551 | #endif | 552 | #endif |
552 | 553 | ||
553 | static void cx18_load_and_init_modules(struct cx18 *cx) | 554 | static void cx18_load_and_init_modules(struct cx18 *cx) |
554 | { | 555 | { |
555 | u32 hw = cx->card->hw_all; | 556 | u32 hw = cx->card->hw_all; |
556 | int i; | 557 | int i; |
557 | 558 | ||
558 | #ifdef MODULE | 559 | #ifdef MODULE |
559 | /* load modules */ | 560 | /* load modules */ |
560 | #ifndef CONFIG_MEDIA_TUNER | 561 | #ifndef CONFIG_MEDIA_TUNER |
561 | hw = cx18_request_module(cx, hw, "tuner", CX18_HW_TUNER); | 562 | hw = cx18_request_module(cx, hw, "tuner", CX18_HW_TUNER); |
562 | #endif | 563 | #endif |
563 | #ifndef CONFIG_VIDEO_CS5345 | 564 | #ifndef CONFIG_VIDEO_CS5345 |
564 | hw = cx18_request_module(cx, hw, "cs5345", CX18_HW_CS5345); | 565 | hw = cx18_request_module(cx, hw, "cs5345", CX18_HW_CS5345); |
565 | #endif | 566 | #endif |
566 | #endif | 567 | #endif |
567 | 568 | ||
568 | /* check which i2c devices are actually found */ | 569 | /* check which i2c devices are actually found */ |
569 | for (i = 0; i < 32; i++) { | 570 | for (i = 0; i < 32; i++) { |
570 | u32 device = 1 << i; | 571 | u32 device = 1 << i; |
571 | 572 | ||
572 | if (!(device & hw)) | 573 | if (!(device & hw)) |
573 | continue; | 574 | continue; |
574 | if (device == CX18_HW_GPIO || device == CX18_HW_TVEEPROM || | 575 | if (device == CX18_HW_GPIO || device == CX18_HW_TVEEPROM || |
575 | device == CX18_HW_CX23418 || device == CX18_HW_DVB) { | 576 | device == CX18_HW_CX23418 || device == CX18_HW_DVB) { |
576 | /* These 'devices' do not use i2c probing */ | 577 | /* These 'devices' do not use i2c probing */ |
577 | cx->hw_flags |= device; | 578 | cx->hw_flags |= device; |
578 | continue; | 579 | continue; |
579 | } | 580 | } |
580 | cx18_i2c_register(cx, i); | 581 | cx18_i2c_register(cx, i); |
581 | if (cx18_i2c_hw_addr(cx, device) > 0) | 582 | if (cx18_i2c_hw_addr(cx, device) > 0) |
582 | cx->hw_flags |= device; | 583 | cx->hw_flags |= device; |
583 | } | 584 | } |
584 | 585 | ||
585 | hw = cx->hw_flags; | 586 | hw = cx->hw_flags; |
586 | } | 587 | } |
587 | 588 | ||
588 | static int __devinit cx18_probe(struct pci_dev *dev, | 589 | static int __devinit cx18_probe(struct pci_dev *dev, |
589 | const struct pci_device_id *pci_id) | 590 | const struct pci_device_id *pci_id) |
590 | { | 591 | { |
591 | int retval = 0; | 592 | int retval = 0; |
592 | int vbi_buf_size; | 593 | int vbi_buf_size; |
593 | u32 devtype; | 594 | u32 devtype; |
594 | struct cx18 *cx; | 595 | struct cx18 *cx; |
595 | 596 | ||
596 | spin_lock(&cx18_cards_lock); | 597 | spin_lock(&cx18_cards_lock); |
597 | 598 | ||
598 | /* Make sure we've got a place for this card */ | 599 | /* Make sure we've got a place for this card */ |
599 | if (cx18_cards_active == CX18_MAX_CARDS) { | 600 | if (cx18_cards_active == CX18_MAX_CARDS) { |
600 | printk(KERN_ERR "cx18: Maximum number of cards detected (%d).\n", | 601 | printk(KERN_ERR "cx18: Maximum number of cards detected (%d).\n", |
601 | cx18_cards_active); | 602 | cx18_cards_active); |
602 | spin_unlock(&cx18_cards_lock); | 603 | spin_unlock(&cx18_cards_lock); |
603 | return -ENOMEM; | 604 | return -ENOMEM; |
604 | } | 605 | } |
605 | 606 | ||
606 | cx = kzalloc(sizeof(struct cx18), GFP_ATOMIC); | 607 | cx = kzalloc(sizeof(struct cx18), GFP_ATOMIC); |
607 | if (!cx) { | 608 | if (!cx) { |
608 | spin_unlock(&cx18_cards_lock); | 609 | spin_unlock(&cx18_cards_lock); |
609 | return -ENOMEM; | 610 | return -ENOMEM; |
610 | } | 611 | } |
611 | cx18_cards[cx18_cards_active] = cx; | 612 | cx18_cards[cx18_cards_active] = cx; |
612 | cx->dev = dev; | 613 | cx->dev = dev; |
613 | cx->num = cx18_cards_active++; | 614 | cx->num = cx18_cards_active++; |
614 | snprintf(cx->name, sizeof(cx->name), "cx18-%d", cx->num); | 615 | snprintf(cx->name, sizeof(cx->name), "cx18-%d", cx->num); |
615 | CX18_INFO("Initializing card #%d\n", cx->num); | 616 | CX18_INFO("Initializing card #%d\n", cx->num); |
616 | 617 | ||
617 | spin_unlock(&cx18_cards_lock); | 618 | spin_unlock(&cx18_cards_lock); |
618 | 619 | ||
619 | cx18_process_options(cx); | 620 | cx18_process_options(cx); |
620 | if (cx->options.cardtype == -1) { | 621 | if (cx->options.cardtype == -1) { |
621 | retval = -ENODEV; | 622 | retval = -ENODEV; |
622 | goto err; | 623 | goto err; |
623 | } | 624 | } |
624 | if (cx18_init_struct1(cx)) { | 625 | if (cx18_init_struct1(cx)) { |
625 | retval = -ENOMEM; | 626 | retval = -ENOMEM; |
626 | goto err; | 627 | goto err; |
627 | } | 628 | } |
628 | 629 | ||
629 | CX18_DEBUG_INFO("base addr: 0x%08x\n", cx->base_addr); | 630 | CX18_DEBUG_INFO("base addr: 0x%08x\n", cx->base_addr); |
630 | 631 | ||
631 | /* PCI Device Setup */ | 632 | /* PCI Device Setup */ |
632 | retval = cx18_setup_pci(cx, dev, pci_id); | 633 | retval = cx18_setup_pci(cx, dev, pci_id); |
633 | if (retval != 0) { | 634 | if (retval != 0) { |
634 | if (retval == -EIO) | 635 | if (retval == -EIO) |
635 | goto free_workqueue; | 636 | goto free_workqueue; |
636 | else if (retval == -ENXIO) | 637 | else if (retval == -ENXIO) |
637 | goto free_mem; | 638 | goto free_mem; |
638 | } | 639 | } |
639 | /* save cx in the pci struct for later use */ | 640 | /* save cx in the pci struct for later use */ |
640 | pci_set_drvdata(dev, cx); | 641 | pci_set_drvdata(dev, cx); |
641 | 642 | ||
642 | /* map io memory */ | 643 | /* map io memory */ |
643 | CX18_DEBUG_INFO("attempting ioremap at 0x%08x len 0x%08x\n", | 644 | CX18_DEBUG_INFO("attempting ioremap at 0x%08x len 0x%08x\n", |
644 | cx->base_addr + CX18_MEM_OFFSET, CX18_MEM_SIZE); | 645 | cx->base_addr + CX18_MEM_OFFSET, CX18_MEM_SIZE); |
645 | cx->enc_mem = ioremap_nocache(cx->base_addr + CX18_MEM_OFFSET, | 646 | cx->enc_mem = ioremap_nocache(cx->base_addr + CX18_MEM_OFFSET, |
646 | CX18_MEM_SIZE); | 647 | CX18_MEM_SIZE); |
647 | if (!cx->enc_mem) { | 648 | if (!cx->enc_mem) { |
648 | CX18_ERR("ioremap failed, perhaps increasing __VMALLOC_RESERVE in page.h\n"); | 649 | CX18_ERR("ioremap failed, perhaps increasing __VMALLOC_RESERVE in page.h\n"); |
649 | CX18_ERR("or disabling CONFIG_HIGHMEM4G into the kernel would help\n"); | 650 | CX18_ERR("or disabling CONFIG_HIGHMEM4G into the kernel would help\n"); |
650 | retval = -ENOMEM; | 651 | retval = -ENOMEM; |
651 | goto free_mem; | 652 | goto free_mem; |
652 | } | 653 | } |
653 | cx->reg_mem = cx->enc_mem + CX18_REG_OFFSET; | 654 | cx->reg_mem = cx->enc_mem + CX18_REG_OFFSET; |
654 | devtype = read_reg(0xC72028); | 655 | devtype = cx18_read_reg(cx, 0xC72028); |
655 | switch (devtype & 0xff000000) { | 656 | switch (devtype & 0xff000000) { |
656 | case 0xff000000: | 657 | case 0xff000000: |
657 | CX18_INFO("cx23418 revision %08x (A)\n", devtype); | 658 | CX18_INFO("cx23418 revision %08x (A)\n", devtype); |
658 | break; | 659 | break; |
659 | case 0x01000000: | 660 | case 0x01000000: |
660 | CX18_INFO("cx23418 revision %08x (B)\n", devtype); | 661 | CX18_INFO("cx23418 revision %08x (B)\n", devtype); |
661 | break; | 662 | break; |
662 | default: | 663 | default: |
663 | CX18_INFO("cx23418 revision %08x (Unknown)\n", devtype); | 664 | CX18_INFO("cx23418 revision %08x (Unknown)\n", devtype); |
664 | break; | 665 | break; |
665 | } | 666 | } |
666 | 667 | ||
667 | cx18_init_power(cx, 1); | 668 | cx18_init_power(cx, 1); |
668 | cx18_init_memory(cx); | 669 | cx18_init_memory(cx); |
669 | 670 | ||
670 | cx->scb = (struct cx18_scb __iomem *)(cx->enc_mem + SCB_OFFSET); | 671 | cx->scb = (struct cx18_scb __iomem *)(cx->enc_mem + SCB_OFFSET); |
671 | cx18_init_scb(cx); | 672 | cx18_init_scb(cx); |
672 | 673 | ||
673 | cx18_gpio_init(cx); | 674 | cx18_gpio_init(cx); |
674 | 675 | ||
675 | /* active i2c */ | 676 | /* active i2c */ |
676 | CX18_DEBUG_INFO("activating i2c...\n"); | 677 | CX18_DEBUG_INFO("activating i2c...\n"); |
677 | if (init_cx18_i2c(cx)) { | 678 | if (init_cx18_i2c(cx)) { |
678 | CX18_ERR("Could not initialize i2c\n"); | 679 | CX18_ERR("Could not initialize i2c\n"); |
679 | goto free_map; | 680 | goto free_map; |
680 | } | 681 | } |
681 | 682 | ||
682 | CX18_DEBUG_INFO("Active card count: %d.\n", cx18_cards_active); | 683 | CX18_DEBUG_INFO("Active card count: %d.\n", cx18_cards_active); |
683 | 684 | ||
684 | if (cx->card->hw_all & CX18_HW_TVEEPROM) { | 685 | if (cx->card->hw_all & CX18_HW_TVEEPROM) { |
685 | /* Based on the model number the cardtype may be changed. | 686 | /* Based on the model number the cardtype may be changed. |
686 | The PCI IDs are not always reliable. */ | 687 | The PCI IDs are not always reliable. */ |
687 | cx18_process_eeprom(cx); | 688 | cx18_process_eeprom(cx); |
688 | } | 689 | } |
689 | if (cx->card->comment) | 690 | if (cx->card->comment) |
690 | CX18_INFO("%s", cx->card->comment); | 691 | CX18_INFO("%s", cx->card->comment); |
691 | if (cx->card->v4l2_capabilities == 0) { | 692 | if (cx->card->v4l2_capabilities == 0) { |
692 | retval = -ENODEV; | 693 | retval = -ENODEV; |
693 | goto free_i2c; | 694 | goto free_i2c; |
694 | } | 695 | } |
695 | cx18_init_memory(cx); | 696 | cx18_init_memory(cx); |
696 | 697 | ||
697 | /* Register IRQ */ | 698 | /* Register IRQ */ |
698 | retval = request_irq(cx->dev->irq, cx18_irq_handler, | 699 | retval = request_irq(cx->dev->irq, cx18_irq_handler, |
699 | IRQF_SHARED | IRQF_DISABLED, cx->name, (void *)cx); | 700 | IRQF_SHARED | IRQF_DISABLED, cx->name, (void *)cx); |
700 | if (retval) { | 701 | if (retval) { |
701 | CX18_ERR("Failed to register irq %d\n", retval); | 702 | CX18_ERR("Failed to register irq %d\n", retval); |
702 | goto free_i2c; | 703 | goto free_i2c; |
703 | } | 704 | } |
704 | 705 | ||
705 | if (cx->std == 0) | 706 | if (cx->std == 0) |
706 | cx->std = V4L2_STD_NTSC_M; | 707 | cx->std = V4L2_STD_NTSC_M; |
707 | 708 | ||
708 | if (cx->options.tuner == -1) { | 709 | if (cx->options.tuner == -1) { |
709 | int i; | 710 | int i; |
710 | 711 | ||
711 | for (i = 0; i < CX18_CARD_MAX_TUNERS; i++) { | 712 | for (i = 0; i < CX18_CARD_MAX_TUNERS; i++) { |
712 | if ((cx->std & cx->card->tuners[i].std) == 0) | 713 | if ((cx->std & cx->card->tuners[i].std) == 0) |
713 | continue; | 714 | continue; |
714 | cx->options.tuner = cx->card->tuners[i].tuner; | 715 | cx->options.tuner = cx->card->tuners[i].tuner; |
715 | break; | 716 | break; |
716 | } | 717 | } |
717 | } | 718 | } |
718 | /* if no tuner was found, then pick the first tuner in the card list */ | 719 | /* if no tuner was found, then pick the first tuner in the card list */ |
719 | if (cx->options.tuner == -1 && cx->card->tuners[0].std) { | 720 | if (cx->options.tuner == -1 && cx->card->tuners[0].std) { |
720 | cx->std = cx->card->tuners[0].std; | 721 | cx->std = cx->card->tuners[0].std; |
721 | if (cx->std & V4L2_STD_PAL) | 722 | if (cx->std & V4L2_STD_PAL) |
722 | cx->std = V4L2_STD_PAL_BG | V4L2_STD_PAL_H; | 723 | cx->std = V4L2_STD_PAL_BG | V4L2_STD_PAL_H; |
723 | else if (cx->std & V4L2_STD_NTSC) | 724 | else if (cx->std & V4L2_STD_NTSC) |
724 | cx->std = V4L2_STD_NTSC_M; | 725 | cx->std = V4L2_STD_NTSC_M; |
725 | else if (cx->std & V4L2_STD_SECAM) | 726 | else if (cx->std & V4L2_STD_SECAM) |
726 | cx->std = V4L2_STD_SECAM_L; | 727 | cx->std = V4L2_STD_SECAM_L; |
727 | cx->options.tuner = cx->card->tuners[0].tuner; | 728 | cx->options.tuner = cx->card->tuners[0].tuner; |
728 | } | 729 | } |
729 | if (cx->options.radio == -1) | 730 | if (cx->options.radio == -1) |
730 | cx->options.radio = (cx->card->radio_input.audio_type != 0); | 731 | cx->options.radio = (cx->card->radio_input.audio_type != 0); |
731 | 732 | ||
732 | /* The card is now fully identified, continue with card-specific | 733 | /* The card is now fully identified, continue with card-specific |
733 | initialization. */ | 734 | initialization. */ |
734 | cx18_init_struct2(cx); | 735 | cx18_init_struct2(cx); |
735 | 736 | ||
736 | cx18_load_and_init_modules(cx); | 737 | cx18_load_and_init_modules(cx); |
737 | 738 | ||
738 | if (cx->std & V4L2_STD_525_60) { | 739 | if (cx->std & V4L2_STD_525_60) { |
739 | cx->is_60hz = 1; | 740 | cx->is_60hz = 1; |
740 | cx->is_out_60hz = 1; | 741 | cx->is_out_60hz = 1; |
741 | } else { | 742 | } else { |
742 | cx->is_50hz = 1; | 743 | cx->is_50hz = 1; |
743 | cx->is_out_50hz = 1; | 744 | cx->is_out_50hz = 1; |
744 | } | 745 | } |
745 | cx->params.video_gop_size = cx->is_60hz ? 15 : 12; | 746 | cx->params.video_gop_size = cx->is_60hz ? 15 : 12; |
746 | 747 | ||
747 | cx->stream_buf_size[CX18_ENC_STREAM_TYPE_MPG] = 0x08000; | 748 | cx->stream_buf_size[CX18_ENC_STREAM_TYPE_MPG] = 0x08000; |
748 | cx->stream_buf_size[CX18_ENC_STREAM_TYPE_TS] = 0x08000; | 749 | cx->stream_buf_size[CX18_ENC_STREAM_TYPE_TS] = 0x08000; |
749 | cx->stream_buf_size[CX18_ENC_STREAM_TYPE_PCM] = 0x01200; | 750 | cx->stream_buf_size[CX18_ENC_STREAM_TYPE_PCM] = 0x01200; |
750 | cx->stream_buf_size[CX18_ENC_STREAM_TYPE_YUV] = 0x20000; | 751 | cx->stream_buf_size[CX18_ENC_STREAM_TYPE_YUV] = 0x20000; |
751 | vbi_buf_size = cx->vbi.raw_size * (cx->is_60hz ? 24 : 36) / 2; | 752 | vbi_buf_size = cx->vbi.raw_size * (cx->is_60hz ? 24 : 36) / 2; |
752 | cx->stream_buf_size[CX18_ENC_STREAM_TYPE_VBI] = vbi_buf_size; | 753 | cx->stream_buf_size[CX18_ENC_STREAM_TYPE_VBI] = vbi_buf_size; |
753 | 754 | ||
754 | if (cx->options.radio > 0) | 755 | if (cx->options.radio > 0) |
755 | cx->v4l2_cap |= V4L2_CAP_RADIO; | 756 | cx->v4l2_cap |= V4L2_CAP_RADIO; |
756 | 757 | ||
757 | if (cx->options.tuner > -1) { | 758 | if (cx->options.tuner > -1) { |
758 | struct tuner_setup setup; | 759 | struct tuner_setup setup; |
759 | 760 | ||
760 | setup.addr = ADDR_UNSET; | 761 | setup.addr = ADDR_UNSET; |
761 | setup.type = cx->options.tuner; | 762 | setup.type = cx->options.tuner; |
762 | setup.mode_mask = T_ANALOG_TV; /* matches TV tuners */ | 763 | setup.mode_mask = T_ANALOG_TV; /* matches TV tuners */ |
763 | setup.tuner_callback = (setup.type == TUNER_XC2028) ? | 764 | setup.tuner_callback = (setup.type == TUNER_XC2028) ? |
764 | cx18_reset_tuner_gpio : NULL; | 765 | cx18_reset_tuner_gpio : NULL; |
765 | cx18_call_i2c_clients(cx, TUNER_SET_TYPE_ADDR, &setup); | 766 | cx18_call_i2c_clients(cx, TUNER_SET_TYPE_ADDR, &setup); |
766 | if (setup.type == TUNER_XC2028) { | 767 | if (setup.type == TUNER_XC2028) { |
767 | static struct xc2028_ctrl ctrl = { | 768 | static struct xc2028_ctrl ctrl = { |
768 | .fname = XC2028_DEFAULT_FIRMWARE, | 769 | .fname = XC2028_DEFAULT_FIRMWARE, |
769 | .max_len = 64, | 770 | .max_len = 64, |
770 | }; | 771 | }; |
771 | struct v4l2_priv_tun_config cfg = { | 772 | struct v4l2_priv_tun_config cfg = { |
772 | .tuner = cx->options.tuner, | 773 | .tuner = cx->options.tuner, |
773 | .priv = &ctrl, | 774 | .priv = &ctrl, |
774 | }; | 775 | }; |
775 | cx18_call_i2c_clients(cx, TUNER_SET_CONFIG, &cfg); | 776 | cx18_call_i2c_clients(cx, TUNER_SET_CONFIG, &cfg); |
776 | } | 777 | } |
777 | } | 778 | } |
778 | 779 | ||
779 | /* The tuner is fixed to the standard. The other inputs (e.g. S-Video) | 780 | /* The tuner is fixed to the standard. The other inputs (e.g. S-Video) |
780 | are not. */ | 781 | are not. */ |
781 | cx->tuner_std = cx->std; | 782 | cx->tuner_std = cx->std; |
782 | 783 | ||
783 | retval = cx18_streams_setup(cx); | 784 | retval = cx18_streams_setup(cx); |
784 | if (retval) { | 785 | if (retval) { |
785 | CX18_ERR("Error %d setting up streams\n", retval); | 786 | CX18_ERR("Error %d setting up streams\n", retval); |
786 | goto free_irq; | 787 | goto free_irq; |
787 | } | 788 | } |
788 | retval = cx18_streams_register(cx); | 789 | retval = cx18_streams_register(cx); |
789 | if (retval) { | 790 | if (retval) { |
790 | CX18_ERR("Error %d registering devices\n", retval); | 791 | CX18_ERR("Error %d registering devices\n", retval); |
791 | goto free_streams; | 792 | goto free_streams; |
792 | } | 793 | } |
793 | 794 | ||
794 | CX18_INFO("Initialized card #%d: %s\n", cx->num, cx->card_name); | 795 | CX18_INFO("Initialized card #%d: %s\n", cx->num, cx->card_name); |
795 | 796 | ||
796 | return 0; | 797 | return 0; |
797 | 798 | ||
798 | free_streams: | 799 | free_streams: |
799 | cx18_streams_cleanup(cx, 1); | 800 | cx18_streams_cleanup(cx, 1); |
800 | free_irq: | 801 | free_irq: |
801 | free_irq(cx->dev->irq, (void *)cx); | 802 | free_irq(cx->dev->irq, (void *)cx); |
802 | free_i2c: | 803 | free_i2c: |
803 | exit_cx18_i2c(cx); | 804 | exit_cx18_i2c(cx); |
804 | free_map: | 805 | free_map: |
805 | cx18_iounmap(cx); | 806 | cx18_iounmap(cx); |
806 | free_mem: | 807 | free_mem: |
807 | release_mem_region(cx->base_addr, CX18_MEM_SIZE); | 808 | release_mem_region(cx->base_addr, CX18_MEM_SIZE); |
808 | free_workqueue: | 809 | free_workqueue: |
809 | err: | 810 | err: |
810 | if (retval == 0) | 811 | if (retval == 0) |
811 | retval = -ENODEV; | 812 | retval = -ENODEV; |
812 | CX18_ERR("Error %d on initialization\n", retval); | 813 | CX18_ERR("Error %d on initialization\n", retval); |
813 | 814 | ||
814 | kfree(cx18_cards[cx18_cards_active]); | 815 | kfree(cx18_cards[cx18_cards_active]); |
815 | cx18_cards[cx18_cards_active] = NULL; | 816 | cx18_cards[cx18_cards_active] = NULL; |
816 | return retval; | 817 | return retval; |
817 | } | 818 | } |
818 | 819 | ||
819 | int cx18_init_on_first_open(struct cx18 *cx) | 820 | int cx18_init_on_first_open(struct cx18 *cx) |
820 | { | 821 | { |
821 | int video_input; | 822 | int video_input; |
822 | int fw_retry_count = 3; | 823 | int fw_retry_count = 3; |
823 | struct v4l2_frequency vf; | 824 | struct v4l2_frequency vf; |
824 | struct cx18_open_id fh; | 825 | struct cx18_open_id fh; |
825 | 826 | ||
826 | fh.cx = cx; | 827 | fh.cx = cx; |
827 | 828 | ||
828 | if (test_bit(CX18_F_I_FAILED, &cx->i_flags)) | 829 | if (test_bit(CX18_F_I_FAILED, &cx->i_flags)) |
829 | return -ENXIO; | 830 | return -ENXIO; |
830 | 831 | ||
831 | if (test_and_set_bit(CX18_F_I_INITED, &cx->i_flags)) | 832 | if (test_and_set_bit(CX18_F_I_INITED, &cx->i_flags)) |
832 | return 0; | 833 | return 0; |
833 | 834 | ||
834 | while (--fw_retry_count > 0) { | 835 | while (--fw_retry_count > 0) { |
835 | /* load firmware */ | 836 | /* load firmware */ |
836 | if (cx18_firmware_init(cx) == 0) | 837 | if (cx18_firmware_init(cx) == 0) |
837 | break; | 838 | break; |
838 | if (fw_retry_count > 1) | 839 | if (fw_retry_count > 1) |
839 | CX18_WARN("Retry loading firmware\n"); | 840 | CX18_WARN("Retry loading firmware\n"); |
840 | } | 841 | } |
841 | 842 | ||
842 | if (fw_retry_count == 0) { | 843 | if (fw_retry_count == 0) { |
843 | set_bit(CX18_F_I_FAILED, &cx->i_flags); | 844 | set_bit(CX18_F_I_FAILED, &cx->i_flags); |
844 | return -ENXIO; | 845 | return -ENXIO; |
845 | } | 846 | } |
846 | set_bit(CX18_F_I_LOADED_FW, &cx->i_flags); | 847 | set_bit(CX18_F_I_LOADED_FW, &cx->i_flags); |
847 | 848 | ||
848 | /* Init the firmware twice to work around a silicon bug | 849 | /* Init the firmware twice to work around a silicon bug |
849 | * transport related. */ | 850 | * transport related. */ |
850 | 851 | ||
851 | fw_retry_count = 3; | 852 | fw_retry_count = 3; |
852 | while (--fw_retry_count > 0) { | 853 | while (--fw_retry_count > 0) { |
853 | /* load firmware */ | 854 | /* load firmware */ |
854 | if (cx18_firmware_init(cx) == 0) | 855 | if (cx18_firmware_init(cx) == 0) |
855 | break; | 856 | break; |
856 | if (fw_retry_count > 1) | 857 | if (fw_retry_count > 1) |
857 | CX18_WARN("Retry loading firmware\n"); | 858 | CX18_WARN("Retry loading firmware\n"); |
858 | } | 859 | } |
859 | 860 | ||
860 | if (fw_retry_count == 0) { | 861 | if (fw_retry_count == 0) { |
861 | set_bit(CX18_F_I_FAILED, &cx->i_flags); | 862 | set_bit(CX18_F_I_FAILED, &cx->i_flags); |
862 | return -ENXIO; | 863 | return -ENXIO; |
863 | } | 864 | } |
864 | 865 | ||
865 | vf.tuner = 0; | 866 | vf.tuner = 0; |
866 | vf.type = V4L2_TUNER_ANALOG_TV; | 867 | vf.type = V4L2_TUNER_ANALOG_TV; |
867 | vf.frequency = 6400; /* the tuner 'baseline' frequency */ | 868 | vf.frequency = 6400; /* the tuner 'baseline' frequency */ |
868 | 869 | ||
869 | /* Set initial frequency. For PAL/SECAM broadcasts no | 870 | /* Set initial frequency. For PAL/SECAM broadcasts no |
870 | 'default' channel exists AFAIK. */ | 871 | 'default' channel exists AFAIK. */ |
871 | if (cx->std == V4L2_STD_NTSC_M_JP) | 872 | if (cx->std == V4L2_STD_NTSC_M_JP) |
872 | vf.frequency = 1460; /* ch. 1 91250*16/1000 */ | 873 | vf.frequency = 1460; /* ch. 1 91250*16/1000 */ |
873 | else if (cx->std & V4L2_STD_NTSC_M) | 874 | else if (cx->std & V4L2_STD_NTSC_M) |
874 | vf.frequency = 1076; /* ch. 4 67250*16/1000 */ | 875 | vf.frequency = 1076; /* ch. 4 67250*16/1000 */ |
875 | 876 | ||
876 | video_input = cx->active_input; | 877 | video_input = cx->active_input; |
877 | cx->active_input++; /* Force update of input */ | 878 | cx->active_input++; /* Force update of input */ |
878 | cx18_s_input(NULL, &fh, video_input); | 879 | cx18_s_input(NULL, &fh, video_input); |
879 | 880 | ||
880 | /* Let the VIDIOC_S_STD ioctl do all the work, keeps the code | 881 | /* Let the VIDIOC_S_STD ioctl do all the work, keeps the code |
881 | in one place. */ | 882 | in one place. */ |
882 | cx->std++; /* Force full standard initialization */ | 883 | cx->std++; /* Force full standard initialization */ |
883 | cx18_s_std(NULL, &fh, &cx->tuner_std); | 884 | cx18_s_std(NULL, &fh, &cx->tuner_std); |
884 | cx18_s_frequency(NULL, &fh, &vf); | 885 | cx18_s_frequency(NULL, &fh, &vf); |
885 | return 0; | 886 | return 0; |
886 | } | 887 | } |
887 | 888 | ||
888 | static void cx18_remove(struct pci_dev *pci_dev) | 889 | static void cx18_remove(struct pci_dev *pci_dev) |
889 | { | 890 | { |
890 | struct cx18 *cx = pci_get_drvdata(pci_dev); | 891 | struct cx18 *cx = pci_get_drvdata(pci_dev); |
891 | 892 | ||
892 | CX18_DEBUG_INFO("Removing Card #%d\n", cx->num); | 893 | CX18_DEBUG_INFO("Removing Card #%d\n", cx->num); |
893 | 894 | ||
894 | /* Stop all captures */ | 895 | /* Stop all captures */ |
895 | CX18_DEBUG_INFO("Stopping all streams\n"); | 896 | CX18_DEBUG_INFO("Stopping all streams\n"); |
896 | if (atomic_read(&cx->tot_capturing) > 0) | 897 | if (atomic_read(&cx->tot_capturing) > 0) |
897 | cx18_stop_all_captures(cx); | 898 | cx18_stop_all_captures(cx); |
898 | 899 | ||
899 | /* Interrupts */ | 900 | /* Interrupts */ |
900 | sw1_irq_disable(IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU); | 901 | cx18_sw1_irq_disable(cx, IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU); |
901 | sw2_irq_disable(IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK); | 902 | cx18_sw2_irq_disable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK); |
902 | 903 | ||
903 | cx18_halt_firmware(cx); | 904 | cx18_halt_firmware(cx); |
904 | 905 | ||
905 | cx18_streams_cleanup(cx, 1); | 906 | cx18_streams_cleanup(cx, 1); |
906 | 907 | ||
907 | exit_cx18_i2c(cx); | 908 | exit_cx18_i2c(cx); |
908 | 909 | ||
909 | free_irq(cx->dev->irq, (void *)cx); | 910 | free_irq(cx->dev->irq, (void *)cx); |
910 | 911 | ||
911 | cx18_iounmap(cx); | 912 | cx18_iounmap(cx); |
912 | 913 | ||
913 | release_mem_region(cx->base_addr, CX18_MEM_SIZE); | 914 | release_mem_region(cx->base_addr, CX18_MEM_SIZE); |
914 | 915 | ||
915 | pci_disable_device(cx->dev); | 916 | pci_disable_device(cx->dev); |
916 | 917 | ||
917 | CX18_INFO("Removed %s, card #%d\n", cx->card_name, cx->num); | 918 | CX18_INFO("Removed %s, card #%d\n", cx->card_name, cx->num); |
918 | } | 919 | } |
919 | 920 | ||
920 | /* define a pci_driver for card detection */ | 921 | /* define a pci_driver for card detection */ |
921 | static struct pci_driver cx18_pci_driver = { | 922 | static struct pci_driver cx18_pci_driver = { |
922 | .name = "cx18", | 923 | .name = "cx18", |
923 | .id_table = cx18_pci_tbl, | 924 | .id_table = cx18_pci_tbl, |
924 | .probe = cx18_probe, | 925 | .probe = cx18_probe, |
925 | .remove = cx18_remove, | 926 | .remove = cx18_remove, |
926 | }; | 927 | }; |
927 | 928 | ||
928 | static int module_start(void) | 929 | static int module_start(void) |
929 | { | 930 | { |
930 | printk(KERN_INFO "cx18: Start initialization, version %s\n", CX18_VERSION); | 931 | printk(KERN_INFO "cx18: Start initialization, version %s\n", CX18_VERSION); |
931 | 932 | ||
932 | memset(cx18_cards, 0, sizeof(cx18_cards)); | 933 | memset(cx18_cards, 0, sizeof(cx18_cards)); |
933 | 934 | ||
934 | /* Validate parameters */ | 935 | /* Validate parameters */ |
935 | if (cx18_first_minor < 0 || cx18_first_minor >= CX18_MAX_CARDS) { | 936 | if (cx18_first_minor < 0 || cx18_first_minor >= CX18_MAX_CARDS) { |
936 | printk(KERN_ERR "cx18: Exiting, ivtv_first_minor must be between 0 and %d\n", | 937 | printk(KERN_ERR "cx18: Exiting, ivtv_first_minor must be between 0 and %d\n", |
937 | CX18_MAX_CARDS - 1); | 938 | CX18_MAX_CARDS - 1); |
938 | return -1; | 939 | return -1; |
939 | } | 940 | } |
940 | 941 | ||
941 | if (cx18_debug < 0 || cx18_debug > 511) { | 942 | if (cx18_debug < 0 || cx18_debug > 511) { |
942 | cx18_debug = 0; | 943 | cx18_debug = 0; |
943 | printk(KERN_INFO "cx18: Debug value must be >= 0 and <= 511!\n"); | 944 | printk(KERN_INFO "cx18: Debug value must be >= 0 and <= 511!\n"); |
944 | } | 945 | } |
945 | 946 | ||
946 | if (pci_register_driver(&cx18_pci_driver)) { | 947 | if (pci_register_driver(&cx18_pci_driver)) { |
947 | printk(KERN_ERR "cx18: Error detecting PCI card\n"); | 948 | printk(KERN_ERR "cx18: Error detecting PCI card\n"); |
948 | return -ENODEV; | 949 | return -ENODEV; |
949 | } | 950 | } |
950 | printk(KERN_INFO "cx18: End initialization\n"); | 951 | printk(KERN_INFO "cx18: End initialization\n"); |
951 | return 0; | 952 | return 0; |
952 | } | 953 | } |
953 | 954 | ||
954 | static void module_cleanup(void) | 955 | static void module_cleanup(void) |
955 | { | 956 | { |
956 | int i; | 957 | int i; |
957 | 958 | ||
958 | pci_unregister_driver(&cx18_pci_driver); | 959 | pci_unregister_driver(&cx18_pci_driver); |
959 | 960 | ||
960 | for (i = 0; i < cx18_cards_active; i++) { | 961 | for (i = 0; i < cx18_cards_active; i++) { |
961 | if (cx18_cards[i] == NULL) | 962 | if (cx18_cards[i] == NULL) |
962 | continue; | 963 | continue; |
963 | kfree(cx18_cards[i]); | 964 | kfree(cx18_cards[i]); |
964 | } | 965 | } |
965 | } | 966 | } |
966 | 967 | ||
967 | module_init(module_start); | 968 | module_init(module_start); |
968 | module_exit(module_cleanup); | 969 | module_exit(module_cleanup); |
969 | 970 |
drivers/media/video/cx18/cx18-driver.h
1 | /* | 1 | /* |
2 | * cx18 driver internal defines and structures | 2 | * cx18 driver internal defines and structures |
3 | * | 3 | * |
4 | * Derived from ivtv-driver.h | 4 | * Derived from ivtv-driver.h |
5 | * | 5 | * |
6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | 6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> |
7 | * | 7 | * |
8 | * This program is free software; you can redistribute it and/or modify | 8 | * This program is free software; you can redistribute it and/or modify |
9 | * it under the terms of the GNU General Public License as published by | 9 | * it under the terms of the GNU General Public License as published by |
10 | * the Free Software Foundation; either version 2 of the License, or | 10 | * the Free Software Foundation; either version 2 of the License, or |
11 | * (at your option) any later version. | 11 | * (at your option) any later version. |
12 | * | 12 | * |
13 | * This program is distributed in the hope that it will be useful, | 13 | * This program is distributed in the hope that it will be useful, |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | * GNU General Public License for more details. | 16 | * GNU General Public License for more details. |
17 | * | 17 | * |
18 | * You should have received a copy of the GNU General Public License | 18 | * You should have received a copy of the GNU General Public License |
19 | * along with this program; if not, write to the Free Software | 19 | * along with this program; if not, write to the Free Software |
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | 20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
21 | * 02111-1307 USA | 21 | * 02111-1307 USA |
22 | */ | 22 | */ |
23 | 23 | ||
24 | #ifndef CX18_DRIVER_H | 24 | #ifndef CX18_DRIVER_H |
25 | #define CX18_DRIVER_H | 25 | #define CX18_DRIVER_H |
26 | 26 | ||
27 | #include <linux/version.h> | 27 | #include <linux/version.h> |
28 | #include <linux/module.h> | 28 | #include <linux/module.h> |
29 | #include <linux/moduleparam.h> | 29 | #include <linux/moduleparam.h> |
30 | #include <linux/init.h> | 30 | #include <linux/init.h> |
31 | #include <linux/delay.h> | 31 | #include <linux/delay.h> |
32 | #include <linux/sched.h> | 32 | #include <linux/sched.h> |
33 | #include <linux/fs.h> | 33 | #include <linux/fs.h> |
34 | #include <linux/pci.h> | 34 | #include <linux/pci.h> |
35 | #include <linux/interrupt.h> | 35 | #include <linux/interrupt.h> |
36 | #include <linux/spinlock.h> | 36 | #include <linux/spinlock.h> |
37 | #include <linux/i2c.h> | 37 | #include <linux/i2c.h> |
38 | #include <linux/i2c-algo-bit.h> | 38 | #include <linux/i2c-algo-bit.h> |
39 | #include <linux/list.h> | 39 | #include <linux/list.h> |
40 | #include <linux/unistd.h> | 40 | #include <linux/unistd.h> |
41 | #include <linux/pagemap.h> | 41 | #include <linux/pagemap.h> |
42 | #include <linux/workqueue.h> | 42 | #include <linux/workqueue.h> |
43 | #include <linux/mutex.h> | 43 | #include <linux/mutex.h> |
44 | 44 | ||
45 | #include <linux/dvb/video.h> | 45 | #include <linux/dvb/video.h> |
46 | #include <linux/dvb/audio.h> | 46 | #include <linux/dvb/audio.h> |
47 | #include <media/v4l2-common.h> | 47 | #include <media/v4l2-common.h> |
48 | #include <media/v4l2-ioctl.h> | 48 | #include <media/v4l2-ioctl.h> |
49 | #include <media/tuner.h> | 49 | #include <media/tuner.h> |
50 | #include "cx18-mailbox.h" | 50 | #include "cx18-mailbox.h" |
51 | #include "cx18-av-core.h" | 51 | #include "cx18-av-core.h" |
52 | #include "cx23418.h" | 52 | #include "cx23418.h" |
53 | 53 | ||
54 | /* DVB */ | 54 | /* DVB */ |
55 | #include "demux.h" | 55 | #include "demux.h" |
56 | #include "dmxdev.h" | 56 | #include "dmxdev.h" |
57 | #include "dvb_demux.h" | 57 | #include "dvb_demux.h" |
58 | #include "dvb_frontend.h" | 58 | #include "dvb_frontend.h" |
59 | #include "dvb_net.h" | 59 | #include "dvb_net.h" |
60 | #include "dvbdev.h" | 60 | #include "dvbdev.h" |
61 | 61 | ||
62 | #ifndef CONFIG_PCI | 62 | #ifndef CONFIG_PCI |
63 | # error "This driver requires kernel PCI support." | 63 | # error "This driver requires kernel PCI support." |
64 | #endif | 64 | #endif |
65 | 65 | ||
66 | #define CX18_MEM_OFFSET 0x00000000 | 66 | #define CX18_MEM_OFFSET 0x00000000 |
67 | #define CX18_MEM_SIZE 0x04000000 | 67 | #define CX18_MEM_SIZE 0x04000000 |
68 | #define CX18_REG_OFFSET 0x02000000 | 68 | #define CX18_REG_OFFSET 0x02000000 |
69 | 69 | ||
70 | /* Maximum cx18 driver instances. */ | 70 | /* Maximum cx18 driver instances. */ |
71 | #define CX18_MAX_CARDS 32 | 71 | #define CX18_MAX_CARDS 32 |
72 | 72 | ||
73 | /* Supported cards */ | 73 | /* Supported cards */ |
74 | #define CX18_CARD_HVR_1600_ESMT 0 /* Hauppauge HVR 1600 (ESMT memory) */ | 74 | #define CX18_CARD_HVR_1600_ESMT 0 /* Hauppauge HVR 1600 (ESMT memory) */ |
75 | #define CX18_CARD_HVR_1600_SAMSUNG 1 /* Hauppauge HVR 1600 (Samsung memory) */ | 75 | #define CX18_CARD_HVR_1600_SAMSUNG 1 /* Hauppauge HVR 1600 (Samsung memory) */ |
76 | #define CX18_CARD_COMPRO_H900 2 /* Compro VideoMate H900 */ | 76 | #define CX18_CARD_COMPRO_H900 2 /* Compro VideoMate H900 */ |
77 | #define CX18_CARD_YUAN_MPC718 3 /* Yuan MPC718 */ | 77 | #define CX18_CARD_YUAN_MPC718 3 /* Yuan MPC718 */ |
78 | #define CX18_CARD_CNXT_RAPTOR_PAL 4 /* Conexant Raptor PAL */ | 78 | #define CX18_CARD_CNXT_RAPTOR_PAL 4 /* Conexant Raptor PAL */ |
79 | #define CX18_CARD_LAST 4 | 79 | #define CX18_CARD_LAST 4 |
80 | 80 | ||
81 | #define CX18_ENC_STREAM_TYPE_MPG 0 | 81 | #define CX18_ENC_STREAM_TYPE_MPG 0 |
82 | #define CX18_ENC_STREAM_TYPE_TS 1 | 82 | #define CX18_ENC_STREAM_TYPE_TS 1 |
83 | #define CX18_ENC_STREAM_TYPE_YUV 2 | 83 | #define CX18_ENC_STREAM_TYPE_YUV 2 |
84 | #define CX18_ENC_STREAM_TYPE_VBI 3 | 84 | #define CX18_ENC_STREAM_TYPE_VBI 3 |
85 | #define CX18_ENC_STREAM_TYPE_PCM 4 | 85 | #define CX18_ENC_STREAM_TYPE_PCM 4 |
86 | #define CX18_ENC_STREAM_TYPE_IDX 5 | 86 | #define CX18_ENC_STREAM_TYPE_IDX 5 |
87 | #define CX18_ENC_STREAM_TYPE_RAD 6 | 87 | #define CX18_ENC_STREAM_TYPE_RAD 6 |
88 | #define CX18_MAX_STREAMS 7 | 88 | #define CX18_MAX_STREAMS 7 |
89 | 89 | ||
90 | /* system vendor and device IDs */ | 90 | /* system vendor and device IDs */ |
91 | #define PCI_VENDOR_ID_CX 0x14f1 | 91 | #define PCI_VENDOR_ID_CX 0x14f1 |
92 | #define PCI_DEVICE_ID_CX23418 0x5b7a | 92 | #define PCI_DEVICE_ID_CX23418 0x5b7a |
93 | 93 | ||
94 | /* subsystem vendor ID */ | 94 | /* subsystem vendor ID */ |
95 | #define CX18_PCI_ID_HAUPPAUGE 0x0070 | 95 | #define CX18_PCI_ID_HAUPPAUGE 0x0070 |
96 | #define CX18_PCI_ID_COMPRO 0x185b | 96 | #define CX18_PCI_ID_COMPRO 0x185b |
97 | #define CX18_PCI_ID_YUAN 0x12ab | 97 | #define CX18_PCI_ID_YUAN 0x12ab |
98 | #define CX18_PCI_ID_CONEXANT 0x14f1 | 98 | #define CX18_PCI_ID_CONEXANT 0x14f1 |
99 | 99 | ||
100 | /* ======================================================================== */ | 100 | /* ======================================================================== */ |
101 | /* ========================== START USER SETTABLE DMA VARIABLES =========== */ | 101 | /* ========================== START USER SETTABLE DMA VARIABLES =========== */ |
102 | /* ======================================================================== */ | 102 | /* ======================================================================== */ |
103 | 103 | ||
104 | /* DMA Buffers, Default size in MB allocated */ | 104 | /* DMA Buffers, Default size in MB allocated */ |
105 | #define CX18_DEFAULT_ENC_TS_BUFFERS 1 | 105 | #define CX18_DEFAULT_ENC_TS_BUFFERS 1 |
106 | #define CX18_DEFAULT_ENC_MPG_BUFFERS 2 | 106 | #define CX18_DEFAULT_ENC_MPG_BUFFERS 2 |
107 | #define CX18_DEFAULT_ENC_IDX_BUFFERS 1 | 107 | #define CX18_DEFAULT_ENC_IDX_BUFFERS 1 |
108 | #define CX18_DEFAULT_ENC_YUV_BUFFERS 2 | 108 | #define CX18_DEFAULT_ENC_YUV_BUFFERS 2 |
109 | #define CX18_DEFAULT_ENC_VBI_BUFFERS 1 | 109 | #define CX18_DEFAULT_ENC_VBI_BUFFERS 1 |
110 | #define CX18_DEFAULT_ENC_PCM_BUFFERS 1 | 110 | #define CX18_DEFAULT_ENC_PCM_BUFFERS 1 |
111 | 111 | ||
112 | /* i2c stuff */ | 112 | /* i2c stuff */ |
113 | #define I2C_CLIENTS_MAX 16 | 113 | #define I2C_CLIENTS_MAX 16 |
114 | 114 | ||
115 | /* debugging */ | 115 | /* debugging */ |
116 | 116 | ||
117 | /* Flag to turn on high volume debugging */ | 117 | /* Flag to turn on high volume debugging */ |
118 | #define CX18_DBGFLG_WARN (1 << 0) | 118 | #define CX18_DBGFLG_WARN (1 << 0) |
119 | #define CX18_DBGFLG_INFO (1 << 1) | 119 | #define CX18_DBGFLG_INFO (1 << 1) |
120 | #define CX18_DBGFLG_API (1 << 2) | 120 | #define CX18_DBGFLG_API (1 << 2) |
121 | #define CX18_DBGFLG_DMA (1 << 3) | 121 | #define CX18_DBGFLG_DMA (1 << 3) |
122 | #define CX18_DBGFLG_IOCTL (1 << 4) | 122 | #define CX18_DBGFLG_IOCTL (1 << 4) |
123 | #define CX18_DBGFLG_FILE (1 << 5) | 123 | #define CX18_DBGFLG_FILE (1 << 5) |
124 | #define CX18_DBGFLG_I2C (1 << 6) | 124 | #define CX18_DBGFLG_I2C (1 << 6) |
125 | #define CX18_DBGFLG_IRQ (1 << 7) | 125 | #define CX18_DBGFLG_IRQ (1 << 7) |
126 | /* Flag to turn on high volume debugging */ | 126 | /* Flag to turn on high volume debugging */ |
127 | #define CX18_DBGFLG_HIGHVOL (1 << 8) | 127 | #define CX18_DBGFLG_HIGHVOL (1 << 8) |
128 | 128 | ||
129 | /* NOTE: extra space before comma in 'cx->num , ## args' is required for | 129 | /* NOTE: extra space before comma in 'cx->num , ## args' is required for |
130 | gcc-2.95, otherwise it won't compile. */ | 130 | gcc-2.95, otherwise it won't compile. */ |
131 | #define CX18_DEBUG(x, type, fmt, args...) \ | 131 | #define CX18_DEBUG(x, type, fmt, args...) \ |
132 | do { \ | 132 | do { \ |
133 | if ((x) & cx18_debug) \ | 133 | if ((x) & cx18_debug) \ |
134 | printk(KERN_INFO "cx18-%d " type ": " fmt, cx->num , ## args); \ | 134 | printk(KERN_INFO "cx18-%d " type ": " fmt, cx->num , ## args); \ |
135 | } while (0) | 135 | } while (0) |
136 | #define CX18_DEBUG_WARN(fmt, args...) CX18_DEBUG(CX18_DBGFLG_WARN, "warning", fmt , ## args) | 136 | #define CX18_DEBUG_WARN(fmt, args...) CX18_DEBUG(CX18_DBGFLG_WARN, "warning", fmt , ## args) |
137 | #define CX18_DEBUG_INFO(fmt, args...) CX18_DEBUG(CX18_DBGFLG_INFO, "info", fmt , ## args) | 137 | #define CX18_DEBUG_INFO(fmt, args...) CX18_DEBUG(CX18_DBGFLG_INFO, "info", fmt , ## args) |
138 | #define CX18_DEBUG_API(fmt, args...) CX18_DEBUG(CX18_DBGFLG_API, "api", fmt , ## args) | 138 | #define CX18_DEBUG_API(fmt, args...) CX18_DEBUG(CX18_DBGFLG_API, "api", fmt , ## args) |
139 | #define CX18_DEBUG_DMA(fmt, args...) CX18_DEBUG(CX18_DBGFLG_DMA, "dma", fmt , ## args) | 139 | #define CX18_DEBUG_DMA(fmt, args...) CX18_DEBUG(CX18_DBGFLG_DMA, "dma", fmt , ## args) |
140 | #define CX18_DEBUG_IOCTL(fmt, args...) CX18_DEBUG(CX18_DBGFLG_IOCTL, "ioctl", fmt , ## args) | 140 | #define CX18_DEBUG_IOCTL(fmt, args...) CX18_DEBUG(CX18_DBGFLG_IOCTL, "ioctl", fmt , ## args) |
141 | #define CX18_DEBUG_FILE(fmt, args...) CX18_DEBUG(CX18_DBGFLG_FILE, "file", fmt , ## args) | 141 | #define CX18_DEBUG_FILE(fmt, args...) CX18_DEBUG(CX18_DBGFLG_FILE, "file", fmt , ## args) |
142 | #define CX18_DEBUG_I2C(fmt, args...) CX18_DEBUG(CX18_DBGFLG_I2C, "i2c", fmt , ## args) | 142 | #define CX18_DEBUG_I2C(fmt, args...) CX18_DEBUG(CX18_DBGFLG_I2C, "i2c", fmt , ## args) |
143 | #define CX18_DEBUG_IRQ(fmt, args...) CX18_DEBUG(CX18_DBGFLG_IRQ, "irq", fmt , ## args) | 143 | #define CX18_DEBUG_IRQ(fmt, args...) CX18_DEBUG(CX18_DBGFLG_IRQ, "irq", fmt , ## args) |
144 | 144 | ||
145 | #define CX18_DEBUG_HIGH_VOL(x, type, fmt, args...) \ | 145 | #define CX18_DEBUG_HIGH_VOL(x, type, fmt, args...) \ |
146 | do { \ | 146 | do { \ |
147 | if (((x) & cx18_debug) && (cx18_debug & CX18_DBGFLG_HIGHVOL)) \ | 147 | if (((x) & cx18_debug) && (cx18_debug & CX18_DBGFLG_HIGHVOL)) \ |
148 | printk(KERN_INFO "cx18%d " type ": " fmt, cx->num , ## args); \ | 148 | printk(KERN_INFO "cx18%d " type ": " fmt, cx->num , ## args); \ |
149 | } while (0) | 149 | } while (0) |
150 | #define CX18_DEBUG_HI_WARN(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_WARN, "warning", fmt , ## args) | 150 | #define CX18_DEBUG_HI_WARN(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_WARN, "warning", fmt , ## args) |
151 | #define CX18_DEBUG_HI_INFO(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_INFO, "info", fmt , ## args) | 151 | #define CX18_DEBUG_HI_INFO(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_INFO, "info", fmt , ## args) |
152 | #define CX18_DEBUG_HI_API(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_API, "api", fmt , ## args) | 152 | #define CX18_DEBUG_HI_API(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_API, "api", fmt , ## args) |
153 | #define CX18_DEBUG_HI_DMA(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_DMA, "dma", fmt , ## args) | 153 | #define CX18_DEBUG_HI_DMA(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_DMA, "dma", fmt , ## args) |
154 | #define CX18_DEBUG_HI_IOCTL(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_IOCTL, "ioctl", fmt , ## args) | 154 | #define CX18_DEBUG_HI_IOCTL(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_IOCTL, "ioctl", fmt , ## args) |
155 | #define CX18_DEBUG_HI_FILE(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_FILE, "file", fmt , ## args) | 155 | #define CX18_DEBUG_HI_FILE(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_FILE, "file", fmt , ## args) |
156 | #define CX18_DEBUG_HI_I2C(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_I2C, "i2c", fmt , ## args) | 156 | #define CX18_DEBUG_HI_I2C(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_I2C, "i2c", fmt , ## args) |
157 | #define CX18_DEBUG_HI_IRQ(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_IRQ, "irq", fmt , ## args) | 157 | #define CX18_DEBUG_HI_IRQ(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_IRQ, "irq", fmt , ## args) |
158 | 158 | ||
159 | /* Standard kernel messages */ | 159 | /* Standard kernel messages */ |
160 | #define CX18_ERR(fmt, args...) printk(KERN_ERR "cx18-%d: " fmt, cx->num , ## args) | 160 | #define CX18_ERR(fmt, args...) printk(KERN_ERR "cx18-%d: " fmt, cx->num , ## args) |
161 | #define CX18_WARN(fmt, args...) printk(KERN_WARNING "cx18-%d: " fmt, cx->num , ## args) | 161 | #define CX18_WARN(fmt, args...) printk(KERN_WARNING "cx18-%d: " fmt, cx->num , ## args) |
162 | #define CX18_INFO(fmt, args...) printk(KERN_INFO "cx18-%d: " fmt, cx->num , ## args) | 162 | #define CX18_INFO(fmt, args...) printk(KERN_INFO "cx18-%d: " fmt, cx->num , ## args) |
163 | 163 | ||
164 | /* Values for CX18_API_DEC_PLAYBACK_SPEED mpeg_frame_type_mask parameter: */ | 164 | /* Values for CX18_API_DEC_PLAYBACK_SPEED mpeg_frame_type_mask parameter: */ |
165 | #define MPEG_FRAME_TYPE_IFRAME 1 | 165 | #define MPEG_FRAME_TYPE_IFRAME 1 |
166 | #define MPEG_FRAME_TYPE_IFRAME_PFRAME 3 | 166 | #define MPEG_FRAME_TYPE_IFRAME_PFRAME 3 |
167 | #define MPEG_FRAME_TYPE_ALL 7 | 167 | #define MPEG_FRAME_TYPE_ALL 7 |
168 | 168 | ||
169 | #define CX18_MAX_PGM_INDEX (400) | 169 | #define CX18_MAX_PGM_INDEX (400) |
170 | 170 | ||
171 | extern int cx18_debug; | 171 | extern int cx18_debug; |
172 | 172 | ||
173 | 173 | ||
174 | struct cx18_options { | 174 | struct cx18_options { |
175 | int megabytes[CX18_MAX_STREAMS]; /* Size in megabytes of each stream */ | 175 | int megabytes[CX18_MAX_STREAMS]; /* Size in megabytes of each stream */ |
176 | int cardtype; /* force card type on load */ | 176 | int cardtype; /* force card type on load */ |
177 | int tuner; /* set tuner on load */ | 177 | int tuner; /* set tuner on load */ |
178 | int radio; /* enable/disable radio */ | 178 | int radio; /* enable/disable radio */ |
179 | }; | 179 | }; |
180 | 180 | ||
181 | /* per-buffer bit flags */ | 181 | /* per-buffer bit flags */ |
182 | #define CX18_F_B_NEED_BUF_SWAP 0 /* this buffer should be byte swapped */ | 182 | #define CX18_F_B_NEED_BUF_SWAP 0 /* this buffer should be byte swapped */ |
183 | 183 | ||
184 | /* per-stream, s_flags */ | 184 | /* per-stream, s_flags */ |
185 | #define CX18_F_S_CLAIMED 3 /* this stream is claimed */ | 185 | #define CX18_F_S_CLAIMED 3 /* this stream is claimed */ |
186 | #define CX18_F_S_STREAMING 4 /* the fw is decoding/encoding this stream */ | 186 | #define CX18_F_S_STREAMING 4 /* the fw is decoding/encoding this stream */ |
187 | #define CX18_F_S_INTERNAL_USE 5 /* this stream is used internally (sliced VBI processing) */ | 187 | #define CX18_F_S_INTERNAL_USE 5 /* this stream is used internally (sliced VBI processing) */ |
188 | #define CX18_F_S_STREAMOFF 7 /* signal end of stream EOS */ | 188 | #define CX18_F_S_STREAMOFF 7 /* signal end of stream EOS */ |
189 | #define CX18_F_S_APPL_IO 8 /* this stream is used read/written by an application */ | 189 | #define CX18_F_S_APPL_IO 8 /* this stream is used read/written by an application */ |
190 | 190 | ||
191 | /* per-cx18, i_flags */ | 191 | /* per-cx18, i_flags */ |
192 | #define CX18_F_I_LOADED_FW 0 /* Loaded the firmware the first time */ | 192 | #define CX18_F_I_LOADED_FW 0 /* Loaded the firmware the first time */ |
193 | #define CX18_F_I_EOS 4 /* End of encoder stream reached */ | 193 | #define CX18_F_I_EOS 4 /* End of encoder stream reached */ |
194 | #define CX18_F_I_RADIO_USER 5 /* The radio tuner is selected */ | 194 | #define CX18_F_I_RADIO_USER 5 /* The radio tuner is selected */ |
195 | #define CX18_F_I_ENC_PAUSED 13 /* the encoder is paused */ | 195 | #define CX18_F_I_ENC_PAUSED 13 /* the encoder is paused */ |
196 | #define CX18_F_I_INITED 21 /* set after first open */ | 196 | #define CX18_F_I_INITED 21 /* set after first open */ |
197 | #define CX18_F_I_FAILED 22 /* set if first open failed */ | 197 | #define CX18_F_I_FAILED 22 /* set if first open failed */ |
198 | 198 | ||
199 | /* These are the VBI types as they appear in the embedded VBI private packets. */ | 199 | /* These are the VBI types as they appear in the embedded VBI private packets. */ |
200 | #define CX18_SLICED_TYPE_TELETEXT_B (1) | 200 | #define CX18_SLICED_TYPE_TELETEXT_B (1) |
201 | #define CX18_SLICED_TYPE_CAPTION_525 (4) | 201 | #define CX18_SLICED_TYPE_CAPTION_525 (4) |
202 | #define CX18_SLICED_TYPE_WSS_625 (5) | 202 | #define CX18_SLICED_TYPE_WSS_625 (5) |
203 | #define CX18_SLICED_TYPE_VPS (7) | 203 | #define CX18_SLICED_TYPE_VPS (7) |
204 | 204 | ||
205 | struct cx18_buffer { | 205 | struct cx18_buffer { |
206 | struct list_head list; | 206 | struct list_head list; |
207 | dma_addr_t dma_handle; | 207 | dma_addr_t dma_handle; |
208 | u32 id; | 208 | u32 id; |
209 | unsigned long b_flags; | 209 | unsigned long b_flags; |
210 | char *buf; | 210 | char *buf; |
211 | 211 | ||
212 | u32 bytesused; | 212 | u32 bytesused; |
213 | u32 readpos; | 213 | u32 readpos; |
214 | }; | 214 | }; |
215 | 215 | ||
216 | struct cx18_queue { | 216 | struct cx18_queue { |
217 | struct list_head list; | 217 | struct list_head list; |
218 | atomic_t buffers; | 218 | atomic_t buffers; |
219 | u32 bytesused; | 219 | u32 bytesused; |
220 | }; | 220 | }; |
221 | 221 | ||
222 | struct cx18_dvb { | 222 | struct cx18_dvb { |
223 | struct dmx_frontend hw_frontend; | 223 | struct dmx_frontend hw_frontend; |
224 | struct dmx_frontend mem_frontend; | 224 | struct dmx_frontend mem_frontend; |
225 | struct dmxdev dmxdev; | 225 | struct dmxdev dmxdev; |
226 | struct dvb_adapter dvb_adapter; | 226 | struct dvb_adapter dvb_adapter; |
227 | struct dvb_demux demux; | 227 | struct dvb_demux demux; |
228 | struct dvb_frontend *fe; | 228 | struct dvb_frontend *fe; |
229 | struct dvb_net dvbnet; | 229 | struct dvb_net dvbnet; |
230 | int enabled; | 230 | int enabled; |
231 | int feeding; | 231 | int feeding; |
232 | struct mutex feedlock; | 232 | struct mutex feedlock; |
233 | }; | 233 | }; |
234 | 234 | ||
235 | struct cx18; /* forward reference */ | 235 | struct cx18; /* forward reference */ |
236 | struct cx18_scb; /* forward reference */ | 236 | struct cx18_scb; /* forward reference */ |
237 | 237 | ||
238 | #define CX18_INVALID_TASK_HANDLE 0xffffffff | 238 | #define CX18_INVALID_TASK_HANDLE 0xffffffff |
239 | 239 | ||
240 | struct cx18_stream { | 240 | struct cx18_stream { |
241 | /* These first four fields are always set, even if the stream | 241 | /* These first four fields are always set, even if the stream |
242 | is not actually created. */ | 242 | is not actually created. */ |
243 | struct video_device *v4l2dev; /* NULL when stream not created */ | 243 | struct video_device *v4l2dev; /* NULL when stream not created */ |
244 | struct cx18 *cx; /* for ease of use */ | 244 | struct cx18 *cx; /* for ease of use */ |
245 | const char *name; /* name of the stream */ | 245 | const char *name; /* name of the stream */ |
246 | int type; /* stream type */ | 246 | int type; /* stream type */ |
247 | u32 handle; /* task handle */ | 247 | u32 handle; /* task handle */ |
248 | unsigned mdl_offset; | 248 | unsigned mdl_offset; |
249 | 249 | ||
250 | u32 id; | 250 | u32 id; |
251 | spinlock_t qlock; /* locks access to the queues */ | 251 | spinlock_t qlock; /* locks access to the queues */ |
252 | unsigned long s_flags; /* status flags, see above */ | 252 | unsigned long s_flags; /* status flags, see above */ |
253 | int dma; /* can be PCI_DMA_TODEVICE, | 253 | int dma; /* can be PCI_DMA_TODEVICE, |
254 | PCI_DMA_FROMDEVICE or | 254 | PCI_DMA_FROMDEVICE or |
255 | PCI_DMA_NONE */ | 255 | PCI_DMA_NONE */ |
256 | u64 dma_pts; | 256 | u64 dma_pts; |
257 | wait_queue_head_t waitq; | 257 | wait_queue_head_t waitq; |
258 | 258 | ||
259 | /* Buffer Stats */ | 259 | /* Buffer Stats */ |
260 | u32 buffers; | 260 | u32 buffers; |
261 | u32 buf_size; | 261 | u32 buf_size; |
262 | 262 | ||
263 | /* Buffer Queues */ | 263 | /* Buffer Queues */ |
264 | struct cx18_queue q_free; /* free buffers */ | 264 | struct cx18_queue q_free; /* free buffers */ |
265 | struct cx18_queue q_full; /* full buffers */ | 265 | struct cx18_queue q_full; /* full buffers */ |
266 | struct cx18_queue q_io; /* waiting for I/O */ | 266 | struct cx18_queue q_io; /* waiting for I/O */ |
267 | 267 | ||
268 | /* DVB / Digital Transport */ | 268 | /* DVB / Digital Transport */ |
269 | struct cx18_dvb dvb; | 269 | struct cx18_dvb dvb; |
270 | }; | 270 | }; |
271 | 271 | ||
272 | struct cx18_open_id { | 272 | struct cx18_open_id { |
273 | u32 open_id; | 273 | u32 open_id; |
274 | int type; | 274 | int type; |
275 | enum v4l2_priority prio; | 275 | enum v4l2_priority prio; |
276 | struct cx18 *cx; | 276 | struct cx18 *cx; |
277 | }; | 277 | }; |
278 | 278 | ||
279 | /* forward declaration of struct defined in cx18-cards.h */ | 279 | /* forward declaration of struct defined in cx18-cards.h */ |
280 | struct cx18_card; | 280 | struct cx18_card; |
281 | 281 | ||
282 | 282 | ||
283 | #define CX18_VBI_FRAMES 32 | 283 | #define CX18_VBI_FRAMES 32 |
284 | 284 | ||
285 | /* VBI data */ | 285 | /* VBI data */ |
286 | struct vbi_info { | 286 | struct vbi_info { |
287 | u32 enc_size; | 287 | u32 enc_size; |
288 | u32 frame; | 288 | u32 frame; |
289 | u8 cc_data_odd[256]; | 289 | u8 cc_data_odd[256]; |
290 | u8 cc_data_even[256]; | 290 | u8 cc_data_even[256]; |
291 | int cc_pos; | 291 | int cc_pos; |
292 | u8 cc_no_update; | 292 | u8 cc_no_update; |
293 | u8 vps[5]; | 293 | u8 vps[5]; |
294 | u8 vps_found; | 294 | u8 vps_found; |
295 | int wss; | 295 | int wss; |
296 | u8 wss_found; | 296 | u8 wss_found; |
297 | u8 wss_no_update; | 297 | u8 wss_no_update; |
298 | u32 raw_decoder_line_size; | 298 | u32 raw_decoder_line_size; |
299 | u8 raw_decoder_sav_odd_field; | 299 | u8 raw_decoder_sav_odd_field; |
300 | u8 raw_decoder_sav_even_field; | 300 | u8 raw_decoder_sav_even_field; |
301 | u32 sliced_decoder_line_size; | 301 | u32 sliced_decoder_line_size; |
302 | u8 sliced_decoder_sav_odd_field; | 302 | u8 sliced_decoder_sav_odd_field; |
303 | u8 sliced_decoder_sav_even_field; | 303 | u8 sliced_decoder_sav_even_field; |
304 | struct v4l2_format in; | 304 | struct v4l2_format in; |
305 | /* convenience pointer to sliced struct in vbi_in union */ | 305 | /* convenience pointer to sliced struct in vbi_in union */ |
306 | struct v4l2_sliced_vbi_format *sliced_in; | 306 | struct v4l2_sliced_vbi_format *sliced_in; |
307 | u32 service_set_in; | 307 | u32 service_set_in; |
308 | int insert_mpeg; | 308 | int insert_mpeg; |
309 | 309 | ||
310 | /* Buffer for the maximum of 2 * 18 * packet_size sliced VBI lines. | 310 | /* Buffer for the maximum of 2 * 18 * packet_size sliced VBI lines. |
311 | One for /dev/vbi0 and one for /dev/vbi8 */ | 311 | One for /dev/vbi0 and one for /dev/vbi8 */ |
312 | struct v4l2_sliced_vbi_data sliced_data[36]; | 312 | struct v4l2_sliced_vbi_data sliced_data[36]; |
313 | 313 | ||
314 | /* Buffer for VBI data inserted into MPEG stream. | 314 | /* Buffer for VBI data inserted into MPEG stream. |
315 | The first byte is a dummy byte that's never used. | 315 | The first byte is a dummy byte that's never used. |
316 | The next 16 bytes contain the MPEG header for the VBI data, | 316 | The next 16 bytes contain the MPEG header for the VBI data, |
317 | the remainder is the actual VBI data. | 317 | the remainder is the actual VBI data. |
318 | The max size accepted by the MPEG VBI reinsertion turns out | 318 | The max size accepted by the MPEG VBI reinsertion turns out |
319 | to be 1552 bytes, which happens to be 4 + (1 + 42) * (2 * 18) bytes, | 319 | to be 1552 bytes, which happens to be 4 + (1 + 42) * (2 * 18) bytes, |
320 | where 4 is a four byte header, 42 is the max sliced VBI payload, 1 is | 320 | where 4 is a four byte header, 42 is the max sliced VBI payload, 1 is |
321 | a single line header byte and 2 * 18 is the number of VBI lines per frame. | 321 | a single line header byte and 2 * 18 is the number of VBI lines per frame. |
322 | 322 | ||
323 | However, it seems that the data must be 1K aligned, so we have to | 323 | However, it seems that the data must be 1K aligned, so we have to |
324 | pad the data until the 1 or 2 K boundary. | 324 | pad the data until the 1 or 2 K boundary. |
325 | 325 | ||
326 | This pointer array will allocate 2049 bytes to store each VBI frame. */ | 326 | This pointer array will allocate 2049 bytes to store each VBI frame. */ |
327 | u8 *sliced_mpeg_data[CX18_VBI_FRAMES]; | 327 | u8 *sliced_mpeg_data[CX18_VBI_FRAMES]; |
328 | u32 sliced_mpeg_size[CX18_VBI_FRAMES]; | 328 | u32 sliced_mpeg_size[CX18_VBI_FRAMES]; |
329 | struct cx18_buffer sliced_mpeg_buf; | 329 | struct cx18_buffer sliced_mpeg_buf; |
330 | u32 inserted_frame; | 330 | u32 inserted_frame; |
331 | 331 | ||
332 | u32 start[2], count; | 332 | u32 start[2], count; |
333 | u32 raw_size; | 333 | u32 raw_size; |
334 | u32 sliced_size; | 334 | u32 sliced_size; |
335 | }; | 335 | }; |
336 | 336 | ||
337 | /* Per cx23418, per I2C bus private algo callback data */ | 337 | /* Per cx23418, per I2C bus private algo callback data */ |
338 | struct cx18_i2c_algo_callback_data { | 338 | struct cx18_i2c_algo_callback_data { |
339 | struct cx18 *cx; | 339 | struct cx18 *cx; |
340 | int bus_index; /* 0 or 1 for the cx23418's 1st or 2nd I2C bus */ | 340 | int bus_index; /* 0 or 1 for the cx23418's 1st or 2nd I2C bus */ |
341 | }; | 341 | }; |
342 | 342 | ||
343 | /* Struct to hold info about cx18 cards */ | 343 | /* Struct to hold info about cx18 cards */ |
344 | struct cx18 { | 344 | struct cx18 { |
345 | int num; /* board number, -1 during init! */ | 345 | int num; /* board number, -1 during init! */ |
346 | char name[8]; /* board name for printk and interrupts (e.g. 'cx180') */ | 346 | char name[8]; /* board name for printk and interrupts (e.g. 'cx180') */ |
347 | struct pci_dev *dev; /* PCI device */ | 347 | struct pci_dev *dev; /* PCI device */ |
348 | const struct cx18_card *card; /* card information */ | 348 | const struct cx18_card *card; /* card information */ |
349 | const char *card_name; /* full name of the card */ | 349 | const char *card_name; /* full name of the card */ |
350 | const struct cx18_card_tuner_i2c *card_i2c; /* i2c addresses to probe for tuner */ | 350 | const struct cx18_card_tuner_i2c *card_i2c; /* i2c addresses to probe for tuner */ |
351 | u8 is_50hz; | 351 | u8 is_50hz; |
352 | u8 is_60hz; | 352 | u8 is_60hz; |
353 | u8 is_out_50hz; | 353 | u8 is_out_50hz; |
354 | u8 is_out_60hz; | 354 | u8 is_out_60hz; |
355 | u8 nof_inputs; /* number of video inputs */ | 355 | u8 nof_inputs; /* number of video inputs */ |
356 | u8 nof_audio_inputs; /* number of audio inputs */ | 356 | u8 nof_audio_inputs; /* number of audio inputs */ |
357 | u16 buffer_id; /* buffer ID counter */ | 357 | u16 buffer_id; /* buffer ID counter */ |
358 | u32 v4l2_cap; /* V4L2 capabilities of card */ | 358 | u32 v4l2_cap; /* V4L2 capabilities of card */ |
359 | u32 hw_flags; /* Hardware description of the board */ | 359 | u32 hw_flags; /* Hardware description of the board */ |
360 | unsigned mdl_offset; | 360 | unsigned mdl_offset; |
361 | struct cx18_scb __iomem *scb; /* pointer to SCB */ | 361 | struct cx18_scb __iomem *scb; /* pointer to SCB */ |
362 | 362 | ||
363 | struct cx18_av_state av_state; | 363 | struct cx18_av_state av_state; |
364 | 364 | ||
365 | /* codec settings */ | 365 | /* codec settings */ |
366 | struct cx2341x_mpeg_params params; | 366 | struct cx2341x_mpeg_params params; |
367 | u32 filter_mode; | 367 | u32 filter_mode; |
368 | u32 temporal_strength; | 368 | u32 temporal_strength; |
369 | u32 spatial_strength; | 369 | u32 spatial_strength; |
370 | 370 | ||
371 | /* dualwatch */ | 371 | /* dualwatch */ |
372 | unsigned long dualwatch_jiffies; | 372 | unsigned long dualwatch_jiffies; |
373 | u16 dualwatch_stereo_mode; | 373 | u16 dualwatch_stereo_mode; |
374 | 374 | ||
375 | /* Digitizer type */ | 375 | /* Digitizer type */ |
376 | int digitizer; /* 0x00EF = saa7114 0x00FO = saa7115 0x0106 = mic */ | 376 | int digitizer; /* 0x00EF = saa7114 0x00FO = saa7115 0x0106 = mic */ |
377 | 377 | ||
378 | struct mutex serialize_lock; /* mutex used to serialize open/close/start/stop/ioctl operations */ | 378 | struct mutex serialize_lock; /* mutex used to serialize open/close/start/stop/ioctl operations */ |
379 | struct cx18_options options; /* User options */ | 379 | struct cx18_options options; /* User options */ |
380 | int stream_buf_size[CX18_MAX_STREAMS]; /* Stream buffer size */ | 380 | int stream_buf_size[CX18_MAX_STREAMS]; /* Stream buffer size */ |
381 | struct cx18_stream streams[CX18_MAX_STREAMS]; /* Stream data */ | 381 | struct cx18_stream streams[CX18_MAX_STREAMS]; /* Stream data */ |
382 | unsigned long i_flags; /* global cx18 flags */ | 382 | unsigned long i_flags; /* global cx18 flags */ |
383 | atomic_t ana_capturing; /* count number of active analog capture streams */ | 383 | atomic_t ana_capturing; /* count number of active analog capture streams */ |
384 | atomic_t tot_capturing; /* total count number of active capture streams */ | 384 | atomic_t tot_capturing; /* total count number of active capture streams */ |
385 | spinlock_t lock; /* lock access to this struct */ | 385 | spinlock_t lock; /* lock access to this struct */ |
386 | int search_pack_header; | 386 | int search_pack_header; |
387 | 387 | ||
388 | spinlock_t dma_reg_lock; /* lock access to DMA engine registers */ | 388 | spinlock_t dma_reg_lock; /* lock access to DMA engine registers */ |
389 | 389 | ||
390 | int open_id; /* incremented each time an open occurs, used as | 390 | int open_id; /* incremented each time an open occurs, used as |
391 | unique ID. Starts at 1, so 0 can be used as | 391 | unique ID. Starts at 1, so 0 can be used as |
392 | uninitialized value in the stream->id. */ | 392 | uninitialized value in the stream->id. */ |
393 | 393 | ||
394 | u32 base_addr; | 394 | u32 base_addr; |
395 | struct v4l2_prio_state prio; | 395 | struct v4l2_prio_state prio; |
396 | 396 | ||
397 | u8 card_rev; | 397 | u8 card_rev; |
398 | void __iomem *enc_mem, *reg_mem; | 398 | void __iomem *enc_mem, *reg_mem; |
399 | 399 | ||
400 | struct vbi_info vbi; | 400 | struct vbi_info vbi; |
401 | 401 | ||
402 | u32 pgm_info_offset; | 402 | u32 pgm_info_offset; |
403 | u32 pgm_info_num; | 403 | u32 pgm_info_num; |
404 | u32 pgm_info_write_idx; | 404 | u32 pgm_info_write_idx; |
405 | u32 pgm_info_read_idx; | 405 | u32 pgm_info_read_idx; |
406 | struct v4l2_enc_idx_entry pgm_info[CX18_MAX_PGM_INDEX]; | 406 | struct v4l2_enc_idx_entry pgm_info[CX18_MAX_PGM_INDEX]; |
407 | 407 | ||
408 | u64 mpg_data_received; | 408 | u64 mpg_data_received; |
409 | u64 vbi_data_inserted; | 409 | u64 vbi_data_inserted; |
410 | 410 | ||
411 | wait_queue_head_t mb_apu_waitq; | 411 | wait_queue_head_t mb_apu_waitq; |
412 | wait_queue_head_t mb_cpu_waitq; | 412 | wait_queue_head_t mb_cpu_waitq; |
413 | wait_queue_head_t mb_epu_waitq; | 413 | wait_queue_head_t mb_epu_waitq; |
414 | wait_queue_head_t mb_hpu_waitq; | 414 | wait_queue_head_t mb_hpu_waitq; |
415 | wait_queue_head_t cap_w; | 415 | wait_queue_head_t cap_w; |
416 | /* when the current DMA is finished this queue is woken up */ | 416 | /* when the current DMA is finished this queue is woken up */ |
417 | wait_queue_head_t dma_waitq; | 417 | wait_queue_head_t dma_waitq; |
418 | 418 | ||
419 | /* i2c */ | 419 | /* i2c */ |
420 | struct i2c_adapter i2c_adap[2]; | 420 | struct i2c_adapter i2c_adap[2]; |
421 | struct i2c_algo_bit_data i2c_algo[2]; | 421 | struct i2c_algo_bit_data i2c_algo[2]; |
422 | struct cx18_i2c_algo_callback_data i2c_algo_cb_data[2]; | 422 | struct cx18_i2c_algo_callback_data i2c_algo_cb_data[2]; |
423 | struct i2c_client i2c_client[2]; | 423 | struct i2c_client i2c_client[2]; |
424 | struct mutex i2c_bus_lock[2]; | 424 | struct mutex i2c_bus_lock[2]; |
425 | struct i2c_client *i2c_clients[I2C_CLIENTS_MAX]; | 425 | struct i2c_client *i2c_clients[I2C_CLIENTS_MAX]; |
426 | 426 | ||
427 | /* gpio */ | 427 | /* gpio */ |
428 | u32 gpio_dir; | 428 | u32 gpio_dir; |
429 | u32 gpio_val; | 429 | u32 gpio_val; |
430 | struct mutex gpio_lock; | 430 | struct mutex gpio_lock; |
431 | 431 | ||
432 | /* v4l2 and User settings */ | 432 | /* v4l2 and User settings */ |
433 | 433 | ||
434 | /* codec settings */ | 434 | /* codec settings */ |
435 | u32 audio_input; | 435 | u32 audio_input; |
436 | u32 active_input; | 436 | u32 active_input; |
437 | u32 active_output; | 437 | u32 active_output; |
438 | v4l2_std_id std; | 438 | v4l2_std_id std; |
439 | v4l2_std_id tuner_std; /* The norm of the tuner (fixed) */ | 439 | v4l2_std_id tuner_std; /* The norm of the tuner (fixed) */ |
440 | }; | 440 | }; |
441 | 441 | ||
442 | /* Globals */ | 442 | /* Globals */ |
443 | extern struct cx18 *cx18_cards[]; | 443 | extern struct cx18 *cx18_cards[]; |
444 | extern int cx18_cards_active; | 444 | extern int cx18_cards_active; |
445 | extern int cx18_first_minor; | 445 | extern int cx18_first_minor; |
446 | extern spinlock_t cx18_cards_lock; | 446 | extern spinlock_t cx18_cards_lock; |
447 | 447 | ||
448 | /*==============Prototypes==================*/ | 448 | /*==============Prototypes==================*/ |
449 | 449 | ||
450 | /* Return non-zero if a signal is pending */ | 450 | /* Return non-zero if a signal is pending */ |
451 | int cx18_msleep_timeout(unsigned int msecs, int intr); | 451 | int cx18_msleep_timeout(unsigned int msecs, int intr); |
452 | 452 | ||
453 | /* Read Hauppauge eeprom */ | 453 | /* Read Hauppauge eeprom */ |
454 | struct tveeprom; /* forward reference */ | 454 | struct tveeprom; /* forward reference */ |
455 | void cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv); | 455 | void cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv); |
456 | 456 | ||
457 | /* First-open initialization: load firmware, etc. */ | 457 | /* First-open initialization: load firmware, etc. */ |
458 | int cx18_init_on_first_open(struct cx18 *cx); | 458 | int cx18_init_on_first_open(struct cx18 *cx); |
459 | 459 | ||
460 | /* This is a PCI post thing, where if the pci register is not read, then | ||
461 | the write doesn't always take effect right away. By reading back the | ||
462 | register any pending PCI writes will be performed (in order), and so | ||
463 | you can be sure that the writes are guaranteed to be done. | ||
464 | |||
465 | Rarely needed, only in some timing sensitive cases. | ||
466 | Apparently if this is not done some motherboards seem | ||
467 | to kill the firmware and get into the broken state until computer is | ||
468 | rebooted. */ | ||
469 | #define write_sync(val, reg) \ | ||
470 | do { writel(val, reg); readl(reg); } while (0) | ||
471 | |||
472 | #define read_reg(reg) readl(cx->reg_mem + (reg)) | ||
473 | #define write_reg(val, reg) writel(val, cx->reg_mem + (reg)) | ||
474 | #define write_reg_sync(val, reg) \ | ||
475 | do { write_reg(val, reg); read_reg(reg); } while (0) | ||
476 | |||
477 | #define read_enc(addr) readl(cx->enc_mem + (u32)(addr)) | ||
478 | #define write_enc(val, addr) writel(val, cx->enc_mem + (u32)(addr)) | ||
479 | #define write_enc_sync(val, addr) \ | ||
480 | do { write_enc(val, addr); read_enc(addr); } while (0) | ||
481 | |||
482 | #define sw1_irq_enable(val) do { \ | ||
483 | write_reg(val, SW1_INT_STATUS); \ | ||
484 | write_reg(read_reg(SW1_INT_ENABLE_PCI) | (val), SW1_INT_ENABLE_PCI); \ | ||
485 | } while (0) | ||
486 | |||
487 | #define sw1_irq_disable(val) \ | ||
488 | write_reg(read_reg(SW1_INT_ENABLE_PCI) & ~(val), SW1_INT_ENABLE_PCI); | ||
489 | |||
490 | #define sw2_irq_enable(val) do { \ | ||
491 | write_reg(val, SW2_INT_STATUS); \ | ||
492 | write_reg(read_reg(SW2_INT_ENABLE_PCI) | (val), SW2_INT_ENABLE_PCI); \ | ||
493 | } while (0) | ||
494 | |||
495 | #define sw2_irq_disable(val) \ | ||
496 | write_reg(read_reg(SW2_INT_ENABLE_PCI) & ~(val), SW2_INT_ENABLE_PCI); | ||
497 | |||
498 | #define setup_page(addr) do { \ | ||
499 | u32 val = read_reg(0xD000F8) & ~0x1f00; \ | ||
500 | write_reg(val | (((addr) >> 17) & 0x1f00), 0xD000F8); \ | ||
501 | } while (0) | ||
502 | |||
503 | #endif /* CX18_DRIVER_H */ | 460 | #endif /* CX18_DRIVER_H */ |
504 | 461 |
drivers/media/video/cx18/cx18-dvb.c
1 | /* | 1 | /* |
2 | * cx18 functions for DVB support | 2 | * cx18 functions for DVB support |
3 | * | 3 | * |
4 | * Copyright (c) 2008 Steven Toth <stoth@linuxtv.org> | 4 | * Copyright (c) 2008 Steven Toth <stoth@linuxtv.org> |
5 | * | 5 | * |
6 | * This program is free software; you can redistribute it and/or modify | 6 | * This program is free software; you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License as published by | 7 | * it under the terms of the GNU General Public License as published by |
8 | * the Free Software Foundation; either version 2 of the License, or | 8 | * the Free Software Foundation; either version 2 of the License, or |
9 | * (at your option) any later version. | 9 | * (at your option) any later version. |
10 | * | 10 | * |
11 | * This program is distributed in the hope that it will be useful, | 11 | * This program is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | * | 14 | * |
15 | * GNU General Public License for more details. | 15 | * GNU General Public License for more details. |
16 | * | 16 | * |
17 | * You should have received a copy of the GNU General Public License | 17 | * You should have received a copy of the GNU General Public License |
18 | * along with this program; if not, write to the Free Software | 18 | * along with this program; if not, write to the Free Software |
19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | 19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
20 | */ | 20 | */ |
21 | 21 | ||
22 | #include "cx18-version.h" | 22 | #include "cx18-version.h" |
23 | #include "cx18-dvb.h" | 23 | #include "cx18-dvb.h" |
24 | #include "cx18-io.h" | ||
24 | #include "cx18-streams.h" | 25 | #include "cx18-streams.h" |
25 | #include "cx18-cards.h" | 26 | #include "cx18-cards.h" |
26 | #include "s5h1409.h" | 27 | #include "s5h1409.h" |
27 | #include "mxl5005s.h" | 28 | #include "mxl5005s.h" |
28 | 29 | ||
29 | DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); | 30 | DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); |
30 | 31 | ||
31 | #define CX18_REG_DMUX_NUM_PORT_0_CONTROL 0xd5a000 | 32 | #define CX18_REG_DMUX_NUM_PORT_0_CONTROL 0xd5a000 |
32 | 33 | ||
33 | static struct mxl5005s_config hauppauge_hvr1600_tuner = { | 34 | static struct mxl5005s_config hauppauge_hvr1600_tuner = { |
34 | .i2c_address = 0xC6 >> 1, | 35 | .i2c_address = 0xC6 >> 1, |
35 | .if_freq = IF_FREQ_5380000HZ, | 36 | .if_freq = IF_FREQ_5380000HZ, |
36 | .xtal_freq = CRYSTAL_FREQ_16000000HZ, | 37 | .xtal_freq = CRYSTAL_FREQ_16000000HZ, |
37 | .agc_mode = MXL_SINGLE_AGC, | 38 | .agc_mode = MXL_SINGLE_AGC, |
38 | .tracking_filter = MXL_TF_C_H, | 39 | .tracking_filter = MXL_TF_C_H, |
39 | .rssi_enable = MXL_RSSI_ENABLE, | 40 | .rssi_enable = MXL_RSSI_ENABLE, |
40 | .cap_select = MXL_CAP_SEL_ENABLE, | 41 | .cap_select = MXL_CAP_SEL_ENABLE, |
41 | .div_out = MXL_DIV_OUT_4, | 42 | .div_out = MXL_DIV_OUT_4, |
42 | .clock_out = MXL_CLOCK_OUT_DISABLE, | 43 | .clock_out = MXL_CLOCK_OUT_DISABLE, |
43 | .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM, | 44 | .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM, |
44 | .top = MXL5005S_TOP_25P2, | 45 | .top = MXL5005S_TOP_25P2, |
45 | .mod_mode = MXL_DIGITAL_MODE, | 46 | .mod_mode = MXL_DIGITAL_MODE, |
46 | .if_mode = MXL_ZERO_IF, | 47 | .if_mode = MXL_ZERO_IF, |
47 | .AgcMasterByte = 0x00, | 48 | .AgcMasterByte = 0x00, |
48 | }; | 49 | }; |
49 | 50 | ||
50 | static struct s5h1409_config hauppauge_hvr1600_config = { | 51 | static struct s5h1409_config hauppauge_hvr1600_config = { |
51 | .demod_address = 0x32 >> 1, | 52 | .demod_address = 0x32 >> 1, |
52 | .output_mode = S5H1409_SERIAL_OUTPUT, | 53 | .output_mode = S5H1409_SERIAL_OUTPUT, |
53 | .gpio = S5H1409_GPIO_ON, | 54 | .gpio = S5H1409_GPIO_ON, |
54 | .qam_if = 44000, | 55 | .qam_if = 44000, |
55 | .inversion = S5H1409_INVERSION_OFF, | 56 | .inversion = S5H1409_INVERSION_OFF, |
56 | .status_mode = S5H1409_DEMODLOCKING, | 57 | .status_mode = S5H1409_DEMODLOCKING, |
57 | .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK | 58 | .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK |
58 | 59 | ||
59 | }; | 60 | }; |
60 | 61 | ||
61 | static int dvb_register(struct cx18_stream *stream); | 62 | static int dvb_register(struct cx18_stream *stream); |
62 | 63 | ||
63 | /* Kernel DVB framework calls this when the feed needs to start. | 64 | /* Kernel DVB framework calls this when the feed needs to start. |
64 | * The CX18 framework should enable the transport DMA handling | 65 | * The CX18 framework should enable the transport DMA handling |
65 | * and queue processing. | 66 | * and queue processing. |
66 | */ | 67 | */ |
67 | static int cx18_dvb_start_feed(struct dvb_demux_feed *feed) | 68 | static int cx18_dvb_start_feed(struct dvb_demux_feed *feed) |
68 | { | 69 | { |
69 | struct dvb_demux *demux = feed->demux; | 70 | struct dvb_demux *demux = feed->demux; |
70 | struct cx18_stream *stream = (struct cx18_stream *) demux->priv; | 71 | struct cx18_stream *stream = (struct cx18_stream *) demux->priv; |
71 | struct cx18 *cx = stream->cx; | 72 | struct cx18 *cx = stream->cx; |
72 | int ret; | 73 | int ret; |
73 | u32 v; | 74 | u32 v; |
74 | 75 | ||
75 | CX18_DEBUG_INFO("Start feed: pid = 0x%x index = %d\n", | 76 | CX18_DEBUG_INFO("Start feed: pid = 0x%x index = %d\n", |
76 | feed->pid, feed->index); | 77 | feed->pid, feed->index); |
77 | 78 | ||
78 | mutex_lock(&cx->serialize_lock); | 79 | mutex_lock(&cx->serialize_lock); |
79 | ret = cx18_init_on_first_open(cx); | 80 | ret = cx18_init_on_first_open(cx); |
80 | mutex_unlock(&cx->serialize_lock); | 81 | mutex_unlock(&cx->serialize_lock); |
81 | if (ret) { | 82 | if (ret) { |
82 | CX18_ERR("Failed to initialize firmware starting DVB feed\n"); | 83 | CX18_ERR("Failed to initialize firmware starting DVB feed\n"); |
83 | return ret; | 84 | return ret; |
84 | } | 85 | } |
85 | ret = -EINVAL; | 86 | ret = -EINVAL; |
86 | 87 | ||
87 | switch (cx->card->type) { | 88 | switch (cx->card->type) { |
88 | case CX18_CARD_HVR_1600_ESMT: | 89 | case CX18_CARD_HVR_1600_ESMT: |
89 | case CX18_CARD_HVR_1600_SAMSUNG: | 90 | case CX18_CARD_HVR_1600_SAMSUNG: |
90 | v = read_reg(CX18_REG_DMUX_NUM_PORT_0_CONTROL); | 91 | v = cx18_read_reg(cx, CX18_REG_DMUX_NUM_PORT_0_CONTROL); |
91 | v |= 0x00400000; /* Serial Mode */ | 92 | v |= 0x00400000; /* Serial Mode */ |
92 | v |= 0x00002000; /* Data Length - Byte */ | 93 | v |= 0x00002000; /* Data Length - Byte */ |
93 | v |= 0x00010000; /* Error - Polarity */ | 94 | v |= 0x00010000; /* Error - Polarity */ |
94 | v |= 0x00020000; /* Error - Passthru */ | 95 | v |= 0x00020000; /* Error - Passthru */ |
95 | v |= 0x000c0000; /* Error - Ignore */ | 96 | v |= 0x000c0000; /* Error - Ignore */ |
96 | write_reg(v, CX18_REG_DMUX_NUM_PORT_0_CONTROL); | 97 | cx18_write_reg(cx, v, CX18_REG_DMUX_NUM_PORT_0_CONTROL); |
97 | break; | 98 | break; |
98 | 99 | ||
99 | default: | 100 | default: |
100 | /* Assumption - Parallel transport - Signalling | 101 | /* Assumption - Parallel transport - Signalling |
101 | * undefined or default. | 102 | * undefined or default. |
102 | */ | 103 | */ |
103 | break; | 104 | break; |
104 | } | 105 | } |
105 | 106 | ||
106 | if (!demux->dmx.frontend) | 107 | if (!demux->dmx.frontend) |
107 | return -EINVAL; | 108 | return -EINVAL; |
108 | 109 | ||
109 | if (stream) { | 110 | if (stream) { |
110 | mutex_lock(&stream->dvb.feedlock); | 111 | mutex_lock(&stream->dvb.feedlock); |
111 | if (stream->dvb.feeding++ == 0) { | 112 | if (stream->dvb.feeding++ == 0) { |
112 | CX18_DEBUG_INFO("Starting Transport DMA\n"); | 113 | CX18_DEBUG_INFO("Starting Transport DMA\n"); |
113 | ret = cx18_start_v4l2_encode_stream(stream); | 114 | ret = cx18_start_v4l2_encode_stream(stream); |
114 | if (ret < 0) { | 115 | if (ret < 0) { |
115 | CX18_DEBUG_INFO( | 116 | CX18_DEBUG_INFO( |
116 | "Failed to start Transport DMA\n"); | 117 | "Failed to start Transport DMA\n"); |
117 | stream->dvb.feeding--; | 118 | stream->dvb.feeding--; |
118 | } | 119 | } |
119 | } else | 120 | } else |
120 | ret = 0; | 121 | ret = 0; |
121 | mutex_unlock(&stream->dvb.feedlock); | 122 | mutex_unlock(&stream->dvb.feedlock); |
122 | } | 123 | } |
123 | 124 | ||
124 | return ret; | 125 | return ret; |
125 | } | 126 | } |
126 | 127 | ||
127 | /* Kernel DVB framework calls this when the feed needs to stop. */ | 128 | /* Kernel DVB framework calls this when the feed needs to stop. */ |
128 | static int cx18_dvb_stop_feed(struct dvb_demux_feed *feed) | 129 | static int cx18_dvb_stop_feed(struct dvb_demux_feed *feed) |
129 | { | 130 | { |
130 | struct dvb_demux *demux = feed->demux; | 131 | struct dvb_demux *demux = feed->demux; |
131 | struct cx18_stream *stream = (struct cx18_stream *)demux->priv; | 132 | struct cx18_stream *stream = (struct cx18_stream *)demux->priv; |
132 | struct cx18 *cx = stream->cx; | 133 | struct cx18 *cx = stream->cx; |
133 | int ret = -EINVAL; | 134 | int ret = -EINVAL; |
134 | 135 | ||
135 | CX18_DEBUG_INFO("Stop feed: pid = 0x%x index = %d\n", | 136 | CX18_DEBUG_INFO("Stop feed: pid = 0x%x index = %d\n", |
136 | feed->pid, feed->index); | 137 | feed->pid, feed->index); |
137 | 138 | ||
138 | if (stream) { | 139 | if (stream) { |
139 | mutex_lock(&stream->dvb.feedlock); | 140 | mutex_lock(&stream->dvb.feedlock); |
140 | if (--stream->dvb.feeding == 0) { | 141 | if (--stream->dvb.feeding == 0) { |
141 | CX18_DEBUG_INFO("Stopping Transport DMA\n"); | 142 | CX18_DEBUG_INFO("Stopping Transport DMA\n"); |
142 | ret = cx18_stop_v4l2_encode_stream(stream, 0); | 143 | ret = cx18_stop_v4l2_encode_stream(stream, 0); |
143 | } else | 144 | } else |
144 | ret = 0; | 145 | ret = 0; |
145 | mutex_unlock(&stream->dvb.feedlock); | 146 | mutex_unlock(&stream->dvb.feedlock); |
146 | } | 147 | } |
147 | 148 | ||
148 | return ret; | 149 | return ret; |
149 | } | 150 | } |
150 | 151 | ||
151 | int cx18_dvb_register(struct cx18_stream *stream) | 152 | int cx18_dvb_register(struct cx18_stream *stream) |
152 | { | 153 | { |
153 | struct cx18 *cx = stream->cx; | 154 | struct cx18 *cx = stream->cx; |
154 | struct cx18_dvb *dvb = &stream->dvb; | 155 | struct cx18_dvb *dvb = &stream->dvb; |
155 | struct dvb_adapter *dvb_adapter; | 156 | struct dvb_adapter *dvb_adapter; |
156 | struct dvb_demux *dvbdemux; | 157 | struct dvb_demux *dvbdemux; |
157 | struct dmx_demux *dmx; | 158 | struct dmx_demux *dmx; |
158 | int ret; | 159 | int ret; |
159 | 160 | ||
160 | if (!dvb) | 161 | if (!dvb) |
161 | return -EINVAL; | 162 | return -EINVAL; |
162 | 163 | ||
163 | ret = dvb_register_adapter(&dvb->dvb_adapter, | 164 | ret = dvb_register_adapter(&dvb->dvb_adapter, |
164 | CX18_DRIVER_NAME, | 165 | CX18_DRIVER_NAME, |
165 | THIS_MODULE, &cx->dev->dev, adapter_nr); | 166 | THIS_MODULE, &cx->dev->dev, adapter_nr); |
166 | if (ret < 0) | 167 | if (ret < 0) |
167 | goto err_out; | 168 | goto err_out; |
168 | 169 | ||
169 | dvb_adapter = &dvb->dvb_adapter; | 170 | dvb_adapter = &dvb->dvb_adapter; |
170 | 171 | ||
171 | dvbdemux = &dvb->demux; | 172 | dvbdemux = &dvb->demux; |
172 | 173 | ||
173 | dvbdemux->priv = (void *)stream; | 174 | dvbdemux->priv = (void *)stream; |
174 | 175 | ||
175 | dvbdemux->filternum = 256; | 176 | dvbdemux->filternum = 256; |
176 | dvbdemux->feednum = 256; | 177 | dvbdemux->feednum = 256; |
177 | dvbdemux->start_feed = cx18_dvb_start_feed; | 178 | dvbdemux->start_feed = cx18_dvb_start_feed; |
178 | dvbdemux->stop_feed = cx18_dvb_stop_feed; | 179 | dvbdemux->stop_feed = cx18_dvb_stop_feed; |
179 | dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | | 180 | dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | |
180 | DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING); | 181 | DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING); |
181 | ret = dvb_dmx_init(dvbdemux); | 182 | ret = dvb_dmx_init(dvbdemux); |
182 | if (ret < 0) | 183 | if (ret < 0) |
183 | goto err_dvb_unregister_adapter; | 184 | goto err_dvb_unregister_adapter; |
184 | 185 | ||
185 | dmx = &dvbdemux->dmx; | 186 | dmx = &dvbdemux->dmx; |
186 | 187 | ||
187 | dvb->hw_frontend.source = DMX_FRONTEND_0; | 188 | dvb->hw_frontend.source = DMX_FRONTEND_0; |
188 | dvb->mem_frontend.source = DMX_MEMORY_FE; | 189 | dvb->mem_frontend.source = DMX_MEMORY_FE; |
189 | dvb->dmxdev.filternum = 256; | 190 | dvb->dmxdev.filternum = 256; |
190 | dvb->dmxdev.demux = dmx; | 191 | dvb->dmxdev.demux = dmx; |
191 | 192 | ||
192 | ret = dvb_dmxdev_init(&dvb->dmxdev, dvb_adapter); | 193 | ret = dvb_dmxdev_init(&dvb->dmxdev, dvb_adapter); |
193 | if (ret < 0) | 194 | if (ret < 0) |
194 | goto err_dvb_dmx_release; | 195 | goto err_dvb_dmx_release; |
195 | 196 | ||
196 | ret = dmx->add_frontend(dmx, &dvb->hw_frontend); | 197 | ret = dmx->add_frontend(dmx, &dvb->hw_frontend); |
197 | if (ret < 0) | 198 | if (ret < 0) |
198 | goto err_dvb_dmxdev_release; | 199 | goto err_dvb_dmxdev_release; |
199 | 200 | ||
200 | ret = dmx->add_frontend(dmx, &dvb->mem_frontend); | 201 | ret = dmx->add_frontend(dmx, &dvb->mem_frontend); |
201 | if (ret < 0) | 202 | if (ret < 0) |
202 | goto err_remove_hw_frontend; | 203 | goto err_remove_hw_frontend; |
203 | 204 | ||
204 | ret = dmx->connect_frontend(dmx, &dvb->hw_frontend); | 205 | ret = dmx->connect_frontend(dmx, &dvb->hw_frontend); |
205 | if (ret < 0) | 206 | if (ret < 0) |
206 | goto err_remove_mem_frontend; | 207 | goto err_remove_mem_frontend; |
207 | 208 | ||
208 | ret = dvb_register(stream); | 209 | ret = dvb_register(stream); |
209 | if (ret < 0) | 210 | if (ret < 0) |
210 | goto err_disconnect_frontend; | 211 | goto err_disconnect_frontend; |
211 | 212 | ||
212 | dvb_net_init(dvb_adapter, &dvb->dvbnet, dmx); | 213 | dvb_net_init(dvb_adapter, &dvb->dvbnet, dmx); |
213 | 214 | ||
214 | CX18_INFO("DVB Frontend registered\n"); | 215 | CX18_INFO("DVB Frontend registered\n"); |
215 | mutex_init(&dvb->feedlock); | 216 | mutex_init(&dvb->feedlock); |
216 | dvb->enabled = 1; | 217 | dvb->enabled = 1; |
217 | return ret; | 218 | return ret; |
218 | 219 | ||
219 | err_disconnect_frontend: | 220 | err_disconnect_frontend: |
220 | dmx->disconnect_frontend(dmx); | 221 | dmx->disconnect_frontend(dmx); |
221 | err_remove_mem_frontend: | 222 | err_remove_mem_frontend: |
222 | dmx->remove_frontend(dmx, &dvb->mem_frontend); | 223 | dmx->remove_frontend(dmx, &dvb->mem_frontend); |
223 | err_remove_hw_frontend: | 224 | err_remove_hw_frontend: |
224 | dmx->remove_frontend(dmx, &dvb->hw_frontend); | 225 | dmx->remove_frontend(dmx, &dvb->hw_frontend); |
225 | err_dvb_dmxdev_release: | 226 | err_dvb_dmxdev_release: |
226 | dvb_dmxdev_release(&dvb->dmxdev); | 227 | dvb_dmxdev_release(&dvb->dmxdev); |
227 | err_dvb_dmx_release: | 228 | err_dvb_dmx_release: |
228 | dvb_dmx_release(dvbdemux); | 229 | dvb_dmx_release(dvbdemux); |
229 | err_dvb_unregister_adapter: | 230 | err_dvb_unregister_adapter: |
230 | dvb_unregister_adapter(dvb_adapter); | 231 | dvb_unregister_adapter(dvb_adapter); |
231 | err_out: | 232 | err_out: |
232 | return ret; | 233 | return ret; |
233 | } | 234 | } |
234 | 235 | ||
235 | void cx18_dvb_unregister(struct cx18_stream *stream) | 236 | void cx18_dvb_unregister(struct cx18_stream *stream) |
236 | { | 237 | { |
237 | struct cx18 *cx = stream->cx; | 238 | struct cx18 *cx = stream->cx; |
238 | struct cx18_dvb *dvb = &stream->dvb; | 239 | struct cx18_dvb *dvb = &stream->dvb; |
239 | struct dvb_adapter *dvb_adapter; | 240 | struct dvb_adapter *dvb_adapter; |
240 | struct dvb_demux *dvbdemux; | 241 | struct dvb_demux *dvbdemux; |
241 | struct dmx_demux *dmx; | 242 | struct dmx_demux *dmx; |
242 | 243 | ||
243 | CX18_INFO("unregister DVB\n"); | 244 | CX18_INFO("unregister DVB\n"); |
244 | 245 | ||
245 | dvb_adapter = &dvb->dvb_adapter; | 246 | dvb_adapter = &dvb->dvb_adapter; |
246 | dvbdemux = &dvb->demux; | 247 | dvbdemux = &dvb->demux; |
247 | dmx = &dvbdemux->dmx; | 248 | dmx = &dvbdemux->dmx; |
248 | 249 | ||
249 | dmx->close(dmx); | 250 | dmx->close(dmx); |
250 | dvb_net_release(&dvb->dvbnet); | 251 | dvb_net_release(&dvb->dvbnet); |
251 | dmx->remove_frontend(dmx, &dvb->mem_frontend); | 252 | dmx->remove_frontend(dmx, &dvb->mem_frontend); |
252 | dmx->remove_frontend(dmx, &dvb->hw_frontend); | 253 | dmx->remove_frontend(dmx, &dvb->hw_frontend); |
253 | dvb_dmxdev_release(&dvb->dmxdev); | 254 | dvb_dmxdev_release(&dvb->dmxdev); |
254 | dvb_dmx_release(dvbdemux); | 255 | dvb_dmx_release(dvbdemux); |
255 | dvb_unregister_frontend(dvb->fe); | 256 | dvb_unregister_frontend(dvb->fe); |
256 | dvb_frontend_detach(dvb->fe); | 257 | dvb_frontend_detach(dvb->fe); |
257 | dvb_unregister_adapter(dvb_adapter); | 258 | dvb_unregister_adapter(dvb_adapter); |
258 | } | 259 | } |
259 | 260 | ||
260 | /* All the DVB attach calls go here, this function get's modified | 261 | /* All the DVB attach calls go here, this function get's modified |
261 | * for each new card. No other function in this file needs | 262 | * for each new card. No other function in this file needs |
262 | * to change. | 263 | * to change. |
263 | */ | 264 | */ |
264 | static int dvb_register(struct cx18_stream *stream) | 265 | static int dvb_register(struct cx18_stream *stream) |
265 | { | 266 | { |
266 | struct cx18_dvb *dvb = &stream->dvb; | 267 | struct cx18_dvb *dvb = &stream->dvb; |
267 | struct cx18 *cx = stream->cx; | 268 | struct cx18 *cx = stream->cx; |
268 | int ret = 0; | 269 | int ret = 0; |
269 | 270 | ||
270 | switch (cx->card->type) { | 271 | switch (cx->card->type) { |
271 | case CX18_CARD_HVR_1600_ESMT: | 272 | case CX18_CARD_HVR_1600_ESMT: |
272 | case CX18_CARD_HVR_1600_SAMSUNG: | 273 | case CX18_CARD_HVR_1600_SAMSUNG: |
273 | dvb->fe = dvb_attach(s5h1409_attach, | 274 | dvb->fe = dvb_attach(s5h1409_attach, |
274 | &hauppauge_hvr1600_config, | 275 | &hauppauge_hvr1600_config, |
275 | &cx->i2c_adap[0]); | 276 | &cx->i2c_adap[0]); |
276 | if (dvb->fe != NULL) { | 277 | if (dvb->fe != NULL) { |
277 | dvb_attach(mxl5005s_attach, dvb->fe, | 278 | dvb_attach(mxl5005s_attach, dvb->fe, |
278 | &cx->i2c_adap[0], | 279 | &cx->i2c_adap[0], |
279 | &hauppauge_hvr1600_tuner); | 280 | &hauppauge_hvr1600_tuner); |
280 | ret = 0; | 281 | ret = 0; |
281 | } | 282 | } |
282 | break; | 283 | break; |
283 | default: | 284 | default: |
284 | /* No Digital Tv Support */ | 285 | /* No Digital Tv Support */ |
285 | break; | 286 | break; |
286 | } | 287 | } |
287 | 288 | ||
288 | if (dvb->fe == NULL) { | 289 | if (dvb->fe == NULL) { |
289 | CX18_ERR("frontend initialization failed\n"); | 290 | CX18_ERR("frontend initialization failed\n"); |
290 | return -1; | 291 | return -1; |
291 | } | 292 | } |
292 | 293 | ||
293 | ret = dvb_register_frontend(&dvb->dvb_adapter, dvb->fe); | 294 | ret = dvb_register_frontend(&dvb->dvb_adapter, dvb->fe); |
294 | if (ret < 0) { | 295 | if (ret < 0) { |
295 | if (dvb->fe->ops.release) | 296 | if (dvb->fe->ops.release) |
296 | dvb->fe->ops.release(dvb->fe); | 297 | dvb->fe->ops.release(dvb->fe); |
297 | return ret; | 298 | return ret; |
298 | } | 299 | } |
299 | 300 | ||
300 | return ret; | 301 | return ret; |
301 | } | 302 | } |
302 | 303 |
drivers/media/video/cx18/cx18-firmware.c
1 | /* | 1 | /* |
2 | * cx18 firmware functions | 2 | * cx18 firmware functions |
3 | * | 3 | * |
4 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | 4 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> |
5 | * | 5 | * |
6 | * This program is free software; you can redistribute it and/or modify | 6 | * This program is free software; you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License as published by | 7 | * it under the terms of the GNU General Public License as published by |
8 | * the Free Software Foundation; either version 2 of the License, or | 8 | * the Free Software Foundation; either version 2 of the License, or |
9 | * (at your option) any later version. | 9 | * (at your option) any later version. |
10 | * | 10 | * |
11 | * This program is distributed in the hope that it will be useful, | 11 | * This program is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | * GNU General Public License for more details. | 14 | * GNU General Public License for more details. |
15 | * | 15 | * |
16 | * You should have received a copy of the GNU General Public License | 16 | * You should have received a copy of the GNU General Public License |
17 | * along with this program; if not, write to the Free Software | 17 | * along with this program; if not, write to the Free Software |
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
19 | * 02111-1307 USA | 19 | * 02111-1307 USA |
20 | */ | 20 | */ |
21 | 21 | ||
22 | #include "cx18-driver.h" | 22 | #include "cx18-driver.h" |
23 | #include "cx18-io.h" | ||
23 | #include "cx18-scb.h" | 24 | #include "cx18-scb.h" |
24 | #include "cx18-irq.h" | 25 | #include "cx18-irq.h" |
25 | #include "cx18-firmware.h" | 26 | #include "cx18-firmware.h" |
26 | #include "cx18-cards.h" | 27 | #include "cx18-cards.h" |
27 | #include <linux/firmware.h> | 28 | #include <linux/firmware.h> |
28 | 29 | ||
29 | #define CX18_PROC_SOFT_RESET 0xc70010 | 30 | #define CX18_PROC_SOFT_RESET 0xc70010 |
30 | #define CX18_DDR_SOFT_RESET 0xc70014 | 31 | #define CX18_DDR_SOFT_RESET 0xc70014 |
31 | #define CX18_CLOCK_SELECT1 0xc71000 | 32 | #define CX18_CLOCK_SELECT1 0xc71000 |
32 | #define CX18_CLOCK_SELECT2 0xc71004 | 33 | #define CX18_CLOCK_SELECT2 0xc71004 |
33 | #define CX18_HALF_CLOCK_SELECT1 0xc71008 | 34 | #define CX18_HALF_CLOCK_SELECT1 0xc71008 |
34 | #define CX18_HALF_CLOCK_SELECT2 0xc7100C | 35 | #define CX18_HALF_CLOCK_SELECT2 0xc7100C |
35 | #define CX18_CLOCK_POLARITY1 0xc71010 | 36 | #define CX18_CLOCK_POLARITY1 0xc71010 |
36 | #define CX18_CLOCK_POLARITY2 0xc71014 | 37 | #define CX18_CLOCK_POLARITY2 0xc71014 |
37 | #define CX18_ADD_DELAY_ENABLE1 0xc71018 | 38 | #define CX18_ADD_DELAY_ENABLE1 0xc71018 |
38 | #define CX18_ADD_DELAY_ENABLE2 0xc7101C | 39 | #define CX18_ADD_DELAY_ENABLE2 0xc7101C |
39 | #define CX18_CLOCK_ENABLE1 0xc71020 | 40 | #define CX18_CLOCK_ENABLE1 0xc71020 |
40 | #define CX18_CLOCK_ENABLE2 0xc71024 | 41 | #define CX18_CLOCK_ENABLE2 0xc71024 |
41 | 42 | ||
42 | #define CX18_REG_BUS_TIMEOUT_EN 0xc72024 | 43 | #define CX18_REG_BUS_TIMEOUT_EN 0xc72024 |
43 | 44 | ||
44 | #define CX18_FAST_CLOCK_PLL_INT 0xc78000 | 45 | #define CX18_FAST_CLOCK_PLL_INT 0xc78000 |
45 | #define CX18_FAST_CLOCK_PLL_FRAC 0xc78004 | 46 | #define CX18_FAST_CLOCK_PLL_FRAC 0xc78004 |
46 | #define CX18_FAST_CLOCK_PLL_POST 0xc78008 | 47 | #define CX18_FAST_CLOCK_PLL_POST 0xc78008 |
47 | #define CX18_FAST_CLOCK_PLL_PRESCALE 0xc7800C | 48 | #define CX18_FAST_CLOCK_PLL_PRESCALE 0xc7800C |
48 | #define CX18_FAST_CLOCK_PLL_ADJUST_BANDWIDTH 0xc78010 | 49 | #define CX18_FAST_CLOCK_PLL_ADJUST_BANDWIDTH 0xc78010 |
49 | 50 | ||
50 | #define CX18_SLOW_CLOCK_PLL_INT 0xc78014 | 51 | #define CX18_SLOW_CLOCK_PLL_INT 0xc78014 |
51 | #define CX18_SLOW_CLOCK_PLL_FRAC 0xc78018 | 52 | #define CX18_SLOW_CLOCK_PLL_FRAC 0xc78018 |
52 | #define CX18_SLOW_CLOCK_PLL_POST 0xc7801C | 53 | #define CX18_SLOW_CLOCK_PLL_POST 0xc7801C |
53 | #define CX18_MPEG_CLOCK_PLL_INT 0xc78040 | 54 | #define CX18_MPEG_CLOCK_PLL_INT 0xc78040 |
54 | #define CX18_MPEG_CLOCK_PLL_FRAC 0xc78044 | 55 | #define CX18_MPEG_CLOCK_PLL_FRAC 0xc78044 |
55 | #define CX18_MPEG_CLOCK_PLL_POST 0xc78048 | 56 | #define CX18_MPEG_CLOCK_PLL_POST 0xc78048 |
56 | #define CX18_PLL_POWER_DOWN 0xc78088 | 57 | #define CX18_PLL_POWER_DOWN 0xc78088 |
57 | #define CX18_SW1_INT_STATUS 0xc73104 | 58 | #define CX18_SW1_INT_STATUS 0xc73104 |
58 | #define CX18_SW1_INT_ENABLE_PCI 0xc7311C | 59 | #define CX18_SW1_INT_ENABLE_PCI 0xc7311C |
59 | #define CX18_SW2_INT_SET 0xc73140 | 60 | #define CX18_SW2_INT_SET 0xc73140 |
60 | #define CX18_SW2_INT_STATUS 0xc73144 | 61 | #define CX18_SW2_INT_STATUS 0xc73144 |
61 | #define CX18_ADEC_CONTROL 0xc78120 | 62 | #define CX18_ADEC_CONTROL 0xc78120 |
62 | 63 | ||
63 | #define CX18_DDR_REQUEST_ENABLE 0xc80000 | 64 | #define CX18_DDR_REQUEST_ENABLE 0xc80000 |
64 | #define CX18_DDR_CHIP_CONFIG 0xc80004 | 65 | #define CX18_DDR_CHIP_CONFIG 0xc80004 |
65 | #define CX18_DDR_REFRESH 0xc80008 | 66 | #define CX18_DDR_REFRESH 0xc80008 |
66 | #define CX18_DDR_TIMING1 0xc8000C | 67 | #define CX18_DDR_TIMING1 0xc8000C |
67 | #define CX18_DDR_TIMING2 0xc80010 | 68 | #define CX18_DDR_TIMING2 0xc80010 |
68 | #define CX18_DDR_POWER_REG 0xc8001C | 69 | #define CX18_DDR_POWER_REG 0xc8001C |
69 | 70 | ||
70 | #define CX18_DDR_TUNE_LANE 0xc80048 | 71 | #define CX18_DDR_TUNE_LANE 0xc80048 |
71 | #define CX18_DDR_INITIAL_EMRS 0xc80054 | 72 | #define CX18_DDR_INITIAL_EMRS 0xc80054 |
72 | #define CX18_DDR_MB_PER_ROW_7 0xc8009C | 73 | #define CX18_DDR_MB_PER_ROW_7 0xc8009C |
73 | #define CX18_DDR_BASE_63_ADDR 0xc804FC | 74 | #define CX18_DDR_BASE_63_ADDR 0xc804FC |
74 | 75 | ||
75 | #define CX18_WMB_CLIENT02 0xc90108 | 76 | #define CX18_WMB_CLIENT02 0xc90108 |
76 | #define CX18_WMB_CLIENT05 0xc90114 | 77 | #define CX18_WMB_CLIENT05 0xc90114 |
77 | #define CX18_WMB_CLIENT06 0xc90118 | 78 | #define CX18_WMB_CLIENT06 0xc90118 |
78 | #define CX18_WMB_CLIENT07 0xc9011C | 79 | #define CX18_WMB_CLIENT07 0xc9011C |
79 | #define CX18_WMB_CLIENT08 0xc90120 | 80 | #define CX18_WMB_CLIENT08 0xc90120 |
80 | #define CX18_WMB_CLIENT09 0xc90124 | 81 | #define CX18_WMB_CLIENT09 0xc90124 |
81 | #define CX18_WMB_CLIENT10 0xc90128 | 82 | #define CX18_WMB_CLIENT10 0xc90128 |
82 | #define CX18_WMB_CLIENT11 0xc9012C | 83 | #define CX18_WMB_CLIENT11 0xc9012C |
83 | #define CX18_WMB_CLIENT12 0xc90130 | 84 | #define CX18_WMB_CLIENT12 0xc90130 |
84 | #define CX18_WMB_CLIENT13 0xc90134 | 85 | #define CX18_WMB_CLIENT13 0xc90134 |
85 | #define CX18_WMB_CLIENT14 0xc90138 | 86 | #define CX18_WMB_CLIENT14 0xc90138 |
86 | 87 | ||
87 | #define CX18_DSP0_INTERRUPT_MASK 0xd0004C | 88 | #define CX18_DSP0_INTERRUPT_MASK 0xd0004C |
88 | 89 | ||
89 | #define APU_ROM_SYNC1 0x6D676553 /* "mgeS" */ | 90 | #define APU_ROM_SYNC1 0x6D676553 /* "mgeS" */ |
90 | #define APU_ROM_SYNC2 0x72646548 /* "rdeH" */ | 91 | #define APU_ROM_SYNC2 0x72646548 /* "rdeH" */ |
91 | 92 | ||
92 | struct cx18_apu_rom_seghdr { | 93 | struct cx18_apu_rom_seghdr { |
93 | u32 sync1; | 94 | u32 sync1; |
94 | u32 sync2; | 95 | u32 sync2; |
95 | u32 addr; | 96 | u32 addr; |
96 | u32 size; | 97 | u32 size; |
97 | }; | 98 | }; |
98 | 99 | ||
99 | static int load_cpu_fw_direct(const char *fn, u8 __iomem *mem, struct cx18 *cx) | 100 | static int load_cpu_fw_direct(const char *fn, u8 __iomem *mem, struct cx18 *cx) |
100 | { | 101 | { |
101 | const struct firmware *fw = NULL; | 102 | const struct firmware *fw = NULL; |
102 | int i, j; | 103 | int i, j; |
103 | unsigned size; | 104 | unsigned size; |
104 | u32 __iomem *dst = (u32 __iomem *)mem; | 105 | u32 __iomem *dst = (u32 __iomem *)mem; |
105 | const u32 *src; | 106 | const u32 *src; |
106 | 107 | ||
107 | if (request_firmware(&fw, fn, &cx->dev->dev)) { | 108 | if (request_firmware(&fw, fn, &cx->dev->dev)) { |
108 | CX18_ERR("Unable to open firmware %s\n", fn); | 109 | CX18_ERR("Unable to open firmware %s\n", fn); |
109 | CX18_ERR("Did you put the firmware in the hotplug firmware directory?\n"); | 110 | CX18_ERR("Did you put the firmware in the hotplug firmware directory?\n"); |
110 | return -ENOMEM; | 111 | return -ENOMEM; |
111 | } | 112 | } |
112 | 113 | ||
113 | src = (const u32 *)fw->data; | 114 | src = (const u32 *)fw->data; |
114 | 115 | ||
115 | for (i = 0; i < fw->size; i += 4096) { | 116 | for (i = 0; i < fw->size; i += 4096) { |
116 | setup_page(i); | 117 | cx18_setup_page(cx, i); |
117 | for (j = i; j < fw->size && j < i + 4096; j += 4) { | 118 | for (j = i; j < fw->size && j < i + 4096; j += 4) { |
118 | /* no need for endianness conversion on the ppc */ | 119 | /* no need for endianness conversion on the ppc */ |
119 | __raw_writel(*src, dst); | 120 | cx18_raw_writel(cx, *src, dst); |
120 | if (__raw_readl(dst) != *src) { | 121 | if (cx18_raw_readl(cx, dst) != *src) { |
121 | CX18_ERR("Mismatch at offset %x\n", i); | 122 | CX18_ERR("Mismatch at offset %x\n", i); |
122 | release_firmware(fw); | 123 | release_firmware(fw); |
123 | return -EIO; | 124 | return -EIO; |
124 | } | 125 | } |
125 | dst++; | 126 | dst++; |
126 | src++; | 127 | src++; |
127 | } | 128 | } |
128 | } | 129 | } |
129 | if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags)) | 130 | if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags)) |
130 | CX18_INFO("loaded %s firmware (%zd bytes)\n", fn, fw->size); | 131 | CX18_INFO("loaded %s firmware (%zd bytes)\n", fn, fw->size); |
131 | size = fw->size; | 132 | size = fw->size; |
132 | release_firmware(fw); | 133 | release_firmware(fw); |
133 | return size; | 134 | return size; |
134 | } | 135 | } |
135 | 136 | ||
136 | static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx) | 137 | static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx) |
137 | { | 138 | { |
138 | const struct firmware *fw = NULL; | 139 | const struct firmware *fw = NULL; |
139 | int i, j; | 140 | int i, j; |
140 | unsigned size; | 141 | unsigned size; |
141 | const u32 *src; | 142 | const u32 *src; |
142 | struct cx18_apu_rom_seghdr seghdr; | 143 | struct cx18_apu_rom_seghdr seghdr; |
143 | const u8 *vers; | 144 | const u8 *vers; |
144 | u32 offset = 0; | 145 | u32 offset = 0; |
145 | u32 apu_version = 0; | 146 | u32 apu_version = 0; |
146 | int sz; | 147 | int sz; |
147 | 148 | ||
148 | if (request_firmware(&fw, fn, &cx->dev->dev)) { | 149 | if (request_firmware(&fw, fn, &cx->dev->dev)) { |
149 | CX18_ERR("unable to open firmware %s\n", fn); | 150 | CX18_ERR("unable to open firmware %s\n", fn); |
150 | CX18_ERR("did you put the firmware in the hotplug firmware directory?\n"); | 151 | CX18_ERR("did you put the firmware in the hotplug firmware directory?\n"); |
151 | return -ENOMEM; | 152 | return -ENOMEM; |
152 | } | 153 | } |
153 | 154 | ||
154 | src = (const u32 *)fw->data; | 155 | src = (const u32 *)fw->data; |
155 | vers = fw->data + sizeof(seghdr); | 156 | vers = fw->data + sizeof(seghdr); |
156 | sz = fw->size; | 157 | sz = fw->size; |
157 | 158 | ||
158 | apu_version = (vers[0] << 24) | (vers[4] << 16) | vers[32]; | 159 | apu_version = (vers[0] << 24) | (vers[4] << 16) | vers[32]; |
159 | while (offset + sizeof(seghdr) < fw->size) { | 160 | while (offset + sizeof(seghdr) < fw->size) { |
160 | /* TODO: byteswapping */ | 161 | /* TODO: byteswapping */ |
161 | memcpy(&seghdr, src + offset / 4, sizeof(seghdr)); | 162 | memcpy(&seghdr, src + offset / 4, sizeof(seghdr)); |
162 | offset += sizeof(seghdr); | 163 | offset += sizeof(seghdr); |
163 | if (seghdr.sync1 != APU_ROM_SYNC1 || | 164 | if (seghdr.sync1 != APU_ROM_SYNC1 || |
164 | seghdr.sync2 != APU_ROM_SYNC2) { | 165 | seghdr.sync2 != APU_ROM_SYNC2) { |
165 | offset += seghdr.size; | 166 | offset += seghdr.size; |
166 | continue; | 167 | continue; |
167 | } | 168 | } |
168 | CX18_DEBUG_INFO("load segment %x-%x\n", seghdr.addr, | 169 | CX18_DEBUG_INFO("load segment %x-%x\n", seghdr.addr, |
169 | seghdr.addr + seghdr.size - 1); | 170 | seghdr.addr + seghdr.size - 1); |
170 | if (offset + seghdr.size > sz) | 171 | if (offset + seghdr.size > sz) |
171 | break; | 172 | break; |
172 | for (i = 0; i < seghdr.size; i += 4096) { | 173 | for (i = 0; i < seghdr.size; i += 4096) { |
173 | setup_page(offset + i); | 174 | cx18_setup_page(cx, offset + i); |
174 | for (j = i; j < seghdr.size && j < i + 4096; j += 4) { | 175 | for (j = i; j < seghdr.size && j < i + 4096; j += 4) { |
175 | /* no need for endianness conversion on the ppc */ | 176 | /* no need for endianness conversion on the ppc */ |
176 | __raw_writel(src[(offset + j) / 4], dst + seghdr.addr + j); | 177 | cx18_raw_writel(cx, src[(offset + j) / 4], |
177 | if (__raw_readl(dst + seghdr.addr + j) != src[(offset + j) / 4]) { | 178 | dst + seghdr.addr + j); |
178 | CX18_ERR("Mismatch at offset %x\n", offset + j); | 179 | if (cx18_raw_readl(cx, dst + seghdr.addr + j) |
180 | != src[(offset + j) / 4]) { | ||
181 | CX18_ERR("Mismatch at offset %x\n", | ||
182 | offset + j); | ||
179 | release_firmware(fw); | 183 | release_firmware(fw); |
180 | return -EIO; | 184 | return -EIO; |
181 | } | 185 | } |
182 | } | 186 | } |
183 | } | 187 | } |
184 | offset += seghdr.size; | 188 | offset += seghdr.size; |
185 | } | 189 | } |
186 | if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags)) | 190 | if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags)) |
187 | CX18_INFO("loaded %s firmware V%08x (%zd bytes)\n", | 191 | CX18_INFO("loaded %s firmware V%08x (%zd bytes)\n", |
188 | fn, apu_version, fw->size); | 192 | fn, apu_version, fw->size); |
189 | size = fw->size; | 193 | size = fw->size; |
190 | release_firmware(fw); | 194 | release_firmware(fw); |
191 | /* Clear bit0 for APU to start from 0 */ | 195 | /* Clear bit0 for APU to start from 0 */ |
192 | write_reg(read_reg(0xc72030) & ~1, 0xc72030); | 196 | cx18_write_reg(cx, cx18_read_reg(cx, 0xc72030) & ~1, 0xc72030); |
193 | return size; | 197 | return size; |
194 | } | 198 | } |
195 | 199 | ||
196 | void cx18_halt_firmware(struct cx18 *cx) | 200 | void cx18_halt_firmware(struct cx18 *cx) |
197 | { | 201 | { |
198 | CX18_DEBUG_INFO("Preparing for firmware halt.\n"); | 202 | CX18_DEBUG_INFO("Preparing for firmware halt.\n"); |
199 | write_reg(0x000F000F, CX18_PROC_SOFT_RESET); /* stop the fw */ | 203 | cx18_write_reg(cx, 0x000F000F, CX18_PROC_SOFT_RESET); /* stop the fw */ |
200 | write_reg(0x00020002, CX18_ADEC_CONTROL); | 204 | cx18_write_reg(cx, 0x00020002, CX18_ADEC_CONTROL); |
201 | } | 205 | } |
202 | 206 | ||
203 | void cx18_init_power(struct cx18 *cx, int lowpwr) | 207 | void cx18_init_power(struct cx18 *cx, int lowpwr) |
204 | { | 208 | { |
205 | /* power-down Spare and AOM PLLs */ | 209 | /* power-down Spare and AOM PLLs */ |
206 | /* power-up fast, slow and mpeg PLLs */ | 210 | /* power-up fast, slow and mpeg PLLs */ |
207 | write_reg(0x00000008, CX18_PLL_POWER_DOWN); | 211 | cx18_write_reg(cx, 0x00000008, CX18_PLL_POWER_DOWN); |
208 | 212 | ||
209 | /* ADEC out of sleep */ | 213 | /* ADEC out of sleep */ |
210 | write_reg(0x00020000, CX18_ADEC_CONTROL); | 214 | cx18_write_reg(cx, 0x00020000, CX18_ADEC_CONTROL); |
211 | 215 | ||
212 | /* The fast clock is at 200/245 MHz */ | 216 | /* The fast clock is at 200/245 MHz */ |
213 | write_reg(lowpwr ? 0xD : 0x11, CX18_FAST_CLOCK_PLL_INT); | 217 | cx18_write_reg(cx, lowpwr ? 0xD : 0x11, CX18_FAST_CLOCK_PLL_INT); |
214 | write_reg(lowpwr ? 0x1EFBF37 : 0x038E3D7, CX18_FAST_CLOCK_PLL_FRAC); | 218 | cx18_write_reg(cx, lowpwr ? 0x1EFBF37 : 0x038E3D7, |
219 | CX18_FAST_CLOCK_PLL_FRAC); | ||
215 | 220 | ||
216 | write_reg(2, CX18_FAST_CLOCK_PLL_POST); | 221 | cx18_write_reg(cx, 2, CX18_FAST_CLOCK_PLL_POST); |
217 | write_reg(1, CX18_FAST_CLOCK_PLL_PRESCALE); | 222 | cx18_write_reg(cx, 1, CX18_FAST_CLOCK_PLL_PRESCALE); |
218 | write_reg(4, CX18_FAST_CLOCK_PLL_ADJUST_BANDWIDTH); | 223 | cx18_write_reg(cx, 4, CX18_FAST_CLOCK_PLL_ADJUST_BANDWIDTH); |
219 | 224 | ||
220 | /* set slow clock to 125/120 MHz */ | 225 | /* set slow clock to 125/120 MHz */ |
221 | write_reg(lowpwr ? 0x11 : 0x10, CX18_SLOW_CLOCK_PLL_INT); | 226 | cx18_write_reg(cx, lowpwr ? 0x11 : 0x10, CX18_SLOW_CLOCK_PLL_INT); |
222 | write_reg(lowpwr ? 0xEBAF05 : 0x18618A8, CX18_SLOW_CLOCK_PLL_FRAC); | 227 | cx18_write_reg(cx, lowpwr ? 0xEBAF05 : 0x18618A8, |
223 | write_reg(4, CX18_SLOW_CLOCK_PLL_POST); | 228 | CX18_SLOW_CLOCK_PLL_FRAC); |
229 | cx18_write_reg(cx, 4, CX18_SLOW_CLOCK_PLL_POST); | ||
224 | 230 | ||
225 | /* mpeg clock pll 54MHz */ | 231 | /* mpeg clock pll 54MHz */ |
226 | write_reg(0xF, CX18_MPEG_CLOCK_PLL_INT); | 232 | cx18_write_reg(cx, 0xF, CX18_MPEG_CLOCK_PLL_INT); |
227 | write_reg(0x2BCFEF, CX18_MPEG_CLOCK_PLL_FRAC); | 233 | cx18_write_reg(cx, 0x2BCFEF, CX18_MPEG_CLOCK_PLL_FRAC); |
228 | write_reg(8, CX18_MPEG_CLOCK_PLL_POST); | 234 | cx18_write_reg(cx, 8, CX18_MPEG_CLOCK_PLL_POST); |
229 | 235 | ||
230 | /* Defaults */ | 236 | /* Defaults */ |
231 | /* APU = SC or SC/2 = 125/62.5 */ | 237 | /* APU = SC or SC/2 = 125/62.5 */ |
232 | /* EPU = SC = 125 */ | 238 | /* EPU = SC = 125 */ |
233 | /* DDR = FC = 180 */ | 239 | /* DDR = FC = 180 */ |
234 | /* ENC = SC = 125 */ | 240 | /* ENC = SC = 125 */ |
235 | /* AI1 = SC = 125 */ | 241 | /* AI1 = SC = 125 */ |
236 | /* VIM2 = disabled */ | 242 | /* VIM2 = disabled */ |
237 | /* PCI = FC/2 = 90 */ | 243 | /* PCI = FC/2 = 90 */ |
238 | /* AI2 = disabled */ | 244 | /* AI2 = disabled */ |
239 | /* DEMUX = disabled */ | 245 | /* DEMUX = disabled */ |
240 | /* AO = SC/2 = 62.5 */ | 246 | /* AO = SC/2 = 62.5 */ |
241 | /* SER = 54MHz */ | 247 | /* SER = 54MHz */ |
242 | /* VFC = disabled */ | 248 | /* VFC = disabled */ |
243 | /* USB = disabled */ | 249 | /* USB = disabled */ |
244 | 250 | ||
245 | write_reg(lowpwr ? 0xFFFF0020 : 0x00060004, CX18_CLOCK_SELECT1); | 251 | cx18_write_reg(cx, lowpwr ? 0xFFFF0020 : 0x00060004, |
246 | write_reg(lowpwr ? 0xFFFF0004 : 0x00060006, CX18_CLOCK_SELECT2); | 252 | CX18_CLOCK_SELECT1); |
253 | cx18_write_reg(cx, lowpwr ? 0xFFFF0004 : 0x00060006, | ||
254 | CX18_CLOCK_SELECT2); | ||
247 | 255 | ||
248 | write_reg(0xFFFF0002, CX18_HALF_CLOCK_SELECT1); | 256 | cx18_write_reg(cx, 0xFFFF0002, CX18_HALF_CLOCK_SELECT1); |
249 | write_reg(0xFFFF0104, CX18_HALF_CLOCK_SELECT2); | 257 | cx18_write_reg(cx, 0xFFFF0104, CX18_HALF_CLOCK_SELECT2); |
250 | 258 | ||
251 | write_reg(0xFFFF9026, CX18_CLOCK_ENABLE1); | 259 | cx18_write_reg(cx, 0xFFFF9026, CX18_CLOCK_ENABLE1); |
252 | write_reg(0xFFFF3105, CX18_CLOCK_ENABLE2); | 260 | cx18_write_reg(cx, 0xFFFF3105, CX18_CLOCK_ENABLE2); |
253 | } | 261 | } |
254 | 262 | ||
255 | void cx18_init_memory(struct cx18 *cx) | 263 | void cx18_init_memory(struct cx18 *cx) |
256 | { | 264 | { |
257 | cx18_msleep_timeout(10, 0); | 265 | cx18_msleep_timeout(10, 0); |
258 | write_reg(0x10000, CX18_DDR_SOFT_RESET); | 266 | cx18_write_reg(cx, 0x10000, CX18_DDR_SOFT_RESET); |
259 | cx18_msleep_timeout(10, 0); | 267 | cx18_msleep_timeout(10, 0); |
260 | 268 | ||
261 | write_reg(cx->card->ddr.chip_config, CX18_DDR_CHIP_CONFIG); | 269 | cx18_write_reg(cx, cx->card->ddr.chip_config, CX18_DDR_CHIP_CONFIG); |
262 | 270 | ||
263 | cx18_msleep_timeout(10, 0); | 271 | cx18_msleep_timeout(10, 0); |
264 | 272 | ||
265 | write_reg(cx->card->ddr.refresh, CX18_DDR_REFRESH); | 273 | cx18_write_reg(cx, cx->card->ddr.refresh, CX18_DDR_REFRESH); |
266 | write_reg(cx->card->ddr.timing1, CX18_DDR_TIMING1); | 274 | cx18_write_reg(cx, cx->card->ddr.timing1, CX18_DDR_TIMING1); |
267 | write_reg(cx->card->ddr.timing2, CX18_DDR_TIMING2); | 275 | cx18_write_reg(cx, cx->card->ddr.timing2, CX18_DDR_TIMING2); |
268 | 276 | ||
269 | cx18_msleep_timeout(10, 0); | 277 | cx18_msleep_timeout(10, 0); |
270 | 278 | ||
271 | /* Initialize DQS pad time */ | 279 | /* Initialize DQS pad time */ |
272 | write_reg(cx->card->ddr.tune_lane, CX18_DDR_TUNE_LANE); | 280 | cx18_write_reg(cx, cx->card->ddr.tune_lane, CX18_DDR_TUNE_LANE); |
273 | write_reg(cx->card->ddr.initial_emrs, CX18_DDR_INITIAL_EMRS); | 281 | cx18_write_reg(cx, cx->card->ddr.initial_emrs, CX18_DDR_INITIAL_EMRS); |
274 | 282 | ||
275 | cx18_msleep_timeout(10, 0); | 283 | cx18_msleep_timeout(10, 0); |
276 | 284 | ||
277 | write_reg(0x20000, CX18_DDR_SOFT_RESET); | 285 | cx18_write_reg(cx, 0x20000, CX18_DDR_SOFT_RESET); |
278 | cx18_msleep_timeout(10, 0); | 286 | cx18_msleep_timeout(10, 0); |
279 | 287 | ||
280 | /* use power-down mode when idle */ | 288 | /* use power-down mode when idle */ |
281 | write_reg(0x00000010, CX18_DDR_POWER_REG); | 289 | cx18_write_reg(cx, 0x00000010, CX18_DDR_POWER_REG); |
282 | 290 | ||
283 | write_reg(0x10001, CX18_REG_BUS_TIMEOUT_EN); | 291 | cx18_write_reg(cx, 0x10001, CX18_REG_BUS_TIMEOUT_EN); |
284 | 292 | ||
285 | write_reg(0x48, CX18_DDR_MB_PER_ROW_7); | 293 | cx18_write_reg(cx, 0x48, CX18_DDR_MB_PER_ROW_7); |
286 | write_reg(0xE0000, CX18_DDR_BASE_63_ADDR); | 294 | cx18_write_reg(cx, 0xE0000, CX18_DDR_BASE_63_ADDR); |
287 | 295 | ||
288 | write_reg(0x00000101, CX18_WMB_CLIENT02); /* AO */ | 296 | cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT02); /* AO */ |
289 | write_reg(0x00000101, CX18_WMB_CLIENT09); /* AI2 */ | 297 | cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT09); /* AI2 */ |
290 | write_reg(0x00000101, CX18_WMB_CLIENT05); /* VIM1 */ | 298 | cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT05); /* VIM1 */ |
291 | write_reg(0x00000101, CX18_WMB_CLIENT06); /* AI1 */ | 299 | cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT06); /* AI1 */ |
292 | write_reg(0x00000101, CX18_WMB_CLIENT07); /* 3D comb */ | 300 | cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT07); /* 3D comb */ |
293 | write_reg(0x00000101, CX18_WMB_CLIENT10); /* ME */ | 301 | cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT10); /* ME */ |
294 | write_reg(0x00000101, CX18_WMB_CLIENT12); /* ENC */ | 302 | cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT12); /* ENC */ |
295 | write_reg(0x00000101, CX18_WMB_CLIENT13); /* PK */ | 303 | cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT13); /* PK */ |
296 | write_reg(0x00000101, CX18_WMB_CLIENT11); /* RC */ | 304 | cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT11); /* RC */ |
297 | write_reg(0x00000101, CX18_WMB_CLIENT14); /* AVO */ | 305 | cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT14); /* AVO */ |
298 | } | 306 | } |
299 | 307 | ||
300 | int cx18_firmware_init(struct cx18 *cx) | 308 | int cx18_firmware_init(struct cx18 *cx) |
301 | { | 309 | { |
302 | /* Allow chip to control CLKRUN */ | 310 | /* Allow chip to control CLKRUN */ |
303 | write_reg(0x5, CX18_DSP0_INTERRUPT_MASK); | 311 | cx18_write_reg(cx, 0x5, CX18_DSP0_INTERRUPT_MASK); |
304 | 312 | ||
305 | write_reg(0x000F000F, CX18_PROC_SOFT_RESET); /* stop the fw */ | 313 | cx18_write_reg(cx, 0x000F000F, CX18_PROC_SOFT_RESET); /* stop the fw */ |
306 | 314 | ||
307 | cx18_msleep_timeout(1, 0); | 315 | cx18_msleep_timeout(1, 0); |
308 | 316 | ||
309 | sw1_irq_enable(IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU); | 317 | cx18_sw1_irq_enable(cx, IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU); |
310 | sw2_irq_enable(IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK); | 318 | cx18_sw2_irq_enable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK); |
311 | 319 | ||
312 | /* Only if the processor is not running */ | 320 | /* Only if the processor is not running */ |
313 | if (read_reg(CX18_PROC_SOFT_RESET) & 8) { | 321 | if (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 8) { |
314 | int sz = load_apu_fw_direct("v4l-cx23418-apu.fw", | 322 | int sz = load_apu_fw_direct("v4l-cx23418-apu.fw", |
315 | cx->enc_mem, cx); | 323 | cx->enc_mem, cx); |
316 | 324 | ||
317 | write_enc(0xE51FF004, 0); | 325 | cx18_write_enc(cx, 0xE51FF004, 0); |
318 | write_enc(0xa00000, 4); /* todo: not hardcoded */ | 326 | cx18_write_enc(cx, 0xa00000, 4); /* todo: not hardcoded */ |
319 | write_reg(0x00010000, CX18_PROC_SOFT_RESET); /* Start APU */ | 327 | /* Start APU */ |
328 | cx18_write_reg(cx, 0x00010000, CX18_PROC_SOFT_RESET); | ||
320 | cx18_msleep_timeout(500, 0); | 329 | cx18_msleep_timeout(500, 0); |
321 | 330 | ||
322 | sz = sz <= 0 ? sz : load_cpu_fw_direct("v4l-cx23418-cpu.fw", | 331 | sz = sz <= 0 ? sz : load_cpu_fw_direct("v4l-cx23418-cpu.fw", |
323 | cx->enc_mem, cx); | 332 | cx->enc_mem, cx); |
324 | 333 | ||
325 | if (sz > 0) { | 334 | if (sz > 0) { |
326 | int retries = 0; | 335 | int retries = 0; |
327 | 336 | ||
328 | /* start the CPU */ | 337 | /* start the CPU */ |
329 | write_reg(0x00080000, CX18_PROC_SOFT_RESET); | 338 | cx18_write_reg(cx, 0x00080000, CX18_PROC_SOFT_RESET); |
330 | while (retries++ < 50) { /* Loop for max 500mS */ | 339 | while (retries++ < 50) { /* Loop for max 500mS */ |
331 | if ((read_reg(CX18_PROC_SOFT_RESET) & 1) == 0) | 340 | if ((cx18_read_reg(cx, CX18_PROC_SOFT_RESET) |
341 | & 1) == 0) | ||
332 | break; | 342 | break; |
333 | cx18_msleep_timeout(10, 0); | 343 | cx18_msleep_timeout(10, 0); |
334 | } | 344 | } |
335 | cx18_msleep_timeout(200, 0); | 345 | cx18_msleep_timeout(200, 0); |
336 | if (retries == 51) { | 346 | if (retries == 51) { |
337 | CX18_ERR("Could not start the CPU\n"); | 347 | CX18_ERR("Could not start the CPU\n"); |
338 | return -EIO; | 348 | return -EIO; |
339 | } | 349 | } |
340 | } | 350 | } |
341 | if (sz <= 0) | 351 | if (sz <= 0) |
342 | return -EIO; | 352 | return -EIO; |
343 | } | 353 | } |
344 | /* initialize GPIO */ | 354 | /* initialize GPIO */ |
345 | write_reg(0x14001400, 0xC78110); | 355 | cx18_write_reg(cx, 0x14001400, 0xC78110); |
346 | return 0; | 356 | return 0; |
347 | } | 357 | } |
348 | 358 |
drivers/media/video/cx18/cx18-gpio.c
1 | /* | 1 | /* |
2 | * cx18 gpio functions | 2 | * cx18 gpio functions |
3 | * | 3 | * |
4 | * Derived from ivtv-gpio.c | 4 | * Derived from ivtv-gpio.c |
5 | * | 5 | * |
6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | 6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> |
7 | * | 7 | * |
8 | * This program is free software; you can redistribute it and/or modify | 8 | * This program is free software; you can redistribute it and/or modify |
9 | * it under the terms of the GNU General Public License as published by | 9 | * it under the terms of the GNU General Public License as published by |
10 | * the Free Software Foundation; either version 2 of the License, or | 10 | * the Free Software Foundation; either version 2 of the License, or |
11 | * (at your option) any later version. | 11 | * (at your option) any later version. |
12 | * | 12 | * |
13 | * This program is distributed in the hope that it will be useful, | 13 | * This program is distributed in the hope that it will be useful, |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | * GNU General Public License for more details. | 16 | * GNU General Public License for more details. |
17 | * | 17 | * |
18 | * You should have received a copy of the GNU General Public License | 18 | * You should have received a copy of the GNU General Public License |
19 | * along with this program; if not, write to the Free Software | 19 | * along with this program; if not, write to the Free Software |
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | 20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
21 | * 02111-1307 USA | 21 | * 02111-1307 USA |
22 | */ | 22 | */ |
23 | 23 | ||
24 | #include "cx18-driver.h" | 24 | #include "cx18-driver.h" |
25 | #include "cx18-io.h" | ||
25 | #include "cx18-cards.h" | 26 | #include "cx18-cards.h" |
26 | #include "cx18-gpio.h" | 27 | #include "cx18-gpio.h" |
27 | #include "tuner-xc2028.h" | 28 | #include "tuner-xc2028.h" |
28 | 29 | ||
29 | /********************* GPIO stuffs *********************/ | 30 | /********************* GPIO stuffs *********************/ |
30 | 31 | ||
31 | /* GPIO registers */ | 32 | /* GPIO registers */ |
32 | #define CX18_REG_GPIO_IN 0xc72010 | 33 | #define CX18_REG_GPIO_IN 0xc72010 |
33 | #define CX18_REG_GPIO_OUT1 0xc78100 | 34 | #define CX18_REG_GPIO_OUT1 0xc78100 |
34 | #define CX18_REG_GPIO_DIR1 0xc78108 | 35 | #define CX18_REG_GPIO_DIR1 0xc78108 |
35 | #define CX18_REG_GPIO_OUT2 0xc78104 | 36 | #define CX18_REG_GPIO_OUT2 0xc78104 |
36 | #define CX18_REG_GPIO_DIR2 0xc7810c | 37 | #define CX18_REG_GPIO_DIR2 0xc7810c |
37 | 38 | ||
38 | /* | 39 | /* |
39 | * HVR-1600 GPIO pins, courtesy of Hauppauge: | 40 | * HVR-1600 GPIO pins, courtesy of Hauppauge: |
40 | * | 41 | * |
41 | * gpio0: zilog ir process reset pin | 42 | * gpio0: zilog ir process reset pin |
42 | * gpio1: zilog programming pin (you should never use this) | 43 | * gpio1: zilog programming pin (you should never use this) |
43 | * gpio12: cx24227 reset pin | 44 | * gpio12: cx24227 reset pin |
44 | * gpio13: cs5345 reset pin | 45 | * gpio13: cs5345 reset pin |
45 | */ | 46 | */ |
46 | 47 | ||
47 | static void gpio_write(struct cx18 *cx) | 48 | static void gpio_write(struct cx18 *cx) |
48 | { | 49 | { |
49 | u32 dir = cx->gpio_dir; | 50 | u32 dir = cx->gpio_dir; |
50 | u32 val = cx->gpio_val; | 51 | u32 val = cx->gpio_val; |
51 | 52 | ||
52 | write_reg((dir & 0xffff) << 16, CX18_REG_GPIO_DIR1); | 53 | cx18_write_reg(cx, (dir & 0xffff) << 16, CX18_REG_GPIO_DIR1); |
53 | write_reg(((dir & 0xffff) << 16) | (val & 0xffff), | 54 | cx18_write_reg(cx, ((dir & 0xffff) << 16) | (val & 0xffff), |
54 | CX18_REG_GPIO_OUT1); | 55 | CX18_REG_GPIO_OUT1); |
55 | write_reg(dir & 0xffff0000, CX18_REG_GPIO_DIR2); | 56 | cx18_write_reg(cx, dir & 0xffff0000, CX18_REG_GPIO_DIR2); |
56 | write_reg_sync((dir & 0xffff0000) | ((val & 0xffff0000) >> 16), | 57 | cx18_write_reg_sync(cx, (dir & 0xffff0000) | ((val & 0xffff0000) >> 16), |
57 | CX18_REG_GPIO_OUT2); | 58 | CX18_REG_GPIO_OUT2); |
58 | } | 59 | } |
59 | 60 | ||
60 | void cx18_reset_i2c_slaves_gpio(struct cx18 *cx) | 61 | void cx18_reset_i2c_slaves_gpio(struct cx18 *cx) |
61 | { | 62 | { |
62 | const struct cx18_gpio_i2c_slave_reset *p; | 63 | const struct cx18_gpio_i2c_slave_reset *p; |
63 | 64 | ||
64 | p = &cx->card->gpio_i2c_slave_reset; | 65 | p = &cx->card->gpio_i2c_slave_reset; |
65 | 66 | ||
66 | if ((p->active_lo_mask | p->active_hi_mask) == 0) | 67 | if ((p->active_lo_mask | p->active_hi_mask) == 0) |
67 | return; | 68 | return; |
68 | 69 | ||
69 | /* Assuming that the masks are a subset of the bits in gpio_dir */ | 70 | /* Assuming that the masks are a subset of the bits in gpio_dir */ |
70 | 71 | ||
71 | /* Assert */ | 72 | /* Assert */ |
72 | mutex_lock(&cx->gpio_lock); | 73 | mutex_lock(&cx->gpio_lock); |
73 | cx->gpio_val = | 74 | cx->gpio_val = |
74 | (cx->gpio_val | p->active_hi_mask) & ~(p->active_lo_mask); | 75 | (cx->gpio_val | p->active_hi_mask) & ~(p->active_lo_mask); |
75 | gpio_write(cx); | 76 | gpio_write(cx); |
76 | schedule_timeout_uninterruptible(msecs_to_jiffies(p->msecs_asserted)); | 77 | schedule_timeout_uninterruptible(msecs_to_jiffies(p->msecs_asserted)); |
77 | 78 | ||
78 | /* Deassert */ | 79 | /* Deassert */ |
79 | cx->gpio_val = | 80 | cx->gpio_val = |
80 | (cx->gpio_val | p->active_lo_mask) & ~(p->active_hi_mask); | 81 | (cx->gpio_val | p->active_lo_mask) & ~(p->active_hi_mask); |
81 | gpio_write(cx); | 82 | gpio_write(cx); |
82 | schedule_timeout_uninterruptible(msecs_to_jiffies(p->msecs_recovery)); | 83 | schedule_timeout_uninterruptible(msecs_to_jiffies(p->msecs_recovery)); |
83 | mutex_unlock(&cx->gpio_lock); | 84 | mutex_unlock(&cx->gpio_lock); |
84 | } | 85 | } |
85 | 86 | ||
86 | void cx18_reset_ir_gpio(void *data) | 87 | void cx18_reset_ir_gpio(void *data) |
87 | { | 88 | { |
88 | struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx; | 89 | struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx; |
89 | const struct cx18_gpio_i2c_slave_reset *p; | 90 | const struct cx18_gpio_i2c_slave_reset *p; |
90 | 91 | ||
91 | p = &cx->card->gpio_i2c_slave_reset; | 92 | p = &cx->card->gpio_i2c_slave_reset; |
92 | 93 | ||
93 | if (p->ir_reset_mask == 0) | 94 | if (p->ir_reset_mask == 0) |
94 | return; | 95 | return; |
95 | 96 | ||
96 | CX18_DEBUG_INFO("Resetting IR microcontroller\n"); | 97 | CX18_DEBUG_INFO("Resetting IR microcontroller\n"); |
97 | 98 | ||
98 | /* | 99 | /* |
99 | Assert timing for the Z8F0811 on HVR-1600 boards: | 100 | Assert timing for the Z8F0811 on HVR-1600 boards: |
100 | 1. Assert RESET for min of 4 clock cycles at 18.432 MHz to initiate | 101 | 1. Assert RESET for min of 4 clock cycles at 18.432 MHz to initiate |
101 | 2. Reset then takes 66 WDT cycles at 10 kHz + 16 xtal clock cycles | 102 | 2. Reset then takes 66 WDT cycles at 10 kHz + 16 xtal clock cycles |
102 | (6,601,085 nanoseconds ~= 7 milliseconds) | 103 | (6,601,085 nanoseconds ~= 7 milliseconds) |
103 | 3. DBG pin must be high before chip exits reset for normal operation. | 104 | 3. DBG pin must be high before chip exits reset for normal operation. |
104 | DBG is open drain and hopefully pulled high since we don't | 105 | DBG is open drain and hopefully pulled high since we don't |
105 | normally drive it (GPIO 1?) for the HVR-1600 | 106 | normally drive it (GPIO 1?) for the HVR-1600 |
106 | 4. Z8F0811 won't exit reset until RESET is deasserted | 107 | 4. Z8F0811 won't exit reset until RESET is deasserted |
107 | */ | 108 | */ |
108 | mutex_lock(&cx->gpio_lock); | 109 | mutex_lock(&cx->gpio_lock); |
109 | cx->gpio_val = cx->gpio_val & ~p->ir_reset_mask; | 110 | cx->gpio_val = cx->gpio_val & ~p->ir_reset_mask; |
110 | gpio_write(cx); | 111 | gpio_write(cx); |
111 | mutex_unlock(&cx->gpio_lock); | 112 | mutex_unlock(&cx->gpio_lock); |
112 | schedule_timeout_uninterruptible(msecs_to_jiffies(p->msecs_asserted)); | 113 | schedule_timeout_uninterruptible(msecs_to_jiffies(p->msecs_asserted)); |
113 | 114 | ||
114 | /* | 115 | /* |
115 | Zilog comes out of reset, loads reset vector address and executes | 116 | Zilog comes out of reset, loads reset vector address and executes |
116 | from there. Required recovery delay unknown. | 117 | from there. Required recovery delay unknown. |
117 | */ | 118 | */ |
118 | mutex_lock(&cx->gpio_lock); | 119 | mutex_lock(&cx->gpio_lock); |
119 | cx->gpio_val = cx->gpio_val | p->ir_reset_mask; | 120 | cx->gpio_val = cx->gpio_val | p->ir_reset_mask; |
120 | gpio_write(cx); | 121 | gpio_write(cx); |
121 | mutex_unlock(&cx->gpio_lock); | 122 | mutex_unlock(&cx->gpio_lock); |
122 | schedule_timeout_uninterruptible(msecs_to_jiffies(p->msecs_recovery)); | 123 | schedule_timeout_uninterruptible(msecs_to_jiffies(p->msecs_recovery)); |
123 | } | 124 | } |
124 | EXPORT_SYMBOL(cx18_reset_ir_gpio); | 125 | EXPORT_SYMBOL(cx18_reset_ir_gpio); |
125 | /* This symbol is exported for use by an infrared module for the IR-blaster */ | 126 | /* This symbol is exported for use by an infrared module for the IR-blaster */ |
126 | 127 | ||
127 | void cx18_gpio_init(struct cx18 *cx) | 128 | void cx18_gpio_init(struct cx18 *cx) |
128 | { | 129 | { |
129 | mutex_lock(&cx->gpio_lock); | 130 | mutex_lock(&cx->gpio_lock); |
130 | cx->gpio_dir = cx->card->gpio_init.direction; | 131 | cx->gpio_dir = cx->card->gpio_init.direction; |
131 | cx->gpio_val = cx->card->gpio_init.initial_value; | 132 | cx->gpio_val = cx->card->gpio_init.initial_value; |
132 | 133 | ||
133 | if (cx->card->tuners[0].tuner == TUNER_XC2028) { | 134 | if (cx->card->tuners[0].tuner == TUNER_XC2028) { |
134 | cx->gpio_dir |= 1 << cx->card->xceive_pin; | 135 | cx->gpio_dir |= 1 << cx->card->xceive_pin; |
135 | cx->gpio_val |= 1 << cx->card->xceive_pin; | 136 | cx->gpio_val |= 1 << cx->card->xceive_pin; |
136 | } | 137 | } |
137 | 138 | ||
138 | if (cx->gpio_dir == 0) { | 139 | if (cx->gpio_dir == 0) { |
139 | mutex_unlock(&cx->gpio_lock); | 140 | mutex_unlock(&cx->gpio_lock); |
140 | return; | 141 | return; |
141 | } | 142 | } |
142 | 143 | ||
143 | CX18_DEBUG_INFO("GPIO initial dir: %08x/%08x out: %08x/%08x\n", | 144 | CX18_DEBUG_INFO("GPIO initial dir: %08x/%08x out: %08x/%08x\n", |
144 | read_reg(CX18_REG_GPIO_DIR1), read_reg(CX18_REG_GPIO_DIR2), | 145 | cx18_read_reg(cx, CX18_REG_GPIO_DIR1), |
145 | read_reg(CX18_REG_GPIO_OUT1), read_reg(CX18_REG_GPIO_OUT2)); | 146 | cx18_read_reg(cx, CX18_REG_GPIO_DIR2), |
147 | cx18_read_reg(cx, CX18_REG_GPIO_OUT1), | ||
148 | cx18_read_reg(cx, CX18_REG_GPIO_OUT2)); | ||
146 | 149 | ||
147 | gpio_write(cx); | 150 | gpio_write(cx); |
148 | mutex_unlock(&cx->gpio_lock); | 151 | mutex_unlock(&cx->gpio_lock); |
149 | } | 152 | } |
150 | 153 | ||
151 | /* Xceive tuner reset function */ | 154 | /* Xceive tuner reset function */ |
152 | int cx18_reset_tuner_gpio(void *dev, int cmd, int value) | 155 | int cx18_reset_tuner_gpio(void *dev, int cmd, int value) |
153 | { | 156 | { |
154 | struct i2c_algo_bit_data *algo = dev; | 157 | struct i2c_algo_bit_data *algo = dev; |
155 | struct cx18_i2c_algo_callback_data *cb_data = algo->data; | 158 | struct cx18_i2c_algo_callback_data *cb_data = algo->data; |
156 | struct cx18 *cx = cb_data->cx; | 159 | struct cx18 *cx = cb_data->cx; |
157 | 160 | ||
158 | if (cmd != XC2028_TUNER_RESET) | 161 | if (cmd != XC2028_TUNER_RESET) |
159 | return 0; | 162 | return 0; |
160 | CX18_DEBUG_INFO("Resetting tuner\n"); | 163 | CX18_DEBUG_INFO("Resetting tuner\n"); |
161 | 164 | ||
162 | mutex_lock(&cx->gpio_lock); | 165 | mutex_lock(&cx->gpio_lock); |
163 | cx->gpio_val &= ~(1 << cx->card->xceive_pin); | 166 | cx->gpio_val &= ~(1 << cx->card->xceive_pin); |
164 | gpio_write(cx); | 167 | gpio_write(cx); |
165 | mutex_unlock(&cx->gpio_lock); | 168 | mutex_unlock(&cx->gpio_lock); |
166 | schedule_timeout_interruptible(msecs_to_jiffies(1)); | 169 | schedule_timeout_interruptible(msecs_to_jiffies(1)); |
167 | 170 | ||
168 | mutex_lock(&cx->gpio_lock); | 171 | mutex_lock(&cx->gpio_lock); |
169 | cx->gpio_val |= 1 << cx->card->xceive_pin; | 172 | cx->gpio_val |= 1 << cx->card->xceive_pin; |
170 | gpio_write(cx); | 173 | gpio_write(cx); |
171 | mutex_unlock(&cx->gpio_lock); | 174 | mutex_unlock(&cx->gpio_lock); |
172 | schedule_timeout_interruptible(msecs_to_jiffies(1)); | 175 | schedule_timeout_interruptible(msecs_to_jiffies(1)); |
173 | return 0; | 176 | return 0; |
174 | } | 177 | } |
175 | 178 | ||
176 | int cx18_gpio(struct cx18 *cx, unsigned int command, void *arg) | 179 | int cx18_gpio(struct cx18 *cx, unsigned int command, void *arg) |
177 | { | 180 | { |
178 | struct v4l2_routing *route = arg; | 181 | struct v4l2_routing *route = arg; |
179 | u32 mask, data; | 182 | u32 mask, data; |
180 | 183 | ||
181 | switch (command) { | 184 | switch (command) { |
182 | case VIDIOC_INT_S_AUDIO_ROUTING: | 185 | case VIDIOC_INT_S_AUDIO_ROUTING: |
183 | if (route->input > 2) | 186 | if (route->input > 2) |
184 | return -EINVAL; | 187 | return -EINVAL; |
185 | mask = cx->card->gpio_audio_input.mask; | 188 | mask = cx->card->gpio_audio_input.mask; |
186 | switch (route->input) { | 189 | switch (route->input) { |
187 | case 0: | 190 | case 0: |
188 | data = cx->card->gpio_audio_input.tuner; | 191 | data = cx->card->gpio_audio_input.tuner; |
189 | break; | 192 | break; |
190 | case 1: | 193 | case 1: |
191 | data = cx->card->gpio_audio_input.linein; | 194 | data = cx->card->gpio_audio_input.linein; |
192 | break; | 195 | break; |
193 | case 2: | 196 | case 2: |
194 | default: | 197 | default: |
195 | data = cx->card->gpio_audio_input.radio; | 198 | data = cx->card->gpio_audio_input.radio; |
196 | break; | 199 | break; |
197 | } | 200 | } |
198 | break; | 201 | break; |
199 | 202 | ||
200 | default: | 203 | default: |
201 | return -EINVAL; | 204 | return -EINVAL; |
202 | } | 205 | } |
203 | if (mask) { | 206 | if (mask) { |
204 | mutex_lock(&cx->gpio_lock); | 207 | mutex_lock(&cx->gpio_lock); |
205 | cx->gpio_val = (cx->gpio_val & ~mask) | (data & mask); | 208 | cx->gpio_val = (cx->gpio_val & ~mask) | (data & mask); |
206 | gpio_write(cx); | 209 | gpio_write(cx); |
207 | mutex_unlock(&cx->gpio_lock); | 210 | mutex_unlock(&cx->gpio_lock); |
208 | } | 211 | } |
209 | return 0; | 212 | return 0; |
210 | } | 213 | } |
211 | 214 |
drivers/media/video/cx18/cx18-i2c.c
1 | /* | 1 | /* |
2 | * cx18 I2C functions | 2 | * cx18 I2C functions |
3 | * | 3 | * |
4 | * Derived from ivtv-i2c.c | 4 | * Derived from ivtv-i2c.c |
5 | * | 5 | * |
6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | 6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> |
7 | * | 7 | * |
8 | * This program is free software; you can redistribute it and/or modify | 8 | * This program is free software; you can redistribute it and/or modify |
9 | * it under the terms of the GNU General Public License as published by | 9 | * it under the terms of the GNU General Public License as published by |
10 | * the Free Software Foundation; either version 2 of the License, or | 10 | * the Free Software Foundation; either version 2 of the License, or |
11 | * (at your option) any later version. | 11 | * (at your option) any later version. |
12 | * | 12 | * |
13 | * This program is distributed in the hope that it will be useful, | 13 | * This program is distributed in the hope that it will be useful, |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | * GNU General Public License for more details. | 16 | * GNU General Public License for more details. |
17 | * | 17 | * |
18 | * You should have received a copy of the GNU General Public License | 18 | * You should have received a copy of the GNU General Public License |
19 | * along with this program; if not, write to the Free Software | 19 | * along with this program; if not, write to the Free Software |
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | 20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
21 | * 02111-1307 USA | 21 | * 02111-1307 USA |
22 | */ | 22 | */ |
23 | 23 | ||
24 | #include "cx18-driver.h" | 24 | #include "cx18-driver.h" |
25 | #include "cx18-io.h" | ||
25 | #include "cx18-cards.h" | 26 | #include "cx18-cards.h" |
26 | #include "cx18-gpio.h" | 27 | #include "cx18-gpio.h" |
27 | #include "cx18-av-core.h" | 28 | #include "cx18-av-core.h" |
28 | #include "cx18-i2c.h" | 29 | #include "cx18-i2c.h" |
29 | 30 | ||
30 | #define CX18_REG_I2C_1_WR 0xf15000 | 31 | #define CX18_REG_I2C_1_WR 0xf15000 |
31 | #define CX18_REG_I2C_1_RD 0xf15008 | 32 | #define CX18_REG_I2C_1_RD 0xf15008 |
32 | #define CX18_REG_I2C_2_WR 0xf25100 | 33 | #define CX18_REG_I2C_2_WR 0xf25100 |
33 | #define CX18_REG_I2C_2_RD 0xf25108 | 34 | #define CX18_REG_I2C_2_RD 0xf25108 |
34 | 35 | ||
35 | #define SETSCL_BIT 0x0001 | 36 | #define SETSCL_BIT 0x0001 |
36 | #define SETSDL_BIT 0x0002 | 37 | #define SETSDL_BIT 0x0002 |
37 | #define GETSCL_BIT 0x0004 | 38 | #define GETSCL_BIT 0x0004 |
38 | #define GETSDL_BIT 0x0008 | 39 | #define GETSDL_BIT 0x0008 |
39 | 40 | ||
40 | #define CX18_CS5345_I2C_ADDR 0x4c | 41 | #define CX18_CS5345_I2C_ADDR 0x4c |
41 | 42 | ||
42 | /* This array should match the CX18_HW_ defines */ | 43 | /* This array should match the CX18_HW_ defines */ |
43 | static const u8 hw_driverids[] = { | 44 | static const u8 hw_driverids[] = { |
44 | I2C_DRIVERID_TUNER, | 45 | I2C_DRIVERID_TUNER, |
45 | I2C_DRIVERID_TVEEPROM, | 46 | I2C_DRIVERID_TVEEPROM, |
46 | I2C_DRIVERID_CS5345, | 47 | I2C_DRIVERID_CS5345, |
47 | 0, /* CX18_HW_GPIO dummy driver ID */ | 48 | 0, /* CX18_HW_GPIO dummy driver ID */ |
48 | 0 /* CX18_HW_CX23418 dummy driver ID */ | 49 | 0 /* CX18_HW_CX23418 dummy driver ID */ |
49 | }; | 50 | }; |
50 | 51 | ||
51 | /* This array should match the CX18_HW_ defines */ | 52 | /* This array should match the CX18_HW_ defines */ |
52 | static const u8 hw_addrs[] = { | 53 | static const u8 hw_addrs[] = { |
53 | 0, | 54 | 0, |
54 | 0, | 55 | 0, |
55 | CX18_CS5345_I2C_ADDR, | 56 | CX18_CS5345_I2C_ADDR, |
56 | 0, /* CX18_HW_GPIO dummy driver ID */ | 57 | 0, /* CX18_HW_GPIO dummy driver ID */ |
57 | 0, /* CX18_HW_CX23418 dummy driver ID */ | 58 | 0, /* CX18_HW_CX23418 dummy driver ID */ |
58 | }; | 59 | }; |
59 | 60 | ||
60 | /* This array should match the CX18_HW_ defines */ | 61 | /* This array should match the CX18_HW_ defines */ |
61 | /* This might well become a card-specific array */ | 62 | /* This might well become a card-specific array */ |
62 | static const u8 hw_bus[] = { | 63 | static const u8 hw_bus[] = { |
63 | 0, | 64 | 0, |
64 | 0, | 65 | 0, |
65 | 0, | 66 | 0, |
66 | 0, /* CX18_HW_GPIO dummy driver ID */ | 67 | 0, /* CX18_HW_GPIO dummy driver ID */ |
67 | 0, /* CX18_HW_CX23418 dummy driver ID */ | 68 | 0, /* CX18_HW_CX23418 dummy driver ID */ |
68 | }; | 69 | }; |
69 | 70 | ||
70 | /* This array should match the CX18_HW_ defines */ | 71 | /* This array should match the CX18_HW_ defines */ |
71 | static const char * const hw_devicenames[] = { | 72 | static const char * const hw_devicenames[] = { |
72 | "tuner", | 73 | "tuner", |
73 | "tveeprom", | 74 | "tveeprom", |
74 | "cs5345", | 75 | "cs5345", |
75 | "gpio", | 76 | "gpio", |
76 | "cx23418", | 77 | "cx23418", |
77 | }; | 78 | }; |
78 | 79 | ||
79 | int cx18_i2c_register(struct cx18 *cx, unsigned idx) | 80 | int cx18_i2c_register(struct cx18 *cx, unsigned idx) |
80 | { | 81 | { |
81 | struct i2c_board_info info; | 82 | struct i2c_board_info info; |
82 | struct i2c_client *c; | 83 | struct i2c_client *c; |
83 | u8 id, bus; | 84 | u8 id, bus; |
84 | int i; | 85 | int i; |
85 | 86 | ||
86 | CX18_DEBUG_I2C("i2c client register\n"); | 87 | CX18_DEBUG_I2C("i2c client register\n"); |
87 | if (idx >= ARRAY_SIZE(hw_driverids) || hw_driverids[idx] == 0) | 88 | if (idx >= ARRAY_SIZE(hw_driverids) || hw_driverids[idx] == 0) |
88 | return -1; | 89 | return -1; |
89 | id = hw_driverids[idx]; | 90 | id = hw_driverids[idx]; |
90 | bus = hw_bus[idx]; | 91 | bus = hw_bus[idx]; |
91 | memset(&info, 0, sizeof(info)); | 92 | memset(&info, 0, sizeof(info)); |
92 | strlcpy(info.type, hw_devicenames[idx], sizeof(info.type)); | 93 | strlcpy(info.type, hw_devicenames[idx], sizeof(info.type)); |
93 | info.addr = hw_addrs[idx]; | 94 | info.addr = hw_addrs[idx]; |
94 | for (i = 0; i < I2C_CLIENTS_MAX; i++) | 95 | for (i = 0; i < I2C_CLIENTS_MAX; i++) |
95 | if (cx->i2c_clients[i] == NULL) | 96 | if (cx->i2c_clients[i] == NULL) |
96 | break; | 97 | break; |
97 | 98 | ||
98 | if (i == I2C_CLIENTS_MAX) { | 99 | if (i == I2C_CLIENTS_MAX) { |
99 | CX18_ERR("insufficient room for new I2C client!\n"); | 100 | CX18_ERR("insufficient room for new I2C client!\n"); |
100 | return -ENOMEM; | 101 | return -ENOMEM; |
101 | } | 102 | } |
102 | 103 | ||
103 | if (id != I2C_DRIVERID_TUNER) { | 104 | if (id != I2C_DRIVERID_TUNER) { |
104 | c = i2c_new_device(&cx->i2c_adap[bus], &info); | 105 | c = i2c_new_device(&cx->i2c_adap[bus], &info); |
105 | if (c->driver == NULL) | 106 | if (c->driver == NULL) |
106 | i2c_unregister_device(c); | 107 | i2c_unregister_device(c); |
107 | else | 108 | else |
108 | cx->i2c_clients[i] = c; | 109 | cx->i2c_clients[i] = c; |
109 | return cx->i2c_clients[i] ? 0 : -ENODEV; | 110 | return cx->i2c_clients[i] ? 0 : -ENODEV; |
110 | } | 111 | } |
111 | 112 | ||
112 | /* special tuner handling */ | 113 | /* special tuner handling */ |
113 | c = i2c_new_probed_device(&cx->i2c_adap[1], &info, cx->card_i2c->radio); | 114 | c = i2c_new_probed_device(&cx->i2c_adap[1], &info, cx->card_i2c->radio); |
114 | if (c && c->driver == NULL) | 115 | if (c && c->driver == NULL) |
115 | i2c_unregister_device(c); | 116 | i2c_unregister_device(c); |
116 | else if (c) | 117 | else if (c) |
117 | cx->i2c_clients[i++] = c; | 118 | cx->i2c_clients[i++] = c; |
118 | c = i2c_new_probed_device(&cx->i2c_adap[1], &info, cx->card_i2c->demod); | 119 | c = i2c_new_probed_device(&cx->i2c_adap[1], &info, cx->card_i2c->demod); |
119 | if (c && c->driver == NULL) | 120 | if (c && c->driver == NULL) |
120 | i2c_unregister_device(c); | 121 | i2c_unregister_device(c); |
121 | else if (c) | 122 | else if (c) |
122 | cx->i2c_clients[i++] = c; | 123 | cx->i2c_clients[i++] = c; |
123 | c = i2c_new_probed_device(&cx->i2c_adap[1], &info, cx->card_i2c->tv); | 124 | c = i2c_new_probed_device(&cx->i2c_adap[1], &info, cx->card_i2c->tv); |
124 | if (c && c->driver == NULL) | 125 | if (c && c->driver == NULL) |
125 | i2c_unregister_device(c); | 126 | i2c_unregister_device(c); |
126 | else if (c) | 127 | else if (c) |
127 | cx->i2c_clients[i++] = c; | 128 | cx->i2c_clients[i++] = c; |
128 | return 0; | 129 | return 0; |
129 | } | 130 | } |
130 | 131 | ||
131 | static int attach_inform(struct i2c_client *client) | 132 | static int attach_inform(struct i2c_client *client) |
132 | { | 133 | { |
133 | return 0; | 134 | return 0; |
134 | } | 135 | } |
135 | 136 | ||
136 | static int detach_inform(struct i2c_client *client) | 137 | static int detach_inform(struct i2c_client *client) |
137 | { | 138 | { |
138 | int i; | 139 | int i; |
139 | struct cx18 *cx = (struct cx18 *)i2c_get_adapdata(client->adapter); | 140 | struct cx18 *cx = (struct cx18 *)i2c_get_adapdata(client->adapter); |
140 | 141 | ||
141 | CX18_DEBUG_I2C("i2c client detach\n"); | 142 | CX18_DEBUG_I2C("i2c client detach\n"); |
142 | for (i = 0; i < I2C_CLIENTS_MAX; i++) { | 143 | for (i = 0; i < I2C_CLIENTS_MAX; i++) { |
143 | if (cx->i2c_clients[i] == client) { | 144 | if (cx->i2c_clients[i] == client) { |
144 | cx->i2c_clients[i] = NULL; | 145 | cx->i2c_clients[i] = NULL; |
145 | break; | 146 | break; |
146 | } | 147 | } |
147 | } | 148 | } |
148 | CX18_DEBUG_I2C("i2c detach [client=%s,%s]\n", | 149 | CX18_DEBUG_I2C("i2c detach [client=%s,%s]\n", |
149 | client->name, (i < I2C_CLIENTS_MAX) ? "ok" : "failed"); | 150 | client->name, (i < I2C_CLIENTS_MAX) ? "ok" : "failed"); |
150 | 151 | ||
151 | return 0; | 152 | return 0; |
152 | } | 153 | } |
153 | 154 | ||
154 | static void cx18_setscl(void *data, int state) | 155 | static void cx18_setscl(void *data, int state) |
155 | { | 156 | { |
156 | struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx; | 157 | struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx; |
157 | int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index; | 158 | int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index; |
158 | u32 addr = bus_index ? CX18_REG_I2C_2_WR : CX18_REG_I2C_1_WR; | 159 | u32 addr = bus_index ? CX18_REG_I2C_2_WR : CX18_REG_I2C_1_WR; |
159 | u32 r = read_reg(addr); | 160 | u32 r = cx18_read_reg(cx, addr); |
160 | 161 | ||
161 | if (state) | 162 | if (state) |
162 | write_reg_sync(r | SETSCL_BIT, addr); | 163 | cx18_write_reg_sync(cx, r | SETSCL_BIT, addr); |
163 | else | 164 | else |
164 | write_reg_sync(r & ~SETSCL_BIT, addr); | 165 | cx18_write_reg_sync(cx, r & ~SETSCL_BIT, addr); |
165 | } | 166 | } |
166 | 167 | ||
167 | static void cx18_setsda(void *data, int state) | 168 | static void cx18_setsda(void *data, int state) |
168 | { | 169 | { |
169 | struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx; | 170 | struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx; |
170 | int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index; | 171 | int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index; |
171 | u32 addr = bus_index ? CX18_REG_I2C_2_WR : CX18_REG_I2C_1_WR; | 172 | u32 addr = bus_index ? CX18_REG_I2C_2_WR : CX18_REG_I2C_1_WR; |
172 | u32 r = read_reg(addr); | 173 | u32 r = cx18_read_reg(cx, addr); |
173 | 174 | ||
174 | if (state) | 175 | if (state) |
175 | write_reg_sync(r | SETSDL_BIT, addr); | 176 | cx18_write_reg_sync(cx, r | SETSDL_BIT, addr); |
176 | else | 177 | else |
177 | write_reg_sync(r & ~SETSDL_BIT, addr); | 178 | cx18_write_reg_sync(cx, r & ~SETSDL_BIT, addr); |
178 | } | 179 | } |
179 | 180 | ||
180 | static int cx18_getscl(void *data) | 181 | static int cx18_getscl(void *data) |
181 | { | 182 | { |
182 | struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx; | 183 | struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx; |
183 | int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index; | 184 | int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index; |
184 | u32 addr = bus_index ? CX18_REG_I2C_2_RD : CX18_REG_I2C_1_RD; | 185 | u32 addr = bus_index ? CX18_REG_I2C_2_RD : CX18_REG_I2C_1_RD; |
185 | 186 | ||
186 | return read_reg(addr) & GETSCL_BIT; | 187 | return cx18_read_reg(cx, addr) & GETSCL_BIT; |
187 | } | 188 | } |
188 | 189 | ||
189 | static int cx18_getsda(void *data) | 190 | static int cx18_getsda(void *data) |
190 | { | 191 | { |
191 | struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx; | 192 | struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx; |
192 | int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index; | 193 | int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index; |
193 | u32 addr = bus_index ? CX18_REG_I2C_2_RD : CX18_REG_I2C_1_RD; | 194 | u32 addr = bus_index ? CX18_REG_I2C_2_RD : CX18_REG_I2C_1_RD; |
194 | 195 | ||
195 | return read_reg(addr) & GETSDL_BIT; | 196 | return cx18_read_reg(cx, addr) & GETSDL_BIT; |
196 | } | 197 | } |
197 | 198 | ||
198 | /* template for i2c-bit-algo */ | 199 | /* template for i2c-bit-algo */ |
199 | static struct i2c_adapter cx18_i2c_adap_template = { | 200 | static struct i2c_adapter cx18_i2c_adap_template = { |
200 | .name = "cx18 i2c driver", | 201 | .name = "cx18 i2c driver", |
201 | .id = I2C_HW_B_CX2341X, | 202 | .id = I2C_HW_B_CX2341X, |
202 | .algo = NULL, /* set by i2c-algo-bit */ | 203 | .algo = NULL, /* set by i2c-algo-bit */ |
203 | .algo_data = NULL, /* filled from template */ | 204 | .algo_data = NULL, /* filled from template */ |
204 | .client_register = attach_inform, | 205 | .client_register = attach_inform, |
205 | .client_unregister = detach_inform, | 206 | .client_unregister = detach_inform, |
206 | .owner = THIS_MODULE, | 207 | .owner = THIS_MODULE, |
207 | }; | 208 | }; |
208 | 209 | ||
209 | #define CX18_SCL_PERIOD (10) /* usecs. 10 usec is period for a 100 KHz clock */ | 210 | #define CX18_SCL_PERIOD (10) /* usecs. 10 usec is period for a 100 KHz clock */ |
210 | #define CX18_ALGO_BIT_TIMEOUT (2) /* seconds */ | 211 | #define CX18_ALGO_BIT_TIMEOUT (2) /* seconds */ |
211 | 212 | ||
212 | static struct i2c_algo_bit_data cx18_i2c_algo_template = { | 213 | static struct i2c_algo_bit_data cx18_i2c_algo_template = { |
213 | .setsda = cx18_setsda, | 214 | .setsda = cx18_setsda, |
214 | .setscl = cx18_setscl, | 215 | .setscl = cx18_setscl, |
215 | .getsda = cx18_getsda, | 216 | .getsda = cx18_getsda, |
216 | .getscl = cx18_getscl, | 217 | .getscl = cx18_getscl, |
217 | .udelay = CX18_SCL_PERIOD/2, /* 1/2 clock period in usec*/ | 218 | .udelay = CX18_SCL_PERIOD/2, /* 1/2 clock period in usec*/ |
218 | .timeout = CX18_ALGO_BIT_TIMEOUT*HZ /* jiffies */ | 219 | .timeout = CX18_ALGO_BIT_TIMEOUT*HZ /* jiffies */ |
219 | }; | 220 | }; |
220 | 221 | ||
221 | static struct i2c_client cx18_i2c_client_template = { | 222 | static struct i2c_client cx18_i2c_client_template = { |
222 | .name = "cx18 internal", | 223 | .name = "cx18 internal", |
223 | }; | 224 | }; |
224 | 225 | ||
225 | int cx18_call_i2c_client(struct cx18 *cx, int addr, unsigned cmd, void *arg) | 226 | int cx18_call_i2c_client(struct cx18 *cx, int addr, unsigned cmd, void *arg) |
226 | { | 227 | { |
227 | struct i2c_client *client; | 228 | struct i2c_client *client; |
228 | int retval; | 229 | int retval; |
229 | int i; | 230 | int i; |
230 | 231 | ||
231 | CX18_DEBUG_I2C("call_i2c_client addr=%02x\n", addr); | 232 | CX18_DEBUG_I2C("call_i2c_client addr=%02x\n", addr); |
232 | for (i = 0; i < I2C_CLIENTS_MAX; i++) { | 233 | for (i = 0; i < I2C_CLIENTS_MAX; i++) { |
233 | client = cx->i2c_clients[i]; | 234 | client = cx->i2c_clients[i]; |
234 | if (client == NULL || client->driver == NULL || | 235 | if (client == NULL || client->driver == NULL || |
235 | client->driver->command == NULL) | 236 | client->driver->command == NULL) |
236 | continue; | 237 | continue; |
237 | if (addr == client->addr) { | 238 | if (addr == client->addr) { |
238 | retval = client->driver->command(client, cmd, arg); | 239 | retval = client->driver->command(client, cmd, arg); |
239 | return retval; | 240 | return retval; |
240 | } | 241 | } |
241 | } | 242 | } |
242 | if (cmd != VIDIOC_G_CHIP_IDENT) | 243 | if (cmd != VIDIOC_G_CHIP_IDENT) |
243 | CX18_ERR("i2c addr 0x%02x not found for cmd 0x%x!\n", | 244 | CX18_ERR("i2c addr 0x%02x not found for cmd 0x%x!\n", |
244 | addr, cmd); | 245 | addr, cmd); |
245 | return -ENODEV; | 246 | return -ENODEV; |
246 | } | 247 | } |
247 | 248 | ||
248 | /* Find the i2c device based on the driver ID and return | 249 | /* Find the i2c device based on the driver ID and return |
249 | its i2c address or -ENODEV if no matching device was found. */ | 250 | its i2c address or -ENODEV if no matching device was found. */ |
250 | static int cx18_i2c_id_addr(struct cx18 *cx, u32 id) | 251 | static int cx18_i2c_id_addr(struct cx18 *cx, u32 id) |
251 | { | 252 | { |
252 | struct i2c_client *client; | 253 | struct i2c_client *client; |
253 | int retval = -ENODEV; | 254 | int retval = -ENODEV; |
254 | int i; | 255 | int i; |
255 | 256 | ||
256 | for (i = 0; i < I2C_CLIENTS_MAX; i++) { | 257 | for (i = 0; i < I2C_CLIENTS_MAX; i++) { |
257 | client = cx->i2c_clients[i]; | 258 | client = cx->i2c_clients[i]; |
258 | if (client == NULL || client->driver == NULL) | 259 | if (client == NULL || client->driver == NULL) |
259 | continue; | 260 | continue; |
260 | if (id == client->driver->id) { | 261 | if (id == client->driver->id) { |
261 | retval = client->addr; | 262 | retval = client->addr; |
262 | break; | 263 | break; |
263 | } | 264 | } |
264 | } | 265 | } |
265 | return retval; | 266 | return retval; |
266 | } | 267 | } |
267 | 268 | ||
268 | /* Find the i2c device name matching the DRIVERID */ | 269 | /* Find the i2c device name matching the DRIVERID */ |
269 | static const char *cx18_i2c_id_name(u32 id) | 270 | static const char *cx18_i2c_id_name(u32 id) |
270 | { | 271 | { |
271 | int i; | 272 | int i; |
272 | 273 | ||
273 | for (i = 0; i < ARRAY_SIZE(hw_driverids); i++) | 274 | for (i = 0; i < ARRAY_SIZE(hw_driverids); i++) |
274 | if (hw_driverids[i] == id) | 275 | if (hw_driverids[i] == id) |
275 | return hw_devicenames[i]; | 276 | return hw_devicenames[i]; |
276 | return "unknown device"; | 277 | return "unknown device"; |
277 | } | 278 | } |
278 | 279 | ||
279 | /* Find the i2c device name matching the CX18_HW_ flag */ | 280 | /* Find the i2c device name matching the CX18_HW_ flag */ |
280 | static const char *cx18_i2c_hw_name(u32 hw) | 281 | static const char *cx18_i2c_hw_name(u32 hw) |
281 | { | 282 | { |
282 | int i; | 283 | int i; |
283 | 284 | ||
284 | for (i = 0; i < ARRAY_SIZE(hw_driverids); i++) | 285 | for (i = 0; i < ARRAY_SIZE(hw_driverids); i++) |
285 | if (1 << i == hw) | 286 | if (1 << i == hw) |
286 | return hw_devicenames[i]; | 287 | return hw_devicenames[i]; |
287 | return "unknown device"; | 288 | return "unknown device"; |
288 | } | 289 | } |
289 | 290 | ||
290 | /* Find the i2c device matching the CX18_HW_ flag and return | 291 | /* Find the i2c device matching the CX18_HW_ flag and return |
291 | its i2c address or -ENODEV if no matching device was found. */ | 292 | its i2c address or -ENODEV if no matching device was found. */ |
292 | int cx18_i2c_hw_addr(struct cx18 *cx, u32 hw) | 293 | int cx18_i2c_hw_addr(struct cx18 *cx, u32 hw) |
293 | { | 294 | { |
294 | int i; | 295 | int i; |
295 | 296 | ||
296 | for (i = 0; i < ARRAY_SIZE(hw_driverids); i++) | 297 | for (i = 0; i < ARRAY_SIZE(hw_driverids); i++) |
297 | if (1 << i == hw) | 298 | if (1 << i == hw) |
298 | return cx18_i2c_id_addr(cx, hw_driverids[i]); | 299 | return cx18_i2c_id_addr(cx, hw_driverids[i]); |
299 | return -ENODEV; | 300 | return -ENODEV; |
300 | } | 301 | } |
301 | 302 | ||
302 | /* Calls i2c device based on CX18_HW_ flag. If hw == 0, then do nothing. | 303 | /* Calls i2c device based on CX18_HW_ flag. If hw == 0, then do nothing. |
303 | If hw == CX18_HW_GPIO then call the gpio handler. */ | 304 | If hw == CX18_HW_GPIO then call the gpio handler. */ |
304 | int cx18_i2c_hw(struct cx18 *cx, u32 hw, unsigned int cmd, void *arg) | 305 | int cx18_i2c_hw(struct cx18 *cx, u32 hw, unsigned int cmd, void *arg) |
305 | { | 306 | { |
306 | int addr; | 307 | int addr; |
307 | 308 | ||
308 | if (hw == 0) | 309 | if (hw == 0) |
309 | return 0; | 310 | return 0; |
310 | 311 | ||
311 | if (hw == CX18_HW_GPIO) | 312 | if (hw == CX18_HW_GPIO) |
312 | return cx18_gpio(cx, cmd, arg); | 313 | return cx18_gpio(cx, cmd, arg); |
313 | 314 | ||
314 | if (hw == CX18_HW_CX23418) | 315 | if (hw == CX18_HW_CX23418) |
315 | return cx18_av_cmd(cx, cmd, arg); | 316 | return cx18_av_cmd(cx, cmd, arg); |
316 | 317 | ||
317 | addr = cx18_i2c_hw_addr(cx, hw); | 318 | addr = cx18_i2c_hw_addr(cx, hw); |
318 | if (addr < 0) { | 319 | if (addr < 0) { |
319 | CX18_ERR("i2c hardware 0x%08x (%s) not found for cmd 0x%x!\n", | 320 | CX18_ERR("i2c hardware 0x%08x (%s) not found for cmd 0x%x!\n", |
320 | hw, cx18_i2c_hw_name(hw), cmd); | 321 | hw, cx18_i2c_hw_name(hw), cmd); |
321 | return addr; | 322 | return addr; |
322 | } | 323 | } |
323 | return cx18_call_i2c_client(cx, addr, cmd, arg); | 324 | return cx18_call_i2c_client(cx, addr, cmd, arg); |
324 | } | 325 | } |
325 | 326 | ||
326 | /* Calls i2c device based on I2C driver ID. */ | 327 | /* Calls i2c device based on I2C driver ID. */ |
327 | int cx18_i2c_id(struct cx18 *cx, u32 id, unsigned int cmd, void *arg) | 328 | int cx18_i2c_id(struct cx18 *cx, u32 id, unsigned int cmd, void *arg) |
328 | { | 329 | { |
329 | int addr; | 330 | int addr; |
330 | 331 | ||
331 | addr = cx18_i2c_id_addr(cx, id); | 332 | addr = cx18_i2c_id_addr(cx, id); |
332 | if (addr < 0) { | 333 | if (addr < 0) { |
333 | if (cmd != VIDIOC_G_CHIP_IDENT) | 334 | if (cmd != VIDIOC_G_CHIP_IDENT) |
334 | CX18_ERR("i2c ID 0x%08x (%s) not found for cmd 0x%x!\n", | 335 | CX18_ERR("i2c ID 0x%08x (%s) not found for cmd 0x%x!\n", |
335 | id, cx18_i2c_id_name(id), cmd); | 336 | id, cx18_i2c_id_name(id), cmd); |
336 | return addr; | 337 | return addr; |
337 | } | 338 | } |
338 | return cx18_call_i2c_client(cx, addr, cmd, arg); | 339 | return cx18_call_i2c_client(cx, addr, cmd, arg); |
339 | } | 340 | } |
340 | 341 | ||
341 | /* broadcast cmd for all I2C clients and for the gpio subsystem */ | 342 | /* broadcast cmd for all I2C clients and for the gpio subsystem */ |
342 | void cx18_call_i2c_clients(struct cx18 *cx, unsigned int cmd, void *arg) | 343 | void cx18_call_i2c_clients(struct cx18 *cx, unsigned int cmd, void *arg) |
343 | { | 344 | { |
344 | if (cx->i2c_adap[0].algo == NULL || cx->i2c_adap[1].algo == NULL) { | 345 | if (cx->i2c_adap[0].algo == NULL || cx->i2c_adap[1].algo == NULL) { |
345 | CX18_ERR("adapter is not set\n"); | 346 | CX18_ERR("adapter is not set\n"); |
346 | return; | 347 | return; |
347 | } | 348 | } |
348 | cx18_av_cmd(cx, cmd, arg); | 349 | cx18_av_cmd(cx, cmd, arg); |
349 | i2c_clients_command(&cx->i2c_adap[0], cmd, arg); | 350 | i2c_clients_command(&cx->i2c_adap[0], cmd, arg); |
350 | i2c_clients_command(&cx->i2c_adap[1], cmd, arg); | 351 | i2c_clients_command(&cx->i2c_adap[1], cmd, arg); |
351 | if (cx->hw_flags & CX18_HW_GPIO) | 352 | if (cx->hw_flags & CX18_HW_GPIO) |
352 | cx18_gpio(cx, cmd, arg); | 353 | cx18_gpio(cx, cmd, arg); |
353 | } | 354 | } |
354 | 355 | ||
355 | /* init + register i2c algo-bit adapter */ | 356 | /* init + register i2c algo-bit adapter */ |
356 | int init_cx18_i2c(struct cx18 *cx) | 357 | int init_cx18_i2c(struct cx18 *cx) |
357 | { | 358 | { |
358 | int i; | 359 | int i; |
359 | CX18_DEBUG_I2C("i2c init\n"); | 360 | CX18_DEBUG_I2C("i2c init\n"); |
360 | 361 | ||
361 | /* Sanity checks for the I2C hardware arrays. They must be the | 362 | /* Sanity checks for the I2C hardware arrays. They must be the |
362 | * same size and GPIO/CX23418 must be the last entries. | 363 | * same size and GPIO/CX23418 must be the last entries. |
363 | */ | 364 | */ |
364 | if (ARRAY_SIZE(hw_driverids) != ARRAY_SIZE(hw_addrs) || | 365 | if (ARRAY_SIZE(hw_driverids) != ARRAY_SIZE(hw_addrs) || |
365 | ARRAY_SIZE(hw_devicenames) != ARRAY_SIZE(hw_addrs) || | 366 | ARRAY_SIZE(hw_devicenames) != ARRAY_SIZE(hw_addrs) || |
366 | CX18_HW_GPIO != (1 << (ARRAY_SIZE(hw_addrs) - 2)) || | 367 | CX18_HW_GPIO != (1 << (ARRAY_SIZE(hw_addrs) - 2)) || |
367 | CX18_HW_CX23418 != (1 << (ARRAY_SIZE(hw_addrs) - 1)) || | 368 | CX18_HW_CX23418 != (1 << (ARRAY_SIZE(hw_addrs) - 1)) || |
368 | hw_driverids[ARRAY_SIZE(hw_addrs) - 1]) { | 369 | hw_driverids[ARRAY_SIZE(hw_addrs) - 1]) { |
369 | CX18_ERR("Mismatched I2C hardware arrays\n"); | 370 | CX18_ERR("Mismatched I2C hardware arrays\n"); |
370 | return -ENODEV; | 371 | return -ENODEV; |
371 | } | 372 | } |
372 | 373 | ||
373 | for (i = 0; i < 2; i++) { | 374 | for (i = 0; i < 2; i++) { |
374 | memcpy(&cx->i2c_adap[i], &cx18_i2c_adap_template, | 375 | memcpy(&cx->i2c_adap[i], &cx18_i2c_adap_template, |
375 | sizeof(struct i2c_adapter)); | 376 | sizeof(struct i2c_adapter)); |
376 | memcpy(&cx->i2c_algo[i], &cx18_i2c_algo_template, | 377 | memcpy(&cx->i2c_algo[i], &cx18_i2c_algo_template, |
377 | sizeof(struct i2c_algo_bit_data)); | 378 | sizeof(struct i2c_algo_bit_data)); |
378 | cx->i2c_algo_cb_data[i].cx = cx; | 379 | cx->i2c_algo_cb_data[i].cx = cx; |
379 | cx->i2c_algo_cb_data[i].bus_index = i; | 380 | cx->i2c_algo_cb_data[i].bus_index = i; |
380 | cx->i2c_algo[i].data = &cx->i2c_algo_cb_data[i]; | 381 | cx->i2c_algo[i].data = &cx->i2c_algo_cb_data[i]; |
381 | cx->i2c_adap[i].algo_data = &cx->i2c_algo[i]; | 382 | cx->i2c_adap[i].algo_data = &cx->i2c_algo[i]; |
382 | 383 | ||
383 | sprintf(cx->i2c_adap[i].name + strlen(cx->i2c_adap[i].name), | 384 | sprintf(cx->i2c_adap[i].name + strlen(cx->i2c_adap[i].name), |
384 | " #%d-%d", cx->num, i); | 385 | " #%d-%d", cx->num, i); |
385 | i2c_set_adapdata(&cx->i2c_adap[i], cx); | 386 | i2c_set_adapdata(&cx->i2c_adap[i], cx); |
386 | 387 | ||
387 | memcpy(&cx->i2c_client[i], &cx18_i2c_client_template, | 388 | memcpy(&cx->i2c_client[i], &cx18_i2c_client_template, |
388 | sizeof(struct i2c_client)); | 389 | sizeof(struct i2c_client)); |
389 | sprintf(cx->i2c_client[i].name + | 390 | sprintf(cx->i2c_client[i].name + |
390 | strlen(cx->i2c_client[i].name), "%d", i); | 391 | strlen(cx->i2c_client[i].name), "%d", i); |
391 | cx->i2c_client[i].adapter = &cx->i2c_adap[i]; | 392 | cx->i2c_client[i].adapter = &cx->i2c_adap[i]; |
392 | cx->i2c_adap[i].dev.parent = &cx->dev->dev; | 393 | cx->i2c_adap[i].dev.parent = &cx->dev->dev; |
393 | } | 394 | } |
394 | 395 | ||
395 | if (read_reg(CX18_REG_I2C_2_WR) != 0x0003c02f) { | 396 | if (cx18_read_reg(cx, CX18_REG_I2C_2_WR) != 0x0003c02f) { |
396 | /* Reset/Unreset I2C hardware block */ | 397 | /* Reset/Unreset I2C hardware block */ |
397 | write_reg(0x10000000, 0xc71004); /* Clock select 220MHz */ | 398 | /* Clock select 220MHz */ |
398 | write_reg_sync(0x10001000, 0xc71024); /* Clock Enable */ | 399 | cx18_write_reg(cx, 0x10000000, 0xc71004); |
400 | /* Clock Enable */ | ||
401 | cx18_write_reg_sync(cx, 0x10001000, 0xc71024); | ||
399 | } | 402 | } |
400 | /* courtesy of Steven Toth <stoth@hauppauge.com> */ | 403 | /* courtesy of Steven Toth <stoth@hauppauge.com> */ |
401 | write_reg_sync(0x00c00000, 0xc7001c); | 404 | cx18_write_reg_sync(cx, 0x00c00000, 0xc7001c); |
402 | mdelay(10); | 405 | mdelay(10); |
403 | write_reg_sync(0x00c000c0, 0xc7001c); | 406 | cx18_write_reg_sync(cx, 0x00c000c0, 0xc7001c); |
404 | mdelay(10); | 407 | mdelay(10); |
405 | write_reg_sync(0x00c00000, 0xc7001c); | 408 | cx18_write_reg_sync(cx, 0x00c00000, 0xc7001c); |
406 | mdelay(10); | 409 | mdelay(10); |
407 | 410 | ||
408 | write_reg_sync(0x00c00000, 0xc730c8); /* Set to edge-triggered intrs. */ | 411 | /* Set to edge-triggered intrs. */ |
409 | write_reg_sync(0x00c00000, 0xc730c4); /* Clear any stale intrs */ | 412 | cx18_write_reg_sync(cx, 0x00c00000, 0xc730c8); |
413 | /* Clear any stale intrs */ | ||
414 | cx18_write_reg_sync(cx, 0x00c00000, 0xc730c4); | ||
410 | 415 | ||
411 | /* Hw I2C1 Clock Freq ~100kHz */ | 416 | /* Hw I2C1 Clock Freq ~100kHz */ |
412 | write_reg_sync(0x00021c0f & ~4, CX18_REG_I2C_1_WR); | 417 | cx18_write_reg_sync(cx, 0x00021c0f & ~4, CX18_REG_I2C_1_WR); |
413 | cx18_setscl(&cx->i2c_algo_cb_data[0], 1); | 418 | cx18_setscl(&cx->i2c_algo_cb_data[0], 1); |
414 | cx18_setsda(&cx->i2c_algo_cb_data[0], 1); | 419 | cx18_setsda(&cx->i2c_algo_cb_data[0], 1); |
415 | 420 | ||
416 | /* Hw I2C2 Clock Freq ~100kHz */ | 421 | /* Hw I2C2 Clock Freq ~100kHz */ |
417 | write_reg_sync(0x00021c0f & ~4, CX18_REG_I2C_2_WR); | 422 | cx18_write_reg_sync(cx, 0x00021c0f & ~4, CX18_REG_I2C_2_WR); |
418 | cx18_setscl(&cx->i2c_algo_cb_data[1], 1); | 423 | cx18_setscl(&cx->i2c_algo_cb_data[1], 1); |
419 | cx18_setsda(&cx->i2c_algo_cb_data[1], 1); | 424 | cx18_setsda(&cx->i2c_algo_cb_data[1], 1); |
420 | 425 | ||
421 | cx18_reset_i2c_slaves_gpio(cx); | 426 | cx18_reset_i2c_slaves_gpio(cx); |
422 | 427 | ||
423 | return i2c_bit_add_bus(&cx->i2c_adap[0]) || | 428 | return i2c_bit_add_bus(&cx->i2c_adap[0]) || |
424 | i2c_bit_add_bus(&cx->i2c_adap[1]); | 429 | i2c_bit_add_bus(&cx->i2c_adap[1]); |
425 | } | 430 | } |
426 | 431 | ||
427 | void exit_cx18_i2c(struct cx18 *cx) | 432 | void exit_cx18_i2c(struct cx18 *cx) |
428 | { | 433 | { |
429 | int i; | 434 | int i; |
430 | CX18_DEBUG_I2C("i2c exit\n"); | 435 | CX18_DEBUG_I2C("i2c exit\n"); |
431 | write_reg(read_reg(CX18_REG_I2C_1_WR) | 4, CX18_REG_I2C_1_WR); | 436 | cx18_write_reg(cx, cx18_read_reg(cx, CX18_REG_I2C_1_WR) | 4, |
432 | write_reg(read_reg(CX18_REG_I2C_2_WR) | 4, CX18_REG_I2C_2_WR); | 437 | CX18_REG_I2C_1_WR); |
438 | cx18_write_reg(cx, cx18_read_reg(cx, CX18_REG_I2C_2_WR) | 4, | ||
439 | CX18_REG_I2C_2_WR); | ||
433 | 440 | ||
434 | for (i = 0; i < 2; i++) { | 441 | for (i = 0; i < 2; i++) { |
435 | i2c_del_adapter(&cx->i2c_adap[i]); | 442 | i2c_del_adapter(&cx->i2c_adap[i]); |
436 | } | 443 | } |
437 | } | 444 | } |
438 | 445 | ||
439 | /* | 446 | /* |
440 | Hauppauge HVR1600 should have: | 447 | Hauppauge HVR1600 should have: |
441 | 32 cx24227 | 448 | 32 cx24227 |
442 | 98 unknown | 449 | 98 unknown |
443 | a0 eeprom | 450 | a0 eeprom |
444 | c2 tuner | 451 | c2 tuner |
445 | e? zilog ir | 452 | e? zilog ir |
446 | */ | 453 | */ |
447 | 454 |
drivers/media/video/cx18/cx18-io.c
File was created | 1 | /* | |
2 | * cx18 driver PCI memory mapped IO access routines | ||
3 | * | ||
4 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | ||
5 | * Copyright (C) 2008 Andy Walls <awalls@radix.net> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | ||
20 | * 02111-1307 USA | ||
21 | */ | ||
22 | |||
23 | #include "cx18-driver.h" | ||
24 | #include "cx18-irq.h" | ||
25 | |||
26 | void cx18_raw_writel(struct cx18 *cx, u32 val, void __iomem *addr) | ||
27 | { | ||
28 | __raw_writel(val, addr); | ||
29 | } | ||
30 | |||
31 | u32 cx18_raw_readl(struct cx18 *cx, const void __iomem *addr) | ||
32 | { | ||
33 | return __raw_readl(addr); | ||
34 | } | ||
35 | |||
36 | u32 cx18_write_sync(struct cx18 *cx, u32 val, void __iomem *addr) | ||
37 | { | ||
38 | writel(val, addr); | ||
39 | return readl(addr); | ||
40 | } | ||
41 | |||
42 | void cx18_writel(struct cx18 *cx, u32 val, void __iomem *addr) | ||
43 | { | ||
44 | writel(val, addr); | ||
45 | } | ||
46 | |||
47 | u32 cx18_readl(struct cx18 *cx, const void __iomem *addr) | ||
48 | { | ||
49 | return readl(addr); | ||
50 | } | ||
51 | |||
52 | |||
53 | /* Access "register" region of CX23418 memory mapped I/O */ | ||
54 | u32 cx18_read_reg(struct cx18 *cx, u32 reg) | ||
55 | { | ||
56 | return readl(cx->reg_mem + reg); | ||
57 | } | ||
58 | |||
59 | void cx18_write_reg(struct cx18 *cx, u32 val, u32 reg) | ||
60 | { | ||
61 | writel(val, cx->reg_mem + reg); | ||
62 | } | ||
63 | |||
64 | u32 cx18_write_reg_sync(struct cx18 *cx, u32 val, u32 reg) | ||
65 | { | ||
66 | return cx18_write_sync(cx, val, cx->reg_mem + reg); | ||
67 | } | ||
68 | |||
69 | /* Access "encoder memory" region of CX23418 memory mapped I/O */ | ||
70 | u32 cx18_read_enc(struct cx18 *cx, u32 addr) | ||
71 | { | ||
72 | return readl(cx->enc_mem + addr); | ||
73 | } | ||
74 | |||
75 | void cx18_write_enc(struct cx18 *cx, u32 val, u32 addr) | ||
76 | { | ||
77 | writel(val, cx->enc_mem + addr); | ||
78 | } | ||
79 | |||
80 | u32 cx18_write_enc_sync(struct cx18 *cx, u32 val, u32 addr) | ||
81 | { | ||
82 | return cx18_write_sync(cx, val, cx->enc_mem + addr); | ||
83 | } | ||
84 | |||
85 | void cx18_memcpy_fromio(struct cx18 *cx, void *to, | ||
86 | const void __iomem *from, unsigned int len) | ||
87 | { | ||
88 | memcpy_fromio(to, from, len); | ||
89 | } | ||
90 | |||
91 | void cx18_memset_io(struct cx18 *cx, void __iomem *addr, int val, size_t count) | ||
92 | { | ||
93 | memset_io(addr, val, count); | ||
94 | } | ||
95 | |||
96 | void cx18_sw1_irq_enable(struct cx18 *cx, u32 val) | ||
97 | { | ||
98 | u32 r; | ||
99 | cx18_write_reg(cx, val, SW1_INT_STATUS); | ||
100 | r = cx18_read_reg(cx, SW1_INT_ENABLE_PCI); | ||
101 | cx18_write_reg(cx, r | val, SW1_INT_ENABLE_PCI); | ||
102 | } | ||
103 | |||
104 | void cx18_sw1_irq_disable(struct cx18 *cx, u32 val) | ||
105 | { | ||
106 | u32 r; | ||
107 | r = cx18_read_reg(cx, SW1_INT_ENABLE_PCI); | ||
108 | cx18_write_reg(cx, r & ~val, SW1_INT_ENABLE_PCI); | ||
109 | } | ||
110 | |||
111 | void cx18_sw2_irq_enable(struct cx18 *cx, u32 val) | ||
112 | { | ||
113 | u32 r; | ||
114 | cx18_write_reg(cx, val, SW2_INT_STATUS); | ||
115 | r = cx18_read_reg(cx, SW2_INT_ENABLE_PCI); | ||
116 | cx18_write_reg(cx, r | val, SW2_INT_ENABLE_PCI); | ||
117 | } | ||
118 | |||
119 | void cx18_sw2_irq_disable(struct cx18 *cx, u32 val) | ||
120 | { | ||
121 | u32 r; | ||
122 | r = cx18_read_reg(cx, SW2_INT_ENABLE_PCI); | ||
123 | cx18_write_reg(cx, r & ~val, SW2_INT_ENABLE_PCI); | ||
124 | } | ||
125 | |||
126 | void cx18_setup_page(struct cx18 *cx, u32 addr) | ||
127 | { | ||
128 | u32 val; | ||
129 | val = cx18_read_reg(cx, 0xD000F8); | ||
130 | val = (val & ~0x1f00) | ((addr >> 17) & 0x1f00); | ||
131 | cx18_write_reg(cx, val, 0xD000F8); | ||
132 | } | ||
133 | |||
134 | /* Tries to recover from the CX23418 responding improperly on the PCI bus */ | ||
135 | int cx18_pci_try_recover(struct cx18 *cx) | ||
136 | { | ||
137 | u16 status; | ||
138 | |||
139 | pci_read_config_word(cx->dev, PCI_STATUS, &status); | ||
140 | pci_write_config_word(cx->dev, PCI_STATUS, status); | ||
141 | return 0; | ||
142 | } | ||
143 |
drivers/media/video/cx18/cx18-io.h
File was created | 1 | /* | |
2 | * cx18 driver PCI memory mapped IO access routines | ||
3 | * | ||
4 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | ||
5 | * Copyright (C) 2008 Andy Walls <awalls@radix.net> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | ||
20 | * 02111-1307 USA | ||
21 | */ | ||
22 | |||
23 | #ifndef CX18_IO_H | ||
24 | #define CX18_IO_H | ||
25 | |||
26 | #include "cx18-driver.h" | ||
27 | |||
28 | /* This is a PCI post thing, where if the pci register is not read, then | ||
29 | the write doesn't always take effect right away. By reading back the | ||
30 | register any pending PCI writes will be performed (in order), and so | ||
31 | you can be sure that the writes are guaranteed to be done. | ||
32 | |||
33 | Rarely needed, only in some timing sensitive cases. | ||
34 | Apparently if this is not done some motherboards seem | ||
35 | to kill the firmware and get into the broken state until computer is | ||
36 | rebooted. */ | ||
37 | u32 cx18_write_sync(struct cx18 *cx, u32 val, void __iomem *addr); | ||
38 | |||
39 | void cx18_writel(struct cx18 *cx, u32 val, void __iomem *addr); | ||
40 | u32 cx18_readl(struct cx18 *cx, const void __iomem *addr); | ||
41 | |||
42 | /* No endiannes conversion calls */ | ||
43 | void cx18_raw_writel(struct cx18 *cx, u32 val, void __iomem *addr); | ||
44 | u32 cx18_raw_readl(struct cx18 *cx, const void __iomem *addr); | ||
45 | |||
46 | /* Access "register" region of CX23418 memory mapped I/O */ | ||
47 | u32 cx18_read_reg(struct cx18 *cx, u32 reg); | ||
48 | void cx18_write_reg(struct cx18 *cx, u32 val, u32 reg); | ||
49 | u32 cx18_write_reg_sync(struct cx18 *cx, u32 val, u32 reg); | ||
50 | |||
51 | /* Access "encoder memory" region of CX23418 memory mapped I/O */ | ||
52 | u32 cx18_read_enc(struct cx18 *cx, u32 addr); | ||
53 | void cx18_write_enc(struct cx18 *cx, u32 val, u32 addr); | ||
54 | u32 cx18_write_enc_sync(struct cx18 *cx, u32 val, u32 addr); | ||
55 | |||
56 | void cx18_memcpy_fromio(struct cx18 *cx, void *to, | ||
57 | const void __iomem *from, unsigned int len); | ||
58 | void cx18_memset_io(struct cx18 *cx, void __iomem *addr, int val, size_t count); | ||
59 | |||
60 | void cx18_sw1_irq_enable(struct cx18 *cx, u32 val); | ||
61 | void cx18_sw1_irq_disable(struct cx18 *cx, u32 val); | ||
62 | void cx18_sw2_irq_enable(struct cx18 *cx, u32 val); | ||
63 | void cx18_sw2_irq_disable(struct cx18 *cx, u32 val); | ||
64 | void cx18_setup_page(struct cx18 *cx, u32 addr); | ||
65 | |||
66 | /* Tries to recover from the CX23418 responding improperly on the PCI bus */ | ||
67 | int cx18_pci_try_recover(struct cx18 *cx); | ||
68 | |||
69 | #endif /* CX18_IO_H */ | ||
70 |
drivers/media/video/cx18/cx18-ioctl.c
1 | /* | 1 | /* |
2 | * cx18 ioctl system call | 2 | * cx18 ioctl system call |
3 | * | 3 | * |
4 | * Derived from ivtv-ioctl.c | 4 | * Derived from ivtv-ioctl.c |
5 | * | 5 | * |
6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | 6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> |
7 | * | 7 | * |
8 | * This program is free software; you can redistribute it and/or modify | 8 | * This program is free software; you can redistribute it and/or modify |
9 | * it under the terms of the GNU General Public License as published by | 9 | * it under the terms of the GNU General Public License as published by |
10 | * the Free Software Foundation; either version 2 of the License, or | 10 | * the Free Software Foundation; either version 2 of the License, or |
11 | * (at your option) any later version. | 11 | * (at your option) any later version. |
12 | * | 12 | * |
13 | * This program is distributed in the hope that it will be useful, | 13 | * This program is distributed in the hope that it will be useful, |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | * GNU General Public License for more details. | 16 | * GNU General Public License for more details. |
17 | * | 17 | * |
18 | * You should have received a copy of the GNU General Public License | 18 | * You should have received a copy of the GNU General Public License |
19 | * along with this program; if not, write to the Free Software | 19 | * along with this program; if not, write to the Free Software |
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | 20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
21 | * 02111-1307 USA | 21 | * 02111-1307 USA |
22 | */ | 22 | */ |
23 | 23 | ||
24 | #include "cx18-driver.h" | 24 | #include "cx18-driver.h" |
25 | #include "cx18-io.h" | ||
25 | #include "cx18-version.h" | 26 | #include "cx18-version.h" |
26 | #include "cx18-mailbox.h" | 27 | #include "cx18-mailbox.h" |
27 | #include "cx18-i2c.h" | 28 | #include "cx18-i2c.h" |
28 | #include "cx18-queue.h" | 29 | #include "cx18-queue.h" |
29 | #include "cx18-fileops.h" | 30 | #include "cx18-fileops.h" |
30 | #include "cx18-vbi.h" | 31 | #include "cx18-vbi.h" |
31 | #include "cx18-audio.h" | 32 | #include "cx18-audio.h" |
32 | #include "cx18-video.h" | 33 | #include "cx18-video.h" |
33 | #include "cx18-streams.h" | 34 | #include "cx18-streams.h" |
34 | #include "cx18-ioctl.h" | 35 | #include "cx18-ioctl.h" |
35 | #include "cx18-gpio.h" | 36 | #include "cx18-gpio.h" |
36 | #include "cx18-controls.h" | 37 | #include "cx18-controls.h" |
37 | #include "cx18-cards.h" | 38 | #include "cx18-cards.h" |
38 | #include "cx18-av-core.h" | 39 | #include "cx18-av-core.h" |
39 | #include <media/tveeprom.h> | 40 | #include <media/tveeprom.h> |
40 | #include <media/v4l2-chip-ident.h> | 41 | #include <media/v4l2-chip-ident.h> |
41 | #include <linux/i2c-id.h> | 42 | #include <linux/i2c-id.h> |
42 | 43 | ||
43 | u16 cx18_service2vbi(int type) | 44 | u16 cx18_service2vbi(int type) |
44 | { | 45 | { |
45 | switch (type) { | 46 | switch (type) { |
46 | case V4L2_SLICED_TELETEXT_B: | 47 | case V4L2_SLICED_TELETEXT_B: |
47 | return CX18_SLICED_TYPE_TELETEXT_B; | 48 | return CX18_SLICED_TYPE_TELETEXT_B; |
48 | case V4L2_SLICED_CAPTION_525: | 49 | case V4L2_SLICED_CAPTION_525: |
49 | return CX18_SLICED_TYPE_CAPTION_525; | 50 | return CX18_SLICED_TYPE_CAPTION_525; |
50 | case V4L2_SLICED_WSS_625: | 51 | case V4L2_SLICED_WSS_625: |
51 | return CX18_SLICED_TYPE_WSS_625; | 52 | return CX18_SLICED_TYPE_WSS_625; |
52 | case V4L2_SLICED_VPS: | 53 | case V4L2_SLICED_VPS: |
53 | return CX18_SLICED_TYPE_VPS; | 54 | return CX18_SLICED_TYPE_VPS; |
54 | default: | 55 | default: |
55 | return 0; | 56 | return 0; |
56 | } | 57 | } |
57 | } | 58 | } |
58 | 59 | ||
59 | static int valid_service_line(int field, int line, int is_pal) | 60 | static int valid_service_line(int field, int line, int is_pal) |
60 | { | 61 | { |
61 | return (is_pal && line >= 6 && (line != 23 || field == 0)) || | 62 | return (is_pal && line >= 6 && (line != 23 || field == 0)) || |
62 | (!is_pal && line >= 10 && line < 22); | 63 | (!is_pal && line >= 10 && line < 22); |
63 | } | 64 | } |
64 | 65 | ||
65 | static u16 select_service_from_set(int field, int line, u16 set, int is_pal) | 66 | static u16 select_service_from_set(int field, int line, u16 set, int is_pal) |
66 | { | 67 | { |
67 | u16 valid_set = (is_pal ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525); | 68 | u16 valid_set = (is_pal ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525); |
68 | int i; | 69 | int i; |
69 | 70 | ||
70 | set = set & valid_set; | 71 | set = set & valid_set; |
71 | if (set == 0 || !valid_service_line(field, line, is_pal)) | 72 | if (set == 0 || !valid_service_line(field, line, is_pal)) |
72 | return 0; | 73 | return 0; |
73 | if (!is_pal) { | 74 | if (!is_pal) { |
74 | if (line == 21 && (set & V4L2_SLICED_CAPTION_525)) | 75 | if (line == 21 && (set & V4L2_SLICED_CAPTION_525)) |
75 | return V4L2_SLICED_CAPTION_525; | 76 | return V4L2_SLICED_CAPTION_525; |
76 | } else { | 77 | } else { |
77 | if (line == 16 && field == 0 && (set & V4L2_SLICED_VPS)) | 78 | if (line == 16 && field == 0 && (set & V4L2_SLICED_VPS)) |
78 | return V4L2_SLICED_VPS; | 79 | return V4L2_SLICED_VPS; |
79 | if (line == 23 && field == 0 && (set & V4L2_SLICED_WSS_625)) | 80 | if (line == 23 && field == 0 && (set & V4L2_SLICED_WSS_625)) |
80 | return V4L2_SLICED_WSS_625; | 81 | return V4L2_SLICED_WSS_625; |
81 | if (line == 23) | 82 | if (line == 23) |
82 | return 0; | 83 | return 0; |
83 | } | 84 | } |
84 | for (i = 0; i < 32; i++) { | 85 | for (i = 0; i < 32; i++) { |
85 | if ((1 << i) & set) | 86 | if ((1 << i) & set) |
86 | return 1 << i; | 87 | return 1 << i; |
87 | } | 88 | } |
88 | return 0; | 89 | return 0; |
89 | } | 90 | } |
90 | 91 | ||
91 | void cx18_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal) | 92 | void cx18_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal) |
92 | { | 93 | { |
93 | u16 set = fmt->service_set; | 94 | u16 set = fmt->service_set; |
94 | int f, l; | 95 | int f, l; |
95 | 96 | ||
96 | fmt->service_set = 0; | 97 | fmt->service_set = 0; |
97 | for (f = 0; f < 2; f++) { | 98 | for (f = 0; f < 2; f++) { |
98 | for (l = 0; l < 24; l++) | 99 | for (l = 0; l < 24; l++) |
99 | fmt->service_lines[f][l] = select_service_from_set(f, l, set, is_pal); | 100 | fmt->service_lines[f][l] = select_service_from_set(f, l, set, is_pal); |
100 | } | 101 | } |
101 | } | 102 | } |
102 | 103 | ||
103 | 104 | ||
104 | u16 cx18_get_service_set(struct v4l2_sliced_vbi_format *fmt) | 105 | u16 cx18_get_service_set(struct v4l2_sliced_vbi_format *fmt) |
105 | { | 106 | { |
106 | int f, l; | 107 | int f, l; |
107 | u16 set = 0; | 108 | u16 set = 0; |
108 | 109 | ||
109 | for (f = 0; f < 2; f++) { | 110 | for (f = 0; f < 2; f++) { |
110 | for (l = 0; l < 24; l++) | 111 | for (l = 0; l < 24; l++) |
111 | set |= fmt->service_lines[f][l]; | 112 | set |= fmt->service_lines[f][l]; |
112 | } | 113 | } |
113 | return set; | 114 | return set; |
114 | } | 115 | } |
115 | 116 | ||
116 | static int cx18_g_fmt_vid_cap(struct file *file, void *fh, | 117 | static int cx18_g_fmt_vid_cap(struct file *file, void *fh, |
117 | struct v4l2_format *fmt) | 118 | struct v4l2_format *fmt) |
118 | { | 119 | { |
119 | struct cx18_open_id *id = fh; | 120 | struct cx18_open_id *id = fh; |
120 | struct cx18 *cx = id->cx; | 121 | struct cx18 *cx = id->cx; |
121 | struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; | 122 | struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; |
122 | 123 | ||
123 | pixfmt->width = cx->params.width; | 124 | pixfmt->width = cx->params.width; |
124 | pixfmt->height = cx->params.height; | 125 | pixfmt->height = cx->params.height; |
125 | pixfmt->colorspace = V4L2_COLORSPACE_SMPTE170M; | 126 | pixfmt->colorspace = V4L2_COLORSPACE_SMPTE170M; |
126 | pixfmt->field = V4L2_FIELD_INTERLACED; | 127 | pixfmt->field = V4L2_FIELD_INTERLACED; |
127 | pixfmt->priv = 0; | 128 | pixfmt->priv = 0; |
128 | if (id->type == CX18_ENC_STREAM_TYPE_YUV) { | 129 | if (id->type == CX18_ENC_STREAM_TYPE_YUV) { |
129 | pixfmt->pixelformat = V4L2_PIX_FMT_HM12; | 130 | pixfmt->pixelformat = V4L2_PIX_FMT_HM12; |
130 | /* YUV size is (Y=(h*w) + UV=(h*(w/2))) */ | 131 | /* YUV size is (Y=(h*w) + UV=(h*(w/2))) */ |
131 | pixfmt->sizeimage = | 132 | pixfmt->sizeimage = |
132 | pixfmt->height * pixfmt->width + | 133 | pixfmt->height * pixfmt->width + |
133 | pixfmt->height * (pixfmt->width / 2); | 134 | pixfmt->height * (pixfmt->width / 2); |
134 | pixfmt->bytesperline = 720; | 135 | pixfmt->bytesperline = 720; |
135 | } else { | 136 | } else { |
136 | pixfmt->pixelformat = V4L2_PIX_FMT_MPEG; | 137 | pixfmt->pixelformat = V4L2_PIX_FMT_MPEG; |
137 | pixfmt->sizeimage = 128 * 1024; | 138 | pixfmt->sizeimage = 128 * 1024; |
138 | pixfmt->bytesperline = 0; | 139 | pixfmt->bytesperline = 0; |
139 | } | 140 | } |
140 | return 0; | 141 | return 0; |
141 | } | 142 | } |
142 | 143 | ||
143 | static int cx18_g_fmt_vbi_cap(struct file *file, void *fh, | 144 | static int cx18_g_fmt_vbi_cap(struct file *file, void *fh, |
144 | struct v4l2_format *fmt) | 145 | struct v4l2_format *fmt) |
145 | { | 146 | { |
146 | struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; | 147 | struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; |
147 | struct v4l2_vbi_format *vbifmt = &fmt->fmt.vbi; | 148 | struct v4l2_vbi_format *vbifmt = &fmt->fmt.vbi; |
148 | 149 | ||
149 | vbifmt->sampling_rate = 27000000; | 150 | vbifmt->sampling_rate = 27000000; |
150 | vbifmt->offset = 248; | 151 | vbifmt->offset = 248; |
151 | vbifmt->samples_per_line = cx->vbi.raw_decoder_line_size - 4; | 152 | vbifmt->samples_per_line = cx->vbi.raw_decoder_line_size - 4; |
152 | vbifmt->sample_format = V4L2_PIX_FMT_GREY; | 153 | vbifmt->sample_format = V4L2_PIX_FMT_GREY; |
153 | vbifmt->start[0] = cx->vbi.start[0]; | 154 | vbifmt->start[0] = cx->vbi.start[0]; |
154 | vbifmt->start[1] = cx->vbi.start[1]; | 155 | vbifmt->start[1] = cx->vbi.start[1]; |
155 | vbifmt->count[0] = vbifmt->count[1] = cx->vbi.count; | 156 | vbifmt->count[0] = vbifmt->count[1] = cx->vbi.count; |
156 | vbifmt->flags = 0; | 157 | vbifmt->flags = 0; |
157 | vbifmt->reserved[0] = 0; | 158 | vbifmt->reserved[0] = 0; |
158 | vbifmt->reserved[1] = 0; | 159 | vbifmt->reserved[1] = 0; |
159 | return 0; | 160 | return 0; |
160 | } | 161 | } |
161 | 162 | ||
162 | static int cx18_g_fmt_sliced_vbi_cap(struct file *file, void *fh, | 163 | static int cx18_g_fmt_sliced_vbi_cap(struct file *file, void *fh, |
163 | struct v4l2_format *fmt) | 164 | struct v4l2_format *fmt) |
164 | { | 165 | { |
165 | return -EINVAL; | 166 | return -EINVAL; |
166 | } | 167 | } |
167 | 168 | ||
168 | static int cx18_try_fmt_vid_cap(struct file *file, void *fh, | 169 | static int cx18_try_fmt_vid_cap(struct file *file, void *fh, |
169 | struct v4l2_format *fmt) | 170 | struct v4l2_format *fmt) |
170 | { | 171 | { |
171 | struct cx18_open_id *id = fh; | 172 | struct cx18_open_id *id = fh; |
172 | struct cx18 *cx = id->cx; | 173 | struct cx18 *cx = id->cx; |
173 | 174 | ||
174 | int w = fmt->fmt.pix.width; | 175 | int w = fmt->fmt.pix.width; |
175 | int h = fmt->fmt.pix.height; | 176 | int h = fmt->fmt.pix.height; |
176 | 177 | ||
177 | w = min(w, 720); | 178 | w = min(w, 720); |
178 | w = max(w, 1); | 179 | w = max(w, 1); |
179 | h = min(h, cx->is_50hz ? 576 : 480); | 180 | h = min(h, cx->is_50hz ? 576 : 480); |
180 | h = max(h, 2); | 181 | h = max(h, 2); |
181 | cx18_g_fmt_vid_cap(file, fh, fmt); | 182 | cx18_g_fmt_vid_cap(file, fh, fmt); |
182 | fmt->fmt.pix.width = w; | 183 | fmt->fmt.pix.width = w; |
183 | fmt->fmt.pix.height = h; | 184 | fmt->fmt.pix.height = h; |
184 | return 0; | 185 | return 0; |
185 | } | 186 | } |
186 | 187 | ||
187 | static int cx18_try_fmt_vbi_cap(struct file *file, void *fh, | 188 | static int cx18_try_fmt_vbi_cap(struct file *file, void *fh, |
188 | struct v4l2_format *fmt) | 189 | struct v4l2_format *fmt) |
189 | { | 190 | { |
190 | return cx18_g_fmt_vbi_cap(file, fh, fmt); | 191 | return cx18_g_fmt_vbi_cap(file, fh, fmt); |
191 | } | 192 | } |
192 | 193 | ||
193 | static int cx18_try_fmt_sliced_vbi_cap(struct file *file, void *fh, | 194 | static int cx18_try_fmt_sliced_vbi_cap(struct file *file, void *fh, |
194 | struct v4l2_format *fmt) | 195 | struct v4l2_format *fmt) |
195 | { | 196 | { |
196 | return -EINVAL; | 197 | return -EINVAL; |
197 | } | 198 | } |
198 | 199 | ||
199 | static int cx18_s_fmt_vid_cap(struct file *file, void *fh, | 200 | static int cx18_s_fmt_vid_cap(struct file *file, void *fh, |
200 | struct v4l2_format *fmt) | 201 | struct v4l2_format *fmt) |
201 | { | 202 | { |
202 | struct cx18_open_id *id = fh; | 203 | struct cx18_open_id *id = fh; |
203 | struct cx18 *cx = id->cx; | 204 | struct cx18 *cx = id->cx; |
204 | int ret; | 205 | int ret; |
205 | int w = fmt->fmt.pix.width; | 206 | int w = fmt->fmt.pix.width; |
206 | int h = fmt->fmt.pix.height; | 207 | int h = fmt->fmt.pix.height; |
207 | 208 | ||
208 | ret = v4l2_prio_check(&cx->prio, &id->prio); | 209 | ret = v4l2_prio_check(&cx->prio, &id->prio); |
209 | if (ret) | 210 | if (ret) |
210 | return ret; | 211 | return ret; |
211 | 212 | ||
212 | ret = cx18_try_fmt_vid_cap(file, fh, fmt); | 213 | ret = cx18_try_fmt_vid_cap(file, fh, fmt); |
213 | if (ret) | 214 | if (ret) |
214 | return ret; | 215 | return ret; |
215 | 216 | ||
216 | if (cx->params.width == w && cx->params.height == h) | 217 | if (cx->params.width == w && cx->params.height == h) |
217 | return 0; | 218 | return 0; |
218 | 219 | ||
219 | if (atomic_read(&cx->ana_capturing) > 0) | 220 | if (atomic_read(&cx->ana_capturing) > 0) |
220 | return -EBUSY; | 221 | return -EBUSY; |
221 | 222 | ||
222 | cx->params.width = w; | 223 | cx->params.width = w; |
223 | cx->params.height = h; | 224 | cx->params.height = h; |
224 | cx18_av_cmd(cx, VIDIOC_S_FMT, fmt); | 225 | cx18_av_cmd(cx, VIDIOC_S_FMT, fmt); |
225 | return cx18_g_fmt_vid_cap(file, fh, fmt); | 226 | return cx18_g_fmt_vid_cap(file, fh, fmt); |
226 | } | 227 | } |
227 | 228 | ||
228 | static int cx18_s_fmt_vbi_cap(struct file *file, void *fh, | 229 | static int cx18_s_fmt_vbi_cap(struct file *file, void *fh, |
229 | struct v4l2_format *fmt) | 230 | struct v4l2_format *fmt) |
230 | { | 231 | { |
231 | struct cx18_open_id *id = fh; | 232 | struct cx18_open_id *id = fh; |
232 | struct cx18 *cx = id->cx; | 233 | struct cx18 *cx = id->cx; |
233 | int ret; | 234 | int ret; |
234 | 235 | ||
235 | ret = v4l2_prio_check(&cx->prio, &id->prio); | 236 | ret = v4l2_prio_check(&cx->prio, &id->prio); |
236 | if (ret) | 237 | if (ret) |
237 | return ret; | 238 | return ret; |
238 | 239 | ||
239 | if (id->type == CX18_ENC_STREAM_TYPE_VBI && | 240 | if (id->type == CX18_ENC_STREAM_TYPE_VBI && |
240 | cx->vbi.sliced_in->service_set && | 241 | cx->vbi.sliced_in->service_set && |
241 | atomic_read(&cx->ana_capturing) > 0) | 242 | atomic_read(&cx->ana_capturing) > 0) |
242 | return -EBUSY; | 243 | return -EBUSY; |
243 | 244 | ||
244 | cx->vbi.sliced_in->service_set = 0; | 245 | cx->vbi.sliced_in->service_set = 0; |
245 | cx18_av_cmd(cx, VIDIOC_S_FMT, &cx->vbi.in); | 246 | cx18_av_cmd(cx, VIDIOC_S_FMT, &cx->vbi.in); |
246 | return cx18_g_fmt_vbi_cap(file, fh, fmt); | 247 | return cx18_g_fmt_vbi_cap(file, fh, fmt); |
247 | } | 248 | } |
248 | 249 | ||
249 | static int cx18_s_fmt_sliced_vbi_cap(struct file *file, void *fh, | 250 | static int cx18_s_fmt_sliced_vbi_cap(struct file *file, void *fh, |
250 | struct v4l2_format *fmt) | 251 | struct v4l2_format *fmt) |
251 | { | 252 | { |
252 | return -EINVAL; | 253 | return -EINVAL; |
253 | } | 254 | } |
254 | 255 | ||
255 | static int cx18_g_chip_ident(struct file *file, void *fh, | 256 | static int cx18_g_chip_ident(struct file *file, void *fh, |
256 | struct v4l2_chip_ident *chip) | 257 | struct v4l2_chip_ident *chip) |
257 | { | 258 | { |
258 | struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; | 259 | struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; |
259 | 260 | ||
260 | chip->ident = V4L2_IDENT_NONE; | 261 | chip->ident = V4L2_IDENT_NONE; |
261 | chip->revision = 0; | 262 | chip->revision = 0; |
262 | if (chip->match_type == V4L2_CHIP_MATCH_HOST) { | 263 | if (chip->match_type == V4L2_CHIP_MATCH_HOST) { |
263 | if (v4l2_chip_match_host(chip->match_type, chip->match_chip)) | 264 | if (v4l2_chip_match_host(chip->match_type, chip->match_chip)) |
264 | chip->ident = V4L2_IDENT_CX23418; | 265 | chip->ident = V4L2_IDENT_CX23418; |
265 | return 0; | 266 | return 0; |
266 | } | 267 | } |
267 | if (chip->match_type == V4L2_CHIP_MATCH_I2C_DRIVER) | 268 | if (chip->match_type == V4L2_CHIP_MATCH_I2C_DRIVER) |
268 | return cx18_i2c_id(cx, chip->match_chip, VIDIOC_G_CHIP_IDENT, | 269 | return cx18_i2c_id(cx, chip->match_chip, VIDIOC_G_CHIP_IDENT, |
269 | chip); | 270 | chip); |
270 | if (chip->match_type == V4L2_CHIP_MATCH_I2C_ADDR) | 271 | if (chip->match_type == V4L2_CHIP_MATCH_I2C_ADDR) |
271 | return cx18_call_i2c_client(cx, chip->match_chip, | 272 | return cx18_call_i2c_client(cx, chip->match_chip, |
272 | VIDIOC_G_CHIP_IDENT, chip); | 273 | VIDIOC_G_CHIP_IDENT, chip); |
273 | return -EINVAL; | 274 | return -EINVAL; |
274 | } | 275 | } |
275 | 276 | ||
276 | #ifdef CONFIG_VIDEO_ADV_DEBUG | 277 | #ifdef CONFIG_VIDEO_ADV_DEBUG |
277 | static int cx18_cxc(struct cx18 *cx, unsigned int cmd, void *arg) | 278 | static int cx18_cxc(struct cx18 *cx, unsigned int cmd, void *arg) |
278 | { | 279 | { |
279 | struct v4l2_register *regs = arg; | 280 | struct v4l2_register *regs = arg; |
280 | unsigned long flags; | 281 | unsigned long flags; |
281 | 282 | ||
282 | if (!capable(CAP_SYS_ADMIN)) | 283 | if (!capable(CAP_SYS_ADMIN)) |
283 | return -EPERM; | 284 | return -EPERM; |
284 | if (regs->reg >= CX18_MEM_OFFSET + CX18_MEM_SIZE) | 285 | if (regs->reg >= CX18_MEM_OFFSET + CX18_MEM_SIZE) |
285 | return -EINVAL; | 286 | return -EINVAL; |
286 | 287 | ||
287 | spin_lock_irqsave(&cx18_cards_lock, flags); | 288 | spin_lock_irqsave(&cx18_cards_lock, flags); |
288 | if (cmd == VIDIOC_DBG_G_REGISTER) | 289 | if (cmd == VIDIOC_DBG_G_REGISTER) |
289 | regs->val = read_enc(regs->reg); | 290 | regs->val = cx18_read_enc(cx, regs->reg); |
290 | else | 291 | else |
291 | write_enc(regs->val, regs->reg); | 292 | cx18_write_enc(cx, regs->val, regs->reg); |
292 | spin_unlock_irqrestore(&cx18_cards_lock, flags); | 293 | spin_unlock_irqrestore(&cx18_cards_lock, flags); |
293 | return 0; | 294 | return 0; |
294 | } | 295 | } |
295 | 296 | ||
296 | static int cx18_g_register(struct file *file, void *fh, | 297 | static int cx18_g_register(struct file *file, void *fh, |
297 | struct v4l2_register *reg) | 298 | struct v4l2_register *reg) |
298 | { | 299 | { |
299 | struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; | 300 | struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; |
300 | 301 | ||
301 | if (v4l2_chip_match_host(reg->match_type, reg->match_chip)) | 302 | if (v4l2_chip_match_host(reg->match_type, reg->match_chip)) |
302 | return cx18_cxc(cx, VIDIOC_DBG_G_REGISTER, reg); | 303 | return cx18_cxc(cx, VIDIOC_DBG_G_REGISTER, reg); |
303 | if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER) | 304 | if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER) |
304 | return cx18_i2c_id(cx, reg->match_chip, VIDIOC_DBG_G_REGISTER, | 305 | return cx18_i2c_id(cx, reg->match_chip, VIDIOC_DBG_G_REGISTER, |
305 | reg); | 306 | reg); |
306 | return cx18_call_i2c_client(cx, reg->match_chip, VIDIOC_DBG_G_REGISTER, | 307 | return cx18_call_i2c_client(cx, reg->match_chip, VIDIOC_DBG_G_REGISTER, |
307 | reg); | 308 | reg); |
308 | } | 309 | } |
309 | 310 | ||
310 | static int cx18_s_register(struct file *file, void *fh, | 311 | static int cx18_s_register(struct file *file, void *fh, |
311 | struct v4l2_register *reg) | 312 | struct v4l2_register *reg) |
312 | { | 313 | { |
313 | struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; | 314 | struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; |
314 | 315 | ||
315 | if (v4l2_chip_match_host(reg->match_type, reg->match_chip)) | 316 | if (v4l2_chip_match_host(reg->match_type, reg->match_chip)) |
316 | return cx18_cxc(cx, VIDIOC_DBG_S_REGISTER, reg); | 317 | return cx18_cxc(cx, VIDIOC_DBG_S_REGISTER, reg); |
317 | if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER) | 318 | if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER) |
318 | return cx18_i2c_id(cx, reg->match_chip, VIDIOC_DBG_S_REGISTER, | 319 | return cx18_i2c_id(cx, reg->match_chip, VIDIOC_DBG_S_REGISTER, |
319 | reg); | 320 | reg); |
320 | return cx18_call_i2c_client(cx, reg->match_chip, VIDIOC_DBG_S_REGISTER, | 321 | return cx18_call_i2c_client(cx, reg->match_chip, VIDIOC_DBG_S_REGISTER, |
321 | reg); | 322 | reg); |
322 | } | 323 | } |
323 | #endif | 324 | #endif |
324 | 325 | ||
325 | static int cx18_g_priority(struct file *file, void *fh, enum v4l2_priority *p) | 326 | static int cx18_g_priority(struct file *file, void *fh, enum v4l2_priority *p) |
326 | { | 327 | { |
327 | struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; | 328 | struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; |
328 | 329 | ||
329 | *p = v4l2_prio_max(&cx->prio); | 330 | *p = v4l2_prio_max(&cx->prio); |
330 | return 0; | 331 | return 0; |
331 | } | 332 | } |
332 | 333 | ||
333 | static int cx18_s_priority(struct file *file, void *fh, enum v4l2_priority prio) | 334 | static int cx18_s_priority(struct file *file, void *fh, enum v4l2_priority prio) |
334 | { | 335 | { |
335 | struct cx18_open_id *id = fh; | 336 | struct cx18_open_id *id = fh; |
336 | struct cx18 *cx = id->cx; | 337 | struct cx18 *cx = id->cx; |
337 | 338 | ||
338 | return v4l2_prio_change(&cx->prio, &id->prio, prio); | 339 | return v4l2_prio_change(&cx->prio, &id->prio, prio); |
339 | } | 340 | } |
340 | 341 | ||
341 | static int cx18_querycap(struct file *file, void *fh, | 342 | static int cx18_querycap(struct file *file, void *fh, |
342 | struct v4l2_capability *vcap) | 343 | struct v4l2_capability *vcap) |
343 | { | 344 | { |
344 | struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; | 345 | struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; |
345 | 346 | ||
346 | strlcpy(vcap->driver, CX18_DRIVER_NAME, sizeof(vcap->driver)); | 347 | strlcpy(vcap->driver, CX18_DRIVER_NAME, sizeof(vcap->driver)); |
347 | strlcpy(vcap->card, cx->card_name, sizeof(vcap->card)); | 348 | strlcpy(vcap->card, cx->card_name, sizeof(vcap->card)); |
348 | strlcpy(vcap->bus_info, pci_name(cx->dev), sizeof(vcap->bus_info)); | 349 | strlcpy(vcap->bus_info, pci_name(cx->dev), sizeof(vcap->bus_info)); |
349 | vcap->version = CX18_DRIVER_VERSION; /* version */ | 350 | vcap->version = CX18_DRIVER_VERSION; /* version */ |
350 | vcap->capabilities = cx->v4l2_cap; /* capabilities */ | 351 | vcap->capabilities = cx->v4l2_cap; /* capabilities */ |
351 | return 0; | 352 | return 0; |
352 | } | 353 | } |
353 | 354 | ||
354 | static int cx18_enumaudio(struct file *file, void *fh, struct v4l2_audio *vin) | 355 | static int cx18_enumaudio(struct file *file, void *fh, struct v4l2_audio *vin) |
355 | { | 356 | { |
356 | struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; | 357 | struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; |
357 | 358 | ||
358 | return cx18_get_audio_input(cx, vin->index, vin); | 359 | return cx18_get_audio_input(cx, vin->index, vin); |
359 | } | 360 | } |
360 | 361 | ||
361 | static int cx18_g_audio(struct file *file, void *fh, struct v4l2_audio *vin) | 362 | static int cx18_g_audio(struct file *file, void *fh, struct v4l2_audio *vin) |
362 | { | 363 | { |
363 | struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; | 364 | struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; |
364 | 365 | ||
365 | vin->index = cx->audio_input; | 366 | vin->index = cx->audio_input; |
366 | return cx18_get_audio_input(cx, vin->index, vin); | 367 | return cx18_get_audio_input(cx, vin->index, vin); |
367 | } | 368 | } |
368 | 369 | ||
369 | static int cx18_s_audio(struct file *file, void *fh, struct v4l2_audio *vout) | 370 | static int cx18_s_audio(struct file *file, void *fh, struct v4l2_audio *vout) |
370 | { | 371 | { |
371 | struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; | 372 | struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; |
372 | 373 | ||
373 | if (vout->index >= cx->nof_audio_inputs) | 374 | if (vout->index >= cx->nof_audio_inputs) |
374 | return -EINVAL; | 375 | return -EINVAL; |
375 | cx->audio_input = vout->index; | 376 | cx->audio_input = vout->index; |
376 | cx18_audio_set_io(cx); | 377 | cx18_audio_set_io(cx); |
377 | return 0; | 378 | return 0; |
378 | } | 379 | } |
379 | 380 | ||
380 | static int cx18_enum_input(struct file *file, void *fh, struct v4l2_input *vin) | 381 | static int cx18_enum_input(struct file *file, void *fh, struct v4l2_input *vin) |
381 | { | 382 | { |
382 | struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; | 383 | struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; |
383 | 384 | ||
384 | /* set it to defaults from our table */ | 385 | /* set it to defaults from our table */ |
385 | return cx18_get_input(cx, vin->index, vin); | 386 | return cx18_get_input(cx, vin->index, vin); |
386 | } | 387 | } |
387 | 388 | ||
388 | static int cx18_cropcap(struct file *file, void *fh, | 389 | static int cx18_cropcap(struct file *file, void *fh, |
389 | struct v4l2_cropcap *cropcap) | 390 | struct v4l2_cropcap *cropcap) |
390 | { | 391 | { |
391 | struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; | 392 | struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; |
392 | 393 | ||
393 | if (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) | 394 | if (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) |
394 | return -EINVAL; | 395 | return -EINVAL; |
395 | cropcap->bounds.top = cropcap->bounds.left = 0; | 396 | cropcap->bounds.top = cropcap->bounds.left = 0; |
396 | cropcap->bounds.width = 720; | 397 | cropcap->bounds.width = 720; |
397 | cropcap->bounds.height = cx->is_50hz ? 576 : 480; | 398 | cropcap->bounds.height = cx->is_50hz ? 576 : 480; |
398 | cropcap->pixelaspect.numerator = cx->is_50hz ? 59 : 10; | 399 | cropcap->pixelaspect.numerator = cx->is_50hz ? 59 : 10; |
399 | cropcap->pixelaspect.denominator = cx->is_50hz ? 54 : 11; | 400 | cropcap->pixelaspect.denominator = cx->is_50hz ? 54 : 11; |
400 | cropcap->defrect = cropcap->bounds; | 401 | cropcap->defrect = cropcap->bounds; |
401 | return 0; | 402 | return 0; |
402 | } | 403 | } |
403 | 404 | ||
404 | static int cx18_s_crop(struct file *file, void *fh, struct v4l2_crop *crop) | 405 | static int cx18_s_crop(struct file *file, void *fh, struct v4l2_crop *crop) |
405 | { | 406 | { |
406 | struct cx18_open_id *id = fh; | 407 | struct cx18_open_id *id = fh; |
407 | struct cx18 *cx = id->cx; | 408 | struct cx18 *cx = id->cx; |
408 | int ret; | 409 | int ret; |
409 | 410 | ||
410 | ret = v4l2_prio_check(&cx->prio, &id->prio); | 411 | ret = v4l2_prio_check(&cx->prio, &id->prio); |
411 | if (ret) | 412 | if (ret) |
412 | return ret; | 413 | return ret; |
413 | 414 | ||
414 | if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) | 415 | if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) |
415 | return -EINVAL; | 416 | return -EINVAL; |
416 | return cx18_av_cmd(cx, VIDIOC_S_CROP, crop); | 417 | return cx18_av_cmd(cx, VIDIOC_S_CROP, crop); |
417 | } | 418 | } |
418 | 419 | ||
419 | static int cx18_g_crop(struct file *file, void *fh, struct v4l2_crop *crop) | 420 | static int cx18_g_crop(struct file *file, void *fh, struct v4l2_crop *crop) |
420 | { | 421 | { |
421 | struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; | 422 | struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; |
422 | 423 | ||
423 | if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) | 424 | if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) |
424 | return -EINVAL; | 425 | return -EINVAL; |
425 | return cx18_av_cmd(cx, VIDIOC_G_CROP, crop); | 426 | return cx18_av_cmd(cx, VIDIOC_G_CROP, crop); |
426 | } | 427 | } |
427 | 428 | ||
428 | static int cx18_enum_fmt_vid_cap(struct file *file, void *fh, | 429 | static int cx18_enum_fmt_vid_cap(struct file *file, void *fh, |
429 | struct v4l2_fmtdesc *fmt) | 430 | struct v4l2_fmtdesc *fmt) |
430 | { | 431 | { |
431 | static struct v4l2_fmtdesc formats[] = { | 432 | static struct v4l2_fmtdesc formats[] = { |
432 | { 0, V4L2_BUF_TYPE_VIDEO_CAPTURE, 0, | 433 | { 0, V4L2_BUF_TYPE_VIDEO_CAPTURE, 0, |
433 | "HM12 (YUV 4:1:1)", V4L2_PIX_FMT_HM12, { 0, 0, 0, 0 } | 434 | "HM12 (YUV 4:1:1)", V4L2_PIX_FMT_HM12, { 0, 0, 0, 0 } |
434 | }, | 435 | }, |
435 | { 1, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FMT_FLAG_COMPRESSED, | 436 | { 1, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FMT_FLAG_COMPRESSED, |
436 | "MPEG", V4L2_PIX_FMT_MPEG, { 0, 0, 0, 0 } | 437 | "MPEG", V4L2_PIX_FMT_MPEG, { 0, 0, 0, 0 } |
437 | } | 438 | } |
438 | }; | 439 | }; |
439 | 440 | ||
440 | if (fmt->index > 1) | 441 | if (fmt->index > 1) |
441 | return -EINVAL; | 442 | return -EINVAL; |
442 | *fmt = formats[fmt->index]; | 443 | *fmt = formats[fmt->index]; |
443 | return 0; | 444 | return 0; |
444 | } | 445 | } |
445 | 446 | ||
446 | static int cx18_g_input(struct file *file, void *fh, unsigned int *i) | 447 | static int cx18_g_input(struct file *file, void *fh, unsigned int *i) |
447 | { | 448 | { |
448 | struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; | 449 | struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; |
449 | 450 | ||
450 | *i = cx->active_input; | 451 | *i = cx->active_input; |
451 | return 0; | 452 | return 0; |
452 | } | 453 | } |
453 | 454 | ||
454 | int cx18_s_input(struct file *file, void *fh, unsigned int inp) | 455 | int cx18_s_input(struct file *file, void *fh, unsigned int inp) |
455 | { | 456 | { |
456 | struct cx18_open_id *id = fh; | 457 | struct cx18_open_id *id = fh; |
457 | struct cx18 *cx = id->cx; | 458 | struct cx18 *cx = id->cx; |
458 | int ret; | 459 | int ret; |
459 | 460 | ||
460 | ret = v4l2_prio_check(&cx->prio, &id->prio); | 461 | ret = v4l2_prio_check(&cx->prio, &id->prio); |
461 | if (ret) | 462 | if (ret) |
462 | return ret; | 463 | return ret; |
463 | 464 | ||
464 | if (inp < 0 || inp >= cx->nof_inputs) | 465 | if (inp < 0 || inp >= cx->nof_inputs) |
465 | return -EINVAL; | 466 | return -EINVAL; |
466 | 467 | ||
467 | if (inp == cx->active_input) { | 468 | if (inp == cx->active_input) { |
468 | CX18_DEBUG_INFO("Input unchanged\n"); | 469 | CX18_DEBUG_INFO("Input unchanged\n"); |
469 | return 0; | 470 | return 0; |
470 | } | 471 | } |
471 | 472 | ||
472 | CX18_DEBUG_INFO("Changing input from %d to %d\n", | 473 | CX18_DEBUG_INFO("Changing input from %d to %d\n", |
473 | cx->active_input, inp); | 474 | cx->active_input, inp); |
474 | 475 | ||
475 | cx->active_input = inp; | 476 | cx->active_input = inp; |
476 | /* Set the audio input to whatever is appropriate for the input type. */ | 477 | /* Set the audio input to whatever is appropriate for the input type. */ |
477 | cx->audio_input = cx->card->video_inputs[inp].audio_index; | 478 | cx->audio_input = cx->card->video_inputs[inp].audio_index; |
478 | 479 | ||
479 | /* prevent others from messing with the streams until | 480 | /* prevent others from messing with the streams until |
480 | we're finished changing inputs. */ | 481 | we're finished changing inputs. */ |
481 | cx18_mute(cx); | 482 | cx18_mute(cx); |
482 | cx18_video_set_io(cx); | 483 | cx18_video_set_io(cx); |
483 | cx18_audio_set_io(cx); | 484 | cx18_audio_set_io(cx); |
484 | cx18_unmute(cx); | 485 | cx18_unmute(cx); |
485 | return 0; | 486 | return 0; |
486 | } | 487 | } |
487 | 488 | ||
488 | static int cx18_g_frequency(struct file *file, void *fh, | 489 | static int cx18_g_frequency(struct file *file, void *fh, |
489 | struct v4l2_frequency *vf) | 490 | struct v4l2_frequency *vf) |
490 | { | 491 | { |
491 | struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; | 492 | struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; |
492 | 493 | ||
493 | if (vf->tuner != 0) | 494 | if (vf->tuner != 0) |
494 | return -EINVAL; | 495 | return -EINVAL; |
495 | 496 | ||
496 | cx18_call_i2c_clients(cx, VIDIOC_G_FREQUENCY, vf); | 497 | cx18_call_i2c_clients(cx, VIDIOC_G_FREQUENCY, vf); |
497 | return 0; | 498 | return 0; |
498 | } | 499 | } |
499 | 500 | ||
500 | int cx18_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf) | 501 | int cx18_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf) |
501 | { | 502 | { |
502 | struct cx18_open_id *id = fh; | 503 | struct cx18_open_id *id = fh; |
503 | struct cx18 *cx = id->cx; | 504 | struct cx18 *cx = id->cx; |
504 | int ret; | 505 | int ret; |
505 | 506 | ||
506 | ret = v4l2_prio_check(&cx->prio, &id->prio); | 507 | ret = v4l2_prio_check(&cx->prio, &id->prio); |
507 | if (ret) | 508 | if (ret) |
508 | return ret; | 509 | return ret; |
509 | 510 | ||
510 | if (vf->tuner != 0) | 511 | if (vf->tuner != 0) |
511 | return -EINVAL; | 512 | return -EINVAL; |
512 | 513 | ||
513 | cx18_mute(cx); | 514 | cx18_mute(cx); |
514 | CX18_DEBUG_INFO("v4l2 ioctl: set frequency %d\n", vf->frequency); | 515 | CX18_DEBUG_INFO("v4l2 ioctl: set frequency %d\n", vf->frequency); |
515 | cx18_call_i2c_clients(cx, VIDIOC_S_FREQUENCY, vf); | 516 | cx18_call_i2c_clients(cx, VIDIOC_S_FREQUENCY, vf); |
516 | cx18_unmute(cx); | 517 | cx18_unmute(cx); |
517 | return 0; | 518 | return 0; |
518 | } | 519 | } |
519 | 520 | ||
520 | static int cx18_g_std(struct file *file, void *fh, v4l2_std_id *std) | 521 | static int cx18_g_std(struct file *file, void *fh, v4l2_std_id *std) |
521 | { | 522 | { |
522 | struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; | 523 | struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; |
523 | 524 | ||
524 | *std = cx->std; | 525 | *std = cx->std; |
525 | return 0; | 526 | return 0; |
526 | } | 527 | } |
527 | 528 | ||
528 | int cx18_s_std(struct file *file, void *fh, v4l2_std_id *std) | 529 | int cx18_s_std(struct file *file, void *fh, v4l2_std_id *std) |
529 | { | 530 | { |
530 | struct cx18_open_id *id = fh; | 531 | struct cx18_open_id *id = fh; |
531 | struct cx18 *cx = id->cx; | 532 | struct cx18 *cx = id->cx; |
532 | int ret; | 533 | int ret; |
533 | 534 | ||
534 | ret = v4l2_prio_check(&cx->prio, &id->prio); | 535 | ret = v4l2_prio_check(&cx->prio, &id->prio); |
535 | if (ret) | 536 | if (ret) |
536 | return ret; | 537 | return ret; |
537 | 538 | ||
538 | if ((*std & V4L2_STD_ALL) == 0) | 539 | if ((*std & V4L2_STD_ALL) == 0) |
539 | return -EINVAL; | 540 | return -EINVAL; |
540 | 541 | ||
541 | if (*std == cx->std) | 542 | if (*std == cx->std) |
542 | return 0; | 543 | return 0; |
543 | 544 | ||
544 | if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) || | 545 | if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) || |
545 | atomic_read(&cx->ana_capturing) > 0) { | 546 | atomic_read(&cx->ana_capturing) > 0) { |
546 | /* Switching standard would turn off the radio or mess | 547 | /* Switching standard would turn off the radio or mess |
547 | with already running streams, prevent that by | 548 | with already running streams, prevent that by |
548 | returning EBUSY. */ | 549 | returning EBUSY. */ |
549 | return -EBUSY; | 550 | return -EBUSY; |
550 | } | 551 | } |
551 | 552 | ||
552 | cx->std = *std; | 553 | cx->std = *std; |
553 | cx->is_60hz = (*std & V4L2_STD_525_60) ? 1 : 0; | 554 | cx->is_60hz = (*std & V4L2_STD_525_60) ? 1 : 0; |
554 | cx->params.is_50hz = cx->is_50hz = !cx->is_60hz; | 555 | cx->params.is_50hz = cx->is_50hz = !cx->is_60hz; |
555 | cx->params.width = 720; | 556 | cx->params.width = 720; |
556 | cx->params.height = cx->is_50hz ? 576 : 480; | 557 | cx->params.height = cx->is_50hz ? 576 : 480; |
557 | cx->vbi.count = cx->is_50hz ? 18 : 12; | 558 | cx->vbi.count = cx->is_50hz ? 18 : 12; |
558 | cx->vbi.start[0] = cx->is_50hz ? 6 : 10; | 559 | cx->vbi.start[0] = cx->is_50hz ? 6 : 10; |
559 | cx->vbi.start[1] = cx->is_50hz ? 318 : 273; | 560 | cx->vbi.start[1] = cx->is_50hz ? 318 : 273; |
560 | cx->vbi.sliced_decoder_line_size = cx->is_60hz ? 272 : 284; | 561 | cx->vbi.sliced_decoder_line_size = cx->is_60hz ? 272 : 284; |
561 | CX18_DEBUG_INFO("Switching standard to %llx.\n", | 562 | CX18_DEBUG_INFO("Switching standard to %llx.\n", |
562 | (unsigned long long) cx->std); | 563 | (unsigned long long) cx->std); |
563 | 564 | ||
564 | /* Tuner */ | 565 | /* Tuner */ |
565 | cx18_call_i2c_clients(cx, VIDIOC_S_STD, &cx->std); | 566 | cx18_call_i2c_clients(cx, VIDIOC_S_STD, &cx->std); |
566 | return 0; | 567 | return 0; |
567 | } | 568 | } |
568 | 569 | ||
569 | static int cx18_s_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) | 570 | static int cx18_s_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) |
570 | { | 571 | { |
571 | struct cx18_open_id *id = fh; | 572 | struct cx18_open_id *id = fh; |
572 | struct cx18 *cx = id->cx; | 573 | struct cx18 *cx = id->cx; |
573 | int ret; | 574 | int ret; |
574 | 575 | ||
575 | ret = v4l2_prio_check(&cx->prio, &id->prio); | 576 | ret = v4l2_prio_check(&cx->prio, &id->prio); |
576 | if (ret) | 577 | if (ret) |
577 | return ret; | 578 | return ret; |
578 | 579 | ||
579 | if (vt->index != 0) | 580 | if (vt->index != 0) |
580 | return -EINVAL; | 581 | return -EINVAL; |
581 | 582 | ||
582 | /* Setting tuner can only set audio mode */ | 583 | /* Setting tuner can only set audio mode */ |
583 | cx18_call_i2c_clients(cx, VIDIOC_S_TUNER, vt); | 584 | cx18_call_i2c_clients(cx, VIDIOC_S_TUNER, vt); |
584 | 585 | ||
585 | return 0; | 586 | return 0; |
586 | } | 587 | } |
587 | 588 | ||
588 | static int cx18_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) | 589 | static int cx18_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) |
589 | { | 590 | { |
590 | struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; | 591 | struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; |
591 | 592 | ||
592 | if (vt->index != 0) | 593 | if (vt->index != 0) |
593 | return -EINVAL; | 594 | return -EINVAL; |
594 | 595 | ||
595 | cx18_call_i2c_clients(cx, VIDIOC_G_TUNER, vt); | 596 | cx18_call_i2c_clients(cx, VIDIOC_G_TUNER, vt); |
596 | 597 | ||
597 | if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) { | 598 | if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) { |
598 | strlcpy(vt->name, "cx18 Radio Tuner", sizeof(vt->name)); | 599 | strlcpy(vt->name, "cx18 Radio Tuner", sizeof(vt->name)); |
599 | vt->type = V4L2_TUNER_RADIO; | 600 | vt->type = V4L2_TUNER_RADIO; |
600 | } else { | 601 | } else { |
601 | strlcpy(vt->name, "cx18 TV Tuner", sizeof(vt->name)); | 602 | strlcpy(vt->name, "cx18 TV Tuner", sizeof(vt->name)); |
602 | vt->type = V4L2_TUNER_ANALOG_TV; | 603 | vt->type = V4L2_TUNER_ANALOG_TV; |
603 | } | 604 | } |
604 | 605 | ||
605 | return 0; | 606 | return 0; |
606 | } | 607 | } |
607 | 608 | ||
608 | static int cx18_g_sliced_vbi_cap(struct file *file, void *fh, | 609 | static int cx18_g_sliced_vbi_cap(struct file *file, void *fh, |
609 | struct v4l2_sliced_vbi_cap *cap) | 610 | struct v4l2_sliced_vbi_cap *cap) |
610 | { | 611 | { |
611 | return -EINVAL; | 612 | return -EINVAL; |
612 | } | 613 | } |
613 | 614 | ||
614 | static int cx18_g_enc_index(struct file *file, void *fh, | 615 | static int cx18_g_enc_index(struct file *file, void *fh, |
615 | struct v4l2_enc_idx *idx) | 616 | struct v4l2_enc_idx *idx) |
616 | { | 617 | { |
617 | return -EINVAL; | 618 | return -EINVAL; |
618 | } | 619 | } |
619 | 620 | ||
620 | static int cx18_encoder_cmd(struct file *file, void *fh, | 621 | static int cx18_encoder_cmd(struct file *file, void *fh, |
621 | struct v4l2_encoder_cmd *enc) | 622 | struct v4l2_encoder_cmd *enc) |
622 | { | 623 | { |
623 | struct cx18_open_id *id = fh; | 624 | struct cx18_open_id *id = fh; |
624 | struct cx18 *cx = id->cx; | 625 | struct cx18 *cx = id->cx; |
625 | u32 h; | 626 | u32 h; |
626 | 627 | ||
627 | switch (enc->cmd) { | 628 | switch (enc->cmd) { |
628 | case V4L2_ENC_CMD_START: | 629 | case V4L2_ENC_CMD_START: |
629 | CX18_DEBUG_IOCTL("V4L2_ENC_CMD_START\n"); | 630 | CX18_DEBUG_IOCTL("V4L2_ENC_CMD_START\n"); |
630 | enc->flags = 0; | 631 | enc->flags = 0; |
631 | return cx18_start_capture(id); | 632 | return cx18_start_capture(id); |
632 | 633 | ||
633 | case V4L2_ENC_CMD_STOP: | 634 | case V4L2_ENC_CMD_STOP: |
634 | CX18_DEBUG_IOCTL("V4L2_ENC_CMD_STOP\n"); | 635 | CX18_DEBUG_IOCTL("V4L2_ENC_CMD_STOP\n"); |
635 | enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END; | 636 | enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END; |
636 | cx18_stop_capture(id, | 637 | cx18_stop_capture(id, |
637 | enc->flags & V4L2_ENC_CMD_STOP_AT_GOP_END); | 638 | enc->flags & V4L2_ENC_CMD_STOP_AT_GOP_END); |
638 | break; | 639 | break; |
639 | 640 | ||
640 | case V4L2_ENC_CMD_PAUSE: | 641 | case V4L2_ENC_CMD_PAUSE: |
641 | CX18_DEBUG_IOCTL("V4L2_ENC_CMD_PAUSE\n"); | 642 | CX18_DEBUG_IOCTL("V4L2_ENC_CMD_PAUSE\n"); |
642 | enc->flags = 0; | 643 | enc->flags = 0; |
643 | if (!atomic_read(&cx->ana_capturing)) | 644 | if (!atomic_read(&cx->ana_capturing)) |
644 | return -EPERM; | 645 | return -EPERM; |
645 | if (test_and_set_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags)) | 646 | if (test_and_set_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags)) |
646 | return 0; | 647 | return 0; |
647 | h = cx18_find_handle(cx); | 648 | h = cx18_find_handle(cx); |
648 | if (h == CX18_INVALID_TASK_HANDLE) { | 649 | if (h == CX18_INVALID_TASK_HANDLE) { |
649 | CX18_ERR("Can't find valid task handle for " | 650 | CX18_ERR("Can't find valid task handle for " |
650 | "V4L2_ENC_CMD_PAUSE\n"); | 651 | "V4L2_ENC_CMD_PAUSE\n"); |
651 | return -EBADFD; | 652 | return -EBADFD; |
652 | } | 653 | } |
653 | cx18_mute(cx); | 654 | cx18_mute(cx); |
654 | cx18_vapi(cx, CX18_CPU_CAPTURE_PAUSE, 1, h); | 655 | cx18_vapi(cx, CX18_CPU_CAPTURE_PAUSE, 1, h); |
655 | break; | 656 | break; |
656 | 657 | ||
657 | case V4L2_ENC_CMD_RESUME: | 658 | case V4L2_ENC_CMD_RESUME: |
658 | CX18_DEBUG_IOCTL("V4L2_ENC_CMD_RESUME\n"); | 659 | CX18_DEBUG_IOCTL("V4L2_ENC_CMD_RESUME\n"); |
659 | enc->flags = 0; | 660 | enc->flags = 0; |
660 | if (!atomic_read(&cx->ana_capturing)) | 661 | if (!atomic_read(&cx->ana_capturing)) |
661 | return -EPERM; | 662 | return -EPERM; |
662 | if (!test_and_clear_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags)) | 663 | if (!test_and_clear_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags)) |
663 | return 0; | 664 | return 0; |
664 | h = cx18_find_handle(cx); | 665 | h = cx18_find_handle(cx); |
665 | if (h == CX18_INVALID_TASK_HANDLE) { | 666 | if (h == CX18_INVALID_TASK_HANDLE) { |
666 | CX18_ERR("Can't find valid task handle for " | 667 | CX18_ERR("Can't find valid task handle for " |
667 | "V4L2_ENC_CMD_RESUME\n"); | 668 | "V4L2_ENC_CMD_RESUME\n"); |
668 | return -EBADFD; | 669 | return -EBADFD; |
669 | } | 670 | } |
670 | cx18_vapi(cx, CX18_CPU_CAPTURE_RESUME, 1, h); | 671 | cx18_vapi(cx, CX18_CPU_CAPTURE_RESUME, 1, h); |
671 | cx18_unmute(cx); | 672 | cx18_unmute(cx); |
672 | break; | 673 | break; |
673 | 674 | ||
674 | default: | 675 | default: |
675 | CX18_DEBUG_IOCTL("Unknown cmd %d\n", enc->cmd); | 676 | CX18_DEBUG_IOCTL("Unknown cmd %d\n", enc->cmd); |
676 | return -EINVAL; | 677 | return -EINVAL; |
677 | } | 678 | } |
678 | return 0; | 679 | return 0; |
679 | } | 680 | } |
680 | 681 | ||
681 | static int cx18_try_encoder_cmd(struct file *file, void *fh, | 682 | static int cx18_try_encoder_cmd(struct file *file, void *fh, |
682 | struct v4l2_encoder_cmd *enc) | 683 | struct v4l2_encoder_cmd *enc) |
683 | { | 684 | { |
684 | struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; | 685 | struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; |
685 | 686 | ||
686 | switch (enc->cmd) { | 687 | switch (enc->cmd) { |
687 | case V4L2_ENC_CMD_START: | 688 | case V4L2_ENC_CMD_START: |
688 | CX18_DEBUG_IOCTL("V4L2_ENC_CMD_START\n"); | 689 | CX18_DEBUG_IOCTL("V4L2_ENC_CMD_START\n"); |
689 | enc->flags = 0; | 690 | enc->flags = 0; |
690 | break; | 691 | break; |
691 | 692 | ||
692 | case V4L2_ENC_CMD_STOP: | 693 | case V4L2_ENC_CMD_STOP: |
693 | CX18_DEBUG_IOCTL("V4L2_ENC_CMD_STOP\n"); | 694 | CX18_DEBUG_IOCTL("V4L2_ENC_CMD_STOP\n"); |
694 | enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END; | 695 | enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END; |
695 | break; | 696 | break; |
696 | 697 | ||
697 | case V4L2_ENC_CMD_PAUSE: | 698 | case V4L2_ENC_CMD_PAUSE: |
698 | CX18_DEBUG_IOCTL("V4L2_ENC_CMD_PAUSE\n"); | 699 | CX18_DEBUG_IOCTL("V4L2_ENC_CMD_PAUSE\n"); |
699 | enc->flags = 0; | 700 | enc->flags = 0; |
700 | break; | 701 | break; |
701 | 702 | ||
702 | case V4L2_ENC_CMD_RESUME: | 703 | case V4L2_ENC_CMD_RESUME: |
703 | CX18_DEBUG_IOCTL("V4L2_ENC_CMD_RESUME\n"); | 704 | CX18_DEBUG_IOCTL("V4L2_ENC_CMD_RESUME\n"); |
704 | enc->flags = 0; | 705 | enc->flags = 0; |
705 | break; | 706 | break; |
706 | 707 | ||
707 | default: | 708 | default: |
708 | CX18_DEBUG_IOCTL("Unknown cmd %d\n", enc->cmd); | 709 | CX18_DEBUG_IOCTL("Unknown cmd %d\n", enc->cmd); |
709 | return -EINVAL; | 710 | return -EINVAL; |
710 | } | 711 | } |
711 | return 0; | 712 | return 0; |
712 | } | 713 | } |
713 | 714 | ||
714 | static int cx18_log_status(struct file *file, void *fh) | 715 | static int cx18_log_status(struct file *file, void *fh) |
715 | { | 716 | { |
716 | struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; | 717 | struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; |
717 | struct v4l2_input vidin; | 718 | struct v4l2_input vidin; |
718 | struct v4l2_audio audin; | 719 | struct v4l2_audio audin; |
719 | int i; | 720 | int i; |
720 | 721 | ||
721 | CX18_INFO("================= START STATUS CARD #%d =================\n", cx->num); | 722 | CX18_INFO("================= START STATUS CARD #%d =================\n", cx->num); |
722 | if (cx->hw_flags & CX18_HW_TVEEPROM) { | 723 | if (cx->hw_flags & CX18_HW_TVEEPROM) { |
723 | struct tveeprom tv; | 724 | struct tveeprom tv; |
724 | 725 | ||
725 | cx18_read_eeprom(cx, &tv); | 726 | cx18_read_eeprom(cx, &tv); |
726 | } | 727 | } |
727 | cx18_call_i2c_clients(cx, VIDIOC_LOG_STATUS, NULL); | 728 | cx18_call_i2c_clients(cx, VIDIOC_LOG_STATUS, NULL); |
728 | cx18_get_input(cx, cx->active_input, &vidin); | 729 | cx18_get_input(cx, cx->active_input, &vidin); |
729 | cx18_get_audio_input(cx, cx->audio_input, &audin); | 730 | cx18_get_audio_input(cx, cx->audio_input, &audin); |
730 | CX18_INFO("Video Input: %s\n", vidin.name); | 731 | CX18_INFO("Video Input: %s\n", vidin.name); |
731 | CX18_INFO("Audio Input: %s\n", audin.name); | 732 | CX18_INFO("Audio Input: %s\n", audin.name); |
732 | mutex_lock(&cx->gpio_lock); | 733 | mutex_lock(&cx->gpio_lock); |
733 | CX18_INFO("GPIO: direction 0x%08x, value 0x%08x\n", | 734 | CX18_INFO("GPIO: direction 0x%08x, value 0x%08x\n", |
734 | cx->gpio_dir, cx->gpio_val); | 735 | cx->gpio_dir, cx->gpio_val); |
735 | mutex_unlock(&cx->gpio_lock); | 736 | mutex_unlock(&cx->gpio_lock); |
736 | CX18_INFO("Tuner: %s\n", | 737 | CX18_INFO("Tuner: %s\n", |
737 | test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) ? "Radio" : "TV"); | 738 | test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) ? "Radio" : "TV"); |
738 | cx2341x_log_status(&cx->params, cx->name); | 739 | cx2341x_log_status(&cx->params, cx->name); |
739 | CX18_INFO("Status flags: 0x%08lx\n", cx->i_flags); | 740 | CX18_INFO("Status flags: 0x%08lx\n", cx->i_flags); |
740 | for (i = 0; i < CX18_MAX_STREAMS; i++) { | 741 | for (i = 0; i < CX18_MAX_STREAMS; i++) { |
741 | struct cx18_stream *s = &cx->streams[i]; | 742 | struct cx18_stream *s = &cx->streams[i]; |
742 | 743 | ||
743 | if (s->v4l2dev == NULL || s->buffers == 0) | 744 | if (s->v4l2dev == NULL || s->buffers == 0) |
744 | continue; | 745 | continue; |
745 | CX18_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n", | 746 | CX18_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n", |
746 | s->name, s->s_flags, | 747 | s->name, s->s_flags, |
747 | (s->buffers - atomic_read(&s->q_free.buffers)) | 748 | (s->buffers - atomic_read(&s->q_free.buffers)) |
748 | * 100 / s->buffers, | 749 | * 100 / s->buffers, |
749 | (s->buffers * s->buf_size) / 1024, s->buffers); | 750 | (s->buffers * s->buf_size) / 1024, s->buffers); |
750 | } | 751 | } |
751 | CX18_INFO("Read MPEG/VBI: %lld/%lld bytes\n", | 752 | CX18_INFO("Read MPEG/VBI: %lld/%lld bytes\n", |
752 | (long long)cx->mpg_data_received, | 753 | (long long)cx->mpg_data_received, |
753 | (long long)cx->vbi_data_inserted); | 754 | (long long)cx->vbi_data_inserted); |
754 | CX18_INFO("================== END STATUS CARD #%d ==================\n", cx->num); | 755 | CX18_INFO("================== END STATUS CARD #%d ==================\n", cx->num); |
755 | return 0; | 756 | return 0; |
756 | } | 757 | } |
757 | 758 | ||
758 | static int cx18_default(struct file *file, void *fh, int cmd, void *arg) | 759 | static int cx18_default(struct file *file, void *fh, int cmd, void *arg) |
759 | { | 760 | { |
760 | struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; | 761 | struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; |
761 | 762 | ||
762 | switch (cmd) { | 763 | switch (cmd) { |
763 | case VIDIOC_INT_S_AUDIO_ROUTING: { | 764 | case VIDIOC_INT_S_AUDIO_ROUTING: { |
764 | struct v4l2_routing *route = arg; | 765 | struct v4l2_routing *route = arg; |
765 | 766 | ||
766 | CX18_DEBUG_IOCTL("VIDIOC_INT_S_AUDIO_ROUTING(%d, %d)\n", | 767 | CX18_DEBUG_IOCTL("VIDIOC_INT_S_AUDIO_ROUTING(%d, %d)\n", |
767 | route->input, route->output); | 768 | route->input, route->output); |
768 | cx18_audio_set_route(cx, route); | 769 | cx18_audio_set_route(cx, route); |
769 | break; | 770 | break; |
770 | } | 771 | } |
771 | 772 | ||
772 | case VIDIOC_INT_RESET: { | 773 | case VIDIOC_INT_RESET: { |
773 | u32 val = *(u32 *)arg; | 774 | u32 val = *(u32 *)arg; |
774 | 775 | ||
775 | if ((val == 0) || (val & 0x01)) | 776 | if ((val == 0) || (val & 0x01)) |
776 | cx18_reset_ir_gpio(&cx->i2c_algo_cb_data[0]); | 777 | cx18_reset_ir_gpio(&cx->i2c_algo_cb_data[0]); |
777 | break; | 778 | break; |
778 | } | 779 | } |
779 | 780 | ||
780 | default: | 781 | default: |
781 | return -EINVAL; | 782 | return -EINVAL; |
782 | } | 783 | } |
783 | return 0; | 784 | return 0; |
784 | } | 785 | } |
785 | 786 | ||
786 | int cx18_v4l2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, | 787 | int cx18_v4l2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, |
787 | unsigned long arg) | 788 | unsigned long arg) |
788 | { | 789 | { |
789 | struct video_device *vfd = video_devdata(filp); | 790 | struct video_device *vfd = video_devdata(filp); |
790 | struct cx18_open_id *id = (struct cx18_open_id *)filp->private_data; | 791 | struct cx18_open_id *id = (struct cx18_open_id *)filp->private_data; |
791 | struct cx18 *cx = id->cx; | 792 | struct cx18 *cx = id->cx; |
792 | int res; | 793 | int res; |
793 | 794 | ||
794 | mutex_lock(&cx->serialize_lock); | 795 | mutex_lock(&cx->serialize_lock); |
795 | 796 | ||
796 | if (cx18_debug & CX18_DBGFLG_IOCTL) | 797 | if (cx18_debug & CX18_DBGFLG_IOCTL) |
797 | vfd->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG; | 798 | vfd->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG; |
798 | res = video_ioctl2(inode, filp, cmd, arg); | 799 | res = video_ioctl2(inode, filp, cmd, arg); |
799 | vfd->debug = 0; | 800 | vfd->debug = 0; |
800 | mutex_unlock(&cx->serialize_lock); | 801 | mutex_unlock(&cx->serialize_lock); |
801 | return res; | 802 | return res; |
802 | } | 803 | } |
803 | 804 | ||
804 | static const struct v4l2_ioctl_ops cx18_ioctl_ops = { | 805 | static const struct v4l2_ioctl_ops cx18_ioctl_ops = { |
805 | .vidioc_querycap = cx18_querycap, | 806 | .vidioc_querycap = cx18_querycap, |
806 | .vidioc_g_priority = cx18_g_priority, | 807 | .vidioc_g_priority = cx18_g_priority, |
807 | .vidioc_s_priority = cx18_s_priority, | 808 | .vidioc_s_priority = cx18_s_priority, |
808 | .vidioc_s_audio = cx18_s_audio, | 809 | .vidioc_s_audio = cx18_s_audio, |
809 | .vidioc_g_audio = cx18_g_audio, | 810 | .vidioc_g_audio = cx18_g_audio, |
810 | .vidioc_enumaudio = cx18_enumaudio, | 811 | .vidioc_enumaudio = cx18_enumaudio, |
811 | .vidioc_enum_input = cx18_enum_input, | 812 | .vidioc_enum_input = cx18_enum_input, |
812 | .vidioc_cropcap = cx18_cropcap, | 813 | .vidioc_cropcap = cx18_cropcap, |
813 | .vidioc_s_crop = cx18_s_crop, | 814 | .vidioc_s_crop = cx18_s_crop, |
814 | .vidioc_g_crop = cx18_g_crop, | 815 | .vidioc_g_crop = cx18_g_crop, |
815 | .vidioc_g_input = cx18_g_input, | 816 | .vidioc_g_input = cx18_g_input, |
816 | .vidioc_s_input = cx18_s_input, | 817 | .vidioc_s_input = cx18_s_input, |
817 | .vidioc_g_frequency = cx18_g_frequency, | 818 | .vidioc_g_frequency = cx18_g_frequency, |
818 | .vidioc_s_frequency = cx18_s_frequency, | 819 | .vidioc_s_frequency = cx18_s_frequency, |
819 | .vidioc_s_tuner = cx18_s_tuner, | 820 | .vidioc_s_tuner = cx18_s_tuner, |
820 | .vidioc_g_tuner = cx18_g_tuner, | 821 | .vidioc_g_tuner = cx18_g_tuner, |
821 | .vidioc_g_enc_index = cx18_g_enc_index, | 822 | .vidioc_g_enc_index = cx18_g_enc_index, |
822 | .vidioc_g_std = cx18_g_std, | 823 | .vidioc_g_std = cx18_g_std, |
823 | .vidioc_s_std = cx18_s_std, | 824 | .vidioc_s_std = cx18_s_std, |
824 | .vidioc_log_status = cx18_log_status, | 825 | .vidioc_log_status = cx18_log_status, |
825 | .vidioc_enum_fmt_vid_cap = cx18_enum_fmt_vid_cap, | 826 | .vidioc_enum_fmt_vid_cap = cx18_enum_fmt_vid_cap, |
826 | .vidioc_encoder_cmd = cx18_encoder_cmd, | 827 | .vidioc_encoder_cmd = cx18_encoder_cmd, |
827 | .vidioc_try_encoder_cmd = cx18_try_encoder_cmd, | 828 | .vidioc_try_encoder_cmd = cx18_try_encoder_cmd, |
828 | .vidioc_g_fmt_vid_cap = cx18_g_fmt_vid_cap, | 829 | .vidioc_g_fmt_vid_cap = cx18_g_fmt_vid_cap, |
829 | .vidioc_g_fmt_vbi_cap = cx18_g_fmt_vbi_cap, | 830 | .vidioc_g_fmt_vbi_cap = cx18_g_fmt_vbi_cap, |
830 | .vidioc_g_fmt_sliced_vbi_cap = cx18_g_fmt_sliced_vbi_cap, | 831 | .vidioc_g_fmt_sliced_vbi_cap = cx18_g_fmt_sliced_vbi_cap, |
831 | .vidioc_s_fmt_vid_cap = cx18_s_fmt_vid_cap, | 832 | .vidioc_s_fmt_vid_cap = cx18_s_fmt_vid_cap, |
832 | .vidioc_s_fmt_vbi_cap = cx18_s_fmt_vbi_cap, | 833 | .vidioc_s_fmt_vbi_cap = cx18_s_fmt_vbi_cap, |
833 | .vidioc_s_fmt_sliced_vbi_cap = cx18_s_fmt_sliced_vbi_cap, | 834 | .vidioc_s_fmt_sliced_vbi_cap = cx18_s_fmt_sliced_vbi_cap, |
834 | .vidioc_try_fmt_vid_cap = cx18_try_fmt_vid_cap, | 835 | .vidioc_try_fmt_vid_cap = cx18_try_fmt_vid_cap, |
835 | .vidioc_try_fmt_vbi_cap = cx18_try_fmt_vbi_cap, | 836 | .vidioc_try_fmt_vbi_cap = cx18_try_fmt_vbi_cap, |
836 | .vidioc_try_fmt_sliced_vbi_cap = cx18_try_fmt_sliced_vbi_cap, | 837 | .vidioc_try_fmt_sliced_vbi_cap = cx18_try_fmt_sliced_vbi_cap, |
837 | .vidioc_g_sliced_vbi_cap = cx18_g_sliced_vbi_cap, | 838 | .vidioc_g_sliced_vbi_cap = cx18_g_sliced_vbi_cap, |
838 | .vidioc_g_chip_ident = cx18_g_chip_ident, | 839 | .vidioc_g_chip_ident = cx18_g_chip_ident, |
839 | #ifdef CONFIG_VIDEO_ADV_DEBUG | 840 | #ifdef CONFIG_VIDEO_ADV_DEBUG |
840 | .vidioc_g_register = cx18_g_register, | 841 | .vidioc_g_register = cx18_g_register, |
841 | .vidioc_s_register = cx18_s_register, | 842 | .vidioc_s_register = cx18_s_register, |
842 | #endif | 843 | #endif |
843 | .vidioc_default = cx18_default, | 844 | .vidioc_default = cx18_default, |
844 | .vidioc_queryctrl = cx18_queryctrl, | 845 | .vidioc_queryctrl = cx18_queryctrl, |
845 | .vidioc_querymenu = cx18_querymenu, | 846 | .vidioc_querymenu = cx18_querymenu, |
846 | .vidioc_g_ext_ctrls = cx18_g_ext_ctrls, | 847 | .vidioc_g_ext_ctrls = cx18_g_ext_ctrls, |
847 | .vidioc_s_ext_ctrls = cx18_s_ext_ctrls, | 848 | .vidioc_s_ext_ctrls = cx18_s_ext_ctrls, |
848 | .vidioc_try_ext_ctrls = cx18_try_ext_ctrls, | 849 | .vidioc_try_ext_ctrls = cx18_try_ext_ctrls, |
849 | }; | 850 | }; |
850 | 851 | ||
851 | void cx18_set_funcs(struct video_device *vdev) | 852 | void cx18_set_funcs(struct video_device *vdev) |
852 | { | 853 | { |
853 | vdev->ioctl_ops = &cx18_ioctl_ops; | 854 | vdev->ioctl_ops = &cx18_ioctl_ops; |
854 | } | 855 | } |
855 | 856 |
drivers/media/video/cx18/cx18-irq.c
1 | /* | 1 | /* |
2 | * cx18 interrupt handling | 2 | * cx18 interrupt handling |
3 | * | 3 | * |
4 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | 4 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> |
5 | * | 5 | * |
6 | * This program is free software; you can redistribute it and/or modify | 6 | * This program is free software; you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License as published by | 7 | * it under the terms of the GNU General Public License as published by |
8 | * the Free Software Foundation; either version 2 of the License, or | 8 | * the Free Software Foundation; either version 2 of the License, or |
9 | * (at your option) any later version. | 9 | * (at your option) any later version. |
10 | * | 10 | * |
11 | * This program is distributed in the hope that it will be useful, | 11 | * This program is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | * GNU General Public License for more details. | 14 | * GNU General Public License for more details. |
15 | * | 15 | * |
16 | * You should have received a copy of the GNU General Public License | 16 | * You should have received a copy of the GNU General Public License |
17 | * along with this program; if not, write to the Free Software | 17 | * along with this program; if not, write to the Free Software |
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
19 | * 02111-1307 USA | 19 | * 02111-1307 USA |
20 | */ | 20 | */ |
21 | 21 | ||
22 | #include "cx18-driver.h" | 22 | #include "cx18-driver.h" |
23 | #include "cx18-io.h" | ||
23 | #include "cx18-firmware.h" | 24 | #include "cx18-firmware.h" |
24 | #include "cx18-fileops.h" | 25 | #include "cx18-fileops.h" |
25 | #include "cx18-queue.h" | 26 | #include "cx18-queue.h" |
26 | #include "cx18-irq.h" | 27 | #include "cx18-irq.h" |
27 | #include "cx18-ioctl.h" | 28 | #include "cx18-ioctl.h" |
28 | #include "cx18-mailbox.h" | 29 | #include "cx18-mailbox.h" |
29 | #include "cx18-vbi.h" | 30 | #include "cx18-vbi.h" |
30 | #include "cx18-scb.h" | 31 | #include "cx18-scb.h" |
31 | 32 | ||
32 | #define DMA_MAGIC_COOKIE 0x000001fe | 33 | #define DMA_MAGIC_COOKIE 0x000001fe |
33 | 34 | ||
34 | static void epu_dma_done(struct cx18 *cx, struct cx18_mailbox *mb) | 35 | static void epu_dma_done(struct cx18 *cx, struct cx18_mailbox *mb) |
35 | { | 36 | { |
36 | u32 handle = mb->args[0]; | 37 | u32 handle = mb->args[0]; |
37 | struct cx18_stream *s = NULL; | 38 | struct cx18_stream *s = NULL; |
38 | struct cx18_buffer *buf; | 39 | struct cx18_buffer *buf; |
39 | u32 off; | 40 | u32 off; |
40 | int i; | 41 | int i; |
41 | int id; | 42 | int id; |
42 | 43 | ||
43 | for (i = 0; i < CX18_MAX_STREAMS; i++) { | 44 | for (i = 0; i < CX18_MAX_STREAMS; i++) { |
44 | s = &cx->streams[i]; | 45 | s = &cx->streams[i]; |
45 | if ((handle == s->handle) && (s->dvb.enabled)) | 46 | if ((handle == s->handle) && (s->dvb.enabled)) |
46 | break; | 47 | break; |
47 | if (s->v4l2dev && handle == s->handle) | 48 | if (s->v4l2dev && handle == s->handle) |
48 | break; | 49 | break; |
49 | } | 50 | } |
50 | if (i == CX18_MAX_STREAMS) { | 51 | if (i == CX18_MAX_STREAMS) { |
51 | CX18_WARN("DMA done for unknown handle %d for stream %s\n", | 52 | CX18_WARN("DMA done for unknown handle %d for stream %s\n", |
52 | handle, s->name); | 53 | handle, s->name); |
53 | mb->error = CXERR_NOT_OPEN; | 54 | mb->error = CXERR_NOT_OPEN; |
54 | mb->cmd = 0; | 55 | mb->cmd = 0; |
55 | cx18_mb_ack(cx, mb); | 56 | cx18_mb_ack(cx, mb); |
56 | return; | 57 | return; |
57 | } | 58 | } |
58 | 59 | ||
59 | off = mb->args[1]; | 60 | off = mb->args[1]; |
60 | if (mb->args[2] != 1) | 61 | if (mb->args[2] != 1) |
61 | CX18_WARN("Ack struct = %d for %s\n", | 62 | CX18_WARN("Ack struct = %d for %s\n", |
62 | mb->args[2], s->name); | 63 | mb->args[2], s->name); |
63 | id = read_enc(off); | 64 | id = cx18_read_enc(cx, off); |
64 | buf = cx18_queue_get_buf_irq(s, id, read_enc(off + 4)); | 65 | buf = cx18_queue_get_buf_irq(s, id, cx18_read_enc(cx, off + 4)); |
65 | CX18_DEBUG_HI_DMA("DMA DONE for %s (buffer %d)\n", s->name, id); | 66 | CX18_DEBUG_HI_DMA("DMA DONE for %s (buffer %d)\n", s->name, id); |
66 | if (buf) { | 67 | if (buf) { |
67 | cx18_buf_sync_for_cpu(s, buf); | 68 | cx18_buf_sync_for_cpu(s, buf); |
68 | if (s->type == CX18_ENC_STREAM_TYPE_TS && s->dvb.enabled) { | 69 | if (s->type == CX18_ENC_STREAM_TYPE_TS && s->dvb.enabled) { |
69 | /* process the buffer here */ | 70 | /* process the buffer here */ |
70 | CX18_DEBUG_HI_DMA("TS recv and sent bytesused=%d\n", | 71 | CX18_DEBUG_HI_DMA("TS recv and sent bytesused=%d\n", |
71 | buf->bytesused); | 72 | buf->bytesused); |
72 | 73 | ||
73 | dvb_dmx_swfilter(&s->dvb.demux, buf->buf, | 74 | dvb_dmx_swfilter(&s->dvb.demux, buf->buf, |
74 | buf->bytesused); | 75 | buf->bytesused); |
75 | 76 | ||
76 | cx18_buf_sync_for_device(s, buf); | 77 | cx18_buf_sync_for_device(s, buf); |
77 | cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle, | 78 | cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle, |
78 | (void __iomem *)&cx->scb->cpu_mdl[buf->id] - cx->enc_mem, | 79 | (void __iomem *)&cx->scb->cpu_mdl[buf->id] - cx->enc_mem, |
79 | 1, buf->id, s->buf_size); | 80 | 1, buf->id, s->buf_size); |
80 | } else | 81 | } else |
81 | set_bit(CX18_F_B_NEED_BUF_SWAP, &buf->b_flags); | 82 | set_bit(CX18_F_B_NEED_BUF_SWAP, &buf->b_flags); |
82 | } else { | 83 | } else { |
83 | CX18_WARN("Could not find buf %d for stream %s\n", | 84 | CX18_WARN("Could not find buf %d for stream %s\n", |
84 | read_enc(off), s->name); | 85 | cx18_read_enc(cx, off), s->name); |
85 | } | 86 | } |
86 | mb->error = 0; | 87 | mb->error = 0; |
87 | mb->cmd = 0; | 88 | mb->cmd = 0; |
88 | cx18_mb_ack(cx, mb); | 89 | cx18_mb_ack(cx, mb); |
89 | wake_up(&cx->dma_waitq); | 90 | wake_up(&cx->dma_waitq); |
90 | if (s->id != -1) | 91 | if (s->id != -1) |
91 | wake_up(&s->waitq); | 92 | wake_up(&s->waitq); |
92 | } | 93 | } |
93 | 94 | ||
94 | static void epu_debug(struct cx18 *cx, struct cx18_mailbox *mb) | 95 | static void epu_debug(struct cx18 *cx, struct cx18_mailbox *mb) |
95 | { | 96 | { |
96 | char str[256] = { 0 }; | 97 | char str[256] = { 0 }; |
97 | char *p; | 98 | char *p; |
98 | 99 | ||
99 | if (mb->args[1]) { | 100 | if (mb->args[1]) { |
100 | setup_page(mb->args[1]); | 101 | cx18_setup_page(cx, mb->args[1]); |
101 | memcpy_fromio(str, cx->enc_mem + mb->args[1], 252); | 102 | cx18_memcpy_fromio(cx, str, cx->enc_mem + mb->args[1], 252); |
102 | str[252] = 0; | 103 | str[252] = 0; |
103 | } | 104 | } |
104 | cx18_mb_ack(cx, mb); | 105 | cx18_mb_ack(cx, mb); |
105 | CX18_DEBUG_INFO("%x %s\n", mb->args[0], str); | 106 | CX18_DEBUG_INFO("%x %s\n", mb->args[0], str); |
106 | p = strchr(str, '.'); | 107 | p = strchr(str, '.'); |
107 | if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags) && p && p > str) | 108 | if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags) && p && p > str) |
108 | CX18_INFO("FW version: %s\n", p - 1); | 109 | CX18_INFO("FW version: %s\n", p - 1); |
109 | } | 110 | } |
110 | 111 | ||
111 | static void hpu_cmd(struct cx18 *cx, u32 sw1) | 112 | static void hpu_cmd(struct cx18 *cx, u32 sw1) |
112 | { | 113 | { |
113 | struct cx18_mailbox mb; | 114 | struct cx18_mailbox mb; |
114 | 115 | ||
115 | if (sw1 & IRQ_CPU_TO_EPU) { | 116 | if (sw1 & IRQ_CPU_TO_EPU) { |
116 | memcpy_fromio(&mb, &cx->scb->cpu2epu_mb, sizeof(mb)); | 117 | cx18_memcpy_fromio(cx, &mb, &cx->scb->cpu2epu_mb, sizeof(mb)); |
117 | mb.error = 0; | 118 | mb.error = 0; |
118 | 119 | ||
119 | switch (mb.cmd) { | 120 | switch (mb.cmd) { |
120 | case CX18_EPU_DMA_DONE: | 121 | case CX18_EPU_DMA_DONE: |
121 | epu_dma_done(cx, &mb); | 122 | epu_dma_done(cx, &mb); |
122 | break; | 123 | break; |
123 | case CX18_EPU_DEBUG: | 124 | case CX18_EPU_DEBUG: |
124 | epu_debug(cx, &mb); | 125 | epu_debug(cx, &mb); |
125 | break; | 126 | break; |
126 | default: | 127 | default: |
127 | CX18_WARN("Unexpected mailbox command %08x\n", mb.cmd); | 128 | CX18_WARN("Unexpected mailbox command %08x\n", mb.cmd); |
128 | break; | 129 | break; |
129 | } | 130 | } |
130 | } | 131 | } |
131 | if (sw1 & (IRQ_APU_TO_EPU | IRQ_HPU_TO_EPU)) | 132 | if (sw1 & (IRQ_APU_TO_EPU | IRQ_HPU_TO_EPU)) |
132 | CX18_WARN("Unexpected interrupt %08x\n", sw1); | 133 | CX18_WARN("Unexpected interrupt %08x\n", sw1); |
133 | } | 134 | } |
134 | 135 | ||
135 | irqreturn_t cx18_irq_handler(int irq, void *dev_id) | 136 | irqreturn_t cx18_irq_handler(int irq, void *dev_id) |
136 | { | 137 | { |
137 | struct cx18 *cx = (struct cx18 *)dev_id; | 138 | struct cx18 *cx = (struct cx18 *)dev_id; |
138 | u32 sw1, sw1_mask; | 139 | u32 sw1, sw1_mask; |
139 | u32 sw2, sw2_mask; | 140 | u32 sw2, sw2_mask; |
140 | u32 hw2, hw2_mask; | 141 | u32 hw2, hw2_mask; |
141 | 142 | ||
142 | spin_lock(&cx->dma_reg_lock); | 143 | spin_lock(&cx->dma_reg_lock); |
143 | 144 | ||
144 | hw2_mask = read_reg(HW2_INT_MASK5_PCI); | 145 | hw2_mask = cx18_read_reg(cx, HW2_INT_MASK5_PCI); |
145 | hw2 = read_reg(HW2_INT_CLR_STATUS) & hw2_mask; | 146 | hw2 = cx18_read_reg(cx, HW2_INT_CLR_STATUS) & hw2_mask; |
146 | sw2_mask = read_reg(SW2_INT_ENABLE_PCI) | IRQ_EPU_TO_HPU_ACK; | 147 | sw2_mask = cx18_read_reg(cx, SW2_INT_ENABLE_PCI) | IRQ_EPU_TO_HPU_ACK; |
147 | sw2 = read_reg(SW2_INT_STATUS) & sw2_mask; | 148 | sw2 = cx18_read_reg(cx, SW2_INT_STATUS) & sw2_mask; |
148 | sw1_mask = read_reg(SW1_INT_ENABLE_PCI) | IRQ_EPU_TO_HPU; | 149 | sw1_mask = cx18_read_reg(cx, SW1_INT_ENABLE_PCI) | IRQ_EPU_TO_HPU; |
149 | sw1 = read_reg(SW1_INT_STATUS) & sw1_mask; | 150 | sw1 = cx18_read_reg(cx, SW1_INT_STATUS) & sw1_mask; |
150 | 151 | ||
151 | write_reg(sw2&sw2_mask, SW2_INT_STATUS); | 152 | cx18_write_reg(cx, sw2&sw2_mask, SW2_INT_STATUS); |
152 | write_reg(sw1&sw1_mask, SW1_INT_STATUS); | 153 | cx18_write_reg(cx, sw1&sw1_mask, SW1_INT_STATUS); |
153 | write_reg(hw2&hw2_mask, HW2_INT_CLR_STATUS); | 154 | cx18_write_reg(cx, hw2&hw2_mask, HW2_INT_CLR_STATUS); |
154 | 155 | ||
155 | if (sw1 || sw2 || hw2) | 156 | if (sw1 || sw2 || hw2) |
156 | CX18_DEBUG_HI_IRQ("SW1: %x SW2: %x HW2: %x\n", sw1, sw2, hw2); | 157 | CX18_DEBUG_HI_IRQ("SW1: %x SW2: %x HW2: %x\n", sw1, sw2, hw2); |
157 | 158 | ||
158 | /* To do: interrupt-based I2C handling | 159 | /* To do: interrupt-based I2C handling |
159 | if (hw2 & 0x00c00000) { | 160 | if (hw2 & 0x00c00000) { |
160 | } | 161 | } |
161 | */ | 162 | */ |
162 | 163 | ||
163 | if (sw2) { | 164 | if (sw2) { |
164 | if (sw2 & (readl(&cx->scb->cpu2hpu_irq_ack) | | 165 | if (sw2 & (cx18_readl(cx, &cx->scb->cpu2hpu_irq_ack) | |
165 | readl(&cx->scb->cpu2epu_irq_ack))) | 166 | cx18_readl(cx, &cx->scb->cpu2epu_irq_ack))) |
166 | wake_up(&cx->mb_cpu_waitq); | 167 | wake_up(&cx->mb_cpu_waitq); |
167 | if (sw2 & (readl(&cx->scb->apu2hpu_irq_ack) | | 168 | if (sw2 & (cx18_readl(cx, &cx->scb->apu2hpu_irq_ack) | |
168 | readl(&cx->scb->apu2epu_irq_ack))) | 169 | cx18_readl(cx, &cx->scb->apu2epu_irq_ack))) |
169 | wake_up(&cx->mb_apu_waitq); | 170 | wake_up(&cx->mb_apu_waitq); |
170 | if (sw2 & readl(&cx->scb->epu2hpu_irq_ack)) | 171 | if (sw2 & cx18_readl(cx, &cx->scb->epu2hpu_irq_ack)) |
171 | wake_up(&cx->mb_epu_waitq); | 172 | wake_up(&cx->mb_epu_waitq); |
172 | if (sw2 & readl(&cx->scb->hpu2epu_irq_ack)) | 173 | if (sw2 & cx18_readl(cx, &cx->scb->hpu2epu_irq_ack)) |
173 | wake_up(&cx->mb_hpu_waitq); | 174 | wake_up(&cx->mb_hpu_waitq); |
174 | } | 175 | } |
175 | 176 | ||
176 | if (sw1) | 177 | if (sw1) |
177 | hpu_cmd(cx, sw1); | 178 | hpu_cmd(cx, sw1); |
178 | spin_unlock(&cx->dma_reg_lock); | 179 | spin_unlock(&cx->dma_reg_lock); |
179 | 180 | ||
180 | return (hw2 | sw1 | sw2) ? IRQ_HANDLED : IRQ_NONE; | 181 | return (hw2 | sw1 | sw2) ? IRQ_HANDLED : IRQ_NONE; |
181 | } | 182 | } |
182 | 183 |
drivers/media/video/cx18/cx18-mailbox.c
1 | /* | 1 | /* |
2 | * cx18 mailbox functions | 2 | * cx18 mailbox functions |
3 | * | 3 | * |
4 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | 4 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> |
5 | * | 5 | * |
6 | * This program is free software; you can redistribute it and/or modify | 6 | * This program is free software; you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License as published by | 7 | * it under the terms of the GNU General Public License as published by |
8 | * the Free Software Foundation; either version 2 of the License, or | 8 | * the Free Software Foundation; either version 2 of the License, or |
9 | * (at your option) any later version. | 9 | * (at your option) any later version. |
10 | * | 10 | * |
11 | * This program is distributed in the hope that it will be useful, | 11 | * This program is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | * GNU General Public License for more details. | 14 | * GNU General Public License for more details. |
15 | * | 15 | * |
16 | * You should have received a copy of the GNU General Public License | 16 | * You should have received a copy of the GNU General Public License |
17 | * along with this program; if not, write to the Free Software | 17 | * along with this program; if not, write to the Free Software |
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
19 | * 02111-1307 USA | 19 | * 02111-1307 USA |
20 | */ | 20 | */ |
21 | 21 | ||
22 | #include <stdarg.h> | 22 | #include <stdarg.h> |
23 | 23 | ||
24 | #include "cx18-driver.h" | 24 | #include "cx18-driver.h" |
25 | #include "cx18-io.h" | ||
25 | #include "cx18-scb.h" | 26 | #include "cx18-scb.h" |
26 | #include "cx18-irq.h" | 27 | #include "cx18-irq.h" |
27 | #include "cx18-mailbox.h" | 28 | #include "cx18-mailbox.h" |
28 | 29 | ||
29 | #define API_FAST (1 << 2) /* Short timeout */ | 30 | #define API_FAST (1 << 2) /* Short timeout */ |
30 | #define API_SLOW (1 << 3) /* Additional 300ms timeout */ | 31 | #define API_SLOW (1 << 3) /* Additional 300ms timeout */ |
31 | 32 | ||
32 | #define APU 0 | 33 | #define APU 0 |
33 | #define CPU 1 | 34 | #define CPU 1 |
34 | #define EPU 2 | 35 | #define EPU 2 |
35 | #define HPU 3 | 36 | #define HPU 3 |
36 | 37 | ||
37 | struct cx18_api_info { | 38 | struct cx18_api_info { |
38 | u32 cmd; | 39 | u32 cmd; |
39 | u8 flags; /* Flags, see above */ | 40 | u8 flags; /* Flags, see above */ |
40 | u8 rpu; /* Processing unit */ | 41 | u8 rpu; /* Processing unit */ |
41 | const char *name; /* The name of the command */ | 42 | const char *name; /* The name of the command */ |
42 | }; | 43 | }; |
43 | 44 | ||
44 | #define API_ENTRY(rpu, x, f) { (x), (f), (rpu), #x } | 45 | #define API_ENTRY(rpu, x, f) { (x), (f), (rpu), #x } |
45 | 46 | ||
46 | static const struct cx18_api_info api_info[] = { | 47 | static const struct cx18_api_info api_info[] = { |
47 | /* MPEG encoder API */ | 48 | /* MPEG encoder API */ |
48 | API_ENTRY(CPU, CX18_CPU_SET_CHANNEL_TYPE, 0), | 49 | API_ENTRY(CPU, CX18_CPU_SET_CHANNEL_TYPE, 0), |
49 | API_ENTRY(CPU, CX18_EPU_DEBUG, 0), | 50 | API_ENTRY(CPU, CX18_EPU_DEBUG, 0), |
50 | API_ENTRY(CPU, CX18_CREATE_TASK, 0), | 51 | API_ENTRY(CPU, CX18_CREATE_TASK, 0), |
51 | API_ENTRY(CPU, CX18_DESTROY_TASK, 0), | 52 | API_ENTRY(CPU, CX18_DESTROY_TASK, 0), |
52 | API_ENTRY(CPU, CX18_CPU_CAPTURE_START, API_SLOW), | 53 | API_ENTRY(CPU, CX18_CPU_CAPTURE_START, API_SLOW), |
53 | API_ENTRY(CPU, CX18_CPU_CAPTURE_STOP, API_SLOW), | 54 | API_ENTRY(CPU, CX18_CPU_CAPTURE_STOP, API_SLOW), |
54 | API_ENTRY(CPU, CX18_CPU_CAPTURE_PAUSE, 0), | 55 | API_ENTRY(CPU, CX18_CPU_CAPTURE_PAUSE, 0), |
55 | API_ENTRY(CPU, CX18_CPU_CAPTURE_RESUME, 0), | 56 | API_ENTRY(CPU, CX18_CPU_CAPTURE_RESUME, 0), |
56 | API_ENTRY(CPU, CX18_CPU_SET_CHANNEL_TYPE, 0), | 57 | API_ENTRY(CPU, CX18_CPU_SET_CHANNEL_TYPE, 0), |
57 | API_ENTRY(CPU, CX18_CPU_SET_STREAM_OUTPUT_TYPE, 0), | 58 | API_ENTRY(CPU, CX18_CPU_SET_STREAM_OUTPUT_TYPE, 0), |
58 | API_ENTRY(CPU, CX18_CPU_SET_VIDEO_IN, 0), | 59 | API_ENTRY(CPU, CX18_CPU_SET_VIDEO_IN, 0), |
59 | API_ENTRY(CPU, CX18_CPU_SET_VIDEO_RATE, 0), | 60 | API_ENTRY(CPU, CX18_CPU_SET_VIDEO_RATE, 0), |
60 | API_ENTRY(CPU, CX18_CPU_SET_VIDEO_RESOLUTION, 0), | 61 | API_ENTRY(CPU, CX18_CPU_SET_VIDEO_RESOLUTION, 0), |
61 | API_ENTRY(CPU, CX18_CPU_SET_FILTER_PARAM, 0), | 62 | API_ENTRY(CPU, CX18_CPU_SET_FILTER_PARAM, 0), |
62 | API_ENTRY(CPU, CX18_CPU_SET_SPATIAL_FILTER_TYPE, 0), | 63 | API_ENTRY(CPU, CX18_CPU_SET_SPATIAL_FILTER_TYPE, 0), |
63 | API_ENTRY(CPU, CX18_CPU_SET_MEDIAN_CORING, 0), | 64 | API_ENTRY(CPU, CX18_CPU_SET_MEDIAN_CORING, 0), |
64 | API_ENTRY(CPU, CX18_CPU_SET_INDEXTABLE, 0), | 65 | API_ENTRY(CPU, CX18_CPU_SET_INDEXTABLE, 0), |
65 | API_ENTRY(CPU, CX18_CPU_SET_AUDIO_PARAMETERS, 0), | 66 | API_ENTRY(CPU, CX18_CPU_SET_AUDIO_PARAMETERS, 0), |
66 | API_ENTRY(CPU, CX18_CPU_SET_VIDEO_MUTE, 0), | 67 | API_ENTRY(CPU, CX18_CPU_SET_VIDEO_MUTE, 0), |
67 | API_ENTRY(CPU, CX18_CPU_SET_AUDIO_MUTE, 0), | 68 | API_ENTRY(CPU, CX18_CPU_SET_AUDIO_MUTE, 0), |
68 | API_ENTRY(CPU, CX18_CPU_SET_MISC_PARAMETERS, 0), | 69 | API_ENTRY(CPU, CX18_CPU_SET_MISC_PARAMETERS, 0), |
69 | API_ENTRY(CPU, CX18_CPU_SET_RAW_VBI_PARAM, API_SLOW), | 70 | API_ENTRY(CPU, CX18_CPU_SET_RAW_VBI_PARAM, API_SLOW), |
70 | API_ENTRY(CPU, CX18_CPU_SET_CAPTURE_LINE_NO, 0), | 71 | API_ENTRY(CPU, CX18_CPU_SET_CAPTURE_LINE_NO, 0), |
71 | API_ENTRY(CPU, CX18_CPU_SET_COPYRIGHT, 0), | 72 | API_ENTRY(CPU, CX18_CPU_SET_COPYRIGHT, 0), |
72 | API_ENTRY(CPU, CX18_CPU_SET_AUDIO_PID, 0), | 73 | API_ENTRY(CPU, CX18_CPU_SET_AUDIO_PID, 0), |
73 | API_ENTRY(CPU, CX18_CPU_SET_VIDEO_PID, 0), | 74 | API_ENTRY(CPU, CX18_CPU_SET_VIDEO_PID, 0), |
74 | API_ENTRY(CPU, CX18_CPU_SET_VER_CROP_LINE, 0), | 75 | API_ENTRY(CPU, CX18_CPU_SET_VER_CROP_LINE, 0), |
75 | API_ENTRY(CPU, CX18_CPU_SET_GOP_STRUCTURE, 0), | 76 | API_ENTRY(CPU, CX18_CPU_SET_GOP_STRUCTURE, 0), |
76 | API_ENTRY(CPU, CX18_CPU_SET_SCENE_CHANGE_DETECTION, 0), | 77 | API_ENTRY(CPU, CX18_CPU_SET_SCENE_CHANGE_DETECTION, 0), |
77 | API_ENTRY(CPU, CX18_CPU_SET_ASPECT_RATIO, 0), | 78 | API_ENTRY(CPU, CX18_CPU_SET_ASPECT_RATIO, 0), |
78 | API_ENTRY(CPU, CX18_CPU_SET_SKIP_INPUT_FRAME, 0), | 79 | API_ENTRY(CPU, CX18_CPU_SET_SKIP_INPUT_FRAME, 0), |
79 | API_ENTRY(CPU, CX18_CPU_SET_SLICED_VBI_PARAM, 0), | 80 | API_ENTRY(CPU, CX18_CPU_SET_SLICED_VBI_PARAM, 0), |
80 | API_ENTRY(CPU, CX18_CPU_SET_USERDATA_PLACE_HOLDER, 0), | 81 | API_ENTRY(CPU, CX18_CPU_SET_USERDATA_PLACE_HOLDER, 0), |
81 | API_ENTRY(CPU, CX18_CPU_GET_ENC_PTS, 0), | 82 | API_ENTRY(CPU, CX18_CPU_GET_ENC_PTS, 0), |
82 | API_ENTRY(CPU, CX18_CPU_DE_SET_MDL_ACK, 0), | 83 | API_ENTRY(CPU, CX18_CPU_DE_SET_MDL_ACK, 0), |
83 | API_ENTRY(CPU, CX18_CPU_DE_SET_MDL, API_FAST), | 84 | API_ENTRY(CPU, CX18_CPU_DE_SET_MDL, API_FAST), |
84 | API_ENTRY(CPU, CX18_APU_RESETAI, API_FAST), | 85 | API_ENTRY(CPU, CX18_APU_RESETAI, API_FAST), |
85 | API_ENTRY(CPU, CX18_CPU_DE_RELEASE_MDL, 0), | 86 | API_ENTRY(CPU, CX18_CPU_DE_RELEASE_MDL, 0), |
86 | API_ENTRY(0, 0, 0), | 87 | API_ENTRY(0, 0, 0), |
87 | }; | 88 | }; |
88 | 89 | ||
89 | static const struct cx18_api_info *find_api_info(u32 cmd) | 90 | static const struct cx18_api_info *find_api_info(u32 cmd) |
90 | { | 91 | { |
91 | int i; | 92 | int i; |
92 | 93 | ||
93 | for (i = 0; api_info[i].cmd; i++) | 94 | for (i = 0; api_info[i].cmd; i++) |
94 | if (api_info[i].cmd == cmd) | 95 | if (api_info[i].cmd == cmd) |
95 | return &api_info[i]; | 96 | return &api_info[i]; |
96 | return NULL; | 97 | return NULL; |
97 | } | 98 | } |
98 | 99 | ||
99 | static struct cx18_mailbox __iomem *cx18_mb_is_complete(struct cx18 *cx, int rpu, | 100 | static struct cx18_mailbox __iomem *cx18_mb_is_complete(struct cx18 *cx, int rpu, |
100 | u32 *state, u32 *irq, u32 *req) | 101 | u32 *state, u32 *irq, u32 *req) |
101 | { | 102 | { |
102 | struct cx18_mailbox __iomem *mb = NULL; | 103 | struct cx18_mailbox __iomem *mb = NULL; |
103 | int wait_count = 0; | 104 | int wait_count = 0; |
104 | u32 ack; | 105 | u32 ack; |
105 | 106 | ||
106 | switch (rpu) { | 107 | switch (rpu) { |
107 | case APU: | 108 | case APU: |
108 | mb = &cx->scb->epu2apu_mb; | 109 | mb = &cx->scb->epu2apu_mb; |
109 | *state = readl(&cx->scb->apu_state); | 110 | *state = cx18_readl(cx, &cx->scb->apu_state); |
110 | *irq = readl(&cx->scb->epu2apu_irq); | 111 | *irq = cx18_readl(cx, &cx->scb->epu2apu_irq); |
111 | break; | 112 | break; |
112 | 113 | ||
113 | case CPU: | 114 | case CPU: |
114 | mb = &cx->scb->epu2cpu_mb; | 115 | mb = &cx->scb->epu2cpu_mb; |
115 | *state = readl(&cx->scb->cpu_state); | 116 | *state = cx18_readl(cx, &cx->scb->cpu_state); |
116 | *irq = readl(&cx->scb->epu2cpu_irq); | 117 | *irq = cx18_readl(cx, &cx->scb->epu2cpu_irq); |
117 | break; | 118 | break; |
118 | 119 | ||
119 | case HPU: | 120 | case HPU: |
120 | mb = &cx->scb->epu2hpu_mb; | 121 | mb = &cx->scb->epu2hpu_mb; |
121 | *state = readl(&cx->scb->hpu_state); | 122 | *state = cx18_readl(cx, &cx->scb->hpu_state); |
122 | *irq = readl(&cx->scb->epu2hpu_irq); | 123 | *irq = cx18_readl(cx, &cx->scb->epu2hpu_irq); |
123 | break; | 124 | break; |
124 | } | 125 | } |
125 | 126 | ||
126 | if (mb == NULL) | 127 | if (mb == NULL) |
127 | return mb; | 128 | return mb; |
128 | 129 | ||
129 | do { | 130 | do { |
130 | *req = readl(&mb->request); | 131 | *req = cx18_readl(cx, &mb->request); |
131 | ack = readl(&mb->ack); | 132 | ack = cx18_readl(cx, &mb->ack); |
132 | wait_count++; | 133 | wait_count++; |
133 | } while (*req != ack && wait_count < 600); | 134 | } while (*req != ack && wait_count < 600); |
134 | 135 | ||
135 | if (*req == ack) { | 136 | if (*req == ack) { |
136 | (*req)++; | 137 | (*req)++; |
137 | if (*req == 0 || *req == 0xffffffff) | 138 | if (*req == 0 || *req == 0xffffffff) |
138 | *req = 1; | 139 | *req = 1; |
139 | return mb; | 140 | return mb; |
140 | } | 141 | } |
141 | return NULL; | 142 | return NULL; |
142 | } | 143 | } |
143 | 144 | ||
144 | long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb) | 145 | long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb) |
145 | { | 146 | { |
146 | const struct cx18_api_info *info = find_api_info(mb->cmd); | 147 | const struct cx18_api_info *info = find_api_info(mb->cmd); |
147 | struct cx18_mailbox __iomem *ack_mb; | 148 | struct cx18_mailbox __iomem *ack_mb; |
148 | u32 ack_irq; | 149 | u32 ack_irq; |
149 | u8 rpu = CPU; | 150 | u8 rpu = CPU; |
150 | 151 | ||
151 | if (info == NULL && mb->cmd) { | 152 | if (info == NULL && mb->cmd) { |
152 | CX18_WARN("Cannot ack unknown command %x\n", mb->cmd); | 153 | CX18_WARN("Cannot ack unknown command %x\n", mb->cmd); |
153 | return -EINVAL; | 154 | return -EINVAL; |
154 | } | 155 | } |
155 | if (info) | 156 | if (info) |
156 | rpu = info->rpu; | 157 | rpu = info->rpu; |
157 | 158 | ||
158 | switch (rpu) { | 159 | switch (rpu) { |
159 | case HPU: | 160 | case HPU: |
160 | ack_irq = IRQ_EPU_TO_HPU_ACK; | 161 | ack_irq = IRQ_EPU_TO_HPU_ACK; |
161 | ack_mb = &cx->scb->hpu2epu_mb; | 162 | ack_mb = &cx->scb->hpu2epu_mb; |
162 | break; | 163 | break; |
163 | case APU: | 164 | case APU: |
164 | ack_irq = IRQ_EPU_TO_APU_ACK; | 165 | ack_irq = IRQ_EPU_TO_APU_ACK; |
165 | ack_mb = &cx->scb->apu2epu_mb; | 166 | ack_mb = &cx->scb->apu2epu_mb; |
166 | break; | 167 | break; |
167 | case CPU: | 168 | case CPU: |
168 | ack_irq = IRQ_EPU_TO_CPU_ACK; | 169 | ack_irq = IRQ_EPU_TO_CPU_ACK; |
169 | ack_mb = &cx->scb->cpu2epu_mb; | 170 | ack_mb = &cx->scb->cpu2epu_mb; |
170 | break; | 171 | break; |
171 | default: | 172 | default: |
172 | CX18_WARN("Unknown RPU for command %x\n", mb->cmd); | 173 | CX18_WARN("Unknown RPU for command %x\n", mb->cmd); |
173 | return -EINVAL; | 174 | return -EINVAL; |
174 | } | 175 | } |
175 | 176 | ||
176 | setup_page(SCB_OFFSET); | 177 | cx18_setup_page(cx, SCB_OFFSET); |
177 | write_sync(mb->request, &ack_mb->ack); | 178 | cx18_write_sync(cx, mb->request, &ack_mb->ack); |
178 | write_reg(ack_irq, SW2_INT_SET); | 179 | cx18_write_reg(cx, ack_irq, SW2_INT_SET); |
179 | return 0; | 180 | return 0; |
180 | } | 181 | } |
181 | 182 | ||
182 | 183 | ||
183 | static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) | 184 | static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) |
184 | { | 185 | { |
185 | const struct cx18_api_info *info = find_api_info(cmd); | 186 | const struct cx18_api_info *info = find_api_info(cmd); |
186 | u32 state = 0, irq = 0, req, oldreq, err; | 187 | u32 state = 0, irq = 0, req, oldreq, err; |
187 | struct cx18_mailbox __iomem *mb; | 188 | struct cx18_mailbox __iomem *mb; |
188 | wait_queue_head_t *waitq; | 189 | wait_queue_head_t *waitq; |
189 | int timeout = 100; | 190 | int timeout = 100; |
190 | int cnt = 0; | 191 | int cnt = 0; |
191 | int sig = 0; | 192 | int sig = 0; |
192 | int i; | 193 | int i; |
193 | 194 | ||
194 | if (info == NULL) { | 195 | if (info == NULL) { |
195 | CX18_WARN("unknown cmd %x\n", cmd); | 196 | CX18_WARN("unknown cmd %x\n", cmd); |
196 | return -EINVAL; | 197 | return -EINVAL; |
197 | } | 198 | } |
198 | 199 | ||
199 | if (cmd == CX18_CPU_DE_SET_MDL) | 200 | if (cmd == CX18_CPU_DE_SET_MDL) |
200 | CX18_DEBUG_HI_API("%s\n", info->name); | 201 | CX18_DEBUG_HI_API("%s\n", info->name); |
201 | else | 202 | else |
202 | CX18_DEBUG_API("%s\n", info->name); | 203 | CX18_DEBUG_API("%s\n", info->name); |
203 | setup_page(SCB_OFFSET); | 204 | cx18_setup_page(cx, SCB_OFFSET); |
204 | mb = cx18_mb_is_complete(cx, info->rpu, &state, &irq, &req); | 205 | mb = cx18_mb_is_complete(cx, info->rpu, &state, &irq, &req); |
205 | 206 | ||
206 | if (mb == NULL) { | 207 | if (mb == NULL) { |
207 | CX18_ERR("mb %s busy\n", info->name); | 208 | CX18_ERR("mb %s busy\n", info->name); |
208 | return -EBUSY; | 209 | return -EBUSY; |
209 | } | 210 | } |
210 | 211 | ||
211 | oldreq = req - 1; | 212 | oldreq = req - 1; |
212 | writel(cmd, &mb->cmd); | 213 | cx18_writel(cx, cmd, &mb->cmd); |
213 | for (i = 0; i < args; i++) | 214 | for (i = 0; i < args; i++) |
214 | writel(data[i], &mb->args[i]); | 215 | cx18_writel(cx, data[i], &mb->args[i]); |
215 | writel(0, &mb->error); | 216 | cx18_writel(cx, 0, &mb->error); |
216 | writel(req, &mb->request); | 217 | cx18_writel(cx, req, &mb->request); |
217 | 218 | ||
218 | switch (info->rpu) { | 219 | switch (info->rpu) { |
219 | case APU: waitq = &cx->mb_apu_waitq; break; | 220 | case APU: waitq = &cx->mb_apu_waitq; break; |
220 | case CPU: waitq = &cx->mb_cpu_waitq; break; | 221 | case CPU: waitq = &cx->mb_cpu_waitq; break; |
221 | case EPU: waitq = &cx->mb_epu_waitq; break; | 222 | case EPU: waitq = &cx->mb_epu_waitq; break; |
222 | case HPU: waitq = &cx->mb_hpu_waitq; break; | 223 | case HPU: waitq = &cx->mb_hpu_waitq; break; |
223 | default: return -EINVAL; | 224 | default: return -EINVAL; |
224 | } | 225 | } |
225 | if (info->flags & API_FAST) | 226 | if (info->flags & API_FAST) |
226 | timeout /= 2; | 227 | timeout /= 2; |
227 | write_reg(irq, SW1_INT_SET); | 228 | cx18_write_reg(cx, irq, SW1_INT_SET); |
228 | 229 | ||
229 | while (!sig && readl(&mb->ack) != readl(&mb->request) && cnt < 660) { | 230 | while (!sig && cx18_readl(cx, &mb->ack) != cx18_readl(cx, &mb->request) |
231 | && cnt < 660) { | ||
230 | if (cnt > 200 && !in_atomic()) | 232 | if (cnt > 200 && !in_atomic()) |
231 | sig = cx18_msleep_timeout(10, 1); | 233 | sig = cx18_msleep_timeout(10, 1); |
232 | cnt++; | 234 | cnt++; |
233 | } | 235 | } |
234 | if (sig) | 236 | if (sig) |
235 | return -EINTR; | 237 | return -EINTR; |
236 | if (cnt == 660) { | 238 | if (cnt == 660) { |
237 | writel(oldreq, &mb->request); | 239 | cx18_writel(cx, oldreq, &mb->request); |
238 | CX18_ERR("mb %s failed\n", info->name); | 240 | CX18_ERR("mb %s failed\n", info->name); |
239 | return -EINVAL; | 241 | return -EINVAL; |
240 | } | 242 | } |
241 | for (i = 0; i < MAX_MB_ARGUMENTS; i++) | 243 | for (i = 0; i < MAX_MB_ARGUMENTS; i++) |
242 | data[i] = readl(&mb->args[i]); | 244 | data[i] = cx18_readl(cx, &mb->args[i]); |
243 | err = readl(&mb->error); | 245 | err = cx18_readl(cx, &mb->error); |
244 | if (!in_atomic() && (info->flags & API_SLOW)) | 246 | if (!in_atomic() && (info->flags & API_SLOW)) |
245 | cx18_msleep_timeout(300, 0); | 247 | cx18_msleep_timeout(300, 0); |
246 | if (err) | 248 | if (err) |
247 | CX18_DEBUG_API("mailbox error %08x for command %s\n", err, | 249 | CX18_DEBUG_API("mailbox error %08x for command %s\n", err, |
248 | info->name); | 250 | info->name); |
249 | return err ? -EIO : 0; | 251 | return err ? -EIO : 0; |
250 | } | 252 | } |
251 | 253 | ||
252 | int cx18_api(struct cx18 *cx, u32 cmd, int args, u32 data[]) | 254 | int cx18_api(struct cx18 *cx, u32 cmd, int args, u32 data[]) |
253 | { | 255 | { |
254 | int res = cx18_api_call(cx, cmd, args, data); | 256 | int res = cx18_api_call(cx, cmd, args, data); |
255 | 257 | ||
256 | /* Allow a single retry, probably already too late though. | 258 | /* Allow a single retry, probably already too late though. |
257 | If there is no free mailbox then that is usually an indication | 259 | If there is no free mailbox then that is usually an indication |
258 | of a more serious problem. */ | 260 | of a more serious problem. */ |
259 | return (res == -EBUSY) ? cx18_api_call(cx, cmd, args, data) : res; | 261 | return (res == -EBUSY) ? cx18_api_call(cx, cmd, args, data) : res; |
260 | } | 262 | } |
261 | 263 | ||
262 | static int cx18_set_filter_param(struct cx18_stream *s) | 264 | static int cx18_set_filter_param(struct cx18_stream *s) |
263 | { | 265 | { |
264 | struct cx18 *cx = s->cx; | 266 | struct cx18 *cx = s->cx; |
265 | u32 mode; | 267 | u32 mode; |
266 | int ret; | 268 | int ret; |
267 | 269 | ||
268 | mode = (cx->filter_mode & 1) ? 2 : (cx->spatial_strength ? 1 : 0); | 270 | mode = (cx->filter_mode & 1) ? 2 : (cx->spatial_strength ? 1 : 0); |
269 | ret = cx18_vapi(cx, CX18_CPU_SET_FILTER_PARAM, 4, | 271 | ret = cx18_vapi(cx, CX18_CPU_SET_FILTER_PARAM, 4, |
270 | s->handle, 1, mode, cx->spatial_strength); | 272 | s->handle, 1, mode, cx->spatial_strength); |
271 | mode = (cx->filter_mode & 2) ? 2 : (cx->temporal_strength ? 1 : 0); | 273 | mode = (cx->filter_mode & 2) ? 2 : (cx->temporal_strength ? 1 : 0); |
272 | ret = ret ? ret : cx18_vapi(cx, CX18_CPU_SET_FILTER_PARAM, 4, | 274 | ret = ret ? ret : cx18_vapi(cx, CX18_CPU_SET_FILTER_PARAM, 4, |
273 | s->handle, 0, mode, cx->temporal_strength); | 275 | s->handle, 0, mode, cx->temporal_strength); |
274 | ret = ret ? ret : cx18_vapi(cx, CX18_CPU_SET_FILTER_PARAM, 4, | 276 | ret = ret ? ret : cx18_vapi(cx, CX18_CPU_SET_FILTER_PARAM, 4, |
275 | s->handle, 2, cx->filter_mode >> 2, 0); | 277 | s->handle, 2, cx->filter_mode >> 2, 0); |
276 | return ret; | 278 | return ret; |
277 | } | 279 | } |
278 | 280 | ||
279 | int cx18_api_func(void *priv, u32 cmd, int in, int out, | 281 | int cx18_api_func(void *priv, u32 cmd, int in, int out, |
280 | u32 data[CX2341X_MBOX_MAX_DATA]) | 282 | u32 data[CX2341X_MBOX_MAX_DATA]) |
281 | { | 283 | { |
282 | struct cx18 *cx = priv; | 284 | struct cx18 *cx = priv; |
283 | struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_MPG]; | 285 | struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_MPG]; |
284 | 286 | ||
285 | switch (cmd) { | 287 | switch (cmd) { |
286 | case CX2341X_ENC_SET_OUTPUT_PORT: | 288 | case CX2341X_ENC_SET_OUTPUT_PORT: |
287 | return 0; | 289 | return 0; |
288 | case CX2341X_ENC_SET_FRAME_RATE: | 290 | case CX2341X_ENC_SET_FRAME_RATE: |
289 | return cx18_vapi(cx, CX18_CPU_SET_VIDEO_IN, 6, | 291 | return cx18_vapi(cx, CX18_CPU_SET_VIDEO_IN, 6, |
290 | s->handle, 0, 0, 0, 0, data[0]); | 292 | s->handle, 0, 0, 0, 0, data[0]); |
291 | case CX2341X_ENC_SET_FRAME_SIZE: | 293 | case CX2341X_ENC_SET_FRAME_SIZE: |
292 | return cx18_vapi(cx, CX18_CPU_SET_VIDEO_RESOLUTION, 3, | 294 | return cx18_vapi(cx, CX18_CPU_SET_VIDEO_RESOLUTION, 3, |
293 | s->handle, data[1], data[0]); | 295 | s->handle, data[1], data[0]); |
294 | case CX2341X_ENC_SET_STREAM_TYPE: | 296 | case CX2341X_ENC_SET_STREAM_TYPE: |
295 | return cx18_vapi(cx, CX18_CPU_SET_STREAM_OUTPUT_TYPE, 2, | 297 | return cx18_vapi(cx, CX18_CPU_SET_STREAM_OUTPUT_TYPE, 2, |
296 | s->handle, data[0]); | 298 | s->handle, data[0]); |
297 | case CX2341X_ENC_SET_ASPECT_RATIO: | 299 | case CX2341X_ENC_SET_ASPECT_RATIO: |
298 | return cx18_vapi(cx, CX18_CPU_SET_ASPECT_RATIO, 2, | 300 | return cx18_vapi(cx, CX18_CPU_SET_ASPECT_RATIO, 2, |
299 | s->handle, data[0]); | 301 | s->handle, data[0]); |
300 | 302 | ||
301 | case CX2341X_ENC_SET_GOP_PROPERTIES: | 303 | case CX2341X_ENC_SET_GOP_PROPERTIES: |
302 | return cx18_vapi(cx, CX18_CPU_SET_GOP_STRUCTURE, 3, | 304 | return cx18_vapi(cx, CX18_CPU_SET_GOP_STRUCTURE, 3, |
303 | s->handle, data[0], data[1]); | 305 | s->handle, data[0], data[1]); |
304 | case CX2341X_ENC_SET_GOP_CLOSURE: | 306 | case CX2341X_ENC_SET_GOP_CLOSURE: |
305 | return 0; | 307 | return 0; |
306 | case CX2341X_ENC_SET_AUDIO_PROPERTIES: | 308 | case CX2341X_ENC_SET_AUDIO_PROPERTIES: |
307 | return cx18_vapi(cx, CX18_CPU_SET_AUDIO_PARAMETERS, 2, | 309 | return cx18_vapi(cx, CX18_CPU_SET_AUDIO_PARAMETERS, 2, |
308 | s->handle, data[0]); | 310 | s->handle, data[0]); |
309 | case CX2341X_ENC_MUTE_AUDIO: | 311 | case CX2341X_ENC_MUTE_AUDIO: |
310 | return cx18_vapi(cx, CX18_CPU_SET_AUDIO_MUTE, 2, | 312 | return cx18_vapi(cx, CX18_CPU_SET_AUDIO_MUTE, 2, |
311 | s->handle, data[0]); | 313 | s->handle, data[0]); |
312 | case CX2341X_ENC_SET_BIT_RATE: | 314 | case CX2341X_ENC_SET_BIT_RATE: |
313 | return cx18_vapi(cx, CX18_CPU_SET_VIDEO_RATE, 5, | 315 | return cx18_vapi(cx, CX18_CPU_SET_VIDEO_RATE, 5, |
314 | s->handle, data[0], data[1], data[2], data[3]); | 316 | s->handle, data[0], data[1], data[2], data[3]); |
315 | case CX2341X_ENC_MUTE_VIDEO: | 317 | case CX2341X_ENC_MUTE_VIDEO: |
316 | return cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2, | 318 | return cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2, |
317 | s->handle, data[0]); | 319 | s->handle, data[0]); |
318 | case CX2341X_ENC_SET_FRAME_DROP_RATE: | 320 | case CX2341X_ENC_SET_FRAME_DROP_RATE: |
319 | return cx18_vapi(cx, CX18_CPU_SET_SKIP_INPUT_FRAME, 2, | 321 | return cx18_vapi(cx, CX18_CPU_SET_SKIP_INPUT_FRAME, 2, |
320 | s->handle, data[0]); | 322 | s->handle, data[0]); |
321 | case CX2341X_ENC_MISC: | 323 | case CX2341X_ENC_MISC: |
322 | return cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 4, | 324 | return cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 4, |
323 | s->handle, data[0], data[1], data[2]); | 325 | s->handle, data[0], data[1], data[2]); |
324 | case CX2341X_ENC_SET_DNR_FILTER_MODE: | 326 | case CX2341X_ENC_SET_DNR_FILTER_MODE: |
325 | cx->filter_mode = (data[0] & 3) | (data[1] << 2); | 327 | cx->filter_mode = (data[0] & 3) | (data[1] << 2); |
326 | return cx18_set_filter_param(s); | 328 | return cx18_set_filter_param(s); |
327 | case CX2341X_ENC_SET_DNR_FILTER_PROPS: | 329 | case CX2341X_ENC_SET_DNR_FILTER_PROPS: |
328 | cx->spatial_strength = data[0]; | 330 | cx->spatial_strength = data[0]; |
329 | cx->temporal_strength = data[1]; | 331 | cx->temporal_strength = data[1]; |
330 | return cx18_set_filter_param(s); | 332 | return cx18_set_filter_param(s); |
331 | case CX2341X_ENC_SET_SPATIAL_FILTER_TYPE: | 333 | case CX2341X_ENC_SET_SPATIAL_FILTER_TYPE: |
332 | return cx18_vapi(cx, CX18_CPU_SET_SPATIAL_FILTER_TYPE, 3, | 334 | return cx18_vapi(cx, CX18_CPU_SET_SPATIAL_FILTER_TYPE, 3, |
333 | s->handle, data[0], data[1]); | 335 | s->handle, data[0], data[1]); |
334 | case CX2341X_ENC_SET_CORING_LEVELS: | 336 | case CX2341X_ENC_SET_CORING_LEVELS: |
335 | return cx18_vapi(cx, CX18_CPU_SET_MEDIAN_CORING, 5, | 337 | return cx18_vapi(cx, CX18_CPU_SET_MEDIAN_CORING, 5, |
336 | s->handle, data[0], data[1], data[2], data[3]); | 338 | s->handle, data[0], data[1], data[2], data[3]); |
337 | } | 339 | } |
338 | CX18_WARN("Unknown cmd %x\n", cmd); | 340 | CX18_WARN("Unknown cmd %x\n", cmd); |
339 | return 0; | 341 | return 0; |
340 | } | 342 | } |
341 | 343 | ||
342 | int cx18_vapi_result(struct cx18 *cx, u32 data[MAX_MB_ARGUMENTS], | 344 | int cx18_vapi_result(struct cx18 *cx, u32 data[MAX_MB_ARGUMENTS], |
343 | u32 cmd, int args, ...) | 345 | u32 cmd, int args, ...) |
344 | { | 346 | { |
345 | va_list ap; | 347 | va_list ap; |
346 | int i; | 348 | int i; |
347 | 349 | ||
348 | va_start(ap, args); | 350 | va_start(ap, args); |
349 | for (i = 0; i < args; i++) | 351 | for (i = 0; i < args; i++) |
350 | data[i] = va_arg(ap, u32); | 352 | data[i] = va_arg(ap, u32); |
351 | va_end(ap); | 353 | va_end(ap); |
352 | return cx18_api(cx, cmd, args, data); | 354 | return cx18_api(cx, cmd, args, data); |
353 | } | 355 | } |
354 | 356 | ||
355 | int cx18_vapi(struct cx18 *cx, u32 cmd, int args, ...) | 357 | int cx18_vapi(struct cx18 *cx, u32 cmd, int args, ...) |
356 | { | 358 | { |
357 | u32 data[MAX_MB_ARGUMENTS]; | 359 | u32 data[MAX_MB_ARGUMENTS]; |
358 | va_list ap; | 360 | va_list ap; |
359 | int i; | 361 | int i; |
360 | 362 | ||
361 | if (cx == NULL) { | 363 | if (cx == NULL) { |
362 | CX18_ERR("cx == NULL (cmd=%x)\n", cmd); | 364 | CX18_ERR("cx == NULL (cmd=%x)\n", cmd); |
363 | return 0; | 365 | return 0; |
364 | } | 366 | } |
365 | if (args > MAX_MB_ARGUMENTS) { | 367 | if (args > MAX_MB_ARGUMENTS) { |
366 | CX18_ERR("args too big (cmd=%x)\n", cmd); | 368 | CX18_ERR("args too big (cmd=%x)\n", cmd); |
367 | args = MAX_MB_ARGUMENTS; | 369 | args = MAX_MB_ARGUMENTS; |
368 | } | 370 | } |
369 | va_start(ap, args); | 371 | va_start(ap, args); |
370 | for (i = 0; i < args; i++) | 372 | for (i = 0; i < args; i++) |
371 | data[i] = va_arg(ap, u32); | 373 | data[i] = va_arg(ap, u32); |
372 | va_end(ap); | 374 | va_end(ap); |
373 | return cx18_api(cx, cmd, args, data); | 375 | return cx18_api(cx, cmd, args, data); |
374 | } | 376 | } |
375 | 377 |
drivers/media/video/cx18/cx18-queue.c
1 | /* | 1 | /* |
2 | * cx18 buffer queues | 2 | * cx18 buffer queues |
3 | * | 3 | * |
4 | * Derived from ivtv-queue.c | 4 | * Derived from ivtv-queue.c |
5 | * | 5 | * |
6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | 6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> |
7 | * | 7 | * |
8 | * This program is free software; you can redistribute it and/or modify | 8 | * This program is free software; you can redistribute it and/or modify |
9 | * it under the terms of the GNU General Public License as published by | 9 | * it under the terms of the GNU General Public License as published by |
10 | * the Free Software Foundation; either version 2 of the License, or | 10 | * the Free Software Foundation; either version 2 of the License, or |
11 | * (at your option) any later version. | 11 | * (at your option) any later version. |
12 | * | 12 | * |
13 | * This program is distributed in the hope that it will be useful, | 13 | * This program is distributed in the hope that it will be useful, |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | * GNU General Public License for more details. | 16 | * GNU General Public License for more details. |
17 | * | 17 | * |
18 | * You should have received a copy of the GNU General Public License | 18 | * You should have received a copy of the GNU General Public License |
19 | * along with this program; if not, write to the Free Software | 19 | * along with this program; if not, write to the Free Software |
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | 20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
21 | * 02111-1307 USA | 21 | * 02111-1307 USA |
22 | */ | 22 | */ |
23 | 23 | ||
24 | #include "cx18-driver.h" | 24 | #include "cx18-driver.h" |
25 | #include "cx18-streams.h" | 25 | #include "cx18-streams.h" |
26 | #include "cx18-queue.h" | 26 | #include "cx18-queue.h" |
27 | #include "cx18-scb.h" | 27 | #include "cx18-scb.h" |
28 | 28 | ||
29 | void cx18_buf_swap(struct cx18_buffer *buf) | 29 | void cx18_buf_swap(struct cx18_buffer *buf) |
30 | { | 30 | { |
31 | int i; | 31 | int i; |
32 | 32 | ||
33 | for (i = 0; i < buf->bytesused; i += 4) | 33 | for (i = 0; i < buf->bytesused; i += 4) |
34 | swab32s((u32 *)(buf->buf + i)); | 34 | swab32s((u32 *)(buf->buf + i)); |
35 | } | 35 | } |
36 | 36 | ||
37 | void cx18_queue_init(struct cx18_queue *q) | 37 | void cx18_queue_init(struct cx18_queue *q) |
38 | { | 38 | { |
39 | INIT_LIST_HEAD(&q->list); | 39 | INIT_LIST_HEAD(&q->list); |
40 | atomic_set(&q->buffers, 0); | 40 | atomic_set(&q->buffers, 0); |
41 | q->bytesused = 0; | 41 | q->bytesused = 0; |
42 | } | 42 | } |
43 | 43 | ||
44 | void cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, | 44 | void cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, |
45 | struct cx18_queue *q) | 45 | struct cx18_queue *q) |
46 | { | 46 | { |
47 | unsigned long flags = 0; | 47 | unsigned long flags = 0; |
48 | 48 | ||
49 | /* clear the buffer if it is going to be enqueued to the free queue */ | 49 | /* clear the buffer if it is going to be enqueued to the free queue */ |
50 | if (q == &s->q_free) { | 50 | if (q == &s->q_free) { |
51 | buf->bytesused = 0; | 51 | buf->bytesused = 0; |
52 | buf->readpos = 0; | 52 | buf->readpos = 0; |
53 | buf->b_flags = 0; | 53 | buf->b_flags = 0; |
54 | } | 54 | } |
55 | spin_lock_irqsave(&s->qlock, flags); | 55 | spin_lock_irqsave(&s->qlock, flags); |
56 | list_add_tail(&buf->list, &q->list); | 56 | list_add_tail(&buf->list, &q->list); |
57 | atomic_inc(&q->buffers); | 57 | atomic_inc(&q->buffers); |
58 | q->bytesused += buf->bytesused - buf->readpos; | 58 | q->bytesused += buf->bytesused - buf->readpos; |
59 | spin_unlock_irqrestore(&s->qlock, flags); | 59 | spin_unlock_irqrestore(&s->qlock, flags); |
60 | } | 60 | } |
61 | 61 | ||
62 | struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q) | 62 | struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q) |
63 | { | 63 | { |
64 | struct cx18_buffer *buf = NULL; | 64 | struct cx18_buffer *buf = NULL; |
65 | unsigned long flags = 0; | 65 | unsigned long flags = 0; |
66 | 66 | ||
67 | spin_lock_irqsave(&s->qlock, flags); | 67 | spin_lock_irqsave(&s->qlock, flags); |
68 | if (!list_empty(&q->list)) { | 68 | if (!list_empty(&q->list)) { |
69 | buf = list_entry(q->list.next, struct cx18_buffer, list); | 69 | buf = list_entry(q->list.next, struct cx18_buffer, list); |
70 | list_del_init(q->list.next); | 70 | list_del_init(q->list.next); |
71 | atomic_dec(&q->buffers); | 71 | atomic_dec(&q->buffers); |
72 | q->bytesused -= buf->bytesused - buf->readpos; | 72 | q->bytesused -= buf->bytesused - buf->readpos; |
73 | } | 73 | } |
74 | spin_unlock_irqrestore(&s->qlock, flags); | 74 | spin_unlock_irqrestore(&s->qlock, flags); |
75 | return buf; | 75 | return buf; |
76 | } | 76 | } |
77 | 77 | ||
78 | struct cx18_buffer *cx18_queue_get_buf_irq(struct cx18_stream *s, u32 id, | 78 | struct cx18_buffer *cx18_queue_get_buf_irq(struct cx18_stream *s, u32 id, |
79 | u32 bytesused) | 79 | u32 bytesused) |
80 | { | 80 | { |
81 | struct cx18 *cx = s->cx; | 81 | struct cx18 *cx = s->cx; |
82 | struct list_head *p; | 82 | struct list_head *p; |
83 | 83 | ||
84 | spin_lock(&s->qlock); | 84 | spin_lock(&s->qlock); |
85 | list_for_each(p, &s->q_free.list) { | 85 | list_for_each(p, &s->q_free.list) { |
86 | struct cx18_buffer *buf = | 86 | struct cx18_buffer *buf = |
87 | list_entry(p, struct cx18_buffer, list); | 87 | list_entry(p, struct cx18_buffer, list); |
88 | 88 | ||
89 | if (buf->id != id) | 89 | if (buf->id != id) |
90 | continue; | 90 | continue; |
91 | buf->bytesused = bytesused; | 91 | buf->bytesused = bytesused; |
92 | /* the transport buffers are handled differently, | 92 | /* the transport buffers are handled differently, |
93 | they are not moved to the full queue */ | 93 | they are not moved to the full queue */ |
94 | if (s->type != CX18_ENC_STREAM_TYPE_TS) { | 94 | if (s->type != CX18_ENC_STREAM_TYPE_TS) { |
95 | atomic_dec(&s->q_free.buffers); | 95 | atomic_dec(&s->q_free.buffers); |
96 | atomic_inc(&s->q_full.buffers); | 96 | atomic_inc(&s->q_full.buffers); |
97 | s->q_full.bytesused += buf->bytesused; | 97 | s->q_full.bytesused += buf->bytesused; |
98 | list_move_tail(&buf->list, &s->q_full.list); | 98 | list_move_tail(&buf->list, &s->q_full.list); |
99 | } | 99 | } |
100 | spin_unlock(&s->qlock); | 100 | spin_unlock(&s->qlock); |
101 | return buf; | 101 | return buf; |
102 | } | 102 | } |
103 | spin_unlock(&s->qlock); | 103 | spin_unlock(&s->qlock); |
104 | CX18_ERR("Cannot find buffer %d for stream %s\n", id, s->name); | 104 | CX18_ERR("Cannot find buffer %d for stream %s\n", id, s->name); |
105 | return NULL; | 105 | return NULL; |
106 | } | 106 | } |
107 | 107 | ||
108 | /* Move all buffers of a queue to q_free, while flushing the buffers */ | 108 | /* Move all buffers of a queue to q_free, while flushing the buffers */ |
109 | static void cx18_queue_flush(struct cx18_stream *s, struct cx18_queue *q) | 109 | static void cx18_queue_flush(struct cx18_stream *s, struct cx18_queue *q) |
110 | { | 110 | { |
111 | unsigned long flags; | 111 | unsigned long flags; |
112 | struct cx18_buffer *buf; | 112 | struct cx18_buffer *buf; |
113 | 113 | ||
114 | if (q == &s->q_free) | 114 | if (q == &s->q_free) |
115 | return; | 115 | return; |
116 | 116 | ||
117 | spin_lock_irqsave(&s->qlock, flags); | 117 | spin_lock_irqsave(&s->qlock, flags); |
118 | while (!list_empty(&q->list)) { | 118 | while (!list_empty(&q->list)) { |
119 | buf = list_entry(q->list.next, struct cx18_buffer, list); | 119 | buf = list_entry(q->list.next, struct cx18_buffer, list); |
120 | list_move_tail(q->list.next, &s->q_free.list); | 120 | list_move_tail(q->list.next, &s->q_free.list); |
121 | buf->bytesused = buf->readpos = buf->b_flags = 0; | 121 | buf->bytesused = buf->readpos = buf->b_flags = 0; |
122 | atomic_inc(&s->q_free.buffers); | 122 | atomic_inc(&s->q_free.buffers); |
123 | } | 123 | } |
124 | cx18_queue_init(q); | 124 | cx18_queue_init(q); |
125 | spin_unlock_irqrestore(&s->qlock, flags); | 125 | spin_unlock_irqrestore(&s->qlock, flags); |
126 | } | 126 | } |
127 | 127 | ||
128 | void cx18_flush_queues(struct cx18_stream *s) | 128 | void cx18_flush_queues(struct cx18_stream *s) |
129 | { | 129 | { |
130 | cx18_queue_flush(s, &s->q_io); | 130 | cx18_queue_flush(s, &s->q_io); |
131 | cx18_queue_flush(s, &s->q_full); | 131 | cx18_queue_flush(s, &s->q_full); |
132 | } | 132 | } |
133 | 133 | ||
134 | int cx18_stream_alloc(struct cx18_stream *s) | 134 | int cx18_stream_alloc(struct cx18_stream *s) |
135 | { | 135 | { |
136 | struct cx18 *cx = s->cx; | 136 | struct cx18 *cx = s->cx; |
137 | int i; | 137 | int i; |
138 | 138 | ||
139 | if (s->buffers == 0) | 139 | if (s->buffers == 0) |
140 | return 0; | 140 | return 0; |
141 | 141 | ||
142 | CX18_DEBUG_INFO("Allocate %s stream: %d x %d buffers (%dkB total)\n", | 142 | CX18_DEBUG_INFO("Allocate %s stream: %d x %d buffers (%dkB total)\n", |
143 | s->name, s->buffers, s->buf_size, | 143 | s->name, s->buffers, s->buf_size, |
144 | s->buffers * s->buf_size / 1024); | 144 | s->buffers * s->buf_size / 1024); |
145 | 145 | ||
146 | if (((char __iomem *)&cx->scb->cpu_mdl[cx->mdl_offset + s->buffers] - | 146 | if (((char __iomem *)&cx->scb->cpu_mdl[cx->mdl_offset + s->buffers] - |
147 | (char __iomem *)cx->scb) > SCB_RESERVED_SIZE) { | 147 | (char __iomem *)cx->scb) > SCB_RESERVED_SIZE) { |
148 | unsigned bufsz = (((char __iomem *)cx->scb) + SCB_RESERVED_SIZE - | 148 | unsigned bufsz = (((char __iomem *)cx->scb) + SCB_RESERVED_SIZE - |
149 | ((char __iomem *)cx->scb->cpu_mdl)); | 149 | ((char __iomem *)cx->scb->cpu_mdl)); |
150 | 150 | ||
151 | CX18_ERR("Too many buffers, cannot fit in SCB area\n"); | 151 | CX18_ERR("Too many buffers, cannot fit in SCB area\n"); |
152 | CX18_ERR("Max buffers = %zd\n", | 152 | CX18_ERR("Max buffers = %zd\n", |
153 | bufsz / sizeof(struct cx18_mdl)); | 153 | bufsz / sizeof(struct cx18_mdl)); |
154 | return -ENOMEM; | 154 | return -ENOMEM; |
155 | } | 155 | } |
156 | 156 | ||
157 | s->mdl_offset = cx->mdl_offset; | 157 | s->mdl_offset = cx->mdl_offset; |
158 | 158 | ||
159 | /* allocate stream buffers. Initially all buffers are in q_free. */ | 159 | /* allocate stream buffers. Initially all buffers are in q_free. */ |
160 | for (i = 0; i < s->buffers; i++) { | 160 | for (i = 0; i < s->buffers; i++) { |
161 | struct cx18_buffer *buf = kzalloc(sizeof(struct cx18_buffer), | 161 | struct cx18_buffer *buf = kzalloc(sizeof(struct cx18_buffer), |
162 | GFP_KERNEL|__GFP_NOWARN); | 162 | GFP_KERNEL|__GFP_NOWARN); |
163 | 163 | ||
164 | if (buf == NULL) | 164 | if (buf == NULL) |
165 | break; | 165 | break; |
166 | buf->buf = kmalloc(s->buf_size, GFP_KERNEL|__GFP_NOWARN); | 166 | buf->buf = kmalloc(s->buf_size, GFP_KERNEL|__GFP_NOWARN); |
167 | if (buf->buf == NULL) { | 167 | if (buf->buf == NULL) { |
168 | kfree(buf); | 168 | kfree(buf); |
169 | break; | 169 | break; |
170 | } | 170 | } |
171 | buf->id = cx->buffer_id++; | 171 | buf->id = cx->buffer_id++; |
172 | INIT_LIST_HEAD(&buf->list); | 172 | INIT_LIST_HEAD(&buf->list); |
173 | /* FIXME - check for mmio */ | ||
173 | buf->dma_handle = pci_map_single(s->cx->dev, | 174 | buf->dma_handle = pci_map_single(s->cx->dev, |
174 | buf->buf, s->buf_size, s->dma); | 175 | buf->buf, s->buf_size, s->dma); |
175 | cx18_buf_sync_for_cpu(s, buf); | 176 | cx18_buf_sync_for_cpu(s, buf); |
176 | cx18_enqueue(s, buf, &s->q_free); | 177 | cx18_enqueue(s, buf, &s->q_free); |
177 | } | 178 | } |
178 | if (i == s->buffers) { | 179 | if (i == s->buffers) { |
179 | cx->mdl_offset += s->buffers; | 180 | cx->mdl_offset += s->buffers; |
180 | return 0; | 181 | return 0; |
181 | } | 182 | } |
182 | CX18_ERR("Couldn't allocate buffers for %s stream\n", s->name); | 183 | CX18_ERR("Couldn't allocate buffers for %s stream\n", s->name); |
183 | cx18_stream_free(s); | 184 | cx18_stream_free(s); |
184 | return -ENOMEM; | 185 | return -ENOMEM; |
185 | } | 186 | } |
186 | 187 | ||
187 | void cx18_stream_free(struct cx18_stream *s) | 188 | void cx18_stream_free(struct cx18_stream *s) |
188 | { | 189 | { |
189 | struct cx18_buffer *buf; | 190 | struct cx18_buffer *buf; |
190 | 191 | ||
191 | /* move all buffers to q_free */ | 192 | /* move all buffers to q_free */ |
192 | cx18_flush_queues(s); | 193 | cx18_flush_queues(s); |
193 | 194 | ||
194 | /* empty q_free */ | 195 | /* empty q_free */ |
195 | while ((buf = cx18_dequeue(s, &s->q_free))) { | 196 | while ((buf = cx18_dequeue(s, &s->q_free))) { |
197 | /* FIXME - check for mmio */ | ||
196 | pci_unmap_single(s->cx->dev, buf->dma_handle, | 198 | pci_unmap_single(s->cx->dev, buf->dma_handle, |
197 | s->buf_size, s->dma); | 199 | s->buf_size, s->dma); |
198 | kfree(buf->buf); | 200 | kfree(buf->buf); |
199 | kfree(buf); | 201 | kfree(buf); |
200 | } | 202 | } |
201 | } | 203 | } |
202 | 204 |
drivers/media/video/cx18/cx18-queue.h
1 | /* | 1 | /* |
2 | * cx18 buffer queues | 2 | * cx18 buffer queues |
3 | * | 3 | * |
4 | * Derived from ivtv-queue.h | 4 | * Derived from ivtv-queue.h |
5 | * | 5 | * |
6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | 6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> |
7 | * | 7 | * |
8 | * This program is free software; you can redistribute it and/or modify | 8 | * This program is free software; you can redistribute it and/or modify |
9 | * it under the terms of the GNU General Public License as published by | 9 | * it under the terms of the GNU General Public License as published by |
10 | * the Free Software Foundation; either version 2 of the License, or | 10 | * the Free Software Foundation; either version 2 of the License, or |
11 | * (at your option) any later version. | 11 | * (at your option) any later version. |
12 | * | 12 | * |
13 | * This program is distributed in the hope that it will be useful, | 13 | * This program is distributed in the hope that it will be useful, |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | * GNU General Public License for more details. | 16 | * GNU General Public License for more details. |
17 | * | 17 | * |
18 | * You should have received a copy of the GNU General Public License | 18 | * You should have received a copy of the GNU General Public License |
19 | * along with this program; if not, write to the Free Software | 19 | * along with this program; if not, write to the Free Software |
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | 20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
21 | * 02111-1307 USA | 21 | * 02111-1307 USA |
22 | */ | 22 | */ |
23 | 23 | ||
24 | #define CX18_DMA_UNMAPPED ((u32) -1) | 24 | #define CX18_DMA_UNMAPPED ((u32) -1) |
25 | 25 | ||
26 | /* cx18_buffer utility functions */ | 26 | /* cx18_buffer utility functions */ |
27 | 27 | ||
28 | static inline void cx18_buf_sync_for_cpu(struct cx18_stream *s, | 28 | static inline void cx18_buf_sync_for_cpu(struct cx18_stream *s, |
29 | struct cx18_buffer *buf) | 29 | struct cx18_buffer *buf) |
30 | { | 30 | { |
31 | /* FIXME check IO transfers */ | ||
31 | pci_dma_sync_single_for_cpu(s->cx->dev, buf->dma_handle, | 32 | pci_dma_sync_single_for_cpu(s->cx->dev, buf->dma_handle, |
32 | s->buf_size, s->dma); | 33 | s->buf_size, s->dma); |
33 | } | 34 | } |
34 | 35 | ||
35 | static inline void cx18_buf_sync_for_device(struct cx18_stream *s, | 36 | static inline void cx18_buf_sync_for_device(struct cx18_stream *s, |
36 | struct cx18_buffer *buf) | 37 | struct cx18_buffer *buf) |
37 | { | 38 | { |
39 | /* FIXME check IO transfers */ | ||
38 | pci_dma_sync_single_for_device(s->cx->dev, buf->dma_handle, | 40 | pci_dma_sync_single_for_device(s->cx->dev, buf->dma_handle, |
39 | s->buf_size, s->dma); | 41 | s->buf_size, s->dma); |
40 | } | 42 | } |
41 | 43 | ||
42 | void cx18_buf_swap(struct cx18_buffer *buf); | 44 | void cx18_buf_swap(struct cx18_buffer *buf); |
43 | 45 | ||
44 | /* cx18_queue utility functions */ | 46 | /* cx18_queue utility functions */ |
45 | void cx18_queue_init(struct cx18_queue *q); | 47 | void cx18_queue_init(struct cx18_queue *q); |
46 | void cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, | 48 | void cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, |
47 | struct cx18_queue *q); | 49 | struct cx18_queue *q); |
48 | struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q); | 50 | struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q); |
49 | struct cx18_buffer *cx18_queue_get_buf_irq(struct cx18_stream *s, u32 id, | 51 | struct cx18_buffer *cx18_queue_get_buf_irq(struct cx18_stream *s, u32 id, |
50 | u32 bytesused); | 52 | u32 bytesused); |
51 | void cx18_flush_queues(struct cx18_stream *s); | 53 | void cx18_flush_queues(struct cx18_stream *s); |
52 | 54 | ||
53 | /* cx18_stream utility functions */ | 55 | /* cx18_stream utility functions */ |
54 | int cx18_stream_alloc(struct cx18_stream *s); | 56 | int cx18_stream_alloc(struct cx18_stream *s); |
55 | void cx18_stream_free(struct cx18_stream *s); | 57 | void cx18_stream_free(struct cx18_stream *s); |
56 | 58 |
drivers/media/video/cx18/cx18-scb.c
1 | /* | 1 | /* |
2 | * cx18 System Control Block initialization | 2 | * cx18 System Control Block initialization |
3 | * | 3 | * |
4 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | 4 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> |
5 | * | 5 | * |
6 | * This program is free software; you can redistribute it and/or modify | 6 | * This program is free software; you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License as published by | 7 | * it under the terms of the GNU General Public License as published by |
8 | * the Free Software Foundation; either version 2 of the License, or | 8 | * the Free Software Foundation; either version 2 of the License, or |
9 | * (at your option) any later version. | 9 | * (at your option) any later version. |
10 | * | 10 | * |
11 | * This program is distributed in the hope that it will be useful, | 11 | * This program is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | * GNU General Public License for more details. | 14 | * GNU General Public License for more details. |
15 | * | 15 | * |
16 | * You should have received a copy of the GNU General Public License | 16 | * You should have received a copy of the GNU General Public License |
17 | * along with this program; if not, write to the Free Software | 17 | * along with this program; if not, write to the Free Software |
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
19 | * 02111-1307 USA | 19 | * 02111-1307 USA |
20 | */ | 20 | */ |
21 | 21 | ||
22 | #include "cx18-driver.h" | 22 | #include "cx18-driver.h" |
23 | #include "cx18-io.h" | ||
23 | #include "cx18-scb.h" | 24 | #include "cx18-scb.h" |
24 | 25 | ||
25 | void cx18_init_scb(struct cx18 *cx) | 26 | void cx18_init_scb(struct cx18 *cx) |
26 | { | 27 | { |
27 | setup_page(SCB_OFFSET); | 28 | cx18_setup_page(cx, SCB_OFFSET); |
28 | memset_io(cx->scb, 0, 0x10000); | 29 | cx18_memset_io(cx, cx->scb, 0, 0x10000); |
29 | 30 | ||
30 | writel(IRQ_APU_TO_CPU, &cx->scb->apu2cpu_irq); | 31 | cx18_writel(cx, IRQ_APU_TO_CPU, &cx->scb->apu2cpu_irq); |
31 | writel(IRQ_CPU_TO_APU_ACK, &cx->scb->cpu2apu_irq_ack); | 32 | cx18_writel(cx, IRQ_CPU_TO_APU_ACK, &cx->scb->cpu2apu_irq_ack); |
32 | writel(IRQ_HPU_TO_CPU, &cx->scb->hpu2cpu_irq); | 33 | cx18_writel(cx, IRQ_HPU_TO_CPU, &cx->scb->hpu2cpu_irq); |
33 | writel(IRQ_CPU_TO_HPU_ACK, &cx->scb->cpu2hpu_irq_ack); | 34 | cx18_writel(cx, IRQ_CPU_TO_HPU_ACK, &cx->scb->cpu2hpu_irq_ack); |
34 | writel(IRQ_PPU_TO_CPU, &cx->scb->ppu2cpu_irq); | 35 | cx18_writel(cx, IRQ_PPU_TO_CPU, &cx->scb->ppu2cpu_irq); |
35 | writel(IRQ_CPU_TO_PPU_ACK, &cx->scb->cpu2ppu_irq_ack); | 36 | cx18_writel(cx, IRQ_CPU_TO_PPU_ACK, &cx->scb->cpu2ppu_irq_ack); |
36 | writel(IRQ_EPU_TO_CPU, &cx->scb->epu2cpu_irq); | 37 | cx18_writel(cx, IRQ_EPU_TO_CPU, &cx->scb->epu2cpu_irq); |
37 | writel(IRQ_CPU_TO_EPU_ACK, &cx->scb->cpu2epu_irq_ack); | 38 | cx18_writel(cx, IRQ_CPU_TO_EPU_ACK, &cx->scb->cpu2epu_irq_ack); |
38 | 39 | ||
39 | writel(IRQ_CPU_TO_APU, &cx->scb->cpu2apu_irq); | 40 | cx18_writel(cx, IRQ_CPU_TO_APU, &cx->scb->cpu2apu_irq); |
40 | writel(IRQ_APU_TO_CPU_ACK, &cx->scb->apu2cpu_irq_ack); | 41 | cx18_writel(cx, IRQ_APU_TO_CPU_ACK, &cx->scb->apu2cpu_irq_ack); |
41 | writel(IRQ_HPU_TO_APU, &cx->scb->hpu2apu_irq); | 42 | cx18_writel(cx, IRQ_HPU_TO_APU, &cx->scb->hpu2apu_irq); |
42 | writel(IRQ_APU_TO_HPU_ACK, &cx->scb->apu2hpu_irq_ack); | 43 | cx18_writel(cx, IRQ_APU_TO_HPU_ACK, &cx->scb->apu2hpu_irq_ack); |
43 | writel(IRQ_PPU_TO_APU, &cx->scb->ppu2apu_irq); | 44 | cx18_writel(cx, IRQ_PPU_TO_APU, &cx->scb->ppu2apu_irq); |
44 | writel(IRQ_APU_TO_PPU_ACK, &cx->scb->apu2ppu_irq_ack); | 45 | cx18_writel(cx, IRQ_APU_TO_PPU_ACK, &cx->scb->apu2ppu_irq_ack); |
45 | writel(IRQ_EPU_TO_APU, &cx->scb->epu2apu_irq); | 46 | cx18_writel(cx, IRQ_EPU_TO_APU, &cx->scb->epu2apu_irq); |
46 | writel(IRQ_APU_TO_EPU_ACK, &cx->scb->apu2epu_irq_ack); | 47 | cx18_writel(cx, IRQ_APU_TO_EPU_ACK, &cx->scb->apu2epu_irq_ack); |
47 | 48 | ||
48 | writel(IRQ_CPU_TO_HPU, &cx->scb->cpu2hpu_irq); | 49 | cx18_writel(cx, IRQ_CPU_TO_HPU, &cx->scb->cpu2hpu_irq); |
49 | writel(IRQ_HPU_TO_CPU_ACK, &cx->scb->hpu2cpu_irq_ack); | 50 | cx18_writel(cx, IRQ_HPU_TO_CPU_ACK, &cx->scb->hpu2cpu_irq_ack); |
50 | writel(IRQ_APU_TO_HPU, &cx->scb->apu2hpu_irq); | 51 | cx18_writel(cx, IRQ_APU_TO_HPU, &cx->scb->apu2hpu_irq); |
51 | writel(IRQ_HPU_TO_APU_ACK, &cx->scb->hpu2apu_irq_ack); | 52 | cx18_writel(cx, IRQ_HPU_TO_APU_ACK, &cx->scb->hpu2apu_irq_ack); |
52 | writel(IRQ_PPU_TO_HPU, &cx->scb->ppu2hpu_irq); | 53 | cx18_writel(cx, IRQ_PPU_TO_HPU, &cx->scb->ppu2hpu_irq); |
53 | writel(IRQ_HPU_TO_PPU_ACK, &cx->scb->hpu2ppu_irq_ack); | 54 | cx18_writel(cx, IRQ_HPU_TO_PPU_ACK, &cx->scb->hpu2ppu_irq_ack); |
54 | writel(IRQ_EPU_TO_HPU, &cx->scb->epu2hpu_irq); | 55 | cx18_writel(cx, IRQ_EPU_TO_HPU, &cx->scb->epu2hpu_irq); |
55 | writel(IRQ_HPU_TO_EPU_ACK, &cx->scb->hpu2epu_irq_ack); | 56 | cx18_writel(cx, IRQ_HPU_TO_EPU_ACK, &cx->scb->hpu2epu_irq_ack); |
56 | 57 | ||
57 | writel(IRQ_CPU_TO_PPU, &cx->scb->cpu2ppu_irq); | 58 | cx18_writel(cx, IRQ_CPU_TO_PPU, &cx->scb->cpu2ppu_irq); |
58 | writel(IRQ_PPU_TO_CPU_ACK, &cx->scb->ppu2cpu_irq_ack); | 59 | cx18_writel(cx, IRQ_PPU_TO_CPU_ACK, &cx->scb->ppu2cpu_irq_ack); |
59 | writel(IRQ_APU_TO_PPU, &cx->scb->apu2ppu_irq); | 60 | cx18_writel(cx, IRQ_APU_TO_PPU, &cx->scb->apu2ppu_irq); |
60 | writel(IRQ_PPU_TO_APU_ACK, &cx->scb->ppu2apu_irq_ack); | 61 | cx18_writel(cx, IRQ_PPU_TO_APU_ACK, &cx->scb->ppu2apu_irq_ack); |
61 | writel(IRQ_HPU_TO_PPU, &cx->scb->hpu2ppu_irq); | 62 | cx18_writel(cx, IRQ_HPU_TO_PPU, &cx->scb->hpu2ppu_irq); |
62 | writel(IRQ_PPU_TO_HPU_ACK, &cx->scb->ppu2hpu_irq_ack); | 63 | cx18_writel(cx, IRQ_PPU_TO_HPU_ACK, &cx->scb->ppu2hpu_irq_ack); |
63 | writel(IRQ_EPU_TO_PPU, &cx->scb->epu2ppu_irq); | 64 | cx18_writel(cx, IRQ_EPU_TO_PPU, &cx->scb->epu2ppu_irq); |
64 | writel(IRQ_PPU_TO_EPU_ACK, &cx->scb->ppu2epu_irq_ack); | 65 | cx18_writel(cx, IRQ_PPU_TO_EPU_ACK, &cx->scb->ppu2epu_irq_ack); |
65 | 66 | ||
66 | writel(IRQ_CPU_TO_EPU, &cx->scb->cpu2epu_irq); | 67 | cx18_writel(cx, IRQ_CPU_TO_EPU, &cx->scb->cpu2epu_irq); |
67 | writel(IRQ_EPU_TO_CPU_ACK, &cx->scb->epu2cpu_irq_ack); | 68 | cx18_writel(cx, IRQ_EPU_TO_CPU_ACK, &cx->scb->epu2cpu_irq_ack); |
68 | writel(IRQ_APU_TO_EPU, &cx->scb->apu2epu_irq); | 69 | cx18_writel(cx, IRQ_APU_TO_EPU, &cx->scb->apu2epu_irq); |
69 | writel(IRQ_EPU_TO_APU_ACK, &cx->scb->epu2apu_irq_ack); | 70 | cx18_writel(cx, IRQ_EPU_TO_APU_ACK, &cx->scb->epu2apu_irq_ack); |
70 | writel(IRQ_HPU_TO_EPU, &cx->scb->hpu2epu_irq); | 71 | cx18_writel(cx, IRQ_HPU_TO_EPU, &cx->scb->hpu2epu_irq); |
71 | writel(IRQ_EPU_TO_HPU_ACK, &cx->scb->epu2hpu_irq_ack); | 72 | cx18_writel(cx, IRQ_EPU_TO_HPU_ACK, &cx->scb->epu2hpu_irq_ack); |
72 | writel(IRQ_PPU_TO_EPU, &cx->scb->ppu2epu_irq); | 73 | cx18_writel(cx, IRQ_PPU_TO_EPU, &cx->scb->ppu2epu_irq); |
73 | writel(IRQ_EPU_TO_PPU_ACK, &cx->scb->epu2ppu_irq_ack); | 74 | cx18_writel(cx, IRQ_EPU_TO_PPU_ACK, &cx->scb->epu2ppu_irq_ack); |
74 | 75 | ||
75 | writel(SCB_OFFSET + offsetof(struct cx18_scb, apu2cpu_mb), | 76 | cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, apu2cpu_mb), |
76 | &cx->scb->apu2cpu_mb_offset); | 77 | &cx->scb->apu2cpu_mb_offset); |
77 | writel(SCB_OFFSET + offsetof(struct cx18_scb, hpu2cpu_mb), | 78 | cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, hpu2cpu_mb), |
78 | &cx->scb->hpu2cpu_mb_offset); | 79 | &cx->scb->hpu2cpu_mb_offset); |
79 | writel(SCB_OFFSET + offsetof(struct cx18_scb, ppu2cpu_mb), | 80 | cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, ppu2cpu_mb), |
80 | &cx->scb->ppu2cpu_mb_offset); | 81 | &cx->scb->ppu2cpu_mb_offset); |
81 | writel(SCB_OFFSET + offsetof(struct cx18_scb, epu2cpu_mb), | 82 | cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, epu2cpu_mb), |
82 | &cx->scb->epu2cpu_mb_offset); | 83 | &cx->scb->epu2cpu_mb_offset); |
83 | writel(SCB_OFFSET + offsetof(struct cx18_scb, cpu2apu_mb), | 84 | cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, cpu2apu_mb), |
84 | &cx->scb->cpu2apu_mb_offset); | 85 | &cx->scb->cpu2apu_mb_offset); |
85 | writel(SCB_OFFSET + offsetof(struct cx18_scb, hpu2apu_mb), | 86 | cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, hpu2apu_mb), |
86 | &cx->scb->hpu2apu_mb_offset); | 87 | &cx->scb->hpu2apu_mb_offset); |
87 | writel(SCB_OFFSET + offsetof(struct cx18_scb, ppu2apu_mb), | 88 | cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, ppu2apu_mb), |
88 | &cx->scb->ppu2apu_mb_offset); | 89 | &cx->scb->ppu2apu_mb_offset); |
89 | writel(SCB_OFFSET + offsetof(struct cx18_scb, epu2apu_mb), | 90 | cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, epu2apu_mb), |
90 | &cx->scb->epu2apu_mb_offset); | 91 | &cx->scb->epu2apu_mb_offset); |
91 | writel(SCB_OFFSET + offsetof(struct cx18_scb, cpu2hpu_mb), | 92 | cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, cpu2hpu_mb), |
92 | &cx->scb->cpu2hpu_mb_offset); | 93 | &cx->scb->cpu2hpu_mb_offset); |
93 | writel(SCB_OFFSET + offsetof(struct cx18_scb, apu2hpu_mb), | 94 | cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, apu2hpu_mb), |
94 | &cx->scb->apu2hpu_mb_offset); | 95 | &cx->scb->apu2hpu_mb_offset); |
95 | writel(SCB_OFFSET + offsetof(struct cx18_scb, ppu2hpu_mb), | 96 | cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, ppu2hpu_mb), |
96 | &cx->scb->ppu2hpu_mb_offset); | 97 | &cx->scb->ppu2hpu_mb_offset); |
97 | writel(SCB_OFFSET + offsetof(struct cx18_scb, epu2hpu_mb), | 98 | cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, epu2hpu_mb), |
98 | &cx->scb->epu2hpu_mb_offset); | 99 | &cx->scb->epu2hpu_mb_offset); |
99 | writel(SCB_OFFSET + offsetof(struct cx18_scb, cpu2ppu_mb), | 100 | cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, cpu2ppu_mb), |
100 | &cx->scb->cpu2ppu_mb_offset); | 101 | &cx->scb->cpu2ppu_mb_offset); |
101 | writel(SCB_OFFSET + offsetof(struct cx18_scb, apu2ppu_mb), | 102 | cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, apu2ppu_mb), |
102 | &cx->scb->apu2ppu_mb_offset); | 103 | &cx->scb->apu2ppu_mb_offset); |
103 | writel(SCB_OFFSET + offsetof(struct cx18_scb, hpu2ppu_mb), | 104 | cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, hpu2ppu_mb), |
104 | &cx->scb->hpu2ppu_mb_offset); | 105 | &cx->scb->hpu2ppu_mb_offset); |
105 | writel(SCB_OFFSET + offsetof(struct cx18_scb, epu2ppu_mb), | 106 | cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, epu2ppu_mb), |
106 | &cx->scb->epu2ppu_mb_offset); | 107 | &cx->scb->epu2ppu_mb_offset); |
107 | writel(SCB_OFFSET + offsetof(struct cx18_scb, cpu2epu_mb), | 108 | cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, cpu2epu_mb), |
108 | &cx->scb->cpu2epu_mb_offset); | 109 | &cx->scb->cpu2epu_mb_offset); |
109 | writel(SCB_OFFSET + offsetof(struct cx18_scb, apu2epu_mb), | 110 | cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, apu2epu_mb), |
110 | &cx->scb->apu2epu_mb_offset); | 111 | &cx->scb->apu2epu_mb_offset); |
111 | writel(SCB_OFFSET + offsetof(struct cx18_scb, hpu2epu_mb), | 112 | cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, hpu2epu_mb), |
112 | &cx->scb->hpu2epu_mb_offset); | 113 | &cx->scb->hpu2epu_mb_offset); |
113 | writel(SCB_OFFSET + offsetof(struct cx18_scb, ppu2epu_mb), | 114 | cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, ppu2epu_mb), |
114 | &cx->scb->ppu2epu_mb_offset); | 115 | &cx->scb->ppu2epu_mb_offset); |
115 | 116 | ||
116 | writel(SCB_OFFSET + offsetof(struct cx18_scb, cpu_state), | 117 | cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, cpu_state), |
117 | &cx->scb->ipc_offset); | 118 | &cx->scb->ipc_offset); |
118 | 119 | ||
119 | writel(1, &cx->scb->hpu_state); | 120 | cx18_writel(cx, 1, &cx->scb->hpu_state); |
120 | writel(1, &cx->scb->epu_state); | 121 | cx18_writel(cx, 1, &cx->scb->epu_state); |
121 | } | 122 | } |
122 | 123 |
drivers/media/video/cx18/cx18-streams.c
1 | /* | 1 | /* |
2 | * cx18 init/start/stop/exit stream functions | 2 | * cx18 init/start/stop/exit stream functions |
3 | * | 3 | * |
4 | * Derived from ivtv-streams.c | 4 | * Derived from ivtv-streams.c |
5 | * | 5 | * |
6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | 6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> |
7 | * | 7 | * |
8 | * This program is free software; you can redistribute it and/or modify | 8 | * This program is free software; you can redistribute it and/or modify |
9 | * it under the terms of the GNU General Public License as published by | 9 | * it under the terms of the GNU General Public License as published by |
10 | * the Free Software Foundation; either version 2 of the License, or | 10 | * the Free Software Foundation; either version 2 of the License, or |
11 | * (at your option) any later version. | 11 | * (at your option) any later version. |
12 | * | 12 | * |
13 | * This program is distributed in the hope that it will be useful, | 13 | * This program is distributed in the hope that it will be useful, |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | * GNU General Public License for more details. | 16 | * GNU General Public License for more details. |
17 | * | 17 | * |
18 | * You should have received a copy of the GNU General Public License | 18 | * You should have received a copy of the GNU General Public License |
19 | * along with this program; if not, write to the Free Software | 19 | * along with this program; if not, write to the Free Software |
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | 20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
21 | * 02111-1307 USA | 21 | * 02111-1307 USA |
22 | */ | 22 | */ |
23 | 23 | ||
24 | #include "cx18-driver.h" | 24 | #include "cx18-driver.h" |
25 | #include "cx18-io.h" | ||
25 | #include "cx18-fileops.h" | 26 | #include "cx18-fileops.h" |
26 | #include "cx18-mailbox.h" | 27 | #include "cx18-mailbox.h" |
27 | #include "cx18-i2c.h" | 28 | #include "cx18-i2c.h" |
28 | #include "cx18-queue.h" | 29 | #include "cx18-queue.h" |
29 | #include "cx18-ioctl.h" | 30 | #include "cx18-ioctl.h" |
30 | #include "cx18-streams.h" | 31 | #include "cx18-streams.h" |
31 | #include "cx18-cards.h" | 32 | #include "cx18-cards.h" |
32 | #include "cx18-scb.h" | 33 | #include "cx18-scb.h" |
33 | #include "cx18-av-core.h" | 34 | #include "cx18-av-core.h" |
34 | #include "cx18-dvb.h" | 35 | #include "cx18-dvb.h" |
35 | 36 | ||
36 | #define CX18_DSP0_INTERRUPT_MASK 0xd0004C | 37 | #define CX18_DSP0_INTERRUPT_MASK 0xd0004C |
37 | 38 | ||
38 | static struct file_operations cx18_v4l2_enc_fops = { | 39 | static struct file_operations cx18_v4l2_enc_fops = { |
39 | .owner = THIS_MODULE, | 40 | .owner = THIS_MODULE, |
40 | .read = cx18_v4l2_read, | 41 | .read = cx18_v4l2_read, |
41 | .open = cx18_v4l2_open, | 42 | .open = cx18_v4l2_open, |
42 | /* FIXME change to video_ioctl2 if serialization lock can be removed */ | 43 | /* FIXME change to video_ioctl2 if serialization lock can be removed */ |
43 | .ioctl = cx18_v4l2_ioctl, | 44 | .ioctl = cx18_v4l2_ioctl, |
44 | .compat_ioctl = v4l_compat_ioctl32, | 45 | .compat_ioctl = v4l_compat_ioctl32, |
45 | .release = cx18_v4l2_close, | 46 | .release = cx18_v4l2_close, |
46 | .poll = cx18_v4l2_enc_poll, | 47 | .poll = cx18_v4l2_enc_poll, |
47 | }; | 48 | }; |
48 | 49 | ||
49 | /* offset from 0 to register ts v4l2 minors on */ | 50 | /* offset from 0 to register ts v4l2 minors on */ |
50 | #define CX18_V4L2_ENC_TS_OFFSET 16 | 51 | #define CX18_V4L2_ENC_TS_OFFSET 16 |
51 | /* offset from 0 to register pcm v4l2 minors on */ | 52 | /* offset from 0 to register pcm v4l2 minors on */ |
52 | #define CX18_V4L2_ENC_PCM_OFFSET 24 | 53 | #define CX18_V4L2_ENC_PCM_OFFSET 24 |
53 | /* offset from 0 to register yuv v4l2 minors on */ | 54 | /* offset from 0 to register yuv v4l2 minors on */ |
54 | #define CX18_V4L2_ENC_YUV_OFFSET 32 | 55 | #define CX18_V4L2_ENC_YUV_OFFSET 32 |
55 | 56 | ||
56 | static struct { | 57 | static struct { |
57 | const char *name; | 58 | const char *name; |
58 | int vfl_type; | 59 | int vfl_type; |
59 | int minor_offset; | 60 | int minor_offset; |
60 | int dma; | 61 | int dma; |
61 | enum v4l2_buf_type buf_type; | 62 | enum v4l2_buf_type buf_type; |
62 | struct file_operations *fops; | 63 | struct file_operations *fops; |
63 | } cx18_stream_info[] = { | 64 | } cx18_stream_info[] = { |
64 | { /* CX18_ENC_STREAM_TYPE_MPG */ | 65 | { /* CX18_ENC_STREAM_TYPE_MPG */ |
65 | "encoder MPEG", | 66 | "encoder MPEG", |
66 | VFL_TYPE_GRABBER, 0, | 67 | VFL_TYPE_GRABBER, 0, |
67 | PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VIDEO_CAPTURE, | 68 | PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VIDEO_CAPTURE, |
68 | &cx18_v4l2_enc_fops | 69 | &cx18_v4l2_enc_fops |
69 | }, | 70 | }, |
70 | { /* CX18_ENC_STREAM_TYPE_TS */ | 71 | { /* CX18_ENC_STREAM_TYPE_TS */ |
71 | "TS", | 72 | "TS", |
72 | VFL_TYPE_GRABBER, -1, | 73 | VFL_TYPE_GRABBER, -1, |
73 | PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VIDEO_CAPTURE, | 74 | PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VIDEO_CAPTURE, |
74 | &cx18_v4l2_enc_fops | 75 | &cx18_v4l2_enc_fops |
75 | }, | 76 | }, |
76 | { /* CX18_ENC_STREAM_TYPE_YUV */ | 77 | { /* CX18_ENC_STREAM_TYPE_YUV */ |
77 | "encoder YUV", | 78 | "encoder YUV", |
78 | VFL_TYPE_GRABBER, CX18_V4L2_ENC_YUV_OFFSET, | 79 | VFL_TYPE_GRABBER, CX18_V4L2_ENC_YUV_OFFSET, |
79 | PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VIDEO_CAPTURE, | 80 | PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VIDEO_CAPTURE, |
80 | &cx18_v4l2_enc_fops | 81 | &cx18_v4l2_enc_fops |
81 | }, | 82 | }, |
82 | { /* CX18_ENC_STREAM_TYPE_VBI */ | 83 | { /* CX18_ENC_STREAM_TYPE_VBI */ |
83 | "encoder VBI", | 84 | "encoder VBI", |
84 | VFL_TYPE_VBI, 0, | 85 | VFL_TYPE_VBI, 0, |
85 | PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VBI_CAPTURE, | 86 | PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VBI_CAPTURE, |
86 | &cx18_v4l2_enc_fops | 87 | &cx18_v4l2_enc_fops |
87 | }, | 88 | }, |
88 | { /* CX18_ENC_STREAM_TYPE_PCM */ | 89 | { /* CX18_ENC_STREAM_TYPE_PCM */ |
89 | "encoder PCM audio", | 90 | "encoder PCM audio", |
90 | VFL_TYPE_GRABBER, CX18_V4L2_ENC_PCM_OFFSET, | 91 | VFL_TYPE_GRABBER, CX18_V4L2_ENC_PCM_OFFSET, |
91 | PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_PRIVATE, | 92 | PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_PRIVATE, |
92 | &cx18_v4l2_enc_fops | 93 | &cx18_v4l2_enc_fops |
93 | }, | 94 | }, |
94 | { /* CX18_ENC_STREAM_TYPE_IDX */ | 95 | { /* CX18_ENC_STREAM_TYPE_IDX */ |
95 | "encoder IDX", | 96 | "encoder IDX", |
96 | VFL_TYPE_GRABBER, -1, | 97 | VFL_TYPE_GRABBER, -1, |
97 | PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VIDEO_CAPTURE, | 98 | PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VIDEO_CAPTURE, |
98 | &cx18_v4l2_enc_fops | 99 | &cx18_v4l2_enc_fops |
99 | }, | 100 | }, |
100 | { /* CX18_ENC_STREAM_TYPE_RAD */ | 101 | { /* CX18_ENC_STREAM_TYPE_RAD */ |
101 | "encoder radio", | 102 | "encoder radio", |
102 | VFL_TYPE_RADIO, 0, | 103 | VFL_TYPE_RADIO, 0, |
103 | PCI_DMA_NONE, V4L2_BUF_TYPE_PRIVATE, | 104 | PCI_DMA_NONE, V4L2_BUF_TYPE_PRIVATE, |
104 | &cx18_v4l2_enc_fops | 105 | &cx18_v4l2_enc_fops |
105 | }, | 106 | }, |
106 | }; | 107 | }; |
107 | 108 | ||
108 | static void cx18_stream_init(struct cx18 *cx, int type) | 109 | static void cx18_stream_init(struct cx18 *cx, int type) |
109 | { | 110 | { |
110 | struct cx18_stream *s = &cx->streams[type]; | 111 | struct cx18_stream *s = &cx->streams[type]; |
111 | struct video_device *dev = s->v4l2dev; | 112 | struct video_device *dev = s->v4l2dev; |
112 | u32 max_size = cx->options.megabytes[type] * 1024 * 1024; | 113 | u32 max_size = cx->options.megabytes[type] * 1024 * 1024; |
113 | 114 | ||
114 | /* we need to keep v4l2dev, so restore it afterwards */ | 115 | /* we need to keep v4l2dev, so restore it afterwards */ |
115 | memset(s, 0, sizeof(*s)); | 116 | memset(s, 0, sizeof(*s)); |
116 | s->v4l2dev = dev; | 117 | s->v4l2dev = dev; |
117 | 118 | ||
118 | /* initialize cx18_stream fields */ | 119 | /* initialize cx18_stream fields */ |
119 | s->cx = cx; | 120 | s->cx = cx; |
120 | s->type = type; | 121 | s->type = type; |
121 | s->name = cx18_stream_info[type].name; | 122 | s->name = cx18_stream_info[type].name; |
122 | s->handle = CX18_INVALID_TASK_HANDLE; | 123 | s->handle = CX18_INVALID_TASK_HANDLE; |
123 | 124 | ||
124 | s->dma = cx18_stream_info[type].dma; | 125 | s->dma = cx18_stream_info[type].dma; |
125 | s->buf_size = cx->stream_buf_size[type]; | 126 | s->buf_size = cx->stream_buf_size[type]; |
126 | if (s->buf_size) | 127 | if (s->buf_size) |
127 | s->buffers = max_size / s->buf_size; | 128 | s->buffers = max_size / s->buf_size; |
128 | if (s->buffers > 63) { | 129 | if (s->buffers > 63) { |
129 | /* Each stream has a maximum of 63 buffers, | 130 | /* Each stream has a maximum of 63 buffers, |
130 | ensure we do not exceed that. */ | 131 | ensure we do not exceed that. */ |
131 | s->buffers = 63; | 132 | s->buffers = 63; |
132 | s->buf_size = (max_size / s->buffers) & ~0xfff; | 133 | s->buf_size = (max_size / s->buffers) & ~0xfff; |
133 | } | 134 | } |
134 | spin_lock_init(&s->qlock); | 135 | spin_lock_init(&s->qlock); |
135 | init_waitqueue_head(&s->waitq); | 136 | init_waitqueue_head(&s->waitq); |
136 | s->id = -1; | 137 | s->id = -1; |
137 | cx18_queue_init(&s->q_free); | 138 | cx18_queue_init(&s->q_free); |
138 | cx18_queue_init(&s->q_full); | 139 | cx18_queue_init(&s->q_full); |
139 | cx18_queue_init(&s->q_io); | 140 | cx18_queue_init(&s->q_io); |
140 | } | 141 | } |
141 | 142 | ||
142 | static int cx18_prep_dev(struct cx18 *cx, int type) | 143 | static int cx18_prep_dev(struct cx18 *cx, int type) |
143 | { | 144 | { |
144 | struct cx18_stream *s = &cx->streams[type]; | 145 | struct cx18_stream *s = &cx->streams[type]; |
145 | u32 cap = cx->v4l2_cap; | 146 | u32 cap = cx->v4l2_cap; |
146 | int minor_offset = cx18_stream_info[type].minor_offset; | 147 | int minor_offset = cx18_stream_info[type].minor_offset; |
147 | int minor; | 148 | int minor; |
148 | 149 | ||
149 | /* These four fields are always initialized. If v4l2dev == NULL, then | 150 | /* These four fields are always initialized. If v4l2dev == NULL, then |
150 | this stream is not in use. In that case no other fields but these | 151 | this stream is not in use. In that case no other fields but these |
151 | four can be used. */ | 152 | four can be used. */ |
152 | s->v4l2dev = NULL; | 153 | s->v4l2dev = NULL; |
153 | s->cx = cx; | 154 | s->cx = cx; |
154 | s->type = type; | 155 | s->type = type; |
155 | s->name = cx18_stream_info[type].name; | 156 | s->name = cx18_stream_info[type].name; |
156 | 157 | ||
157 | /* Check whether the radio is supported */ | 158 | /* Check whether the radio is supported */ |
158 | if (type == CX18_ENC_STREAM_TYPE_RAD && !(cap & V4L2_CAP_RADIO)) | 159 | if (type == CX18_ENC_STREAM_TYPE_RAD && !(cap & V4L2_CAP_RADIO)) |
159 | return 0; | 160 | return 0; |
160 | 161 | ||
161 | /* Check whether VBI is supported */ | 162 | /* Check whether VBI is supported */ |
162 | if (type == CX18_ENC_STREAM_TYPE_VBI && | 163 | if (type == CX18_ENC_STREAM_TYPE_VBI && |
163 | !(cap & (V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE))) | 164 | !(cap & (V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE))) |
164 | return 0; | 165 | return 0; |
165 | 166 | ||
166 | /* card number + user defined offset + device offset */ | 167 | /* card number + user defined offset + device offset */ |
167 | minor = cx->num + cx18_first_minor + minor_offset; | 168 | minor = cx->num + cx18_first_minor + minor_offset; |
168 | 169 | ||
169 | /* User explicitly selected 0 buffers for these streams, so don't | 170 | /* User explicitly selected 0 buffers for these streams, so don't |
170 | create them. */ | 171 | create them. */ |
171 | if (cx18_stream_info[type].dma != PCI_DMA_NONE && | 172 | if (cx18_stream_info[type].dma != PCI_DMA_NONE && |
172 | cx->options.megabytes[type] == 0) { | 173 | cx->options.megabytes[type] == 0) { |
173 | CX18_INFO("Disabled %s device\n", cx18_stream_info[type].name); | 174 | CX18_INFO("Disabled %s device\n", cx18_stream_info[type].name); |
174 | return 0; | 175 | return 0; |
175 | } | 176 | } |
176 | 177 | ||
177 | cx18_stream_init(cx, type); | 178 | cx18_stream_init(cx, type); |
178 | 179 | ||
179 | if (minor_offset == -1) | 180 | if (minor_offset == -1) |
180 | return 0; | 181 | return 0; |
181 | 182 | ||
182 | /* allocate and initialize the v4l2 video device structure */ | 183 | /* allocate and initialize the v4l2 video device structure */ |
183 | s->v4l2dev = video_device_alloc(); | 184 | s->v4l2dev = video_device_alloc(); |
184 | if (s->v4l2dev == NULL) { | 185 | if (s->v4l2dev == NULL) { |
185 | CX18_ERR("Couldn't allocate v4l2 video_device for %s\n", | 186 | CX18_ERR("Couldn't allocate v4l2 video_device for %s\n", |
186 | s->name); | 187 | s->name); |
187 | return -ENOMEM; | 188 | return -ENOMEM; |
188 | } | 189 | } |
189 | 190 | ||
190 | snprintf(s->v4l2dev->name, sizeof(s->v4l2dev->name), "cx18-%d", | 191 | snprintf(s->v4l2dev->name, sizeof(s->v4l2dev->name), "cx18-%d", |
191 | cx->num); | 192 | cx->num); |
192 | 193 | ||
193 | s->v4l2dev->minor = minor; | 194 | s->v4l2dev->minor = minor; |
194 | s->v4l2dev->parent = &cx->dev->dev; | 195 | s->v4l2dev->parent = &cx->dev->dev; |
195 | s->v4l2dev->fops = cx18_stream_info[type].fops; | 196 | s->v4l2dev->fops = cx18_stream_info[type].fops; |
196 | s->v4l2dev->release = video_device_release; | 197 | s->v4l2dev->release = video_device_release; |
197 | s->v4l2dev->tvnorms = V4L2_STD_ALL; | 198 | s->v4l2dev->tvnorms = V4L2_STD_ALL; |
198 | cx18_set_funcs(s->v4l2dev); | 199 | cx18_set_funcs(s->v4l2dev); |
199 | return 0; | 200 | return 0; |
200 | } | 201 | } |
201 | 202 | ||
202 | /* Initialize v4l2 variables and register v4l2 devices */ | 203 | /* Initialize v4l2 variables and register v4l2 devices */ |
203 | int cx18_streams_setup(struct cx18 *cx) | 204 | int cx18_streams_setup(struct cx18 *cx) |
204 | { | 205 | { |
205 | int type; | 206 | int type; |
206 | 207 | ||
207 | /* Setup V4L2 Devices */ | 208 | /* Setup V4L2 Devices */ |
208 | for (type = 0; type < CX18_MAX_STREAMS; type++) { | 209 | for (type = 0; type < CX18_MAX_STREAMS; type++) { |
209 | /* Prepare device */ | 210 | /* Prepare device */ |
210 | if (cx18_prep_dev(cx, type)) | 211 | if (cx18_prep_dev(cx, type)) |
211 | break; | 212 | break; |
212 | 213 | ||
213 | /* Allocate Stream */ | 214 | /* Allocate Stream */ |
214 | if (cx18_stream_alloc(&cx->streams[type])) | 215 | if (cx18_stream_alloc(&cx->streams[type])) |
215 | break; | 216 | break; |
216 | } | 217 | } |
217 | if (type == CX18_MAX_STREAMS) | 218 | if (type == CX18_MAX_STREAMS) |
218 | return 0; | 219 | return 0; |
219 | 220 | ||
220 | /* One or more streams could not be initialized. Clean 'em all up. */ | 221 | /* One or more streams could not be initialized. Clean 'em all up. */ |
221 | cx18_streams_cleanup(cx, 0); | 222 | cx18_streams_cleanup(cx, 0); |
222 | return -ENOMEM; | 223 | return -ENOMEM; |
223 | } | 224 | } |
224 | 225 | ||
225 | static int cx18_reg_dev(struct cx18 *cx, int type) | 226 | static int cx18_reg_dev(struct cx18 *cx, int type) |
226 | { | 227 | { |
227 | struct cx18_stream *s = &cx->streams[type]; | 228 | struct cx18_stream *s = &cx->streams[type]; |
228 | int vfl_type = cx18_stream_info[type].vfl_type; | 229 | int vfl_type = cx18_stream_info[type].vfl_type; |
229 | int minor; | 230 | int minor; |
230 | 231 | ||
231 | /* TODO: Shouldn't this be a VFL_TYPE_TRANSPORT or something? | 232 | /* TODO: Shouldn't this be a VFL_TYPE_TRANSPORT or something? |
232 | * We need a VFL_TYPE_TS defined. | 233 | * We need a VFL_TYPE_TS defined. |
233 | */ | 234 | */ |
234 | if (strcmp("TS", s->name) == 0) { | 235 | if (strcmp("TS", s->name) == 0) { |
235 | /* just return if no DVB is supported */ | 236 | /* just return if no DVB is supported */ |
236 | if ((cx->card->hw_all & CX18_HW_DVB) == 0) | 237 | if ((cx->card->hw_all & CX18_HW_DVB) == 0) |
237 | return 0; | 238 | return 0; |
238 | if (cx18_dvb_register(s) < 0) { | 239 | if (cx18_dvb_register(s) < 0) { |
239 | CX18_ERR("DVB failed to register\n"); | 240 | CX18_ERR("DVB failed to register\n"); |
240 | return -EINVAL; | 241 | return -EINVAL; |
241 | } | 242 | } |
242 | } | 243 | } |
243 | 244 | ||
244 | if (s->v4l2dev == NULL) | 245 | if (s->v4l2dev == NULL) |
245 | return 0; | 246 | return 0; |
246 | 247 | ||
247 | minor = s->v4l2dev->minor; | 248 | minor = s->v4l2dev->minor; |
248 | 249 | ||
249 | /* Register device. First try the desired minor, then any free one. */ | 250 | /* Register device. First try the desired minor, then any free one. */ |
250 | if (video_register_device(s->v4l2dev, vfl_type, minor) && | 251 | if (video_register_device(s->v4l2dev, vfl_type, minor) && |
251 | video_register_device(s->v4l2dev, vfl_type, -1)) { | 252 | video_register_device(s->v4l2dev, vfl_type, -1)) { |
252 | CX18_ERR("Couldn't register v4l2 device for %s minor %d\n", | 253 | CX18_ERR("Couldn't register v4l2 device for %s minor %d\n", |
253 | s->name, minor); | 254 | s->name, minor); |
254 | video_device_release(s->v4l2dev); | 255 | video_device_release(s->v4l2dev); |
255 | s->v4l2dev = NULL; | 256 | s->v4l2dev = NULL; |
256 | return -ENOMEM; | 257 | return -ENOMEM; |
257 | } | 258 | } |
258 | minor = s->v4l2dev->minor; | 259 | minor = s->v4l2dev->minor; |
259 | 260 | ||
260 | switch (vfl_type) { | 261 | switch (vfl_type) { |
261 | case VFL_TYPE_GRABBER: | 262 | case VFL_TYPE_GRABBER: |
262 | CX18_INFO("Registered device video%d for %s (%d MB)\n", | 263 | CX18_INFO("Registered device video%d for %s (%d MB)\n", |
263 | minor, s->name, cx->options.megabytes[type]); | 264 | minor, s->name, cx->options.megabytes[type]); |
264 | break; | 265 | break; |
265 | 266 | ||
266 | case VFL_TYPE_RADIO: | 267 | case VFL_TYPE_RADIO: |
267 | CX18_INFO("Registered device radio%d for %s\n", | 268 | CX18_INFO("Registered device radio%d for %s\n", |
268 | minor - MINOR_VFL_TYPE_RADIO_MIN, s->name); | 269 | minor - MINOR_VFL_TYPE_RADIO_MIN, s->name); |
269 | break; | 270 | break; |
270 | 271 | ||
271 | case VFL_TYPE_VBI: | 272 | case VFL_TYPE_VBI: |
272 | if (cx->options.megabytes[type]) | 273 | if (cx->options.megabytes[type]) |
273 | CX18_INFO("Registered device vbi%d for %s (%d MB)\n", | 274 | CX18_INFO("Registered device vbi%d for %s (%d MB)\n", |
274 | minor - MINOR_VFL_TYPE_VBI_MIN, | 275 | minor - MINOR_VFL_TYPE_VBI_MIN, |
275 | s->name, cx->options.megabytes[type]); | 276 | s->name, cx->options.megabytes[type]); |
276 | else | 277 | else |
277 | CX18_INFO("Registered device vbi%d for %s\n", | 278 | CX18_INFO("Registered device vbi%d for %s\n", |
278 | minor - MINOR_VFL_TYPE_VBI_MIN, s->name); | 279 | minor - MINOR_VFL_TYPE_VBI_MIN, s->name); |
279 | break; | 280 | break; |
280 | } | 281 | } |
281 | 282 | ||
282 | return 0; | 283 | return 0; |
283 | } | 284 | } |
284 | 285 | ||
285 | /* Register v4l2 devices */ | 286 | /* Register v4l2 devices */ |
286 | int cx18_streams_register(struct cx18 *cx) | 287 | int cx18_streams_register(struct cx18 *cx) |
287 | { | 288 | { |
288 | int type; | 289 | int type; |
289 | int err = 0; | 290 | int err = 0; |
290 | 291 | ||
291 | /* Register V4L2 devices */ | 292 | /* Register V4L2 devices */ |
292 | for (type = 0; type < CX18_MAX_STREAMS; type++) | 293 | for (type = 0; type < CX18_MAX_STREAMS; type++) |
293 | err |= cx18_reg_dev(cx, type); | 294 | err |= cx18_reg_dev(cx, type); |
294 | 295 | ||
295 | if (err == 0) | 296 | if (err == 0) |
296 | return 0; | 297 | return 0; |
297 | 298 | ||
298 | /* One or more streams could not be initialized. Clean 'em all up. */ | 299 | /* One or more streams could not be initialized. Clean 'em all up. */ |
299 | cx18_streams_cleanup(cx, 1); | 300 | cx18_streams_cleanup(cx, 1); |
300 | return -ENOMEM; | 301 | return -ENOMEM; |
301 | } | 302 | } |
302 | 303 | ||
303 | /* Unregister v4l2 devices */ | 304 | /* Unregister v4l2 devices */ |
304 | void cx18_streams_cleanup(struct cx18 *cx, int unregister) | 305 | void cx18_streams_cleanup(struct cx18 *cx, int unregister) |
305 | { | 306 | { |
306 | struct video_device *vdev; | 307 | struct video_device *vdev; |
307 | int type; | 308 | int type; |
308 | 309 | ||
309 | /* Teardown all streams */ | 310 | /* Teardown all streams */ |
310 | for (type = 0; type < CX18_MAX_STREAMS; type++) { | 311 | for (type = 0; type < CX18_MAX_STREAMS; type++) { |
311 | if (cx->streams[type].dvb.enabled) { | 312 | if (cx->streams[type].dvb.enabled) { |
312 | cx18_dvb_unregister(&cx->streams[type]); | 313 | cx18_dvb_unregister(&cx->streams[type]); |
313 | cx->streams[type].dvb.enabled = false; | 314 | cx->streams[type].dvb.enabled = false; |
314 | } | 315 | } |
315 | 316 | ||
316 | vdev = cx->streams[type].v4l2dev; | 317 | vdev = cx->streams[type].v4l2dev; |
317 | 318 | ||
318 | cx->streams[type].v4l2dev = NULL; | 319 | cx->streams[type].v4l2dev = NULL; |
319 | if (vdev == NULL) | 320 | if (vdev == NULL) |
320 | continue; | 321 | continue; |
321 | 322 | ||
322 | cx18_stream_free(&cx->streams[type]); | 323 | cx18_stream_free(&cx->streams[type]); |
323 | 324 | ||
324 | /* Unregister or release device */ | 325 | /* Unregister or release device */ |
325 | if (unregister) | 326 | if (unregister) |
326 | video_unregister_device(vdev); | 327 | video_unregister_device(vdev); |
327 | else | 328 | else |
328 | video_device_release(vdev); | 329 | video_device_release(vdev); |
329 | } | 330 | } |
330 | } | 331 | } |
331 | 332 | ||
332 | static void cx18_vbi_setup(struct cx18_stream *s) | 333 | static void cx18_vbi_setup(struct cx18_stream *s) |
333 | { | 334 | { |
334 | struct cx18 *cx = s->cx; | 335 | struct cx18 *cx = s->cx; |
335 | int raw = cx->vbi.sliced_in->service_set == 0; | 336 | int raw = cx->vbi.sliced_in->service_set == 0; |
336 | u32 data[CX2341X_MBOX_MAX_DATA]; | 337 | u32 data[CX2341X_MBOX_MAX_DATA]; |
337 | int lines; | 338 | int lines; |
338 | 339 | ||
339 | if (cx->is_60hz) { | 340 | if (cx->is_60hz) { |
340 | cx->vbi.count = 12; | 341 | cx->vbi.count = 12; |
341 | cx->vbi.start[0] = 10; | 342 | cx->vbi.start[0] = 10; |
342 | cx->vbi.start[1] = 273; | 343 | cx->vbi.start[1] = 273; |
343 | } else { /* PAL/SECAM */ | 344 | } else { /* PAL/SECAM */ |
344 | cx->vbi.count = 18; | 345 | cx->vbi.count = 18; |
345 | cx->vbi.start[0] = 6; | 346 | cx->vbi.start[0] = 6; |
346 | cx->vbi.start[1] = 318; | 347 | cx->vbi.start[1] = 318; |
347 | } | 348 | } |
348 | 349 | ||
349 | /* setup VBI registers */ | 350 | /* setup VBI registers */ |
350 | cx18_av_cmd(cx, VIDIOC_S_FMT, &cx->vbi.in); | 351 | cx18_av_cmd(cx, VIDIOC_S_FMT, &cx->vbi.in); |
351 | 352 | ||
352 | /* determine number of lines and total number of VBI bytes. | 353 | /* determine number of lines and total number of VBI bytes. |
353 | A raw line takes 1443 bytes: 2 * 720 + 4 byte frame header - 1 | 354 | A raw line takes 1443 bytes: 2 * 720 + 4 byte frame header - 1 |
354 | The '- 1' byte is probably an unused U or V byte. Or something... | 355 | The '- 1' byte is probably an unused U or V byte. Or something... |
355 | A sliced line takes 51 bytes: 4 byte frame header, 4 byte internal | 356 | A sliced line takes 51 bytes: 4 byte frame header, 4 byte internal |
356 | header, 42 data bytes + checksum (to be confirmed) */ | 357 | header, 42 data bytes + checksum (to be confirmed) */ |
357 | if (raw) { | 358 | if (raw) { |
358 | lines = cx->vbi.count * 2; | 359 | lines = cx->vbi.count * 2; |
359 | } else { | 360 | } else { |
360 | lines = cx->is_60hz ? 24 : 38; | 361 | lines = cx->is_60hz ? 24 : 38; |
361 | if (cx->is_60hz) | 362 | if (cx->is_60hz) |
362 | lines += 2; | 363 | lines += 2; |
363 | } | 364 | } |
364 | 365 | ||
365 | cx->vbi.enc_size = lines * | 366 | cx->vbi.enc_size = lines * |
366 | (raw ? cx->vbi.raw_size : cx->vbi.sliced_size); | 367 | (raw ? cx->vbi.raw_size : cx->vbi.sliced_size); |
367 | 368 | ||
368 | data[0] = s->handle; | 369 | data[0] = s->handle; |
369 | /* Lines per field */ | 370 | /* Lines per field */ |
370 | data[1] = (lines / 2) | ((lines / 2) << 16); | 371 | data[1] = (lines / 2) | ((lines / 2) << 16); |
371 | /* bytes per line */ | 372 | /* bytes per line */ |
372 | data[2] = (raw ? cx->vbi.raw_size : cx->vbi.sliced_size); | 373 | data[2] = (raw ? cx->vbi.raw_size : cx->vbi.sliced_size); |
373 | /* Every X number of frames a VBI interrupt arrives | 374 | /* Every X number of frames a VBI interrupt arrives |
374 | (frames as in 25 or 30 fps) */ | 375 | (frames as in 25 or 30 fps) */ |
375 | data[3] = 1; | 376 | data[3] = 1; |
376 | /* Setup VBI for the cx25840 digitizer */ | 377 | /* Setup VBI for the cx25840 digitizer */ |
377 | if (raw) { | 378 | if (raw) { |
378 | data[4] = 0x20602060; | 379 | data[4] = 0x20602060; |
379 | data[5] = 0x30703070; | 380 | data[5] = 0x30703070; |
380 | } else { | 381 | } else { |
381 | data[4] = 0xB0F0B0F0; | 382 | data[4] = 0xB0F0B0F0; |
382 | data[5] = 0xA0E0A0E0; | 383 | data[5] = 0xA0E0A0E0; |
383 | } | 384 | } |
384 | 385 | ||
385 | CX18_DEBUG_INFO("Setup VBI h: %d lines %x bpl %d fr %d %x %x\n", | 386 | CX18_DEBUG_INFO("Setup VBI h: %d lines %x bpl %d fr %d %x %x\n", |
386 | data[0], data[1], data[2], data[3], data[4], data[5]); | 387 | data[0], data[1], data[2], data[3], data[4], data[5]); |
387 | 388 | ||
388 | if (s->type == CX18_ENC_STREAM_TYPE_VBI) | 389 | if (s->type == CX18_ENC_STREAM_TYPE_VBI) |
389 | cx18_api(cx, CX18_CPU_SET_RAW_VBI_PARAM, 6, data); | 390 | cx18_api(cx, CX18_CPU_SET_RAW_VBI_PARAM, 6, data); |
390 | } | 391 | } |
391 | 392 | ||
392 | int cx18_start_v4l2_encode_stream(struct cx18_stream *s) | 393 | int cx18_start_v4l2_encode_stream(struct cx18_stream *s) |
393 | { | 394 | { |
394 | u32 data[MAX_MB_ARGUMENTS]; | 395 | u32 data[MAX_MB_ARGUMENTS]; |
395 | struct cx18 *cx = s->cx; | 396 | struct cx18 *cx = s->cx; |
396 | struct list_head *p; | 397 | struct list_head *p; |
397 | int ts = 0; | 398 | int ts = 0; |
398 | int captype = 0; | 399 | int captype = 0; |
399 | 400 | ||
400 | if (s->v4l2dev == NULL && s->dvb.enabled == 0) | 401 | if (s->v4l2dev == NULL && s->dvb.enabled == 0) |
401 | return -EINVAL; | 402 | return -EINVAL; |
402 | 403 | ||
403 | CX18_DEBUG_INFO("Start encoder stream %s\n", s->name); | 404 | CX18_DEBUG_INFO("Start encoder stream %s\n", s->name); |
404 | 405 | ||
405 | switch (s->type) { | 406 | switch (s->type) { |
406 | case CX18_ENC_STREAM_TYPE_MPG: | 407 | case CX18_ENC_STREAM_TYPE_MPG: |
407 | captype = CAPTURE_CHANNEL_TYPE_MPEG; | 408 | captype = CAPTURE_CHANNEL_TYPE_MPEG; |
408 | cx->mpg_data_received = cx->vbi_data_inserted = 0; | 409 | cx->mpg_data_received = cx->vbi_data_inserted = 0; |
409 | cx->dualwatch_jiffies = jiffies; | 410 | cx->dualwatch_jiffies = jiffies; |
410 | cx->dualwatch_stereo_mode = cx->params.audio_properties & 0x300; | 411 | cx->dualwatch_stereo_mode = cx->params.audio_properties & 0x300; |
411 | cx->search_pack_header = 0; | 412 | cx->search_pack_header = 0; |
412 | break; | 413 | break; |
413 | 414 | ||
414 | case CX18_ENC_STREAM_TYPE_TS: | 415 | case CX18_ENC_STREAM_TYPE_TS: |
415 | captype = CAPTURE_CHANNEL_TYPE_TS; | 416 | captype = CAPTURE_CHANNEL_TYPE_TS; |
416 | ts = 1; | 417 | ts = 1; |
417 | break; | 418 | break; |
418 | case CX18_ENC_STREAM_TYPE_YUV: | 419 | case CX18_ENC_STREAM_TYPE_YUV: |
419 | captype = CAPTURE_CHANNEL_TYPE_YUV; | 420 | captype = CAPTURE_CHANNEL_TYPE_YUV; |
420 | break; | 421 | break; |
421 | case CX18_ENC_STREAM_TYPE_PCM: | 422 | case CX18_ENC_STREAM_TYPE_PCM: |
422 | captype = CAPTURE_CHANNEL_TYPE_PCM; | 423 | captype = CAPTURE_CHANNEL_TYPE_PCM; |
423 | break; | 424 | break; |
424 | case CX18_ENC_STREAM_TYPE_VBI: | 425 | case CX18_ENC_STREAM_TYPE_VBI: |
425 | captype = cx->vbi.sliced_in->service_set ? | 426 | captype = cx->vbi.sliced_in->service_set ? |
426 | CAPTURE_CHANNEL_TYPE_SLICED_VBI : CAPTURE_CHANNEL_TYPE_VBI; | 427 | CAPTURE_CHANNEL_TYPE_SLICED_VBI : CAPTURE_CHANNEL_TYPE_VBI; |
427 | cx->vbi.frame = 0; | 428 | cx->vbi.frame = 0; |
428 | cx->vbi.inserted_frame = 0; | 429 | cx->vbi.inserted_frame = 0; |
429 | memset(cx->vbi.sliced_mpeg_size, | 430 | memset(cx->vbi.sliced_mpeg_size, |
430 | 0, sizeof(cx->vbi.sliced_mpeg_size)); | 431 | 0, sizeof(cx->vbi.sliced_mpeg_size)); |
431 | break; | 432 | break; |
432 | default: | 433 | default: |
433 | return -EINVAL; | 434 | return -EINVAL; |
434 | } | 435 | } |
435 | 436 | ||
436 | /* mute/unmute video */ | 437 | /* mute/unmute video */ |
437 | cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2, | 438 | cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2, |
438 | s->handle, !!test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)); | 439 | s->handle, !!test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)); |
439 | 440 | ||
440 | /* Clear Streamoff flags in case left from last capture */ | 441 | /* Clear Streamoff flags in case left from last capture */ |
441 | clear_bit(CX18_F_S_STREAMOFF, &s->s_flags); | 442 | clear_bit(CX18_F_S_STREAMOFF, &s->s_flags); |
442 | 443 | ||
443 | cx18_vapi_result(cx, data, CX18_CREATE_TASK, 1, CPU_CMD_MASK_CAPTURE); | 444 | cx18_vapi_result(cx, data, CX18_CREATE_TASK, 1, CPU_CMD_MASK_CAPTURE); |
444 | s->handle = data[0]; | 445 | s->handle = data[0]; |
445 | cx18_vapi(cx, CX18_CPU_SET_CHANNEL_TYPE, 2, s->handle, captype); | 446 | cx18_vapi(cx, CX18_CPU_SET_CHANNEL_TYPE, 2, s->handle, captype); |
446 | 447 | ||
447 | if (atomic_read(&cx->ana_capturing) == 0 && !ts) { | 448 | if (atomic_read(&cx->ana_capturing) == 0 && !ts) { |
448 | /* Stuff from Windows, we don't know what it is */ | 449 | /* Stuff from Windows, we don't know what it is */ |
449 | cx18_vapi(cx, CX18_CPU_SET_VER_CROP_LINE, 2, s->handle, 0); | 450 | cx18_vapi(cx, CX18_CPU_SET_VER_CROP_LINE, 2, s->handle, 0); |
450 | cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 3, 1); | 451 | cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 3, 1); |
451 | cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 8, 0); | 452 | cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 8, 0); |
452 | cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 4, 1); | 453 | cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 4, 1); |
453 | cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 2, s->handle, 12); | 454 | cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 2, s->handle, 12); |
454 | 455 | ||
455 | cx18_vapi(cx, CX18_CPU_SET_CAPTURE_LINE_NO, 3, | 456 | cx18_vapi(cx, CX18_CPU_SET_CAPTURE_LINE_NO, 3, |
456 | s->handle, cx->digitizer, cx->digitizer); | 457 | s->handle, cx->digitizer, cx->digitizer); |
457 | 458 | ||
458 | /* Setup VBI */ | 459 | /* Setup VBI */ |
459 | if (cx->v4l2_cap & V4L2_CAP_VBI_CAPTURE) | 460 | if (cx->v4l2_cap & V4L2_CAP_VBI_CAPTURE) |
460 | cx18_vbi_setup(s); | 461 | cx18_vbi_setup(s); |
461 | 462 | ||
462 | /* assign program index info. | 463 | /* assign program index info. |
463 | Mask 7: select I/P/B, Num_req: 400 max */ | 464 | Mask 7: select I/P/B, Num_req: 400 max */ |
464 | cx18_vapi_result(cx, data, CX18_CPU_SET_INDEXTABLE, 1, 0); | 465 | cx18_vapi_result(cx, data, CX18_CPU_SET_INDEXTABLE, 1, 0); |
465 | 466 | ||
466 | /* Setup API for Stream */ | 467 | /* Setup API for Stream */ |
467 | cx2341x_update(cx, cx18_api_func, NULL, &cx->params); | 468 | cx2341x_update(cx, cx18_api_func, NULL, &cx->params); |
468 | } | 469 | } |
469 | 470 | ||
470 | if (atomic_read(&cx->tot_capturing) == 0) { | 471 | if (atomic_read(&cx->tot_capturing) == 0) { |
471 | clear_bit(CX18_F_I_EOS, &cx->i_flags); | 472 | clear_bit(CX18_F_I_EOS, &cx->i_flags); |
472 | write_reg(7, CX18_DSP0_INTERRUPT_MASK); | 473 | cx18_write_reg(cx, 7, CX18_DSP0_INTERRUPT_MASK); |
473 | } | 474 | } |
474 | 475 | ||
475 | cx18_vapi(cx, CX18_CPU_DE_SET_MDL_ACK, 3, s->handle, | 476 | cx18_vapi(cx, CX18_CPU_DE_SET_MDL_ACK, 3, s->handle, |
476 | (void __iomem *)&cx->scb->cpu_mdl_ack[s->type][0] - cx->enc_mem, | 477 | (void __iomem *)&cx->scb->cpu_mdl_ack[s->type][0] - cx->enc_mem, |
477 | (void __iomem *)&cx->scb->cpu_mdl_ack[s->type][1] - cx->enc_mem); | 478 | (void __iomem *)&cx->scb->cpu_mdl_ack[s->type][1] - cx->enc_mem); |
478 | 479 | ||
479 | list_for_each(p, &s->q_free.list) { | 480 | list_for_each(p, &s->q_free.list) { |
480 | struct cx18_buffer *buf = list_entry(p, struct cx18_buffer, list); | 481 | struct cx18_buffer *buf = list_entry(p, struct cx18_buffer, list); |
481 | 482 | ||
482 | writel(buf->dma_handle, &cx->scb->cpu_mdl[buf->id].paddr); | 483 | cx18_writel(cx, buf->dma_handle, |
483 | writel(s->buf_size, &cx->scb->cpu_mdl[buf->id].length); | 484 | &cx->scb->cpu_mdl[buf->id].paddr); |
485 | cx18_writel(cx, s->buf_size, &cx->scb->cpu_mdl[buf->id].length); | ||
484 | cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle, | 486 | cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle, |
485 | (void __iomem *)&cx->scb->cpu_mdl[buf->id] - cx->enc_mem, | 487 | (void __iomem *)&cx->scb->cpu_mdl[buf->id] - cx->enc_mem, |
486 | 1, buf->id, s->buf_size); | 488 | 1, buf->id, s->buf_size); |
487 | } | 489 | } |
488 | /* begin_capture */ | 490 | /* begin_capture */ |
489 | if (cx18_vapi(cx, CX18_CPU_CAPTURE_START, 1, s->handle)) { | 491 | if (cx18_vapi(cx, CX18_CPU_CAPTURE_START, 1, s->handle)) { |
490 | CX18_DEBUG_WARN("Error starting capture!\n"); | 492 | CX18_DEBUG_WARN("Error starting capture!\n"); |
491 | /* Ensure we're really not capturing before releasing MDLs */ | 493 | /* Ensure we're really not capturing before releasing MDLs */ |
492 | if (s->type == CX18_ENC_STREAM_TYPE_MPG) | 494 | if (s->type == CX18_ENC_STREAM_TYPE_MPG) |
493 | cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 2, s->handle, 1); | 495 | cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 2, s->handle, 1); |
494 | else | 496 | else |
495 | cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 1, s->handle); | 497 | cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 1, s->handle); |
496 | cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle); | 498 | cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle); |
497 | cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle); | 499 | cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle); |
498 | /* FIXME - clean-up DSP0_INT mask, i_flags, s_flags, etc. */ | 500 | /* FIXME - clean-up DSP0_INT mask, i_flags, s_flags, etc. */ |
499 | return -EINVAL; | 501 | return -EINVAL; |
500 | } | 502 | } |
501 | 503 | ||
502 | /* you're live! sit back and await interrupts :) */ | 504 | /* you're live! sit back and await interrupts :) */ |
503 | if (!ts) | 505 | if (!ts) |
504 | atomic_inc(&cx->ana_capturing); | 506 | atomic_inc(&cx->ana_capturing); |
505 | atomic_inc(&cx->tot_capturing); | 507 | atomic_inc(&cx->tot_capturing); |
506 | return 0; | 508 | return 0; |
507 | } | 509 | } |
508 | 510 | ||
509 | void cx18_stop_all_captures(struct cx18 *cx) | 511 | void cx18_stop_all_captures(struct cx18 *cx) |
510 | { | 512 | { |
511 | int i; | 513 | int i; |
512 | 514 | ||
513 | for (i = CX18_MAX_STREAMS - 1; i >= 0; i--) { | 515 | for (i = CX18_MAX_STREAMS - 1; i >= 0; i--) { |
514 | struct cx18_stream *s = &cx->streams[i]; | 516 | struct cx18_stream *s = &cx->streams[i]; |
515 | 517 | ||
516 | if (s->v4l2dev == NULL && s->dvb.enabled == 0) | 518 | if (s->v4l2dev == NULL && s->dvb.enabled == 0) |
517 | continue; | 519 | continue; |
518 | if (test_bit(CX18_F_S_STREAMING, &s->s_flags)) | 520 | if (test_bit(CX18_F_S_STREAMING, &s->s_flags)) |
519 | cx18_stop_v4l2_encode_stream(s, 0); | 521 | cx18_stop_v4l2_encode_stream(s, 0); |
520 | } | 522 | } |
521 | } | 523 | } |
522 | 524 | ||
523 | int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end) | 525 | int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end) |
524 | { | 526 | { |
525 | struct cx18 *cx = s->cx; | 527 | struct cx18 *cx = s->cx; |
526 | unsigned long then; | 528 | unsigned long then; |
527 | 529 | ||
528 | if (s->v4l2dev == NULL && s->dvb.enabled == 0) | 530 | if (s->v4l2dev == NULL && s->dvb.enabled == 0) |
529 | return -EINVAL; | 531 | return -EINVAL; |
530 | 532 | ||
531 | /* This function assumes that you are allowed to stop the capture | 533 | /* This function assumes that you are allowed to stop the capture |
532 | and that we are actually capturing */ | 534 | and that we are actually capturing */ |
533 | 535 | ||
534 | CX18_DEBUG_INFO("Stop Capture\n"); | 536 | CX18_DEBUG_INFO("Stop Capture\n"); |
535 | 537 | ||
536 | if (atomic_read(&cx->tot_capturing) == 0) | 538 | if (atomic_read(&cx->tot_capturing) == 0) |
537 | return 0; | 539 | return 0; |
538 | 540 | ||
539 | if (s->type == CX18_ENC_STREAM_TYPE_MPG) | 541 | if (s->type == CX18_ENC_STREAM_TYPE_MPG) |
540 | cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 2, s->handle, !gop_end); | 542 | cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 2, s->handle, !gop_end); |
541 | else | 543 | else |
542 | cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 1, s->handle); | 544 | cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 1, s->handle); |
543 | 545 | ||
544 | then = jiffies; | 546 | then = jiffies; |
545 | 547 | ||
546 | if (s->type == CX18_ENC_STREAM_TYPE_MPG && gop_end) { | 548 | if (s->type == CX18_ENC_STREAM_TYPE_MPG && gop_end) { |
547 | CX18_INFO("ignoring gop_end: not (yet?) supported by the firmware\n"); | 549 | CX18_INFO("ignoring gop_end: not (yet?) supported by the firmware\n"); |
548 | } | 550 | } |
549 | 551 | ||
550 | /* Tell the CX23418 it can't use our buffers anymore */ | 552 | /* Tell the CX23418 it can't use our buffers anymore */ |
551 | cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle); | 553 | cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle); |
552 | 554 | ||
553 | if (s->type != CX18_ENC_STREAM_TYPE_TS) | 555 | if (s->type != CX18_ENC_STREAM_TYPE_TS) |
554 | atomic_dec(&cx->ana_capturing); | 556 | atomic_dec(&cx->ana_capturing); |
555 | atomic_dec(&cx->tot_capturing); | 557 | atomic_dec(&cx->tot_capturing); |
556 | 558 | ||
557 | /* Clear capture and no-read bits */ | 559 | /* Clear capture and no-read bits */ |
558 | clear_bit(CX18_F_S_STREAMING, &s->s_flags); | 560 | clear_bit(CX18_F_S_STREAMING, &s->s_flags); |
559 | 561 | ||
560 | cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle); | 562 | cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle); |
561 | s->handle = CX18_INVALID_TASK_HANDLE; | 563 | s->handle = CX18_INVALID_TASK_HANDLE; |
562 | 564 | ||
563 | if (atomic_read(&cx->tot_capturing) > 0) | 565 | if (atomic_read(&cx->tot_capturing) > 0) |
564 | return 0; | 566 | return 0; |
565 | 567 | ||
566 | write_reg(5, CX18_DSP0_INTERRUPT_MASK); | 568 | cx18_write_reg(cx, 5, CX18_DSP0_INTERRUPT_MASK); |
567 | wake_up(&s->waitq); | 569 | wake_up(&s->waitq); |
568 | 570 | ||
569 | return 0; | 571 | return 0; |
570 | } | 572 | } |
571 | 573 | ||
572 | u32 cx18_find_handle(struct cx18 *cx) | 574 | u32 cx18_find_handle(struct cx18 *cx) |
573 | { | 575 | { |
574 | int i; | 576 | int i; |
575 | 577 | ||
576 | /* find first available handle to be used for global settings */ | 578 | /* find first available handle to be used for global settings */ |
577 | for (i = 0; i < CX18_MAX_STREAMS; i++) { | 579 | for (i = 0; i < CX18_MAX_STREAMS; i++) { |
578 | struct cx18_stream *s = &cx->streams[i]; | 580 | struct cx18_stream *s = &cx->streams[i]; |
579 | 581 | ||
580 | if (s->v4l2dev && (s->handle != CX18_INVALID_TASK_HANDLE)) | 582 | if (s->v4l2dev && (s->handle != CX18_INVALID_TASK_HANDLE)) |
581 | return s->handle; | 583 | return s->handle; |
582 | } | 584 | } |
583 | return CX18_INVALID_TASK_HANDLE; | 585 | return CX18_INVALID_TASK_HANDLE; |
584 | } | 586 | } |
585 | 587 |