fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / sound / pci / hda / hda_codec.c
index 4a6dd97..18bbc87 100644 (file)
 #include <linux/slab.h>
 #include <linux/pci.h>
 #include <linux/moduleparam.h>
+#include <linux/mutex.h>
 #include <sound/core.h>
 #include "hda_codec.h"
 #include <sound/asoundef.h>
+#include <sound/tlv.h>
 #include <sound/initval.h>
 #include "hda_local.h"
 
@@ -49,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 */
@@ -76,15 +80,17 @@ unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid, int dire
                                unsigned int verb, unsigned int parm)
 {
        unsigned int res;
-       down(&codec->bus->cmd_mutex);
+       mutex_lock(&codec->bus->cmd_mutex);
        if (! codec->bus->ops.command(codec, nid, direct, verb, parm))
                res = codec->bus->ops.get_response(codec);
        else
                res = (unsigned int)-1;
-       up(&codec->bus->cmd_mutex);
+       mutex_unlock(&codec->bus->cmd_mutex);
        return res;
 }
 
+EXPORT_SYMBOL(snd_hda_codec_read);
+
 /**
  * snd_hda_codec_write - send a single command without waiting for response
  * @codec: the HDA codec
@@ -101,12 +107,14 @@ int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int direct,
                         unsigned int verb, unsigned int parm)
 {
        int err;
-       down(&codec->bus->cmd_mutex);
+       mutex_lock(&codec->bus->cmd_mutex);
        err = codec->bus->ops.command(codec, nid, direct, verb, parm);
-       up(&codec->bus->cmd_mutex);
+       mutex_unlock(&codec->bus->cmd_mutex);
        return err;
 }
 
+EXPORT_SYMBOL(snd_hda_codec_write);
+
 /**
  * snd_hda_sequence_write - sequence writes
  * @codec: the HDA codec
@@ -121,6 +129,8 @@ void snd_hda_sequence_write(struct hda_codec *codec, const struct hda_verb *seq)
                snd_hda_codec_write(codec, seq->nid, 0, seq->verb, seq->param);
 }
 
+EXPORT_SYMBOL(snd_hda_sequence_write);
+
 /**
  * snd_hda_get_sub_nodes - get the range of sub nodes
  * @codec: the HDA codec
@@ -139,6 +149,8 @@ int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid, hda_nid_t *sta
        return (int)(parm & 0x7fff);
 }
 
+EXPORT_SYMBOL(snd_hda_get_sub_nodes);
+
 /**
  * snd_hda_get_connections - get connection list
  * @codec: the HDA codec
@@ -255,13 +267,16 @@ int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex)
        return 0;
 }
 
+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;
 
@@ -294,13 +309,14 @@ static int init_unsol_queue(struct hda_bus *bus)
                snd_printk(KERN_ERR "hda_codec: can't allocate unsolicited queue\n");
                return -ENOMEM;
        }
-       unsol->workq = create_workqueue("hda_codec");
+       unsol->workq = create_singlethread_workqueue("hda_codec");
        if (! unsol->workq) {
                snd_printk(KERN_ERR "hda_codec: can't create workqueue\n");
                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;
 }
@@ -371,7 +387,7 @@ int snd_hda_bus_new(struct snd_card *card, const struct hda_bus_template *temp,
        bus->modelname = temp->modelname;
        bus->ops = temp->ops;
 
-       init_MUTEX(&bus->cmd_mutex);
+       mutex_init(&bus->cmd_mutex);
        INIT_LIST_HEAD(&bus->codec_list);
 
        if ((err = snd_device_new(card, SNDRV_DEV_BUS, bus, &dev_ops)) < 0) {
@@ -383,6 +399,7 @@ int snd_hda_bus_new(struct snd_card *card, const struct hda_bus_template *temp,
        return 0;
 }
 
+EXPORT_SYMBOL(snd_hda_bus_new);
 
 /*
  * find a matching codec preset
@@ -396,7 +413,9 @@ static const struct hda_codec_preset *find_codec_preset(struct hda_codec *codec)
                        u32 mask = preset->mask;
                        if (! mask)
                                mask = ~0;
-                       if (preset->id == (codec->vendor_id & mask))
+                       if (preset->id == (codec->vendor_id & mask) &&
+                           (! preset->rev ||
+                            preset->rev == codec->revision_id))
                                return preset;
                }
        }
@@ -523,13 +542,19 @@ int snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
 
        codec->bus = bus;
        codec->addr = codec_addr;
-       init_MUTEX(&codec->spdif_mutex);
+       mutex_init(&codec->spdif_mutex);
        init_amp_hash(codec);
 
        list_add_tail(&codec->list, &bus->codec_list);
        bus->caddr_tbl[codec_addr] = codec;
 
        codec->vendor_id = snd_hda_param_read(codec, AC_NODE_ROOT, AC_PAR_VENDOR_ID);
+       if (codec->vendor_id == -1)
+               /* read again, hopefully the access method was corrected
+                * in the last read...
+                */
+               codec->vendor_id = snd_hda_param_read(codec, AC_NODE_ROOT,
+                                                     AC_PAR_VENDOR_ID);
        codec->subsystem_id = snd_hda_param_read(codec, AC_NODE_ROOT, AC_PAR_SUBSYSTEM_ID);
        codec->revision_id = snd_hda_param_read(codec, AC_NODE_ROOT, AC_PAR_REV_ID);
 
