V4L/DVB (5034): Pvrusb2: Enable radio mode round #1
[linux-2.6/x86.git] / drivers / media / video / pvrusb2 / pvrusb2-encoder.c
blobd094cac9f7a59cb37c349dd26f1acd9e26be1871
1 /*
3 * $Id$
5 * Copyright (C) 2005 Mike Isely <isely@pobox.com>
6 * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
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
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.
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
23 #include <linux/device.h> // for linux/firmware.h
24 #include <linux/firmware.h>
25 #include "pvrusb2-util.h"
26 #include "pvrusb2-encoder.h"
27 #include "pvrusb2-hdw-internal.h"
28 #include "pvrusb2-debug.h"
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
38 static int pvr2_encoder_write_words(struct pvr2_hdw *hdw,
39 const u32 *data, unsigned int dlen)
41 unsigned int idx;
42 int ret;
43 unsigned int offs = 0;
44 unsigned int chunkCnt;
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.
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]);
66 ret = pvr2_send_request(hdw,
67 hdw->cmd_buffer,1+(chunkCnt*7),
68 NULL,0);
69 if (ret) return ret;
70 data += chunkCnt;
71 dlen -= chunkCnt;
72 offs += chunkCnt;
75 return 0;
79 static int pvr2_encoder_read_words(struct pvr2_hdw *hdw,int statusFl,
80 u32 *data, unsigned int dlen)
82 unsigned int idx;
83 int ret;
84 unsigned int offs = 0;
85 unsigned int chunkCnt;
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.
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;
108 for (idx = 0; idx < chunkCnt; idx++) {
109 data[idx] = PVR2_COMPOSE_LE(hdw->cmd_buffer,idx*4);
111 data += chunkCnt;
112 dlen -= chunkCnt;
113 offs += chunkCnt;
116 return 0;
120 /* 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. */
125 static int pvr2_encoder_cmd(void *ctxt,
126 int cmd,
127 int arg_cnt_send,
128 int arg_cnt_recv,
129 u32 *argp)
131 unsigned int poll_count;
132 int ret = 0;
133 unsigned int idx;
134 /* These sizes look to be limited by the FX2 firmware implementation */
135 u32 wrData[16];
136 u32 rdData[16];
137 struct pvr2_hdw *hdw = (struct pvr2_hdw *)ctxt;
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.
152 First, write out the entire set of words, with the first word
153 being zero.
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").
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.
164 Next, read back 32 words and compare with the original
165 arugments. Hopefully they will match.
167 Finally, write out just the first word again, but set it to
168 0x0 this time (which probably means "idle").
172 if (arg_cnt_send > (sizeof(wrData)/sizeof(wrData[0]))-4) {
173 pvr2_trace(
174 PVR2_TRACE_ERROR_LEGS,
175 "Failed to write cx23416 command"
176 " - too many input arguments"
177 " (was given %u limit %u)",
178 arg_cnt_send,
179 (unsigned int)(sizeof(wrData)/sizeof(wrData[0])) - 4);
180 return -EINVAL;
183 if (arg_cnt_recv > (sizeof(rdData)/sizeof(rdData[0]))-4) {
184 pvr2_trace(
185 PVR2_TRACE_ERROR_LEGS,
186 "Failed to write cx23416 command"
187 " - too many return arguments"
188 " (was given %u limit %u)",
189 arg_cnt_recv,
190 (unsigned int)(sizeof(rdData)/sizeof(rdData[0])) - 4);
191 return -EINVAL;
195 LOCK_TAKE(hdw->ctl_lock); do {
197 wrData[0] = 0;
198 wrData[1] = cmd;
199 wrData[2] = 0;
200 wrData[3] = 0x00060000;
201 for (idx = 0; idx < arg_cnt_send; idx++) {
202 wrData[idx+4] = argp[idx];
204 for (; idx < (sizeof(wrData)/sizeof(wrData[0]))-4; idx++) {
205 wrData[idx+4] = 0;
208 ret = pvr2_encoder_write_words(hdw,wrData,idx);
209 if (ret) break;
210 wrData[0] = IVTV_MBOX_DRIVER_DONE|IVTV_MBOX_DRIVER_BUSY;
211 ret = pvr2_encoder_write_words(hdw,wrData,1);
212 if (ret) break;
213 poll_count = 0;
214 while (1) {
215 if (poll_count < 10000000) poll_count++;
216 ret = pvr2_encoder_read_words(hdw,!0,rdData,1);
217 if (ret) break;
218 if (rdData[0] & IVTV_MBOX_FIRMWARE_DONE) {
219 break;
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);
230 for (idx = 4; idx < arg_cnt_send; idx++) {
231 pvr2_trace(
232 PVR2_TRACE_ERROR_LEGS,
233 "Encoder arg%d: 0x%08x",
234 idx-3,wrData[idx]);
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;
245 if (ret) break;
246 wrData[0] = 0x7;
247 ret = pvr2_encoder_read_words(
248 hdw,0,rdData,
249 sizeof(rdData)/sizeof(rdData[0]));
250 if (ret) break;
251 for (idx = 0; idx < arg_cnt_recv; idx++) {
252 argp[idx] = rdData[idx+4];
255 wrData[0] = 0x0;
256 ret = pvr2_encoder_write_words(hdw,wrData,1);
257 if (ret) break;
259 } while(0); LOCK_GIVE(hdw->ctl_lock);
261 return ret;
265 static int pvr2_encoder_vcmd(struct pvr2_hdw *hdw, int cmd,
266 int args, ...)
268 va_list vl;
269 unsigned int idx;
270 u32 data[12];
272 if (args > sizeof(data)/sizeof(data[0])) {
273 pvr2_trace(
274 PVR2_TRACE_ERROR_LEGS,
275 "Failed to write cx23416 command"
276 " - too many arguments"
277 " (was given %u limit %u)",
278 args,(unsigned int)(sizeof(data)/sizeof(data[0])));
279 return -EINVAL;
282 va_start(vl, args);
283 for (idx = 0; idx < args; idx++) {
284 data[idx] = va_arg(vl, u32);
286 va_end(vl);
288 return pvr2_encoder_cmd(hdw,cmd,args,0,data);
291 int pvr2_encoder_configure(struct pvr2_hdw *hdw)
293 int ret;
294 pvr2_trace(PVR2_TRACE_ENCODER,"pvr2_encoder_configure"
295 " (cx2341x module)");
296 hdw->enc_ctl_state.port = CX2341X_PORT_STREAMING;
297 hdw->enc_ctl_state.width = hdw->res_hor_val;
298 hdw->enc_ctl_state.height = hdw->res_ver_val;
299 hdw->enc_ctl_state.is_50hz = ((hdw->std_mask_cur &
300 (V4L2_STD_NTSC|V4L2_STD_PAL_M)) ?
301 0 : 1);
303 ret = 0;
305 if (!ret) ret = pvr2_encoder_vcmd(
306 hdw,CX2341X_ENC_SET_NUM_VSYNC_LINES, 2,
307 0xf0, 0xf0);
309 /* setup firmware to notify us about some events (don't know why...) */
310 if (!ret) ret = pvr2_encoder_vcmd(
311 hdw,CX2341X_ENC_SET_EVENT_NOTIFICATION, 4,
312 0, 0, 0x10000000, 0xffffffff);
314 if (!ret) ret = pvr2_encoder_vcmd(
315 hdw,CX2341X_ENC_SET_VBI_LINE, 5,
316 0xffffffff,0,0,0,0);
318 if (ret) {
319 pvr2_trace(PVR2_TRACE_ERROR_LEGS,
320 "Failed to configure cx23416");
321 return ret;
324 ret = cx2341x_update(hdw,pvr2_encoder_cmd,
325 (hdw->enc_cur_valid ? &hdw->enc_cur_state : NULL),
326 &hdw->enc_ctl_state);
327 if (ret) {
328 pvr2_trace(PVR2_TRACE_ERROR_LEGS,
329 "Error from cx2341x module code=%d",ret);
330 return ret;
333 ret = 0;
335 if (!ret) ret = pvr2_encoder_vcmd(
336 hdw, CX2341X_ENC_INITIALIZE_INPUT, 0);
338 if (ret) {
339 pvr2_trace(PVR2_TRACE_ERROR_LEGS,
340 "Failed to initialize cx23416 video input");
341 return ret;
344 hdw->subsys_enabled_mask |= (1<<PVR2_SUBSYS_B_ENC_CFG);
345 memcpy(&hdw->enc_cur_state,&hdw->enc_ctl_state,
346 sizeof(struct cx2341x_mpeg_params));
347 hdw->enc_cur_valid = !0;
348 return 0;
352 int pvr2_encoder_start(struct pvr2_hdw *hdw)
354 int status;
356 /* unmask some interrupts */
357 pvr2_write_register(hdw, 0x0048, 0xbfffffff);
359 /* change some GPIO data */
360 pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000481);
361 pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000000);
363 pvr2_encoder_vcmd(hdw,CX2341X_ENC_MUTE_VIDEO,1,
364 hdw->input_val == PVR2_CVAL_INPUT_RADIO ? 1 : 0);
366 if (hdw->config == pvr2_config_vbi) {
367 status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2,
368 0x01,0x14);
369 } else if (hdw->config == pvr2_config_mpeg) {
370 status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2,
371 0,0x13);
372 } else {
373 status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2,
374 0,0x13);
376 if (!status) {
377 hdw->subsys_enabled_mask |= (1<<PVR2_SUBSYS_B_ENC_RUN);
379 return status;
382 int pvr2_encoder_stop(struct pvr2_hdw *hdw)
384 int status;
386 /* mask all interrupts */
387 pvr2_write_register(hdw, 0x0048, 0xffffffff);
389 if (hdw->config == pvr2_config_vbi) {
390 status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3,
391 0x01,0x01,0x14);
392 } else if (hdw->config == pvr2_config_mpeg) {
393 status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3,
394 0x01,0,0x13);
395 } else {
396 status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3,
397 0x01,0,0x13);
400 /* change some GPIO data */
401 /* Note: Bit d7 of dir appears to control the LED. So we shut it
402 off here. */
403 pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000401);
404 pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000000);
406 if (!status) {
407 hdw->subsys_enabled_mask &= ~(1<<PVR2_SUBSYS_B_ENC_RUN);
409 return status;
414 Stuff for Emacs to see, in order to encourage consistent editing style:
415 *** Local Variables: ***
416 *** mode: c ***
417 *** fill-column: 70 ***
418 *** tab-width: 8 ***
419 *** c-basic-offset: 8 ***
420 *** End: ***