diff options
Diffstat (limited to 'linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c')
-rw-r--r-- | linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c | 377 |
1 files changed, 161 insertions, 216 deletions
diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c b/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c index 958500466..5b68bad01 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c @@ -23,41 +23,11 @@ #include "compat.h" #include <linux/device.h> // for linux/firmware.h #include <linux/firmware.h> -#include <linux/videodev2.h> -#include <media/cx2341x.h> #include "pvrusb2-util.h" #include "pvrusb2-encoder.h" #include "pvrusb2-hdw-internal.h" #include "pvrusb2-debug.h" -static u32 pvr_tbl_emphasis [] = { - [PVR2_CVAL_AUDIOEMPHASIS_NONE] = 0x0 << 12, - [PVR2_CVAL_AUDIOEMPHASIS_50_15] = 0x1 << 12, - [PVR2_CVAL_AUDIOEMPHASIS_CCITT] = 0x3 << 12, -}; - -static u32 pvr_tbl_srate[] = { - [PVR2_CVAL_SRATE_48] = 0x01, - [PVR2_CVAL_SRATE_44_1] = 0x00, -}; - -static u32 pvr_tbl_audiobitrate[] = { - [PVR2_CVAL_AUDIOBITRATE_384] = 0xe << 4, - [PVR2_CVAL_AUDIOBITRATE_320] = 0xd << 4, - [PVR2_CVAL_AUDIOBITRATE_256] = 0xc << 4, - [PVR2_CVAL_AUDIOBITRATE_224] = 0xb << 4, - [PVR2_CVAL_AUDIOBITRATE_192] = 0xa << 4, - [PVR2_CVAL_AUDIOBITRATE_160] = 0x9 << 4, - [PVR2_CVAL_AUDIOBITRATE_128] = 0x8 << 4, - [PVR2_CVAL_AUDIOBITRATE_112] = 0x7 << 4, - [PVR2_CVAL_AUDIOBITRATE_96] = 0x6 << 4, - [PVR2_CVAL_AUDIOBITRATE_80] = 0x5 << 4, - [PVR2_CVAL_AUDIOBITRATE_64] = 0x4 << 4, - [PVR2_CVAL_AUDIOBITRATE_56] = 0x3 << 4, - [PVR2_CVAL_AUDIOBITRATE_48] = 0x2 << 4, - [PVR2_CVAL_AUDIOBITRATE_32] = 0x1 << 4, - [PVR2_CVAL_AUDIOBITRATE_VBR] = 0x0 << 4, -}; /* Firmware mailbox flags - definitions found from ivtv */ @@ -66,7 +36,7 @@ static u32 pvr_tbl_audiobitrate[] = { #define IVTV_MBOX_DRIVER_BUSY 0x00000001 -static int pvr2_write_encoder_words(struct pvr2_hdw *hdw, +static int pvr2_encoder_write_words(struct pvr2_hdw *hdw, const u32 *data, unsigned int dlen) { unsigned int idx; @@ -107,7 +77,7 @@ static int pvr2_write_encoder_words(struct pvr2_hdw *hdw, } -static int pvr2_read_encoder_words(struct pvr2_hdw *hdw,int statusFl, +static int pvr2_encoder_read_words(struct pvr2_hdw *hdw,int statusFl, u32 *data, unsigned int dlen) { unsigned int idx; @@ -148,15 +118,46 @@ static int pvr2_read_encoder_words(struct pvr2_hdw *hdw,int statusFl, } -static int pvr2_write_encoder_vcmd (struct pvr2_hdw *hdw, u8 cmd, - int args, ...) +/* This prototype is set up to be compatible with the + cx2341x_mbox_func prototype in cx2341x.h, which should be in + kernels 2.6.18 or later. We do this so that we can enable + cx2341x.ko to write to our encoder (by handing it a pointer to this + function). For earlier kernels this doesn't really matter. */ +static int pvr2_encoder_cmd(void *ctxt, + int cmd, + int arg_cnt_send, + int arg_cnt_recv, + u32 *argp) { unsigned int poll_count; int ret = 0; - va_list vl; unsigned int idx; + /* These sizes look to be limited by the FX2 firmware implementation */ u32 wrData[16]; - u32 rdData[32]; + u32 rdData[16]; + struct pvr2_hdw *hdw = (struct pvr2_hdw *)ctxt; + +#if 0 + { + char buf[150]; + unsigned int ccnt,bcnt; + + bcnt = scnprintf(buf,sizeof(buf),"Encoder Cmd %02x (",cmd); + for (idx = 0; idx < arg_cnt_send; idx++) { + if (idx) { + ccnt = scnprintf(buf+bcnt, + sizeof(buf)-bcnt," "); + bcnt += ccnt; + } + ccnt = scnprintf(buf+bcnt,sizeof(buf)-bcnt, + "0x%x",argp[idx]); + bcnt += ccnt; + } + ccnt = scnprintf(buf+bcnt,sizeof(buf)-bcnt,")"); + bcnt += ccnt; + pvr2_trace(PVR2_TRACE_ENCODER,"%.*s",bcnt,buf); + } +#endif /* @@ -190,6 +191,28 @@ static int pvr2_write_encoder_vcmd (struct pvr2_hdw *hdw, u8 cmd, */ + if (arg_cnt_send > (sizeof(wrData)/sizeof(wrData[0]))-4) { + pvr2_trace( + PVR2_TRACE_ERROR_LEGS, + "Failed to write cx23416 command" + " - too many input arguments" + " (was given %u limit %u)", + arg_cnt_send, + (sizeof(wrData)/sizeof(wrData[0])) - 4); + return -EINVAL; + } + + if (arg_cnt_recv > (sizeof(rdData)/sizeof(rdData[0]))-4) { + pvr2_trace( + PVR2_TRACE_ERROR_LEGS, + "Failed to write cx23416 command" + " - too many return arguments" + " (was given %u limit %u)", + arg_cnt_recv, + (sizeof(rdData)/sizeof(rdData[0])) - 4); + return -EINVAL; + } + LOCK_TAKE(hdw->ctl_lock); do { @@ -197,25 +220,22 @@ static int pvr2_write_encoder_vcmd (struct pvr2_hdw *hdw, u8 cmd, wrData[1] = cmd; wrData[2] = 0; wrData[3] = 0x00060000; - va_start(vl, args); - for (idx = 0; idx < args; idx++) { - wrData[idx+4] = va_arg(vl, u32); + for (idx = 0; idx < arg_cnt_send; idx++) { + wrData[idx+4] = argp[idx]; } - va_end(vl); - args += 4; - while (args < sizeof(wrData)/sizeof(wrData[0])) { - wrData[args++] = 0; + for (; idx < (sizeof(wrData)/sizeof(wrData[0]))-4; idx++) { + wrData[idx+4] = 0; } - ret = pvr2_write_encoder_words(hdw,wrData,args); + ret = pvr2_encoder_write_words(hdw,wrData,idx); if (ret) break; wrData[0] = IVTV_MBOX_DRIVER_DONE|IVTV_MBOX_DRIVER_BUSY; - ret = pvr2_write_encoder_words(hdw,wrData,1); + ret = pvr2_encoder_write_words(hdw,wrData,1); if (ret) break; poll_count = 0; while (1) { if (poll_count < 10000000) poll_count++; - ret = pvr2_read_encoder_words(hdw,!0,rdData,1); + ret = pvr2_encoder_read_words(hdw,!0,rdData,1); if (ret) break; if (rdData[0] & IVTV_MBOX_FIRMWARE_DONE) { break; @@ -229,7 +249,7 @@ static int pvr2_write_encoder_vcmd (struct pvr2_hdw *hdw, u8 cmd, pvr2_trace( PVR2_TRACE_ERROR_LEGS, "Encoder command: 0x%02x",cmd); - for (idx = 4; idx < args; idx++) { + for (idx = 4; idx < arg_cnt_send; idx++) { pvr2_trace( PVR2_TRACE_ERROR_LEGS, "Encoder arg%d: 0x%08x", @@ -246,8 +266,11 @@ static int pvr2_write_encoder_vcmd (struct pvr2_hdw *hdw, u8 cmd, } if (ret) break; wrData[0] = 0x7; - ret = pvr2_read_encoder_words(hdw,0,rdData,16); + ret = pvr2_encoder_read_words( + hdw,0,rdData, + sizeof(rdData)/sizeof(rdData[0])); if (ret) break; +#if 0 for (idx = 0; idx < args; idx++) { if (rdData[idx] != wrData[idx]) { pvr2_trace( @@ -257,9 +280,13 @@ static int pvr2_write_encoder_vcmd (struct pvr2_hdw *hdw, u8 cmd, idx,wrData[idx],rdData[idx]); } } +#endif + for (idx = 0; idx < arg_cnt_recv; idx++) { + argp[idx] = rdData[idx+4]; + } wrData[0] = 0x0; - ret = pvr2_write_encoder_words(hdw,wrData,1); + ret = pvr2_encoder_write_words(hdw,wrData,1); if (ret) break; } while(0); LOCK_GIVE(hdw->ctl_lock); @@ -267,176 +294,94 @@ static int pvr2_write_encoder_vcmd (struct pvr2_hdw *hdw, u8 cmd, return ret; } -int pvr2_encoder_configure(struct pvr2_hdw *hdw) + +static int pvr2_encoder_vcmd(struct pvr2_hdw *hdw, int cmd, + int args, ...) { - int ret = 0, audio, i; - v4l2_std_id vd_std = hdw->std_mask_cur; - int height = hdw->res_ver_val; - int width = hdw->res_hor_val; - int height_full = !hdw->interlace_val; - - int is_30fps, is_ntsc; - - if (vd_std & V4L2_STD_NTSC) { - is_ntsc=1; - is_30fps=1; - } else if (vd_std & V4L2_STD_PAL_M) { - is_ntsc=0; - is_30fps=1; - } else { - is_ntsc=0; - is_30fps=0; + va_list vl; + unsigned int idx; + u32 data[12]; + + if (args > sizeof(data)/sizeof(data[0])) { + pvr2_trace( + PVR2_TRACE_ERROR_LEGS, + "Failed to write cx23416 command" + " - too many arguments" + " (was given %u limit %u)", + args,sizeof(data)/sizeof(data[0])); + return -EINVAL; } - pvr2_trace(PVR2_TRACE_ENCODER,"pvr2_encoder_configure"); - - /* set stream output port. Some notes here: The ivtv-derived - encoder documentation says that this command only gets a - single argument. However the Windows driver for the model - 29xxx series hardware has been sending 0x01 as a second - argument, while the Windows driver for the model 24xxx - series hardware has been sending 0x02 as a second argument. - Confusing matters further are the observations that 0x01 - for that second argument simply won't work on the 24xxx - hardware, while 0x02 will work on the 29xxx - except that - when we use 0x02 then xawtv breaks due to a loss of - synchronization with the mpeg packet headers. While xawtv - should be fixed to let it resync better (I did try to - contact Gerd about this but he has not answered), it has - also been determined that sending 0x00 as this mystery - second argument seems to work on both hardware models AND - xawtv works again. So we're going to send 0x00. */ - ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_SET_OUTPUT_PORT, 2, - 0x01, 0x00); - - /* set the Program Index Information. We want I,P,B frames (max 400) */ - ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_SET_PGM_INDEX_INFO, 2, - 0x07, 0x0190); - - /* NOTE : windows driver sends these */ - /* Mike Isely <isely@pobox.com> 7-Mar-2006 The windows driver - sends the following commands but if we do the same then - many apps are no longer able to read the video stream. - Leaving these out seems to do no harm at all, so they're - commented out for that reason. */ -#ifdef notdef - ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 5,0,0,0); - ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 3,1,0,0); - ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 8,0,0,0); - ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 4,1,0,0); - ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 0,3,0,0); - ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_MISC,4,15,0,0,0); -#endif + va_start(vl, args); + for (idx = 0; idx < args; idx++) { + data[idx] = va_arg(vl, u32); + } + va_end(vl); - /* Strange compared to ivtv data. */ -#if 0 - ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, - 0x0120, 0x0120); - ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, - 0x0131, 0x0131); -#endif - ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, - 0xf0, 0xf0); + return pvr2_encoder_cmd(hdw,cmd,args,0,data); +} - /* setup firmware to notify us about some events (don't know why...) */ - ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_SET_EVENT_NOTIFICATION, 4, - 0, 0, 0x10000000, 0xffffffff); +int pvr2_encoder_configure(struct pvr2_hdw *hdw) +{ + int ret; + pvr2_trace(PVR2_TRACE_ENCODER,"pvr2_encoder_configure" + " (cx2341x module)"); + hdw->enc_ctl_state.port = CX2341X_PORT_STREAMING; + hdw->enc_ctl_state.width = hdw->res_hor_val; + hdw->enc_ctl_state.height = hdw->res_ver_val; + hdw->enc_ctl_state.is_50hz = ((hdw->std_mask_cur & + (V4L2_STD_NTSC|V4L2_STD_PAL_M)) ? + 0 : 1); - /* set fps to 25 or 30 (1 or 0)*/ - ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_SET_FRAME_RATE, 1, - is_30fps ? 0 : 1); + ret = 0; - /* set encoding resolution */ - ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_SET_FRAME_SIZE, 2, - (height_full ? height : (height / 2)), - width); - /* set encoding aspect ratio to 4:3 */ - ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_SET_ASPECT_RATIO, 1, - 0x02); + if (!ret) ret = pvr2_encoder_vcmd( + hdw,CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, + 0xf0, 0xf0); - /* VBI */ + /* setup firmware to notify us about some events (don't know why...) */ + if (!ret) ret = pvr2_encoder_vcmd( + hdw,CX2341X_ENC_SET_EVENT_NOTIFICATION, 4, + 0, 0, 0x10000000, 0xffffffff); + + if (!ret) ret = pvr2_encoder_vcmd( + hdw,CX2341X_ENC_SET_VBI_LINE, 5, + 0xffffffff,0,0,0,0); + + if (ret) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Failed to configure cx32416"); + return ret; + } - if (hdw->config == pvr2_config_vbi) { - int lines = 2 * (is_30fps ? 12 : 18); - int size = (4*((lines*1443+3)/4)) / lines; - ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_SET_VBI_CONFIG, 7, - 0xbd05, 1, 4, - 0x25256262, 0x387f7f7f, - lines , size); -// 0x25256262, 0x13135454, lines , size); - /* select vbi lines */ -#define line_used(l) (is_30fps ? (l >= 10 && l <= 21) : (l >= 6 && l <= 23)) - for (i = 2 ; i <= 24 ; i++){ - ret |= pvr2_write_encoder_vcmd( - hdw,CX2341X_ENC_SET_VBI_LINE, 5, - i-1,line_used(i), 0, 0, 0); - ret |= pvr2_write_encoder_vcmd( - hdw,CX2341X_ENC_SET_VBI_LINE, 5, - (i-1) | (1 << 31), - line_used(i), 0, 0, 0); - } - } else { - ret |= pvr2_write_encoder_vcmd( - hdw,CX2341X_ENC_SET_VBI_LINE, 5, - 0xffffffff,0,0,0,0); + ret = cx2341x_update(hdw,pvr2_encoder_cmd, + (hdw->enc_cur_valid ? &hdw->enc_cur_state : 0), + &hdw->enc_ctl_state); + if (ret) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Error from cx2341x module code=%d",ret); + return ret; } - /* set stream type, depending on resolution. */ - ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_SET_STREAM_TYPE, 1, - height_full ? 0x0a : 0x0b); - /* set video bitrate */ - ret |= pvr2_write_encoder_vcmd( - hdw, CX2341X_ENC_SET_BIT_RATE, 3, - (hdw->vbr_val ? 1 : 0), - hdw->videobitrate_val, - hdw->videopeak_val / 400); - /* setup GOP structure (GOP size = 0f or 0c, 3-1 = 2 B-frames) */ - ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_SET_GOP_PROPERTIES, 2, - is_30fps ? 0x0f : 0x0c, 0x03); - - /* enable 3:2 pulldown */ - ret |= pvr2_write_encoder_vcmd(hdw,CX2341X_ENC_SET_3_2_PULLDOWN,1,0); - - /* set GOP open/close property (open) */ - ret |= pvr2_write_encoder_vcmd(hdw,CX2341X_ENC_SET_GOP_CLOSURE,1,0); - - /* set audio stream properties 0x40b9? 0100 0000 1011 1001 */ - audio = (pvr_tbl_audiobitrate[hdw->audiobitrate_val] | - pvr_tbl_srate[hdw->srate_val] | - hdw->audiolayer_val << 2 | - (hdw->audiocrc_val ? 1 << 14 : 0) | - pvr_tbl_emphasis[hdw->audioemphasis_val]); - - ret |= pvr2_write_encoder_vcmd(hdw,CX2341X_ENC_SET_AUDIO_PROPERTIES,1, - audio); - - /* set dynamic noise reduction filter to manual, Horiz/Vert */ - ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_SET_DNR_FILTER_MODE, 2, - 0, 0x03); - - /* dynamic noise reduction filter param */ - ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_SET_DNR_FILTER_PROPS, 2 - , 0, 0); - - /* dynamic noise reduction median filter */ - ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_SET_CORING_LEVELS, 4, - 0, 0xff, 0, 0xff); - - /* spacial prefiler parameter */ - ret |= pvr2_write_encoder_vcmd(hdw, - CX2341X_ENC_SET_SPATIAL_FILTER_TYPE, 2, - 0x01, 0x01); - - /* initialize video input */ - ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_INITIALIZE_INPUT, 0); - - if (!ret) { - hdw->subsys_enabled_mask |= (1<<PVR2_SUBSYS_B_ENC_CFG); + ret = 0; + + if (!ret) ret = pvr2_encoder_vcmd( + hdw, CX2341X_ENC_INITIALIZE_INPUT, 0); + + if (ret) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Failed to initialize cx32416 video input"); + return ret; } - return ret; + hdw->subsys_enabled_mask |= (1<<PVR2_SUBSYS_B_ENC_CFG); + memcpy(&hdw->enc_cur_state,&hdw->enc_ctl_state, + sizeof(struct cx2341x_mpeg_params)); + hdw->enc_cur_valid = !0; + return 0; } + int pvr2_encoder_start(struct pvr2_hdw *hdw) { int status; @@ -449,14 +394,14 @@ int pvr2_encoder_start(struct pvr2_hdw *hdw) pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000000); if (hdw->config == pvr2_config_vbi) { - status = pvr2_write_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2, - 0x01,0x14); + status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2, + 0x01,0x14); } else if (hdw->config == pvr2_config_mpeg) { - status = pvr2_write_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2, - 0,0x13); + status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2, + 0,0x13); } else { - status = pvr2_write_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2, - 0,0x13); + status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2, + 0,0x13); } if (!status) { hdw->subsys_enabled_mask |= (1<<PVR2_SUBSYS_B_ENC_RUN); @@ -472,14 +417,14 @@ int pvr2_encoder_stop(struct pvr2_hdw *hdw) pvr2_write_register(hdw, 0x0048, 0xffffffff); if (hdw->config == pvr2_config_vbi) { - status = pvr2_write_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3, - 0x01,0x01,0x14); + status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3, + 0x01,0x01,0x14); } else if (hdw->config == pvr2_config_mpeg) { - status = pvr2_write_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3, - 0x01,0,0x13); + status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3, + 0x01,0,0x13); } else { - status = pvr2_write_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3, - 0x01,0,0x13); + status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3, + 0x01,0,0x13); } /* change some GPIO data */ |