Merge branch 'for-linus/samsung-2635' of git://git.fluff.org/bjdooks/linux
[cascardo/linux.git] / sound / pci / hda / hda_codec.c
index ba2098d..720a81d 100644 (file)
@@ -396,15 +396,18 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
                        }
                        for (n = prev_nid + 1; n <= val; n++) {
                                if (conns >= max_conns) {
-                                       snd_printk(KERN_ERR
-                                                  "Too many connections\n");
+                                       snd_printk(KERN_ERR "hda_codec: "
+                                                  "Too many connections %d for NID 0x%x\n",
+                                                  conns, nid);
                                        return -EINVAL;
                                }
                                conn_list[conns++] = n;
                        }
                } else {
                        if (conns >= max_conns) {
-                               snd_printk(KERN_ERR "Too many connections\n");
+                               snd_printk(KERN_ERR "hda_codec: "
+                                          "Too many connections %d for NID 0x%x\n",
+                                          conns, nid);
                                return -EINVAL;
                        }
                        conn_list[conns++] = val;
@@ -730,15 +733,17 @@ static void /*__devinit*/ setup_fg_nodes(struct hda_codec *codec)
        total_nodes = snd_hda_get_sub_nodes(codec, AC_NODE_ROOT, &nid);
        for (i = 0; i < total_nodes; i++, nid++) {
                function_id = snd_hda_param_read(codec, nid,
-                                               AC_PAR_FUNCTION_TYPE) & 0xff;
-               switch (function_id) {
+                                               AC_PAR_FUNCTION_TYPE);
+               switch (function_id & 0xff) {
                case AC_GRP_AUDIO_FUNCTION:
                        codec->afg = nid;
-                       codec->function_id = function_id;
+                       codec->afg_function_id = function_id & 0xff;
+                       codec->afg_unsol = (function_id >> 8) & 1;
                        break;
                case AC_GRP_MODEM_FUNCTION:
                        codec->mfg = nid;
-                       codec->function_id = function_id;
+                       codec->mfg_function_id = function_id & 0xff;
+                       codec->mfg_unsol = (function_id >> 8) & 1;
                        break;
                default:
                        break;
@@ -965,6 +970,36 @@ static void restore_init_pincfgs(struct hda_codec *codec)
        snd_array_free(&codec->init_pins);
 }
 
+/*
+ * audio-converter setup caches
+ */
+struct hda_cvt_setup {
+       hda_nid_t nid;
+       u8 stream_tag;
+       u8 channel_id;
+       u16 format_id;
+       unsigned char active;   /* cvt is currently used */
+       unsigned char dirty;    /* setups should be cleared */
+};
+
+/* get or create a cache entry for the given audio converter NID */
+static struct hda_cvt_setup *
+get_hda_cvt_setup(struct hda_codec *codec, hda_nid_t nid)
+{
+       struct hda_cvt_setup *p;
+       int i;
+
+       for (i = 0; i < codec->cvt_setups.used; i++) {
+               p = snd_array_elem(&codec->cvt_setups, i);
+               if (p->nid == nid)
+                       return p;
+       }
+       p = snd_array_new(&codec->cvt_setups);
+       if (p)
+               p->nid = nid;
+       return p;
+}
+
 /*
  * codec destructor
  */
@@ -1033,12 +1068,14 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus,
        codec->addr = codec_addr;
        mutex_init(&codec->spdif_mutex);
        mutex_init(&codec->control_mutex);
+       mutex_init(&codec->prepare_mutex);
        init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
        init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
        snd_array_init(&codec->mixers, sizeof(struct hda_nid_item), 32);
        snd_array_init(&codec->nids, sizeof(struct hda_nid_item), 32);
        snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16);
        snd_array_init(&codec->driver_pins, sizeof(struct hda_pincfg), 16);
+       snd_array_init(&codec->cvt_setups, sizeof(struct hda_cvt_setup), 8);
        if (codec->bus->modelname) {
                codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL);
                if (!codec->modelname) {
@@ -1176,16 +1213,51 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
                                u32 stream_tag,
                                int channel_id, int format)
 {
+       struct hda_cvt_setup *p;
+       unsigned int oldval, newval;
+       int i;
+
        if (!nid)
                return;
 
        snd_printdd("hda_codec_setup_stream: "
                    "NID=0x%x, stream=0x%x, channel=%d, format=0x%x\n",
                    nid, stream_tag, channel_id, format);
-       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID,
-                           (stream_tag << 4) | channel_id);
-       msleep(1);
-       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, format);
+       p = get_hda_cvt_setup(codec, nid);
+       if (!p)
+               return;
+       /* update the stream-id if changed */
+       if (p->stream_tag != stream_tag || p->channel_id != channel_id) {
+               oldval = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
+               newval = (stream_tag << 4) | channel_id;
+               if (oldval != newval)
+                       snd_hda_codec_write(codec, nid, 0,
+                                           AC_VERB_SET_CHANNEL_STREAMID,
+                                           newval);
+               p->stream_tag = stream_tag;
+               p->channel_id = channel_id;
+       }
+       /* update the format-id if changed */
+       if (p->format_id != format) {
+               oldval = snd_hda_codec_read(codec, nid, 0,
+                                           AC_VERB_GET_STREAM_FORMAT, 0);
+               if (oldval != format) {
+                       msleep(1);
+                       snd_hda_codec_write(codec, nid, 0,
+                                           AC_VERB_SET_STREAM_FORMAT,
+                                           format);
+               }
+               p->format_id = format;
+       }
+       p->active = 1;
+       p->dirty = 0;
+
+       /* make other inactive cvts with the same stream-tag dirty */
+       for (i = 0; i < codec->cvt_setups.used; i++) {
+               p = snd_array_elem(&codec->cvt_setups, i);
+               if (!p->active && p->stream_tag == stream_tag)
+                       p->dirty = 1;
+       }
 }
 EXPORT_SYMBOL_HDA(snd_hda_codec_setup_stream);
 
