diff options
Diffstat (limited to 'drivers/media/video/cx18')
-rw-r--r-- | drivers/media/video/cx18/cx18-audio.c | 44 | ||||
-rw-r--r-- | drivers/media/video/cx18/cx18-av-core.c | 374 | ||||
-rw-r--r-- | drivers/media/video/cx18/cx18-av-firmware.c | 82 | ||||
-rw-r--r-- | drivers/media/video/cx18/cx18-av-vbi.c | 4 | ||||
-rw-r--r-- | drivers/media/video/cx18/cx18-cards.c | 63 | ||||
-rw-r--r-- | drivers/media/video/cx18/cx18-controls.c | 6 | ||||
-rw-r--r-- | drivers/media/video/cx18/cx18-driver.c | 100 | ||||
-rw-r--r-- | drivers/media/video/cx18/cx18-driver.h | 22 | ||||
-rw-r--r-- | drivers/media/video/cx18/cx18-dvb.c | 54 | ||||
-rw-r--r-- | drivers/media/video/cx18/cx18-fileops.c | 7 | ||||
-rw-r--r-- | drivers/media/video/cx18/cx18-mailbox.c | 114 | ||||
-rw-r--r-- | drivers/media/video/cx18/cx18-mailbox.h | 2 | ||||
-rw-r--r-- | drivers/media/video/cx18/cx18-queue.c | 85 | ||||
-rw-r--r-- | drivers/media/video/cx18/cx18-streams.c | 44 | ||||
-rw-r--r-- | drivers/media/video/cx18/cx18-streams.h | 20 | ||||
-rw-r--r-- | drivers/media/video/cx18/cx18-version.h | 2 |
16 files changed, 773 insertions, 250 deletions
diff --git a/drivers/media/video/cx18/cx18-audio.c b/drivers/media/video/cx18/cx18-audio.c index 7a8ad5963de8..35268923911c 100644 --- a/drivers/media/video/cx18/cx18-audio.c +++ b/drivers/media/video/cx18/cx18-audio.c | |||
@@ -26,14 +26,18 @@ | |||
26 | #include "cx18-cards.h" | 26 | #include "cx18-cards.h" |
27 | #include "cx18-audio.h" | 27 | #include "cx18-audio.h" |
28 | 28 | ||
29 | #define CX18_AUDIO_ENABLE 0xc72014 | 29 | #define CX18_AUDIO_ENABLE 0xc72014 |
30 | #define CX18_AI1_MUX_MASK 0x30 | ||
31 | #define CX18_AI1_MUX_I2S1 0x00 | ||
32 | #define CX18_AI1_MUX_I2S2 0x10 | ||
33 | #define CX18_AI1_MUX_843_I2S 0x20 | ||
30 | 34 | ||
31 | /* Selects the audio input and output according to the current | 35 | /* Selects the audio input and output according to the current |
32 | settings. */ | 36 | settings. */ |
33 | int cx18_audio_set_io(struct cx18 *cx) | 37 | int cx18_audio_set_io(struct cx18 *cx) |
34 | { | 38 | { |
35 | const struct cx18_card_audio_input *in; | 39 | const struct cx18_card_audio_input *in; |
36 | u32 val; | 40 | u32 u, v; |
37 | int err; | 41 | int err; |
38 | 42 | ||
39 | /* Determine which input to use */ | 43 | /* Determine which input to use */ |
@@ -52,9 +56,37 @@ int cx18_audio_set_io(struct cx18 *cx) | |||
52 | return err; | 56 | return err; |
53 | 57 | ||
54 | /* FIXME - this internal mux should be abstracted to a subdev */ | 58 | /* FIXME - this internal mux should be abstracted to a subdev */ |
55 | val = cx18_read_reg(cx, CX18_AUDIO_ENABLE) & ~0x30; | 59 | u = cx18_read_reg(cx, CX18_AUDIO_ENABLE); |
56 | val |= (in->audio_input > CX18_AV_AUDIO_SERIAL2) ? 0x20 : | 60 | v = u & ~CX18_AI1_MUX_MASK; |
57 | (in->audio_input << 4); | 61 | switch (in->audio_input) { |
58 | cx18_write_reg_expect(cx, val | 0xb00, CX18_AUDIO_ENABLE, val, 0x30); | 62 | case CX18_AV_AUDIO_SERIAL1: |
63 | v |= CX18_AI1_MUX_I2S1; | ||
64 | break; | ||
65 | case CX18_AV_AUDIO_SERIAL2: | ||
66 | v |= CX18_AI1_MUX_I2S2; | ||
67 | break; | ||
68 | default: | ||
69 | v |= CX18_AI1_MUX_843_I2S; | ||
70 | break; | ||
71 | } | ||
72 | if (v == u) { | ||
73 | /* force a toggle of some AI1 MUX control bits */ | ||
74 | u &= ~CX18_AI1_MUX_MASK; | ||
75 | switch (in->audio_input) { | ||
76 | case CX18_AV_AUDIO_SERIAL1: | ||
77 | u |= CX18_AI1_MUX_843_I2S; | ||
78 | break; | ||
79 | case CX18_AV_AUDIO_SERIAL2: | ||
80 | u |= CX18_AI1_MUX_843_I2S; | ||
81 | break; | ||
82 | default: | ||
83 | u |= CX18_AI1_MUX_I2S1; | ||
84 | break; | ||
85 | } | ||
86 | cx18_write_reg_expect(cx, u | 0xb00, CX18_AUDIO_ENABLE, | ||
87 | u, CX18_AI1_MUX_MASK); | ||
88 | } | ||
89 | cx18_write_reg_expect(cx, v | 0xb00, CX18_AUDIO_ENABLE, | ||
90 | v, CX18_AI1_MUX_MASK); | ||
59 | return 0; | 91 | return 0; |
60 | } | 92 | } |
diff --git a/drivers/media/video/cx18/cx18-av-core.c b/drivers/media/video/cx18/cx18-av-core.c index cf2bd888a429..536dedb23ba3 100644 --- a/drivers/media/video/cx18/cx18-av-core.c +++ b/drivers/media/video/cx18/cx18-av-core.c | |||
@@ -99,9 +99,39 @@ int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 and_mask, | |||
99 | or_value); | 99 | or_value); |
100 | } | 100 | } |
101 | 101 | ||
102 | static void cx18_av_initialize(struct cx18 *cx) | 102 | static int cx18_av_init(struct v4l2_subdev *sd, u32 val) |
103 | { | 103 | { |
104 | struct cx18_av_state *state = &cx->av_state; | 104 | struct cx18 *cx = v4l2_get_subdevdata(sd); |
105 | |||
106 | /* | ||
107 | * The crystal freq used in calculations in this driver will be | ||
108 | * 28.636360 MHz. | ||
109 | * Aim to run the PLLs' VCOs near 400 MHz to minimze errors. | ||
110 | */ | ||
111 | |||
112 | /* | ||
113 | * VDCLK Integer = 0x0f, Post Divider = 0x04 | ||
114 | * AIMCLK Integer = 0x0e, Post Divider = 0x16 | ||
115 | */ | ||
116 | cx18_av_write4(cx, CXADEC_PLL_CTRL1, 0x160e040f); | ||
117 | |||
118 | /* VDCLK Fraction = 0x2be2fe */ | ||
119 | /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz before post divide */ | ||
120 | cx18_av_write4(cx, CXADEC_VID_PLL_FRAC, 0x002be2fe); | ||
121 | |||
122 | /* AIMCLK Fraction = 0x05227ad */ | ||
123 | /* xtal * 0xe.2913d68/0x16 = 48000 * 384: 406 MHz pre post-div*/ | ||
124 | cx18_av_write4(cx, CXADEC_AUX_PLL_FRAC, 0x005227ad); | ||
125 | |||
126 | /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x16 */ | ||
127 | cx18_av_write(cx, CXADEC_I2S_MCLK, 0x56); | ||
128 | return 0; | ||
129 | } | ||
130 | |||
131 | static void cx18_av_initialize(struct v4l2_subdev *sd) | ||
132 | { | ||
133 | struct cx18_av_state *state = to_cx18_av_state(sd); | ||
134 | struct cx18 *cx = v4l2_get_subdevdata(sd); | ||
105 | u32 v; | 135 | u32 v; |
106 | 136 | ||
107 | cx18_av_loadfw(cx); | 137 | cx18_av_loadfw(cx); |
@@ -150,6 +180,26 @@ static void cx18_av_initialize(struct cx18 *cx) | |||
150 | cx18_av_write4(cx, CXADEC_SOFT_RST_CTRL, 0x8000); | 180 | cx18_av_write4(cx, CXADEC_SOFT_RST_CTRL, 0x8000); |
151 | cx18_av_write4(cx, CXADEC_SOFT_RST_CTRL, 0); | 181 | cx18_av_write4(cx, CXADEC_SOFT_RST_CTRL, 0); |
152 | 182 | ||
183 | /* | ||
184 | * Disable Video Auto-config of the Analog Front End and Video PLL. | ||
185 | * | ||
186 | * Since we only use BT.656 pixel mode, which works for both 525 and 625 | ||
187 | * line systems, it's just easier for us to set registers | ||
188 | * 0x102 (CXADEC_CHIP_CTRL), 0x104-0x106 (CXADEC_AFE_CTRL), | ||
189 | * 0x108-0x109 (CXADEC_PLL_CTRL1), and 0x10c-0x10f (CXADEC_VID_PLL_FRAC) | ||
190 | * ourselves, than to run around cleaning up after the auto-config. | ||
191 | * | ||
192 | * (Note: my CX23418 chip doesn't seem to let the ACFG_DIS bit | ||
193 | * get set to 1, but OTOH, it doesn't seem to do AFE and VID PLL | ||
194 | * autoconfig either.) | ||
195 | * | ||
196 | * As a default, also turn off Dual mode for ADC2 and set ADC2 to CH3. | ||
197 | */ | ||
198 | cx18_av_and_or4(cx, CXADEC_CHIP_CTRL, 0xFFFBFFFF, 0x00120000); | ||
199 | |||
200 | /* Setup the Video and and Aux/Audio PLLs */ | ||
201 | cx18_av_init(sd, 0); | ||
202 | |||
153 | /* set video to auto-detect */ | 203 | /* set video to auto-detect */ |
154 | /* Clear bits 11-12 to enable slow locking mode. Set autodetect mode */ | 204 | /* Clear bits 11-12 to enable slow locking mode. Set autodetect mode */ |
155 | /* set the comb notch = 1 */ | 205 | /* set the comb notch = 1 */ |
@@ -176,12 +226,23 @@ static void cx18_av_initialize(struct cx18 *cx) | |||
176 | /* EncSetSignalStd(dwDevNum, pEnc->dwSigStd); */ | 226 | /* EncSetSignalStd(dwDevNum, pEnc->dwSigStd); */ |
177 | /* EncSetVideoInput(dwDevNum, pEnc->VidIndSelection); */ | 227 | /* EncSetVideoInput(dwDevNum, pEnc->VidIndSelection); */ |
178 | 228 | ||
179 | v = cx18_av_read4(cx, CXADEC_AFE_CTRL); | 229 | /* |
180 | v &= 0xFFFBFFFF; /* turn OFF bit 18 for droop_comp_ch1 */ | 230 | * Analog Front End (AFE) |
181 | v &= 0xFFFF7FFF; /* turn OFF bit 9 for clamp_sel_ch1 */ | 231 | * Default to luma on ch1/ADC1, chroma on ch2/ADC2, SIF on ch3/ADC2 |
182 | v &= 0xFFFFFFFE; /* turn OFF bit 0 for 12db_ch1 */ | 232 | * bypass_ch[1-3] use filter |
183 | /* v |= 0x00000001;*/ /* turn ON bit 0 for 12db_ch1 */ | 233 | * droop_comp_ch[1-3] disable |
184 | cx18_av_write4(cx, CXADEC_AFE_CTRL, v); | 234 | * clamp_en_ch[1-3] disable |
235 | * aud_in_sel ADC2 | ||
236 | * luma_in_sel ADC1 | ||
237 | * chroma_in_sel ADC2 | ||
238 | * clamp_sel_ch[2-3] midcode | ||
239 | * clamp_sel_ch1 video decoder | ||
240 | * vga_sel_ch3 audio decoder | ||
241 | * vga_sel_ch[1-2] video decoder | ||
242 | * half_bw_ch[1-3] disable | ||
243 | * +12db_ch[1-3] disable | ||
244 | */ | ||
245 | cx18_av_and_or4(cx, CXADEC_AFE_CTRL, 0xFF000000, 0x00005D00); | ||
185 | 246 | ||
186 | /* if(dwEnable && dw3DCombAvailable) { */ | 247 | /* if(dwEnable && dw3DCombAvailable) { */ |
187 | /* CxDevWrReg(CXADEC_SRC_COMB_CFG, 0x7728021F); */ | 248 | /* CxDevWrReg(CXADEC_SRC_COMB_CFG, 0x7728021F); */ |
@@ -195,50 +256,18 @@ static void cx18_av_initialize(struct cx18 *cx) | |||
195 | 256 | ||
196 | static int cx18_av_reset(struct v4l2_subdev *sd, u32 val) | 257 | static int cx18_av_reset(struct v4l2_subdev *sd, u32 val) |
197 | { | 258 | { |
198 | struct cx18 *cx = v4l2_get_subdevdata(sd); | 259 | cx18_av_initialize(sd); |
199 | |||
200 | cx18_av_initialize(cx); | ||
201 | return 0; | ||
202 | } | ||
203 | |||
204 | static int cx18_av_init(struct v4l2_subdev *sd, u32 val) | ||
205 | { | ||
206 | struct cx18 *cx = v4l2_get_subdevdata(sd); | ||
207 | |||
208 | /* | ||
209 | * The crystal freq used in calculations in this driver will be | ||
210 | * 28.636360 MHz. | ||
211 | * Aim to run the PLLs' VCOs near 400 MHz to minimze errors. | ||
212 | */ | ||
213 | |||
214 | /* | ||
215 | * VDCLK Integer = 0x0f, Post Divider = 0x04 | ||
216 | * AIMCLK Integer = 0x0e, Post Divider = 0x16 | ||
217 | */ | ||
218 | cx18_av_write4(cx, CXADEC_PLL_CTRL1, 0x160e040f); | ||
219 | |||
220 | /* VDCLK Fraction = 0x2be2fe */ | ||
221 | /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz before post divide */ | ||
222 | cx18_av_write4(cx, CXADEC_VID_PLL_FRAC, 0x002be2fe); | ||
223 | |||
224 | /* AIMCLK Fraction = 0x05227ad */ | ||
225 | /* xtal * 0xe.2913d68/0x16 = 48000 * 384: 406 MHz pre post-div*/ | ||
226 | cx18_av_write4(cx, CXADEC_AUX_PLL_FRAC, 0x005227ad); | ||
227 | |||
228 | /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x16 */ | ||
229 | cx18_av_write(cx, CXADEC_I2S_MCLK, 0x56); | ||
230 | return 0; | 260 | return 0; |
231 | } | 261 | } |
232 | 262 | ||
233 | static int cx18_av_load_fw(struct v4l2_subdev *sd) | 263 | static int cx18_av_load_fw(struct v4l2_subdev *sd) |
234 | { | 264 | { |
235 | struct cx18_av_state *state = to_cx18_av_state(sd); | 265 | struct cx18_av_state *state = to_cx18_av_state(sd); |
236 | struct cx18 *cx = v4l2_get_subdevdata(sd); | ||
237 | 266 | ||
238 | if (!state->is_initialized) { | 267 | if (!state->is_initialized) { |
239 | /* initialize on first use */ | 268 | /* initialize on first use */ |
240 | state->is_initialized = 1; | 269 | state->is_initialized = 1; |
241 | cx18_av_initialize(cx); | 270 | cx18_av_initialize(sd); |
242 | } | 271 | } |
243 | return 0; | 272 | return 0; |
244 | } | 273 | } |
@@ -248,8 +277,15 @@ void cx18_av_std_setup(struct cx18 *cx) | |||
248 | struct cx18_av_state *state = &cx->av_state; | 277 | struct cx18_av_state *state = &cx->av_state; |
249 | struct v4l2_subdev *sd = &state->sd; | 278 | struct v4l2_subdev *sd = &state->sd; |
250 | v4l2_std_id std = state->std; | 279 | v4l2_std_id std = state->std; |
280 | |||
281 | /* | ||
282 | * Video ADC crystal clock to pixel clock SRC decimation ratio | ||
283 | * 28.636360 MHz/13.5 Mpps * 256 = 0x21f.07b | ||
284 | */ | ||
285 | const int src_decimation = 0x21f; | ||
286 | |||
251 | int hblank, hactive, burst, vblank, vactive, sc; | 287 | int hblank, hactive, burst, vblank, vactive, sc; |
252 | int vblank656, src_decimation; | 288 | int vblank656; |
253 | int luma_lpf, uv_lpf, comb; | 289 | int luma_lpf, uv_lpf, comb; |
254 | u32 pll_int, pll_frac, pll_post; | 290 | u32 pll_int, pll_frac, pll_post; |
255 | 291 | ||
@@ -259,40 +295,96 @@ void cx18_av_std_setup(struct cx18 *cx) | |||
259 | else | 295 | else |
260 | cx18_av_write(cx, 0x49f, 0x14); | 296 | cx18_av_write(cx, 0x49f, 0x14); |
261 | 297 | ||
298 | /* | ||
299 | * Note: At the end of a field, there are 3 sets of half line duration | ||
300 | * (double horizontal rate) pulses: | ||
301 | * | ||
302 | * 5 (625) or 6 (525) half-lines to blank for the vertical retrace | ||
303 | * 5 (625) or 6 (525) vertical sync pulses of half line duration | ||
304 | * 5 (625) or 6 (525) half-lines of equalization pulses | ||
305 | */ | ||
262 | if (std & V4L2_STD_625_50) { | 306 | if (std & V4L2_STD_625_50) { |
263 | /* FIXME - revisit these for Sliced VBI */ | 307 | /* |
308 | * The following relationships of half line counts should hold: | ||
309 | * 625 = vblank656 + vactive | ||
310 | * 10 = vblank656 - vblank = vsync pulses + equalization pulses | ||
311 | * | ||
312 | * vblank656: half lines after line 625/mid-313 of blanked video | ||
313 | * vblank: half lines, after line 5/317, of blanked video | ||
314 | * vactive: half lines of active video + | ||
315 | * 5 half lines after the end of active video | ||
316 | * | ||
317 | * As far as I can tell: | ||
318 | * vblank656 starts counting from the falling edge of the first | ||
319 | * vsync pulse (start of line 1 or mid-313) | ||
320 | * vblank starts counting from the after the 5 vsync pulses and | ||
321 | * 5 or 4 equalization pulses (start of line 6 or 318) | ||
322 | * | ||
323 | * For 625 line systems the driver will extract VBI information | ||
324 | * from lines 6-23 and lines 318-335 (but the slicer can only | ||
325 | * handle 17 lines, not the 18 in the vblank region). | ||
326 | * In addition, we need vblank656 and vblank to be one whole | ||
327 | * line longer, to cover line 24 and 336, so the SAV/EAV RP | ||
328 | * codes get generated such that the encoder can actually | ||
329 | * extract line 23 & 335 (WSS). We'll lose 1 line in each field | ||
330 | * at the top of the screen. | ||
331 | * | ||
332 | * It appears the 5 half lines that happen after active | ||
333 | * video must be included in vactive (579 instead of 574), | ||
334 | * otherwise the colors get badly displayed in various regions | ||
335 | * of the screen. I guess the chroma comb filter gets confused | ||
336 | * without them (at least when a PVR-350 is the PAL source). | ||
337 | */ | ||
338 | vblank656 = 48; /* lines 1 - 24 & 313 - 336 */ | ||
339 | vblank = 38; /* lines 6 - 24 & 318 - 336 */ | ||
340 | vactive = 579; /* lines 24 - 313 & 337 - 626 */ | ||
341 | |||
342 | /* | ||
343 | * For a 13.5 Mpps clock and 15,625 Hz line rate, a line is | ||
344 | * is 864 pixels = 720 active + 144 blanking. ITU-R BT.601 | ||
345 | * specifies 12 luma clock periods or ~ 0.9 * 13.5 Mpps after | ||
346 | * the end of active video to start a horizontal line, so that | ||
347 | * leaves 132 pixels of hblank to ignore. | ||
348 | */ | ||
264 | hblank = 132; | 349 | hblank = 132; |
265 | hactive = 720; | 350 | hactive = 720; |
266 | burst = 93; | ||
267 | vblank = 36; | ||
268 | vactive = 580; | ||
269 | vblank656 = 40; | ||
270 | src_decimation = 0x21f; | ||
271 | 351 | ||
352 | /* | ||
353 | * Burst gate delay (for 625 line systems) | ||
354 | * Hsync leading edge to color burst rise = 5.6 us | ||
355 | * Color burst width = 2.25 us | ||
356 | * Gate width = 4 pixel clocks | ||
357 | * (5.6 us + 2.25/2 us) * 13.5 Mpps + 4/2 clocks = 92.79 clocks | ||
358 | */ | ||
359 | burst = 93; | ||
272 | luma_lpf = 2; | 360 | luma_lpf = 2; |
273 | if (std & V4L2_STD_PAL) { | 361 | if (std & V4L2_STD_PAL) { |
274 | uv_lpf = 1; | 362 | uv_lpf = 1; |
275 | comb = 0x20; | 363 | comb = 0x20; |
276 | sc = 688739; | 364 | /* sc = 4433618.75 * src_decimation/28636360 * 2^13 */ |
365 | sc = 688700; | ||
277 | } else if (std == V4L2_STD_PAL_Nc) { | 366 | } else if (std == V4L2_STD_PAL_Nc) { |
278 | uv_lpf = 1; | 367 | uv_lpf = 1; |
279 | comb = 0x20; | 368 | comb = 0x20; |
280 | sc = 556453; | 369 | /* sc = 3582056.25 * src_decimation/28636360 * 2^13 */ |
370 | sc = 556422; | ||
281 | } else { /* SECAM */ | 371 | } else { /* SECAM */ |
282 | uv_lpf = 0; | 372 | uv_lpf = 0; |
283 | comb = 0; | 373 | comb = 0; |
284 | sc = 672351; | 374 | /* (fr + fb)/2 = (4406260 + 4250000)/2 = 4328130 */ |
375 | /* sc = 4328130 * src_decimation/28636360 * 2^13 */ | ||
376 | sc = 672314; | ||
285 | } | 377 | } |
286 | } else { | 378 | } else { |
287 | /* | 379 | /* |
288 | * The following relationships of half line counts should hold: | 380 | * The following relationships of half line counts should hold: |
289 | * 525 = vsync + vactive + vblank656 | 381 | * 525 = prevsync + vblank656 + vactive |
290 | * 12 = vblank656 - vblank | 382 | * 12 = vblank656 - vblank = vsync pulses + equalization pulses |
291 | * | 383 | * |
292 | * vsync: always 6 half-lines of vsync pulses | 384 | * prevsync: 6 half-lines before the vsync pulses |
293 | * vactive: half lines of active video | ||
294 | * vblank656: half lines, after line 3/mid-266, of blanked video | 385 | * vblank656: half lines, after line 3/mid-266, of blanked video |
295 | * vblank: half lines, after line 9/272, of blanked video | 386 | * vblank: half lines, after line 9/272, of blanked video |
387 | * vactive: half lines of active video | ||
296 | * | 388 | * |
297 | * As far as I can tell: | 389 | * As far as I can tell: |
298 | * vblank656 starts counting from the falling edge of the first | 390 | * vblank656 starts counting from the falling edge of the first |
@@ -319,20 +411,30 @@ void cx18_av_std_setup(struct cx18 *cx) | |||
319 | luma_lpf = 1; | 411 | luma_lpf = 1; |
320 | uv_lpf = 1; | 412 | uv_lpf = 1; |
321 | 413 | ||
322 | src_decimation = 0x21f; | 414 | /* |
415 | * Burst gate delay (for 525 line systems) | ||
416 | * Hsync leading edge to color burst rise = 5.3 us | ||
417 | * Color burst width = 2.5 us | ||
418 | * Gate width = 4 pixel clocks | ||
419 | * (5.3 us + 2.5/2 us) * 13.5 Mpps + 4/2 clocks = 90.425 clocks | ||
420 | */ | ||
323 | if (std == V4L2_STD_PAL_60) { | 421 | if (std == V4L2_STD_PAL_60) { |
324 | burst = 0x5b; | 422 | burst = 90; |
325 | luma_lpf = 2; | 423 | luma_lpf = 2; |
326 | comb = 0x20; | 424 | comb = 0x20; |
327 | sc = 688739; | 425 | /* sc = 4433618.75 * src_decimation/28636360 * 2^13 */ |
426 | sc = 688700; | ||
328 | } else if (std == V4L2_STD_PAL_M) { | 427 | } else if (std == V4L2_STD_PAL_M) { |
329 | burst = 0x61; | 428 | /* The 97 needs to be verified against PAL-M timings */ |
429 | burst = 97; | ||
330 | comb = 0x20; | 430 | comb = 0x20; |
331 | sc = 555452; | 431 | /* sc = 3575611.49 * src_decimation/28636360 * 2^13 */ |
432 | sc = 555421; | ||
332 | } else { | 433 | } else { |
333 | burst = 0x5b; | 434 | burst = 90; |
334 | comb = 0x66; | 435 | comb = 0x66; |
335 | sc = 556063; | 436 | /* sc = 3579545.45.. * src_decimation/28636360 * 2^13 */ |
437 | sc = 556032; | ||
336 | } | 438 | } |
337 | } | 439 | } |
338 | 440 | ||
@@ -344,23 +446,26 @@ void cx18_av_std_setup(struct cx18 *cx) | |||
344 | pll_int, pll_frac, pll_post); | 446 | pll_int, pll_frac, pll_post); |
345 | 447 | ||
346 | if (pll_post) { | 448 | if (pll_post) { |
347 | int fin, fsc, pll; | 449 | int fsc, pll; |
450 | u64 tmp; | ||
348 | 451 | ||
349 | pll = (28636360L * ((((u64)pll_int) << 25) + pll_frac)) >> 25; | 452 | pll = (28636360L * ((((u64)pll_int) << 25) + pll_frac)) >> 25; |
350 | pll /= pll_post; | 453 | pll /= pll_post; |
351 | CX18_DEBUG_INFO_DEV(sd, "PLL = %d.%06d MHz\n", | 454 | CX18_DEBUG_INFO_DEV(sd, "Video PLL = %d.%06d MHz\n", |
352 | pll / 1000000, pll % 1000000); | 455 | pll / 1000000, pll % 1000000); |
353 | CX18_DEBUG_INFO_DEV(sd, "PLL/8 = %d.%06d MHz\n", | 456 | CX18_DEBUG_INFO_DEV(sd, "Pixel rate = %d.%06d Mpixel/sec\n", |
354 | pll / 8000000, (pll / 8) % 1000000); | 457 | pll / 8000000, (pll / 8) % 1000000); |
355 | 458 | ||
356 | fin = ((u64)src_decimation * pll) >> 12; | 459 | CX18_DEBUG_INFO_DEV(sd, "ADC XTAL/pixel clock decimation ratio " |
357 | CX18_DEBUG_INFO_DEV(sd, "ADC Sampling freq = %d.%06d MHz\n", | 460 | "= %d.%03d\n", src_decimation / 256, |
358 | fin / 1000000, fin % 1000000); | 461 | ((src_decimation % 256) * 1000) / 256); |
359 | 462 | ||
360 | fsc = (((u64)sc) * pll) >> 24L; | 463 | tmp = 28636360 * (u64) sc; |
464 | do_div(tmp, src_decimation); | ||
465 | fsc = tmp >> 13; | ||
361 | CX18_DEBUG_INFO_DEV(sd, | 466 | CX18_DEBUG_INFO_DEV(sd, |
362 | "Chroma sub-carrier freq = %d.%06d MHz\n", | 467 | "Chroma sub-carrier initial freq = %d.%06d " |
363 | fsc / 1000000, fsc % 1000000); | 468 | "MHz\n", fsc / 1000000, fsc % 1000000); |
364 | 469 | ||
365 | CX18_DEBUG_INFO_DEV(sd, "hblank %i, hactive %i, vblank %i, " | 470 | CX18_DEBUG_INFO_DEV(sd, "hblank %i, hactive %i, vblank %i, " |
366 | "vactive %i, vblank656 %i, src_dec %i, " | 471 | "vactive %i, vblank656 %i, src_dec %i, " |
@@ -470,16 +575,23 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input, | |||
470 | { | 575 | { |
471 | struct cx18_av_state *state = &cx->av_state; | 576 | struct cx18_av_state *state = &cx->av_state; |
472 | struct v4l2_subdev *sd = &state->sd; | 577 | struct v4l2_subdev *sd = &state->sd; |
473 | u8 is_composite = (vid_input >= CX18_AV_COMPOSITE1 && | 578 | |
474 | vid_input <= CX18_AV_COMPOSITE8); | 579 | enum analog_signal_type { |
475 | u8 reg; | 580 | NONE, CVBS, Y, C, SIF, Pb, Pr |
476 | u8 v; | 581 | } ch[3] = {NONE, NONE, NONE}; |
582 | |||
583 | u8 afe_mux_cfg; | ||
584 | u8 adc2_cfg; | ||
585 | u32 afe_cfg; | ||
586 | int i; | ||
477 | 587 | ||
478 | CX18_DEBUG_INFO_DEV(sd, "decoder set video input %d, audio input %d\n", | 588 | CX18_DEBUG_INFO_DEV(sd, "decoder set video input %d, audio input %d\n", |
479 | vid_input, aud_input); | 589 | vid_input, aud_input); |
480 | 590 | ||
481 | if (is_composite) { | 591 | if (vid_input >= CX18_AV_COMPOSITE1 && |
482 | reg = 0xf0 + (vid_input - CX18_AV_COMPOSITE1); | 592 | vid_input <= CX18_AV_COMPOSITE8) { |
593 | afe_mux_cfg = 0xf0 + (vid_input - CX18_AV_COMPOSITE1); | ||
594 | ch[0] = CVBS; | ||
483 | } else { | 595 | } else { |
484 | int luma = vid_input & 0xf0; | 596 | int luma = vid_input & 0xf0; |
485 | int chroma = vid_input & 0xf00; | 597 | int chroma = vid_input & 0xf00; |
@@ -493,26 +605,45 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input, | |||
493 | vid_input); | 605 | vid_input); |
494 | return -EINVAL; | 606 | return -EINVAL; |
495 | } | 607 | } |
496 | reg = 0xf0 + ((luma - CX18_AV_SVIDEO_LUMA1) >> 4); | 608 | afe_mux_cfg = 0xf0 + ((luma - CX18_AV_SVIDEO_LUMA1) >> 4); |
609 | ch[0] = Y; | ||
497 | if (chroma >= CX18_AV_SVIDEO_CHROMA7) { | 610 | if (chroma >= CX18_AV_SVIDEO_CHROMA7) { |
498 | reg &= 0x3f; | 611 | afe_mux_cfg &= 0x3f; |
499 | reg |= (chroma - CX18_AV_SVIDEO_CHROMA7) >> 2; | 612 | afe_mux_cfg |= (chroma - CX18_AV_SVIDEO_CHROMA7) >> 2; |
613 | ch[2] = C; | ||
500 | } else { | 614 | } else { |
501 | reg &= 0xcf; | 615 | afe_mux_cfg &= 0xcf; |
502 | reg |= (chroma - CX18_AV_SVIDEO_CHROMA4) >> 4; | 616 | afe_mux_cfg |= (chroma - CX18_AV_SVIDEO_CHROMA4) >> 4; |
617 | ch[1] = C; | ||
503 | } | 618 | } |
504 | } | 619 | } |
620 | /* TODO: LeadTek WinFast DVR3100 H & WinFast PVR2100 can do Y/Pb/Pr */ | ||
505 | 621 | ||
506 | switch (aud_input) { | 622 | switch (aud_input) { |
507 | case CX18_AV_AUDIO_SERIAL1: | 623 | case CX18_AV_AUDIO_SERIAL1: |
508 | case CX18_AV_AUDIO_SERIAL2: | 624 | case CX18_AV_AUDIO_SERIAL2: |
509 | /* do nothing, use serial audio input */ | 625 | /* do nothing, use serial audio input */ |
510 | break; | 626 | break; |
511 | case CX18_AV_AUDIO4: reg &= ~0x30; break; | 627 | case CX18_AV_AUDIO4: |
512 | case CX18_AV_AUDIO5: reg &= ~0x30; reg |= 0x10; break; | 628 | afe_mux_cfg &= ~0x30; |
513 | case CX18_AV_AUDIO6: reg &= ~0x30; reg |= 0x20; break; | 629 | ch[1] = SIF; |
514 | case CX18_AV_AUDIO7: reg &= ~0xc0; break; | 630 | break; |
515 | case CX18_AV_AUDIO8: reg &= ~0xc0; reg |= 0x40; break; | 631 | case CX18_AV_AUDIO5: |
632 | afe_mux_cfg = (afe_mux_cfg & ~0x30) | 0x10; | ||
633 | ch[1] = SIF; | ||
634 | break; | ||
635 | case CX18_AV_AUDIO6: | ||
636 | afe_mux_cfg = (afe_mux_cfg & ~0x30) | 0x20; | ||
637 | ch[1] = SIF; | ||
638 | break; | ||
639 | case CX18_AV_AUDIO7: | ||
640 | afe_mux_cfg &= ~0xc0; | ||
641 | ch[2] = SIF; | ||
642 | break; | ||
643 | case CX18_AV_AUDIO8: | ||
644 | afe_mux_cfg = (afe_mux_cfg & ~0xc0) | 0x40; | ||
645 | ch[2] = SIF; | ||
646 | break; | ||
516 | 647 | ||
517 | default: | 648 | default: |
518 | CX18_ERR_DEV(sd, "0x%04x is not a valid audio input!\n", | 649 | CX18_ERR_DEV(sd, "0x%04x is not a valid audio input!\n", |
@@ -520,24 +651,65 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input, | |||
520 | return -EINVAL; | 651 | return -EINVAL; |
521 | } | 652 | } |
522 | 653 | ||
523 | cx18_av_write_expect(cx, 0x103, reg, reg, 0xf7); | 654 | /* Set up analog front end multiplexers */ |
655 | cx18_av_write_expect(cx, 0x103, afe_mux_cfg, afe_mux_cfg, 0xf7); | ||
524 | /* Set INPUT_MODE to Composite (0) or S-Video (1) */ | 656 | /* Set INPUT_MODE to Composite (0) or S-Video (1) */ |
525 | cx18_av_and_or(cx, 0x401, ~0x6, is_composite ? 0 : 0x02); | 657 | cx18_av_and_or(cx, 0x401, ~0x6, ch[0] == CVBS ? 0 : 0x02); |
526 | 658 | ||
527 | /* Set CH_SEL_ADC2 to 1 if input comes from CH3 */ | 659 | /* Set CH_SEL_ADC2 to 1 if input comes from CH3 */ |
528 | v = cx18_av_read(cx, 0x102); | 660 | adc2_cfg = cx18_av_read(cx, 0x102); |
529 | if (reg & 0x80) | 661 | if (ch[2] == NONE) |
530 | v &= ~0x2; | 662 | adc2_cfg &= ~0x2; /* No sig on CH3, set ADC2 to CH2 for input */ |
531 | else | 663 | else |
532 | v |= 0x2; | 664 | adc2_cfg |= 0x2; /* Signal on CH3, set ADC2 to CH3 for input */ |
665 | |||
533 | /* Set DUAL_MODE_ADC2 to 1 if input comes from both CH2 and CH3 */ | 666 | /* Set DUAL_MODE_ADC2 to 1 if input comes from both CH2 and CH3 */ |
534 | if ((reg & 0xc0) != 0xc0 && (reg & 0x30) != 0x30) | 667 | if (ch[1] != NONE && ch[2] != NONE) |
535 | v |= 0x4; | 668 | adc2_cfg |= 0x4; /* Set dual mode */ |
536 | else | 669 | else |
537 | v &= ~0x4; | 670 | adc2_cfg &= ~0x4; /* Clear dual mode */ |
538 | cx18_av_write_expect(cx, 0x102, v, v, 0x17); | 671 | cx18_av_write_expect(cx, 0x102, adc2_cfg, adc2_cfg, 0x17); |
672 | |||
673 | /* Configure the analog front end */ | ||
674 | afe_cfg = cx18_av_read4(cx, CXADEC_AFE_CTRL); | ||
675 | afe_cfg &= 0xff000000; | ||
676 | afe_cfg |= 0x00005000; /* CHROMA_IN, AUD_IN: ADC2; LUMA_IN: ADC1 */ | ||
677 | if (ch[1] != NONE && ch[2] != NONE) | ||
678 | afe_cfg |= 0x00000030; /* half_bw_ch[2-3] since in dual mode */ | ||
679 | |||
680 | for (i = 0; i < 3; i++) { | ||
681 | switch (ch[i]) { | ||
682 | default: | ||
683 | case NONE: | ||
684 | /* CLAMP_SEL = Fixed to midcode clamp level */ | ||
685 | afe_cfg |= (0x00000200 << i); | ||
686 | break; | ||
687 | case CVBS: | ||
688 | case Y: | ||
689 | if (i > 0) | ||
690 | afe_cfg |= 0x00002000; /* LUMA_IN_SEL: ADC2 */ | ||
691 | break; | ||
692 | case C: | ||
693 | case Pb: | ||
694 | case Pr: | ||
695 | /* CLAMP_SEL = Fixed to midcode clamp level */ | ||
696 | afe_cfg |= (0x00000200 << i); | ||
697 | if (i == 0 && ch[i] == C) | ||
698 | afe_cfg &= ~0x00001000; /* CHROMA_IN_SEL ADC1 */ | ||
699 | break; | ||
700 | case SIF: | ||
701 | /* | ||
702 | * VGA_GAIN_SEL = Audio Decoder | ||
703 | * CLAMP_SEL = Fixed to midcode clamp level | ||
704 | */ | ||
705 | afe_cfg |= (0x00000240 << i); | ||
706 | if (i == 0) | ||
707 | afe_cfg &= ~0x00004000; /* AUD_IN_SEL ADC1 */ | ||
708 | break; | ||
709 | } | ||
710 | } | ||
539 | 711 | ||
540 | /*cx18_av_and_or4(cx, 0x104, ~0x001b4180, 0x00004180);*/ | 712 | cx18_av_write4(cx, CXADEC_AFE_CTRL, afe_cfg); |
541 | 713 | ||
542 | state->vid_input = vid_input; | 714 | state->vid_input = vid_input; |
543 | state->aud_input = aud_input; | 715 | state->aud_input = aud_input; |
@@ -858,9 +1030,9 @@ static int cx18_av_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) | |||
858 | * cx18_av_std_setup(), above standard values: | 1030 | * cx18_av_std_setup(), above standard values: |
859 | * | 1031 | * |
860 | * 480 + 1 for 60 Hz systems | 1032 | * 480 + 1 for 60 Hz systems |
861 | * 576 + 4 for 50 Hz systems | 1033 | * 576 + 3 for 50 Hz systems |
862 | */ | 1034 | */ |
863 | Vlines = pix->height + (is_50Hz ? 4 : 1); | 1035 | Vlines = pix->height + (is_50Hz ? 3 : 1); |
864 | 1036 | ||
865 | /* | 1037 | /* |
866 | * Invalid height and width scaling requests are: | 1038 | * Invalid height and width scaling requests are: |
diff --git a/drivers/media/video/cx18/cx18-av-firmware.c b/drivers/media/video/cx18/cx18-av-firmware.c index 49a55cc8d839..b9e8cc5d264a 100644 --- a/drivers/media/video/cx18/cx18-av-firmware.c +++ b/drivers/media/video/cx18/cx18-av-firmware.c | |||
@@ -24,15 +24,63 @@ | |||
24 | #include "cx18-io.h" | 24 | #include "cx18-io.h" |
25 | #include <linux/firmware.h> | 25 | #include <linux/firmware.h> |
26 | 26 | ||
27 | #define CX18_AUDIO_ENABLE 0xc72014 | 27 | #define CX18_AUDIO_ENABLE 0xc72014 |
28 | #define CX18_AI1_MUX_MASK 0x30 | ||
29 | #define CX18_AI1_MUX_I2S1 0x00 | ||
30 | #define CX18_AI1_MUX_I2S2 0x10 | ||
31 | #define CX18_AI1_MUX_843_I2S 0x20 | ||
32 | #define CX18_AI1_MUX_INVALID 0x30 | ||
33 | |||
28 | #define FWFILE "v4l-cx23418-dig.fw" | 34 | #define FWFILE "v4l-cx23418-dig.fw" |
29 | 35 | ||
36 | static int cx18_av_verifyfw(struct cx18 *cx, const struct firmware *fw) | ||
37 | { | ||
38 | struct v4l2_subdev *sd = &cx->av_state.sd; | ||
39 | int ret = 0; | ||
40 | const u8 *data; | ||
41 | u32 size; | ||
42 | int addr; | ||
43 | u32 expected, dl_control; | ||
44 | |||
45 | /* Ensure we put the 8051 in reset and enable firmware upload mode */ | ||
46 | dl_control = cx18_av_read4(cx, CXADEC_DL_CTL); | ||
47 | do { | ||
48 | dl_control &= 0x00ffffff; | ||
49 | dl_control |= 0x0f000000; | ||
50 | cx18_av_write4_noretry(cx, CXADEC_DL_CTL, dl_control); | ||
51 | dl_control = cx18_av_read4(cx, CXADEC_DL_CTL); | ||
52 | } while ((dl_control & 0xff000000) != 0x0f000000); | ||
53 | |||
54 | /* Read and auto increment until at address 0x0000 */ | ||
55 | while (dl_control & 0x3fff) | ||
56 | dl_control = cx18_av_read4(cx, CXADEC_DL_CTL); | ||
57 | |||
58 | data = fw->data; | ||
59 | size = fw->size; | ||
60 | for (addr = 0; addr < size; addr++) { | ||
61 | dl_control &= 0xffff3fff; /* ignore top 2 bits of address */ | ||
62 | expected = 0x0f000000 | ((u32)data[addr] << 16) | addr; | ||
63 | if (expected != dl_control) { | ||
64 | CX18_ERR_DEV(sd, "verification of %s firmware load " | ||
65 | "failed: expected %#010x got %#010x\n", | ||
66 | FWFILE, expected, dl_control); | ||
67 | ret = -EIO; | ||
68 | break; | ||
69 | } | ||
70 | dl_control = cx18_av_read4(cx, CXADEC_DL_CTL); | ||
71 | } | ||
72 | if (ret == 0) | ||
73 | CX18_INFO_DEV(sd, "verified load of %s firmware (%d bytes)\n", | ||
74 | FWFILE, size); | ||
75 | return ret; | ||
76 | } | ||
77 | |||
30 | int cx18_av_loadfw(struct cx18 *cx) | 78 | int cx18_av_loadfw(struct cx18 *cx) |
31 | { | 79 | { |
32 | struct v4l2_subdev *sd = &cx->av_state.sd; | 80 | struct v4l2_subdev *sd = &cx->av_state.sd; |
33 | const struct firmware *fw = NULL; | 81 | const struct firmware *fw = NULL; |
34 | u32 size; | 82 | u32 size; |
35 | u32 v; | 83 | u32 u, v; |
36 | const u8 *ptr; | 84 | const u8 *ptr; |
37 | int i; | 85 | int i; |
38 | int retries1 = 0; | 86 | int retries1 = 0; |
@@ -95,6 +143,12 @@ int cx18_av_loadfw(struct cx18 *cx) | |||
95 | } | 143 | } |
96 | 144 | ||
97 | cx18_av_write4_expect(cx, CXADEC_DL_CTL, | 145 | cx18_av_write4_expect(cx, CXADEC_DL_CTL, |
146 | 0x03000000 | fw->size, 0x03000000, 0x13000000); | ||
147 | |||
148 | CX18_INFO_DEV(sd, "loaded %s firmware (%d bytes)\n", FWFILE, size); | ||
149 | |||
150 | if (cx18_av_verifyfw(cx, fw) == 0) | ||
151 | cx18_av_write4_expect(cx, CXADEC_DL_CTL, | ||
98 | 0x13000000 | fw->size, 0x13000000, 0x13000000); | 152 | 0x13000000 | fw->size, 0x13000000, 0x13000000); |
99 | 153 | ||
100 | /* Output to the 416 */ | 154 | /* Output to the 416 */ |
@@ -135,6 +189,28 @@ int cx18_av_loadfw(struct cx18 *cx) | |||
135 | cx18_write_reg_expect(cx, v & 0xFFFFFBFF, CX18_AUDIO_ENABLE, | 189 | cx18_write_reg_expect(cx, v & 0xFFFFFBFF, CX18_AUDIO_ENABLE, |
136 | 0, 0x400); | 190 | 0, 0x400); |
137 | 191 | ||
192 | /* Toggle the AI1 MUX */ | ||
193 | v = cx18_read_reg(cx, CX18_AUDIO_ENABLE); | ||
194 | u = v & CX18_AI1_MUX_MASK; | ||
195 | v &= ~CX18_AI1_MUX_MASK; | ||
196 | if (u == CX18_AI1_MUX_843_I2S || u == CX18_AI1_MUX_INVALID) { | ||
197 | /* Switch to I2S1 */ | ||
198 | v |= CX18_AI1_MUX_I2S1; | ||
199 | cx18_write_reg_expect(cx, v | 0xb00, CX18_AUDIO_ENABLE, | ||
200 | v, CX18_AI1_MUX_MASK); | ||
201 | /* Switch back to the A/V decoder core I2S output */ | ||
202 | v = (v & ~CX18_AI1_MUX_MASK) | CX18_AI1_MUX_843_I2S; | ||
203 | } else { | ||
204 | /* Switch to the A/V decoder core I2S output */ | ||
205 | v |= CX18_AI1_MUX_843_I2S; | ||
206 | cx18_write_reg_expect(cx, v | 0xb00, CX18_AUDIO_ENABLE, | ||
207 | v, CX18_AI1_MUX_MASK); | ||
208 | /* Switch back to I2S1 or I2S2 */ | ||
209 | v = (v & ~CX18_AI1_MUX_MASK) | u; | ||
210 | } | ||
211 | cx18_write_reg_expect(cx, v | 0xb00, CX18_AUDIO_ENABLE, | ||
212 | v, CX18_AI1_MUX_MASK); | ||
213 | |||
138 | /* Enable WW auto audio standard detection */ | 214 | /* Enable WW auto audio standard detection */ |
139 | v = cx18_av_read4(cx, CXADEC_STD_DET_CTL); | 215 | v = cx18_av_read4(cx, CXADEC_STD_DET_CTL); |
140 | v |= 0xFF; /* Auto by default */ | 216 | v |= 0xFF; /* Auto by default */ |
@@ -143,7 +219,5 @@ int cx18_av_loadfw(struct cx18 *cx) | |||
143 | cx18_av_write4_expect(cx, CXADEC_STD_DET_CTL, v, v, 0x3F00FFFF); | 219 | cx18_av_write4_expect(cx, CXADEC_STD_DET_CTL, v, v, 0x3F00FFFF); |
144 | 220 | ||
145 | release_firmware(fw); | 221 | release_firmware(fw); |
146 | |||
147 | CX18_INFO_DEV(sd, "loaded %s firmware (%d bytes)\n", FWFILE, size); | ||
148 | return 0; | 222 | return 0; |
149 | } | 223 | } |
diff --git a/drivers/media/video/cx18/cx18-av-vbi.c b/drivers/media/video/cx18/cx18-av-vbi.c index 23b31670bf1d..a51732bcca4b 100644 --- a/drivers/media/video/cx18/cx18-av-vbi.c +++ b/drivers/media/video/cx18/cx18-av-vbi.c | |||
@@ -255,8 +255,8 @@ int cx18_av_vbi_s_fmt(struct cx18 *cx, struct v4l2_format *fmt) | |||
255 | } | 255 | } |
256 | 256 | ||
257 | cx18_av_write(cx, 0x43c, 0x16); | 257 | cx18_av_write(cx, 0x43c, 0x16); |
258 | /* FIXME - should match vblank set in cx18_av_std_setup() */ | 258 | /* Should match vblank set in cx18_av_std_setup() */ |
259 | cx18_av_write(cx, 0x474, is_pal ? 0x2a : 26); | 259 | cx18_av_write(cx, 0x474, is_pal ? 38 : 26); |
260 | return 0; | 260 | return 0; |
261 | } | 261 | } |
262 | 262 | ||
diff --git a/drivers/media/video/cx18/cx18-cards.c b/drivers/media/video/cx18/cx18-cards.c index 9bc221837847..c92a25036f0e 100644 --- a/drivers/media/video/cx18/cx18-cards.c +++ b/drivers/media/video/cx18/cx18-cards.c | |||
@@ -340,13 +340,12 @@ static const struct cx18_card cx18_card_toshiba_qosmio_dvbt = { | |||
340 | 340 | ||
341 | static const struct cx18_card_pci_info cx18_pci_leadtek_pvr2100[] = { | 341 | static const struct cx18_card_pci_info cx18_pci_leadtek_pvr2100[] = { |
342 | { PCI_DEVICE_ID_CX23418, CX18_PCI_ID_LEADTEK, 0x6f27 }, /* PVR2100 */ | 342 | { PCI_DEVICE_ID_CX23418, CX18_PCI_ID_LEADTEK, 0x6f27 }, /* PVR2100 */ |
343 | { PCI_DEVICE_ID_CX23418, CX18_PCI_ID_LEADTEK, 0x6690 }, /* DVR3100 H */ | ||
344 | { 0, 0, 0 } | 343 | { 0, 0, 0 } |
345 | }; | 344 | }; |
346 | 345 | ||
347 | static const struct cx18_card cx18_card_leadtek_pvr2100 = { | 346 | static const struct cx18_card cx18_card_leadtek_pvr2100 = { |
348 | .type = CX18_CARD_LEADTEK_PVR2100, | 347 | .type = CX18_CARD_LEADTEK_PVR2100, |
349 | .name = "Leadtek WinFast PVR2100/DVR3100 H", | 348 | .name = "Leadtek WinFast PVR2100", |
350 | .comment = "Experimenters and photos needed for device to work well.\n" | 349 | .comment = "Experimenters and photos needed for device to work well.\n" |
351 | "\tTo help, mail the ivtv-devel list (www.ivtvdriver.org).\n", | 350 | "\tTo help, mail the ivtv-devel list (www.ivtvdriver.org).\n", |
352 | .v4l2_capabilities = CX18_CAP_ENCODER, | 351 | .v4l2_capabilities = CX18_CAP_ENCODER, |
@@ -365,15 +364,12 @@ static const struct cx18_card cx18_card_leadtek_pvr2100 = { | |||
365 | { CX18_CARD_INPUT_LINE_IN1, CX18_AV_AUDIO_SERIAL1, 1 }, | 364 | { CX18_CARD_INPUT_LINE_IN1, CX18_AV_AUDIO_SERIAL1, 1 }, |
366 | }, | 365 | }, |
367 | .tuners = { | 366 | .tuners = { |
368 | /* XC3028 tuner */ | 367 | /* XC2028 tuner */ |
369 | { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 }, | 368 | { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 }, |
370 | }, | 369 | }, |
371 | .radio_input = { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 2 }, | 370 | .radio_input = { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 2 }, |
372 | .ddr = { | 371 | .ddr = { |
373 | /* | 372 | /* Pointer to proper DDR config values provided by Terry Wu */ |
374 | * Pointer to proper DDR config values provided by | ||
375 | * Terry Wu <terrywu at leadtek.com.tw> | ||
376 | */ | ||
377 | .chip_config = 0x303, | 373 | .chip_config = 0x303, |
378 | .refresh = 0x3bb, | 374 | .refresh = 0x3bb, |
379 | .timing1 = 0x24220e83, | 375 | .timing1 = 0x24220e83, |
@@ -392,6 +388,58 @@ static const struct cx18_card cx18_card_leadtek_pvr2100 = { | |||
392 | 388 | ||
393 | /* ------------------------------------------------------------------------- */ | 389 | /* ------------------------------------------------------------------------- */ |
394 | 390 | ||
391 | /* Leadtek WinFast DVR3100 H */ | ||
392 | |||
393 | static const struct cx18_card_pci_info cx18_pci_leadtek_dvr3100h[] = { | ||
394 | { PCI_DEVICE_ID_CX23418, CX18_PCI_ID_LEADTEK, 0x6690 }, /* DVR3100 H */ | ||
395 | { 0, 0, 0 } | ||
396 | }; | ||
397 | |||
398 | static const struct cx18_card cx18_card_leadtek_dvr3100h = { | ||
399 | .type = CX18_CARD_LEADTEK_DVR3100H, | ||
400 | .name = "Leadtek WinFast DVR3100 H", | ||
401 | .comment = "Simultaneous DVB-T and Analog capture supported,\n" | ||
402 | "\texcept when capturing Analog from the antenna input.\n", | ||
403 | .v4l2_capabilities = CX18_CAP_ENCODER, | ||
404 | .hw_audio_ctrl = CX18_HW_418_AV, | ||
405 | .hw_muxer = CX18_HW_GPIO_MUX, | ||
406 | .hw_all = CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_MUX | | ||
407 | CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL, | ||
408 | .video_inputs = { | ||
409 | { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 }, | ||
410 | { CX18_CARD_INPUT_SVIDEO1, 1, | ||
411 | CX18_AV_SVIDEO_LUMA3 | CX18_AV_SVIDEO_CHROMA4 }, | ||
412 | { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE7 }, | ||
413 | }, | ||
414 | .audio_inputs = { | ||
415 | { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 0 }, | ||
416 | { CX18_CARD_INPUT_LINE_IN1, CX18_AV_AUDIO_SERIAL1, 1 }, | ||
417 | }, | ||
418 | .tuners = { | ||
419 | /* XC3028 tuner */ | ||
420 | { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 }, | ||
421 | }, | ||
422 | .radio_input = { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 2 }, | ||
423 | .ddr = { | ||
424 | /* Pointer to proper DDR config values provided by Terry Wu */ | ||
425 | .chip_config = 0x303, | ||
426 | .refresh = 0x3bb, | ||
427 | .timing1 = 0x24220e83, | ||
428 | .timing2 = 0x1f, | ||
429 | .tune_lane = 0, | ||
430 | .initial_emrs = 0x2, | ||
431 | }, | ||
432 | .gpio_init.initial_value = 0x6, | ||
433 | .gpio_init.direction = 0x7, | ||
434 | .gpio_audio_input = { .mask = 0x7, | ||
435 | .tuner = 0x6, .linein = 0x2, .radio = 0x2 }, | ||
436 | .xceive_pin = 1, | ||
437 | .pci_list = cx18_pci_leadtek_dvr3100h, | ||
438 | .i2c = &cx18_i2c_std, | ||
439 | }; | ||
440 | |||
441 | /* ------------------------------------------------------------------------- */ | ||
442 | |||
395 | static const struct cx18_card *cx18_card_list[] = { | 443 | static const struct cx18_card *cx18_card_list[] = { |
396 | &cx18_card_hvr1600_esmt, | 444 | &cx18_card_hvr1600_esmt, |
397 | &cx18_card_hvr1600_samsung, | 445 | &cx18_card_hvr1600_samsung, |
@@ -400,6 +448,7 @@ static const struct cx18_card *cx18_card_list[] = { | |||
400 | &cx18_card_cnxt_raptor_pal, | 448 | &cx18_card_cnxt_raptor_pal, |
401 | &cx18_card_toshiba_qosmio_dvbt, | 449 | &cx18_card_toshiba_qosmio_dvbt, |
402 | &cx18_card_leadtek_pvr2100, | 450 | &cx18_card_leadtek_pvr2100, |
451 | &cx18_card_leadtek_dvr3100h, | ||
403 | }; | 452 | }; |
404 | 453 | ||
405 | const struct cx18_card *cx18_get_card(u16 index) | 454 | const struct cx18_card *cx18_get_card(u16 index) |
diff --git a/drivers/media/video/cx18/cx18-controls.c b/drivers/media/video/cx18/cx18-controls.c index 82fc2f9d4021..8e35c3aed544 100644 --- a/drivers/media/video/cx18/cx18-controls.c +++ b/drivers/media/video/cx18/cx18-controls.c | |||
@@ -176,8 +176,10 @@ static int cx18_setup_vbi_fmt(struct cx18 *cx, | |||
176 | return -EBUSY; | 176 | return -EBUSY; |
177 | 177 | ||
178 | if (fmt != V4L2_MPEG_STREAM_VBI_FMT_IVTV || | 178 | if (fmt != V4L2_MPEG_STREAM_VBI_FMT_IVTV || |
179 | type != V4L2_MPEG_STREAM_TYPE_MPEG2_PS) { | 179 | !(type == V4L2_MPEG_STREAM_TYPE_MPEG2_PS || |
180 | /* We don't do VBI insertion aside from IVTV format in a PS */ | 180 | type == V4L2_MPEG_STREAM_TYPE_MPEG2_DVD || |
181 | type == V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD)) { | ||
182 | /* Only IVTV fmt VBI insertion & only MPEG-2 PS type streams */ | ||
181 | cx->vbi.insert_mpeg = V4L2_MPEG_STREAM_VBI_FMT_NONE; | 183 | cx->vbi.insert_mpeg = V4L2_MPEG_STREAM_VBI_FMT_NONE; |
182 | CX18_DEBUG_INFO("disabled insertion of sliced VBI data into " | 184 | CX18_DEBUG_INFO("disabled insertion of sliced VBI data into " |
183 | "the MPEG stream\n"); | 185 | "the MPEG stream\n"); |
diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c index 49b1c3d7b1a8..92026e82e10e 100644 --- a/drivers/media/video/cx18/cx18-driver.c +++ b/drivers/media/video/cx18/cx18-driver.c | |||
@@ -30,6 +30,7 @@ | |||
30 | #include "cx18-irq.h" | 30 | #include "cx18-irq.h" |
31 | #include "cx18-gpio.h" | 31 | #include "cx18-gpio.h" |
32 | #include "cx18-firmware.h" | 32 | #include "cx18-firmware.h" |
33 | #include "cx18-queue.h" | ||
33 | #include "cx18-streams.h" | 34 | #include "cx18-streams.h" |
34 | #include "cx18-av-core.h" | 35 | #include "cx18-av-core.h" |
35 | #include "cx18-scb.h" | 36 | #include "cx18-scb.h" |
@@ -151,7 +152,8 @@ MODULE_PARM_DESC(cardtype, | |||
151 | "\t\t\t 4 = Yuan MPC718\n" | 152 | "\t\t\t 4 = Yuan MPC718\n" |
152 | "\t\t\t 5 = Conexant Raptor PAL/SECAM\n" | 153 | "\t\t\t 5 = Conexant Raptor PAL/SECAM\n" |
153 | "\t\t\t 6 = Toshiba Qosmio DVB-T/Analog\n" | 154 | "\t\t\t 6 = Toshiba Qosmio DVB-T/Analog\n" |
154 | "\t\t\t 7 = Leadtek WinFast PVR2100/DVR3100 H\n" | 155 | "\t\t\t 7 = Leadtek WinFast PVR2100\n" |
156 | "\t\t\t 8 = Leadtek WinFast DVR3100 H\n" | ||
155 | "\t\t\t 0 = Autodetect (default)\n" | 157 | "\t\t\t 0 = Autodetect (default)\n" |
156 | "\t\t\t-1 = Ignore this card\n\t\t"); | 158 | "\t\t\t-1 = Ignore this card\n\t\t"); |
157 | MODULE_PARM_DESC(pal, "Set PAL standard: B, G, H, D, K, I, M, N, Nc, 60"); | 159 | MODULE_PARM_DESC(pal, "Set PAL standard: B, G, H, D, K, I, M, N, Nc, 60"); |
@@ -312,7 +314,7 @@ static void cx18_process_eeprom(struct cx18 *cx) | |||
312 | CX18_INFO("Autodetected %s\n", cx->card_name); | 314 | CX18_INFO("Autodetected %s\n", cx->card_name); |
313 | 315 | ||
314 | if (tv.tuner_type == TUNER_ABSENT) | 316 | if (tv.tuner_type == TUNER_ABSENT) |
315 | CX18_ERR("tveeprom cannot autodetect tuner!"); | 317 | CX18_ERR("tveeprom cannot autodetect tuner!\n"); |
316 | 318 | ||
317 | if (cx->options.tuner == -1) | 319 | if (cx->options.tuner == -1) |
318 | cx->options.tuner = tv.tuner_type; | 320 | cx->options.tuner = tv.tuner_type; |
@@ -546,6 +548,40 @@ done: | |||
546 | cx->card_i2c = cx->card->i2c; | 548 | cx->card_i2c = cx->card->i2c; |
547 | } | 549 | } |
548 | 550 | ||
551 | static int __devinit cx18_create_in_workq(struct cx18 *cx) | ||
552 | { | ||
553 | snprintf(cx->in_workq_name, sizeof(cx->in_workq_name), "%s-in", | ||
554 | cx->v4l2_dev.name); | ||
555 | cx->in_work_queue = create_singlethread_workqueue(cx->in_workq_name); | ||
556 | if (cx->in_work_queue == NULL) { | ||
557 | CX18_ERR("Unable to create incoming mailbox handler thread\n"); | ||
558 | return -ENOMEM; | ||
559 | } | ||
560 | return 0; | ||
561 | } | ||
562 | |||
563 | static int __devinit cx18_create_out_workq(struct cx18 *cx) | ||
564 | { | ||
565 | snprintf(cx->out_workq_name, sizeof(cx->out_workq_name), "%s-out", | ||
566 | cx->v4l2_dev.name); | ||
567 | cx->out_work_queue = create_workqueue(cx->out_workq_name); | ||
568 | if (cx->out_work_queue == NULL) { | ||
569 | CX18_ERR("Unable to create outgoing mailbox handler threads\n"); | ||
570 | return -ENOMEM; | ||
571 | } | ||
572 | return 0; | ||
573 | } | ||
574 | |||
575 | static void __devinit cx18_init_in_work_orders(struct cx18 *cx) | ||
576 | { | ||
577 | int i; | ||
578 | for (i = 0; i < CX18_MAX_IN_WORK_ORDERS; i++) { | ||
579 | cx->in_work_order[i].cx = cx; | ||
580 | cx->in_work_order[i].str = cx->epu_debug_str; | ||
581 | INIT_WORK(&cx->in_work_order[i].work, cx18_in_work_handler); | ||
582 | } | ||
583 | } | ||
584 | |||
549 | /* Precondition: the cx18 structure has been memset to 0. Only | 585 | /* Precondition: the cx18 structure has been memset to 0. Only |
550 | the dev and instance fields have been filled in. | 586 | the dev and instance fields have been filled in. |
551 | No assumptions on the card type may be made here (see cx18_init_struct2 | 587 | No assumptions on the card type may be made here (see cx18_init_struct2 |
@@ -553,7 +589,7 @@ done: | |||
553 | */ | 589 | */ |
554 | static int __devinit cx18_init_struct1(struct cx18 *cx) | 590 | static int __devinit cx18_init_struct1(struct cx18 *cx) |
555 | { | 591 | { |
556 | int i; | 592 | int ret; |
557 | 593 | ||
558 | cx->base_addr = pci_resource_start(cx->pci_dev, 0); | 594 | cx->base_addr = pci_resource_start(cx->pci_dev, 0); |
559 | 595 | ||
@@ -562,18 +598,18 @@ static int __devinit cx18_init_struct1(struct cx18 *cx) | |||
562 | mutex_init(&cx->epu2apu_mb_lock); | 598 | mutex_init(&cx->epu2apu_mb_lock); |
563 | mutex_init(&cx->epu2cpu_mb_lock); | 599 | mutex_init(&cx->epu2cpu_mb_lock); |
564 | 600 | ||
565 | cx->work_queue = create_singlethread_workqueue(cx->v4l2_dev.name); | 601 | ret = cx18_create_out_workq(cx); |
566 | if (cx->work_queue == NULL) { | 602 | if (ret) |
567 | CX18_ERR("Unable to create work hander thread\n"); | 603 | return ret; |
568 | return -ENOMEM; | ||
569 | } | ||
570 | 604 | ||
571 | for (i = 0; i < CX18_MAX_EPU_WORK_ORDERS; i++) { | 605 | ret = cx18_create_in_workq(cx); |
572 | cx->epu_work_order[i].cx = cx; | 606 | if (ret) { |
573 | cx->epu_work_order[i].str = cx->epu_debug_str; | 607 | destroy_workqueue(cx->out_work_queue); |
574 | INIT_WORK(&cx->epu_work_order[i].work, cx18_epu_work_handler); | 608 | return ret; |
575 | } | 609 | } |
576 | 610 | ||
611 | cx18_init_in_work_orders(cx); | ||
612 | |||
577 | /* start counting open_id at 1 */ | 613 | /* start counting open_id at 1 */ |
578 | cx->open_id = 1; | 614 | cx->open_id = 1; |
579 | 615 | ||
@@ -759,17 +795,17 @@ static int __devinit cx18_probe(struct pci_dev *pci_dev, | |||
759 | retval = -ENODEV; | 795 | retval = -ENODEV; |
760 | goto err; | 796 | goto err; |
761 | } | 797 | } |
762 | if (cx18_init_struct1(cx)) { | 798 | |
763 | retval = -ENOMEM; | 799 | retval = cx18_init_struct1(cx); |
800 | if (retval) | ||
764 | goto err; | 801 | goto err; |
765 | } | ||
766 | 802 | ||
767 | CX18_DEBUG_INFO("base addr: 0x%08x\n", cx->base_addr); | 803 | CX18_DEBUG_INFO("base addr: 0x%08x\n", cx->base_addr); |
768 | 804 | ||
769 | /* PCI Device Setup */ | 805 | /* PCI Device Setup */ |
770 | retval = cx18_setup_pci(cx, pci_dev, pci_id); | 806 | retval = cx18_setup_pci(cx, pci_dev, pci_id); |
771 | if (retval != 0) | 807 | if (retval != 0) |
772 | goto free_workqueue; | 808 | goto free_workqueues; |
773 | 809 | ||
774 | /* map io memory */ | 810 | /* map io memory */ |
775 | CX18_DEBUG_INFO("attempting ioremap at 0x%08x len 0x%08x\n", | 811 | CX18_DEBUG_INFO("attempting ioremap at 0x%08x len 0x%08x\n", |
@@ -943,8 +979,9 @@ free_map: | |||
943 | cx18_iounmap(cx); | 979 | cx18_iounmap(cx); |
944 | free_mem: | 980 | free_mem: |
945 | release_mem_region(cx->base_addr, CX18_MEM_SIZE); | 981 | release_mem_region(cx->base_addr, CX18_MEM_SIZE); |
946 | free_workqueue: | 982 | free_workqueues: |
947 | destroy_workqueue(cx->work_queue); | 983 | destroy_workqueue(cx->in_work_queue); |
984 | destroy_workqueue(cx->out_work_queue); | ||
948 | err: | 985 | err: |
949 | if (retval == 0) | 986 | if (retval == 0) |
950 | retval = -ENODEV; | 987 | retval = -ENODEV; |
@@ -1053,11 +1090,19 @@ int cx18_init_on_first_open(struct cx18 *cx) | |||
1053 | return 0; | 1090 | return 0; |
1054 | } | 1091 | } |
1055 | 1092 | ||
1056 | static void cx18_cancel_epu_work_orders(struct cx18 *cx) | 1093 | static void cx18_cancel_in_work_orders(struct cx18 *cx) |
1057 | { | 1094 | { |
1058 | int i; | 1095 | int i; |
1059 | for (i = 0; i < CX18_MAX_EPU_WORK_ORDERS; i++) | 1096 | for (i = 0; i < CX18_MAX_IN_WORK_ORDERS; i++) |
1060 | cancel_work_sync(&cx->epu_work_order[i].work); | 1097 | cancel_work_sync(&cx->in_work_order[i].work); |
1098 | } | ||
1099 | |||
1100 | static void cx18_cancel_out_work_orders(struct cx18 *cx) | ||
1101 | { | ||
1102 | int i; | ||
1103 | for (i = 0; i < CX18_MAX_STREAMS; i++) | ||
1104 | if (&cx->streams[i].video_dev != NULL) | ||
1105 | cancel_work_sync(&cx->streams[i].out_work_order); | ||
1061 | } | 1106 | } |
1062 | 1107 | ||
1063 | static void cx18_remove(struct pci_dev *pci_dev) | 1108 | static void cx18_remove(struct pci_dev *pci_dev) |
@@ -1073,15 +1118,20 @@ static void cx18_remove(struct pci_dev *pci_dev) | |||
1073 | if (atomic_read(&cx->tot_capturing) > 0) | 1118 | if (atomic_read(&cx->tot_capturing) > 0) |
1074 | cx18_stop_all_captures(cx); | 1119 | cx18_stop_all_captures(cx); |
1075 | 1120 | ||
1076 | /* Interrupts */ | 1121 | /* Stop interrupts that cause incoming work to be queued */ |
1077 | cx18_sw1_irq_disable(cx, IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU); | 1122 | cx18_sw1_irq_disable(cx, IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU); |
1123 | |||
1124 | /* Incoming work can cause outgoing work, so clean up incoming first */ | ||
1125 | cx18_cancel_in_work_orders(cx); | ||
1126 | cx18_cancel_out_work_orders(cx); | ||
1127 | |||
1128 | /* Stop ack interrupts that may have been needed for work to finish */ | ||
1078 | cx18_sw2_irq_disable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK); | 1129 | cx18_sw2_irq_disable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK); |
1079 | 1130 | ||
1080 | cx18_halt_firmware(cx); | 1131 | cx18_halt_firmware(cx); |
1081 | 1132 | ||
1082 | cx18_cancel_epu_work_orders(cx); | 1133 | destroy_workqueue(cx->in_work_queue); |
1083 | 1134 | destroy_workqueue(cx->out_work_queue); | |
1084 | destroy_workqueue(cx->work_queue); | ||
1085 | 1135 | ||
1086 | cx18_streams_cleanup(cx, 1); | 1136 | cx18_streams_cleanup(cx, 1); |
1087 | 1137 | ||
diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h index ece4f281ef42..c6a1e907f63a 100644 --- a/drivers/media/video/cx18/cx18-driver.h +++ b/drivers/media/video/cx18/cx18-driver.h | |||
@@ -80,8 +80,9 @@ | |||
80 | #define CX18_CARD_YUAN_MPC718 3 /* Yuan MPC718 */ | 80 | #define CX18_CARD_YUAN_MPC718 3 /* Yuan MPC718 */ |
81 | #define CX18_CARD_CNXT_RAPTOR_PAL 4 /* Conexant Raptor PAL */ | 81 | #define CX18_CARD_CNXT_RAPTOR_PAL 4 /* Conexant Raptor PAL */ |
82 | #define CX18_CARD_TOSHIBA_QOSMIO_DVBT 5 /* Toshiba Qosmio Interal DVB-T/Analog*/ | 82 | #define CX18_CARD_TOSHIBA_QOSMIO_DVBT 5 /* Toshiba Qosmio Interal DVB-T/Analog*/ |
83 | #define CX18_CARD_LEADTEK_PVR2100 6 /* Leadtek WinFast PVR2100/DVR3100 H */ | 83 | #define CX18_CARD_LEADTEK_PVR2100 6 /* Leadtek WinFast PVR2100 */ |
84 | #define CX18_CARD_LAST 6 | 84 | #define CX18_CARD_LEADTEK_DVR3100H 7 /* Leadtek WinFast DVR3100 H */ |
85 | #define CX18_CARD_LAST 7 | ||
85 | 86 | ||
86 | #define CX18_ENC_STREAM_TYPE_MPG 0 | 87 | #define CX18_ENC_STREAM_TYPE_MPG 0 |
87 | #define CX18_ENC_STREAM_TYPE_TS 1 | 88 | #define CX18_ENC_STREAM_TYPE_TS 1 |
@@ -254,6 +255,7 @@ struct cx18_options { | |||
254 | #define CX18_F_S_INTERNAL_USE 5 /* this stream is used internally (sliced VBI processing) */ | 255 | #define CX18_F_S_INTERNAL_USE 5 /* this stream is used internally (sliced VBI processing) */ |
255 | #define CX18_F_S_STREAMOFF 7 /* signal end of stream EOS */ | 256 | #define CX18_F_S_STREAMOFF 7 /* signal end of stream EOS */ |
256 | #define CX18_F_S_APPL_IO 8 /* this stream is used read/written by an application */ | 257 | #define CX18_F_S_APPL_IO 8 /* this stream is used read/written by an application */ |
258 | #define CX18_F_S_STOPPING 9 /* telling the fw to stop capturing */ | ||
257 | 259 | ||
258 | /* per-cx18, i_flags */ | 260 | /* per-cx18, i_flags */ |
259 | #define CX18_F_I_LOADED_FW 0 /* Loaded firmware 1st time */ | 261 | #define CX18_F_I_LOADED_FW 0 /* Loaded firmware 1st time */ |
@@ -285,6 +287,7 @@ struct cx18_queue { | |||
285 | struct list_head list; | 287 | struct list_head list; |
286 | atomic_t buffers; | 288 | atomic_t buffers; |
287 | u32 bytesused; | 289 | u32 bytesused; |
290 | spinlock_t lock; | ||
288 | }; | 291 | }; |
289 | 292 | ||
290 | struct cx18_dvb { | 293 | struct cx18_dvb { |
@@ -305,7 +308,7 @@ struct cx18_scb; /* forward reference */ | |||
305 | 308 | ||
306 | 309 | ||
307 | #define CX18_MAX_MDL_ACKS 2 | 310 | #define CX18_MAX_MDL_ACKS 2 |
308 | #define CX18_MAX_EPU_WORK_ORDERS (CX18_MAX_FW_MDLS_PER_STREAM + 7) | 311 | #define CX18_MAX_IN_WORK_ORDERS (CX18_MAX_FW_MDLS_PER_STREAM + 7) |
309 | /* CPU_DE_RELEASE_MDL can burst CX18_MAX_FW_MDLS_PER_STREAM orders in a group */ | 312 | /* CPU_DE_RELEASE_MDL can burst CX18_MAX_FW_MDLS_PER_STREAM orders in a group */ |
310 | 313 | ||
311 | #define CX18_F_EWO_MB_STALE_UPON_RECEIPT 0x1 | 314 | #define CX18_F_EWO_MB_STALE_UPON_RECEIPT 0x1 |
@@ -313,7 +316,7 @@ struct cx18_scb; /* forward reference */ | |||
313 | #define CX18_F_EWO_MB_STALE \ | 316 | #define CX18_F_EWO_MB_STALE \ |
314 | (CX18_F_EWO_MB_STALE_UPON_RECEIPT | CX18_F_EWO_MB_STALE_WHILE_PROC) | 317 | (CX18_F_EWO_MB_STALE_UPON_RECEIPT | CX18_F_EWO_MB_STALE_WHILE_PROC) |
315 | 318 | ||
316 | struct cx18_epu_work_order { | 319 | struct cx18_in_work_order { |
317 | struct work_struct work; | 320 | struct work_struct work; |
318 | atomic_t pending; | 321 | atomic_t pending; |
319 | struct cx18 *cx; | 322 | struct cx18 *cx; |
@@ -337,7 +340,6 @@ struct cx18_stream { | |||
337 | unsigned mdl_offset; | 340 | unsigned mdl_offset; |
338 | 341 | ||
339 | u32 id; | 342 | u32 id; |
340 | struct mutex qlock; /* locks access to the queues */ | ||
341 | unsigned long s_flags; /* status flags, see above */ | 343 | unsigned long s_flags; /* status flags, see above */ |
342 | int dma; /* can be PCI_DMA_TODEVICE, | 344 | int dma; /* can be PCI_DMA_TODEVICE, |
343 | PCI_DMA_FROMDEVICE or | 345 | PCI_DMA_FROMDEVICE or |
@@ -353,6 +355,8 @@ struct cx18_stream { | |||
353 | struct cx18_queue q_busy; /* busy buffers - in use by firmware */ | 355 | struct cx18_queue q_busy; /* busy buffers - in use by firmware */ |
354 | struct cx18_queue q_full; /* full buffers - data for user apps */ | 356 | struct cx18_queue q_full; /* full buffers - data for user apps */ |
355 | 357 | ||
358 | struct work_struct out_work_order; | ||
359 | |||
356 | /* DVB / Digital Transport */ | 360 | /* DVB / Digital Transport */ |
357 | struct cx18_dvb dvb; | 361 | struct cx18_dvb dvb; |
358 | }; | 362 | }; |
@@ -568,10 +572,14 @@ struct cx18 { | |||
568 | u32 sw2_irq_mask; | 572 | u32 sw2_irq_mask; |
569 | u32 hw2_irq_mask; | 573 | u32 hw2_irq_mask; |
570 | 574 | ||
571 | struct workqueue_struct *work_queue; | 575 | struct workqueue_struct *in_work_queue; |
572 | struct cx18_epu_work_order epu_work_order[CX18_MAX_EPU_WORK_ORDERS]; | 576 | char in_workq_name[11]; /* "cx18-NN-in" */ |
577 | struct cx18_in_work_order in_work_order[CX18_MAX_IN_WORK_ORDERS]; | ||
573 | char epu_debug_str[256]; /* CX18_EPU_DEBUG is rare: use shared space */ | 578 | char epu_debug_str[256]; /* CX18_EPU_DEBUG is rare: use shared space */ |
574 | 579 | ||
580 | struct workqueue_struct *out_work_queue; | ||
581 | char out_workq_name[12]; /* "cx18-NN-out" */ | ||
582 | |||
575 | /* i2c */ | 583 | /* i2c */ |
576 | struct i2c_adapter i2c_adap[2]; | 584 | struct i2c_adapter i2c_adap[2]; |
577 | struct i2c_algo_bit_data i2c_algo[2]; | 585 | struct i2c_algo_bit_data i2c_algo[2]; |
diff --git a/drivers/media/video/cx18/cx18-dvb.c b/drivers/media/video/cx18/cx18-dvb.c index 3b86f57cd15a..6ea3fe623ef4 100644 --- a/drivers/media/video/cx18/cx18-dvb.c +++ b/drivers/media/video/cx18/cx18-dvb.c | |||
@@ -23,14 +23,20 @@ | |||
23 | #include "cx18-version.h" | 23 | #include "cx18-version.h" |
24 | #include "cx18-dvb.h" | 24 | #include "cx18-dvb.h" |
25 | #include "cx18-io.h" | 25 | #include "cx18-io.h" |
26 | #include "cx18-queue.h" | ||
26 | #include "cx18-streams.h" | 27 | #include "cx18-streams.h" |
27 | #include "cx18-cards.h" | 28 | #include "cx18-cards.h" |
29 | #include "cx18-gpio.h" | ||
28 | #include "s5h1409.h" | 30 | #include "s5h1409.h" |
29 | #include "mxl5005s.h" | 31 | #include "mxl5005s.h" |
32 | #include "zl10353.h" | ||
33 | #include "tuner-xc2028.h" | ||
30 | 34 | ||
31 | DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); | 35 | DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); |
32 | 36 | ||
33 | #define CX18_REG_DMUX_NUM_PORT_0_CONTROL 0xd5a000 | 37 | #define CX18_REG_DMUX_NUM_PORT_0_CONTROL 0xd5a000 |
38 | #define CX18_CLOCK_ENABLE2 0xc71024 | ||
39 | #define CX18_DMUX_CLK_MASK 0x0080 | ||
34 | 40 | ||
35 | static struct mxl5005s_config hauppauge_hvr1600_tuner = { | 41 | static struct mxl5005s_config hauppauge_hvr1600_tuner = { |
36 | .i2c_address = 0xC6 >> 1, | 42 | .i2c_address = 0xC6 >> 1, |
@@ -57,7 +63,15 @@ static struct s5h1409_config hauppauge_hvr1600_config = { | |||
57 | .inversion = S5H1409_INVERSION_OFF, | 63 | .inversion = S5H1409_INVERSION_OFF, |
58 | .status_mode = S5H1409_DEMODLOCKING, | 64 | .status_mode = S5H1409_DEMODLOCKING, |
59 | .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK | 65 | .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK |
66 | }; | ||
60 | 67 | ||
68 | /* Information/confirmation of proper config values provided by Terry Wu */ | ||
69 | static struct zl10353_config leadtek_dvr3100h_demod = { | ||
70 | .demod_address = 0x1e >> 1, /* Datasheet suggested straps */ | ||
71 | .if2 = 45600, /* 4.560 MHz IF from the XC3028 */ | ||
72 | .parallel_ts = 1, /* Not a serial TS */ | ||
73 | .no_tuner = 1, /* XC3028 is not behind the gate */ | ||
74 | .disable_i2c_gate_ctrl = 1, /* Disable the I2C gate */ | ||
61 | }; | 75 | }; |
62 | 76 | ||
63 | static int dvb_register(struct cx18_stream *stream); | 77 | static int dvb_register(struct cx18_stream *stream); |
@@ -98,6 +112,7 @@ static int cx18_dvb_start_feed(struct dvb_demux_feed *feed) | |||
98 | cx18_write_reg(cx, v, CX18_REG_DMUX_NUM_PORT_0_CONTROL); | 112 | cx18_write_reg(cx, v, CX18_REG_DMUX_NUM_PORT_0_CONTROL); |
99 | break; | 113 | break; |
100 | 114 | ||
115 | case CX18_CARD_LEADTEK_DVR3100H: | ||
101 | default: | 116 | default: |
102 | /* Assumption - Parallel transport - Signalling | 117 | /* Assumption - Parallel transport - Signalling |
103 | * undefined or default. | 118 | * undefined or default. |
@@ -267,8 +282,7 @@ void cx18_dvb_unregister(struct cx18_stream *stream) | |||
267 | } | 282 | } |
268 | 283 | ||
269 | /* All the DVB attach calls go here, this function get's modified | 284 | /* All the DVB attach calls go here, this function get's modified |
270 | * for each new card. No other function in this file needs | 285 | * for each new card. cx18_dvb_start_feed() will also need changes. |
271 | * to change. | ||
272 | */ | 286 | */ |
273 | static int dvb_register(struct cx18_stream *stream) | 287 | static int dvb_register(struct cx18_stream *stream) |
274 | { | 288 | { |
@@ -289,6 +303,29 @@ static int dvb_register(struct cx18_stream *stream) | |||
289 | ret = 0; | 303 | ret = 0; |
290 | } | 304 | } |
291 | break; | 305 | break; |
306 | case CX18_CARD_LEADTEK_DVR3100H: | ||
307 | dvb->fe = dvb_attach(zl10353_attach, | ||
308 | &leadtek_dvr3100h_demod, | ||
309 | &cx->i2c_adap[1]); | ||
310 | if (dvb->fe != NULL) { | ||
311 | struct dvb_frontend *fe; | ||
312 | struct xc2028_config cfg = { | ||
313 | .i2c_adap = &cx->i2c_adap[1], | ||
314 | .i2c_addr = 0xc2 >> 1, | ||
315 | .ctrl = NULL, | ||
316 | }; | ||
317 | static struct xc2028_ctrl ctrl = { | ||
318 | .fname = XC2028_DEFAULT_FIRMWARE, | ||
319 | .max_len = 64, | ||
320 | .demod = XC3028_FE_ZARLINK456, | ||
321 | .type = XC2028_AUTO, | ||
322 | }; | ||
323 | |||
324 | fe = dvb_attach(xc2028_attach, dvb->fe, &cfg); | ||
325 | if (fe != NULL && fe->ops.tuner_ops.set_config != NULL) | ||
326 | fe->ops.tuner_ops.set_config(fe, &ctrl); | ||
327 | } | ||
328 | break; | ||
292 | default: | 329 | default: |
293 | /* No Digital Tv Support */ | 330 | /* No Digital Tv Support */ |
294 | break; | 331 | break; |
@@ -299,6 +336,8 @@ static int dvb_register(struct cx18_stream *stream) | |||
299 | return -1; | 336 | return -1; |
300 | } | 337 | } |
301 | 338 | ||
339 | dvb->fe->callback = cx18_reset_tuner_gpio; | ||
340 | |||
302 | ret = dvb_register_frontend(&dvb->dvb_adapter, dvb->fe); | 341 | ret = dvb_register_frontend(&dvb->dvb_adapter, dvb->fe); |
303 | if (ret < 0) { | 342 | if (ret < 0) { |
304 | if (dvb->fe->ops.release) | 343 | if (dvb->fe->ops.release) |
@@ -306,5 +345,16 @@ static int dvb_register(struct cx18_stream *stream) | |||
306 | return ret; | 345 | return ret; |
307 | } | 346 | } |
308 | 347 | ||
348 | /* | ||
349 | * The firmware seems to enable the TS DMUX clock | ||
350 | * under various circumstances. However, since we know we | ||
351 | * might use it, let's just turn it on ourselves here. | ||
352 | */ | ||
353 | cx18_write_reg_expect(cx, | ||
354 | (CX18_DMUX_CLK_MASK << 16) | CX18_DMUX_CLK_MASK, | ||
355 | CX18_CLOCK_ENABLE2, | ||
356 | CX18_DMUX_CLK_MASK, | ||
357 | (CX18_DMUX_CLK_MASK << 16) | CX18_DMUX_CLK_MASK); | ||
358 | |||
309 | return ret; | 359 | return ret; |
310 | } | 360 | } |
diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c index b3889c0b2697..29969c18949c 100644 --- a/drivers/media/video/cx18/cx18-fileops.c +++ b/drivers/media/video/cx18/cx18-fileops.c | |||
@@ -265,8 +265,13 @@ static size_t cx18_copy_buf_to_user(struct cx18_stream *s, | |||
265 | * an MPEG-2 Program Pack start code, and provide only | 265 | * an MPEG-2 Program Pack start code, and provide only |
266 | * up to that point to the user, so it's easy to insert VBI data | 266 | * up to that point to the user, so it's easy to insert VBI data |
267 | * the next time around. | 267 | * the next time around. |
268 | * | ||
269 | * This will not work for an MPEG-2 TS and has only been | ||
270 | * verified by analysis to work for an MPEG-2 PS. Helen Buus | ||
271 | * pointed out this works for the CX23416 MPEG-2 DVD compatible | ||
272 | * stream, and research indicates both the MPEG 2 SVCD and DVD | ||
273 | * stream types use an MPEG-2 PS container. | ||
268 | */ | 274 | */ |
269 | /* FIXME - This only works for an MPEG-2 PS, not a TS */ | ||
270 | /* | 275 | /* |
271 | * An MPEG-2 Program Stream (PS) is a series of | 276 | * An MPEG-2 Program Stream (PS) is a series of |
272 | * MPEG-2 Program Packs terminated by an | 277 | * MPEG-2 Program Packs terminated by an |
diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c index 2226e5791e99..afe46c3d4057 100644 --- a/drivers/media/video/cx18/cx18-mailbox.c +++ b/drivers/media/video/cx18/cx18-mailbox.c | |||
@@ -131,7 +131,7 @@ static void dump_mb(struct cx18 *cx, struct cx18_mailbox *mb, char *name) | |||
131 | * Functions that run in a work_queue work handling context | 131 | * Functions that run in a work_queue work handling context |
132 | */ | 132 | */ |
133 | 133 | ||
134 | static void epu_dma_done(struct cx18 *cx, struct cx18_epu_work_order *order) | 134 | static void epu_dma_done(struct cx18 *cx, struct cx18_in_work_order *order) |
135 | { | 135 | { |
136 | u32 handle, mdl_ack_count, id; | 136 | u32 handle, mdl_ack_count, id; |
137 | struct cx18_mailbox *mb; | 137 | struct cx18_mailbox *mb; |
@@ -191,29 +191,30 @@ static void epu_dma_done(struct cx18 *cx, struct cx18_epu_work_order *order) | |||
191 | if (buf == NULL) { | 191 | if (buf == NULL) { |
192 | CX18_WARN("Could not find buf %d for stream %s\n", | 192 | CX18_WARN("Could not find buf %d for stream %s\n", |
193 | id, s->name); | 193 | id, s->name); |
194 | /* Put as many buffers as possible back into fw use */ | ||
195 | cx18_stream_load_fw_queue(s); | ||
196 | continue; | 194 | continue; |
197 | } | 195 | } |
198 | 196 | ||
199 | if (s->type == CX18_ENC_STREAM_TYPE_TS && s->dvb.enabled) { | 197 | CX18_DEBUG_HI_DMA("%s recv bytesused = %d\n", |
200 | CX18_DEBUG_HI_DMA("TS recv bytesused = %d\n", | 198 | s->name, buf->bytesused); |
201 | buf->bytesused); | 199 | |
202 | dvb_dmx_swfilter(&s->dvb.demux, buf->buf, | 200 | if (s->type != CX18_ENC_STREAM_TYPE_TS) |
203 | buf->bytesused); | 201 | cx18_enqueue(s, buf, &s->q_full); |
202 | else { | ||
203 | if (s->dvb.enabled) | ||
204 | dvb_dmx_swfilter(&s->dvb.demux, buf->buf, | ||
205 | buf->bytesused); | ||
206 | cx18_enqueue(s, buf, &s->q_free); | ||
204 | } | 207 | } |
205 | /* Put as many buffers as possible back into fw use */ | ||
206 | cx18_stream_load_fw_queue(s); | ||
207 | /* Put back TS buffer, since it was removed from all queues */ | ||
208 | if (s->type == CX18_ENC_STREAM_TYPE_TS) | ||
209 | cx18_stream_put_buf_fw(s, buf); | ||
210 | } | 208 | } |
209 | /* Put as many buffers as possible back into fw use */ | ||
210 | cx18_stream_load_fw_queue(s); | ||
211 | |||
211 | wake_up(&cx->dma_waitq); | 212 | wake_up(&cx->dma_waitq); |
212 | if (s->id != -1) | 213 | if (s->id != -1) |
213 | wake_up(&s->waitq); | 214 | wake_up(&s->waitq); |
214 | } | 215 | } |
215 | 216 | ||
216 | static void epu_debug(struct cx18 *cx, struct cx18_epu_work_order *order) | 217 | static void epu_debug(struct cx18 *cx, struct cx18_in_work_order *order) |
217 | { | 218 | { |
218 | char *p; | 219 | char *p; |
219 | char *str = order->str; | 220 | char *str = order->str; |
@@ -224,7 +225,7 @@ static void epu_debug(struct cx18 *cx, struct cx18_epu_work_order *order) | |||
224 | CX18_INFO("FW version: %s\n", p - 1); | 225 | CX18_INFO("FW version: %s\n", p - 1); |
225 | } | 226 | } |
226 | 227 | ||
227 | static void epu_cmd(struct cx18 *cx, struct cx18_epu_work_order *order) | 228 | static void epu_cmd(struct cx18 *cx, struct cx18_in_work_order *order) |
228 | { | 229 | { |
229 | switch (order->rpu) { | 230 | switch (order->rpu) { |
230 | case CPU: | 231 | case CPU: |
@@ -253,18 +254,18 @@ static void epu_cmd(struct cx18 *cx, struct cx18_epu_work_order *order) | |||
253 | } | 254 | } |
254 | 255 | ||
255 | static | 256 | static |
256 | void free_epu_work_order(struct cx18 *cx, struct cx18_epu_work_order *order) | 257 | void free_in_work_order(struct cx18 *cx, struct cx18_in_work_order *order) |
257 | { | 258 | { |
258 | atomic_set(&order->pending, 0); | 259 | atomic_set(&order->pending, 0); |
259 | } | 260 | } |
260 | 261 | ||
261 | void cx18_epu_work_handler(struct work_struct *work) | 262 | void cx18_in_work_handler(struct work_struct *work) |
262 | { | 263 | { |
263 | struct cx18_epu_work_order *order = | 264 | struct cx18_in_work_order *order = |
264 | container_of(work, struct cx18_epu_work_order, work); | 265 | container_of(work, struct cx18_in_work_order, work); |
265 | struct cx18 *cx = order->cx; | 266 | struct cx18 *cx = order->cx; |
266 | epu_cmd(cx, order); | 267 | epu_cmd(cx, order); |
267 | free_epu_work_order(cx, order); | 268 | free_in_work_order(cx, order); |
268 | } | 269 | } |
269 | 270 | ||
270 | 271 | ||
@@ -272,7 +273,7 @@ void cx18_epu_work_handler(struct work_struct *work) | |||
272 | * Functions that run in an interrupt handling context | 273 | * Functions that run in an interrupt handling context |
273 | */ | 274 | */ |
274 | 275 | ||
275 | static void mb_ack_irq(struct cx18 *cx, struct cx18_epu_work_order *order) | 276 | static void mb_ack_irq(struct cx18 *cx, struct cx18_in_work_order *order) |
276 | { | 277 | { |
277 | struct cx18_mailbox __iomem *ack_mb; | 278 | struct cx18_mailbox __iomem *ack_mb; |
278 | u32 ack_irq, req; | 279 | u32 ack_irq, req; |
@@ -308,7 +309,7 @@ static void mb_ack_irq(struct cx18 *cx, struct cx18_epu_work_order *order) | |||
308 | return; | 309 | return; |
309 | } | 310 | } |
310 | 311 | ||
311 | static int epu_dma_done_irq(struct cx18 *cx, struct cx18_epu_work_order *order) | 312 | static int epu_dma_done_irq(struct cx18 *cx, struct cx18_in_work_order *order) |
312 | { | 313 | { |
313 | u32 handle, mdl_ack_offset, mdl_ack_count; | 314 | u32 handle, mdl_ack_offset, mdl_ack_count; |
314 | struct cx18_mailbox *mb; | 315 | struct cx18_mailbox *mb; |
@@ -334,7 +335,7 @@ static int epu_dma_done_irq(struct cx18 *cx, struct cx18_epu_work_order *order) | |||
334 | } | 335 | } |
335 | 336 | ||
336 | static | 337 | static |
337 | int epu_debug_irq(struct cx18 *cx, struct cx18_epu_work_order *order) | 338 | int epu_debug_irq(struct cx18 *cx, struct cx18_in_work_order *order) |
338 | { | 339 | { |
339 | u32 str_offset; | 340 | u32 str_offset; |
340 | char *str = order->str; | 341 | char *str = order->str; |
@@ -355,7 +356,7 @@ int epu_debug_irq(struct cx18 *cx, struct cx18_epu_work_order *order) | |||
355 | } | 356 | } |
356 | 357 | ||
357 | static inline | 358 | static inline |
358 | int epu_cmd_irq(struct cx18 *cx, struct cx18_epu_work_order *order) | 359 | int epu_cmd_irq(struct cx18 *cx, struct cx18_in_work_order *order) |
359 | { | 360 | { |
360 | int ret = -1; | 361 | int ret = -1; |
361 | 362 | ||
@@ -387,12 +388,12 @@ int epu_cmd_irq(struct cx18 *cx, struct cx18_epu_work_order *order) | |||
387 | } | 388 | } |
388 | 389 | ||
389 | static inline | 390 | static inline |
390 | struct cx18_epu_work_order *alloc_epu_work_order_irq(struct cx18 *cx) | 391 | struct cx18_in_work_order *alloc_in_work_order_irq(struct cx18 *cx) |
391 | { | 392 | { |
392 | int i; | 393 | int i; |
393 | struct cx18_epu_work_order *order = NULL; | 394 | struct cx18_in_work_order *order = NULL; |
394 | 395 | ||
395 | for (i = 0; i < CX18_MAX_EPU_WORK_ORDERS; i++) { | 396 | for (i = 0; i < CX18_MAX_IN_WORK_ORDERS; i++) { |
396 | /* | 397 | /* |
397 | * We only need "pending" atomic to inspect its contents, | 398 | * We only need "pending" atomic to inspect its contents, |
398 | * and need not do a check and set because: | 399 | * and need not do a check and set because: |
@@ -401,8 +402,8 @@ struct cx18_epu_work_order *alloc_epu_work_order_irq(struct cx18 *cx) | |||
401 | * 2. "pending" is only set here, and we're serialized because | 402 | * 2. "pending" is only set here, and we're serialized because |
402 | * we're called in an IRQ handler context. | 403 | * we're called in an IRQ handler context. |
403 | */ | 404 | */ |
404 | if (atomic_read(&cx->epu_work_order[i].pending) == 0) { | 405 | if (atomic_read(&cx->in_work_order[i].pending) == 0) { |
405 | order = &cx->epu_work_order[i]; | 406 | order = &cx->in_work_order[i]; |
406 | atomic_set(&order->pending, 1); | 407 | atomic_set(&order->pending, 1); |
407 | break; | 408 | break; |
408 | } | 409 | } |
@@ -414,7 +415,7 @@ void cx18_api_epu_cmd_irq(struct cx18 *cx, int rpu) | |||
414 | { | 415 | { |
415 | struct cx18_mailbox __iomem *mb; | 416 | struct cx18_mailbox __iomem *mb; |
416 | struct cx18_mailbox *order_mb; | 417 | struct cx18_mailbox *order_mb; |
417 | struct cx18_epu_work_order *order; | 418 | struct cx18_in_work_order *order; |
418 | int submit; | 419 | int submit; |
419 | 420 | ||
420 | switch (rpu) { | 421 | switch (rpu) { |
@@ -428,7 +429,7 @@ void cx18_api_epu_cmd_irq(struct cx18 *cx, int rpu) | |||
428 | return; | 429 | return; |
429 | } | 430 | } |
430 | 431 | ||
431 | order = alloc_epu_work_order_irq(cx); | 432 | order = alloc_in_work_order_irq(cx); |
432 | if (order == NULL) { | 433 | if (order == NULL) { |
433 | CX18_WARN("Unable to find blank work order form to schedule " | 434 | CX18_WARN("Unable to find blank work order form to schedule " |
434 | "incoming mailbox command processing\n"); | 435 | "incoming mailbox command processing\n"); |
@@ -461,7 +462,7 @@ void cx18_api_epu_cmd_irq(struct cx18 *cx, int rpu) | |||
461 | */ | 462 | */ |
462 | submit = epu_cmd_irq(cx, order); | 463 | submit = epu_cmd_irq(cx, order); |
463 | if (submit > 0) { | 464 | if (submit > 0) { |
464 | queue_work(cx->work_queue, &order->work); | 465 | queue_work(cx->in_work_queue, &order->work); |
465 | } | 466 | } |
466 | } | 467 | } |
467 | 468 | ||
@@ -478,9 +479,10 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) | |||
478 | u32 __iomem *xpu_state; | 479 | u32 __iomem *xpu_state; |
479 | wait_queue_head_t *waitq; | 480 | wait_queue_head_t *waitq; |
480 | struct mutex *mb_lock; | 481 | struct mutex *mb_lock; |
481 | long int timeout, ret; | 482 | unsigned long int t0, timeout, ret; |
482 | int i; | 483 | int i; |
483 | char argstr[MAX_MB_ARGUMENTS*11+1]; | 484 | char argstr[MAX_MB_ARGUMENTS*11+1]; |
485 | DEFINE_WAIT(w); | ||
484 | 486 | ||
485 | if (info == NULL) { | 487 | if (info == NULL) { |
486 | CX18_WARN("unknown cmd %x\n", cmd); | 488 | CX18_WARN("unknown cmd %x\n", cmd); |
@@ -562,25 +564,49 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) | |||
562 | 564 | ||
563 | CX18_DEBUG_HI_IRQ("sending interrupt SW1: %x to send %s\n", | 565 | CX18_DEBUG_HI_IRQ("sending interrupt SW1: %x to send %s\n", |
564 | irq, info->name); | 566 | irq, info->name); |
567 | |||
568 | /* So we don't miss the wakeup, prepare to wait before notifying fw */ | ||
569 | prepare_to_wait(waitq, &w, TASK_UNINTERRUPTIBLE); | ||
565 | cx18_write_reg_expect(cx, irq, SW1_INT_SET, irq, irq); | 570 | cx18_write_reg_expect(cx, irq, SW1_INT_SET, irq, irq); |
566 | 571 | ||
567 | ret = wait_event_timeout( | 572 | t0 = jiffies; |
568 | *waitq, | 573 | ack = cx18_readl(cx, &mb->ack); |
569 | cx18_readl(cx, &mb->ack) == cx18_readl(cx, &mb->request), | 574 | if (ack != req) { |
570 | timeout); | 575 | schedule_timeout(timeout); |
576 | ret = jiffies - t0; | ||
577 | ack = cx18_readl(cx, &mb->ack); | ||
578 | } else { | ||
579 | ret = jiffies - t0; | ||
580 | } | ||
571 | 581 | ||
572 | if (ret == 0) { | 582 | finish_wait(waitq, &w); |
573 | /* Timed out */ | 583 | |
584 | if (req != ack) { | ||
574 | mutex_unlock(mb_lock); | 585 | mutex_unlock(mb_lock); |
575 | CX18_DEBUG_WARN("sending %s timed out waiting %d msecs for RPU " | 586 | if (ret >= timeout) { |
576 | "acknowledgement\n", | 587 | /* Timed out */ |
577 | info->name, jiffies_to_msecs(timeout)); | 588 | CX18_DEBUG_WARN("sending %s timed out waiting %d msecs " |
589 | "for RPU acknowledgement\n", | ||
590 | info->name, jiffies_to_msecs(ret)); | ||
591 | } else { | ||
592 | CX18_DEBUG_WARN("woken up before mailbox ack was ready " | ||
593 | "after submitting %s to RPU. only " | ||
594 | "waited %d msecs on req %u but awakened" | ||
595 | " with unmatched ack %u\n", | ||
596 | info->name, | ||
597 | jiffies_to_msecs(ret), | ||
598 | req, ack); | ||
599 | } | ||
578 | return -EINVAL; | 600 | return -EINVAL; |
579 | } | 601 | } |
580 | 602 | ||
581 | if (ret != timeout) | 603 | if (ret >= timeout) |
604 | CX18_DEBUG_WARN("failed to be awakened upon RPU acknowledgment " | ||
605 | "sending %s; timed out waiting %d msecs\n", | ||
606 | info->name, jiffies_to_msecs(ret)); | ||
607 | else | ||
582 | CX18_DEBUG_HI_API("waited %u msecs for %s to be acked\n", | 608 | CX18_DEBUG_HI_API("waited %u msecs for %s to be acked\n", |
583 | jiffies_to_msecs(timeout-ret), info->name); | 609 | jiffies_to_msecs(ret), info->name); |
584 | 610 | ||
585 | /* Collect data returned by the XPU */ | 611 | /* Collect data returned by the XPU */ |
586 | for (i = 0; i < MAX_MB_ARGUMENTS; i++) | 612 | for (i = 0; i < MAX_MB_ARGUMENTS; i++) |
diff --git a/drivers/media/video/cx18/cx18-mailbox.h b/drivers/media/video/cx18/cx18-mailbox.h index ce2b6686aa00..e23aaac5b280 100644 --- a/drivers/media/video/cx18/cx18-mailbox.h +++ b/drivers/media/video/cx18/cx18-mailbox.h | |||
@@ -95,6 +95,6 @@ int cx18_api_func(void *priv, u32 cmd, int in, int out, | |||
95 | 95 | ||
96 | void cx18_api_epu_cmd_irq(struct cx18 *cx, int rpu); | 96 | void cx18_api_epu_cmd_irq(struct cx18 *cx, int rpu); |
97 | 97 | ||
98 | void cx18_epu_work_handler(struct work_struct *work); | 98 | void cx18_in_work_handler(struct work_struct *work); |
99 | 99 | ||
100 | #endif | 100 | #endif |
diff --git a/drivers/media/video/cx18/cx18-queue.c b/drivers/media/video/cx18/cx18-queue.c index 3046b8e74345..fa1ed7897d97 100644 --- a/drivers/media/video/cx18/cx18-queue.c +++ b/drivers/media/video/cx18/cx18-queue.c | |||
@@ -23,8 +23,8 @@ | |||
23 | */ | 23 | */ |
24 | 24 | ||
25 | #include "cx18-driver.h" | 25 | #include "cx18-driver.h" |
26 | #include "cx18-streams.h" | ||
27 | #include "cx18-queue.h" | 26 | #include "cx18-queue.h" |
27 | #include "cx18-streams.h" | ||
28 | #include "cx18-scb.h" | 28 | #include "cx18-scb.h" |
29 | 29 | ||
30 | void cx18_buf_swap(struct cx18_buffer *buf) | 30 | void cx18_buf_swap(struct cx18_buffer *buf) |
@@ -53,13 +53,13 @@ struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, | |||
53 | buf->skipped = 0; | 53 | buf->skipped = 0; |
54 | } | 54 | } |
55 | 55 | ||
56 | mutex_lock(&s->qlock); | ||
57 | |||
58 | /* q_busy is restricted to a max buffer count imposed by firmware */ | 56 | /* q_busy is restricted to a max buffer count imposed by firmware */ |
59 | if (q == &s->q_busy && | 57 | if (q == &s->q_busy && |
60 | atomic_read(&q->buffers) >= CX18_MAX_FW_MDLS_PER_STREAM) | 58 | atomic_read(&q->buffers) >= CX18_MAX_FW_MDLS_PER_STREAM) |
61 | q = &s->q_free; | 59 | q = &s->q_free; |
62 | 60 | ||
61 | spin_lock(&q->lock); | ||
62 | |||
63 | if (to_front) | 63 | if (to_front) |
64 | list_add(&buf->list, &q->list); /* LIFO */ | 64 | list_add(&buf->list, &q->list); /* LIFO */ |
65 | else | 65 | else |
@@ -67,7 +67,7 @@ struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, | |||
67 | q->bytesused += buf->bytesused - buf->readpos; | 67 | q->bytesused += buf->bytesused - buf->readpos; |
68 | atomic_inc(&q->buffers); | 68 | atomic_inc(&q->buffers); |
69 | 69 | ||
70 | mutex_unlock(&s->qlock); | 70 | spin_unlock(&q->lock); |
71 | return q; | 71 | return q; |
72 | } | 72 | } |
73 | 73 | ||
@@ -75,7 +75,7 @@ struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q) | |||
75 | { | 75 | { |
76 | struct cx18_buffer *buf = NULL; | 76 | struct cx18_buffer *buf = NULL; |
77 | 77 | ||
78 | mutex_lock(&s->qlock); | 78 | spin_lock(&q->lock); |
79 | if (!list_empty(&q->list)) { | 79 | if (!list_empty(&q->list)) { |
80 | buf = list_first_entry(&q->list, struct cx18_buffer, list); | 80 | buf = list_first_entry(&q->list, struct cx18_buffer, list); |
81 | list_del_init(&buf->list); | 81 | list_del_init(&buf->list); |
@@ -83,7 +83,7 @@ struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q) | |||
83 | buf->skipped = 0; | 83 | buf->skipped = 0; |
84 | atomic_dec(&q->buffers); | 84 | atomic_dec(&q->buffers); |
85 | } | 85 | } |
86 | mutex_unlock(&s->qlock); | 86 | spin_unlock(&q->lock); |
87 | return buf; | 87 | return buf; |
88 | } | 88 | } |
89 | 89 | ||
@@ -94,9 +94,23 @@ struct cx18_buffer *cx18_queue_get_buf(struct cx18_stream *s, u32 id, | |||
94 | struct cx18_buffer *buf; | 94 | struct cx18_buffer *buf; |
95 | struct cx18_buffer *tmp; | 95 | struct cx18_buffer *tmp; |
96 | struct cx18_buffer *ret = NULL; | 96 | struct cx18_buffer *ret = NULL; |
97 | 97 | LIST_HEAD(sweep_up); | |
98 | mutex_lock(&s->qlock); | 98 | |
99 | /* | ||
100 | * We don't have to acquire multiple q locks here, because we are | ||
101 | * serialized by the single threaded work handler. | ||
102 | * Buffers from the firmware will thus remain in order as | ||
103 | * they are moved from q_busy to q_full or to the dvb ring buffer. | ||
104 | */ | ||
105 | spin_lock(&s->q_busy.lock); | ||
99 | list_for_each_entry_safe(buf, tmp, &s->q_busy.list, list) { | 106 | list_for_each_entry_safe(buf, tmp, &s->q_busy.list, list) { |
107 | /* | ||
108 | * We should find what the firmware told us is done, | ||
109 | * right at the front of the queue. If we don't, we likely have | ||
110 | * missed a buffer done message from the firmware. | ||
111 | * Once we skip a buffer repeatedly, relative to the size of | ||
112 | * q_busy, we have high confidence we've missed it. | ||
113 | */ | ||
100 | if (buf->id != id) { | 114 | if (buf->id != id) { |
101 | buf->skipped++; | 115 | buf->skipped++; |
102 | if (buf->skipped >= atomic_read(&s->q_busy.buffers)-1) { | 116 | if (buf->skipped >= atomic_read(&s->q_busy.buffers)-1) { |
@@ -105,38 +119,41 @@ struct cx18_buffer *cx18_queue_get_buf(struct cx18_stream *s, u32 id, | |||
105 | "times - it must have dropped out of " | 119 | "times - it must have dropped out of " |
106 | "rotation\n", s->name, buf->id, | 120 | "rotation\n", s->name, buf->id, |
107 | buf->skipped); | 121 | buf->skipped); |
108 | /* move it to q_free */ | 122 | /* Sweep it up to put it back into rotation */ |
109 | list_move_tail(&buf->list, &s->q_free.list); | 123 | list_move_tail(&buf->list, &sweep_up); |
110 | buf->bytesused = buf->readpos = buf->b_flags = | ||
111 | buf->skipped = 0; | ||
112 | atomic_dec(&s->q_busy.buffers); | 124 | atomic_dec(&s->q_busy.buffers); |
113 | atomic_inc(&s->q_free.buffers); | ||
114 | } | 125 | } |
115 | continue; | 126 | continue; |
116 | } | 127 | } |
117 | 128 | /* | |
118 | buf->bytesused = bytesused; | 129 | * We pull the desired buffer off of the queue here. Something |
119 | /* Sync the buffer before we release the qlock */ | 130 | * will have to put it back on a queue later. |
120 | cx18_buf_sync_for_cpu(s, buf); | 131 | */ |
121 | if (s->type == CX18_ENC_STREAM_TYPE_TS) { | 132 | list_del_init(&buf->list); |
122 | /* | ||
123 | * TS doesn't use q_full. As we pull the buffer off of | ||
124 | * the queue here, the caller will have to put it back. | ||
125 | */ | ||
126 | list_del_init(&buf->list); | ||
127 | } else { | ||
128 | /* Move buffer from q_busy to q_full */ | ||
129 | list_move_tail(&buf->list, &s->q_full.list); | ||
130 | set_bit(CX18_F_B_NEED_BUF_SWAP, &buf->b_flags); | ||
131 | s->q_full.bytesused += buf->bytesused; | ||
132 | atomic_inc(&s->q_full.buffers); | ||
133 | } | ||
134 | atomic_dec(&s->q_busy.buffers); | 133 | atomic_dec(&s->q_busy.buffers); |
135 | |||
136 | ret = buf; | 134 | ret = buf; |
137 | break; | 135 | break; |
138 | } | 136 | } |
139 | mutex_unlock(&s->qlock); | 137 | spin_unlock(&s->q_busy.lock); |
138 | |||
139 | /* | ||
140 | * We found the buffer for which we were looking. Get it ready for | ||
141 | * the caller to put on q_full or in the dvb ring buffer. | ||
142 | */ | ||
143 | if (ret != NULL) { | ||
144 | ret->bytesused = bytesused; | ||
145 | ret->skipped = 0; | ||
146 | /* readpos and b_flags were 0'ed when the buf went on q_busy */ | ||
147 | cx18_buf_sync_for_cpu(s, ret); | ||
148 | if (s->type != CX18_ENC_STREAM_TYPE_TS) | ||
149 | set_bit(CX18_F_B_NEED_BUF_SWAP, &ret->b_flags); | ||
150 | } | ||
151 | |||
152 | /* Put any buffers the firmware is ignoring back into normal rotation */ | ||
153 | list_for_each_entry_safe(buf, tmp, &sweep_up, list) { | ||
154 | list_del_init(&buf->list); | ||
155 | cx18_enqueue(s, buf, &s->q_free); | ||
156 | } | ||
140 | return ret; | 157 | return ret; |
141 | } | 158 | } |
142 | 159 | ||
@@ -148,7 +165,7 @@ static void cx18_queue_flush(struct cx18_stream *s, struct cx18_queue *q) | |||
148 | if (q == &s->q_free) | 165 | if (q == &s->q_free) |
149 | return; | 166 | return; |
150 | 167 | ||
151 | mutex_lock(&s->qlock); | 168 | spin_lock(&q->lock); |
152 | while (!list_empty(&q->list)) { | 169 | while (!list_empty(&q->list)) { |
153 | buf = list_first_entry(&q->list, struct cx18_buffer, list); | 170 | buf = list_first_entry(&q->list, struct cx18_buffer, list); |
154 | list_move_tail(&buf->list, &s->q_free.list); | 171 | list_move_tail(&buf->list, &s->q_free.list); |
@@ -156,7 +173,7 @@ static void cx18_queue_flush(struct cx18_stream *s, struct cx18_queue *q) | |||
156 | atomic_inc(&s->q_free.buffers); | 173 | atomic_inc(&s->q_free.buffers); |
157 | } | 174 | } |
158 | cx18_queue_init(q); | 175 | cx18_queue_init(q); |
159 | mutex_unlock(&s->qlock); | 176 | spin_unlock(&q->lock); |
160 | } | 177 | } |
161 | 178 | ||
162 | void cx18_flush_queues(struct cx18_stream *s) | 179 | void cx18_flush_queues(struct cx18_stream *s) |
diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c index 0932b76b2373..54d248e16d85 100644 --- a/drivers/media/video/cx18/cx18-streams.c +++ b/drivers/media/video/cx18/cx18-streams.c | |||
@@ -116,12 +116,16 @@ static void cx18_stream_init(struct cx18 *cx, int type) | |||
116 | s->buffers = cx->stream_buffers[type]; | 116 | s->buffers = cx->stream_buffers[type]; |
117 | s->buf_size = cx->stream_buf_size[type]; | 117 | s->buf_size = cx->stream_buf_size[type]; |
118 | 118 | ||
119 | mutex_init(&s->qlock); | ||
120 | init_waitqueue_head(&s->waitq); | 119 | init_waitqueue_head(&s->waitq); |
121 | s->id = -1; | 120 | s->id = -1; |
121 | spin_lock_init(&s->q_free.lock); | ||
122 | cx18_queue_init(&s->q_free); | 122 | cx18_queue_init(&s->q_free); |
123 | spin_lock_init(&s->q_busy.lock); | ||
123 | cx18_queue_init(&s->q_busy); | 124 | cx18_queue_init(&s->q_busy); |
125 | spin_lock_init(&s->q_full.lock); | ||
124 | cx18_queue_init(&s->q_full); | 126 | cx18_queue_init(&s->q_full); |
127 | |||
128 | INIT_WORK(&s->out_work_order, cx18_out_work_handler); | ||
125 | } | 129 | } |
126 | 130 | ||
127 | static int cx18_prep_dev(struct cx18 *cx, int type) | 131 | static int cx18_prep_dev(struct cx18 *cx, int type) |
@@ -367,9 +371,14 @@ static void cx18_vbi_setup(struct cx18_stream *s) | |||
367 | * Tell the encoder to capture 21-4+1=18 lines per field, | 371 | * Tell the encoder to capture 21-4+1=18 lines per field, |
368 | * since we want lines 10 through 21. | 372 | * since we want lines 10 through 21. |
369 | * | 373 | * |
370 | * FIXME - revisit for 625/50 systems | 374 | * For 625/50 systems, according to the VIP 2 & BT.656 std: |
375 | * The EAV RP code's Field bit toggles on line 1, a few lines | ||
376 | * after the Vertcal Blank bit has already toggled. | ||
377 | * (We've actually set the digitizer so that the Field bit | ||
378 | * toggles on line 2.) Tell the encoder to capture 23-2+1=22 | ||
379 | * lines per field, since we want lines 6 through 23. | ||
371 | */ | 380 | */ |
372 | lines = cx->is_60hz ? (21 - 4 + 1) * 2 : 38; | 381 | lines = cx->is_60hz ? (21 - 4 + 1) * 2 : (23 - 2 + 1) * 2; |
373 | } | 382 | } |
374 | 383 | ||
375 | data[0] = s->handle; | 384 | data[0] = s->handle; |
@@ -431,14 +440,16 @@ static void cx18_vbi_setup(struct cx18_stream *s) | |||
431 | cx18_api(cx, CX18_CPU_SET_RAW_VBI_PARAM, 6, data); | 440 | cx18_api(cx, CX18_CPU_SET_RAW_VBI_PARAM, 6, data); |
432 | } | 441 | } |
433 | 442 | ||
434 | struct cx18_queue *cx18_stream_put_buf_fw(struct cx18_stream *s, | 443 | static |
435 | struct cx18_buffer *buf) | 444 | struct cx18_queue *_cx18_stream_put_buf_fw(struct cx18_stream *s, |
445 | struct cx18_buffer *buf) | ||
436 | { | 446 | { |
437 | struct cx18 *cx = s->cx; | 447 | struct cx18 *cx = s->cx; |
438 | struct cx18_queue *q; | 448 | struct cx18_queue *q; |
439 | 449 | ||
440 | /* Don't give it to the firmware, if we're not running a capture */ | 450 | /* Don't give it to the firmware, if we're not running a capture */ |
441 | if (s->handle == CX18_INVALID_TASK_HANDLE || | 451 | if (s->handle == CX18_INVALID_TASK_HANDLE || |
452 | test_bit(CX18_F_S_STOPPING, &s->s_flags) || | ||
442 | !test_bit(CX18_F_S_STREAMING, &s->s_flags)) | 453 | !test_bit(CX18_F_S_STREAMING, &s->s_flags)) |
443 | return cx18_enqueue(s, buf, &s->q_free); | 454 | return cx18_enqueue(s, buf, &s->q_free); |
444 | 455 | ||
@@ -453,7 +464,8 @@ struct cx18_queue *cx18_stream_put_buf_fw(struct cx18_stream *s, | |||
453 | return q; | 464 | return q; |
454 | } | 465 | } |
455 | 466 | ||
456 | void cx18_stream_load_fw_queue(struct cx18_stream *s) | 467 | static |
468 | void _cx18_stream_load_fw_queue(struct cx18_stream *s) | ||
457 | { | 469 | { |
458 | struct cx18_queue *q; | 470 | struct cx18_queue *q; |
459 | struct cx18_buffer *buf; | 471 | struct cx18_buffer *buf; |
@@ -467,11 +479,19 @@ void cx18_stream_load_fw_queue(struct cx18_stream *s) | |||
467 | buf = cx18_dequeue(s, &s->q_free); | 479 | buf = cx18_dequeue(s, &s->q_free); |
468 | if (buf == NULL) | 480 | if (buf == NULL) |
469 | break; | 481 | break; |
470 | q = cx18_stream_put_buf_fw(s, buf); | 482 | q = _cx18_stream_put_buf_fw(s, buf); |
471 | } while (atomic_read(&s->q_busy.buffers) < CX18_MAX_FW_MDLS_PER_STREAM | 483 | } while (atomic_read(&s->q_busy.buffers) < CX18_MAX_FW_MDLS_PER_STREAM |
472 | && q == &s->q_busy); | 484 | && q == &s->q_busy); |
473 | } | 485 | } |
474 | 486 | ||
487 | void cx18_out_work_handler(struct work_struct *work) | ||
488 | { | ||
489 | struct cx18_stream *s = | ||
490 | container_of(work, struct cx18_stream, out_work_order); | ||
491 | |||
492 | _cx18_stream_load_fw_queue(s); | ||
493 | } | ||
494 | |||
475 | int cx18_start_v4l2_encode_stream(struct cx18_stream *s) | 495 | int cx18_start_v4l2_encode_stream(struct cx18_stream *s) |
476 | { | 496 | { |
477 | u32 data[MAX_MB_ARGUMENTS]; | 497 | u32 data[MAX_MB_ARGUMENTS]; |
@@ -600,19 +620,20 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) | |||
600 | 620 | ||
601 | /* Init all the cpu_mdls for this stream */ | 621 | /* Init all the cpu_mdls for this stream */ |
602 | cx18_flush_queues(s); | 622 | cx18_flush_queues(s); |
603 | mutex_lock(&s->qlock); | 623 | spin_lock(&s->q_free.lock); |
604 | list_for_each_entry(buf, &s->q_free.list, list) { | 624 | list_for_each_entry(buf, &s->q_free.list, list) { |
605 | cx18_writel(cx, buf->dma_handle, | 625 | cx18_writel(cx, buf->dma_handle, |
606 | &cx->scb->cpu_mdl[buf->id].paddr); | 626 | &cx->scb->cpu_mdl[buf->id].paddr); |
607 | cx18_writel(cx, s->buf_size, &cx->scb->cpu_mdl[buf->id].length); | 627 | cx18_writel(cx, s->buf_size, &cx->scb->cpu_mdl[buf->id].length); |
608 | } | 628 | } |
609 | mutex_unlock(&s->qlock); | 629 | spin_unlock(&s->q_free.lock); |
610 | cx18_stream_load_fw_queue(s); | 630 | _cx18_stream_load_fw_queue(s); |
611 | 631 | ||
612 | /* begin_capture */ | 632 | /* begin_capture */ |
613 | if (cx18_vapi(cx, CX18_CPU_CAPTURE_START, 1, s->handle)) { | 633 | if (cx18_vapi(cx, CX18_CPU_CAPTURE_START, 1, s->handle)) { |
614 | CX18_DEBUG_WARN("Error starting capture!\n"); | 634 | CX18_DEBUG_WARN("Error starting capture!\n"); |
615 | /* Ensure we're really not capturing before releasing MDLs */ | 635 | /* Ensure we're really not capturing before releasing MDLs */ |
636 | set_bit(CX18_F_S_STOPPING, &s->s_flags); | ||
616 | if (s->type == CX18_ENC_STREAM_TYPE_MPG) | 637 | if (s->type == CX18_ENC_STREAM_TYPE_MPG) |
617 | cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 2, s->handle, 1); | 638 | cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 2, s->handle, 1); |
618 | else | 639 | else |
@@ -622,6 +643,7 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) | |||
622 | cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle); | 643 | cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle); |
623 | cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle); | 644 | cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle); |
624 | s->handle = CX18_INVALID_TASK_HANDLE; | 645 | s->handle = CX18_INVALID_TASK_HANDLE; |
646 | clear_bit(CX18_F_S_STOPPING, &s->s_flags); | ||
625 | if (atomic_read(&cx->tot_capturing) == 0) { | 647 | if (atomic_read(&cx->tot_capturing) == 0) { |
626 | set_bit(CX18_F_I_EOS, &cx->i_flags); | 648 | set_bit(CX18_F_I_EOS, &cx->i_flags); |
627 | cx18_write_reg(cx, 5, CX18_DSP0_INTERRUPT_MASK); | 649 | cx18_write_reg(cx, 5, CX18_DSP0_INTERRUPT_MASK); |
@@ -666,6 +688,7 @@ int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end) | |||
666 | if (atomic_read(&cx->tot_capturing) == 0) | 688 | if (atomic_read(&cx->tot_capturing) == 0) |
667 | return 0; | 689 | return 0; |
668 | 690 | ||
691 | set_bit(CX18_F_S_STOPPING, &s->s_flags); | ||
669 | if (s->type == CX18_ENC_STREAM_TYPE_MPG) | 692 | if (s->type == CX18_ENC_STREAM_TYPE_MPG) |
670 | cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 2, s->handle, !gop_end); | 693 | cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 2, s->handle, !gop_end); |
671 | else | 694 | else |
@@ -689,6 +712,7 @@ int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end) | |||
689 | 712 | ||
690 | cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle); | 713 | cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle); |
691 | s->handle = CX18_INVALID_TASK_HANDLE; | 714 | s->handle = CX18_INVALID_TASK_HANDLE; |
715 | clear_bit(CX18_F_S_STOPPING, &s->s_flags); | ||
692 | 716 | ||
693 | if (atomic_read(&cx->tot_capturing) > 0) | 717 | if (atomic_read(&cx->tot_capturing) > 0) |
694 | return 0; | 718 | return 0; |
diff --git a/drivers/media/video/cx18/cx18-streams.h b/drivers/media/video/cx18/cx18-streams.h index 420e0a172945..1afc3fd9d822 100644 --- a/drivers/media/video/cx18/cx18-streams.h +++ b/drivers/media/video/cx18/cx18-streams.h | |||
@@ -28,10 +28,24 @@ int cx18_streams_setup(struct cx18 *cx); | |||
28 | int cx18_streams_register(struct cx18 *cx); | 28 | int cx18_streams_register(struct cx18 *cx); |
29 | void cx18_streams_cleanup(struct cx18 *cx, int unregister); | 29 | void cx18_streams_cleanup(struct cx18 *cx, int unregister); |
30 | 30 | ||
31 | /* Related to submission of buffers to firmware */ | ||
32 | static inline void cx18_stream_load_fw_queue(struct cx18_stream *s) | ||
33 | { | ||
34 | struct cx18 *cx = s->cx; | ||
35 | queue_work(cx->out_work_queue, &s->out_work_order); | ||
36 | } | ||
37 | |||
38 | static inline void cx18_stream_put_buf_fw(struct cx18_stream *s, | ||
39 | struct cx18_buffer *buf) | ||
40 | { | ||
41 | /* Put buf on q_free; the out work handler will move buf(s) to q_busy */ | ||
42 | cx18_enqueue(s, buf, &s->q_free); | ||
43 | cx18_stream_load_fw_queue(s); | ||
44 | } | ||
45 | |||
46 | void cx18_out_work_handler(struct work_struct *work); | ||
47 | |||
31 | /* Capture related */ | 48 | /* Capture related */ |
32 | void cx18_stream_load_fw_queue(struct cx18_stream *s); | ||
33 | struct cx18_queue *cx18_stream_put_buf_fw(struct cx18_stream *s, | ||
34 | struct cx18_buffer *buf); | ||
35 | int cx18_start_v4l2_encode_stream(struct cx18_stream *s); | 49 | int cx18_start_v4l2_encode_stream(struct cx18_stream *s); |
36 | int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end); | 50 | int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end); |
37 | 51 | ||
diff --git a/drivers/media/video/cx18/cx18-version.h b/drivers/media/video/cx18/cx18-version.h index bd9bd44da791..45494b094e7f 100644 --- a/drivers/media/video/cx18/cx18-version.h +++ b/drivers/media/video/cx18/cx18-version.h | |||
@@ -24,7 +24,7 @@ | |||
24 | 24 | ||
25 | #define CX18_DRIVER_NAME "cx18" | 25 | #define CX18_DRIVER_NAME "cx18" |
26 | #define CX18_DRIVER_VERSION_MAJOR 1 | 26 | #define CX18_DRIVER_VERSION_MAJOR 1 |
27 | #define CX18_DRIVER_VERSION_MINOR 1 | 27 | #define CX18_DRIVER_VERSION_MINOR 2 |
28 | #define CX18_DRIVER_VERSION_PATCHLEVEL 0 | 28 | #define CX18_DRIVER_VERSION_PATCHLEVEL 0 |
29 | 29 | ||
30 | #define CX18_VERSION __stringify(CX18_DRIVER_VERSION_MAJOR) "." __stringify(CX18_DRIVER_VERSION_MINOR) "." __stringify(CX18_DRIVER_VERSION_PATCHLEVEL) | 30 | #define CX18_VERSION __stringify(CX18_DRIVER_VERSION_MAJOR) "." __stringify(CX18_DRIVER_VERSION_MINOR) "." __stringify(CX18_DRIVER_VERSION_PATCHLEVEL) |