Merge branch 'topic/usb-audio' into for-linus
[sfrench/cifs-2.6.git] / sound / usb / usbmixer.c
index 00397c8a765ba62b6f004e5a36d818aa8f25f923..ecb58e7a6245f5bea88294093628a23c54744643 100644 (file)
@@ -66,6 +66,7 @@ static const struct rc_config {
        { USB_ID(0x041e, 0x3000), 0, 1, 2, 1,  18, 0x0013 }, /* Extigy       */
        { USB_ID(0x041e, 0x3020), 2, 1, 6, 6,  18, 0x0013 }, /* Audigy 2 NX  */
        { USB_ID(0x041e, 0x3040), 2, 2, 6, 6,  2,  0x6e91 }, /* Live! 24-bit */
+       { USB_ID(0x041e, 0x3048), 2, 2, 6, 6,  2,  0x6e91 }, /* Toshiba SB0500 */
 };
 
 struct usb_mixer_interface {
@@ -78,7 +79,6 @@ struct usb_mixer_interface {
 
        /* Sound Blaster remote control stuff */
        const struct rc_config *rc_cfg;
-       unsigned long rc_hwdep_open;
        u32 rc_code;
        wait_queue_head_t rc_waitq;
        struct urb *rc_urb;
@@ -110,6 +110,8 @@ struct mixer_build {
        const struct usbmix_selector_map *selector_map;
 };
 
+#define MAX_CHANNELS   10      /* max logical channels */
+
 struct usb_mixer_elem_info {
        struct usb_mixer_interface *mixer;
        struct usb_mixer_elem_info *next_id_elem; /* list of controls with same id */
@@ -120,6 +122,8 @@ struct usb_mixer_elem_info {
        int channels;
        int val_type;
        int min, max, res;
+       int cached;
+       int cache_val[MAX_CHANNELS];
        u8 initialized;
 };
 
@@ -181,8 +185,6 @@ enum {
        USB_PROC_DCR_RELEASE = 6,
 };
 
-#define MAX_CHANNELS   10      /* max logical channels */
-
 
 /*
  * manual mapping of mixer names
@@ -219,7 +221,10 @@ static int check_ignored_ctl(struct mixer_build *state, int unitid, int control)
        for (p = state->map; p->id; p++) {
                if (p->id == unitid && ! p->name &&
                    (! control || ! p->control || control == p->control)) {
-                       // printk("ignored control %d:%d\n", unitid, control);
+                       /*
+                       printk(KERN_DEBUG "ignored control %d:%d\n",
+                              unitid, control);
+                       */
                        return 1;
                }
        }
@@ -376,11 +381,35 @@ static int get_cur_ctl_value(struct usb_mixer_elem_info *cval, int validx, int *
 }
 
 /* channel = 0: master, 1 = first channel */
-static inline int get_cur_mix_value(struct usb_mixer_elem_info *cval, int channel, int *value)
+static inline int get_cur_mix_raw(struct usb_mixer_elem_info *cval,
+                                 int channel, int *value)
 {
        return get_ctl_value(cval, GET_CUR, (cval->control << 8) | channel, value);
 }
 
+static int get_cur_mix_value(struct usb_mixer_elem_info *cval,
+                            int channel, int index, int *value)
+{
+       int err;
+
+       if (cval->cached & (1 << channel)) {
+               *value = cval->cache_val[index];
+               return 0;
+       }
+       err = get_cur_mix_raw(cval, channel, value);
+       if (err < 0) {
+               if (!cval->mixer->ignore_ctl_error)
+                       snd_printd(KERN_ERR "cannot get current value for "
+                                  "control %d ch %d: err = %d\n",
+                                  cval->control, channel, err);
+               return err;
+       }
+       cval->cached |= 1 << channel;
+       cval->cache_val[index] = *value;
+       return 0;
+}
+
+
 /*
  * set a mixer value
  */
@@ -412,9 +441,17 @@ static int set_cur_ctl_value(struct usb_mixer_elem_info *cval, int validx, int v
        return set_ctl_value(cval, SET_CUR, validx, value);
 }
 
-static inline int set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel, int value)
+static int set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel,
+                            int index, int value)
 {
-       return set_ctl_value(cval, SET_CUR, (cval->control << 8) | channel, value);
+       int err;
+       err = set_ctl_value(cval, SET_CUR, (cval->control << 8) | channel,
+                           value);
+       if (err < 0)
+               return err;
+       cval->cached |= 1 << channel;
+       cval->cache_val[index] = value;
+       return 0;
 }
 
 /*
@@ -718,7 +755,7 @@ static int get_min_max(struct usb_mixer_elem_info *cval, int default_min)
                if (cval->min + cval->res < cval->max) {
                        int last_valid_res = cval->res;
                        int saved, test, check;
-                       get_cur_mix_value(cval, minchn, &saved);
+                       get_cur_mix_raw(cval, minchn, &saved);
                        for (;;) {
                                test = saved;
                                if (test < cval->max)
@@ -726,8 +763,8 @@ static int get_min_max(struct usb_mixer_elem_info *cval, int default_min)
                                else
                                        test -= cval->res;
                                if (test < cval->min || test > cval->max ||
-                                   set_cur_mix_value(cval, minchn, test) ||
-                                   get_cur_mix_value(cval, minchn, &check)) {
+                                   set_cur_mix_value(cval, minchn, 0, test) ||
+                                   get_cur_mix_raw(cval, minchn, &check)) {
                                        cval->res = last_valid_res;
                                        break;
                                }
@@ -735,7 +772,7 @@ static int get_min_max(struct usb_mixer_elem_info *cval, int default_min)
                                        break;
                                cval->res *= 2;
                        }
-                       set_cur_mix_value(cval, minchn, saved);
+                       set_cur_mix_value(cval, minchn, 0, saved);
                }
 
                cval->initialized = 1;
@@ -775,35 +812,25 @@ static int mixer_ctl_feature_get(struct snd_kcontrol *kcontrol, struct snd_ctl_e
        struct usb_mixer_elem_info *cval = kcontrol->private_data;
        int c, cnt, val, err;
 
+       ucontrol->value.integer.value[0] = cval->min;
        if (cval->cmask) {
                cnt = 0;
                for (c = 0; c < MAX_CHANNELS; c++) {
-                       if (cval->cmask & (1 << c)) {
-                               err = get_cur_mix_value(cval, c + 1, &val);
-                               if (err < 0) {
-                                       if (cval->mixer->ignore_ctl_error) {
-                                               ucontrol->value.integer.value[0] = cval->min;
-                                               return 0;
-                                       }
-                                       snd_printd(KERN_ERR "cannot get current value for control %d ch %d: err = %d\n", cval->control, c + 1, err);
-                                       return err;
-                               }
-                               val = get_relative_value(cval, val);
-                               ucontrol->value.integer.value[cnt] = val;
-                               cnt++;
-                       }
+                       if (!(cval->cmask & (1 << c)))
+                               continue;
+                       err = get_cur_mix_value(cval, c + 1, cnt, &val);
+                       if (err < 0)
+                               return cval->mixer->ignore_ctl_error ? 0 : err;
+                       val = get_relative_value(cval, val);
+                       ucontrol->value.integer.value[cnt] = val;
+                       cnt++;
                }
+               return 0;
        } else {
                /* master channel */