@@ -580,6 +605,8 @@ int snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
        return 0;
 }
 
+EXPORT_SYMBOL(snd_hda_codec_new);
+
 /**
  * snd_hda_codec_setup_stream - set up the codec for streaming
  * @codec: the CODEC to set up
@@ -602,6 +629,7 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, u32 stre
        snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, format);
 }
 
+EXPORT_SYMBOL(snd_hda_codec_setup_stream);
 
 /*
  * amp access functions
@@ -722,7 +750,8 @@ static void put_vol_mute(struct hda_codec *codec, struct hda_amp_info *info,
 /*
  * read AMP value.  The volume is between 0 to 0x7f, 0x80 = mute bit.
  */
-static int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int index)
+int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch,
+                          int direction, int index)
 {
        struct hda_amp_info *info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, index));
        if (! info)
@@ -733,7 +762,8 @@ static int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch
 /*
  * update the AMP value, mask = bit mask to set, val = the value
  */
-static int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int idx, int mask, int val)
+int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
+                            int direction, int idx, int mask, int val)
 {
        struct hda_amp_info *info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, idx));
 
@@ -816,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)
 {
@@ -881,12 +936,12 @@ int snd_hda_mixer_bind_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_
        unsigned long pval;
        int err;
 
-       down(&codec->spdif_mutex); /* reuse spdif_mutex */
+       mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */
        pval = kcontrol->private_value;
        kcontrol->private_value = pval & ~AMP_VAL_IDX_MASK; /* index 0 */
        err = snd_hda_mixer_amp_switch_get(kcontrol, ucontrol);
        kcontrol->private_value = pval;
-       up(&codec->spdif_mutex);
+       mutex_unlock(&codec->spdif_mutex);
        return err;
 }
 
@@ -896,7 +951,7 @@ int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
        unsigned long pval;
        int i, indices, err = 0, change = 0;
 
-       down(&codec->spdif_mutex); /* reuse spdif_mutex */
+       mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */
        pval = kcontrol->private_value;
        indices = (pval & AMP_VAL_IDX_MASK) >> AMP_VAL_IDX_SHIFT;
        for (i = 0; i < indices; i++) {
@@ -907,7 +962,7 @@ int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
                change |= err;
        }
        kcontrol->private_value = pval;
-       up(&codec->spdif_mutex);
+       mutex_unlock(&codec->spdif_mutex);
        return err < 0 ? err : change;
 }
 
@@ -1011,7 +1066,7 @@ static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol, struct snd_c
        unsigned short val;
        int change;
 
-       down(&codec->spdif_mutex);
+       mutex_lock(&codec->spdif_mutex);
        codec->spdif_status = ucontrol->value.iec958.status[0] |
                ((unsigned int)ucontrol->value.iec958.status[1] << 8) |
                ((unsigned int)ucontrol->value.iec958.status[2] << 16) |
@@ -1026,7 +1081,7 @@ static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol, struct snd_c
                snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_2, val >> 8);
        }
 
-       up(&codec->spdif_mutex);
+       mutex_unlock(&codec->spdif_mutex);
        return change;
 }
 
@@ -1054,7 +1109,7 @@ static int snd_hda_spdif_out_switch_put(struct snd_kcontrol *kcontrol, struct sn
        unsigned short val;
        int change;
 
-       down(&codec->spdif_mutex);
+       mutex_lock(&codec->spdif_mutex);
        val = codec->spdif_ctls & ~1;
        if (ucontrol->value.integer.value[0])
                val |= 1;
@@ -1066,7 +1121,7 @@ static int snd_hda_spdif_out_switch_put(struct snd_kcontrol *kcontrol, struct sn
                                    AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT |
                                    AC_AMP_SET_OUTPUT | ((val & 1) ? 0 : 0x80));
        }
-       up(&codec->spdif_mutex);
+       mutex_unlock(&codec->spdif_mutex);
        return change;
 }
 
