static int _tm6000_start_audio_dma(struct snd_tm6000_card *chip)
 {
        struct tm6000_core *core = chip->core;
-       int val;
 
        dprintk(1, "Starting audio DMA\n");
 
        /* Enables audio */
-       val = tm6000_get_reg(core, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0x0);
-       val |= 0x20;
-       tm6000_set_reg(core, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, val);
+       tm6000_set_reg_mask(core, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0x40, 0x40);
 
        tm6000_set_audio_bitrate(core, 48000);
 
 static int _tm6000_stop_audio_dma(struct snd_tm6000_card *chip)
 {
        struct tm6000_core *core = chip->core;
-       int val;
+
        dprintk(1, "Stopping audio DMA\n");
 
-       /* Enables audio */
-       val = tm6000_get_reg(core, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0x0);
-       val &= ~0x20;
-       tm6000_set_reg(core, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, val);
+       /* Disables audio */
+       tm6000_set_reg_mask(core, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0x00, 0x40);
 
        tm6000_set_reg(core, TM6010_REQ08_R01_A_INIT, 0);
 
 
        char            *name;
 
        struct tm6000_capabilities caps;
+       enum            tm6000_inaudio aradio;
+       enum            tm6000_inaudio avideo;
 
        enum            tm6000_devtype type;    /* variant of the chipset */
        int             tuner_type;     /* type of the tuner */
                .tuner_addr   = 0xc2 >> 1,
                .demod_addr   = 0x1e >> 1,
                .type         = TM6010,
+               .avideo       = TM6000_AIP_SIF1,
+               .aradio       = TM6000_AIP_LINE1,
                .caps = {
                        .has_tuner    = 1,
                        .has_dvb      = 1,
                .tuner_type   = TUNER_XC5000,
                .tuner_addr   = 0xc2 >> 1,
                .type         = TM6010,
+               .avideo       = TM6000_AIP_SIF1,
+               .aradio       = TM6000_AIP_LINE1,
                .caps = {
                        .has_tuner    = 1,
                        .has_dvb      = 0,
                struct xc5000_config ctl = {
                        .i2c_address = dev->tuner_addr,
                        .if_khz      = 4570,
-                       .radio_input = XC5000_RADIO_FM1,
+                       .radio_input = XC5000_RADIO_FM1_MONO,
                        };
 
                xc5000_cfg.tuner = TUNER_XC5000;
                xc5000_cfg.priv  = &ctl;
 
-
                v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config,
                                     &xc5000_cfg);
                }
 
        dev->caps = tm6000_boards[dev->model].caps;
 
+       dev->avideo = tm6000_boards[dev->model].avideo;
+       dev->aradio = tm6000_boards[dev->model].aradio;
        /* initialize hardware */
        rc = tm6000_init(dev);
        if (rc < 0)
 
 }
 EXPORT_SYMBOL_GPL(tm6000_get_reg);
 
+int tm6000_set_reg_mask(struct tm6000_core *dev, u8 req, u16 value,
+                                               u16 index, u16 mask)
+{
+       int rc;
+       u8 buf[1];
+       u8 new_index;
+
+       rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR, req,
+                                       value, index, buf, 1);
+
+       if (rc < 0)
+               return rc;
+
+       new_index = (buf[0] & ~mask) | (index & mask);
+
+       if (new_index == index)
+               return 0;
+
+       return tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR,
+                                     req, value, new_index, NULL, 0);
+}
+EXPORT_SYMBOL_GPL(tm6000_set_reg_mask);
+
 int tm6000_get_reg16(struct tm6000_core *dev, u8 req, u16 value, u16 index)
 {
        int rc;
        struct v4l2_frequency f;
 
        if (dev->dev_type == TM6010) {
-               int val;
-
                /* Enable video */
-               val = tm6000_get_reg(dev, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0);
-               val |= 0x60;
-               tm6000_set_reg(dev, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, val);
-               val = tm6000_get_reg(dev,
-                       TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0);
-               val &= ~0x40;
-               tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, val);
 
+               tm6000_set_reg_mask(dev, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF,
+                                                       0x60, 0x60);
+               tm6000_set_reg_mask(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE,
+                                                       0x00, 0x40);
                tm6000_set_reg(dev, TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfc);
 
        } else {
        { TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xf0 },
        { TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2 },
        { TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, 0x60 },
+       { TM6010_REQ08_R03_A_AUTO_GAIN_CTRL, 0x00},
+       { TM6010_REQ08_R04_A_SIF_AMP_CTRL, 0x80},
+       { TM6010_REQ08_R0C_A_ASD_THRES2, 0x0a},
+       { TM6010_REQ08_R0D_A_AMD_THRES, 0x40},
+       { TM6010_REQ08_R1A_A_NICAM_SER_MAX, 0x64},
+       { TM6010_REQ08_R1B_A_NICAM_SER_MIN, 0x20},
+       { TM6010_REQ08_R16_A_AGC_GAIN_MAX, 0xfe},
+       { TM6010_REQ08_R17_A_AGC_GAIN_MIN, 0x01},
        { TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfc },
 
        { TM6010_REQ07_R3F_RESET, 0x01 },
 
 int tm6000_set_audio_bitrate(struct tm6000_core *dev, int bitrate)
 {
-       int val;
+       int val = 0;
+       u8 areg_f0 = 0x60; /* ADC MCLK = 250 Fs */
+       u8 areg_0a = 0x91; /* SIF 48KHz */
+
+       switch (bitrate) {
+       case 48000:
+               areg_f0 = 0x60; /* ADC MCLK = 250 Fs */
+               areg_0a = 0x91; /* SIF 48KHz */
+               dev->audio_bitrate = bitrate;
+               break;
+       case 32000:
+               areg_f0 = 0x00; /* ADC MCLK = 375 Fs */
+               areg_0a = 0x90; /* SIF 32KHz */
+               dev->audio_bitrate = bitrate;
+               break;
+       default:
+               return -EINVAL;
+       }
+
 
        /* enable I2S, if we use sif or external I2S device */
        if (dev->dev_type == TM6010) {
-               val = tm6000_get_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD, 0);
-               if (val < 0)
-                       return val;
-               val = (val & 0xf0) | 0x1; /* 48 kHz, not muted */
-               val = tm6000_set_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD, val);
+               val = tm6000_set_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD, areg_0a);
                if (val < 0)
                        return val;
