1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * |
4 | * Copyright (C) 2005 Mike Isely <isely@pobox.com> |
5 | * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> |
6 | */ |
7 | |
8 | #include <linux/device.h> // for linux/firmware.h |
9 | #include <linux/firmware.h> |
10 | #include "pvrusb2-util.h" |
11 | #include "pvrusb2-encoder.h" |
12 | #include "pvrusb2-hdw-internal.h" |
13 | #include "pvrusb2-debug.h" |
14 | #include "pvrusb2-fx2-cmd.h" |
15 | |
16 | |
17 | |
18 | /* Firmware mailbox flags - definitions found from ivtv */ |
19 | #define IVTV_MBOX_FIRMWARE_DONE 0x00000004 |
20 | #define IVTV_MBOX_DRIVER_DONE 0x00000002 |
21 | #define IVTV_MBOX_DRIVER_BUSY 0x00000001 |
22 | |
23 | #define MBOX_BASE 0x44 |
24 | |
25 | |
26 | static int pvr2_encoder_write_words(struct pvr2_hdw *hdw, |
27 | unsigned int offs, |
28 | const u32 *data, unsigned int dlen) |
29 | { |
30 | unsigned int idx,addr; |
31 | unsigned int bAddr; |
32 | int ret; |
33 | unsigned int chunkCnt; |
34 | |
35 | /* |
36 | |
37 | Format: First byte must be 0x01. Remaining 32 bit words are |
38 | spread out into chunks of 7 bytes each, with the first 4 bytes |
39 | being the data word (little endian), and the next 3 bytes |
40 | being the address where that data word is to be written (big |
41 | endian). Repeat request for additional words, with offset |
42 | adjusted accordingly. |
43 | |
44 | */ |
45 | while (dlen) { |
46 | chunkCnt = 8; |
47 | if (chunkCnt > dlen) chunkCnt = dlen; |
48 | memset(hdw->cmd_buffer,0,sizeof(hdw->cmd_buffer)); |
49 | bAddr = 0; |
50 | hdw->cmd_buffer[bAddr++] = FX2CMD_MEM_WRITE_DWORD; |
51 | for (idx = 0; idx < chunkCnt; idx++) { |
52 | addr = idx + offs; |
53 | hdw->cmd_buffer[bAddr+6] = (addr & 0xffu); |
54 | hdw->cmd_buffer[bAddr+5] = ((addr>>8) & 0xffu); |
55 | hdw->cmd_buffer[bAddr+4] = ((addr>>16) & 0xffu); |
56 | PVR2_DECOMPOSE_LE(hdw->cmd_buffer, bAddr,data[idx]); |
57 | bAddr += 7; |
58 | } |
59 | ret = pvr2_send_request(hdw, |
60 | write_ptr: hdw->cmd_buffer,write_len: 1+(chunkCnt*7), |
61 | NULL,read_len: 0); |
62 | if (ret) return ret; |
63 | data += chunkCnt; |
64 | dlen -= chunkCnt; |
65 | offs += chunkCnt; |
66 | } |
67 | |
68 | return 0; |
69 | } |
70 | |
71 | |
72 | static int pvr2_encoder_read_words(struct pvr2_hdw *hdw, |
73 | unsigned int offs, |
74 | u32 *data, unsigned int dlen) |
75 | { |
76 | unsigned int idx; |
77 | int ret; |
78 | unsigned int chunkCnt; |
79 | |
80 | /* |
81 | |
82 | Format: First byte must be 0x02 (status check) or 0x28 (read |
83 | back block of 32 bit words). Next 6 bytes must be zero, |
84 | followed by a single byte of MBOX_BASE+offset for portion to |
85 | be read. Returned data is packed set of 32 bits words that |
86 | were read. |
87 | |
88 | */ |
89 | |
90 | while (dlen) { |
91 | chunkCnt = 16; |
92 | if (chunkCnt > dlen) chunkCnt = dlen; |
93 | if (chunkCnt < 16) chunkCnt = 1; |
94 | hdw->cmd_buffer[0] = |
95 | ((chunkCnt == 1) ? |
96 | FX2CMD_MEM_READ_DWORD : FX2CMD_MEM_READ_64BYTES); |
97 | hdw->cmd_buffer[1] = 0; |
98 | hdw->cmd_buffer[2] = 0; |
99 | hdw->cmd_buffer[3] = 0; |
100 | hdw->cmd_buffer[4] = 0; |
101 | hdw->cmd_buffer[5] = ((offs>>16) & 0xffu); |
102 | hdw->cmd_buffer[6] = ((offs>>8) & 0xffu); |
103 | hdw->cmd_buffer[7] = (offs & 0xffu); |
104 | ret = pvr2_send_request(hdw, |
105 | write_ptr: hdw->cmd_buffer,write_len: 8, |
106 | read_ptr: hdw->cmd_buffer, |
107 | read_len: (chunkCnt == 1 ? 4 : 16 * 4)); |
108 | if (ret) return ret; |
109 | |
110 | for (idx = 0; idx < chunkCnt; idx++) { |
111 | data[idx] = PVR2_COMPOSE_LE(hdw->cmd_buffer,idx*4); |
112 | } |
113 | data += chunkCnt; |
114 | dlen -= chunkCnt; |
115 | offs += chunkCnt; |
116 | } |
117 | |
118 | return 0; |
119 | } |
120 | |
121 | |
122 | /* This prototype is set up to be compatible with the |
123 | cx2341x_mbox_func prototype in cx2341x.h, which should be in |
124 | kernels 2.6.18 or later. We do this so that we can enable |
125 | cx2341x.ko to write to our encoder (by handing it a pointer to this |
126 | function). For earlier kernels this doesn't really matter. */ |
127 | static int pvr2_encoder_cmd(void *ctxt, |
128 | u32 cmd, |
129 | int arg_cnt_send, |
130 | int arg_cnt_recv, |
131 | u32 *argp) |
132 | { |
133 | unsigned int poll_count; |
134 | unsigned int try_count = 0; |
135 | int retry_flag; |
136 | int ret = 0; |
137 | unsigned int idx; |
138 | /* These sizes look to be limited by the FX2 firmware implementation */ |
139 | u32 wrData[16]; |
140 | u32 rdData[16]; |
141 | struct pvr2_hdw *hdw = (struct pvr2_hdw *)ctxt; |
142 | |
143 | |
144 | /* |
145 | |
146 | The encoder seems to speak entirely using blocks 32 bit words. |
147 | In ivtv driver terms, this is a mailbox at MBOX_BASE which we |
148 | populate with data and watch what the hardware does with it. |
149 | The first word is a set of flags used to control the |
150 | transaction, the second word is the command to execute, the |
151 | third byte is zero (ivtv driver suggests that this is some |
152 | kind of return value), and the fourth byte is a specified |
153 | timeout (windows driver always uses 0x00060000 except for one |
154 | case when it is zero). All successive words are the argument |
155 | words for the command. |
156 | |
157 | First, write out the entire set of words, with the first word |
158 | being zero. |
159 | |
160 | Next, write out just the first word again, but set it to |
161 | IVTV_MBOX_DRIVER_DONE | IVTV_DRIVER_BUSY this time (which |
162 | probably means "go"). |
163 | |
164 | Next, read back the return count words. Check the first word, |
165 | which should have IVTV_MBOX_FIRMWARE_DONE set. If however |
166 | that bit is not set, then the command isn't done so repeat the |
167 | read until it is set. |
168 | |
169 | Finally, write out just the first word again, but set it to |
170 | 0x0 this time (which probably means "idle"). |
171 | |
172 | */ |
173 | |
174 | if (arg_cnt_send > (ARRAY_SIZE(wrData) - 4)) { |
175 | pvr2_trace( |
176 | PVR2_TRACE_ERROR_LEGS, |
177 | "Failed to write cx23416 command - too many input arguments (was given %u limit %lu)" , |
178 | arg_cnt_send, (long unsigned) ARRAY_SIZE(wrData) - 4); |
179 | return -EINVAL; |
180 | } |
181 | |
182 | if (arg_cnt_recv > (ARRAY_SIZE(rdData) - 4)) { |
183 | pvr2_trace( |
184 | PVR2_TRACE_ERROR_LEGS, |
185 | "Failed to write cx23416 command - too many return arguments (was given %u limit %lu)" , |
186 | arg_cnt_recv, (long unsigned) ARRAY_SIZE(rdData) - 4); |
187 | return -EINVAL; |
188 | } |
189 | |
190 | |
191 | LOCK_TAKE(hdw->ctl_lock); |
192 | while (1) { |
193 | if (!hdw->state_encoder_ok) { |
194 | ret = -EIO; |
195 | break; |
196 | } |
197 | |
198 | retry_flag = 0; |
199 | try_count++; |
200 | ret = 0; |
201 | wrData[0] = 0; |
202 | wrData[1] = cmd; |
203 | wrData[2] = 0; |
204 | wrData[3] = 0x00060000; |
205 | for (idx = 0; idx < arg_cnt_send; idx++) { |
206 | wrData[idx+4] = argp[idx]; |
207 | } |
208 | for (; idx < ARRAY_SIZE(wrData) - 4; idx++) { |
209 | wrData[idx+4] = 0; |
210 | } |
211 | |
212 | ret = pvr2_encoder_write_words(hdw,MBOX_BASE,data: wrData,dlen: idx); |
213 | if (ret) break; |
214 | wrData[0] = IVTV_MBOX_DRIVER_DONE|IVTV_MBOX_DRIVER_BUSY; |
215 | ret = pvr2_encoder_write_words(hdw,MBOX_BASE,data: wrData,dlen: 1); |
216 | if (ret) break; |
217 | poll_count = 0; |
218 | while (1) { |
219 | poll_count++; |
220 | ret = pvr2_encoder_read_words(hdw,MBOX_BASE,data: rdData, |
221 | dlen: arg_cnt_recv+4); |
222 | if (ret) { |
223 | break; |
224 | } |
225 | if (rdData[0] & IVTV_MBOX_FIRMWARE_DONE) { |
226 | break; |
227 | } |
228 | if (rdData[0] && (poll_count < 1000)) continue; |
229 | if (!rdData[0]) { |
230 | retry_flag = !0; |
231 | pvr2_trace( |
232 | PVR2_TRACE_ERROR_LEGS, |
233 | "Encoder timed out waiting for us; arranging to retry" ); |
234 | } else { |
235 | pvr2_trace( |
236 | PVR2_TRACE_ERROR_LEGS, |
237 | "***WARNING*** device's encoder appears to be stuck (status=0x%08x)" , |
238 | rdData[0]); |
239 | } |
240 | pvr2_trace( |
241 | PVR2_TRACE_ERROR_LEGS, |
242 | "Encoder command: 0x%02x" ,cmd); |
243 | for (idx = 4; idx < arg_cnt_send; idx++) { |
244 | pvr2_trace( |
245 | PVR2_TRACE_ERROR_LEGS, |
246 | "Encoder arg%d: 0x%08x" , |
247 | idx-3,wrData[idx]); |
248 | } |
249 | ret = -EBUSY; |
250 | break; |
251 | } |
252 | if (retry_flag) { |
253 | if (try_count < 20) continue; |
254 | pvr2_trace( |
255 | PVR2_TRACE_ERROR_LEGS, |
256 | "Too many retries..." ); |
257 | ret = -EBUSY; |
258 | } |
259 | if (ret) { |
260 | del_timer_sync(timer: &hdw->encoder_run_timer); |
261 | hdw->state_encoder_ok = 0; |
262 | pvr2_trace(PVR2_TRACE_STBITS, |
263 | "State bit %s <-- %s" , |
264 | "state_encoder_ok" , |
265 | (hdw->state_encoder_ok ? "true" : "false" )); |
266 | if (hdw->state_encoder_runok) { |
267 | hdw->state_encoder_runok = 0; |
268 | pvr2_trace(PVR2_TRACE_STBITS, |
269 | "State bit %s <-- %s" , |
270 | "state_encoder_runok" , |
271 | (hdw->state_encoder_runok ? |
272 | "true" : "false" )); |
273 | } |
274 | pvr2_trace( |
275 | PVR2_TRACE_ERROR_LEGS, |
276 | "Giving up on command. This is normally recovered via a firmware reload and re-initialization; concern is only warranted if this happens repeatedly and rapidly." ); |
277 | break; |
278 | } |
279 | wrData[0] = 0x7; |
280 | for (idx = 0; idx < arg_cnt_recv; idx++) { |
281 | argp[idx] = rdData[idx+4]; |
282 | } |
283 | |
284 | wrData[0] = 0x0; |
285 | ret = pvr2_encoder_write_words(hdw,MBOX_BASE,data: wrData,dlen: 1); |
286 | break; |
287 | } |
288 | LOCK_GIVE(hdw->ctl_lock); |
289 | |
290 | return ret; |
291 | } |
292 | |
293 | |
294 | static int pvr2_encoder_vcmd(struct pvr2_hdw *hdw, int cmd, |
295 | int args, ...) |
296 | { |
297 | va_list vl; |
298 | unsigned int idx; |
299 | u32 data[12]; |
300 | |
301 | if (args > ARRAY_SIZE(data)) { |
302 | pvr2_trace( |
303 | PVR2_TRACE_ERROR_LEGS, |
304 | "Failed to write cx23416 command - too many arguments (was given %u limit %lu)" , |
305 | args, (long unsigned) ARRAY_SIZE(data)); |
306 | return -EINVAL; |
307 | } |
308 | |
309 | va_start(vl, args); |
310 | for (idx = 0; idx < args; idx++) { |
311 | data[idx] = va_arg(vl, u32); |
312 | } |
313 | va_end(vl); |
314 | |
315 | return pvr2_encoder_cmd(ctxt: hdw,cmd,arg_cnt_send: args,arg_cnt_recv: 0,argp: data); |
316 | } |
317 | |
318 | |
319 | /* This implements some extra setup for the encoder that seems to be |
320 | specific to the PVR USB2 hardware. */ |
321 | static int pvr2_encoder_prep_config(struct pvr2_hdw *hdw) |
322 | { |
323 | int ret = 0; |
324 | int encMisc3Arg = 0; |
325 | |
326 | #if 0 |
327 | /* This inexplicable bit happens in the Hauppauge windows |
328 | driver (for both 24xxx and 29xxx devices). However I |
329 | currently see no difference in behavior with or without |
330 | this stuff. Leave this here as a note of its existence, |
331 | but don't use it. */ |
332 | LOCK_TAKE(hdw->ctl_lock); do { |
333 | u32 dat[1]; |
334 | dat[0] = 0x80000640; |
335 | pvr2_encoder_write_words(hdw,0x01fe,dat,1); |
336 | pvr2_encoder_write_words(hdw,0x023e,dat,1); |
337 | } while(0); LOCK_GIVE(hdw->ctl_lock); |
338 | #endif |
339 | |
340 | /* Mike Isely <isely@pobox.com> 26-Jan-2006 The windows driver |
341 | sends the following list of ENC_MISC commands (for both |
342 | 24xxx and 29xxx devices). Meanings are not entirely clear, |
343 | however without the ENC_MISC(3,1) command then we risk |
344 | random perpetual video corruption whenever the video input |
345 | breaks up for a moment (like when switching channels). */ |
346 | |
347 | |
348 | #if 0 |
349 | /* This ENC_MISC(5,0) command seems to hurt 29xxx sync |
350 | performance on channel changes, but is not a problem on |
351 | 24xxx devices. */ |
352 | ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 5,0,0,0); |
353 | #endif |
354 | |
355 | /* This ENC_MISC(3,encMisc3Arg) command is critical - without |
356 | it there will eventually be video corruption. Also, the |
357 | saa7115 case is strange - the Windows driver is passing 1 |
358 | regardless of device type but if we have 1 for saa7115 |
359 | devices the video turns sluggish. */ |
360 | if (hdw->hdw_desc->flag_has_cx25840) { |
361 | encMisc3Arg = 1; |
362 | } else { |
363 | encMisc3Arg = 0; |
364 | } |
365 | ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,args: 4, 3, |
366 | encMisc3Arg,0,0); |
367 | |
368 | ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,args: 4, 8,0,0,0); |
369 | |
370 | #if 0 |
371 | /* This ENC_MISC(4,1) command is poisonous, so it is commented |
372 | out. But I'm leaving it here anyway to document its |
373 | existence in the Windows driver. The effect of this |
374 | command is that apps displaying the stream become sluggish |
375 | with stuttering video. */ |
376 | ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 4,1,0,0); |
377 | #endif |
378 | |
379 | ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,args: 4, 0,3,0,0); |
380 | ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,args: 4,15,0,0,0); |
381 | |
382 | /* prevent the PTSs from slowly drifting away in the generated |
383 | MPEG stream */ |
384 | ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC, args: 2, 4, 1); |
385 | |
386 | return ret; |
387 | } |
388 | |
389 | int pvr2_encoder_adjust(struct pvr2_hdw *hdw) |
390 | { |
391 | int ret; |
392 | ret = cx2341x_update(priv: hdw,func: pvr2_encoder_cmd, |
393 | old: (hdw->enc_cur_valid ? &hdw->enc_cur_state : NULL), |
394 | new: &hdw->enc_ctl_state); |
395 | if (ret) { |
396 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, |
397 | "Error from cx2341x module code=%d" ,ret); |
398 | } else { |
399 | hdw->enc_cur_state = hdw->enc_ctl_state; |
400 | hdw->enc_cur_valid = !0; |
401 | } |
402 | return ret; |
403 | } |
404 | |
405 | |
406 | int pvr2_encoder_configure(struct pvr2_hdw *hdw) |
407 | { |
408 | int ret; |
409 | int val; |
410 | pvr2_trace(PVR2_TRACE_ENCODER, "pvr2_encoder_configure (cx2341x module)" ); |
411 | hdw->enc_ctl_state.port = CX2341X_PORT_STREAMING; |
412 | hdw->enc_ctl_state.width = hdw->res_hor_val; |
413 | hdw->enc_ctl_state.height = hdw->res_ver_val; |
414 | hdw->enc_ctl_state.is_50hz = ((hdw->std_mask_cur & V4L2_STD_525_60) ? |
415 | 0 : 1); |
416 | |
417 | ret = 0; |
418 | |
419 | ret |= pvr2_encoder_prep_config(hdw); |
420 | |
421 | /* saa7115: 0xf0 */ |
422 | val = 0xf0; |
423 | if (hdw->hdw_desc->flag_has_cx25840) { |
424 | /* ivtv cx25840: 0x140 */ |
425 | val = 0x140; |
426 | } |
427 | |
428 | if (!ret) ret = pvr2_encoder_vcmd( |
429 | hdw,CX2341X_ENC_SET_NUM_VSYNC_LINES, args: 2, |
430 | val, val); |
431 | |
432 | /* setup firmware to notify us about some events (don't know why...) */ |
433 | if (!ret) ret = pvr2_encoder_vcmd( |
434 | hdw,CX2341X_ENC_SET_EVENT_NOTIFICATION, args: 4, |
435 | 0, 0, 0x10000000, 0xffffffff); |
436 | |
437 | if (!ret) ret = pvr2_encoder_vcmd( |
438 | hdw,CX2341X_ENC_SET_VBI_LINE, args: 5, |
439 | 0xffffffff,0,0,0,0); |
440 | |
441 | if (ret) { |
442 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, |
443 | "Failed to configure cx23416" ); |
444 | return ret; |
445 | } |
446 | |
447 | ret = pvr2_encoder_adjust(hdw); |
448 | if (ret) return ret; |
449 | |
450 | ret = pvr2_encoder_vcmd( |
451 | hdw, CX2341X_ENC_INITIALIZE_INPUT, args: 0); |
452 | |
453 | if (ret) { |
454 | pvr2_trace(PVR2_TRACE_ERROR_LEGS, |
455 | "Failed to initialize cx23416 video input" ); |
456 | return ret; |
457 | } |
458 | |
459 | return 0; |
460 | } |
461 | |
462 | |
463 | int pvr2_encoder_start(struct pvr2_hdw *hdw) |
464 | { |
465 | int status; |
466 | |
467 | /* unmask some interrupts */ |
468 | pvr2_write_register(hdw, 0x0048, 0xbfffffff); |
469 | |
470 | pvr2_encoder_vcmd(hdw,CX2341X_ENC_MUTE_VIDEO,args: 1, |
471 | hdw->input_val == PVR2_CVAL_INPUT_RADIO ? 1 : 0); |
472 | |
473 | switch (hdw->active_stream_type) { |
474 | case pvr2_config_vbi: |
475 | status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,args: 2, |
476 | 0x01,0x14); |
477 | break; |
478 | case pvr2_config_mpeg: |
479 | status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,args: 2, |
480 | 0,0x13); |
481 | break; |
482 | default: /* Unhandled cases for now */ |
483 | status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,args: 2, |
484 | 0,0x13); |
485 | break; |
486 | } |
487 | return status; |
488 | } |
489 | |
490 | int pvr2_encoder_stop(struct pvr2_hdw *hdw) |
491 | { |
492 | int status; |
493 | |
494 | /* mask all interrupts */ |
495 | pvr2_write_register(hdw, 0x0048, 0xffffffff); |
496 | |
497 | switch (hdw->active_stream_type) { |
498 | case pvr2_config_vbi: |
499 | status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,args: 3, |
500 | 0x01,0x01,0x14); |
501 | break; |
502 | case pvr2_config_mpeg: |
503 | status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,args: 3, |
504 | 0x01,0,0x13); |
505 | break; |
506 | default: /* Unhandled cases for now */ |
507 | status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,args: 3, |
508 | 0x01,0,0x13); |
509 | break; |
510 | } |
511 | |
512 | return status; |
513 | } |
514 | |