@@ -1150,13 +1205,13 @@ static int snd_hda_spdif_in_switch_put(struct snd_kcontrol *kcontrol, struct snd
        unsigned int val = !!ucontrol->value.integer.value[0];
        int change;
 
-       down(&codec->spdif_mutex);
+       mutex_lock(&codec->spdif_mutex);
        change = codec->spdif_in_enable != val;
        if (change || codec->in_resume) {
                codec->spdif_in_enable = val;
                snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, val);
        }
-       up(&codec->spdif_mutex);
+       mutex_unlock(&codec->spdif_mutex);
        return change;
 }
 
@@ -1285,6 +1340,7 @@ int snd_hda_build_controls(struct hda_bus *bus)
        return 0;
 }
 
+EXPORT_SYMBOL(snd_hda_build_controls);
 
 /*
  * stream formats
@@ -1311,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 */
 };
 
@@ -1373,6 +1426,8 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate,
        return val;
 }
 
+EXPORT_SYMBOL(snd_hda_calc_stream_format);
+
 /**
  * snd_hda_query_supported_pcm - query the supported PCM rates and formats
  * @codec: the HDA codec
@@ -1449,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 */
@@ -1654,6 +1709,7 @@ int snd_hda_build_pcms(struct hda_bus *bus)
        return 0;
 }
 
+EXPORT_SYMBOL(snd_hda_build_pcms);
 
 /**
  * snd_hda_check_board_config - compare the current codec with the config table
@@ -1824,13 +1880,13 @@ int snd_hda_input_mux_put(struct hda_codec *codec, const struct hda_input_mux *i
  */
 int snd_hda_multi_out_dig_open(struct hda_codec *codec, struct hda_multi_out *mout)
 {
-       down(&codec->spdif_mutex);
+       mutex_lock(&codec->spdif_mutex);
        if (mout->dig_out_used) {
-               up(&codec->spdif_mutex);
+               mutex_unlock(&codec->spdif_mutex);
                return -EBUSY; /* already being used */
        }
        mout->dig_out_used = HDA_DIG_EXCLUSIVE;
-       up(&codec->spdif_mutex);
+       mutex_unlock(&codec->spdif_mutex);
        return 0;
 }
 
@@ -1839,9 +1895,9 @@ int snd_hda_multi_out_dig_open(struct hda_codec *codec, struct hda_multi_out *mo
  */
 int snd_hda_multi_out_dig_close(struct hda_codec *codec, struct hda_multi_out *mout)
 {
-       down(&codec->spdif_mutex);
+       mutex_lock(&codec->spdif_mutex);
        mout->dig_out_used = 0;
-       up(&codec->spdif_mutex);
+       mutex_unlock(&codec->spdif_mutex);
        return 0;
 }
 
@@ -1869,7 +1925,7 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, struct hda_multi_o
        int chs = substream->runtime->channels;
        int i;
 
-       down(&codec->spdif_mutex);
+       mutex_lock(&codec->spdif_mutex);
        if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) {
                if (chs == 2 &&
                    snd_hda_is_supported_format(codec, mout->dig_out_nid, format) &&
@@ -1883,13 +1939,20 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, struct hda_multi_o
                        snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 0, 0, 0);
                }
        }
-       up(&codec->spdif_mutex);
+       mutex_unlock(&codec->spdif_mutex);
 
        /* 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 */
+       for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
+               if (mout->extra_out_nid[i])
+                       snd_hda_codec_setup_stream(codec,
+                                                  mout->extra_out_nid[i],
+                                                  stream_tag, 0, format);
+
        /* surrounds */
        for (i = 1; i < mout->num_dacs; i++) {
                if (chs >= (i + 1) * 2) /* independent out */
@@ -1914,12 +1977,17 @@ int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec, struct hda_multi_o
                snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0);
        if (mout->hp_nid)
                snd_hda_codec_setup_stream(codec, mout->hp_nid, 0, 0, 0);
-       down(&codec->spdif_mutex);
+       for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
+               if (mout->extra_out_nid[i])
+                       snd_hda_codec_setup_stream(codec,
+                                                  mout->extra_out_nid[i],
+                                                  0, 0, 0);
+       mutex_lock(&codec->spdif_mutex);
        if (mout->dig_out_nid && mout->dig_out_used == HDA_DIG_ANALOG_DUP) {
                snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 0, 0, 0);
                mout->dig_out_used = 0;
        }
-       up(&codec->spdif_mutex);
+       mutex_unlock(&codec->spdif_mutex);
        return 0;
 }
 
@@ -1935,13 +2003,29 @@ static int is_in_nid_list(hda_nid_t nid, hda_nid_t *list)
        return 0;
 }
 