-       }
 
-       /* different reg's to set audio bitrate */
-       if (dev->dev_type == TM6010) {
-               val = tm6000_get_reg(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG,
-                       0x0);
+               val = tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG,
+                                                       areg_f0, 0xf0);
                if (val < 0)
                        return val;
        } else {
-               val = tm6000_get_reg(dev, TM6000_REQ07_REB_VADC_AADC_MODE, 0x0);
+               val = tm6000_set_reg_mask(dev, TM6000_REQ07_REB_VADC_AADC_MODE,
+                                                       areg_f0, 0xf0);
                if (val < 0)
                        return val;
        }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(tm6000_set_audio_bitrate);
 
-       val &= 0x0f;            /* Preserve the audio input control bits */
-       switch (bitrate) {
-       case 44100:
-               val |= 0xd0;
-               dev->audio_bitrate = bitrate;
+int tm6000_set_audio_input(struct tm6000_core *dev, enum tm6000_inaudio ainp)
+{
+       if (dev->dev_type == TM6010) {
+               /* Audio crossbar setting, default SIF1 */
+               u8 areg_f0 = 0x03;
+
+               switch (ainp) {
+               case TM6000_AIP_SIF1:
+               case TM6000_AIP_SIF2:
+                       areg_f0 = 0x03;
+                       break;
+               case TM6000_AIP_LINE1:
+                       areg_f0 = 0x00;
+                       break;
+               case TM6000_AIP_LINE2:
+                       areg_f0 = 0x08;
+                       break;
+               default:
+                       return 0;
+                       break;
+               }
+               /* Set audio input crossbar */
+               tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG,
+                                                       areg_f0, 0x0f);
+       } else {
+               /* Audio setting, default LINE1 */
+               u8 areg_eb = 0x00;
+
+               switch (ainp) {
+               case TM6000_AIP_LINE1:
+                       areg_eb = 0x00;
+                       break;
+               case TM6000_AIP_LINE2:
+                       areg_eb = 0x04;
+                       break;
+               default:
+                       return 0;
+                       break;
+               }
+               /* Set audio input */
+               tm6000_set_reg_mask(dev, TM6000_REQ07_REB_VADC_AADC_MODE,
+                                                       areg_eb, 0x0f);
+       }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(tm6000_set_audio_input);
+
+void tm6010_set_mute_sif(struct tm6000_core *dev, u8 mute)
+{
+       u8 mute_reg = 0;
+
+       if (mute)
+               mute_reg = 0x08;
+
+       tm6000_set_reg_mask(dev, TM6010_REQ08_R0A_A_I2S_MOD, mute_reg, 0x08);
+}
+
+void tm6010_set_mute_adc(struct tm6000_core *dev, u8 mute)
+{
+       u8 mute_reg = 0;
+
+       if (mute)
+               mute_reg = 0x20;
+
+       if (dev->dev_type == TM6010) {
+               tm6000_set_reg_mask(dev, TM6010_REQ08_RF2_LEFT_CHANNEL_VOL,
+                                                       mute_reg, 0x20);
+               tm6000_set_reg_mask(dev, TM6010_REQ08_RF3_RIGHT_CHANNEL_VOL,
+                                                       mute_reg, 0x20);
+       } else {
+               tm6000_set_reg_mask(dev, TM6000_REQ07_REC_VADC_AADC_LVOL,
+                                                       mute_reg, 0x20);
+               tm6000_set_reg_mask(dev, TM6000_REQ07_RED_VADC_AADC_RVOL,
+                                                       mute_reg, 0x20);
+       }
+}
+
+int tm6000_tvaudio_set_mute(struct tm6000_core *dev, u8 mute)
+{
+       enum tm6000_inaudio ainp;
+
+       if (dev->radio)
+               ainp = dev->aradio;
+       else
+               ainp = dev->avideo;
+
+       switch (ainp) {
+       case TM6000_AIP_SIF1:
+       case TM6000_AIP_SIF2:
+               if (dev->dev_type == TM6010)
+                       tm6010_set_mute_sif(dev, mute);
+               else {
+                       printk(KERN_INFO "ERROR: TM5600 and TM6000 don't has"
+                                       " SIF audio inputs. Please check the %s"
+                                       " configuration.\n", dev->name);
+                       return -EINVAL;
+               }
                break;
-       case 48000:
-               val |= 0x60;
-               dev->audio_bitrate = bitrate;
+       case TM6000_AIP_LINE1:
+       case TM6000_AIP_LINE2:
+               tm6010_set_mute_adc(dev, mute);
+               break;
+       default:
+               return -EINVAL;
                break;
        }
-       if (dev->dev_type == TM6010)
-               val = tm6000_set_reg(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG,
-                       val);
-       else
-               val = tm6000_set_reg(dev, TM6000_REQ07_REB_VADC_AADC_MODE, val);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(tm6000_tvaudio_set_mute);
+
+void tm6010_set_volume_sif(struct tm6000_core *dev, int vol)
+{
+       u8 vol_reg;
 
-       return val;
+       vol_reg = vol & 0x0F;
+
+       if (vol < 0)
+               vol_reg |= 0x40;
+
+       tm6000_set_reg(dev, TM6010_REQ08_R07_A_LEFT_VOL, vol_reg);
+       tm6000_set_reg(dev, TM6010_REQ08_R08_A_RIGHT_VOL, vol_reg);
 }
-EXPORT_SYMBOL_GPL(tm6000_set_audio_bitrate);
+
+void tm6010_set_volume_adc(struct tm6000_core *dev, int vol)
+{
+       u8 vol_reg;
+
+       vol_reg = (vol + 0x10) & 0x1f;
+
+       if (dev->dev_type == TM6010) {
+               tm6000_set_reg(dev, TM6010_REQ08_RF2_LEFT_CHANNEL_VOL, vol_reg);
+               tm6000_set_reg(dev, TM6010_REQ08_RF3_RIGHT_CHANNEL_VOL, vol_reg);
+       } else {
+               tm6000_set_reg(dev, TM6000_REQ07_REC_VADC_AADC_LVOL, vol_reg);
+               tm6000_set_reg(dev, TM6000_REQ07_RED_VADC_AADC_RVOL, vol_reg);
+       }
+}
+
+void tm6000_set_volume(struct tm6000_core *dev, int vol)
+{
+       enum tm6000_inaudio ainp;
+
+       if (dev->radio) {
+               ainp = dev->aradio;
+               vol += 8; /* Offset to 0 dB */
+       } else
+               ainp = dev->avideo;
+
+       switch (ainp) {
+       case TM6000_AIP_SIF1:
+       case TM6000_AIP_SIF2:
+               if (dev->dev_type == TM6010)
+                       tm6010_set_volume_sif(dev, vol);
+               else
+                       printk(KERN_INFO "ERROR: TM5600 and TM6000 don't has"
+                                       " SIF audio inputs. Please check the %s"
+                                       " configuration.\n", dev->name);
+               break;
+       case TM6000_AIP_LINE1:
+       case TM6000_AIP_LINE2:
+               tm6010_set_volume_adc(dev, vol);
+               break;
+       default:
+               break;
+       }
+}
+EXPORT_SYMBOL_GPL(tm6000_set_volume);
 
 static LIST_HEAD(tm6000_devlist);
 static DEFINE_MUTEX(tm6000_devlist_mutex);
 
        uint8_t mono_flag = 0;  /* No mono */
        uint8_t nicam_flag = 0; /* No NICAM */
 
+       if (dev->radio) {
+               tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x00);
+               tm6000_set_reg(dev, TM6010_REQ08_R02_A_FIX_GAIN_CTRL, 0x04);
+               tm6000_set_reg(dev, TM6010_REQ08_R03_A_AUTO_GAIN_CTRL, 0x00);
+               tm6000_set_reg(dev, TM6010_REQ08_R04_A_SIF_AMP_CTRL, 0x80);
+               tm6000_set_reg(dev, TM6010_REQ08_R05_A_STANDARD_MOD, 0x0c);
+               tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD, 0x00);
+               tm6000_set_reg(dev, TM6010_REQ08_R09_A_MAIN_VOL, 0x18);
+               tm6000_set_reg(dev, TM6010_REQ08_R0C_A_ASD_THRES2, 0x0a);
+               tm6000_set_reg(dev, TM6010_REQ08_R0D_A_AMD_THRES, 0x40);
+               tm6000_set_reg(dev, TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfc);
+               tm6000_set_reg(dev, TM6010_REQ08_R1E_A_GAIN_DEEMPH_OUT, 0x13);
+               tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x80);
+               return 0;
+       }
+
        switch (std) {
 #if 0
        case DK_MONO:
        case EIAJ:
                areg_05 = 0x02;
                break;
-       case FM_RADIO:
-               tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x00);
-               tm6000_set_reg(dev, TM6010_REQ08_R02_A_FIX_GAIN_CTRL, 0x04);
-               tm6000_set_reg(dev, TM6010_REQ08_R03_A_AUTO_GAIN_CTRL, 0x00);
-               tm6000_set_reg(dev, TM6010_REQ08_R05_A_STANDARD_MOD, 0x0c);
-               tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD, 0x00);
-               tm6000_set_reg(dev, TM6010_REQ08_R09_A_MAIN_VOL, 0x18);
-               tm6000_set_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD, 0x91);
-               tm6000_set_reg(dev, TM6010_REQ08_R16_A_AGC_GAIN_MAX, 0xfe);
-               tm6000_set_reg(dev, TM6010_REQ08_R17_A_AGC_GAIN_MIN, 0x01);
-               tm6000_set_reg(dev, TM6010_REQ08_R1E_A_GAIN_DEEMPH_OUT, 0x13);
-               tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x80);
-               return 0;
-               break;
        case I_NICAM:
                areg_05 = 0x08;
                nicam_flag = 1;
                areg_05 = 0x0a;
                nicam_flag = 1;
                break;
