Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394...
[pandora-kernel.git] / sound / pci / hda / hda_codec.c
index 23201f3..18bbc87 100644 (file)
@@ -29,6 +29,7 @@
 #include <sound/core.h>
 #include "hda_codec.h"
 #include <sound/asoundef.h>
+#include <sound/tlv.h>
 #include <sound/initval.h>
 #include "hda_local.h"
 
@@ -50,8 +51,10 @@ struct hda_vendor_id {
 /* codec vendor labels */
 static struct hda_vendor_id hda_vendor_ids[] = {
        { 0x10ec, "Realtek" },
+       { 0x1057, "Motorola" },
        { 0x11d4, "Analog Devices" },
        { 0x13f6, "C-Media" },
+       { 0x14f1, "Conexant" },
        { 0x434d, "C-Media" },
        { 0x8384, "SigmaTel" },
        {} /* terminator */
@@ -269,10 +272,11 @@ EXPORT_SYMBOL(snd_hda_queue_unsol_event);
 /*
  * process queueud unsolicited events
  */
-static void process_unsol_events(void *data)
+static void process_unsol_events(struct work_struct *work)
 {
-       struct hda_bus *bus = data;
-       struct hda_bus_unsolicited *unsol = bus->unsol;
+       struct hda_bus_unsolicited *unsol =
+               container_of(work, struct hda_bus_unsolicited, work);
+       struct hda_bus *bus = unsol->bus;
        struct hda_codec *codec;
        unsigned int rp, caddr, res;
 
@@ -311,7 +315,8 @@ static int init_unsol_queue(struct hda_bus *bus)
                kfree(unsol);
                return -ENOMEM;
        }
-       INIT_WORK(&unsol->work, process_unsol_events, bus);
+       INIT_WORK(&unsol->work, process_unsol_events);
+       unsol->bus = bus;
        bus->unsol = unsol;
        return 0;
 }
@@ -841,6 +846,31 @@ int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
        return change;
 }
 
+int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+                         unsigned int size, unsigned int __user *_tlv)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       hda_nid_t nid = get_amp_nid(kcontrol);
+       int dir = get_amp_direction(kcontrol);
+       u32 caps, val1, val2;
+
+       if (size < 4 * sizeof(unsigned int))
+               return -ENOMEM;
+       caps = query_amp_caps(codec, nid, dir);
+       val2 = (((caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT) + 1) * 25;
+       val1 = -((caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT);
+       val1 = ((int)val1) * ((int)val2);
+       if (put_user(SNDRV_CTL_TLVT_DB_SCALE, _tlv))
+               return -EFAULT;
+       if (put_user(2 * sizeof(unsigned int), _tlv + 1))
+               return -EFAULT;
+       if (put_user(val1, _tlv + 2))
+               return -EFAULT;
+       if (put_user(val2, _tlv + 3))
+               return -EFAULT;
+       return 0;
+}
+
 /* switch */
 int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
 {
@@ -1337,9 +1367,6 @@ static struct hda_rate_tbl rate_bits[] = {
        { 176400, SNDRV_PCM_RATE_176400, 0x5800 },/* 4 x 44 */
        { 192000, SNDRV_PCM_RATE_192000, 0x1800 }, /* 4 x 48 */
 
-       /* not autodetected value */
-       { 9600, SNDRV_PCM_RATE_KNOT, 0x0400 }, /* 1/5 x 48 */
-
        { 0 } /* terminator */
 };
 
@@ -1477,10 +1504,10 @@ int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
                                formats |= SNDRV_PCM_FMTBIT_S32_LE;
                                if (val & AC_SUPPCM_BITS_32)
                                        bps = 32;
-                               else if (val & AC_SUPPCM_BITS_20)
-                                       bps = 20;
                                else if (val & AC_SUPPCM_BITS_24)
                                        bps = 24;
+                               else if (val & AC_SUPPCM_BITS_20)
+                                       bps = 20;
                        }
                }
                else if (streams == AC_SUPFMT_FLOAT32) { /* should be exclusive */
@@ -1916,7 +1943,7 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, struct hda_multi_o
 
        /* front */
        snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag, 0, format);