-/* parse all pin widgets and store the useful pin nids to cfg */
+/*
+ * Parse all pin widgets and store the useful pin nids to cfg
+ *
+ * The number of line-outs or any primary output is stored in line_outs,
+ * and the corresponding output pins are assigned to line_out_pins[],
+ * in the order of front, rear, CLFE, side, ...
+ *
+ * If more extra outputs (speaker and headphone) are found, the pins are
+ * 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.
+ * 
+ * The analog input pins are assigned to input_pins array.
+ * The digital input/output pins are assigned to dig_in_pin and dig_out_pin,
+ * respectively.
+ */
 int snd_hda_parse_pin_def_config(struct hda_codec *codec, struct auto_pin_cfg *cfg,
                                 hda_nid_t *ignore_nids)
 {
        hda_nid_t nid, nid_start;
        int i, j, nodes;
-       short seq, sequences[4], assoc_line_out;
+       short seq, assoc_line_out, sequences[ARRAY_SIZE(cfg->line_out_pins)];
 
        memset(cfg, 0, sizeof(*cfg));
 
@@ -1983,17 +2067,32 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, struct auto_pin_cfg *c
                        cfg->line_outs++;
                        break;
                case AC_JACK_SPEAKER:
-                       cfg->speaker_pin = nid;
+                       if (cfg->speaker_outs >= ARRAY_SIZE(cfg->speaker_pins))
+                               continue;
+                       cfg->speaker_pins[cfg->speaker_outs] = nid;
+                       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;
@@ -2048,6 +2147,50 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, struct auto_pin_cfg *c
                break;
        }
 
+       /*
+        * debug prints of the parsed results
+        */
+       snd_printd("autoconfig: line_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
+                  cfg->line_outs, cfg->line_out_pins[0], cfg->line_out_pins[1],
+                  cfg->line_out_pins[2], cfg->line_out_pins[3],
+                  cfg->line_out_pins[4]);
+       snd_printd("   speaker_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
+                  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_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],
+                  cfg->input_pins[AUTO_PIN_FRONT_MIC],
+                  cfg->input_pins[AUTO_PIN_LINE],
+                  cfg->input_pins[AUTO_PIN_FRONT_LINE],
+                  cfg->input_pins[AUTO_PIN_CD],
+                  cfg->input_pins[AUTO_PIN_AUX]);
+
+       /*
+        * FIX-UP: if no line-outs are detected, try to use speaker or HP pin
+        * as a primary output
+        */
+       if (! cfg->line_outs) {
+               if (cfg->speaker_outs) {
+                       cfg->line_outs = cfg->speaker_outs;
+                       memcpy(cfg->line_out_pins, cfg->speaker_pins,
+                              sizeof(cfg->speaker_pins));
+                       cfg->speaker_outs = 0;
+                       memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins));
+               } 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));
+               }
+       }
+
        return 0;
 }
 
@@ -2085,6 +2228,8 @@ int snd_hda_suspend(struct hda_bus *bus, pm_message_t state)
        return 0;
 }
 
+EXPORT_SYMBOL(snd_hda_suspend);
+
 /**
  * snd_hda_resume - resume the codecs
  * @bus: the HDA bus
@@ -2107,6 +2252,8 @@ int snd_hda_resume(struct hda_bus *bus)
        return 0;
 }
 
+EXPORT_SYMBOL(snd_hda_resume);
+
 /**
  * snd_hda_resume_ctls - resume controls in the new control list
  * @codec: the HDA codec
@@ -2166,25 +2313,6 @@ int snd_hda_resume_spdif_in(struct hda_codec *codec)
 }
 #endif
 
-/*
- * symbols exported for controller modules
- */
-EXPORT_SYMBOL(snd_hda_codec_read);
-EXPORT_SYMBOL(snd_hda_codec_write);
-EXPORT_SYMBOL(snd_hda_sequence_write);
-EXPORT_SYMBOL(snd_hda_get_sub_nodes);
-EXPORT_SYMBOL(snd_hda_queue_unsol_event);
-EXPORT_SYMBOL(snd_hda_bus_new);
-EXPORT_SYMBOL(snd_hda_codec_new);
-EXPORT_SYMBOL(snd_hda_codec_setup_stream);
-EXPORT_SYMBOL(snd_hda_calc_stream_format);
-EXPORT_SYMBOL(snd_hda_build_pcms);
-EXPORT_SYMBOL(snd_hda_build_controls);
-#ifdef CONFIG_PM
-EXPORT_SYMBOL(snd_hda_suspend);
-EXPORT_SYMBOL(snd_hda_resume);
-#endif
-
 /*
  *  INIT part
  */