+       default:
+               /* do nothink */
+               break;
        }
 
 #if 0
 
 /* Declare static vars that will be used as parameters */
 static unsigned int vid_limit = 16;    /* Video memory limit, in Mb */
 static int video_nr = -1;              /* /dev/videoN, -1 for autodetect */
+static int radio_nr = -1;              /* /dev/radioN, -1 for autodetect */
 
 /* Debug level */
 int tm6000_debug;
 EXPORT_SYMBOL_GPL(tm6000_debug);
 
+static const struct v4l2_queryctrl no_ctrl = {
+       .name  = "42",
+       .flags = V4L2_CTRL_FLAG_DISABLED,
+};
+
 /* supported controls */
 static struct v4l2_queryctrl tm6000_qctrl[] = {
        {
                .step          = 0x1,
                .default_value = 0,
                .flags         = 0,
+       },
+               /* --- audio --- */
+       {
+               .id            = V4L2_CID_AUDIO_MUTE,
+               .name          = "Mute",
+               .minimum       = 0,
+               .maximum       = 1,
+               .type          = V4L2_CTRL_TYPE_BOOLEAN,
+       }, {
+               .id            = V4L2_CID_AUDIO_VOLUME,
+               .name          = "Volume",
+               .minimum       = -15,
+               .maximum       = 15,
+               .step          = 1,
+               .default_value = 0,
+               .type          = V4L2_CTRL_TYPE_INTEGER,
        }
 };
 