-       if (mout->hp_nid)
+       if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT])
                /* headphone out will just decode front left/right (stereo) */
                snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, 0, format);
        /* extra outputs copied from front */
@@ -1984,7 +2011,7 @@ static int is_in_nid_list(hda_nid_t nid, hda_nid_t *list)
  * in the order of front, rear, CLFE, side, ...
  *
  * If more extra outputs (speaker and headphone) are found, the pins are
- * assisnged to hp_pin and speaker_pins[], respectively.  If no line-out jack
+ * assisnged to hp_pins[] and speaker_pins[], respectively.  If no line-out jack
  * is detected, one of speaker of HP pins is assigned as the primary
  * output, i.e. to line_out_pins[0].  So, line_outs is always positive
  * if any analog output exists.
@@ -2046,14 +2073,26 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, struct auto_pin_cfg *c
                        cfg->speaker_outs++;
                        break;
                case AC_JACK_HP_OUT:
-                       cfg->hp_pin = nid;
+                       if (cfg->hp_outs >= ARRAY_SIZE(cfg->hp_pins))
+                               continue;
+                       cfg->hp_pins[cfg->hp_outs] = nid;
+                       cfg->hp_outs++;
                        break;
-               case AC_JACK_MIC_IN:
-                       if (loc == AC_JACK_LOC_FRONT)
-                               cfg->input_pins[AUTO_PIN_FRONT_MIC] = nid;
-                       else
-                               cfg->input_pins[AUTO_PIN_MIC] = nid;
+               case AC_JACK_MIC_IN: {
+                       int preferred, alt;
+                       if (loc == AC_JACK_LOC_FRONT) {
+                               preferred = AUTO_PIN_FRONT_MIC;
+                               alt = AUTO_PIN_MIC;
+                       } else {
+                               preferred = AUTO_PIN_MIC;
+                               alt = AUTO_PIN_FRONT_MIC;
+                       }
+                       if (!cfg->input_pins[preferred])
+                               cfg->input_pins[preferred] = nid;
+                       else if (!cfg->input_pins[alt])
+                               cfg->input_pins[alt] = nid;
                        break;
+               }
                case AC_JACK_LINE_IN:
                        if (loc == AC_JACK_LOC_FRONT)
                                cfg->input_pins[AUTO_PIN_FRONT_LINE] = nid;
@@ -2119,8 +2158,10 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, struct auto_pin_cfg *c
                   cfg->speaker_outs, cfg->speaker_pins[0],
                   cfg->speaker_pins[1], cfg->speaker_pins[2],
                   cfg->speaker_pins[3], cfg->speaker_pins[4]);
-       snd_printd("   hp=0x%x, dig_out=0x%x, din_in=0x%x\n",
-                  cfg->hp_pin, cfg->dig_out_pin, cfg->dig_in_pin);
+       snd_printd("   hp_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
+                  cfg->hp_outs, cfg->hp_pins[0],
+                  cfg->hp_pins[1], cfg->hp_pins[2],
+                  cfg->hp_pins[3], cfg->hp_pins[4]);
        snd_printd("   inputs: mic=0x%x, fmic=0x%x, line=0x%x, fline=0x%x,"
                   " cd=0x%x, aux=0x%x\n",
                   cfg->input_pins[AUTO_PIN_MIC],
@@ -2141,10 +2182,12 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, struct auto_pin_cfg *c
                               sizeof(cfg->speaker_pins));
                        cfg->speaker_outs = 0;
                        memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins));
-               } else if (cfg->hp_pin) {
-                       cfg->line_outs = 1;
-                       cfg->line_out_pins[0] = cfg->hp_pin;
-                       cfg->hp_pin = 0;
+               } else if (cfg->hp_outs) {
+                       cfg->line_outs = cfg->hp_outs;
+                       memcpy(cfg->line_out_pins, cfg->hp_pins,
+                              sizeof(cfg->hp_pins));
+                       cfg->hp_outs = 0;
+                       memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins));
                }
        }