@@ -1196,17 +1268,54 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_setup_stream);
  */
 void snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid)
 {
+       struct hda_cvt_setup *p;
+
        if (!nid)
                return;
 
        snd_printdd("hda_codec_cleanup_stream: NID=0x%x\n", nid);
+       /* here we just clear the active flag; actual clean-ups will be done
+        * in purify_inactive_streams()
+        */
+       p = get_hda_cvt_setup(codec, nid);
+       if (p)
+               p->active = 0;
+}
+EXPORT_SYMBOL_HDA(snd_hda_codec_cleanup_stream);
+
+static void really_cleanup_stream(struct hda_codec *codec,
+                                 struct hda_cvt_setup *q)
+{
+       hda_nid_t nid = q->nid;
        snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0);
-#if 0 /* keep the format */
-       msleep(1);
        snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, 0);
-#endif
+       memset(q, 0, sizeof(*q));
+       q->nid = nid;
+}
+
+/* clean up the all conflicting obsolete streams */
+static void purify_inactive_streams(struct hda_codec *codec)
+{
+       int i;
+
+       for (i = 0; i < codec->cvt_setups.used; i++) {
+               struct hda_cvt_setup *p = snd_array_elem(&codec->cvt_setups, i);
+               if (p->dirty)
+                       really_cleanup_stream(codec, p);
+       }
+}
+
+/* clean up all streams; called from suspend */
+static void hda_cleanup_all_streams(struct hda_codec *codec)
+{
+       int i;
+
+       for (i = 0; i < codec->cvt_setups.used; i++) {
+               struct hda_cvt_setup *p = snd_array_elem(&codec->cvt_setups, i);
+               if (p->stream_tag)
+                       really_cleanup_stream(codec, p);
+       }
 }
-EXPORT_SYMBOL_HDA(snd_hda_codec_cleanup_stream);
 
 /*
  * amp access functions
@@ -1565,6 +1674,17 @@ void snd_hda_codec_resume_amp(struct hda_codec *codec)
 EXPORT_SYMBOL_HDA(snd_hda_codec_resume_amp);
 #endif /* SND_HDA_NEEDS_RESUME */
 
