Index: ivtv/driver/ivtv-api.c diff -u ivtv/driver/ivtv-api.c:1.1 ivtv/driver/ivtv-api.c:1.4 --- ivtv/driver/ivtv-api.c:1.1 Sun Mar 7 11:26:54 2004 +++ ivtv/driver/ivtv-api.c Sun Mar 21 08:24:56 2004 @@ -31,6 +31,7 @@ static int ivtv_v4l2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); static int ivtv_v4l2_pre_init(struct ivtv *itv); +static void set_mode_change_callback(struct ivtv *itv, int enabled); struct file_operations ivtv_v4l2_fops = { read: ivtv_v4l2_read, @@ -466,6 +467,88 @@ u32 ivtv_audio_mask_generation = 0x10000; +struct v4l2_queryctrl ivtv_ctrl_menu_dyn = { + .id = V4L2_CID_IVTV_DYN, + .type = V4L2_CTRL_TYPE_MENU, + .name = "Dynamic Stereo/Mono Watch", + .minimum = 0, + .maximum = 2, + .step = 1, + .default_value = 0, + .flags = 0, + .reserved = {0,0} +}; + +struct v4l2_querymenu ivtv_ctrl_query_dyn[] = { + /* ID, Index, Name, Reserved */ + {V4L2_CID_IVTV_DYN, 0, "Disabled", 0}, + {V4L2_CID_IVTV_DYN, 1, "Always mono", 0}, + {V4L2_CID_IVTV_DYN, 2, "Stereo if available", 0} +}; + +u32 ivtv_audio_tbl_dyn[] = { + /* setting */ + IVTV_CODEC_AUDIO_DYNSTEREO_STATIC, + IVTV_CODEC_AUDIO_DYNSTEREO_MONO, + IVTV_CODEC_AUDIO_DYNSTEREO_STEREO +}; + +u32 ivtv_audio_mask_dyn = IVTV_CODEC_AUDIO_DYNSTEREO_ENABLED_MASK ; + +struct v4l2_queryctrl ivtv_ctrl_menu_dyn_joint = { + .id = V4L2_CID_IVTV_DYN_JOINT, + .type = V4L2_CTRL_TYPE_MENU, + .name = "Dynamic Stereo Mode", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0, + .reserved = {0,0} +}; + +struct v4l2_querymenu ivtv_ctrl_query_dyn_joint [] = { + /* ID, Index, Name, Reserved */ + {V4L2_CID_IVTV_DYN_JOINT, 0, "Split", 0}, + {V4L2_CID_IVTV_DYN_JOINT, 1, "Joint", 0} +}; + +u32 ivtv_audio_tbl_dyn_joint[] = { + /* setting */ + IVTV_CODEC_AUDIO_DYNSTEREO_SPLIT, + IVTV_CODEC_AUDIO_DYNSTEREO_JOINT +}; + +u32 ivtv_audio_mask_dyn_joint = IVTV_CODEC_AUDIO_DYNSTEREO_JOINT_MASK; + +struct v4l2_queryctrl ivtv_ctrl_menu_dyn_lang = { + .id = V4L2_CID_IVTV_DYN_LANG, + .type = V4L2_CTRL_TYPE_MENU, + .name = "Dynamic Language Selection", + .minimum = 0, + .maximum = 2, + .step = 1, + .default_value = 0, + .flags = 0, + .reserved = {0,0} +}; + +struct v4l2_querymenu ivtv_ctrl_query_dyn_lang [] = { + /* ID, Index, Name, Reserved */ + {V4L2_CID_IVTV_DYN_LANG, 0, "Language 1", 0}, + {V4L2_CID_IVTV_DYN_LANG, 2, "Language 2", 0}, + {V4L2_CID_IVTV_DYN_LANG, 3, "Bilingual (Dual)", 0} +}; + +u32 ivtv_audio_tbl_dyn_lang[] = { + /* setting */ + IVTV_CODEC_AUDIO_DYNSTEREO_LANG1, + IVTV_CODEC_AUDIO_DYNSTEREO_LANG2, + IVTV_CODEC_AUDIO_DYNSTEREO_DUAL +}; + +u32 ivtv_audio_mask_dyn_lang = IVTV_CODEC_AUDIO_DYNSTEREO_LANG_MASK; + /* 3 stream types: mpeg, yuv, passthru */ struct ivtv_v4l2_stream tmk_mpg_stream = { @@ -802,6 +885,212 @@ return retval; } +static int mode_change_callback(int oldstereo, int newstereo, void* userdata) { + /* + * called by msp3400.c when a stereo mode change has been detected, + * on msp3400's watchstereo thread, mostly + */ + struct ivtv* ivtv = (struct ivtv*)userdata; + int retval = oldstereo; // default 'static' behaviour (no change) + u32 audio_bitmap; + IVTV_DEBUG(IVTV_DEBUG_INFO, + "stereo mode_change_callback(%d, %d, 0x%lx) start.\n", + oldstereo, + newstereo, + (unsigned long)userdata); + + if(!ivtv) { // that's obviously wrong. + IVTV_DEBUG(IVTV_DEBUG_ERR, + "NULL userdata passed to mode_change_callback(%d, %d, NULL).\n", + oldstereo, + newstereo); + return retval; + } + + audio_bitmap = ivtv->v4l2.codec.audio_bitmap; + if( (audio_bitmap & IVTV_CODEC_AUDIO_DYNSTEREO_ENABLED_MASK) != 0 ) { + u32 data = ivtv->v4l2.codec.audio_bitmap; + const u32 old_mpeg_stereo = data&IVTV_CODEC_AUDIO_MPEG_STEREO_MASK; + u32 new_mpeg_stereo = old_mpeg_stereo; + + if( (newstereo&VIDEO_SOUND_LANG1) ) { /* bilingual audio mode detected */ + switch(audio_bitmap & IVTV_CODEC_AUDIO_DYNSTEREO_LANG_MASK) { + + case IVTV_CODEC_AUDIO_DYNSTEREO_LANG1: + new_mpeg_stereo=IVTV_CODEC_AUDIO_MPEG_STEREO_MONO; + retval=VIDEO_SOUND_LANG1; + break; + case IVTV_CODEC_AUDIO_DYNSTEREO_LANG2: + new_mpeg_stereo=IVTV_CODEC_AUDIO_MPEG_STEREO_MONO; + retval=VIDEO_SOUND_LANG2; + break; + case IVTV_CODEC_AUDIO_DYNSTEREO_DUAL: + new_mpeg_stereo=IVTV_CODEC_AUDIO_MPEG_STEREO_DUAL; + retval=VIDEO_SOUND_STEREO; + break; + default: + IVTV_DEBUG(IVTV_DEBUG_ERR, + "unknown audio_bitmap dynstereo lang value 0x%lx\n", + audio_bitmap & IVTV_CODEC_AUDIO_DYNSTEREO_LANG_MASK); + break; + } + } else if( (newstereo&VIDEO_SOUND_STEREO) ) { /* stereo audio mode detected */ + switch(audio_bitmap & IVTV_CODEC_AUDIO_DYNSTEREO_ENABLED_MASK) { + + case IVTV_CODEC_AUDIO_DYNSTEREO_STEREO: + if( (audio_bitmap & IVTV_CODEC_AUDIO_DYNSTEREO_JOINT) ) { + new_mpeg_stereo=IVTV_CODEC_AUDIO_MPEG_STEREO_JOINT; + } else { + new_mpeg_stereo=IVTV_CODEC_AUDIO_MPEG_STEREO_STEREO; + } + retval=VIDEO_SOUND_STEREO; + break; + case IVTV_CODEC_AUDIO_DYNSTEREO_MONO: + new_mpeg_stereo=IVTV_CODEC_AUDIO_MPEG_STEREO_MONO; + retval=VIDEO_SOUND_MONO; + break; + default: + IVTV_DEBUG(IVTV_DEBUG_ERR, + "unknown audio_bitmap dynstereo enabled value 0x%lx\n", + audio_bitmap & IVTV_CODEC_AUDIO_DYNSTEREO_ENABLED_MASK); + break; + } + } else { /* mono audio mode detected */ + new_mpeg_stereo=IVTV_CODEC_AUDIO_MPEG_STEREO_MONO; + retval=VIDEO_SOUND_MONO; + } + + if( new_mpeg_stereo != old_mpeg_stereo ) { + u32 newdata[IVTV_MBOX_MAX_DATA]; + u32 result; + const u32 new_audio_bitmap = ( data & ( ~ IVTV_CODEC_AUDIO_MPEG_STEREO_MASK ) ) | new_mpeg_stereo; + IVTV_DEBUG(IVTV_DEBUG_INFO, + "set audio mode from 0x%lx to 0x%lx\n", + data, + newdata[0]); + + newdata[0]=new_audio_bitmap & 0xffff; + int x = ivtv_api(ivtv->enc_mbox, + &ivtv->enc_msem, + IVTV_API_ASSIGN_AUDIO_PROPERTIES, + &result, + 1, + &newdata[0]); + if (x) { + IVTV_DEBUG(IVTV_DEBUG_ERR, "IVTV_API_ASSIGN_AUDIO_PROPERTIES error. Code %d\n",x); + } else { + ivtv->v4l2.codec.audio_bitmap=new_audio_bitmap; + } + } + } + IVTV_DEBUG(IVTV_DEBUG_INFO, + "mode_change_callback(%d, %d, 0x%lx) -> %d\n", + oldstereo, + newstereo, + (unsigned long)userdata, + retval); + return retval; +} +/* called when stereo mode configuration was changed and + * the callback needs to be called again to update + * stereo settings. + */ +static int update_stereo_mode(struct ivtv *ivtv) { + /* this function is complicated just because + * values must be converted from v4l1 into v4l2 + * and back... the worst is that msp3400.c will + * do the same conversions, but backwards. yet, + * I have to use the v4l2 calls for that because + * VIDIOCGAUDIO will not give me all the information + * I need. + */ + struct v4l2_tuner vt; + memset(&vt, 0, sizeof(struct v4l2_tuner)); + ivtv_call_i2c_client(ivtv,IVTV_MSP3400_I2C_ADDR,VIDIOC_G_TUNER, &vt); + u32 subchans = vt.rxsubchans; + int mode=0; + int oldmode=0; + int newmode; + // v4l2 subchans -> v4l1 detected mode + if(subchans&V4L2_TUNER_SUB_MONO) + mode|=VIDEO_SOUND_MONO; + if(subchans&V4L2_TUNER_SUB_STEREO) + mode|=VIDEO_SOUND_STEREO; + if(subchans&V4L2_TUNER_SUB_LANG1) + mode|=VIDEO_SOUND_LANG1; + if(subchans&V4L2_TUNER_SUB_LANG2) + mode|=VIDEO_SOUND_LANG2; + // v4l2 audiomode -> v4l1 current mode + switch(vt.audmode) { + case V4L2_TUNER_MODE_LANG1: + oldmode=VIDEO_SOUND_LANG1; + break; + case V4L2_TUNER_MODE_LANG2: + oldmode=VIDEO_SOUND_LANG2; + break; + case V4L2_TUNER_MODE_STEREO: + oldmode=VIDEO_SOUND_STEREO; + break; + case V4L2_TUNER_MODE_MONO: + default: + oldmode=VIDEO_SOUND_MONO; + break; + } + newmode=mode_change_callback(oldmode, mode, ivtv); + printk("update_stereo_mode: mode_change_callback(%d, %d, 0x%lx) -> %d\n", + oldmode, + mode, + ivtv, + newmode); + if(newmode!=oldmode) { + // v4l1 newmode -> v4l2 audiomode + u32 audmode; + if(newmode&VIDEO_SOUND_LANG1) { + audmode=V4L2_TUNER_MODE_LANG1; + } else if(newmode&VIDEO_SOUND_LANG2) { + audmode=V4L2_TUNER_MODE_LANG2; + } else if(newmode&VIDEO_SOUND_STEREO) { + audmode=V4L2_TUNER_MODE_STEREO; + } else { + audmode=V4L2_TUNER_MODE_MONO; + } + if(audmode!=vt.audmode) { + vt.audmode=audmode; + ivtv_call_i2c_client(ivtv,IVTV_MSP3400_I2C_ADDR,VIDIOC_S_TUNER, &vt); + return 1; + } + } + return 0; +} + +/* add the value in ivtv_dynstereo to audio_bitmap if + * the higher bits are null (it's just a temporary hack, + * I hope) + */ +static void set_default_ivtv_dynstereo(struct ivtv* ivtv) { + if( ! (ivtv->v4l2.codec.audio_bitmap&IVTV_CODEC_AUDIO_DYNSTEREO_MASK) ) { + ivtv->v4l2.codec.audio_bitmap|=(ivtv_dynstereo&0xffff0000); + } +} + + +static void set_mode_change_callback(struct ivtv* ivtv, int enabled) { + if(enabled) { + struct msp_stereo_event ev; + ev.callback = mode_change_callback; + ev.userdata = ivtv; + ivtv_call_i2c_client(ivtv, + IVTV_MSP3400_I2C_ADDR, + MSP_STEREO_EVENT, + &ev); + } else { + ivtv_call_i2c_client(ivtv, + IVTV_MSP3400_I2C_ADDR, + MSP_STEREO_EVENT, + NULL); + } +} + /* After setting the audio.active param, call this to * get the right input.. think of it as a resolver */ int ivtv_set_audio(struct ivtv *itv, int *map) { @@ -1002,12 +1291,34 @@ itv->v4l2.audio_meta[8].setting = ivtv_ctrl_menu_generation.default_value; itv->v4l2.audio_meta[8].table = &ivtv_audio_tbl_generation[0]; + /* V4L2_CID_IVTV_DYN*/ + itv->v4l2.audio_meta[9].ctrl = &ivtv_ctrl_menu_dyn; + itv->v4l2.audio_meta[9].menu = ivtv_ctrl_query_dyn; + itv->v4l2.audio_meta[9].mask = ivtv_audio_mask_dyn; + itv->v4l2.audio_meta[9].setting = ivtv_ctrl_menu_dyn.default_value; + itv->v4l2.audio_meta[9].table = &ivtv_audio_tbl_dyn[0]; + + /* V4L2_CID_IVTV_DYN_JOINT*/ + itv->v4l2.audio_meta[10].ctrl = &ivtv_ctrl_menu_dyn_joint; + itv->v4l2.audio_meta[10].menu = ivtv_ctrl_query_dyn_joint; + itv->v4l2.audio_meta[10].mask = ivtv_audio_mask_dyn_joint; + itv->v4l2.audio_meta[10].setting = ivtv_ctrl_menu_dyn_joint.default_value; + itv->v4l2.audio_meta[10].table = &ivtv_audio_tbl_dyn_joint[0]; + + /* V4L2_CID_IVTV_DYN_LANG*/ + itv->v4l2.audio_meta[11].ctrl = &ivtv_ctrl_menu_dyn_lang; + itv->v4l2.audio_meta[11].menu = ivtv_ctrl_query_dyn_lang; + itv->v4l2.audio_meta[11].mask = ivtv_audio_mask_dyn_lang; + itv->v4l2.audio_meta[11].setting = ivtv_ctrl_menu_dyn_lang.default_value; + itv->v4l2.audio_meta[11].table = &ivtv_audio_tbl_dyn_lang[0]; + itv->v4l2.codec.audio_bitmap = 0; for (x = 0; x < IVTV_V4L2_AUDIO_MENUCOUNT; x++) { temp = itv->v4l2.audio_meta[x].setting; itv->v4l2.codec.audio_bitmap |= itv->v4l2.audio_meta[x].table[temp]; } + set_default_ivtv_dynstereo(itv); retval = ivtv_set_audio(itv,tmk_audio_mapping); if (retval) { @@ -1015,6 +1326,8 @@ return retval; } + set_mode_change_callback(itv, 1/*enable*/); + //FIXME Setup components here? tuner channel etc return 0; } @@ -1140,11 +1453,17 @@ if (x) IVTV_DEBUG(IVTV_DEBUG_ERR, "init error 11. Code %d\n",x); /*assign audio properties */ - data[0] = itv->v4l2.codec.audio_bitmap; + data[0] = itv->v4l2.codec.audio_bitmap & 0xffff; x = ivtv_api(itv->enc_mbox, &itv->enc_msem, IVTV_API_ASSIGN_AUDIO_PROPERTIES, - &result, 1, &data[0]); - if (x) IVTV_DEBUG(IVTV_DEBUG_ERR, "init error 12. Code %d\n",x); - + &result, 1, &data[0]); + if (x) IVTV_DEBUG(IVTV_DEBUG_ERR, "init error 12. Code %d\n",x); + if( (itv->v4l2.codec.audio_bitmap & IVTV_CODEC_AUDIO_DYNSTEREO_ENABLED_MASK) ) { + /* update the stereo part of this property using the msp3400, + * through the callback, if necessary + */ + update_stereo_mode(itv); + } + /*assign dnr filter mode */ data[0] = itv->v4l2.codec.dnr_mode; data[1] = itv->v4l2.codec.dnr_type; @@ -1412,7 +1731,7 @@ } else { data[2] = 480; /* YUV source height*/ } - data[3] = itv->v4l2.codec.audio_bitmap; /* Audio settings to use, + data[3] = itv->v4l2.codec.audio_bitmap & 0xffff; /* Audio settings to use, bitmap. see docs.*/ x = ivtv_api(itv->dec_mbox, &itv->dec_msem, @@ -1480,6 +1799,7 @@ int x; IVTV_DEBUG(IVTV_DEBUG_INFO,"v4l2 unregister\n"); + set_mode_change_callback(itv, 0/*disable*/); if (atomic_read(&itv->capturing) >= 0) ivtv_stop_all_captures(itv); if (itv->v4l2.tuner.table.tuner) kfree(itv->v4l2.tuner.table.tuner); @@ -2232,6 +2552,8 @@ int off = vctrl->id - V4L2_CID_PRIVATE_BASE; s32 v = vctrl->value; if (off < IVTV_V4L2_AUDIO_MENUCOUNT) { + u32 audio_bitmap_dyn = itv->v4l2.codec.audio_bitmap&IVTV_CODEC_AUDIO_DYNSTEREO_MASK; + if ((v<=itv->v4l2.audio_meta[off].ctrl->maximum) && (v>=itv->v4l2.audio_meta[off].ctrl->minimum)) { itv->v4l2.audio_meta[off].setting = v; @@ -2254,6 +2576,10 @@ "ctrl: value out of range\n"); return -ERANGE; } + set_default_ivtv_dynstereo(itv); + if( audio_bitmap_dyn != itv->v4l2.codec.audio_bitmap&IVTV_CODEC_AUDIO_DYNSTEREO_MASK) { + update_stereo_mode(itv); + } } else { switch (vctrl->id) { case V4L2_CID_IVTV_DEC_SMOOTH_FF: @@ -2605,6 +2931,7 @@ //FIXME copy_from_user needed struct v4l2_frequency *vf=(struct v4l2_frequency *)arg; struct msp_matrix mspm; + unsigned long freq=0; IVTV_DEBUG(IVTV_DEBUG_INFO,"v4l2 ioctl: set frequency\n"); @@ -2632,7 +2959,7 @@ /* Unmute */ ivtv_set_audio(itv,tmk_audio_mapping); - + ivtv_call_i2c_client(itv,IVTV_MSP3400_I2C_ADDR, VIDIOCSFREQ,&freq); break; } case VIDIOC_ENUMSTD: { @@ -2715,15 +3042,15 @@ case VIDIOC_S_TUNER: { /* Setting tuner can only set audio mode */ //FIXME copy_from_user needed struct v4l2_tuner *vt=(struct v4l2_tuner *)arg; + struct video_audio va; IVTV_DEBUG(IVTV_DEBUG_INFO,"v4l2 ioctl: set tuner\n"); if ((vt->index < 0) || (vt->index >= itv->v4l2.tuner.count)) return -EINVAL; - /* looks like tuner.c doesn't support selection - * fallback to stereo... */ - vt->audmode = V4L2_TUNER_MODE_STEREO; + /* Regarding audio, the demodulator is our tuner */ + ivtv_call_i2c_client(itv,IVTV_MSP3400_I2C_ADDR,VIDIOC_S_TUNER, vt); break; } case VIDIOC_G_TUNER: { @@ -2748,6 +3075,9 @@ } else { vt->signal = 0; } + + /* ask msp3400 to set audmode and rxsubchans */ + ivtv_call_i2c_client(itv,IVTV_MSP3400_I2C_ADDR,VIDIOC_G_TUNER, vt); break; } case MSP_SET_MATRIX: { @@ -2795,8 +3125,13 @@ return -EINVAL; } else { /* Passed the garbage check */ + u32 audio_bitmap_dyn = itv->v4l2.codec.audio_bitmap&IVTV_CODEC_AUDIO_DYNSTEREO_MASK; memcpy(&(itv->v4l2.codec), codec, sizeof(struct ivtv_ioctl_codec)); + set_default_ivtv_dynstereo(itv); + if( audio_bitmap_dyn != (codec->audio_bitmap&IVTV_CODEC_AUDIO_DYNSTEREO_MASK)) { + update_stereo_mode(itv); + } } /* VCD streamtype has some quirks. Handle them here */ Index: ivtv/driver/ivtv-driver.c diff -u ivtv/driver/ivtv-driver.c:1.2 ivtv/driver/ivtv-driver.c:1.3 --- ivtv/driver/ivtv-driver.c:1.2 Sun Mar 7 11:41:14 2004 +++ ivtv/driver/ivtv-driver.c Sun Mar 21 08:24:56 2004 @@ -72,6 +72,7 @@ #endif int ivtv_pal = 0; +long ivtv_dynstereo = 0x00000000; /* low debugging by default */ @@ -164,6 +165,12 @@ MODULE_PARM(ivtv_pal, "i"); MODULE_PARM_DESC(ivtv_pal, "\nUse PAL as default video mode instead of NTSC"); +MODULE_PARM(ivtv_dynstereo, "l"); +MODULE_PARM_DESC(ivtv_dynstereo, + "\nForce dynamic stereo configuration. This sets the default for\n" + "the first 2 bytes of the audio_bitmap field. See, in ivtv.h,\n" + "the IVTV_CODEC_AUDIO_DYNSTEREO_ flags."); + MODULE_AUTHOR("Kevin Thayer"); MODULE_DESCRIPTION("Alpha iTVC15 driver"); MODULE_SUPPORTED_DEVICE("iTVC15/16 mpg2 encoder (aka WinTV PVR 250/350)"); Index: ivtv/driver/ivtv.h diff -u ivtv/driver/ivtv.h:1.2 ivtv/driver/ivtv.h:1.4 --- ivtv/driver/ivtv.h:1.2 Sun Mar 7 11:41:14 2004 +++ ivtv/driver/ivtv.h Sun Mar 21 08:24:56 2004 @@ -439,8 +439,11 @@ #define V4L2_CID_IVTV_CRC (V4L2_CID_PRIVATE_BASE + 6) #define V4L2_CID_IVTV_COPYRIGHT (V4L2_CID_PRIVATE_BASE + 7) #define V4L2_CID_IVTV_GEN (V4L2_CID_PRIVATE_BASE + 8) +#define V4L2_CID_IVTV_DYN (V4L2_CID_PRIVATE_BASE + 9) +#define V4L2_CID_IVTV_DYN_JOINT (V4L2_CID_PRIVATE_BASE + 10) +#define V4L2_CID_IVTV_DYN_LANG (V4L2_CID_PRIVATE_BASE + 11) -#define IVTV_V4L2_AUDIO_MENUCOUNT 9 /* # of v4l controls */ +#define IVTV_V4L2_AUDIO_MENUCOUNT 12 /* # of v4l controls */ #define IVTV_DEC_PRIVATE_BASE (V4L2_CID_PRIVATE_BASE + IVTV_V4L2_AUDIO_MENUCOUNT) @@ -527,7 +530,7 @@ /* For use with IVTV_IOC_G_CODEC and IVTV_IOC_S_CODEC */ struct ivtv_ioctl_codec { u32 aspect; - u32 audio_bitmap; + u32 audio_bitmap; /* see IVTV_CODEC_AUDIO */ u32 bframes; u32 bitrate_mode; u32 bitrate; @@ -543,8 +546,35 @@ u32 stream_type; }; +/* audio_bitmap: + * bits 0-15 follow iso11172, mostly, see http://ivtv.sourceforge.net/firmware-api.html. + * bits 16-32 are ivtv driver extensions as defined below: + */ +#define IVTV_CODEC_AUDIO_MPEG_STEREO_MASK (0x03 << 8) +#define IVTV_CODEC_AUDIO_MPEG_STEREO_STEREO (0x00 << 8) +#define IVTV_CODEC_AUDIO_MPEG_STEREO_JOINT (0x01 << 8) +#define IVTV_CODEC_AUDIO_MPEG_STEREO_DUAL (0x02 << 8) +#define IVTV_CODEC_AUDIO_MPEG_STEREO_MONO (0x03 << 8) + +#define IVTV_CODEC_AUDIO_DYNSTEREO_STATIC (0x00 << 16) /* compatibility mode, strictly follow audio mode in bits 9:8 */ +#define IVTV_CODEC_AUDIO_DYNSTEREO_MONO (0x01 << 16) /* always force mono mode 0x00010000 */ +#define IVTV_CODEC_AUDIO_DYNSTEREO_STEREO (0x02 << 16) /* use stereo mode if available 0x00020000 */ +#define IVTV_CODEC_AUDIO_DYNSTEREO_ENABLED_MASK (0x03 << 16) /* mask for this setting (static/mono/stereo) 0x00030000*/ + +#define IVTV_CODEC_AUDIO_DYNSTEREO_SPLIT (0x0 << 17) /* when encoding in stereo, use split mode */ +#define IVTV_CODEC_AUDIO_DYNSTEREO_JOINT (0x1 << 17) /* when encoding in stereo, use JOINT mode 0x00040000*/ +#define IVTV_CODEC_AUDIO_DYNSTEREO_JOINT_MASK (0x1 << 17) /* mask for this setting (split/joint) */ + +#define IVTV_CODEC_AUDIO_DYNSTEREO_LANG1 (0x00 << 19) /* record only lang1 of bilingual programs */ +#define IVTV_CODEC_AUDIO_DYNSTEREO_LANG2 (0x01 << 19) /* record only lang2 of bilingual programs 0x00080000*/ +#define IVTV_CODEC_AUDIO_DYNSTEREO_DUAL (0x02 << 19) /* record both lang1 and lang2 of bilingual programs 0x00100000 */ +#define IVTV_CODEC_AUDIO_DYNSTEREO_LANG_MASK (0x03 << 19) /* mask for this setting (lang1/lang2/dual) 0x00180000 */ + +#define IVTV_CODEC_AUDIO_DYNSTEREO_MASK (0x1f << 16) + extern int ivtv_debug; extern int ivtv_pal; +extern long ivtv_dynstereo; /* Scatter-Gather array element, used in DMA transfers */ struct ivtv_SG_element { Index: ivtv/driver/msp3400.c diff -u ivtv/driver/msp3400.c:1.1 ivtv/driver/msp3400.c:1.5 --- ivtv/driver/msp3400.c:1.1 Sun Mar 7 11:26:54 2004 +++ ivtv/driver/msp3400.c Sun Mar 21 10:09:28 2004 @@ -44,6 +44,7 @@ #include#include #include +#include #include #include @@ -84,7 +85,9 @@ int nicam; int mode; int norm; - int stereo; + int stereo; /* current stereo mode, set by msp3400c_setstereo() */ + int detected_stereo; /* detected stereo mode, set by autodetect_stereo */ + struct msp_stereo_event stereo_event; int nicam_on; int acb; int main, second; /* sound carrier */ @@ -101,7 +104,7 @@ struct task_struct *thread; wait_queue_head_t wq; - struct semaphore *notify; + struct semaphore *notify, *ack; int active,restart,rmmod; int watch_stereo; @@ -411,6 +414,7 @@ dprintk("msp3400: setmode: %d\n",type); msp->mode = type; msp->stereo = VIDEO_SOUND_MONO; + msp->detected_stereo = VIDEO_SOUND_MONO; msp3400c_write(client,I2C_MSP3400C_DEM, 0x00bb, /* ad_cv */ msp_init_data[type].ad_cv); @@ -482,6 +486,11 @@ int nicam=0; /* channel source: FM/AM or nicam */ int src=0; + if( (mode&(VIDEO_SOUND_LANG1|VIDEO_SOUND_LANG2)) == (VIDEO_SOUND_LANG1|VIDEO_SOUND_LANG2) ) { + mode=VIDEO_SOUND_LANG1; + } + msp->stereo=mode; + /* switch demodulator */ switch (msp->mode) { case MSP_MODE_FM_TERRA: @@ -636,7 +645,7 @@ { struct msp3400c *msp = client->data; int val; - int newstereo = msp->stereo; + int newstereo = msp->detected_stereo; int newnicam = msp->nicam_on; int update = 0; @@ -704,12 +713,16 @@ if (val & 0x0100) newstereo |= VIDEO_SOUND_LANG1; break; } - if (newstereo != msp->stereo) { - update = 1; - dprintk("msp34xx: watch: stereo %d => %d\n", - msp->stereo,newstereo); - msp->stereo = newstereo; - } + dprintk("msp34xx: watch: detected_stereo=%d, newstereo=%d\n", + msp->detected_stereo, + newstereo); + if(msp->detected_stereo!=newstereo) { + dprintk("msp34xx: watch: stereo %d => %d\n", + msp->detected_stereo, + newstereo); + msp->detected_stereo=newstereo; + update = 1; + } if (newnicam != msp->nicam_on) { update = 1; dprintk("msp34xx: watch: nicam %d => %d\n", @@ -731,18 +744,29 @@ wake_up_interruptible(&msp->wq); } +static void detected_stereo_changed(struct i2c_client *client) +{ + struct msp3400c *msp = client->data; + int newstereo=msp->detected_stereo; + if(msp->stereo_event.callback) { + dprintk("msp34xx: calling callback oldstereo=%d, newstereo=%d \n", + msp->stereo, + newstereo); + newstereo=msp->stereo_event.callback(msp->stereo, newstereo, msp->stereo_event.userdata); + dprintk("msp34xx: callback finished stereo=%d\n", + msp->stereo); + } + msp3400c_setstereo(client, newstereo); +} + + /* stereo/multilang monitoring */ static void watch_stereo(struct i2c_client *client) { struct msp3400c *msp = client->data; if (autodetect_stereo(client)) { - if (msp->stereo & VIDEO_SOUND_STEREO) - msp3400c_setstereo(client,VIDEO_SOUND_STEREO); - else if (msp->stereo & VIDEO_SOUND_LANG1) - msp3400c_setstereo(client,VIDEO_SOUND_LANG1); - else - msp3400c_setstereo(client,VIDEO_SOUND_MONO); + detected_stereo_changed(client); } if (once) msp->watch_stereo = 0; @@ -778,6 +802,10 @@ goto done; if (debug > 1) printk("msp3400: thread: sleep\n"); + if(msp->ack != NULL) { + up(msp->ack); + msp->ack=NULL; + } interruptible_sleep_on(&msp->wq); if (debug > 1) printk("msp3400: thread: wakeup\n"); @@ -888,8 +916,9 @@ msp->second = carrier_detect_55[max2].cdo; msp3400c_setmode(client, MSP_MODE_FM_TERRA); msp->nicam_on = 0; - msp3400c_setstereo(client, VIDEO_SOUND_MONO); - msp->watch_stereo = 1; + msp->detected_stereo = VIDEO_SOUND_MONO; + msp3400c_setstereo(client, VIDEO_SOUND_MONO); + msp->watch_stereo = 1; } else if (max2 == 1 && msp->nicam) { /* B/G NICAM */ msp->second = carrier_detect_55[max2].cdo; @@ -915,14 +944,16 @@ msp->second = carrier_detect_65[max2].cdo; msp3400c_setmode(client, MSP_MODE_FM_TERRA); msp->nicam_on = 0; + msp->detected_stereo = VIDEO_SOUND_MONO; msp3400c_setstereo(client, VIDEO_SOUND_MONO); - msp->watch_stereo = 1; + msp->watch_stereo = 1; } else if (max2 == 0 && msp->norm == VIDEO_MODE_SECAM) { /* L NICAM or AM-mono */ msp->second = carrier_detect_65[max2].cdo; msp3400c_setmode(client, MSP_MODE_AM_NICAM); msp->nicam_on = 0; + msp->detected_stereo = VIDEO_SOUND_MONO; msp3400c_setstereo(client, VIDEO_SOUND_MONO); msp3400c_setcarrier(client, msp->second, msp->main); /* volume prescale for SCART (AM mono input) */ @@ -946,7 +977,7 @@ msp3400c_setmode(client, MSP_MODE_FM_TERRA); msp->nicam_on = 0; msp3400c_setcarrier(client, msp->second, msp->main); - msp->stereo = VIDEO_SOUND_MONO; + msp->detected_stereo = VIDEO_SOUND_MONO; msp3400c_setstereo(client, VIDEO_SOUND_MONO); break; } @@ -1032,6 +1063,10 @@ goto done; if (debug > 1) printk("msp3410: thread: sleep\n"); + if(msp->ack!=NULL) { + up(msp->ack); + msp->ack=NULL; + } interruptible_sleep_on(&msp->wq); if (debug > 1) printk("msp3410: thread: wakeup\n"); @@ -1155,29 +1190,29 @@ else msp->mode = MSP_MODE_FM_NICAM2; /* just turn on stereo */ - msp->stereo = VIDEO_SOUND_STEREO; + msp->detected_stereo = VIDEO_SOUND_STEREO; msp->nicam_on = 1; - msp->watch_stereo = 1; msp3400c_setstereo(client,VIDEO_SOUND_STEREO); + msp->watch_stereo = 1; break; case 0x0009: msp->mode = MSP_MODE_AM_NICAM; - msp->stereo = VIDEO_SOUND_MONO; msp->nicam_on = 1; - msp3400c_setstereo(client,VIDEO_SOUND_MONO); - msp->watch_stereo = 1; + msp->detected_stereo = VIDEO_SOUND_MONO; + msp3400c_setstereo(client, VIDEO_SOUND_MONO); + msp->watch_stereo = 1; break; case 0x0020: /* BTSC */ /* just turn on stereo */ msp->mode = MSP_MODE_BTSC; - msp->stereo = VIDEO_SOUND_STEREO; + msp->detected_stereo = VIDEO_SOUND_STEREO; msp->nicam_on = 0; msp->watch_stereo = 1; msp3400c_setstereo(client,VIDEO_SOUND_STEREO); break; case 0x0040: /* FM radio */ msp->mode = MSP_MODE_FM_RADIO; - msp->stereo = VIDEO_SOUND_STEREO; + msp->detected_stereo = VIDEO_SOUND_STEREO; msp->nicam_on = 0; msp->watch_stereo = 0; /* scart routing */ @@ -1188,7 +1223,7 @@ break; case 0x0003: msp->mode = MSP_MODE_FM_TERRA; - msp->stereo = VIDEO_SOUND_MONO; + msp->detected_stereo = VIDEO_SOUND_MONO; msp->nicam_on = 0; msp->watch_stereo = 1; break; @@ -1358,7 +1393,8 @@ int i; /* shutdown control thread */ - del_timer(&msp->wake_stereo); + msp->watch_stereo=0; // make sure the timer is not re-added by the thread + del_timer_sync(&msp->wake_stereo); if (msp->thread) { msp->notify = &sem; @@ -1367,6 +1403,7 @@ down(&sem); msp->notify = NULL; } + del_timer_sync(&msp->wake_stereo); // to be really sure the timer is deleted msp3400c_reset(client); /* update our own array */ @@ -1424,17 +1461,17 @@ switch (*sarg) { case AUDIO_RADIO: msp->mode = MSP_MODE_FM_RADIO; - msp->stereo = VIDEO_SOUND_STEREO; + msp->detected_stereo = VIDEO_SOUND_STEREO; msp3400c_set_scart(client,SCART_IN2,0); msp3400c_write(client,I2C_MSP3400C_DFP,0x000d,0x1900); - msp3400c_setstereo(client,msp->stereo); + msp3400c_setstereo(client,VIDEO_SOUND_STEREO); break; case AUDIO_EXTERN: msp->mode = MSP_MODE_EXTERN; - msp->stereo = VIDEO_SOUND_STEREO; + msp->detected_stereo = VIDEO_SOUND_STEREO; msp3400c_set_scart(client,SCART_IN1,0); msp3400c_write(client,I2C_MSP3400C_DFP,0x000d,0x1900); - msp3400c_setstereo(client,msp->stereo); + msp3400c_setstereo(client,VIDEO_SOUND_STEREO); break; case AUDIO_TUNER: msp->mode = -1; @@ -1520,7 +1557,9 @@ va->treble = msp->treble; if (msp->norm != VIDEO_MODE_RADIO) { - autodetect_stereo(client); + /* alternative: autodetect_stereo(client); va->mode = msp->detected_stereo + * returning the *current* stereo mode makes more sense, though. + */ va->mode = msp->stereo; } break; @@ -1542,13 +1581,67 @@ msp3400c_settreble(client,msp->treble); if (va->mode != 0) { - msp->watch_stereo=0; - del_timer(&msp->wake_stereo); - msp->stereo = va->mode; msp3400c_setstereo(client,va->mode); } break; } + case VIDIOC_G_TUNER: + { + struct v4l2_tuner *vt=(struct v4l2_tuner *)arg; + /* only set rxsubchans and audmode */ + + u32 rxsubchans=0; + u32 audmode; + + autodetect_stereo(client); + if(msp->detected_stereo&VIDEO_SOUND_STEREO) + rxsubchans |= V4L2_TUNER_SUB_STEREO; + if(msp->detected_stereo&VIDEO_SOUND_LANG1) + rxsubchans |= V4L2_TUNER_SUB_LANG1; + if(msp->detected_stereo&VIDEO_SOUND_LANG2) + rxsubchans |= V4L2_TUNER_SUB_LANG2; + if(msp->detected_stereo&VIDEO_SOUND_MONO) + rxsubchans |= V4L2_TUNER_SUB_MONO; + + if(msp->stereo&VIDEO_SOUND_LANG2) + audmode = V4L2_TUNER_MODE_LANG2; + else if(msp->stereo&VIDEO_SOUND_LANG1) + audmode = V4L2_TUNER_MODE_LANG1; + else if(msp->stereo&VIDEO_SOUND_STEREO) + audmode = V4L2_TUNER_MODE_STEREO; + else + audmode = V4L2_TUNER_MODE_MONO; + + vt->rxsubchans = rxsubchans; + vt->audmode = audmode; + break; + } + case VIDIOC_S_TUNER: + { + struct v4l2_tuner *vt=(struct v4l2_tuner *)arg; + /* set mode based on audmode, ignore the rest */ + int newstereo=0; + switch(vt->audmode) { + case V4L2_TUNER_MODE_MONO: + newstereo=VIDEO_SOUND_MONO; + break; + + case V4L2_TUNER_MODE_STEREO: + newstereo=VIDEO_SOUND_STEREO; + break; + + case V4L2_TUNER_MODE_LANG1: + newstereo=VIDEO_SOUND_LANG1; + break; + + case V4L2_TUNER_MODE_LANG2: + newstereo=VIDEO_SOUND_LANG2; + break; + } + msp3400c_setstereo(client, newstereo); + break; + } + case VIDIOCSCHAN: { struct video_channel *vc = arg; @@ -1556,6 +1649,8 @@ dprintk(KERN_DEBUG "msp34xx: VIDIOCSCHAN\n"); dprintk("msp34xx: switching to TV mode\n"); msp->norm = vc->norm; + msp->detected_stereo = VIDEO_SOUND_MONO; + detected_stereo_changed(client); msp_wake_thread(client); break; } @@ -1563,6 +1658,8 @@ { /* new channel -- kick audio carrier scan */ dprintk(KERN_DEBUG "msp34xx: VIDIOCSFREQ\n"); + msp->detected_stereo = VIDEO_SOUND_MONO; + detected_stereo_changed(client); msp_wake_thread(client); break; } @@ -1574,6 +1671,34 @@ msp3400c_set_scart(client, mspm->input, mspm->output); break; } + case MSP_STEREO_EVENT: + { + struct msp_stereo_event *ev = arg; + dprintk(KERN_DEBUG "msp34xx: MSP_STEREO_EVENT\n"); + if(ev) { + dprintk(KERN_DEBUG + "msp34xx: set stereo mode change callback to 0x%lx.\n", + ev->callback); + memcpy(&msp->stereo_event, ev, sizeof(struct msp_stereo_event)); + } else { + dprintk(KERN_DEBUG + "msp34xx: unset stereo mode change callback.\n"); + memset(&msp->stereo_event, 0, sizeof(struct msp_stereo_event)); + } + if(msp->thread) { + /* make sure the previous callback has finished executing + * before returning + */ + struct semaphore ack_semaphore; + init_MUTEX_LOCKED(&ack_semaphore); + msp->ack=&ack_semaphore; + wake_up_interruptible(&msp->wq); + down(&ack_semaphore); + msp->ack=NULL; /* the thread does that, too, but I'm paranoid. */ + } + break; + } + default: /* nothing */ break; Index: ivtv/driver/msp3400.h diff -u ivtv/driver/msp3400.h:1.1 ivtv/driver/msp3400.h:1.2 --- ivtv/driver/msp3400.h:1.1 Sun Mar 7 11:26:54 2004 +++ ivtv/driver/msp3400.h Sat Mar 20 19:12:28 2004 @@ -13,11 +13,18 @@ int output; }; +typedef int (*t_msp_stereo_cb)(int old_stereo_mode, int new_stereo_mode, void *userdata); +struct msp_stereo_event { + void *userdata; + t_msp_stereo_cb callback; +}; + #define MSP_SET_DFPREG _IOW('m',15,struct msp_dfpreg) #define MSP_GET_DFPREG _IOW('m',16,struct msp_dfpreg) /* ioctl for MSP_SET_MATRIX will have to be registered */ #define MSP_SET_MATRIX _IOW('m',17,struct msp_matrix) +#define MSP_STEREO_EVENT _IOW('m',18,struct msp_stereo_event) #define SCART_MASK 0 #define SCART_IN1 1 Index: ivtv/utils/test_ioctl.c diff -u ivtv/utils/test_ioctl.c:1.1 ivtv/utils/test_ioctl.c:1.4 --- ivtv/utils/test_ioctl.c:1.1 Sun Mar 7 11:26:54 2004 +++ ivtv/utils/test_ioctl.c Sat Mar 20 19:12:28 2004 @@ -40,6 +40,8 @@ #define OptFrameSync (1L<<18) #define OptSetDebugLevel (1L<<19) #define OptGetDebugLevel (1L<<20) +#define OptSetAudioMode (1L<<21) +#define OptGetAudioMode (1L<<22) /* Codec's specified */ #define CAspect (1L<<1) @@ -124,6 +126,8 @@ printf(" contrast =<#> Picture contrast or luma gain. [0 - 127]\n"); printf(" volume =<#> Overall audio volume. [0 - 65535]\n"); printf(" mute =<#> Mute audio, i. e. set the volume to zero [boolean]\n"); + printf(" -z, --set-audiomode= Possible values: 0(mono),1(stereo),2(lang2),3(lang1)\n"); + printf(" -Z, --get-audiomode\n"); printf(" -D --debug=level sets/gets the module ivtv_debug variable\n"); exit(0); } @@ -318,6 +322,7 @@ struct v4l2_frequency vf; /* get_freq/set_freq */ struct v4l2_standard vs; /* list_std */ struct msp_matrix mspm; /* set_io */ + int mode = V4L2_TUNER_MODE_STEREO; /* set audio mode */ int debug_level = 0; struct ivtv_ioctl_framesync frameinfo; int new_debug_level, gdebug_level; @@ -353,11 +358,13 @@ {"set-io", 1, 0, 0}, {"list-ctrls", 0, 0, 0}, {"set-ctrl", 1, 0, 0}, + {"set-audiomode", 1, 0, 0}, + {"get-audiomode", 0, 0, 0}, {"sync", 0, 0, 0}, {"debug", 1, 0, 0}, {0, 0, 0, 0} }; - ch = getopt_long(argc, argv, ":hc:Cd:ef:g:Gmnop:q:r:stu:v:aYy:D:", + ch = getopt_long(argc, argv, ":hc:Cd:ef:g:Gmnop:q:r:stu:v:aYy:D:z:Z", long_options, &option_index); if (ch == -1) break; @@ -408,6 +415,10 @@ ch = 'y'; else if (!strcmp(long_options[option_index].name, "debug")) ch = 'D'; + else if (!strcmp(long_options[option_index].name, "set-audiomode")) + ch = 'z'; + else if (!strcmp(long_options[option_index].name, "get-audiomode")) + ch = 'Z'; } switch (ch) { @@ -797,6 +808,13 @@ } } break; + case 'z': + set_opts |= OptSetAudioMode; + mode = strtol(optarg, 0L, 0); + break; + case 'Z': + set_opts |= OptGetAudioMode; + break; case ':': fprintf(stderr, "Option `%s' requires a value\n", argv[optind]); usage(); @@ -935,7 +953,7 @@ else { printf("Codec parameters\n"); printf("aspect : %d\n", codec.aspect); - printf("audio : 0x%04x\n", codec.audio_bitmask); + printf("audio : 0x%08x\n", codec.audio_bitmask); printf("bframes : %d\n", codec.bframes); printf("bitrate_mode: %d\n", codec.bitrate_mode); printf("bitrate : %d\n", codec.bitrate); @@ -1110,6 +1128,43 @@ } } + if (set_opts & OptGetAudioMode) { + struct v4l2_tuner vt; + printf("ioctl: VIDIOC_G_TUNER\n"); + memset(&vt, 0, sizeof(struct v4l2_tuner)); + if (ioctl(fd, VIDIOC_G_TUNER, &vt) < 0) { + fprintf(stderr, "ioctl: VIDIOC_G_TUNER failed\n"); + exit(1); + } + printf(" current audio mode : %d\n", vt.audmode); + printf(" (stereo %d, lang1 %d, lang2 %d, mono %d)\n", + V4L2_TUNER_MODE_STEREO, + V4L2_TUNER_MODE_LANG1, + V4L2_TUNER_MODE_LANG2, + V4L2_TUNER_MODE_MONO); + printf(" available subchannels: 0x%x\n", vt.rxsubchans); + printf(" (stereo 0x%x, lang1 0x%x, lang2 0x%x, mono 0x%x)\n", + V4L2_TUNER_SUB_STEREO, + V4L2_TUNER_SUB_LANG1, + V4L2_TUNER_SUB_LANG2, + V4L2_TUNER_SUB_MONO); + } + if (set_opts & OptSetAudioMode) { + struct v4l2_tuner vt; + printf("ioctl: VIDIOC_S_TUNER\n"); + memset(&vt, 0, sizeof(struct v4l2_tuner)); + if (ioctl(fd, VIDIOC_G_TUNER, &vt) < 0) { + fprintf(stderr, "ioctl: VIDIOC_G_TUNER failed\n"); + exit(1); + } + vt.audmode=mode; + if (ioctl(fd, VIDIOC_S_TUNER, &vt) < 0) { + fprintf(stderr, "ioctl: VIDIOC_S_TUNER failed\n"); + exit(1); + } + } + + close(fd); exit(0); }