X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fmedia%2Fvideo%2Fmsp3400-driver.c;h=69ed369c2f4801227f57c529123d5235bfd039ef;hb=987b0145d94eecf292d8b301228356f44611ab7c;hp=b806999d6e0fc69e11c01c2accf67ba4a38562d5;hpb=f7ed79d23a47594e7834d66a8f14449796d4f3e6;p=linux-2.6.git diff --git a/drivers/media/video/msp3400-driver.c b/drivers/media/video/msp3400-driver.c index b806999d6..69ed369c2 100644 --- a/drivers/media/video/msp3400-driver.c +++ b/drivers/media/video/msp3400-driver.c @@ -53,11 +53,10 @@ #include #include #include -#include -#include +#include #include #include -#include "msp3400-driver.h" +#include "msp3400.h" /* ---------------------------------------------------------------------- */ @@ -246,31 +245,31 @@ int msp_write_dsp(struct i2c_client *client, int addr, int val) * ----------------------------------------------------------------------- */ static int scarts[3][9] = { - /* MASK IN1 IN2 IN3 IN4 IN1_DA IN2_DA MONO MUTE */ + /* MASK IN1 IN2 IN1_DA IN2_DA IN3 IN4 MONO MUTE */ /* SCART DSP Input select */ - { 0x0320, 0x0000, 0x0200, 0x0300, 0x0020, -1, -1, 0x0100, 0x0320 }, + { 0x0320, 0x0000, 0x0200, -1, -1, 0x0300, 0x0020, 0x0100, 0x0320 }, /* SCART1 Output select */ - { 0x0c40, 0x0440, 0x0400, 0x0000, 0x0840, 0x0c00, 0x0040, 0x0800, 0x0c40 }, + { 0x0c40, 0x0440, 0x0400, 0x0c00, 0x0040, 0x0000, 0x0840, 0x0800, 0x0c40 }, /* SCART2 Output select */ - { 0x3080, 0x1000, 0x1080, 0x2080, 0x3080, 0x0000, 0x0080, 0x2000, 0x3000 }, + { 0x3080, 0x1000, 0x1080, 0x0000, 0x0080, 0x2080, 0x3080, 0x2000, 0x3000 }, }; static char *scart_names[] = { - "in1", "in2", "in3", "in4", "in1 da", "in2 da", "mono", "mute" + "mask", "in1", "in2", "in1 da", "in2 da", "in3", "in4", "mono", "mute" }; void msp_set_scart(struct i2c_client *client, int in, int out) { struct msp_state *state = i2c_get_clientdata(client); - state->in_scart = in; + state->in_scart=in; - if (in >= 0 && in <= 7 && out >= 0 && out <= 2) { - if (-1 == scarts[out][in + 1]) + if (in >= 1 && in <= 8 && out >= 0 && out <= 2) { + if (-1 == scarts[out][in]) return; - state->acb &= ~scarts[out][0]; - state->acb |= scarts[out][in + 1]; + state->acb &= ~scarts[out][SCART_MASK]; + state->acb |= scarts[out][in]; } else state->acb = 0xf60; /* Mute Input and SCART 1 Output */ @@ -279,8 +278,20 @@ void msp_set_scart(struct i2c_client *client, int in, int out) msp_write_dsp(client, 0x13, state->acb); /* Sets I2S speed 0 = 1.024 Mbps, 1 = 2.048 Mbps */ - if (state->has_i2s_conf) - msp_write_dem(client, 0x40, state->i2s_mode); + msp_write_dem(client, 0x40, state->i2s_mode); +} + +void msp_set_mute(struct i2c_client *client) +{ + struct msp_state *state = i2c_get_clientdata(client); + + v4l_dbg(1, msp_debug, client, "mute audio\n"); + msp_write_dsp(client, 0x0000, 0); + msp_write_dsp(client, 0x0007, 1); + if (state->has_scart2_out_volume) + msp_write_dsp(client, 0x0040, 1); + if (state->has_headphones) + msp_write_dsp(client, 0x0006, 0); } void msp_set_audio(struct i2c_client *client) @@ -288,19 +299,17 @@ void msp_set_audio(struct i2c_client *client) struct msp_state *state = i2c_get_clientdata(client); int bal = 0, bass, treble, loudness; int val = 0; - int reallymuted = state->muted | state->scan_in_progress; - if (!reallymuted) + if (!state->muted) val = (state->volume * 0x7f / 65535) << 8; - v4l_dbg(1, msp_debug, client, "mute=%s scanning=%s volume=%d\n", - state->muted ? "on" : "off", state->scan_in_progress ? "yes" : "no", - state->volume); + v4l_dbg(1, msp_debug, client, "mute=%s volume=%d\n", + state->muted ? "on" : "off", state->volume); msp_write_dsp(client, 0x0000, val); - msp_write_dsp(client, 0x0007, reallymuted ? 0x1 : (val | 0x1)); + msp_write_dsp(client, 0x0007, state->muted ? 0x1 : (val | 0x1)); if (state->has_scart2_out_volume) - msp_write_dsp(client, 0x0040, reallymuted ? 0x1 : (val | 0x1)); + msp_write_dsp(client, 0x0040, state->muted ? 0x1 : (val | 0x1)); if (state->has_headphones) msp_write_dsp(client, 0x0006, val); if (!state->has_sound_processing) @@ -327,6 +336,37 @@ void msp_set_audio(struct i2c_client *client) msp_write_dsp(client, 0x0033, loudness); } +int msp_modus(struct i2c_client *client) +{ + struct msp_state *state = i2c_get_clientdata(client); + + if (state->radio) { + v4l_dbg(1, msp_debug, client, "video mode selected to Radio\n"); + return 0x0003; + } + + if (state->v4l2_std & V4L2_STD_PAL) { + v4l_dbg(1, msp_debug, client, "video mode selected to PAL\n"); + +#if 1 + /* experimental: not sure this works with all chip versions */ + return 0x7003; +#else + /* previous value, try this if it breaks ... */ + return 0x1003; +#endif + } + if (state->v4l2_std & V4L2_STD_NTSC) { + v4l_dbg(1, msp_debug, client, "video mode selected to NTSC\n"); + return 0x2003; + } + if (state->v4l2_std & V4L2_STD_SECAM) { + v4l_dbg(1, msp_debug, client, "video mode selected to SECAM\n"); + return 0x0003; + } + return 0x0003; +} + /* ------------------------------------------------------------------------ */ @@ -336,6 +376,7 @@ static void msp_wake_thread(struct i2c_client *client) if (NULL == state->kthread) return; + msp_set_mute(client); state->watch_stereo = 0; state->restart = 1; wake_up_interruptible(&state->wq); @@ -363,15 +404,19 @@ int msp_sleep(struct msp_state *state, int timeout) /* ------------------------------------------------------------------------ */ -static int msp_mode_v4l2_to_v4l1(int rxsubchans, int audmode) +static int msp_mode_v4l2_to_v4l1(int rxsubchans) { - if (rxsubchans == V4L2_TUNER_SUB_MONO) - return VIDEO_SOUND_MONO; - if (rxsubchans == V4L2_TUNER_SUB_STEREO) - return VIDEO_SOUND_STEREO; - if (audmode == V4L2_TUNER_MODE_LANG2) - return VIDEO_SOUND_LANG2; - return VIDEO_SOUND_LANG1; + int mode = 0; + + if (rxsubchans & V4L2_TUNER_SUB_STEREO) + mode |= VIDEO_SOUND_STEREO; + if (rxsubchans & V4L2_TUNER_SUB_LANG2) + mode |= VIDEO_SOUND_LANG2; + if (rxsubchans & V4L2_TUNER_SUB_LANG1) + mode |= VIDEO_SOUND_LANG1; + if (mode == 0) + mode |= VIDEO_SOUND_MONO; + return mode; } static int msp_mode_v4l1_to_v4l2(int mode) @@ -385,6 +430,21 @@ static int msp_mode_v4l1_to_v4l2(int mode) return V4L2_TUNER_MODE_MONO; } +static void msp_any_detect_stereo(struct i2c_client *client) +{ + struct msp_state *state = i2c_get_clientdata(client); + + switch (state->opmode) { + case OPMODE_MANUAL: + case OPMODE_AUTODETECT: + autodetect_stereo(client); + break; + case OPMODE_AUTOSELECT: + msp34xxg_detect_stereo(client); + break; + } +} + static struct v4l2_queryctrl msp_qctrl_std[] = { { .id = V4L2_CID_AUDIO_VOLUME, @@ -446,6 +506,22 @@ static struct v4l2_queryctrl msp_qctrl_sound_processing[] = { }; +static void msp_any_set_audmode(struct i2c_client *client, int audmode) +{ + struct msp_state *state = i2c_get_clientdata(client); + + switch (state->opmode) { + case OPMODE_MANUAL: + case OPMODE_AUTODETECT: + state->watch_stereo = 0; + msp3400c_setstereo(client, audmode); + break; + case OPMODE_AUTOSELECT: + msp34xxg_set_audmode(client, audmode); + break; + } +} + static int msp_get_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) { struct msp_state *state = i2c_get_clientdata(client); @@ -540,11 +616,52 @@ static int msp_set_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) { struct msp_state *state = i2c_get_clientdata(client); + u16 *sarg = arg; + int scart = 0; if (msp_debug >= 2) v4l_i2c_print_ioctl(client, cmd); switch (cmd) { + case AUDC_SET_INPUT: + if (*sarg == state->input) + break; + state->input = *sarg; + switch (*sarg) { + case AUDIO_RADIO: + /* Hauppauge uses IN2 for the radio */ + state->mode = MSP_MODE_FM_RADIO; + scart = SCART_IN2; + break; + case AUDIO_EXTERN_1: + /* IN1 is often used for external input ... */ + state->mode = MSP_MODE_EXTERN; + scart = SCART_IN1; + break; + case AUDIO_EXTERN_2: + /* ... sometimes it is IN2 through ;) */ + state->mode = MSP_MODE_EXTERN; + scart = SCART_IN2; + break; + case AUDIO_TUNER: + state->mode = -1; + break; + default: + if (*sarg & AUDIO_MUTE) + msp_set_scart(client, SCART_MUTE, 0); + break; + } + if (scart) { + state->rxsubchans = V4L2_TUNER_SUB_STEREO; + state->audmode = V4L2_TUNER_MODE_STEREO; + msp_set_scart(client, scart, 0); + msp_write_dsp(client, 0x000d, 0x1900); + if (state->opmode != OPMODE_AUTOSELECT) + msp3400c_setstereo(client, state->audmode); + } + msp_wake_thread(client); + break; + case AUDC_SET_RADIO: if (state->radio) return 0; @@ -554,8 +671,8 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) switch (state->opmode) { case OPMODE_MANUAL: /* set msp3400 to FM radio mode */ - msp3400c_set_mode(client, MSP_MODE_FM_RADIO); - msp3400c_set_carrier(client, MSP_CARRIER(10.7), + msp3400c_setmode(client, MSP_MODE_FM_RADIO); + msp3400c_setcarrier(client, MSP_CARRIER(10.7), MSP_CARRIER(10.7)); msp_set_audio(client); break; @@ -589,8 +706,8 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) if (state->radio) break; if (state->opmode == OPMODE_AUTOSELECT) - msp_detect_stereo(client); - va->mode = msp_mode_v4l2_to_v4l1(state->rxsubchans, state->audmode); + msp_any_detect_stereo(client); + va->mode = msp_mode_v4l2_to_v4l1(state->rxsubchans); break; } @@ -605,11 +722,8 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) state->treble = va->treble; msp_set_audio(client); - if (va->mode != 0 && state->radio == 0 && - state->audmode != msp_mode_v4l1_to_v4l2(va->mode)) { - state->audmode = msp_mode_v4l1_to_v4l2(va->mode); - msp_set_audmode(client); - } + if (va->mode != 0 && state->radio == 0) + msp_any_set_audmode(client, msp_mode_v4l1_to_v4l2(va->mode)); break; } @@ -645,6 +759,15 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) break; } + /* msp34xx specific */ + case MSP_SET_MATRIX: + { + struct msp_matrix *mspm = arg; + + msp_set_scart(client, mspm->input, mspm->output); + break; + } + /* --- v4l2 ioctls --- */ case VIDIOC_S_STD: { @@ -658,38 +781,101 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) return 0; } - case VIDIOC_INT_G_AUDIO_ROUTING: + case VIDIOC_ENUMINPUT: + { + struct v4l2_input *i = arg; + + if (i->index != 0) + return -EINVAL; + + i->type = V4L2_INPUT_TYPE_TUNER; + switch (i->index) { + case AUDIO_RADIO: + strcpy(i->name, "Radio"); + break; + case AUDIO_EXTERN_1: + strcpy(i->name, "Extern 1"); + break; + case AUDIO_EXTERN_2: + strcpy(i->name, "Extern 2"); + break; + case AUDIO_TUNER: + strcpy(i->name, "Television"); + break; + default: + return -EINVAL; + } + return 0; + } + + case VIDIOC_G_AUDIO: { - struct v4l2_routing *rt = arg; + struct v4l2_audio *a = arg; + + memset(a, 0, sizeof(*a)); + + switch (a->index) { + case AUDIO_RADIO: + strcpy(a->name, "Radio"); + break; + case AUDIO_EXTERN_1: + strcpy(a->name, "Extern 1"); + break; + case AUDIO_EXTERN_2: + strcpy(a->name, "Extern 2"); + break; + case AUDIO_TUNER: + strcpy(a->name, "Television"); + break; + default: + return -EINVAL; + } + + msp_any_detect_stereo(client); + if (state->audmode == V4L2_TUNER_MODE_STEREO) { + a->capability = V4L2_AUDCAP_STEREO; + } - *rt = state->routing; break; } - case VIDIOC_INT_S_AUDIO_ROUTING: + case VIDIOC_S_AUDIO: { - struct v4l2_routing *rt = arg; - int tuner = (rt->input >> 3) & 1; - int sc_in = rt->input & 0x7; - int sc1_out = rt->output & 0xf; - int sc2_out = (rt->output >> 4) & 0xf; - u16 val, reg; - - if (state->routing.input == rt->input && - state->routing.output == rt->output) + struct v4l2_audio *sarg = arg; + + switch (sarg->index) { + case AUDIO_RADIO: + /* Hauppauge uses IN2 for the radio */ + state->mode = MSP_MODE_FM_RADIO; + scart = SCART_IN2; + break; + case AUDIO_EXTERN_1: + /* IN1 is often used for external input ... */ + state->mode = MSP_MODE_EXTERN; + scart = SCART_IN1; + break; + case AUDIO_EXTERN_2: + /* ... sometimes it is IN2 through ;) */ + state->mode = MSP_MODE_EXTERN; + scart = SCART_IN2; + break; + case AUDIO_TUNER: + state->mode = -1; break; - state->routing = *rt; - msp_set_scart(client, sc_in, 0); - msp_set_scart(client, sc1_out, 1); - msp_set_scart(client, sc2_out, 2); - msp_set_audmode(client); - reg = (state->opmode == OPMODE_AUTOSELECT) ? 0x30 : 0xbb; - val = msp_read_dem(client, reg); - if (tuner != ((val >> 8) & 1)) { - msp_write_dem(client, reg, (val & ~0x100) | (tuner << 8)); - /* wake thread when a new tuner input is chosen */ - msp_wake_thread(client); } + if (scart) { + state->rxsubchans = V4L2_TUNER_SUB_STEREO; + state->audmode = V4L2_TUNER_MODE_STEREO; + msp_set_scart(client, scart, 0); + msp_write_dsp(client, 0x000d, 0x1900); + } + if (sarg->capability == V4L2_AUDCAP_STEREO) { + state->audmode = V4L2_TUNER_MODE_STEREO; + } else { + state->audmode &= ~V4L2_TUNER_MODE_STEREO; + } + msp_any_set_audmode(client, state->audmode); + msp_wake_thread(client); break; } @@ -700,10 +886,10 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) if (state->radio) break; if (state->opmode == OPMODE_AUTOSELECT) - msp_detect_stereo(client); + msp_any_detect_stereo(client); vt->audmode = state->audmode; vt->rxsubchans = state->rxsubchans; - vt->capability |= V4L2_TUNER_CAP_STEREO | + vt->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; break; } @@ -712,13 +898,48 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) { struct v4l2_tuner *vt = (struct v4l2_tuner *)arg; - if (state->radio) /* TODO: add mono/stereo support for radio */ - break; - if (state->audmode == vt->audmode) + if (state->radio) break; - state->audmode = vt->audmode; /* only set audmode */ - msp_set_audmode(client); + if (vt->audmode != -1 && vt->audmode != 0) + msp_any_set_audmode(client, vt->audmode); + break; + } + + case VIDIOC_G_AUDOUT: + { + struct v4l2_audioout *a = (struct v4l2_audioout *)arg; + int idx = a->index; + + memset(a, 0, sizeof(*a)); + + switch (idx) { + case 0: + strcpy(a->name, "Scart1 Out"); + break; + case 1: + strcpy(a->name, "Scart2 Out"); + break; + case 2: + strcpy(a->name, "I2S Out"); + break; + default: + return -EINVAL; + } + break; + + } + + case VIDIOC_S_AUDOUT: + { + struct v4l2_audioout *a = (struct v4l2_audioout *)arg; + + if (a->index < 0 || a->index > 2) + return -EINVAL; + + v4l_dbg(1, msp_debug, client, "Setting audio out on msp34xx to input %i\n", a->index); + msp_set_scart(client, state->in_scart, a->index + 1); + break; } @@ -772,7 +993,7 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) const char *p; if (state->opmode == OPMODE_AUTOSELECT) - msp_detect_stereo(client); + msp_any_detect_stereo(client); v4l_info(client, "%s rev1 = 0x%04x rev2 = 0x%04x\n", client->name, state->rev1, state->rev2); v4l_info(client, "Audio: volume %d%s\n", @@ -799,16 +1020,12 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) (state->rxsubchans & V4L2_TUNER_SUB_STEREO) ? "stereo" : "mono", (state->rxsubchans & V4L2_TUNER_SUB_LANG2) ? ", dual" : ""); } else { - if (state->opmode == OPMODE_AUTODETECT) - v4l_info(client, "Mode: %s\n", p); + v4l_info(client, "Mode: %s\n", p); v4l_info(client, "Standard: %s (%s%s)\n", msp_standard_std_name(state->std), (state->rxsubchans & V4L2_TUNER_SUB_STEREO) ? "stereo" : "mono", (state->rxsubchans & V4L2_TUNER_SUB_LANG2) ? ", dual" : ""); } - v4l_info(client, "Audmode: 0x%04x\n", state->audmode); - v4l_info(client, "Routing: 0x%08x (input) 0x%08x (output)\n", - state->routing.input, state->routing.output); v4l_info(client, "ACB: 0x%04x\n", state->acb); break; } @@ -877,7 +1094,6 @@ static int msp_attach(struct i2c_adapter *adapter, int address, int kind) memset(state, 0, sizeof(*state)); state->v4l2_std = V4L2_STD_NTSC; - state->audmode = V4L2_TUNER_MODE_STEREO; state->volume = 58880; /* 0db gain */ state->balance = 32768; /* 0db gain */ state->bass = 32768; @@ -887,9 +1103,6 @@ static int msp_attach(struct i2c_adapter *adapter, int address, int kind) state->muted = 0; state->i2s_mode = 0; init_waitqueue_head(&state->wq); - /* These are the reset input/output positions */ - state->routing.input = MSP_INPUT_DEFAULT; - state->routing.output = MSP_OUTPUT_DEFAULT; state->rev1 = msp_read_dsp(client, 0x1e); if (state->rev1 != -1) @@ -921,16 +1134,13 @@ static int msp_attach(struct i2c_adapter *adapter, int address, int kind) state->has_radio = msp_revision >= 'G'; /* Has headphones output: not for stripped down products */ state->has_headphones = msp_prod_lo < 5; - /* Has scart2 input: not in stripped down products of the '3' family */ - state->has_scart2 = msp_family >= 4 || msp_prod_lo < 7; - /* Has scart3 input: not in stripped down products of the '3' family */ - state->has_scart3 = msp_family >= 4 || msp_prod_lo < 5; /* Has scart4 input: not in pre D revisions, not in stripped D revs */ state->has_scart4 = msp_family >= 4 || (msp_revision >= 'D' && msp_prod_lo < 5); - /* Has scart2 output: not in stripped down products of the '3' family */ - state->has_scart2_out = msp_family >= 4 || msp_prod_lo < 5; + /* Has scart2 and scart3 inputs and scart2 output: not in stripped + down products of the '3' family */ + state->has_scart23_in_scart2_out = msp_family >= 4 || msp_prod_lo < 5; /* Has scart2 a volume control? Not in pre-D revisions. */ - state->has_scart2_out_volume = msp_revision > 'C' && state->has_scart2_out; + state->has_scart2_out_volume = msp_revision > 'C' && state->has_scart23_in_scart2_out; /* Has a configurable i2s out? */ state->has_i2s_conf = msp_revision >= 'G' && msp_prod_lo < 7; /* Has subwoofer output: not in pre-D revs and not in stripped down products */