Merge remote branch 'alsa/devel' into topic/misc
authorTakashi Iwai <tiwai@suse.de>
Fri, 12 Feb 2010 09:42:38 +0000 (10:42 +0100)
committerTakashi Iwai <tiwai@suse.de>
Fri, 12 Feb 2010 09:42:38 +0000 (10:42 +0100)
1  2 
sound/usb/usbmixer.c

diff --combined sound/usb/usbmixer.c
@@@ -123,6 -123,7 +123,7 @@@ struct usb_mixer_elem_info 
        int channels;
        int val_type;
        int min, max, res;
+       int dBmin, dBmax;
        int cached;
        int cache_val[MAX_CHANNELS];
        u8 initialized;
@@@ -186,21 -187,6 +187,21 @@@ enum 
        USB_PROC_DCR_RELEASE = 6,
  };
  
 +/*E-mu 0202(0404) eXtension Unit(XU) control*/
 +enum {
 +      USB_XU_CLOCK_RATE               = 0xe301,
 +      USB_XU_CLOCK_SOURCE             = 0xe302,
 +      USB_XU_DIGITAL_IO_STATUS        = 0xe303,
 +      USB_XU_DEVICE_OPTIONS           = 0xe304,
 +      USB_XU_DIRECT_MONITORING        = 0xe305,
 +      USB_XU_METERING                 = 0xe306
 +};
 +enum {
 +      USB_XU_CLOCK_SOURCE_SELECTOR = 0x02,    /* clock source*/
 +      USB_XU_CLOCK_RATE_SELECTOR = 0x03,      /* clock rate */
 +      USB_XU_DIGITAL_FORMAT_SELECTOR = 0x01,  /* the spdif format */
 +      USB_XU_SOFT_LIMIT_SELECTOR = 0x03       /* soft limiter */
 +};
  
  /*
   * manual mapping of mixer names
   */
  #include "usbmixer_maps.c"
  
- /* get the mapped name if the unit matches */
static int check_mapped_name(struct mixer_build *state, int unitid, int control, char *buf, int buflen)
+ static const struct usbmix_name_map *
find_map(struct mixer_build *state, int unitid, int control)
  {
-       const struct usbmix_name_map *p;
+       const struct usbmix_name_map *p = state->map;
  
-       if (! state->map)
-               return 0;
+       if (!p)
+               return NULL;
  
        for (p = state->map; p->id; p++) {
-               if (p->id == unitid && p->name &&
-                   (! control || ! p->control || control == p->control)) {
-                       buflen--;
-                       return strlcpy(buf, p->name, buflen);
-               }
+               if (p->id == unitid &&
+                   (!control || !p->control || control == p->control))
+                       return p;
        }
-       return 0;
+       return NULL;
  }
  
