blob: ee5eb26ad56d0477ef7cc4bd2f6576d726afa021 [file] [log] [blame]
Mike Iselyd8554972006-06-26 20:58:46 -03001/*
2 *
3 * $Id$
4 *
5 * Copyright (C) 2005 Mike Isely <isely@pobox.com>
6 * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23#include <linux/device.h> // for linux/firmware.h
24#include <linux/firmware.h>
Mike Iselyd8554972006-06-26 20:58:46 -030025#include "pvrusb2-util.h"
26#include "pvrusb2-encoder.h"
27#include "pvrusb2-hdw-internal.h"
28#include "pvrusb2-debug.h"
Michael Krufky8d364362007-01-22 02:17:55 -030029#include "pvrusb2-fx2-cmd.h"
Mike Iselyd8554972006-06-26 20:58:46 -030030
Mike Iselyd8554972006-06-26 20:58:46 -030031
32
33/* Firmware mailbox flags - definitions found from ivtv */
34#define IVTV_MBOX_FIRMWARE_DONE 0x00000004
35#define IVTV_MBOX_DRIVER_DONE 0x00000002
36#define IVTV_MBOX_DRIVER_BUSY 0x00000001
37
38
Mike Iselyeacbe7c2006-06-25 20:04:06 -030039static int pvr2_encoder_write_words(struct pvr2_hdw *hdw,
Mike Iselyd8554972006-06-26 20:58:46 -030040 const u32 *data, unsigned int dlen)
41{
42 unsigned int idx;
43 int ret;
44 unsigned int offs = 0;
45 unsigned int chunkCnt;
46
47 /*
48
49 Format: First byte must be 0x01. Remaining 32 bit words are
50 spread out into chunks of 7 bytes each, little-endian ordered,
51 offset at zero within each 2 blank bytes following and a
52 single byte that is 0x44 plus the offset of the word. Repeat
53 request for additional words, with offset adjusted
54 accordingly.
55
56 */
57 while (dlen) {
58 chunkCnt = 8;
59 if (chunkCnt > dlen) chunkCnt = dlen;
60 memset(hdw->cmd_buffer,0,sizeof(hdw->cmd_buffer));
Michael Krufky8d364362007-01-22 02:17:55 -030061 hdw->cmd_buffer[0] = FX2CMD_MEM_WRITE_DWORD;
Mike Iselyd8554972006-06-26 20:58:46 -030062 for (idx = 0; idx < chunkCnt; idx++) {
63 hdw->cmd_buffer[1+(idx*7)+6] = 0x44 + idx + offs;
64 PVR2_DECOMPOSE_LE(hdw->cmd_buffer, 1+(idx*7),
65 data[idx]);
66 }
67 ret = pvr2_send_request(hdw,
68 hdw->cmd_buffer,1+(chunkCnt*7),
Mike Iselya0fd1cb2006-06-30 11:35:28 -030069 NULL,0);
Mike Iselyd8554972006-06-26 20:58:46 -030070 if (ret) return ret;
71 data += chunkCnt;
72 dlen -= chunkCnt;
73 offs += chunkCnt;
74 }
75
76 return 0;
77}
78
79
Mike Iselyeacbe7c2006-06-25 20:04:06 -030080static int pvr2_encoder_read_words(struct pvr2_hdw *hdw,int statusFl,
Mike Iselyd8554972006-06-26 20:58:46 -030081 u32 *data, unsigned int dlen)
82{
83 unsigned int idx;
84 int ret;
85 unsigned int offs = 0;
86 unsigned int chunkCnt;
87
88 /*
89
90 Format: First byte must be 0x02 (status check) or 0x28 (read
91 back block of 32 bit words). Next 6 bytes must be zero,
92 followed by a single byte of 0x44+offset for portion to be
93 read. Returned data is packed set of 32 bits words that were
94 read.
95
96 */
97
98 while (dlen) {
99 chunkCnt = 16;
100 if (chunkCnt > dlen) chunkCnt = dlen;
101 memset(hdw->cmd_buffer,0,sizeof(hdw->cmd_buffer));
Michael Krufky8d364362007-01-22 02:17:55 -0300102 hdw->cmd_buffer[0] =
103 (statusFl ? FX2CMD_MEM_READ_DWORD : FX2CMD_MEM_READ_64BYTES);
Mike Iselyd8554972006-06-26 20:58:46 -0300104 hdw->cmd_buffer[7] = 0x44 + offs;
105 ret = pvr2_send_request(hdw,
106 hdw->cmd_buffer,8,
107 hdw->cmd_buffer,chunkCnt * 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
Mike Iselyeacbe7c2006-06-25 20:04:06 -0300122/* 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. */
127static int pvr2_encoder_cmd(void *ctxt,
128 int cmd,
129 int arg_cnt_send,
130 int arg_cnt_recv,
131 u32 *argp)
Mike Iselyd8554972006-06-26 20:58:46 -0300132{
133 unsigned int poll_count;
134 int ret = 0;
Mike Iselyd8554972006-06-26 20:58:46 -0300135 unsigned int idx;
Mike Iselyeacbe7c2006-06-25 20:04:06 -0300136 /* These sizes look to be limited by the FX2 firmware implementation */
Mike Iselyd8554972006-06-26 20:58:46 -0300137 u32 wrData[16];
Mike Iselyeacbe7c2006-06-25 20:04:06 -0300138 u32 rdData[16];
139 struct pvr2_hdw *hdw = (struct pvr2_hdw *)ctxt;
Mike Iselyd8554972006-06-26 20:58:46 -0300140
Mike Iselyc05c0462006-06-25 20:04:25 -0300141
Mike Iselyd8554972006-06-26 20:58:46 -0300142 /*
143
144 The encoder seems to speak entirely using blocks 32 bit words.
145 In ivtv driver terms, this is a mailbox which we populate with
146 data and watch what the hardware does with it. The first word
147 is a set of flags used to control the transaction, the second
148 word is the command to execute, the third byte is zero (ivtv
149 driver suggests that this is some kind of return value), and
150 the fourth byte is a specified timeout (windows driver always
151 uses 0x00060000 except for one case when it is zero). All
152 successive words are the argument words for the command.
153
154 First, write out the entire set of words, with the first word
155 being zero.
156
157 Next, write out just the first word again, but set it to
158 IVTV_MBOX_DRIVER_DONE | IVTV_DRIVER_BUSY this time (which
159 probably means "go").
160
161 Next, read back 16 words as status. Check the first word,
162 which should have IVTV_MBOX_FIRMWARE_DONE set. If however
163 that bit is not set, then the command isn't done so repeat the
164 read.
165
166 Next, read back 32 words and compare with the original
167 arugments. Hopefully they will match.
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
Ahmed S. Darwisheca8ebf2007-01-20 00:35:03 -0300174 if (arg_cnt_send > (ARRAY_SIZE(wrData) - 4)) {
Mike Iselyeacbe7c2006-06-25 20:04:06 -0300175 pvr2_trace(
176 PVR2_TRACE_ERROR_LEGS,
177 "Failed to write cx23416 command"
178 " - too many input arguments"
Mauro Carvalho Chehab69b04f02007-01-21 22:02:35 -0300179 " (was given %u limit %lu)",
180 arg_cnt_send, (long unsigned) ARRAY_SIZE(wrData) - 4);
Mike Iselyeacbe7c2006-06-25 20:04:06 -0300181 return -EINVAL;
182 }
183
Ahmed S. Darwisheca8ebf2007-01-20 00:35:03 -0300184 if (arg_cnt_recv > (ARRAY_SIZE(rdData) - 4)) {
Mike Iselyeacbe7c2006-06-25 20:04:06 -0300185 pvr2_trace(
186 PVR2_TRACE_ERROR_LEGS,
187 "Failed to write cx23416 command"
188 " - too many return arguments"
Mauro Carvalho Chehab69b04f02007-01-21 22:02:35 -0300189 " (was given %u limit %lu)",
190 arg_cnt_recv, (long unsigned) ARRAY_SIZE(rdData) - 4);
Mike Iselyeacbe7c2006-06-25 20:04:06 -0300191 return -EINVAL;
192 }
193
Mike Iselyd8554972006-06-26 20:58:46 -0300194
195 LOCK_TAKE(hdw->ctl_lock); do {
196
197 wrData[0] = 0;
198 wrData[1] = cmd;
199 wrData[2] = 0;
200 wrData[3] = 0x00060000;
Mike Iselyeacbe7c2006-06-25 20:04:06 -0300201 for (idx = 0; idx < arg_cnt_send; idx++) {
202 wrData[idx+4] = argp[idx];
Mike Iselyd8554972006-06-26 20:58:46 -0300203 }
Ahmed S. Darwisheca8ebf2007-01-20 00:35:03 -0300204 for (; idx < ARRAY_SIZE(wrData) - 4; idx++) {
Mike Iselyeacbe7c2006-06-25 20:04:06 -0300205 wrData[idx+4] = 0;
Mike Iselyd8554972006-06-26 20:58:46 -0300206 }
207
Mike Iselyeacbe7c2006-06-25 20:04:06 -0300208 ret = pvr2_encoder_write_words(hdw,wrData,idx);
Mike Iselyd8554972006-06-26 20:58:46 -0300209 if (ret) break;
210 wrData[0] = IVTV_MBOX_DRIVER_DONE|IVTV_MBOX_DRIVER_BUSY;
Mike Iselyeacbe7c2006-06-25 20:04:06 -0300211 ret = pvr2_encoder_write_words(hdw,wrData,1);
Mike Iselyd8554972006-06-26 20:58:46 -0300212 if (ret) break;
213 poll_count = 0;
214 while (1) {
215 if (poll_count < 10000000) poll_count++;
Mike Iselyeacbe7c2006-06-25 20:04:06 -0300216 ret = pvr2_encoder_read_words(hdw,!0,rdData,1);
Mike Iselyd8554972006-06-26 20:58:46 -0300217 if (ret) break;
218 if (rdData[0] & IVTV_MBOX_FIRMWARE_DONE) {
219 break;
220 }
221 if (poll_count == 100) {
222 pvr2_trace(
223 PVR2_TRACE_ERROR_LEGS,
224 "***WARNING*** device's encoder"
225 " appears to be stuck"
226 " (status=0%08x)",rdData[0]);
227 pvr2_trace(
228 PVR2_TRACE_ERROR_LEGS,
229 "Encoder command: 0x%02x",cmd);
Mike Iselyeacbe7c2006-06-25 20:04:06 -0300230 for (idx = 4; idx < arg_cnt_send; idx++) {
Mike Iselyd8554972006-06-26 20:58:46 -0300231 pvr2_trace(
232 PVR2_TRACE_ERROR_LEGS,
233 "Encoder arg%d: 0x%08x",
234 idx-3,wrData[idx]);
235 }
236 pvr2_trace(
237 PVR2_TRACE_ERROR_LEGS,
238 "Giving up waiting."
239 " It is likely that"
240 " this is a bad idea...");
241 ret = -EBUSY;
242 break;
243 }
244 }
245 if (ret) break;
246 wrData[0] = 0x7;
Mike Iselyeacbe7c2006-06-25 20:04:06 -0300247 ret = pvr2_encoder_read_words(
Ahmed S. Darwisheca8ebf2007-01-20 00:35:03 -0300248 hdw,0,rdData, ARRAY_SIZE(rdData));
Mike Iselyd8554972006-06-26 20:58:46 -0300249 if (ret) break;
Mike Iselyeacbe7c2006-06-25 20:04:06 -0300250 for (idx = 0; idx < arg_cnt_recv; idx++) {
251 argp[idx] = rdData[idx+4];
Mike Iselyd8554972006-06-26 20:58:46 -0300252 }
253
254 wrData[0] = 0x0;
Mike Iselyeacbe7c2006-06-25 20:04:06 -0300255 ret = pvr2_encoder_write_words(hdw,wrData,1);
Mike Iselyd8554972006-06-26 20:58:46 -0300256 if (ret) break;
257
258 } while(0); LOCK_GIVE(hdw->ctl_lock);
259
260 return ret;
261}
262
Mike Iselyeacbe7c2006-06-25 20:04:06 -0300263
264static int pvr2_encoder_vcmd(struct pvr2_hdw *hdw, int cmd,
265 int args, ...)
266{
267 va_list vl;
268 unsigned int idx;
269 u32 data[12];
270
Ahmed S. Darwisheca8ebf2007-01-20 00:35:03 -0300271 if (args > ARRAY_SIZE(data)) {
Mike Iselyeacbe7c2006-06-25 20:04:06 -0300272 pvr2_trace(
273 PVR2_TRACE_ERROR_LEGS,
274 "Failed to write cx23416 command"
275 " - too many arguments"
Mauro Carvalho Chehab69b04f02007-01-21 22:02:35 -0300276 " (was given %u limit %lu)",
277 args, (long unsigned) ARRAY_SIZE(data));
Mike Iselyeacbe7c2006-06-25 20:04:06 -0300278 return -EINVAL;
279 }
280
281 va_start(vl, args);
282 for (idx = 0; idx < args; idx++) {
283 data[idx] = va_arg(vl, u32);
284 }
285 va_end(vl);
286
287 return pvr2_encoder_cmd(hdw,cmd,args,0,data);
288}
289
Mike Iselyd8554972006-06-26 20:58:46 -0300290int pvr2_encoder_configure(struct pvr2_hdw *hdw)
291{
Mike Iselyb30d2442006-06-25 20:05:01 -0300292 int ret;
293 pvr2_trace(PVR2_TRACE_ENCODER,"pvr2_encoder_configure"
294 " (cx2341x module)");
295 hdw->enc_ctl_state.port = CX2341X_PORT_STREAMING;
296 hdw->enc_ctl_state.width = hdw->res_hor_val;
297 hdw->enc_ctl_state.height = hdw->res_ver_val;
298 hdw->enc_ctl_state.is_50hz = ((hdw->std_mask_cur &
299 (V4L2_STD_NTSC|V4L2_STD_PAL_M)) ?
300 0 : 1);
Mike Iselyd8554972006-06-26 20:58:46 -0300301
Mike Iselyb30d2442006-06-25 20:05:01 -0300302 ret = 0;
Mike Iselyd8554972006-06-26 20:58:46 -0300303
Mike Iselyb30d2442006-06-25 20:05:01 -0300304 if (!ret) ret = pvr2_encoder_vcmd(
305 hdw,CX2341X_ENC_SET_NUM_VSYNC_LINES, 2,
306 0xf0, 0xf0);
Mike Iselyd8554972006-06-26 20:58:46 -0300307
308 /* setup firmware to notify us about some events (don't know why...) */
Mike Iselyb30d2442006-06-25 20:05:01 -0300309 if (!ret) ret = pvr2_encoder_vcmd(
310 hdw,CX2341X_ENC_SET_EVENT_NOTIFICATION, 4,
311 0, 0, 0x10000000, 0xffffffff);
Mike Iselyd8554972006-06-26 20:58:46 -0300312
Mike Iselyb30d2442006-06-25 20:05:01 -0300313 if (!ret) ret = pvr2_encoder_vcmd(
314 hdw,CX2341X_ENC_SET_VBI_LINE, 5,
315 0xffffffff,0,0,0,0);
Mike Iselyd8554972006-06-26 20:58:46 -0300316
Mike Iselyb30d2442006-06-25 20:05:01 -0300317 if (ret) {
318 pvr2_trace(PVR2_TRACE_ERROR_LEGS,
Mauro Carvalho Chehab58f56cb2006-08-30 05:44:31 -0300319 "Failed to configure cx23416");
Mike Iselyb30d2442006-06-25 20:05:01 -0300320 return ret;
Mike Iselyd8554972006-06-26 20:58:46 -0300321 }
322
Mike Iselyb30d2442006-06-25 20:05:01 -0300323 ret = cx2341x_update(hdw,pvr2_encoder_cmd,
Mike Iselya0fd1cb2006-06-30 11:35:28 -0300324 (hdw->enc_cur_valid ? &hdw->enc_cur_state : NULL),
Mike Iselyb30d2442006-06-25 20:05:01 -0300325 &hdw->enc_ctl_state);
326 if (ret) {
327 pvr2_trace(PVR2_TRACE_ERROR_LEGS,
328 "Error from cx2341x module code=%d",ret);
329 return ret;
Mike Iselyd8554972006-06-26 20:58:46 -0300330 }
331
Mike Iselyb30d2442006-06-25 20:05:01 -0300332 ret = 0;
333
334 if (!ret) ret = pvr2_encoder_vcmd(
335 hdw, CX2341X_ENC_INITIALIZE_INPUT, 0);
336
337 if (ret) {
338 pvr2_trace(PVR2_TRACE_ERROR_LEGS,
Mauro Carvalho Chehab58f56cb2006-08-30 05:44:31 -0300339 "Failed to initialize cx23416 video input");
Mike Iselyb30d2442006-06-25 20:05:01 -0300340 return ret;
341 }
342
343 hdw->subsys_enabled_mask |= (1<<PVR2_SUBSYS_B_ENC_CFG);
344 memcpy(&hdw->enc_cur_state,&hdw->enc_ctl_state,
345 sizeof(struct cx2341x_mpeg_params));
346 hdw->enc_cur_valid = !0;
347 return 0;
Mike Iselyd8554972006-06-26 20:58:46 -0300348}
349
Mike Iselyb30d2442006-06-25 20:05:01 -0300350
Mike Iselyd8554972006-06-26 20:58:46 -0300351int pvr2_encoder_start(struct pvr2_hdw *hdw)
352{
353 int status;
354
355 /* unmask some interrupts */
356 pvr2_write_register(hdw, 0x0048, 0xbfffffff);
357
358 /* change some GPIO data */
359 pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000481);
360 pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000000);
361
Pantelis Koukousoulas275b2e22006-12-27 23:05:19 -0300362 pvr2_encoder_vcmd(hdw,CX2341X_ENC_MUTE_VIDEO,1,
363 hdw->input_val == PVR2_CVAL_INPUT_RADIO ? 1 : 0);
364
Mike Isely16eb40d2006-12-30 18:27:32 -0300365 switch (hdw->config) {
366 case pvr2_config_vbi:
Mike Iselyeacbe7c2006-06-25 20:04:06 -0300367 status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2,
368 0x01,0x14);
Mike Isely16eb40d2006-12-30 18:27:32 -0300369 break;
370 case pvr2_config_mpeg:
Mike Iselyeacbe7c2006-06-25 20:04:06 -0300371 status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2,
372 0,0x13);
Mike Isely16eb40d2006-12-30 18:27:32 -0300373 break;
374 default: /* Unhandled cases for now */
Mike Iselyeacbe7c2006-06-25 20:04:06 -0300375 status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2,
376 0,0x13);
Mike Isely16eb40d2006-12-30 18:27:32 -0300377 break;
Mike Iselyd8554972006-06-26 20:58:46 -0300378 }
379 if (!status) {
380 hdw->subsys_enabled_mask |= (1<<PVR2_SUBSYS_B_ENC_RUN);
381 }
382 return status;
383}
384
385int pvr2_encoder_stop(struct pvr2_hdw *hdw)
386{
387 int status;
388
389 /* mask all interrupts */
390 pvr2_write_register(hdw, 0x0048, 0xffffffff);
391
Mike Isely16eb40d2006-12-30 18:27:32 -0300392 switch (hdw->config) {
393 case pvr2_config_vbi:
Mike Iselyeacbe7c2006-06-25 20:04:06 -0300394 status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3,
395 0x01,0x01,0x14);
Mike Isely16eb40d2006-12-30 18:27:32 -0300396 break;
397 case pvr2_config_mpeg:
Mike Iselyeacbe7c2006-06-25 20:04:06 -0300398 status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3,
399 0x01,0,0x13);
Mike Isely16eb40d2006-12-30 18:27:32 -0300400 break;
401 default: /* Unhandled cases for now */
Mike Iselyeacbe7c2006-06-25 20:04:06 -0300402 status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3,
403 0x01,0,0x13);
Mike Isely16eb40d2006-12-30 18:27:32 -0300404 break;
Mike Iselyd8554972006-06-26 20:58:46 -0300405 }
406
407 /* change some GPIO data */
408 /* Note: Bit d7 of dir appears to control the LED. So we shut it
409 off here. */
410 pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000401);
411 pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000000);
412
413 if (!status) {
414 hdw->subsys_enabled_mask &= ~(1<<PVR2_SUBSYS_B_ENC_RUN);
415 }
416 return status;
417}
418
419
420/*
421 Stuff for Emacs to see, in order to encourage consistent editing style:
422 *** Local Variables: ***
423 *** mode: c ***
424 *** fill-column: 70 ***
425 *** tab-width: 8 ***
426 *** c-basic-offset: 8 ***
427 *** End: ***
428 */