blob: 9e43182231a5bc2ed7688ab47ecb7122c3b4aa58 [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"
29
Mike Iselyd8554972006-06-26 20:58:46 -030030
31
32/* Firmware mailbox flags - definitions found from ivtv */
33#define IVTV_MBOX_FIRMWARE_DONE 0x00000004
34#define IVTV_MBOX_DRIVER_DONE 0x00000002
35#define IVTV_MBOX_DRIVER_BUSY 0x00000001
36
37
Mike Iselyeacbe7c2006-06-25 20:04:06 -030038static int pvr2_encoder_write_words(struct pvr2_hdw *hdw,
Mike Iselyd8554972006-06-26 20:58:46 -030039 const u32 *data, unsigned int dlen)
40{
41 unsigned int idx;
42 int ret;
43 unsigned int offs = 0;
44 unsigned int chunkCnt;
45
46 /*
47
48 Format: First byte must be 0x01. Remaining 32 bit words are
49 spread out into chunks of 7 bytes each, little-endian ordered,
50 offset at zero within each 2 blank bytes following and a
51 single byte that is 0x44 plus the offset of the word. Repeat
52 request for additional words, with offset adjusted
53 accordingly.
54
55 */
56 while (dlen) {
57 chunkCnt = 8;
58 if (chunkCnt > dlen) chunkCnt = dlen;
59 memset(hdw->cmd_buffer,0,sizeof(hdw->cmd_buffer));
60 hdw->cmd_buffer[0] = 0x01;
61 for (idx = 0; idx < chunkCnt; idx++) {
62 hdw->cmd_buffer[1+(idx*7)+6] = 0x44 + idx + offs;
63 PVR2_DECOMPOSE_LE(hdw->cmd_buffer, 1+(idx*7),
64 data[idx]);
65 }
66 ret = pvr2_send_request(hdw,
67 hdw->cmd_buffer,1+(chunkCnt*7),
Mike Iselya0fd1cb2006-06-30 11:35:28 -030068 NULL,0);
Mike Iselyd8554972006-06-26 20:58:46 -030069 if (ret) return ret;
70 data += chunkCnt;
71 dlen -= chunkCnt;
72 offs += chunkCnt;
73 }
74
75 return 0;
76}
77
78
Mike Iselyeacbe7c2006-06-25 20:04:06 -030079static int pvr2_encoder_read_words(struct pvr2_hdw *hdw,int statusFl,
Mike Iselyd8554972006-06-26 20:58:46 -030080 u32 *data, unsigned int dlen)
81{
82 unsigned int idx;
83 int ret;
84 unsigned int offs = 0;
85 unsigned int chunkCnt;
86
87 /*
88
89 Format: First byte must be 0x02 (status check) or 0x28 (read
90 back block of 32 bit words). Next 6 bytes must be zero,
91 followed by a single byte of 0x44+offset for portion to be
92 read. Returned data is packed set of 32 bits words that were
93 read.
94
95 */
96
97 while (dlen) {
98 chunkCnt = 16;
99 if (chunkCnt > dlen) chunkCnt = dlen;
100 memset(hdw->cmd_buffer,0,sizeof(hdw->cmd_buffer));
101 hdw->cmd_buffer[0] = statusFl ? 0x02 : 0x28;
102 hdw->cmd_buffer[7] = 0x44 + offs;
103 ret = pvr2_send_request(hdw,
104 hdw->cmd_buffer,8,
105 hdw->cmd_buffer,chunkCnt * 4);
106 if (ret) return ret;
107
108 for (idx = 0; idx < chunkCnt; idx++) {
109 data[idx] = PVR2_COMPOSE_LE(hdw->cmd_buffer,idx*4);
110 }
111 data += chunkCnt;
112 dlen -= chunkCnt;
113 offs += chunkCnt;
114 }
115
116 return 0;
117}
118
119
Mike Iselyeacbe7c2006-06-25 20:04:06 -0300120/* This prototype is set up to be compatible with the
121 cx2341x_mbox_func prototype in cx2341x.h, which should be in
122 kernels 2.6.18 or later. We do this so that we can enable
123 cx2341x.ko to write to our encoder (by handing it a pointer to this
124 function). For earlier kernels this doesn't really matter. */
125static int pvr2_encoder_cmd(void *ctxt,
126 int cmd,
127 int arg_cnt_send,
128 int arg_cnt_recv,
129 u32 *argp)
Mike Iselyd8554972006-06-26 20:58:46 -0300130{
131 unsigned int poll_count;
132 int ret = 0;
Mike Iselyd8554972006-06-26 20:58:46 -0300133 unsigned int idx;
Mike Iselyeacbe7c2006-06-25 20:04:06 -0300134 /* These sizes look to be limited by the FX2 firmware implementation */
Mike Iselyd8554972006-06-26 20:58:46 -0300135 u32 wrData[16];
Mike Iselyeacbe7c2006-06-25 20:04:06 -0300136 u32 rdData[16];
137 struct pvr2_hdw *hdw = (struct pvr2_hdw *)ctxt;
Mike Iselyd8554972006-06-26 20:58:46 -0300138
Mike Iselyc05c0462006-06-25 20:04:25 -0300139
Mike Iselyd8554972006-06-26 20:58:46 -0300140 /*
141
142 The encoder seems to speak entirely using blocks 32 bit words.
143 In ivtv driver terms, this is a mailbox which we populate with
144 data and watch what the hardware does with it. The first word
145 is a set of flags used to control the transaction, the second
146 word is the command to execute, the third byte is zero (ivtv
147 driver suggests that this is some kind of return value), and
148 the fourth byte is a specified timeout (windows driver always
149 uses 0x00060000 except for one case when it is zero). All
150 successive words are the argument words for the command.
151
152 First, write out the entire set of words, with the first word
153 being zero.
154
155 Next, write out just the first word again, but set it to
156 IVTV_MBOX_DRIVER_DONE | IVTV_DRIVER_BUSY this time (which
157 probably means "go").
158
159 Next, read back 16 words as status. Check the first word,
160 which should have IVTV_MBOX_FIRMWARE_DONE set. If however
161 that bit is not set, then the command isn't done so repeat the
162 read.
163
164 Next, read back 32 words and compare with the original
165 arugments. Hopefully they will match.
166
167 Finally, write out just the first word again, but set it to
168 0x0 this time (which probably means "idle").
169
170 */
171
Ahmed S. Darwisheca8ebf2007-01-20 00:35:03 -0300172 if (arg_cnt_send > (ARRAY_SIZE(wrData) - 4)) {
Mike Iselyeacbe7c2006-06-25 20:04:06 -0300173 pvr2_trace(
174 PVR2_TRACE_ERROR_LEGS,
175 "Failed to write cx23416 command"
176 " - too many input arguments"
Mauro Carvalho Chehab69b04f02007-01-21 22:02:35 -0300177 " (was given %u limit %lu)",
178 arg_cnt_send, (long unsigned) ARRAY_SIZE(wrData) - 4);
Mike Iselyeacbe7c2006-06-25 20:04:06 -0300179 return -EINVAL;
180 }
181
Ahmed S. Darwisheca8ebf2007-01-20 00:35:03 -0300182 if (arg_cnt_recv > (ARRAY_SIZE(rdData) - 4)) {
Mike Iselyeacbe7c2006-06-25 20:04:06 -0300183 pvr2_trace(
184 PVR2_TRACE_ERROR_LEGS,
185 "Failed to write cx23416 command"
186 " - too many return arguments"
Mauro Carvalho Chehab69b04f02007-01-21 22:02:35 -0300187 " (was given %u limit %lu)",
188 arg_cnt_recv, (long unsigned) ARRAY_SIZE(rdData) - 4);
Mike Iselyeacbe7c2006-06-25 20:04:06 -0300189 return -EINVAL;
190 }
191
Mike Iselyd8554972006-06-26 20:58:46 -0300192
193 LOCK_TAKE(hdw->ctl_lock); do {
194
195 wrData[0] = 0;
196 wrData[1] = cmd;
197 wrData[2] = 0;
198 wrData[3] = 0x00060000;
Mike Iselyeacbe7c2006-06-25 20:04:06 -0300199 for (idx = 0; idx < arg_cnt_send; idx++) {
200 wrData[idx+4] = argp[idx];
Mike Iselyd8554972006-06-26 20:58:46 -0300201 }
Ahmed S. Darwisheca8ebf2007-01-20 00:35:03 -0300202 for (; idx < ARRAY_SIZE(wrData) - 4; idx++) {
Mike Iselyeacbe7c2006-06-25 20:04:06 -0300203 wrData[idx+4] = 0;
Mike Iselyd8554972006-06-26 20:58:46 -0300204 }
205
Mike Iselyeacbe7c2006-06-25 20:04:06 -0300206 ret = pvr2_encoder_write_words(hdw,wrData,idx);
Mike Iselyd8554972006-06-26 20:58:46 -0300207 if (ret) break;
208 wrData[0] = IVTV_MBOX_DRIVER_DONE|IVTV_MBOX_DRIVER_BUSY;
Mike Iselyeacbe7c2006-06-25 20:04:06 -0300209 ret = pvr2_encoder_write_words(hdw,wrData,1);
Mike Iselyd8554972006-06-26 20:58:46 -0300210 if (ret) break;
211 poll_count = 0;
212 while (1) {
213 if (poll_count < 10000000) poll_count++;
Mike Iselyeacbe7c2006-06-25 20:04:06 -0300214 ret = pvr2_encoder_read_words(hdw,!0,rdData,1);
Mike Iselyd8554972006-06-26 20:58:46 -0300215 if (ret) break;
216 if (rdData[0] & IVTV_MBOX_FIRMWARE_DONE) {
217 break;
218 }
219 if (poll_count == 100) {
220 pvr2_trace(
221 PVR2_TRACE_ERROR_LEGS,
222 "***WARNING*** device's encoder"
223 " appears to be stuck"
224 " (status=0%08x)",rdData[0]);
225 pvr2_trace(
226 PVR2_TRACE_ERROR_LEGS,
227 "Encoder command: 0x%02x",cmd);
Mike Iselyeacbe7c2006-06-25 20:04:06 -0300228 for (idx = 4; idx < arg_cnt_send; idx++) {
Mike Iselyd8554972006-06-26 20:58:46 -0300229 pvr2_trace(
230 PVR2_TRACE_ERROR_LEGS,
231 "Encoder arg%d: 0x%08x",
232 idx-3,wrData[idx]);
233 }
234 pvr2_trace(
235 PVR2_TRACE_ERROR_LEGS,
236 "Giving up waiting."
237 " It is likely that"
238 " this is a bad idea...");
239 ret = -EBUSY;
240 break;
241 }
242 }
243 if (ret) break;
244 wrData[0] = 0x7;
Mike Iselyeacbe7c2006-06-25 20:04:06 -0300245 ret = pvr2_encoder_read_words(
Ahmed S. Darwisheca8ebf2007-01-20 00:35:03 -0300246 hdw,0,rdData, ARRAY_SIZE(rdData));
Mike Iselyd8554972006-06-26 20:58:46 -0300247 if (ret) break;
Mike Iselyeacbe7c2006-06-25 20:04:06 -0300248 for (idx = 0; idx < arg_cnt_recv; idx++) {
249 argp[idx] = rdData[idx+4];
Mike Iselyd8554972006-06-26 20:58:46 -0300250 }
251
252 wrData[0] = 0x0;
Mike Iselyeacbe7c2006-06-25 20:04:06 -0300253 ret = pvr2_encoder_write_words(hdw,wrData,1);
Mike Iselyd8554972006-06-26 20:58:46 -0300254 if (ret) break;
255
256 } while(0); LOCK_GIVE(hdw->ctl_lock);
257
258 return ret;
259}
260
Mike Iselyeacbe7c2006-06-25 20:04:06 -0300261
262static int pvr2_encoder_vcmd(struct pvr2_hdw *hdw, int cmd,
263 int args, ...)
264{
265 va_list vl;
266 unsigned int idx;
267 u32 data[12];
268
Ahmed S. Darwisheca8ebf2007-01-20 00:35:03 -0300269 if (args > ARRAY_SIZE(data)) {
Mike Iselyeacbe7c2006-06-25 20:04:06 -0300270 pvr2_trace(
271 PVR2_TRACE_ERROR_LEGS,
272 "Failed to write cx23416 command"
273 " - too many arguments"
Mauro Carvalho Chehab69b04f02007-01-21 22:02:35 -0300274 " (was given %u limit %lu)",
275 args, (long unsigned) ARRAY_SIZE(data));
Mike Iselyeacbe7c2006-06-25 20:04:06 -0300276 return -EINVAL;
277 }
278
279 va_start(vl, args);
280 for (idx = 0; idx < args; idx++) {
281 data[idx] = va_arg(vl, u32);
282 }
283 va_end(vl);
284
285 return pvr2_encoder_cmd(hdw,cmd,args,0,data);
286}
287
Mike Iselyd8554972006-06-26 20:58:46 -0300288int pvr2_encoder_configure(struct pvr2_hdw *hdw)
289{
Mike Iselyb30d2442006-06-25 20:05:01 -0300290 int ret;
291 pvr2_trace(PVR2_TRACE_ENCODER,"pvr2_encoder_configure"
292 " (cx2341x module)");
293 hdw->enc_ctl_state.port = CX2341X_PORT_STREAMING;
294 hdw->enc_ctl_state.width = hdw->res_hor_val;
295 hdw->enc_ctl_state.height = hdw->res_ver_val;
296 hdw->enc_ctl_state.is_50hz = ((hdw->std_mask_cur &
297 (V4L2_STD_NTSC|V4L2_STD_PAL_M)) ?
298 0 : 1);
Mike Iselyd8554972006-06-26 20:58:46 -0300299
Mike Iselyb30d2442006-06-25 20:05:01 -0300300 ret = 0;
Mike Iselyd8554972006-06-26 20:58:46 -0300301
Mike Iselyb30d2442006-06-25 20:05:01 -0300302 if (!ret) ret = pvr2_encoder_vcmd(
303 hdw,CX2341X_ENC_SET_NUM_VSYNC_LINES, 2,
304 0xf0, 0xf0);
Mike Iselyd8554972006-06-26 20:58:46 -0300305
306 /* setup firmware to notify us about some events (don't know why...) */
Mike Iselyb30d2442006-06-25 20:05:01 -0300307 if (!ret) ret = pvr2_encoder_vcmd(
308 hdw,CX2341X_ENC_SET_EVENT_NOTIFICATION, 4,
309 0, 0, 0x10000000, 0xffffffff);
Mike Iselyd8554972006-06-26 20:58:46 -0300310
Mike Iselyb30d2442006-06-25 20:05:01 -0300311 if (!ret) ret = pvr2_encoder_vcmd(
312 hdw,CX2341X_ENC_SET_VBI_LINE, 5,
313 0xffffffff,0,0,0,0);
Mike Iselyd8554972006-06-26 20:58:46 -0300314
Mike Iselyb30d2442006-06-25 20:05:01 -0300315 if (ret) {
316 pvr2_trace(PVR2_TRACE_ERROR_LEGS,
Mauro Carvalho Chehab58f56cb2006-08-30 05:44:31 -0300317 "Failed to configure cx23416");
Mike Iselyb30d2442006-06-25 20:05:01 -0300318 return ret;
Mike Iselyd8554972006-06-26 20:58:46 -0300319 }
320
Mike Iselyb30d2442006-06-25 20:05:01 -0300321 ret = cx2341x_update(hdw,pvr2_encoder_cmd,
Mike Iselya0fd1cb2006-06-30 11:35:28 -0300322 (hdw->enc_cur_valid ? &hdw->enc_cur_state : NULL),
Mike Iselyb30d2442006-06-25 20:05:01 -0300323 &hdw->enc_ctl_state);
324 if (ret) {
325 pvr2_trace(PVR2_TRACE_ERROR_LEGS,
326 "Error from cx2341x module code=%d",ret);
327 return ret;
Mike Iselyd8554972006-06-26 20:58:46 -0300328 }
329
Mike Iselyb30d2442006-06-25 20:05:01 -0300330 ret = 0;
331
332 if (!ret) ret = pvr2_encoder_vcmd(
333 hdw, CX2341X_ENC_INITIALIZE_INPUT, 0);
334
335 if (ret) {
336 pvr2_trace(PVR2_TRACE_ERROR_LEGS,
Mauro Carvalho Chehab58f56cb2006-08-30 05:44:31 -0300337 "Failed to initialize cx23416 video input");
Mike Iselyb30d2442006-06-25 20:05:01 -0300338 return ret;
339 }
340
341 hdw->subsys_enabled_mask |= (1<<PVR2_SUBSYS_B_ENC_CFG);
342 memcpy(&hdw->enc_cur_state,&hdw->enc_ctl_state,
343 sizeof(struct cx2341x_mpeg_params));
344 hdw->enc_cur_valid = !0;
345 return 0;
Mike Iselyd8554972006-06-26 20:58:46 -0300346}
347
Mike Iselyb30d2442006-06-25 20:05:01 -0300348
Mike Iselyd8554972006-06-26 20:58:46 -0300349int pvr2_encoder_start(struct pvr2_hdw *hdw)
350{
351 int status;
352
353 /* unmask some interrupts */
354 pvr2_write_register(hdw, 0x0048, 0xbfffffff);
355
356 /* change some GPIO data */
357 pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000481);
358 pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000000);
359
Pantelis Koukousoulas275b2e22006-12-27 23:05:19 -0300360 pvr2_encoder_vcmd(hdw,CX2341X_ENC_MUTE_VIDEO,1,
361 hdw->input_val == PVR2_CVAL_INPUT_RADIO ? 1 : 0);
362
Mike Isely16eb40d2006-12-30 18:27:32 -0300363 switch (hdw->config) {
364 case pvr2_config_vbi:
Mike Iselyeacbe7c2006-06-25 20:04:06 -0300365 status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2,
366 0x01,0x14);
Mike Isely16eb40d2006-12-30 18:27:32 -0300367 break;
368 case pvr2_config_mpeg:
Mike Iselyeacbe7c2006-06-25 20:04:06 -0300369 status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2,
370 0,0x13);
Mike Isely16eb40d2006-12-30 18:27:32 -0300371 break;
372 default: /* Unhandled cases for now */
Mike Iselyeacbe7c2006-06-25 20:04:06 -0300373 status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2,
374 0,0x13);
Mike Isely16eb40d2006-12-30 18:27:32 -0300375 break;
Mike Iselyd8554972006-06-26 20:58:46 -0300376 }
377 if (!status) {
378 hdw->subsys_enabled_mask |= (1<<PVR2_SUBSYS_B_ENC_RUN);
379 }
380 return status;
381}
382
383int pvr2_encoder_stop(struct pvr2_hdw *hdw)
384{
385 int status;
386
387 /* mask all interrupts */
388 pvr2_write_register(hdw, 0x0048, 0xffffffff);
389
Mike Isely16eb40d2006-12-30 18:27:32 -0300390 switch (hdw->config) {
391 case pvr2_config_vbi:
Mike Iselyeacbe7c2006-06-25 20:04:06 -0300392 status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3,
393 0x01,0x01,0x14);
Mike Isely16eb40d2006-12-30 18:27:32 -0300394 break;
395 case pvr2_config_mpeg:
Mike Iselyeacbe7c2006-06-25 20:04:06 -0300396 status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3,
397 0x01,0,0x13);
Mike Isely16eb40d2006-12-30 18:27:32 -0300398 break;
399 default: /* Unhandled cases for now */
Mike Iselyeacbe7c2006-06-25 20:04:06 -0300400 status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3,
401 0x01,0,0x13);
Mike Isely16eb40d2006-12-30 18:27:32 -0300402 break;
Mike Iselyd8554972006-06-26 20:58:46 -0300403 }
404
405 /* change some GPIO data */
406 /* Note: Bit d7 of dir appears to control the LED. So we shut it
407 off here. */
408 pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000401);
409 pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000000);
410
411 if (!status) {
412 hdw->subsys_enabled_mask &= ~(1<<PVR2_SUBSYS_B_ENC_RUN);
413 }
414 return status;
415}
416
417
418/*
419 Stuff for Emacs to see, in order to encourage consistent editing style:
420 *** Local Variables: ***
421 *** mode: c ***
422 *** fill-column: 70 ***
423 *** tab-width: 8 ***
424 *** c-basic-offset: 8 ***
425 *** End: ***
426 */