Merge remote-tracking branches 'asoc/topic/hdmi', 'asoc/topic/intel', 'asoc/topic...
authorMark Brown <broonie@kernel.org>
Mon, 8 Dec 2014 13:12:00 +0000 (13:12 +0000)
committerMark Brown <broonie@kernel.org>
Mon, 8 Dec 2014 13:12:00 +0000 (13:12 +0000)
1  2  3  4  5  6 
sound/soc/intel/sst-haswell-pcm.c
sound/soc/soc-jack.c

index 4df867cbb92a1992154b12bf1f69293accd51367,33fc5c3abf558e52850e46c523702559e203b1bc,f6a9acf6a4ef7ffe1dfddc2449a05cb14f9297b3,33fc5c3abf558e52850e46c523702559e203b1bc,33fc5c3abf558e52850e46c523702559e203b1bc,33fc5c3abf558e52850e46c523702559e203b1bc..0180b386c4211f9b0de80ef2ec82d7bcae2714b5
      #include <linux/dma-mapping.h>
      #include <linux/slab.h>
      #include <linux/delay.h>
++ +++#include <linux/pm_runtime.h>
      #include <asm/page.h>
      #include <asm/pgtable.h>
      #include <sound/core.h>
@@@@@@@ -73,6 -73,6 -74,13 -73,6 -73,6 -73,6 +74,13 @@@@@@@ static const u32 volume_map[] = 
      #define HSW_PCM_PERIODS_MAX       64
      #define HSW_PCM_PERIODS_MIN       2
      
++ +++#define HSW_PCM_DAI_ID_SYSTEM     0
++ +++#define HSW_PCM_DAI_ID_OFFLOAD0   1
++ +++#define HSW_PCM_DAI_ID_OFFLOAD1   2
++ +++#define HSW_PCM_DAI_ID_LOOPBACK   3
++ +++#define HSW_PCM_DAI_ID_CAPTURE    4
++ +++
++ +++
      static const struct snd_pcm_hardware hsw_pcm_hardware = {
        .info                   = SNDRV_PCM_INFO_MMAP |
                                  SNDRV_PCM_INFO_MMAP_VALID |
        .buffer_bytes_max       = HSW_PCM_PERIODS_MAX * PAGE_SIZE,
      };
      
++ +++struct hsw_pcm_module_map {
++ +++  int dai_id;
++ +++  enum sst_hsw_module_id mod_id;
++ +++};
++ +++
      /* private data for each PCM DSP stream */
      struct hsw_pcm_data {
        int dai_id;
        struct sst_hsw_stream *stream;
++ +++  struct sst_module_runtime *runtime;
++ +++  struct sst_module_runtime_context context;
++ +++  struct snd_pcm *hsw_pcm;
        u32 volume[2];
        struct snd_pcm_substream *substream;
        struct snd_compr_stream *cstream;
        unsigned int wpos;
        struct mutex mutex;
        bool allocated;
++ +++  int persistent_offset;
++ +++};
++ +++
++ +++enum hsw_pm_state {
++ +++  HSW_PM_STATE_D3 = 0,
++ +++  HSW_PM_STATE_D0 = 1,
      };
      
      /* private data for the driver */
      struct hsw_priv_data {
        /* runtime DSP */
        struct sst_hsw *hsw;
++ +++  struct device *dev;
++ +++  enum hsw_pm_state pm_state;
++ +++  struct snd_soc_card *soc_card;
      
        /* page tables */
        struct snd_dma_buffer dmab[HSW_PCM_COUNT][2];
@@@@@@@ -138,21 -138,21 -163,25 -138,21 -138,21 -138,21 +163,25 @@@@@@@ static inline unsigned int hsw_ipc_to_m
      static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
      {
-- ---  struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
-- ---  struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(cmpnt);
++ +++  struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
        struct soc_mixer_control *mc =
                (struct soc_mixer_control *)kcontrol->private_value;
++ +++  struct hsw_priv_data *pdata =
++ +++          snd_soc_platform_get_drvdata(platform);
        struct hsw_pcm_data *pcm_data = &pdata->pcm[mc->reg];
        struct sst_hsw *hsw = pdata->hsw;
        u32 volume;
      
        mutex_lock(&pcm_data->mutex);
++ +++  pm_runtime_get_sync(pdata->dev);
      
        if (!pcm_data->stream) {
                pcm_data->volume[0] =
                        hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
                pcm_data->volume[1] =
                        hsw_mixer_to_ipc(ucontrol->value.integer.value[1]);
++ +++          pm_runtime_mark_last_busy(pdata->dev);
++ +++          pm_runtime_put_autosuspend(pdata->dev);
                mutex_unlock(&pcm_data->mutex);
                return 0;
        }
        if (ucontrol->value.integer.value[0] ==
                ucontrol->value.integer.value[1]) {
                volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
-- ---          sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, 2, volume);
++ +++          /* apply volume value to all channels */
++ +++          sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, SST_HSW_CHANNELS_ALL, volume);
        } else {
                volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
                sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, 0, volume);
                sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, 1, volume);
        }
      