+static u32 get_amp_max_value(struct hda_codec *codec, hda_nid_t nid, int dir,
+                            unsigned int ofs)
+{
+       u32 caps = query_amp_caps(codec, nid, dir);
+       /* get num steps */
+       caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
+       if (ofs < caps)
+               caps -= ofs;
+       return caps;
+}
+
 /**
  * snd_hda_mixer_amp_volume_info - Info callback for a standard AMP mixer
  *
@@ -1579,23 +1699,17 @@ int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol,
        u8 chs = get_amp_channels(kcontrol);
        int dir = get_amp_direction(kcontrol);
        unsigned int ofs = get_amp_offset(kcontrol);
-       u32 caps;
 
-       caps = query_amp_caps(codec, nid, dir);
-       /* num steps */
-       caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
-       if (!caps) {
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = chs == 3 ? 2 : 1;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = get_amp_max_value(codec, nid, dir, ofs);
+       if (!uinfo->value.integer.max) {
                printk(KERN_WARNING "hda_codec: "
                       "num_steps = 0 for NID=0x%x (ctl = %s)\n", nid,
                       kcontrol->id.name);
                return -EINVAL;
        }
-       if (ofs < caps)
-               caps -= ofs;
-       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-       uinfo->count = chs == 3 ? 2 : 1;
-       uinfo->value.integer.min = 0;
-       uinfo->value.integer.max = caps;
        return 0;
 }
 EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_info);
@@ -1620,8 +1734,14 @@ update_amp_value(struct hda_codec *codec, hda_nid_t nid,
                 int ch, int dir, int idx, unsigned int ofs,
                 unsigned int val)
 {
+       unsigned int maxval;
+
        if (val > 0)
                val += ofs;
+       /* ofs = 0: raw max value */
+       maxval = get_amp_max_value(codec, nid, dir, 0);
+       if (val > maxval)
+               val = maxval;
        return snd_hda_codec_amp_update(codec, nid, ch, dir, idx,
                                        HDA_AMP_VOLMASK, val);
 }