+static const unsigned int CTRLS = ARRAY_SIZE(tm6000_qctrl);
 static int qctl_regs[ARRAY_SIZE(tm6000_qctrl)];
 
 static struct tm6000_fmt format[] = {
        }
 };
 
+static const struct v4l2_queryctrl *ctrl_by_id(unsigned int id)
+{
+       unsigned int i;
+
+       for (i = 0; i < CTRLS; i++)
+               if (tm6000_qctrl[i].id == id)
+                       return tm6000_qctrl+i;
+       return NULL;
+}
+
 /* ------------------------------------------------------------------
  *     DMA and thread functions
  * ------------------------------------------------------------------
        char *voutp = NULL;
        unsigned int linewidth;
 
-       /* get video buffer */
-       get_next_buf(dma_q, &vbuf);
-       if (!vbuf)
-               return rc;
-       voutp = videobuf_to_vmalloc(&vbuf->vb);
-       if (!voutp)
-               return 0;
+       if (!dev->radio) {
+               /* get video buffer */
+               get_next_buf(dma_q, &vbuf);
+
+               if (!vbuf)
+                       return rc;
+               voutp = videobuf_to_vmalloc(&vbuf->vb);
+
+               if (!voutp)
+                       return 0;
+       }
 
        for (ptr = data; ptr < endp;) {
                if (!dev->isoc_ctl.cmd) {
                         */
                        switch (cmd) {
                        case TM6000_URB_MSG_VIDEO:
-                               if ((dev->isoc_ctl.vfield != field) &&
-                                       (field == 1)) {
+                               if (!dev->radio) {
+                                       if ((dev->isoc_ctl.vfield != field) &&
+                                               (field == 1)) {
                                        /* Announces that a new buffer
                                         * were filled
                                         */
-                                       buffer_filled(dev, dma_q, vbuf);
-                                       dprintk(dev, V4L2_DEBUG_ISOC,
+                                               buffer_filled(dev, dma_q, vbuf);
+                                               dprintk(dev, V4L2_DEBUG_ISOC,
                                                        "new buffer filled\n");
-                                       get_next_buf(dma_q, &vbuf);
-                                       if (!vbuf)
-                                               return rc;
-                                       voutp = videobuf_to_vmalloc(&vbuf->vb);
-                                       if (!voutp)
-                                               return rc;
-                                       memset(voutp, 0, vbuf->vb.size);
-                               }
-                               linewidth = vbuf->vb.width << 1;
-                               pos = ((line << 1) - field - 1) * linewidth +
-                                       block * TM6000_URB_MSG_LEN;
-                               /* Don't allow to write out of the buffer */
-                               if (pos + size > vbuf->vb.size)
-                                       cmd = TM6000_URB_MSG_ERR;
-                               dev->isoc_ctl.vfield = field;
+                                               get_next_buf(dma_q, &vbuf);
+                                               if (!vbuf)
+                                                       return rc;
+                                               voutp = videobuf_to_vmalloc(&vbuf->vb);
+                                               if (!voutp)
+                                                       return rc;
+                                               memset(voutp, 0, vbuf->vb.size);
+                                       }
+                                       linewidth = vbuf->vb.width << 1;
+                                       pos = ((line << 1) - field - 1) *
+                                       linewidth + block * TM6000_URB_MSG_LEN;
+                                       /* Don't allow to write out of the buffer */
+                                       if (pos + size > vbuf->vb.size)
+                                               cmd = TM6000_URB_MSG_ERR;
+                                       dev->isoc_ctl.vfield = field;
+                       }
                                break;
                        case TM6000_URB_MSG_VBI:
                                break;
 /*
  * Allocate URBs and start IRQ
  */
-static int tm6000_prepare_isoc(struct tm6000_core *dev, unsigned int framesize)
+static int tm6000_prepare_isoc(struct tm6000_core *dev)
 {
        struct tm6000_dmaqueue *dma_q = &dev->vidq;
        int i, j, sb_size, pipe, size, max_packets, num_bufs = 8;
 
        dev->isoc_ctl.max_pkt_size = size;
 
-       max_packets = (framesize + size - 1) / size;
-
-       if (max_packets > TM6000_MAX_ISO_PACKETS)
-               max_packets = TM6000_MAX_ISO_PACKETS;
-
+       max_packets = TM6000_MAX_ISO_PACKETS;
        sb_size = max_packets * size;
 
        dev->isoc_ctl.num_bufs = num_bufs;
                urb_init = 1;
 
        if (urb_init) {
-               rc = tm6000_prepare_isoc(dev, buf->vb.size);
+               rc = tm6000_prepare_isoc(dev);
                if (rc < 0)
                        goto fail;
 
        case V4L2_CID_HUE:
                val = tm6000_get_reg(dev, TM6010_REQ07_R0B_CHROMA_HUE_PHASE_ADJ, 0);
                return 0;
+       case V4L2_CID_AUDIO_MUTE:
+               val = dev->ctl_mute;
+               return 0;
+       case V4L2_CID_AUDIO_VOLUME:
+               val = dev->ctl_volume;
+               return 0;
        default:
                return -EINVAL;
        }
        case V4L2_CID_HUE:
                tm6000_set_reg(dev, TM6010_REQ07_R0B_CHROMA_HUE_PHASE_ADJ, val);
                return 0;
+       case V4L2_CID_AUDIO_MUTE:
+               dev->ctl_mute = val;
+               tm6000_tvaudio_set_mute(dev, val);
+               return 0;
+       case V4L2_CID_AUDIO_VOLUME:
+               dev->ctl_volume = val;
+               tm6000_set_volume(dev, val);
+               return 0;
        }
        return -EINVAL;
 }
        if (unlikely(UNSET == dev->tuner_type))
                return -EINVAL;
 
-       f->type = V4L2_TUNER_ANALOG_TV;
+       f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
        f->frequency = dev->freq;
 
        v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_frequency, f);
        struct tm6000_fh   *fh  = priv;
        struct tm6000_core *dev = fh->dev;
 
-       if (unlikely(f->type != V4L2_TUNER_ANALOG_TV))
-               return -EINVAL;
-
        if (unlikely(UNSET == dev->tuner_type))
                return -EINVAL;
        if (unlikely(f->tuner != 0))
                return -EINVAL;
+       if (0 == fh->radio && V4L2_TUNER_ANALOG_TV != f->type)
+               return -EINVAL;
+       if (1 == fh->radio && V4L2_TUNER_RADIO != f->type)
+               return -EINVAL;
 
        dev->freq = f->frequency;
        v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, f);
        return 0;
 }
 