- /* check whether the control should be ignored */
- static int check_ignored_ctl(struct mixer_build *state, int unitid, int control)
+ /* get the mapped name if the unit matches */
+ static int
+ check_mapped_name(const struct usbmix_name_map *p, char *buf, int buflen)
  {
-       const struct usbmix_name_map *p;
+       if (!p || !p->name)
+               return 0;
  
-       if (! state->map)
+       buflen--;
+       return strlcpy(buf, p->name, buflen);
+ }
+ /* check whether the control should be ignored */
+ static inline int
+ check_ignored_ctl(const struct usbmix_name_map *p)
+ {
+       if (!p || p->name || p->dB)
                return 0;
-       for (p = state->map; p->id; p++) {
-               if (p->id == unitid && ! p->name &&
-                   (! control || ! p->control || control == p->control)) {
-                       /*
-                       printk(KERN_DEBUG "ignored control %d:%d\n",
-                              unitid, control);
-                       */
-                       return 1;
-               }
+       return 1;
+ }
+ /* dB mapping */
+ static inline void check_mapped_dB(const struct usbmix_name_map *p,
+                                  struct usb_mixer_elem_info *cval)
+ {
+       if (p && p->dB) {
+               cval->dBmin = p->dB->min;
+               cval->dBmax = p->dB->max;
        }
-       return 0;
  }
  
  /* get the mapped selector source name */
@@@ -481,20 -475,8 +490,8 @@@ static int mixer_vol_tlv(struct snd_kco
  
        if (size < sizeof(scale))
                return -ENOMEM;
-       /* USB descriptions contain the dB scale in 1/256 dB unit
-        * while ALSA TLV contains in 1/100 dB unit
-        */
-       scale[2] = (convert_signed_value(cval, cval->min) * 100) / 256;
-       scale[3] = (convert_signed_value(cval, cval->max) * 100) / 256;
-       if (scale[3] <= scale[2]) {
-               /* something is wrong; assume it's either from/to 0dB */
-               if (scale[2] < 0)
-                       scale[3] = 0;
-               else if (scale[2] > 0)
-                       scale[2] = 0;
-               else /* totally crap, return an error */
-                       return -EINVAL;
-       }
+       scale[2] = cval->dBmin;
+       scale[3] = cval->dBmax;
        if (copy_to_user(_tlv, scale, sizeof(scale)))
                return -EFAULT;
        return 0;
@@@ -735,6 -717,7 +732,7 @@@ static int get_min_max(struct usb_mixer
        cval->min = default_min;
        cval->max = cval->min + 1;
        cval->res = 1;
+       cval->dBmin = cval->dBmax = 0;
  
        if (cval->val_type == USB_MIXER_BOOLEAN ||
            cval->val_type == USB_MIXER_INV_BOOLEAN) {
  
                cval->initialized = 1;
        }
+       /* USB descriptions contain the dB scale in 1/256 dB unit
+        * while ALSA TLV contains in 1/100 dB unit
+        */
+       cval->dBmin = (convert_signed_value(cval, cval->min) * 100) / 256;
+       cval->dBmax = (convert_signed_value(cval, cval->max) * 100) / 256;
+       if (cval->dBmin > cval->dBmax) {
+               /* something is wrong; assume it's either from/to 0dB */
+               if (cval->dBmin < 0)
+                       cval->dBmax = 0;
+               else if (cval->dBmin > 0)
+                       cval->dBmin = 0;
+               if (cval->dBmin > cval->dBmax) {
+                       /* totally crap, return an error */
+                       return -EINVAL;
+               }
+       }
        return 0;
  }
  
@@@ -927,6 -928,7 +943,7 @@@ static void build_feature_ctl(struct mi
        int nameid = desc[desc[0] - 1];
        struct snd_kcontrol *kctl;
        struct usb_mixer_elem_info *cval;
+       const struct usbmix_name_map *map;
  
        control++; /* change from zero-based to 1-based value */
  
                return;
        }
  
-       if (check_ignored_ctl(state, unitid, control))
+       map = find_map(state, unitid, control);
+       if (check_ignored_ctl(map))
                return;
  
        cval = kzalloc(sizeof(*cval), GFP_KERNEL);
        }
        kctl->private_free = usb_mixer_elem_free;
  
-       len = check_mapped_name(state, unitid, control, kctl->id.name, sizeof(kctl->id.name));
+       len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name));
        mapped_name = len != 0;
        if (! len && nameid)
-               len = snd_usb_copy_string_desc(state, nameid, kctl->id.name, sizeof(kctl->id.name));
+               len = snd_usb_copy_string_desc(state, nameid,
+                               kctl->id.name, sizeof(kctl->id.name));
  
        switch (control) {
        case USB_FEATURE_MUTE:
                        kctl->vd[0].access |= 
                                SNDRV_CTL_ELEM_ACCESS_TLV_READ |
                                SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
+                       check_mapped_dB(map, cval);
                }
                break;
  
@@@ -1137,8 -1142,10 +1157,10 @@@ static void build_mixer_unit_ctl(struc
        unsigned int num_outs = desc[5 + input_pins];
        unsigned int i, len;
        struct snd_kcontrol *kctl;
+       const struct usbmix_name_map *map;
  
-       if (check_ignored_ctl(state, unitid, 0))
+       map = find_map(state, unitid, 0);
+       if (check_ignored_ctl(map))
                return;
  
        cval = kzalloc(sizeof(*cval), GFP_KERNEL);
        }
        kctl->private_free = usb_mixer_elem_free;
  
-       len = check_mapped_name(state, unitid, 0, kctl->id.name, sizeof(kctl->id.name));
+       len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name));
        if (! len)
                len = get_term_name(state, iterm, kctl->id.name, sizeof(kctl->id.name), 0);
        if (! len)
@@@ -1345,32 -1352,7 +1367,32 @@@ static struct procunit_info procunits[
        { USB_PROC_DCR, "DCR", dcr_proc_info },
        { 0 },
  };
 -
 +/*
 + * predefined data for extension units
 + */
 +static struct procunit_value_info clock_rate_xu_info[] = {
 +       { USB_XU_CLOCK_RATE_SELECTOR, "Selector", USB_MIXER_U8, 0 },
 +       { 0 }
 +};
 +static struct procunit_value_info clock_source_xu_info[] = {
 +      { USB_XU_CLOCK_SOURCE_SELECTOR, "External", USB_MIXER_BOOLEAN },
 +      { 0 }
 +};
 +static struct procunit_value_info spdif_format_xu_info[] = {
 +      { USB_XU_DIGITAL_FORMAT_SELECTOR, "SPDIF/AC3", USB_MIXER_BOOLEAN },
 +      { 0 }
 +};
 +static struct procunit_value_info soft_limit_xu_info[] = {
 +      { USB_XU_SOFT_LIMIT_SELECTOR, " ", USB_MIXER_BOOLEAN },
 +      { 0 }
 +};
 +static struct procunit_info extunits[] = {
 +      { USB_XU_CLOCK_RATE, "Clock rate", clock_rate_xu_info },
 +      { USB_XU_CLOCK_SOURCE, "DigitalIn CLK source", clock_source_xu_info },
 +      { USB_XU_DIGITAL_IO_STATUS, "DigitalOut format:", spdif_format_xu_info },
 +      { USB_XU_DEVICE_OPTIONS, "AnalogueIn Soft Limit", soft_limit_xu_info },
 +      { 0 }
 +};
  /*
   * build a processing/extension unit
   */
