ALSA: hda/ca0132: Add PCM enhancements
authorIan Minett <ian_minett@creativelabs.com>
Fri, 21 Dec 2012 02:53:36 +0000 (18:53 -0800)
committerTakashi Iwai <tiwai@suse.de>
Tue, 15 Jan 2013 15:59:21 +0000 (16:59 +0100)
Remove the playback PCM open callback.
PCM stream setup and cleanup functions are added for use by PCM callbacks.
Delay stream cleanup if effects are on, to allow time for any effects tail to
finish.
Add the analog capture PCM callbacks.
Change the max channels of analog playback to 6.
Add two new PCMs: AMic2 and What-U-Hear.

Signed-off-by: Ian Minett <ian_minett@creativelabs.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/pci/hda/patch_ca0132.c

index 91c4a5017282ec11cc16d45d222f91cbf19e70e3..748fca78131c96befac15c99cd797ac1c2a0da46 100644 (file)
@@ -2630,17 +2630,62 @@ static bool dspload_wait_loaded(struct hda_codec *codec)
        CA0132_CODEC_MUTE_MONO(xname, nid, 3, dir)
 
 /*
- * PCM callbacks
+ * PCM stuffs
  */
-static int ca0132_playback_pcm_open(struct hda_pcm_stream *hinfo,
-                                   struct hda_codec *codec,
-                                   struct snd_pcm_substream *substream)
+static void ca0132_setup_stream(struct hda_codec *codec, hda_nid_t nid,
+                                u32 stream_tag,
+                                int channel_id, int format)
 {
-       struct ca0132_spec *spec = codec->spec;
-       return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
-                                            hinfo);
+       unsigned int oldval, newval;
+
+       if (!nid)
+               return;
+
+       snd_printdd(
+                  "ca0132_setup_stream: NID=0x%x, stream=0x%x, "
+                  "channel=%d, format=0x%x\n",
+                  nid, stream_tag, channel_id, format);
+
+       /* update the format-id if changed */
+       oldval = snd_hda_codec_read(codec, nid, 0,
+                                   AC_VERB_GET_STREAM_FORMAT,
+                                   0);
+       if (oldval != format) {
+               msleep(20);
+               snd_hda_codec_write(codec, nid, 0,
+                                   AC_VERB_SET_STREAM_FORMAT,
+                                   format);
+       }
+
+       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);
+       }
+}
+
+static void ca0132_cleanup_stream(struct hda_codec *codec, hda_nid_t nid)
+{
+       unsigned int val;
+
+       if (!nid)
+               return;
+
+       snd_printdd(KERN_INFO "ca0132_cleanup_stream: NID=0x%x\n", nid);
+
+       val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
+       if (!val)
+               return;
+
+       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, 0);
+       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0);
 }
 
+/*
+ * PCM callbacks
+ */
 static int ca0132_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
                        struct hda_codec *codec,
                        unsigned int stream_tag,
@@ -2648,8 +2693,10 @@ static int ca0132_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
                        struct snd_pcm_substream *substream)
 {
        struct ca0132_spec *spec = codec->spec;
-       return snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
-                                               stream_tag, format, substream);
+
+       ca0132_setup_stream(codec, spec->dacs[0], stream_tag, 0, format);
+
+       return 0;
 }
 
 static int ca0132_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
@@ -2657,7 +2704,18 @@ static int ca0132_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
                        struct snd_pcm_substream *substream)
 {
        struct ca0132_spec *spec = codec->spec;
-       return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
+
+       if (spec->dsp_state == DSP_DOWNLOADING)
+               return 0;
+
+       /*If Playback effects are on, allow stream some time to flush
+        *effects tail*/
+       if (spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID])
+               msleep(50);
+
+       ca0132_cleanup_stream(codec, spec->dacs[0]);
+
+       return 0;
 }
 
 /*
@@ -2698,6 +2756,36 @@ static int ca0132_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
        return snd_hda_multi_out_dig_close(codec, &spec->multiout);
 }
 
+/*
+ * Analog capture
+ */
+static int ca0132_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+                                       struct hda_codec *codec,
+                                       unsigned int stream_tag,
+                                       unsigned int format,
+                                       struct snd_pcm_substream *substream)
+{
+       struct ca0132_spec *spec = codec->spec;
+
+       ca0132_setup_stream(codec, spec->adcs[substream->number],
+                           stream_tag, 0, format);
+
+       return 0;
+}
+
+static int ca0132_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+                       struct hda_codec *codec,
+                       struct snd_pcm_substream *substream)
+{
+       struct ca0132_spec *spec = codec->spec;
+
+       if (spec->dsp_state == DSP_DOWNLOADING)
+               return 0;
+
+       ca0132_cleanup_stream(codec, hinfo->nid);
+       return 0;
+}
+
 /*
  * Select the active output.
  * If autodetect is enabled, output will be selected based on jack detection.
@@ -3509,9 +3597,8 @@ static struct snd_kcontrol_new ca0132_mixer[] = {
 static struct hda_pcm_stream ca0132_pcm_analog_playback = {
        .substreams = 1,
        .channels_min = 2,
-       .channels_max = 2,
+       .channels_max = 6,
        .ops = {
-               .open = ca0132_playback_pcm_open,
                .prepare = ca0132_playback_pcm_prepare,
                .cleanup = ca0132_playback_pcm_cleanup
        },
@@ -3521,6 +3608,10 @@ static struct hda_pcm_stream ca0132_pcm_analog_capture = {
        .substreams = 1,
        .channels_min = 2,
        .channels_max = 2,
+       .ops = {
+               .prepare = ca0132_capture_pcm_prepare,
+               .cleanup = ca0132_capture_pcm_cleanup
+       },
 };
 
 static struct hda_pcm_stream ca0132_pcm_digital_playback = {
@@ -3555,10 +3646,24 @@ static int ca0132_build_pcms(struct hda_codec *codec)
        info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
                spec->multiout.max_channels;
        info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture;
-       info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_inputs;
+       info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1;
        info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0];
        codec->num_pcms++;
 
+       info++;
+       info->name = "CA0132 Analog Mic-In2";
+       info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture;
+       info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1;
+       info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[1];
+       codec->num_pcms++;
+
+       info++;
+       info->name = "CA0132 What U Hear";
+       info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture;
+       info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1;
+       info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[2];
+       codec->num_pcms++;
+
        if (!spec->dig_out && !spec->dig_in)
                return 0;