+static int radio_querycap(struct file *file, void *priv,
+                                       struct v4l2_capability *cap)
+{
+       struct tm6000_fh *fh = file->private_data;
+       struct tm6000_core *dev = fh->dev;
+
+       strcpy(cap->driver, "tm6000");
+       strlcpy(cap->card, dev->name, sizeof(dev->name));
+       sprintf(cap->bus_info, "USB%04x:%04x",
+               le16_to_cpu(dev->udev->descriptor.idVendor),
+               le16_to_cpu(dev->udev->descriptor.idProduct));
+       cap->version = dev->dev_type;
+       cap->capabilities = V4L2_CAP_TUNER;
+
+       return 0;
+}
+
+static int radio_g_tuner(struct file *file, void *priv,
+                                       struct v4l2_tuner *t)
+{
+       struct tm6000_fh *fh = file->private_data;
+       struct tm6000_core *dev = fh->dev;
+
+       if (0 != t->index)
+               return -EINVAL;
+
+       memset(t, 0, sizeof(*t));
+       strcpy(t->name, "Radio");
+       t->type = V4L2_TUNER_RADIO;
+
+       v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t);
+
+       if ((dev->aradio == TM6000_AIP_LINE1) ||
+                               (dev->aradio == TM6000_AIP_LINE2)) {
+               t->rxsubchans = V4L2_TUNER_SUB_MONO;
+       }
+       else {
+               t->rxsubchans = V4L2_TUNER_SUB_STEREO;
+       }
+
+       return 0;
+}
+
+static int radio_s_tuner(struct file *file, void *priv,
+                                       struct v4l2_tuner *t)
+{
+       struct tm6000_fh *fh = file->private_data;
+       struct tm6000_core *dev = fh->dev;
+
+       if (0 != t->index)
+               return -EINVAL;
+
+       v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t);
+
+       return 0;
+}
+
+static int radio_enum_input(struct file *file, void *priv,
+                                       struct v4l2_input *i)
+{
+       if (i->index != 0)
+               return -EINVAL;
+
+       strcpy(i->name, "Radio");
+       i->type = V4L2_INPUT_TYPE_TUNER;
+
+       return 0;
+}
+
+static int radio_g_input(struct file *filp, void *priv, unsigned int *i)
+{
+       *i = 0;
+       return 0;
+}
+
+static int radio_g_audio(struct file *file, void *priv,
+                                       struct v4l2_audio *a)
+{
+       memset(a, 0, sizeof(*a));
+       strcpy(a->name, "Radio");
+       return 0;
+}
+
+static int radio_s_audio(struct file *file, void *priv,
+                                       struct v4l2_audio *a)
+{
+       return 0;
+}
+
+static int radio_s_input(struct file *filp, void *priv, unsigned int i)
+{
+       return 0;
+}
+
+static int radio_s_std(struct file *file, void *fh, v4l2_std_id *norm)
+{
+       return 0;
+}
+
+static int radio_queryctrl(struct file *file, void *priv,
+                                       struct v4l2_queryctrl *c)
+{
+       const struct v4l2_queryctrl *ctrl;
+
+       if (c->id <  V4L2_CID_BASE ||
+           c->id >= V4L2_CID_LASTP1)
+               return -EINVAL;
+       if (c->id == V4L2_CID_AUDIO_MUTE) {
+               ctrl = ctrl_by_id(c->id);
+               *c = *ctrl;
+       } else
+               *c = no_ctrl;
+
+       return 0;
+}
+
 /* ------------------------------------------------------------------
        File operations for the device
    ------------------------------------------------------------------*/
        struct tm6000_fh *fh;
        enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        int i, rc;
