msp_write_dsp(client, 0x0003, treble); /* loudspeaker */
 }
 
-int msp_modus(struct i2c_client *client, int norm)
+int msp_modus(struct i2c_client *client)
 {
-       switch (norm) {
-       case VIDEO_MODE_PAL:
+       struct msp_state *state = i2c_get_clientdata(client);
+
+       if (state->radio) {
+               v4l_dbg(1, client, "video mode selected to Radio\n");
+               return 0x0003;
+       }
+
+       if (state->std & V4L2_STD_PAL) {
                v4l_dbg(1, client, "video mode selected to PAL\n");
 
 #if 1
                /* previous value, try this if it breaks ... */
                return 0x1003;
 #endif
-       case VIDEO_MODE_NTSC:  /* BTSC */
+       }
+       if (state->std & V4L2_STD_NTSC) {
                v4l_dbg(1, client, "video mode selected to NTSC\n");
                return 0x2003;
-       case VIDEO_MODE_SECAM:
+       }
+       if (state->std & V4L2_STD_SECAM) {
                v4l_dbg(1, client, "video mode selected to SECAM\n");
                return 0x0003;
-       case VIDEO_MODE_RADIO:
-               v4l_dbg(1, client, "video mode selected to Radio\n");
-               return 0x0003;
-       case VIDEO_MODE_AUTO:
-               v4l_dbg(1, client, "video mode selected to Auto\n");
-               return 0x2003;
-       default:
-               return 0x0003;
-       }
-}
-
-int msp_standard(int norm)
-{
-       switch (norm) {
-       case VIDEO_MODE_PAL:
-               return 1;
-       case VIDEO_MODE_NTSC:  /* BTSC */
-               return 0x0020;
-       case VIDEO_MODE_SECAM:
-               return 1;
-       case VIDEO_MODE_RADIO:
-               return 0x0040;
-       default:
-               return 1;
        }
+       return 0x0003;
 }
 
 /* ------------------------------------------------------------------------ */
                break;
 
        case AUDC_SET_RADIO:
-               state->norm = VIDEO_MODE_RADIO;
+               state->radio = 1;
                v4l_dbg(1, client, "switching to radio mode\n");
                state->watch_stereo = 0;
                switch (state->opmode) {
                state->treble = va->treble;
                msp_set_audio(client);
 
-               if (va->mode != 0 && state->norm != VIDEO_MODE_RADIO)
+               if (va->mode != 0 && state->radio == 0)
                        msp_any_set_audmode(client, msp_mode_v4l1_to_v4l2(va->mode));
                break;
        }
        {
                struct video_channel *vc = arg;
 
-               state->norm = vc->norm;
+               state->radio = 0;
+               if (vc->norm == VIDEO_MODE_PAL)
+                       state->std = V4L2_STD_PAL;
+               else if (vc->norm == VIDEO_MODE_SECAM)
+                       state->std = V4L2_STD_SECAM;
+               else
+                       state->std = V4L2_STD_NTSC;
                msp_wake_thread(client);
                break;
        }
        {
                v4l2_std_id *id = arg;
 
-               /*FIXME: use V4L2 mode flags on msp3400 instead of V4L1*/
-               if (*id & V4L2_STD_PAL) {
-                       state->norm = VIDEO_MODE_PAL;
-               } else if (*id & V4L2_STD_SECAM) {
-                       state->norm = VIDEO_MODE_SECAM;
-               } else {
-                       state->norm = VIDEO_MODE_NTSC;
-               }
-
+               state->std = *id;
+               state->radio = 0;
                msp_wake_thread(client);
                return 0;
        }
        struct i2c_client *client;
        struct msp_state *state;
        int (*thread_func)(void *data) = NULL;
+       int msp_hard;
+       int msp_family;
+       int msp_revision;
+       int msp_product, msp_prod_hi, msp_prod_lo;
+       int msp_rom;
 
        client = kmalloc(sizeof(*client), GFP_KERNEL);
        if (client == NULL)
        i2c_set_clientdata(client, state);
 
        memset(state, 0, sizeof(*state));
-       state->norm = VIDEO_MODE_NTSC;
+       state->std = V4L2_STD_NTSC;
        state->volume = 58880;  /* 0db gain */
        state->balance = 32768; /* 0db gain */
        state->bass = 32768;
 
        msp_set_audio(client);
 
-       snprintf(client->name, sizeof(client->name), "MSP%c4%02d%c-%c%d",
-                ((state->rev1 >> 4) & 0x0f) + '3',
-                (state->rev2 >> 8) & 0xff,
-                (state->rev1 & 0x0f) + '@',
-                ((state->rev1 >> 8) & 0xff) + '@',
-                state->rev2 & 0x1f);
+       msp_family = ((state->rev1 >> 4) & 0x0f) + 3;
+       msp_product = (state->rev2 >> 8) & 0xff;
+       msp_prod_hi = msp_product / 10;
+       msp_prod_lo = msp_product % 10;
+       msp_revision = (state->rev1 & 0x0f) + '@';
+       msp_hard = ((state->rev1 >> 8) & 0xff) + '@';
+       msp_rom = state->rev2 & 0x1f;
+       snprintf(client->name, sizeof(client->name), "MSP%d4%02d%c-%c%d",
+                       msp_family, msp_product,
+                       msp_revision, msp_hard, msp_rom);
+
+       /* Has NICAM support: all mspx41x and mspx45x products have NICAM */
+       state->has_nicam = msp_prod_hi == 1 || msp_prod_hi == 5;
+       /* Has radio support: was added with revision G */
+       state->has_radio = msp_revision >= 'G';
+       /* Has headphones output: not for stripped down products */
+       state->has_headphones = 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 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 subwoofer output: not in pre-D revs and not in stripped down products */
+       state->has_subwoofer = msp_revision >= 'D' && msp_prod_lo < 5;
+       /* Has soundprocessing (bass/treble/balance/loudness/equalizer): not in
+          stripped down products */
+       state->has_sound_processing = msp_prod_lo < 7;
+       /* Has Virtual Dolby Surround: only in msp34x1 */
+       state->has_virtual_dolby_surround = msp_revision == 'G' && msp_prod_lo == 1;
+       /* Has Virtual Dolby Surround & Dolby Pro Logic: only in msp34x2 */
+       state->has_dolby_pro_logic = msp_revision == 'G' && msp_prod_lo == 2;
 
        state->opmode = opmode;
        if (state->opmode == OPMODE_AUTO) {
                /* MSP revision G and up have both autodetect and autoselect */
-               if ((state->rev1 & 0x0f) >= 'G'-'@')
+               if (msp_revision >= 'G')
                        state->opmode = OPMODE_AUTOSELECT;
                /* MSP revision D and up have autodetect */
-               else if ((state->rev1 & 0x0f) >= 'D'-'@')
+               else if (msp_revision >= 'D')
                        state->opmode = OPMODE_AUTODETECT;
                else
                        state->opmode = OPMODE_MANUAL;
        /* hello world :-) */
        v4l_info(client, "%s found @ 0x%x (%s)\n", client->name, address << 1, adapter->name);
        v4l_info(client, "%s ", client->name);
-       if (HAVE_NICAM(state) && HAVE_RADIO(state))
+       if (state->has_nicam && state->has_radio)
                printk("supports nicam and radio, ");
-       else if (HAVE_NICAM(state))
+       else if (state->has_nicam)
                printk("supports nicam, ");
-       else if (HAVE_RADIO(state))
+       else if (state->has_radio)
                printk("supports radio, ");
        printk("mode is ");
 
 
        msp_write_dsp(client, 0x000a, msp3400c_init_data[type].dsp_src);
        msp_write_dsp(client, 0x000e, msp3400c_init_data[type].dsp_matrix);
 
-       if (HAVE_NICAM(state)) {
+       if (state->has_nicam) {
                /* nicam prescale */
                msp_write_dsp(client, 0x0010, 0x5a00); /* was: 0x3000 */
        }
                if (kthread_should_stop())
                        break;
 
-               if (VIDEO_MODE_RADIO == state->norm ||
-                   MSP_MODE_EXTERN  == state->mode) {
+               if (state->radio || MSP_MODE_EXTERN == state->mode) {
                        /* no carrier scan, just unmute */
                        v4l_dbg(1, client, "thread: no carrier scan\n");
                        msp_set_audio(client);
                cd = msp3400c_carrier_detect_main;
                count = ARRAY_SIZE(msp3400c_carrier_detect_main);
 
-               if (amsound && (state->norm == VIDEO_MODE_SECAM)) {
+               if (amsound && (state->std & V4L2_STD_SECAM)) {
                        /* autodetect doesn't work well with AM ... */
                        max1 = 3;
                        count = 0;
                        break;
                }
 
-               if (amsound && (state->norm == VIDEO_MODE_SECAM)) {
+               if (amsound && (state->std & V4L2_STD_SECAM)) {
                        /* autodetect doesn't work well with AM ... */
                        cd = NULL;
                        count = 0;
                                state->nicam_on = 0;
                                msp3400c_setstereo(client, V4L2_TUNER_MODE_MONO);
                                state->watch_stereo = 1;
-                       } else if (max2 == 1 && HAVE_NICAM(state)) {
+                       } else if (max2 == 1 && state->has_nicam) {
                                /* B/G NICAM */
                                state->second = msp3400c_carrier_detect_55[max2].cdo;
                                msp3400c_setmode(client, MSP_MODE_FM_NICAM1);
                                state->nicam_on = 0;
                                msp3400c_setstereo(client, V4L2_TUNER_MODE_MONO);
                                state->watch_stereo = 1;
-                       } else if (max2 == 0 &&
-                                  state->norm == VIDEO_MODE_SECAM) {
+                       } else if (max2 == 0 && (state->std & V4L2_STD_SECAM)) {
                                /* L NICAM or AM-mono */
                                state->second = msp3400c_carrier_detect_65[max2].cdo;
                                msp3400c_setmode(client, MSP_MODE_AM_NICAM);
                                /* volume prescale for SCART (AM mono input) */
                                msp_write_dsp(client, 0x000d, 0x1900);
                                state->watch_stereo = 1;
-                       } else if (max2 == 0 && HAVE_NICAM(state)) {
+                       } else if (max2 == 0 && state->has_nicam) {
                                /* D/K NICAM */
                                state->second = msp3400c_carrier_detect_65[max2].cdo;
                                msp3400c_setmode(client, MSP_MODE_FM_NICAM1);
                        goto restart;
 
                /* start autodetect */
-               mode = msp_modus(client, state->norm);
-               std  = msp_standard(state->norm);
+               mode = msp_modus(client);
+               std = (state->std & V4L2_STD_NTSC) ? 0x20 : 1;
                msp_write_dem(client, 0x30, mode);
                msp_write_dem(client, 0x20, std);
                state->watch_stereo = 0;
                state->main   = msp_modelist[i].main;
                state->second = msp_modelist[i].second;
 
-               if (amsound && (state->norm == VIDEO_MODE_SECAM) && (val != 0x0009)) {
+               if (amsound && (state->std & V4L2_STD_SECAM) && (val != 0x0009)) {
                        /* autodetection has failed, let backup */
                        v4l_dbg(1, client, "autodetection failed,"
                                " switching to backup mode: %s (0x%04x)\n",
                        msp3400c_setstereo(client,V4L2_TUNER_MODE_STEREO);
                        break;
                case 0x0040: /* FM radio */
-                       state->mode   = MSP_MODE_FM_RADIO;
+                       state->mode = MSP_MODE_FM_RADIO;
                        state->rxsubchans = V4L2_TUNER_SUB_STEREO;
                        state->audmode = V4L2_TUNER_MODE_STEREO;
                        state->nicam_on = 0;
                        state->watch_stereo = 0;
-                       /* not needed in theory if HAVE_RADIO(), but
+                       /* not needed in theory if we have radio, but
                           short programming enables carrier mute */
                        msp3400c_setmode(client,MSP_MODE_FM_RADIO);
                        msp3400c_setcarrier(client, MSP_CARRIER(10.7),
        msp_write_dem(client, 0x40, state->i2s_mode);
 
        /* step-by-step initialisation, as described in the manual */
-       modus = msp_modus(client, state->norm);
-       std   = msp_standard(state->norm);
+       modus = msp_modus(client);
+       if (state->radio)
+               std = 0x40;
+       else
+               std = (state->std & V4L2_STD_NTSC) ? 0x20 : 1;
        modus &= ~0x03; /* STATUS_CHANGE = 0 */
        modus |= 0x01;  /* AUTOMATIC_SOUND_DETECTION = 1 */
        if (msp_write_dem(client, 0x30, modus))