++ +++  pm_runtime_mark_last_busy(pdata->dev);
++ +++  pm_runtime_put_autosuspend(pdata->dev);
        mutex_unlock(&pcm_data->mutex);
        return 0;
      }
      static int hsw_stream_volume_get(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
      {
-- ---  struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
-- ---  struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(cmpnt);
++ +++  struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
        struct soc_mixer_control *mc =
                (struct soc_mixer_control *)kcontrol->private_value;
++ +++  struct hsw_priv_data *pdata =
++ +++          snd_soc_platform_get_drvdata(platform);
        struct hsw_pcm_data *pcm_data = &pdata->pcm[mc->reg];
        struct sst_hsw *hsw = pdata->hsw;
        u32 volume;
      
        mutex_lock(&pcm_data->mutex);
++ +++  pm_runtime_get_sync(pdata->dev);
      
        if (!pcm_data->stream) {
                ucontrol->value.integer.value[0] =
                        hsw_ipc_to_mixer(pcm_data->volume[0]);
                ucontrol->value.integer.value[1] =
                        hsw_ipc_to_mixer(pcm_data->volume[1]);
++ +++          pm_runtime_mark_last_busy(pdata->dev);
++ +++          pm_runtime_put_autosuspend(pdata->dev);
                mutex_unlock(&pcm_data->mutex);
                return 0;
        }
        ucontrol->value.integer.value[0] = hsw_ipc_to_mixer(volume);
        sst_hsw_stream_get_volume(hsw, pcm_data->stream, 0, 1, &volume);
        ucontrol->value.integer.value[1] = hsw_ipc_to_mixer(volume);
++ +++
++ +++  pm_runtime_mark_last_busy(pdata->dev);
++ +++  pm_runtime_put_autosuspend(pdata->dev);
        mutex_unlock(&pcm_data->mutex);
      
        return 0;
      static int hsw_volume_put(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
      {
-- ---  struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
-- ---  struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(cmpnt);
++ +++  struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
++ +++  struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform);
        struct sst_hsw *hsw = pdata->hsw;
        u32 volume;
      
++ +++  pm_runtime_get_sync(pdata->dev);
++ +++
        if (ucontrol->value.integer.value[0] ==
                ucontrol->value.integer.value[1]) {
      
                volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
-- ---          sst_hsw_mixer_set_volume(hsw, 0, 2, volume);
++ +++          sst_hsw_mixer_set_volume(hsw, 0, SST_HSW_CHANNELS_ALL, volume);
      
        } else {
                volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
                sst_hsw_mixer_set_volume(hsw, 0, 1, volume);
        }
      
++ +++  pm_runtime_mark_last_busy(pdata->dev);
++ +++  pm_runtime_put_autosuspend(pdata->dev);
        return 0;
      }
      
      static int hsw_volume_get(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
      {
-- ---  struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
-- ---  struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(cmpnt);
++ +++  struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
++ +++  struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform);
        struct sst_hsw *hsw = pdata->hsw;
        unsigned int volume = 0;
      
++ +++  pm_runtime_get_sync(pdata->dev);
        sst_hsw_mixer_get_volume(hsw, 0, 0, &volume);
        ucontrol->value.integer.value[0] = hsw_ipc_to_mixer(volume);
      
        sst_hsw_mixer_get_volume(hsw, 0, 1, &volume);
        ucontrol->value.integer.value[1] = hsw_ipc_to_mixer(volume);
      
++ +++  pm_runtime_mark_last_busy(pdata->dev);
++ +++  pm_runtime_put_autosuspend(pdata->dev);
        return 0;
      }
      
@@@@@@@ -252,23 -252,23 -298,19 -252,23 -252,23 -252,23 +298,19 @@@@@@@ static const DECLARE_TLV_DB_SCALE(hsw_v
      static const struct snd_kcontrol_new hsw_volume_controls[] = {
        /* Global DSP volume */
        SOC_DOUBLE_EXT_TLV("Master Playback Volume", 0, 0, 8,
-- ---          ARRAY_SIZE(volume_map) -1, 0,
++ +++          ARRAY_SIZE(volume_map) - 1, 0,
                hsw_volume_get, hsw_volume_put, hsw_vol_tlv),
        /* Offload 0 volume */
        SOC_DOUBLE_EXT_TLV("Media0 Playback Volume", 1, 0, 8,
-- ---          ARRAY_SIZE(volume_map), 0,
++ +++          ARRAY_SIZE(volume_map) - 1, 0,
                hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv),
        /* Offload 1 volume */
        SOC_DOUBLE_EXT_TLV("Media1 Playback Volume", 2, 0, 8,
-- ---          ARRAY_SIZE(volume_map), 0,
-- ---          hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv),
-- ---  /* Loopback volume */
-- ---  SOC_DOUBLE_EXT_TLV("Loopback Capture Volume", 3, 0, 8,
-- ---          ARRAY_SIZE(volume_map), 0,
++ +++          ARRAY_SIZE(volume_map) - 1, 0,
                hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv),
        /* Mic Capture volume */
-- ---  SOC_DOUBLE_EXT_TLV("Mic Capture Volume", 4, 0, 8,
-- ---          ARRAY_SIZE(volume_map), 0,
++ +++  SOC_DOUBLE_EXT_TLV("Mic Capture Volume", 0, 0, 8,
++ +++          ARRAY_SIZE(volume_map) - 1, 0,
                hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv),
      };
      
@@@@@@@ -354,8 -354,8 -396,14 -354,8 -354,8 -354,8 +396,14 @@@@@@@ static int hsw_pcm_hw_params(struct snd
        /* DSP stream type depends on DAI ID */
        switch (rtd->cpu_dai->id) {
        case 0:
-- ---          stream_type = SST_HSW_STREAM_TYPE_SYSTEM;
-- ---          module_id = SST_HSW_MODULE_PCM_SYSTEM;
++ +++          if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
++ +++                  stream_type = SST_HSW_STREAM_TYPE_SYSTEM;
++ +++                  module_id = SST_HSW_MODULE_PCM_SYSTEM;
++ +++          }
++ +++          else {
++ +++                  stream_type = SST_HSW_STREAM_TYPE_CAPTURE;
++ +++                  module_id = SST_HSW_MODULE_PCM_CAPTURE;
++ +++          }
                break;
        case 1:
        case 2:
                path_id = SST_HSW_STREAM_PATH_SSP0_OUT;
                module_id = SST_HSW_MODULE_PCM_REFERENCE;
                break;
-- ---  case 4:
-- ---          stream_type = SST_HSW_STREAM_TYPE_CAPTURE;
-- ---          module_id = SST_HSW_MODULE_PCM_CAPTURE;
-- ---          break;
        default:
                dev_err(rtd->dev, "error: invalid DAI ID %d\n",
                        rtd->cpu_dai->id);
                return ret;
        }
      
-- ---  /* we only support stereo atm */
        channels = params_channels(params);
-- ---  if (channels != 2) {
-- ---          dev_err(rtd->dev, "error: invalid channels %d\n", channels);
-- ---          return -EINVAL;
-- ---  }
-- ---
        map = create_channel_map(SST_HSW_CHANNEL_CONFIG_STEREO);
        sst_hsw_stream_set_map_config(hsw, pcm_data->stream,
                        map, SST_HSW_CHANNEL_CONFIG_STEREO);
                return -EINVAL;
        }
      
-- ---  /* we use hardcoded memory offsets atm, will be updated for new FW */
-- ---  if (stream_type == SST_HSW_STREAM_TYPE_CAPTURE) {
-- ---          sst_hsw_stream_set_module_info(hsw, pcm_data->stream,
-- ---                  SST_HSW_MODULE_PCM_CAPTURE, module_data->entry);
-- ---          sst_hsw_stream_set_pmemory_info(hsw, pcm_data->stream,
-- ---                  0x449400, 0x4000);
-- ---          sst_hsw_stream_set_smemory_info(hsw, pcm_data->stream,
-- ---                  0x400000, 0);
-- ---  } else { /* stream_type == SST_HSW_STREAM_TYPE_SYSTEM */
-- ---          sst_hsw_stream_set_module_info(hsw, pcm_data->stream,
-- ---                  SST_HSW_MODULE_PCM_SYSTEM, module_data->entry);
-- ---
-- ---          sst_hsw_stream_set_pmemory_info(hsw, pcm_data->stream,
-- ---                  module_data->offset, module_data->size);
-- ---          sst_hsw_stream_set_pmemory_info(hsw, pcm_data->stream,
-- ---                  0x44d400, 0x3800);
-- ---
-- ---          sst_hsw_stream_set_smemory_info(hsw, pcm_data->stream,
-- ---                  module_data->offset, module_data->size);
-- ---          sst_hsw_stream_set_smemory_info(hsw, pcm_data->stream,
-- ---                  0x400000, 0);
-- ---  }
++ +++  sst_hsw_stream_set_module_info(hsw, pcm_data->stream,
++ +++          pcm_data->runtime);
      
        ret = sst_hsw_stream_commit(hsw, pcm_data->stream);
        if (ret < 0) {
                dev_err(rtd->dev, "error: failed to commit stream %d\n", ret);
                return ret;
        }
-- ---  pcm_data->allocated = true;
++ +++
++ +++  if (!pcm_data->allocated) {
++ +++          /* Set previous saved volume */
++ +++          sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0,
++ +++                          0, pcm_data->volume[0]);
++ +++          sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0,
++ +++                          1, pcm_data->volume[1]);
++ +++          pcm_data->allocated = true;
++ +++  }
      
        ret = sst_hsw_stream_pause(hsw, pcm_data->stream, 1);
        if (ret < 0)
@@@@@@@ -558,7 -558,7 -584,7 -558,7 -558,7 -558,7 +584,7 @@@@@@@ static u32 hsw_notify_pointer(struct ss
        pos = frames_to_bytes(runtime,
                (runtime->control->appl_ptr % runtime->buffer_size));
      
-- ---  dev_dbg(rtd->dev, "PCM: App pointer %d bytes\n", pos);
++ +++  dev_vdbg(rtd->dev, "PCM: App pointer %d bytes\n", pos);
      
        /* let alsa know we have play a period */
        snd_pcm_period_elapsed(substream);
@@@@@@@ -580,7 -580,7 -606,7 -580,7 -580,7 -580,7 +606,7 @@@@@@@ static snd_pcm_uframes_t hsw_pcm_pointe
        offset = bytes_to_frames(runtime, position);
        ppos = sst_hsw_get_dsp_presentation_position(hsw, pcm_data->stream);
      
-- ---  dev_dbg(rtd->dev, "PCM: DMA pointer %du bytes, pos %llu\n",
++ +++  dev_vdbg(rtd->dev, "PCM: DMA pointer %du bytes, pos %llu\n",
                position, ppos);
        return offset;
      }
@@@@@@@ -596,6 -596,6 -622,7 -596,6 -596,6 -596,6 +622,7 @@@@@@@ static int hsw_pcm_open(struct snd_pcm_
        pcm_data = &pdata->pcm[rtd->cpu_dai->id];
      
        mutex_lock(&pcm_data->mutex);
++ +++  pm_runtime_get_sync(pdata->dev);
      
        snd_soc_pcm_set_drvdata(rtd, pcm_data);
        pcm_data->substream = substream;
                hsw_notify_pointer, pcm_data);
        if (pcm_data->stream == NULL) {
                dev_err(rtd->dev, "error: failed to create stream\n");
++ +++          pm_runtime_mark_last_busy(pdata->dev);
++ +++          pm_runtime_put_autosuspend(pdata->dev);
                mutex_unlock(&pcm_data->mutex);
                return -EINVAL;
        }
      
-- ---  /* Set previous saved volume */
-- ---  sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0,
-- ---                  0, pcm_data->volume[0]);
-- ---  sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0,
-- ---                  1, pcm_data->volume[1]);
-- ---
        mutex_unlock(&pcm_data->mutex);
        return 0;
      }