+       int radio = 0;
 
        printk(KERN_INFO "tm6000: open called (dev=%s)\n",
                video_device_node_name(vdev));
        dprintk(dev, V4L2_DEBUG_OPEN, "tm6000: open called (dev=%s)\n",
                video_device_node_name(vdev));
 
+       switch (vdev->vfl_type) {
+       case VFL_TYPE_GRABBER:
+               type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+               break;
+       case VFL_TYPE_VBI:
+               type = V4L2_BUF_TYPE_VBI_CAPTURE;
+               break;
+       case VFL_TYPE_RADIO:
+               radio = 1;
+               break;
+       }
 
        /* If more than one user, mutex should be added */
        dev->users++;
 
        file->private_data = fh;
        fh->dev      = dev;
-
-       fh->type     = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       fh->radio    = radio;
+       dev->radio   = radio;
+       fh->type     = type;
        dev->fourcc  = format[0].fourcc;
 
        fh->fmt      = format_by_fourcc(dev->fourcc);
                        V4L2_FIELD_INTERLACED,
                        sizeof(struct tm6000_buffer), fh, &dev->lock);
 
+       if (fh->radio) {
+               dprintk(dev, V4L2_DEBUG_OPEN, "video_open: setting radio device\n");
+               tm6000_set_audio_input(dev, dev->aradio);
+               tm6000_set_volume(dev, dev->ctl_volume);
+               v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_radio);
+               tm6000_prepare_isoc(dev);
+               tm6000_start_thread(dev);
+       }
+       else {
+               tm6000_set_audio_input(dev, dev->avideo);
+               tm6000_set_volume(dev, dev->ctl_volume);
+       }
+
        return 0;
 }
 
        .current_norm   = V4L2_STD_NTSC_M,
 };
 