@@ -2912,6 +3032,7 @@ static void hda_call_codec_suspend(struct hda_codec *codec)
 {
        if (codec->patch_ops.suspend)
                codec->patch_ops.suspend(codec, PMSG_SUSPEND);
+       hda_cleanup_all_streams(codec);
        hda_set_power_state(codec,
                            codec->afg ? codec->afg : codec->mfg,
                            AC_PWRST_D3);
@@ -2999,26 +3120,31 @@ struct hda_rate_tbl {
        unsigned int hda_fmt;
 };
 
+/* rate = base * mult / div */
+#define HDA_RATE(base, mult, div) \
+       (AC_FMT_BASE_##base##K | (((mult) - 1) << AC_FMT_MULT_SHIFT) | \
+        (((div) - 1) << AC_FMT_DIV_SHIFT))
+
 static struct hda_rate_tbl rate_bits[] = {
        /* rate in Hz, ALSA rate bitmask, HDA format value */
 
        /* autodetected value used in snd_hda_query_supported_pcm */
-       { 8000, SNDRV_PCM_RATE_8000, 0x0500 }, /* 1/6 x 48 */
-       { 11025, SNDRV_PCM_RATE_11025, 0x4300 }, /* 1/4 x 44 */
-       { 16000, SNDRV_PCM_RATE_16000, 0x0200 }, /* 1/3 x 48 */
-       { 22050, SNDRV_PCM_RATE_22050, 0x4100 }, /* 1/2 x 44 */
-       { 32000, SNDRV_PCM_RATE_32000, 0x0a00 }, /* 2/3 x 48 */
-       { 44100, SNDRV_PCM_RATE_44100, 0x4000 }, /* 44 */
-       { 48000, SNDRV_PCM_RATE_48000, 0x0000 }, /* 48 */
-       { 88200, SNDRV_PCM_RATE_88200, 0x4800 }, /* 2 x 44 */
-       { 96000, SNDRV_PCM_RATE_96000, 0x0800 }, /* 2 x 48 */
-       { 176400, SNDRV_PCM_RATE_176400, 0x5800 },/* 4 x 44 */
-       { 192000, SNDRV_PCM_RATE_192000, 0x1800 }, /* 4 x 48 */
+       { 8000, SNDRV_PCM_RATE_8000, HDA_RATE(48, 1, 6) },
+       { 11025, SNDRV_PCM_RATE_11025, HDA_RATE(44, 1, 4) },
+       { 16000, SNDRV_PCM_RATE_16000, HDA_RATE(48, 1, 3) },
+       { 22050, SNDRV_PCM_RATE_22050, HDA_RATE(44, 1, 2) },
+       { 32000, SNDRV_PCM_RATE_32000, HDA_RATE(48, 2, 3) },
+       { 44100, SNDRV_PCM_RATE_44100, HDA_RATE(44, 1, 1) },
+       { 48000, SNDRV_PCM_RATE_48000, HDA_RATE(48, 1, 1) },
+       { 88200, SNDRV_PCM_RATE_88200, HDA_RATE(44, 2, 1) },
+       { 96000, SNDRV_PCM_RATE_96000, HDA_RATE(48, 2, 1) },
+       { 176400, SNDRV_PCM_RATE_176400, HDA_RATE(44, 4, 1) },
+       { 192000, SNDRV_PCM_RATE_192000, HDA_RATE(48, 4, 1) },
 #define AC_PAR_PCM_RATE_BITS   11
        /* up to bits 10, 384kHZ isn't supported properly */
 
        /* not autodetected value */
-       { 9600, SNDRV_PCM_RATE_KNOT, 0x0400 }, /* 1/5 x 48 */
+       { 9600, SNDRV_PCM_RATE_KNOT, HDA_RATE(48, 1, 5) },
 
        { 0 } /* terminator */
 };
@@ -3037,7 +3163,8 @@ static struct hda_rate_tbl rate_bits[] = {
 unsigned int snd_hda_calc_stream_format(unsigned int rate,
                                        unsigned int channels,
                                        unsigned int format,
-                                       unsigned int maxbps)
+                                       unsigned int maxbps,
+                                       unsigned short spdif_ctls)
 {
        int i;
        unsigned int val = 0;
@@ -3060,20 +3187,20 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate,
 
        switch (snd_pcm_format_width(format)) {
        case 8:
-               val |= 0x00;
+               val |= AC_FMT_BITS_8;
                break;
        case 16:
-               val |= 0x10;
+               val |= AC_FMT_BITS_16;
                break;
        case 20:
        case 24:
        case 32:
                if (maxbps >= 32 || format == SNDRV_PCM_FORMAT_FLOAT_LE)
-                       val |= 0x40;
+                       val |= AC_FMT_BITS_32;
                else if (maxbps >= 24)
-                       val |= 0x30;
+                       val |= AC_FMT_BITS_24;
                else
-                       val |= 0x20;
+                       val |= AC_FMT_BITS_20;
                break;
        default:
                snd_printdd("invalid format width %d\n",
@@ -3081,6 +3208,9 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate,
                return 0;
        }
 
+       if (spdif_ctls & AC_DIG1_NONAUDIO)
+               val |= AC_FMT_TYPE_NON_PCM;
+
        return val;
 }
 EXPORT_SYMBOL_HDA(snd_hda_calc_stream_format);
@@ -3352,6 +3482,35 @@ static int set_pcm_default_values(struct hda_codec *codec,
        return 0;
 }
 
+/*
+ * codec prepare/cleanup entries
+ */
+int snd_hda_codec_prepare(struct hda_codec *codec,
+                         struct hda_pcm_stream *hinfo,
+                         unsigned int stream,
+                         unsigned int format,
+                         struct snd_pcm_substream *substream)
+{
+       int ret;
+       mutex_lock(&codec->prepare_mutex);
+       ret = hinfo->ops.prepare(hinfo, codec, stream, format, substream);
+       if (ret >= 0)
+               purify_inactive_streams(codec);
+       mutex_unlock(&codec->prepare_mutex);
+       return ret;
+}
+EXPORT_SYMBOL_HDA(snd_hda_codec_prepare);
+
+void snd_hda_codec_cleanup(struct hda_codec *codec,
+                          struct hda_pcm_stream *hinfo,
+                          struct snd_pcm_substream *substream)
+{
+       mutex_lock(&codec->prepare_mutex);
+       hinfo->ops.cleanup(hinfo, codec, substream);
+       mutex_unlock(&codec->prepare_mutex);
+}
+EXPORT_SYMBOL_HDA(snd_hda_codec_cleanup);
+
 /* global */
 const char *snd_hda_pcm_type_name[HDA_PCM_NTYPES] = {
        "Audio", "SPDIF", "HDMI", "Modem"