@@@@@@@ -645,6 -645,6 -668,8 -645,6 -645,6 -645,6 +668,8 @@@@@@@ static int hsw_pcm_close(struct snd_pcm
        pcm_data->stream = NULL;
      
      out:
++ +++  pm_runtime_mark_last_busy(pdata->dev);
++ +++  pm_runtime_put_autosuspend(pdata->dev);
        mutex_unlock(&pcm_data->mutex);
        return ret;
      }
@@@@@@@ -660,6 -660,6 -685,56 -660,6 -660,6 -660,6 +685,56 @@@@@@@ static struct snd_pcm_ops hsw_pcm_ops 
        .page           = snd_pcm_sgbuf_ops_page,
      };
      
++ +++/* static mappings between PCMs and modules - may be dynamic in future */
++ +++static struct hsw_pcm_module_map mod_map[] = {
++ +++  {HSW_PCM_DAI_ID_SYSTEM, SST_HSW_MODULE_PCM_SYSTEM},
++ +++  {HSW_PCM_DAI_ID_OFFLOAD0, SST_HSW_MODULE_PCM},
++ +++  {HSW_PCM_DAI_ID_OFFLOAD1, SST_HSW_MODULE_PCM},
++ +++  {HSW_PCM_DAI_ID_LOOPBACK, SST_HSW_MODULE_PCM_REFERENCE},
++ +++  {HSW_PCM_DAI_ID_CAPTURE, SST_HSW_MODULE_PCM_CAPTURE},
++ +++};
++ +++
++ +++static int hsw_pcm_create_modules(struct hsw_priv_data *pdata)
++ +++{
++ +++  struct sst_hsw *hsw = pdata->hsw;
++ +++  struct hsw_pcm_data *pcm_data;
++ +++  int i;
++ +++
++ +++  for (i = 0; i < ARRAY_SIZE(mod_map); i++) {
++ +++          pcm_data = &pdata->pcm[i];
++ +++
++ +++          /* create new runtime module, use same offset if recreated */
++ +++          pcm_data->runtime = sst_hsw_runtime_module_create(hsw,
++ +++                  mod_map[i].mod_id, pcm_data->persistent_offset);
++ +++          if (pcm_data->runtime == NULL)
++ +++                  goto err;
++ +++          pcm_data->persistent_offset =
++ +++                  pcm_data->runtime->persistent_offset;
++ +++  }
++ +++
++ +++  return 0;
++ +++
++ +++err:
++ +++  for (--i; i >= 0; i--) {
++ +++          pcm_data = &pdata->pcm[i];
++ +++          sst_hsw_runtime_module_free(pcm_data->runtime);
++ +++  }
++ +++
++ +++  return -ENODEV;
++ +++}
++ +++
++ +++static void hsw_pcm_free_modules(struct hsw_priv_data *pdata)
++ +++{
++ +++  struct hsw_pcm_data *pcm_data;
++ +++  int i;
++ +++
++ +++  for (i = 0; i < ARRAY_SIZE(mod_map); i++) {
++ +++          pcm_data = &pdata->pcm[i];
++ +++
++ +++          sst_hsw_runtime_module_free(pcm_data->runtime);
++ +++  }
++ +++}
++ +++
      static void hsw_pcm_free(struct snd_pcm *pcm)
      {
        snd_pcm_lib_preallocate_free_for_all(pcm);
@@@@@@@ -670,6 -670,6 -745,7 -670,6 -670,6 -670,6 +745,7 @@@@@@@ static int hsw_pcm_new(struct snd_soc_p
        struct snd_pcm *pcm = rtd->pcm;
        struct snd_soc_platform *platform = rtd->platform;
        struct sst_pdata *pdata = dev_get_platdata(platform->dev);
++ +++  struct hsw_priv_data *priv_data = dev_get_drvdata(platform->dev);
        struct device *dev = pdata->dma_dev;
        int ret = 0;
      
                        return ret;
                }
        }