+static const struct v4l2_file_operations radio_fops = {
+       .owner    = THIS_MODULE,
+       .open     = tm6000_open,
+       .release  = tm6000_release,
+       .ioctl    = video_ioctl2,
+};
+
+static const struct v4l2_ioctl_ops radio_ioctl_ops = {
+       .vidioc_querycap        = radio_querycap,
+       .vidioc_g_tuner         = radio_g_tuner,
+       .vidioc_enum_input      = radio_enum_input,
+       .vidioc_g_audio         = radio_g_audio,
+       .vidioc_s_tuner         = radio_s_tuner,
+       .vidioc_s_audio         = radio_s_audio,
+       .vidioc_s_input         = radio_s_input,
+       .vidioc_s_std           = radio_s_std,
+       .vidioc_queryctrl       = radio_queryctrl,
+       .vidioc_g_input         = radio_g_input,
+       .vidioc_g_ctrl          = vidioc_g_ctrl,
+       .vidioc_s_ctrl          = vidioc_s_ctrl,
+       .vidioc_g_frequency     = vidioc_g_frequency,
+       .vidioc_s_frequency     = vidioc_s_frequency,
+};
+
+struct video_device tm6000_radio_template = {
+       .name                   = "tm6000",
+       .fops                   = &radio_fops,
+       .ioctl_ops              = &radio_ioctl_ops,
+};
+
 /* -----------------------------------------------------------------
  *     Initialization and module stuff
  * ------------------------------------------------------------------
        printk(KERN_INFO "%s: registered device %s\n",
               dev->name, video_device_node_name(dev->vfd));
 
+       dev->radio_dev = vdev_init(dev, &tm6000_radio_template,
+                                                  "radio");
+       if (!dev->radio_dev) {
+               printk(KERN_INFO "%s: can't register radio device\n",
+                      dev->name);
+               return ret; /* FIXME release resource */
+       }
+
+       ret = video_register_device(dev->radio_dev, VFL_TYPE_RADIO,
+                                   radio_nr);
+       if (ret < 0) {
+               printk(KERN_INFO "%s: can't register radio device\n",
+                      dev->name);
+               return ret; /* FIXME release resource */
+       }
+
+       printk(KERN_INFO "%s: registered device %s\n",
+              dev->name, video_device_node_name(dev->radio_dev));
+
        printk(KERN_INFO "Trident TVMaster TM5600/TM6000/TM6010 USB2 board (Load status: %d)\n", ret);
        return ret;
 }
 {
        video_unregister_device(dev->vfd);
 
+       if (dev->radio_dev) {
+               if (video_is_registered(dev->radio_dev))
+                       video_unregister_device(dev->radio_dev);
+               else
+                       video_device_release(dev->radio_dev);
+               dev->radio_dev = NULL;
+       }
+
        return 0;
 }
 
 
        TM6010,
 };
 