@@@ -1382,6 -1364,7 +1404,7 @@@ static int build_audio_procunit(struct 
        int i, err, nameid, type, len;
        struct procunit_info *info;
        struct procunit_value_info *valinfo;
+       const struct usbmix_name_map *map;
        static struct procunit_value_info default_value_info[] = {
                { 0x01, "Switch", USB_MIXER_BOOLEAN },
                { 0 }
                /* FIXME: bitmap might be longer than 8bit */
                if (! (dsc[12 + num_ins] & (1 << (valinfo->control - 1))))
                        continue;
-               if (check_ignored_ctl(state, unitid, valinfo->control))
+               map = find_map(state, unitid, valinfo->control);
+               if (check_ignored_ctl(map))
                        continue;
                cval = kzalloc(sizeof(*cval), GFP_KERNEL);
                if (! cval) {
                        cval->max = dsc[15];
                        cval->res = 1;
                        cval->initialized = 1;
 -              } else
 -                      get_min_max(cval, valinfo->min_value);
 +              } else {
 +                      if (type == USB_XU_CLOCK_RATE) {
 +                              /* E-Mu USB 0404/0202/TrackerPre
 +                               * samplerate control quirk
 +                               */
 +                              cval->min = 0;
 +                              cval->max = 5;
 +                              cval->res = 1;
 +                              cval->initialized = 1;
 +                      } else
 +                              get_min_max(cval, valinfo->min_value);
 +              }
  
                kctl = snd_ctl_new1(&mixer_procunit_ctl, cval);
                if (! kctl) {
                }
                kctl->private_free = usb_mixer_elem_free;
  
-               if (check_mapped_name(state, unitid, cval->control, kctl->id.name, sizeof(kctl->id.name)))
-                       ;
+               if (check_mapped_name(map, kctl->id.name,
+                                               sizeof(kctl->id.name)))
+                       /* nothing */ ;
                else if (info->name)
                        strlcpy(kctl->id.name, info->name, sizeof(kctl->id.name));
                else {
@@@ -1483,7 -1458,7 +1508,7 @@@ static int parse_audio_processing_unit(
  
  static int parse_audio_extension_unit(struct mixer_build *state, int unitid, unsigned char *desc)
  {
 -      return build_audio_procunit(state, unitid, desc, NULL, "Extension Unit");
 +      return build_audio_procunit(state, unitid, desc, extunits, "Extension Unit");
  }
  
  
@@@ -1592,6 -1567,7 +1617,7 @@@ static int parse_audio_selector_unit(st
        int err;
        struct usb_mixer_elem_info *cval;
        struct snd_kcontrol *kctl;
+       const struct usbmix_name_map *map;
        char **namelist;
  
        if (! num_ins || desc[0] < 5 + num_ins) {
        if (num_ins == 1) /* only one ? nonsense! */
                return 0;
  
-       if (check_ignored_ctl(state, unitid, 0))
+       map = find_map(state, unitid, 0);
+       if (check_ignored_ctl(map))
                return 0;
  
        cval = kzalloc(sizeof(*cval), GFP_KERNEL);
        kctl->private_free = usb_mixer_selector_elem_free;
  
        nameid = desc[desc[0] - 1];
-       len = check_mapped_name(state, unitid, 0, kctl->id.name, sizeof(kctl->id.name));
+       len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name));
        if (len)
                ;
        else if (nameid)
@@@ -2159,23 -2136,6 +2186,23 @@@ static int snd_xonar_u1_controls_create
        return 0;
  }
  
 +void snd_emuusb_set_samplerate(struct snd_usb_audio *chip,
 +              unsigned char samplerate_id)
 +{
 +       struct usb_mixer_interface *mixer;
 +       struct usb_mixer_elem_info *cval;
 +       int unitid = 12; /* SamleRate ExtensionUnit ID */
 +
 +       list_for_each_entry(mixer, &chip->mixer_list, list) {
 +               cval = mixer->id_elems[unitid];
 +               if (cval) {
 +                      set_cur_ctl_value(cval, cval->control << 8, samplerate_id);
 +                      snd_usb_mixer_notify_id(mixer, unitid);
 +               }
 +               break;
 +       }
 +}
 +
  int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
                         int ignore_error)
  {