++ +++  priv_data->pcm[rtd->cpu_dai->id].hsw_pcm = pcm;
      
        return ret;
      }
      
      #define HSW_FORMATS \
 -----  (SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE | \
 -----  SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S16_LE |\
 -----  SNDRV_PCM_FMTBIT_S8)
 +++++  (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE)
      
      static struct snd_soc_dai_driver hsw_dais[] = {
        {
                .name  = "System Pin",
++ +++          .id = HSW_PCM_DAI_ID_SYSTEM,
                .playback = {
                        .stream_name = "System Playback",
                        .channels_min = 2,
                        .rates = SNDRV_PCM_RATE_48000,
                        .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
                },
++ +++          .capture = {
++ +++                  .stream_name = "Analog Capture",
++ +++                  .channels_min = 2,
++ +++                  .channels_max = 4,
++ +++                  .rates = SNDRV_PCM_RATE_48000,
++ +++                  .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
++ +++          },
        },
        {
                /* PCM */
                .name  = "Offload0 Pin",
++ +++          .id = HSW_PCM_DAI_ID_OFFLOAD0,
                .playback = {
                        .stream_name = "Offload0 Playback",
                        .channels_min = 2,
        {
                /* PCM */
                .name  = "Offload1 Pin",
++ +++          .id = HSW_PCM_DAI_ID_OFFLOAD1,
                .playback = {
                        .stream_name = "Offload1 Playback",
                        .channels_min = 2,
        },
        {
                .name  = "Loopback Pin",
++ +++          .id = HSW_PCM_DAI_ID_LOOPBACK,
                .capture = {
                        .stream_name = "Loopback Capture",
                        .channels_min = 2,
                        .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
                },
        },
-- ---  {
-- ---          .name  = "Capture Pin",
-- ---          .capture = {
-- ---                  .stream_name = "Analog Capture",
-- ---                  .channels_min = 2,
-- ---                  .channels_max = 2,
-- ---                  .rates = SNDRV_PCM_RATE_48000,
-- ---                  .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
-- ---          },
-- ---  },
      };
      
      static const struct snd_soc_dapm_widget widgets[] = {
@@@@@@@ -776,9 -778,9 -856,20 -778,9 -778,9 -778,9 +854,20 @@@@@@@ static int hsw_pcm_probe(struct snd_soc
      {
        struct hsw_priv_data *priv_data = snd_soc_platform_get_drvdata(platform);
        struct sst_pdata *pdata = dev_get_platdata(platform->dev);
-- ---  struct device *dma_dev = pdata->dma_dev;
++ +++  struct device *dma_dev, *dev;
        int i, ret = 0;
      
++ +++  if (!pdata)
++ +++          return -ENODEV;
++ +++
++ +++  dev = platform->dev;
++ +++  dma_dev = pdata->dma_dev;
++ +++
++ +++  priv_data->hsw = pdata->dsp;
++ +++  priv_data->dev = platform->dev;
++ +++  priv_data->pm_state = HSW_PM_STATE_D0;
++ +++  priv_data->soc_card = platform->component.card;
++ +++
        /* allocate DSP buffer page tables */
        for (i = 0; i < ARRAY_SIZE(hsw_dais); i++) {
      
                }
        }
      
++ +++  /* allocate runtime modules */
++ +++  hsw_pcm_create_modules(priv_data);
++ +++
++ +++  /* enable runtime PM with auto suspend */
++ +++  pm_runtime_set_autosuspend_delay(platform->dev,
++ +++          SST_RUNTIME_SUSPEND_DELAY);
++ +++  pm_runtime_use_autosuspend(platform->dev);
++ +++  pm_runtime_enable(platform->dev);
++ +++  pm_runtime_idle(platform->dev);
++ +++
        return 0;
      
      err:
@@@@@@@ -819,6 -821,6 -920,9 -821,6 -821,6 -821,6 +918,9 @@@@@@@ static int hsw_pcm_remove(struct snd_so
                snd_soc_platform_get_drvdata(platform);
        int i;
      
++ +++  pm_runtime_disable(platform->dev);
++ +++  hsw_pcm_free_modules(priv_data);
++ +++
        for (i = 0; i < ARRAY_SIZE(hsw_dais); i++) {
                if (hsw_dais[i].playback.channels_min)
                        snd_dma_free_pages(&priv_data->dmab[i][0]);
@@@@@@@ -896,10 -898,10 -1000,181 -898,10 -898,10 -898,10 +998,181 @@@@@@@ static int hsw_pcm_dev_remove(struct pl
        return 0;
      }
      
++ +++#ifdef CONFIG_PM_RUNTIME
++ +++
++ +++static int hsw_pcm_runtime_idle(struct device *dev)
++ +++{
++ +++  return 0;
++ +++}
++ +++
++ +++static int hsw_pcm_runtime_suspend(struct device *dev)
++ +++{
++ +++  struct hsw_priv_data *pdata = dev_get_drvdata(dev);
++ +++  struct sst_hsw *hsw = pdata->hsw;
++ +++
++ +++  if (pdata->pm_state == HSW_PM_STATE_D3)
++ +++          return 0;
++ +++
++ +++  sst_hsw_dsp_runtime_suspend(hsw);
++ +++  sst_hsw_dsp_runtime_sleep(hsw);
++ +++  pdata->pm_state = HSW_PM_STATE_D3;
++ +++
++ +++  return 0;
++ +++}
++ +++
++ +++static int hsw_pcm_runtime_resume(struct device *dev)
++ +++{
++ +++  struct hsw_priv_data *pdata = dev_get_drvdata(dev);
++ +++  struct sst_hsw *hsw = pdata->hsw;
++ +++  int ret;
++ +++
++ +++  if (pdata->pm_state == HSW_PM_STATE_D0)
++ +++          return 0;
++ +++
++ +++  ret = sst_hsw_dsp_load(hsw);
++ +++  if (ret < 0) {
++ +++          dev_err(dev, "failed to reload %d\n", ret);
++ +++          return ret;
++ +++  }
++ +++
++ +++  ret = hsw_pcm_create_modules(pdata);
++ +++  if (ret < 0) {
++ +++          dev_err(dev, "failed to create modules %d\n", ret);
++ +++          return ret;
++ +++  }
++ +++
++ +++  ret = sst_hsw_dsp_runtime_resume(hsw);
++ +++  if (ret < 0)
++ +++          return ret;
++ +++  else if (ret == 1) /* no action required */
++ +++          return 0;
++ +++
++ +++  pdata->pm_state = HSW_PM_STATE_D0;
++ +++  return ret;
++ +++}
++ +++
++ +++#else
++ +++#define hsw_pcm_runtime_idle              NULL
++ +++#define hsw_pcm_runtime_suspend           NULL
++ +++#define hsw_pcm_runtime_resume            NULL
++ +++#endif
++ +++
++ +++#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PM_RUNTIME)
++ +++
++ +++static void hsw_pcm_complete(struct device *dev)
++ +++{
++ +++  struct hsw_priv_data *pdata = dev_get_drvdata(dev);
++ +++  struct sst_hsw *hsw = pdata->hsw;
++ +++  struct hsw_pcm_data *pcm_data;
++ +++  int i, err;
++ +++
++ +++  if (pdata->pm_state == HSW_PM_STATE_D0)
++ +++          return;
++ +++
++ +++  err = sst_hsw_dsp_load(hsw);
++ +++  if (err < 0) {
++ +++          dev_err(dev, "failed to reload %d\n", err);
++ +++          return;
++ +++  }
++ +++
++ +++  err = hsw_pcm_create_modules(pdata);
++ +++  if (err < 0) {
++ +++          dev_err(dev, "failed to create modules %d\n", err);
++ +++          return;
++ +++  }
++ +++
++ +++  for (i = 0; i < HSW_PCM_DAI_ID_CAPTURE + 1; i++) {
++ +++          pcm_data = &pdata->pcm[i];
++ +++
++ +++          if (!pcm_data->substream)
++ +++                  continue;
++ +++
++ +++          err = sst_module_runtime_restore(pcm_data->runtime,
++ +++                  &pcm_data->context);
++ +++          if (err < 0)
++ +++                  dev_err(dev, "failed to restore context for PCM %d\n", i);
++ +++  }
++ +++
++ +++  snd_soc_resume(pdata->soc_card->dev);
++ +++
++ +++  err = sst_hsw_dsp_runtime_resume(hsw);
++ +++  if (err < 0)
++ +++          return;
++ +++  else if (err == 1) /* no action required */
++ +++          return;
++ +++
++ +++  pdata->pm_state = HSW_PM_STATE_D0;
++ +++  return;
++ +++}
++ +++
++ +++static int hsw_pcm_prepare(struct device *dev)
++ +++{
++ +++  struct hsw_priv_data *pdata = dev_get_drvdata(dev);
++ +++  struct sst_hsw *hsw = pdata->hsw;
++ +++  struct hsw_pcm_data *pcm_data;
++ +++  int i, err;
++ +++
++ +++  if (pdata->pm_state == HSW_PM_STATE_D3)
++ +++          return 0;
++ +++  /* suspend all active streams */
++ +++  for (i = 0; i < HSW_PCM_DAI_ID_CAPTURE + 1; i++) {
++ +++          pcm_data = &pdata->pcm[i];
++ +++
++ +++          if (!pcm_data->substream)
++ +++                  continue;
++ +++          dev_dbg(dev, "suspending pcm %d\n", i);
++ +++          snd_pcm_suspend_all(pcm_data->hsw_pcm);
++ +++
++ +++          /* We need to wait until the DSP FW stops the streams */
++ +++          msleep(2);
++ +++  }
++ +++
++ +++  snd_soc_suspend(pdata->soc_card->dev);
++ +++  snd_soc_poweroff(pdata->soc_card->dev);
++ +++
++ +++  /* enter D3 state and stall */
++ +++  sst_hsw_dsp_runtime_suspend(hsw);
++ +++
++ +++  /* preserve persistent memory */
++ +++  for (i = 0; i < HSW_PCM_DAI_ID_CAPTURE + 1; i++) {
++ +++          pcm_data = &pdata->pcm[i];
++ +++
++ +++          if (!pcm_data->substream)
++ +++                  continue;
++ +++
++ +++          dev_dbg(dev, "saving context pcm %d\n", i);
++ +++          err = sst_module_runtime_save(pcm_data->runtime,
++ +++                  &pcm_data->context);
++ +++          if (err < 0)
++ +++                  dev_err(dev, "failed to save context for PCM %d\n", i);
++ +++  }
++ +++
++ +++  /* put the DSP to sleep */
++ +++  sst_hsw_dsp_runtime_sleep(hsw);
++ +++  pdata->pm_state = HSW_PM_STATE_D3;
++ +++
++ +++  return 0;
++ +++}
++ +++
++ +++#else
++ +++#define hsw_pcm_prepare           NULL
++ +++#define hsw_pcm_complete  NULL
++ +++#endif
++ +++
++ +++static const struct dev_pm_ops hsw_pcm_pm = {
++ +++  .runtime_idle = hsw_pcm_runtime_idle,
++ +++  .runtime_suspend = hsw_pcm_runtime_suspend,
++ +++  .runtime_resume = hsw_pcm_runtime_resume,
++ +++  .prepare = hsw_pcm_prepare,
++ +++  .complete = hsw_pcm_complete,
++ +++};
++ +++
      static struct platform_driver hsw_pcm_driver = {
        .driver = {
                .name = "haswell-pcm-audio",
                .owner = THIS_MODULE,
++ +++          .pm = &hsw_pcm_pm,
++ +++
        },
      
        .probe = hsw_pcm_dev_probe,
diff --combined sound/soc/soc-jack.c
index ef1d42d7c6f6edd7201136623440aad700604c43,ab47fea997a3cd4d9b8897ab9e2ea6cf495c56dc,ab47fea997a3cd4d9b8897ab9e2ea6cf495c56dc,f921d0098518d0bacd56adf279410aa8822a218b,ab47fea997a3cd4d9b8897ab9e2ea6cf495c56dc,ab47fea997a3cd4d9b8897ab9e2ea6cf495c56dc..4380dcc064a5301a9fa4a077566d6bc450b1d28f
@@@@@@@ -116,7 -116,7 -116,7 -116,7 -116,7 -116,7 +116,7 @@@@@@@ EXPORT_SYMBOL_GPL(snd_soc_jack_report)
       *
       * @jack:  ASoC jack
       * @count: Number of zones
 ----- * @zone:  Array of zones
 +++++ * @zones:  Array of zones
       *
       * After this function has been called the zones specified in the
       * array will be associated with the jack.
@@@@@@@ -309,7 -309,7 -309,7 -309,7 -309,7 -309,7 +309,7 @@@@@@@ int snd_soc_jack_add_gpios(struct snd_s
                        /* GPIO descriptor */
                        gpios[i].desc = gpiod_get_index(gpios[i].gpiod_dev,
                                                        gpios[i].name,
--- --                                                  gpios[i].idx);
+++ ++                                                  gpios[i].idx, GPIOD_IN);
                        if (IS_ERR(gpios[i].desc)) {
                                ret = PTR_ERR(gpios[i].desc);
                                dev_err(gpios[i].gpiod_dev,
                                goto undo;
                        }
      
--- --                  ret = gpio_request(gpios[i].gpio, gpios[i].name);
+++ ++                  ret = gpio_request_one(gpios[i].gpio, GPIOF_IN,
+++ ++                                         gpios[i].name);
                        if (ret)
                                goto undo;
      
                        gpios[i].desc = gpio_to_desc(gpios[i].gpio);
                }
      
--- --          ret = gpiod_direction_input(gpios[i].desc);
--- --          if (ret)
--- --                  goto err;
--- --
                INIT_DELAYED_WORK(&gpios[i].work, gpio_work);
                gpios[i].jack = jack;