-               err = get_cur_mix_value(cval, 0, &val);
-               if (err < 0) {
-                       if (cval->mixer->ignore_ctl_error) {
-                               ucontrol->value.integer.value[0] = cval->min;
-                               return 0;
-                       }
-                       snd_printd(KERN_ERR "cannot get current value for control %d master ch: err = %d\n", cval->control, err);
-                       return err;
-               }
+               err = get_cur_mix_value(cval, 0, 0, &val);
+               if (err < 0)
+                       return cval->mixer->ignore_ctl_error ? 0 : err;
                val = get_relative_value(cval, val);
                ucontrol->value.integer.value[0] = val;
        }
@@ -820,34 +847,28 @@ static int mixer_ctl_feature_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
        if (cval->cmask) {
                cnt = 0;
                for (c = 0; c < MAX_CHANNELS; c++) {
-                       if (cval->cmask & (1 << c)) {
-                               err = get_cur_mix_value(cval, c + 1, &oval);
-                               if (err < 0) {
-                                       if (cval->mixer->ignore_ctl_error)
-                                               return 0;
-                                       return err;
-                               }
-                               val = ucontrol->value.integer.value[cnt];
-                               val = get_abs_value(cval, val);
-                               if (oval != val) {
-                                       set_cur_mix_value(cval, c + 1, val);
-                                       changed = 1;
-                               }
-                               get_cur_mix_value(cval, c + 1, &val);
-                               cnt++;
+                       if (!(cval->cmask & (1 << c)))
+                               continue;
+                       err = get_cur_mix_value(cval, c + 1, cnt, &oval);
+                       if (err < 0)
+                               return cval->mixer->ignore_ctl_error ? 0 : err;
+                       val = ucontrol->value.integer.value[cnt];
+                       val = get_abs_value(cval, val);
+                       if (oval != val) {
+                               set_cur_mix_value(cval, c + 1, cnt, val);
+                               changed = 1;
                        }
+                       cnt++;
                }
        } else {
                /* master channel */
-               err = get_cur_mix_value(cval, 0, &oval);
-               if (err < 0 && cval->mixer->ignore_ctl_error)
-                       return 0;
+               err = get_cur_mix_value(cval, 0, 0, &oval);
                if (err < 0)
-                       return err;
+                       return cval->mixer->ignore_ctl_error ? 0 : err;
                val = ucontrol->value.integer.value[0];
                val = get_abs_value(cval, val);
                if (val != oval) {
-                       set_cur_mix_value(cval, 0, val);
+                       set_cur_mix_value(cval, 0, 0, val);
                        changed = 1;
                }
        }
@@ -1706,7 +1727,8 @@ static void snd_usb_mixer_memory_change(struct usb_mixer_interface *mixer,
                break;
        /* live24ext: 4 = line-in jack */
        case 3: /* hp-out jack (may actuate Mute) */
-               if (mixer->chip->usb_id == USB_ID(0x041e, 0x3040))
+               if (mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
+                   mixer->chip->usb_id == USB_ID(0x041e, 0x3048))
                        snd_usb_mixer_notify_id(mixer, mixer->rc_cfg->mute_mixer_id);
                break;
        default:
@@ -1797,24 +1819,6 @@ static void snd_usb_soundblaster_remote_complete(struct urb *urb)
        wake_up(&mixer->rc_waitq);
 }
 
-static int snd_usb_sbrc_hwdep_open(struct snd_hwdep *hw, struct file *file)
-{
-       struct usb_mixer_interface *mixer = hw->private_data;
-
-       if (test_and_set_bit(0, &mixer->rc_hwdep_open))
-               return -EBUSY;
-       return 0;
-}
-
-static int snd_usb_sbrc_hwdep_release(struct snd_hwdep *hw, struct file *file)
-{
-       struct usb_mixer_interface *mixer = hw->private_data;
-
-       clear_bit(0, &mixer->rc_hwdep_open);
-       smp_mb__after_clear_bit();
-       return 0;
-}
-
 static long snd_usb_sbrc_hwdep_read(struct snd_hwdep *hw, char __user *buf,
                                     long count, loff_t *offset)
 {
@@ -1867,9 +1871,8 @@ static int snd_usb_soundblaster_remote_init(struct usb_mixer_interface *mixer)
        hwdep->iface = SNDRV_HWDEP_IFACE_SB_RC;
        hwdep->private_data = mixer;
        hwdep->ops.read = snd_usb_sbrc_hwdep_read;
-       hwdep->ops.open = snd_usb_sbrc_hwdep_open;
-       hwdep->ops.release = snd_usb_sbrc_hwdep_release;
        hwdep->ops.poll = snd_usb_sbrc_hwdep_poll;
+       hwdep->exclusive = 1;
 
        mixer->rc_urb = usb_alloc_urb(0, GFP_KERNEL);
        if (!mixer->rc_urb)
@@ -1956,8 +1959,9 @@ static int snd_audigy2nx_controls_create(struct usb_mixer_interface *mixer)
        int i, err;
 
        for (i = 0; i < ARRAY_SIZE(snd_audigy2nx_controls); ++i) {
-               if (i > 1 &&  /* Live24ext has 2 LEDs only */
-                       mixer->chip->usb_id == USB_ID(0x041e, 0x3040))
+               if (i > 1 && /* Live24ext has 2 LEDs only */
+                       (mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
+                        mixer->chip->usb_id == USB_ID(0x041e, 0x3048)))
                        break; 
                err = snd_ctl_add(mixer->chip->card,
                                  snd_ctl_new1(&snd_audigy2nx_controls[i], mixer));
@@ -1994,7 +1998,8 @@ static void snd_audigy2nx_proc_read(struct snd_info_entry *entry,
        snd_iprintf(buffer, "%s jacks\n\n", mixer->chip->card->shortname);
        if (mixer->chip->usb_id == USB_ID(0x041e, 0x3020))
                jacks = jacks_audigy2nx;
-       else if (mixer->chip->usb_id == USB_ID(0x041e, 0x3040))
+       else if (mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
+                mixer->chip->usb_id == USB_ID(0x041e, 0x3048))
                jacks = jacks_live24ext;
        else
                return;
@@ -2044,7 +2049,8 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
                goto _error;
 
        if (mixer->chip->usb_id == USB_ID(0x041e, 0x3020) ||
-           mixer->chip->usb_id == USB_ID(0x041e, 0x3040)) {
+           mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
+           mixer->chip->usb_id == USB_ID(0x041e, 0x3048)) {
                struct snd_info_entry *entry;
 
                if ((err = snd_audigy2nx_controls_create(mixer)) < 0)