+enum tm6000_inaudio {
+       TM6000_AIP_UNK = 0,
+       TM6000_AIP_SIF1,
+       TM6000_AIP_SIF2,
+       TM6000_AIP_LINE1,
+       TM6000_AIP_LINE2,
+};
+
 /* ------------------------------------------------------------------
  *     Basic structures
  * ------------------------------------------------------------------
 
        char                            *ir_codes;
 
+       __u8                            radio;
+
        /* Demodulator configuration */
        int                             demod_addr;     /* demodulator address */
 
        bool                            is_res_read;
 
        struct video_device             *vfd;
+       struct video_device             *radio_dev;
        struct tm6000_dmaqueue          vidq;
        struct v4l2_device              v4l2_dev;
 
 
        enum tm6000_mode                mode;
 
+       int                             ctl_mute;             /* audio */
+       int                             ctl_volume;
+
        /* DVB-T support */
        struct tm6000_dvb               *dvb;
 
        struct snd_tm6000_card          *adev;
        struct work_struct              wq_trigger;   /* Trigger to start/stop audio for alsa module */
        atomic_t                        stream_started;  /* stream should be running if true */
-
+       enum tm6000_inaudio             avideo;
+       enum tm6000_inaudio             aradio;
 
        struct tm6000_IR                *ir;
 
 
 struct tm6000_fh {
        struct tm6000_core           *dev;
+       unsigned int                 radio;
 
        /* video capture */
        struct tm6000_fmt            *fmt;
 int tm6000_get_reg16(struct tm6000_core *dev, u8 req, u16 value, u16 index);
 int tm6000_get_reg32(struct tm6000_core *dev, u8 req, u16 value, u16 index);
 int tm6000_set_reg(struct tm6000_core *dev, u8 req, u16 value, u16 index);
+int tm6000_set_reg_mask(struct tm6000_core *dev, u8 req, u16 value,
+                                               u16 index, u16 mask);
 int tm6000_i2c_reset(struct tm6000_core *dev, u16 tsleep);
 int tm6000_init(struct tm6000_core *dev);
 
 int tm6000_init_analog_mode(struct tm6000_core *dev);
 int tm6000_init_digital_mode(struct tm6000_core *dev);
 int tm6000_set_audio_bitrate(struct tm6000_core *dev, int bitrate);
+int tm6000_set_audio_input(struct tm6000_core *dev, enum tm6000_inaudio ainp);
+int tm6000_tvaudio_set_mute(struct tm6000_core *dev, u8 mute);
+void tm6000_set_volume(struct tm6000_core *dev, int vol);
 
 int tm6000_v4l2_register(struct tm6000_core *dev);
 int tm6000_v4l2_unregister(struct tm6000_core *dev);