]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - sound/usb/usbmixer.c
Merge branch 'topic/usb-audio' into for-linus
[linux-2.6-omap-h63xx.git] / sound / usb / usbmixer.c
index bc8bd00047ad587272a2c082d4e11d92e0f9b2a3..ecb58e7a6245f5bea88294093628a23c54744643 100644 (file)
@@ -79,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;
@@ -111,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 */
@@ -121,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;
 };
 
@@ -182,8 +185,6 @@ enum {
        USB_PROC_DCR_RELEASE = 6,
 };
 
-#define MAX_CHANNELS   10      /* max logical channels */
-
 
 /*
  * manual mapping of mixer names
@@ -220,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;
                }
        }
@@ -377,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
  */
@@ -413,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;
 }
 
 /*
@@ -719,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)
@@ -727,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;
                                }
@@ -736,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;
@@ -776,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;
        }
@@ -821,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;
                }
        }
@@ -1799,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)
 {
@@ -1869,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)