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;
}
Exemple #7
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;
}