static void ux500_msp_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { int ret; struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); bool is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); dev_dbg(dai->dev, "%s: MSP %d (%s): Enter.\n", __func__, dai->id, snd_pcm_stream_str(substream)); if (drvdata->vape_opp_constraint == 1) { prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP, "ux500_msp_i2s", 50); drvdata->vape_opp_constraint = 0; } if (ux500_msp_i2s_close(drvdata->msp, is_playback ? MSP_DIR_TX : MSP_DIR_RX)) { dev_err(dai->dev, "%s: Error: MSP %d (%s): Unable to close i2s.\n", __func__, dai->id, snd_pcm_stream_str(substream)); } /* Disable and unprepare clocks */ clk_disable_unprepare(drvdata->clk); clk_disable_unprepare(drvdata->pclk); /* Disable regulator */ ret = regulator_disable(drvdata->reg_vape); if (ret < 0) dev_err(dai->dev, "%s: ERROR: Failed to disable regulator (%d)!\n", __func__, ret); }
static int ux500_msp_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { int ret = 0; struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); dev_dbg(dai->dev, "%s: MSP %d (%s): Enter (msp->id = %d, cmd = %d).\n", __func__, dai->id, snd_pcm_stream_str(substream), (int)drvdata->msp->id, cmd); ret = ux500_msp_i2s_trigger(drvdata->msp, cmd, substream->stream); return ret; }
static struct dma_chan *ux500_pcm_request_chan(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_substream *substream) { struct snd_soc_dai *dai = rtd->cpu_dai; struct device *dev = dai->dev; u16 per_data_width, mem_data_width; struct stedma40_chan_cfg *dma_cfg; struct ux500_msp_dma_params *dma_params; dev_dbg(dev, "%s: MSP %d (%s): Enter.\n", __func__, dai->id, snd_pcm_stream_str(substream)); dma_params = snd_soc_dai_get_dma_data(dai, substream); dma_cfg = dma_params->dma_cfg; mem_data_width = STEDMA40_HALFWORD_WIDTH; switch (dma_params->data_size) { case 32: per_data_width = STEDMA40_WORD_WIDTH; break; case 16: per_data_width = STEDMA40_HALFWORD_WIDTH; break; case 8: per_data_width = STEDMA40_BYTE_WIDTH; break; default: per_data_width = STEDMA40_WORD_WIDTH; } if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { dma_cfg->src_info.data_width = mem_data_width; dma_cfg->dst_info.data_width = per_data_width; } else { dma_cfg->src_info.data_width = per_data_width; dma_cfg->dst_info.data_width = mem_data_width; } return snd_dmaengine_pcm_request_channel(stedma40_filter, dma_cfg); }
static int ux500_msp_dai_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { unsigned int mask, slots_active; struct snd_pcm_runtime *runtime = substream->runtime; struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); dev_dbg(dai->dev, "%s: MSP %d (%s): Enter.\n", __func__, dai->id, snd_pcm_stream_str(substream)); switch (drvdata->fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 1, 2); break; case SND_SOC_DAIFMT_DSP_B: case SND_SOC_DAIFMT_DSP_A: mask = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? drvdata->tx_mask : drvdata->rx_mask; slots_active = hweight32(mask); dev_dbg(dai->dev, "TDM-slots active: %d", slots_active); snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, slots_active, slots_active); break; default: dev_err(dai->dev, "%s: Error: Unsupported protocol (fmt = 0x%x)!\n", __func__, drvdata->fmt); return -EINVAL; } return 0; }
static int ux500_msp_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { int ret = 0; struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); dev_dbg(dai->dev, "%s: MSP %d (%s): Enter.\n", __func__, dai->id, snd_pcm_stream_str(substream)); /* Enable regulator */ ret = regulator_enable(drvdata->reg_vape); if (ret != 0) { dev_err(drvdata->msp->dev, "%s: Failed to enable regulator!\n", __func__); return ret; } /* Prepare and enable clocks */ dev_dbg(dai->dev, "%s: Enabling MSP-clocks.\n", __func__); ret = clk_prepare_enable(drvdata->pclk); if (ret) { dev_err(drvdata->msp->dev, "%s: Failed to prepare/enable pclk!\n", __func__); goto err_pclk; } ret = clk_prepare_enable(drvdata->clk); if (ret) { dev_err(drvdata->msp->dev, "%s: Failed to prepare/enable clk!\n", __func__); goto err_clk; } return ret; err_clk: clk_disable_unprepare(drvdata->pclk); err_pclk: regulator_disable(drvdata->reg_vape); return ret; }
static int ux500_msp_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { int ret = 0; struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); dev_dbg(dai->dev, "%s: MSP %d (%s): Enter.\n", __func__, dai->id, snd_pcm_stream_str(substream)); /* Enable regulator */ ret = regulator_enable(drvdata->reg_vape); if (ret != 0) { dev_err(drvdata->msp->dev, "%s: Failed to enable regulator!\n", __func__); return ret; } /* Enable clock */ dev_dbg(dai->dev, "%s: Enabling MSP-clock.\n", __func__); clk_enable(drvdata->clk); return 0; }
static int ux500_msp_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { int ret = 0; struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); struct snd_pcm_runtime *runtime = substream->runtime; struct ux500_msp_config msp_config; dev_dbg(dai->dev, "%s: MSP %d (%s): Enter (rate = %d).\n", __func__, dai->id, snd_pcm_stream_str(substream), runtime->rate); setup_msp_config(substream, dai, &msp_config); ret = ux500_msp_i2s_open(drvdata->msp, &msp_config); if (ret < 0) { dev_err(dai->dev, "%s: Error: msp_setup failed (ret = %d)!\n", __func__, ret); return ret; } /* Set OPP-level */ if ((drvdata->fmt & SND_SOC_DAIFMT_MASTER_MASK) && (drvdata->msp->f_bitclk > 19200000)) { /* If the bit-clock is higher than 19.2MHz, Vape should be * run in 100% OPP. Only when bit-clock is used (MSP master) */ prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP, "ux500-msp-i2s", 100); drvdata->vape_opp_constraint = 1; } else { prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP, "ux500-msp-i2s", 50); drvdata->vape_opp_constraint = 0; } return ret; }
static int ux500_pcm_open(struct snd_pcm_substream *substream) { int stream_id = substream->pstr->stream; struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *dai = rtd->cpu_dai; struct device *dev = dai->dev; int ret; struct ux500_msp_dma_params *dma_params; u16 per_data_width, mem_data_width; struct stedma40_chan_cfg *dma_cfg; dev_dbg(dev, "%s: MSP %d (%s): Enter.\n", __func__, dai->id, snd_pcm_stream_str(substream)); dev_dbg(dev, "%s: Set runtime hwparams.\n", __func__); if (stream_id == SNDRV_PCM_STREAM_PLAYBACK) snd_soc_set_runtime_hwparams(substream, &ux500_pcm_hw_playback); else snd_soc_set_runtime_hwparams(substream, &ux500_pcm_hw_capture); /* ensure that buffer size is a multiple of period size */ ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); if (ret < 0) { dev_err(dev, "%s: Error: snd_pcm_hw_constraints failed (%d)\n", __func__, ret); return ret; } dev_dbg(dev, "%s: Set hw-struct for %s.\n", __func__, snd_pcm_stream_str(substream)); runtime->hw = (stream_id == SNDRV_PCM_STREAM_PLAYBACK) ? ux500_pcm_hw_playback : ux500_pcm_hw_capture; mem_data_width = STEDMA40_HALFWORD_WIDTH; dma_params = snd_soc_dai_get_dma_data(dai, substream); switch (dma_params->data_size) { case 32: per_data_width = STEDMA40_WORD_WIDTH; break; case 16: per_data_width = STEDMA40_HALFWORD_WIDTH; break; case 8: per_data_width = STEDMA40_BYTE_WIDTH; break; default: per_data_width = STEDMA40_WORD_WIDTH; dev_warn(rtd->platform->dev, "%s: Unknown data-size (%d)! Assuming 32 bits.\n", __func__, dma_params->data_size); } dma_cfg = dma_params->dma_cfg; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { dma_cfg->src_info.data_width = mem_data_width; dma_cfg->dst_info.data_width = per_data_width; } else { dma_cfg->src_info.data_width = per_data_width; dma_cfg->dst_info.data_width = mem_data_width; } ret = snd_dmaengine_pcm_open(substream, stedma40_filter, dma_cfg); if (ret) { dev_dbg(dai->dev, "%s: ERROR: snd_dmaengine_pcm_open failed (%d)!\n", __func__, ret); return ret; } snd_dmaengine_pcm_set_data(substream, dma_cfg); return 0; }