コード例 #1
0
/**
 * fsl_ssi_startup: create a new substream
 *
 * This is the first function called when a stream is opened.
 *
 * If this is the first stream open, then grab the IRQ and program most of
 * the SSI registers.
 */
static int fsl_ssi_startup(struct snd_pcm_substream *substream,
			   struct snd_soc_dai *dai)
{
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
	struct fsl_ssi_private *ssi_private =
		snd_soc_dai_get_drvdata(rtd->cpu_dai);
	int synchronous = ssi_private->cpu_dai_drv.symmetric_rates;
	unsigned long flags;

	if (ssi_private->ssi_on_imx) {
		pm_runtime_get_sync(dai->dev);

		clk_prepare_enable(ssi_private->coreclk);
		clk_prepare_enable(ssi_private->clk);

		/* When using dual fifo mode, it would be safer if we ensure
		 * its period size to be an even number. If appearing to an
		 * odd number, the 2nd fifo might be neglected by SDMA sciprt
		 * at the end of each period.
		 */
		if (ssi_private->use_dual_fifo)
			snd_pcm_hw_constraint_step(substream->runtime, 0,
					SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 2);
	}

	/*
	 * If this is the first stream opened, then request the IRQ
	 * and initialize the SSI registers.
	 */
	if (!dai->active) {
		struct ccsr_ssi __iomem *ssi = ssi_private->ssi;

		/*
		 * Section 16.5 of the MPC8610 reference manual says that the
		 * SSI needs to be disabled before updating the registers we set
		 * here.
		 */
		write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0);

		/*
		 * Program the SSI into I2S Slave Non-Network Synchronous mode.
		 * Also enable the transmit and receive FIFO.
		 *
		 * FIXME: Little-endian samples require a different shift dir
		 */
		ssi_private->i2s_mode = CCSR_SSI_SCR_I2S_MODE_SLAVE;
		write_ssi_mask(&ssi->scr,
			CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_SYN,
			CCSR_SSI_SCR_TFR_CLK_DIS | ssi_private->i2s_mode
			| (synchronous ? CCSR_SSI_SCR_SYN : 0));

		write_ssi(CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 |
			 CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TEFS |
			 CCSR_SSI_STCR_TSCKP, &ssi->stcr);

		write_ssi(CCSR_SSI_SRCR_RXBIT0 | CCSR_SSI_SRCR_RFEN0 |
			 CCSR_SSI_SRCR_RFSI | CCSR_SSI_SRCR_REFS |
			 CCSR_SSI_SRCR_RSCKP, &ssi->srcr);

		/*
		 * The DC and PM bits are only used if the SSI is the clock
		 * master.
		 */

		/* Enable the interrupts and DMA requests */
		write_ssi(SIER_FLAGS, &ssi->sier);

		/*
		 * Set the watermark for transmit FIFI 0 and receive FIFO 0. We
		 * don't use FIFO 1.  We program the transmit water to signal a
		 * DMA transfer if there are only two (or fewer) elements left
		 * in the FIFO.  Two elements equals one frame (left channel,
		 * right channel).  This value, however, depends on the depth of
		 * the transmit buffer.
		 *
		 * We program the receive FIFO to notify us if at least two
		 * elements (one frame) have been written to the FIFO.  We could
		 * make this value larger (and maybe we should), but this way
		 * data will be written to memory as soon as it's available.
		 */
		write_ssi(CCSR_SSI_SFCSR_TFWM0(ssi_private->fifo_depth - 2) |
			CCSR_SSI_SFCSR_RFWM0(ssi_private->fifo_depth - 2) |
			CCSR_SSI_SFCSR_TFWM1(ssi_private->fifo_depth - 2) |
			CCSR_SSI_SFCSR_RFWM1(ssi_private->fifo_depth - 2),
			&ssi->sfcsr);

		/* Select Single/Dual fifo mode */
		if (ssi_private->use_dual_fifo) {
			write_ssi_mask(&ssi->srcr, 0, CCSR_SSI_SRCR_RFEN1);
			write_ssi_mask(&ssi->stcr, 0, CCSR_SSI_STCR_TFEN1);
			write_ssi_mask(&ssi->scr, 0, CCSR_SSI_SCR_TCH_EN);
		} else {
			write_ssi_mask(&ssi->srcr, CCSR_SSI_SRCR_RFEN1, 0);
			write_ssi_mask(&ssi->stcr, CCSR_SSI_STCR_TFEN1, 0);
			write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_TCH_EN, 0);
		}

		/*
		 * We keep the SSI disabled because if we enable it, then the
		 * DMA controller will start.  It's not supposed to start until
		 * the SCR.TE (or SCR.RE) bit is set, but it does anyway.  The
		 * DMA controller will transfer one "BWC" of data (i.e. the
		 * amount of data that the MR.BWC bits are set to).  The reason
		 * this is bad is because at this point, the PCM driver has not
		 * finished initializing the DMA controller.
		 */

		/* Set default slot number -- 2 */
		write_ssi_mask(&ssi->stccr, CCSR_SSI_SxCCR_DC_MASK,
				CCSR_SSI_SxCCR_DC(2));
		write_ssi_mask(&ssi->srccr, CCSR_SSI_SxCCR_DC_MASK,
				CCSR_SSI_SxCCR_DC(2));

		spin_lock_irqsave(&ssi_private->baudclk_lock, flags);
		ssi_private->baudclk_locked = false;
		spin_unlock_irqrestore(&ssi_private->baudclk_lock, flags);
	}

	return 0;
}
コード例 #2
0
ファイル: kirkwood-i2s.c プロジェクト: Scorpio92/mstar6a918
static int kirkwood_i2s_rec_trigger(struct snd_pcm_substream *substream,
                                    int cmd, struct snd_soc_dai *dai)
{
    struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai);
    unsigned long value;

    value = readl(priv->io + KIRKWOOD_RECCTL);

    switch (cmd) {
    case SNDRV_PCM_TRIGGER_START:
        /* stop audio, enable interrupts */
        value = readl(priv->io + KIRKWOOD_RECCTL);
        value |= KIRKWOOD_RECCTL_PAUSE;
        writel(value, priv->io + KIRKWOOD_RECCTL);

        value = readl(priv->io + KIRKWOOD_INT_MASK);
        value |= KIRKWOOD_INT_CAUSE_REC_BYTES;
        writel(value, priv->io + KIRKWOOD_INT_MASK);

        /* configure audio & enable i2s record */
        value = readl(priv->io + KIRKWOOD_RECCTL);
        value &= ~KIRKWOOD_RECCTL_BURST_MASK;
        value &= ~KIRKWOOD_RECCTL_MONO;
        value &= ~(KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE
                   | KIRKWOOD_RECCTL_SPDIF_EN);

        if (priv->burst == 32)
            value |= KIRKWOOD_RECCTL_BURST_32;
        else
            value |= KIRKWOOD_RECCTL_BURST_128;
        value |= KIRKWOOD_RECCTL_I2S_EN;

        writel(value, priv->io + KIRKWOOD_RECCTL);
        break;

    case SNDRV_PCM_TRIGGER_STOP:
        /* stop audio, disable interrupts */
        value = readl(priv->io + KIRKWOOD_RECCTL);
        value |= KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE;
        writel(value, priv->io + KIRKWOOD_RECCTL);

        value = readl(priv->io + KIRKWOOD_INT_MASK);
        value &= ~KIRKWOOD_INT_CAUSE_REC_BYTES;
        writel(value, priv->io + KIRKWOOD_INT_MASK);

        /* disable all records */
        value = readl(priv->io + KIRKWOOD_RECCTL);
        value &= ~(KIRKWOOD_RECCTL_I2S_EN | KIRKWOOD_RECCTL_SPDIF_EN);
        writel(value, priv->io + KIRKWOOD_RECCTL);
        break;

    case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
    case SNDRV_PCM_TRIGGER_SUSPEND:
        value = readl(priv->io + KIRKWOOD_RECCTL);
        value |= KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE;
        writel(value, priv->io + KIRKWOOD_RECCTL);
        break;

    case SNDRV_PCM_TRIGGER_RESUME:
    case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
        value = readl(priv->io + KIRKWOOD_RECCTL);
        value &= ~(KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE);
        writel(value, priv->io + KIRKWOOD_RECCTL);
        break;

    default:
        return -EINVAL;
    }

    return 0;
}
コード例 #3
0
/**
 * This function is used to calculate the divisors of psr, pm, fp and it is
 * supposed to be called in set_dai_sysclk() and set_bclk().
 *
 * @ratio: desired overall ratio for the paticipating dividers
 * @usefp: for HCK setting, there is no need to set fp divider
 * @fp: bypass other dividers by setting fp directly if fp != 0
 * @tx: current setting is for playback or capture
 */
static int fsl_esai_divisor_cal(struct snd_soc_dai *dai, bool tx, u32 ratio,
                                bool usefp, u32 fp)
{
    struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
    u32 psr, pm = 999, maxfp, prod, sub, savesub, i, j;

    maxfp = usefp ? 16 : 1;

    if (usefp && fp)
        goto out_fp;

    if (ratio > 2 * 8 * 256 * maxfp || ratio < 2) {
        dev_err(dai->dev, "the ratio is out of range (2 ~ %d)\n",
                2 * 8 * 256 * maxfp);
        return -EINVAL;
    } else if (ratio % 2) {
        dev_err(dai->dev, "the raio must be even if using upper divider\n");
        return -EINVAL;
    }

    ratio /= 2;

    psr = ratio <= 256 * maxfp ? ESAI_xCCR_xPSR_BYPASS : ESAI_xCCR_xPSR_DIV8;

    /* Set the max fluctuation -- 0.1% of the max devisor */
    savesub = (psr ? 1 : 8)  * 256 * maxfp / 1000;

    /* Find the best value for PM */
    for (i = 1; i <= 256; i++) {
        for (j = 1; j <= maxfp; j++) {
            /* PSR (1 or 8) * PM (1 ~ 256) * FP (1 ~ 16) */
            prod = (psr ? 1 : 8) * i * j;

            if (prod == ratio)
                sub = 0;
            else if (prod / ratio == 1)
                sub = prod - ratio;
            else if (ratio / prod == 1)
                sub = ratio - prod;
            else
                continue;

            /* Calculate the fraction */
            sub = sub * 1000 / ratio;
            if (sub < savesub) {
                savesub = sub;
                pm = i;
                fp = j;
            }

            /* We are lucky */
            if (savesub == 0)
                goto out;
        }
    }

    if (pm == 999) {
        dev_err(dai->dev, "failed to calculate proper divisors\n");
        return -EINVAL;
    }

out:
    regmap_update_bits(esai_priv->regmap, REG_ESAI_xCCR(tx),
                       ESAI_xCCR_xPSR_MASK | ESAI_xCCR_xPM_MASK,
                       psr | ESAI_xCCR_xPM(pm));

out_fp:
    /* Bypass fp if not being required */
    if (maxfp <= 1)
        return 0;

    regmap_update_bits(esai_priv->regmap, REG_ESAI_xCCR(tx),
                       ESAI_xCCR_xFP_MASK, ESAI_xCCR_xFP(fp));

    return 0;
}
コード例 #4
0
ファイル: uniperif_player.c プロジェクト: AshishNamdev/linux
static int uni_player_prepare(struct snd_pcm_substream *substream,
			      struct snd_soc_dai *dai)
{
	struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
	struct uniperif *player = priv->dai_data.uni;
	struct snd_pcm_runtime *runtime = substream->runtime;
	int transfer_size, trigger_limit;
	int ret;

	/* The player should be stopped */
	if (player->state != UNIPERIF_STATE_STOPPED) {
		dev_err(player->dev, "%s: invalid player state %d\n", __func__,
			player->state);
		return -EINVAL;
	}

	/* Calculate transfer size (in fifo cells and bytes) for frame count */
	if (player->type == SND_ST_UNIPERIF_TYPE_TDM) {
		/* transfer size = user frame size (in 32 bits FIFO cell) */
		transfer_size =
			sti_uniperiph_get_user_frame_size(runtime) / 4;
	} else {
		transfer_size = runtime->channels * UNIPERIF_FIFO_FRAMES;
	}

	/* Calculate number of empty cells available before asserting DREQ */
	if (player->ver < SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0) {
		trigger_limit = UNIPERIF_FIFO_SIZE - transfer_size;
	} else {
		/*
		 * Since SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0
		 * FDMA_TRIGGER_LIMIT also controls when the state switches
		 * from OFF or STANDBY to AUDIO DATA.
		 */
		trigger_limit = transfer_size;
	}

	/* Trigger limit must be an even number */
	if ((!trigger_limit % 2) || (trigger_limit != 1 && transfer_size % 2) ||
	    (trigger_limit > UNIPERIF_CONFIG_DMA_TRIG_LIMIT_MASK(player))) {
		dev_err(player->dev, "invalid trigger limit %d\n",
			trigger_limit);
		return -EINVAL;
	}

	SET_UNIPERIF_CONFIG_DMA_TRIG_LIMIT(player, trigger_limit);

	/* Uniperipheral setup depends on player type */
	switch (player->type) {
	case SND_ST_UNIPERIF_TYPE_HDMI:
		ret = uni_player_prepare_iec958(player, runtime);
		break;
	case SND_ST_UNIPERIF_TYPE_PCM:
		ret = uni_player_prepare_pcm(player, runtime);
		break;
	case SND_ST_UNIPERIF_TYPE_SPDIF:
		ret = uni_player_prepare_iec958(player, runtime);
		break;
	case SND_ST_UNIPERIF_TYPE_TDM:
		ret = uni_player_prepare_tdm(player, runtime);
		break;
	default:
		dev_err(player->dev, "invalid player type\n");
		return -EINVAL;
	}

	if (ret)
		return ret;

	switch (player->daifmt & SND_SOC_DAIFMT_INV_MASK) {
	case SND_SOC_DAIFMT_NB_NF:
		SET_UNIPERIF_I2S_FMT_LR_POL_LOW(player);
		SET_UNIPERIF_I2S_FMT_SCLK_EDGE_RISING(player);
		break;
	case SND_SOC_DAIFMT_NB_IF:
		SET_UNIPERIF_I2S_FMT_LR_POL_HIG(player);
		SET_UNIPERIF_I2S_FMT_SCLK_EDGE_RISING(player);
		break;
	case SND_SOC_DAIFMT_IB_NF:
		SET_UNIPERIF_I2S_FMT_LR_POL_LOW(player);
		SET_UNIPERIF_I2S_FMT_SCLK_EDGE_FALLING(player);
		break;
	case SND_SOC_DAIFMT_IB_IF:
		SET_UNIPERIF_I2S_FMT_LR_POL_HIG(player);
		SET_UNIPERIF_I2S_FMT_SCLK_EDGE_FALLING(player);
		break;
	}

	switch (player->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) {
	case SND_SOC_DAIFMT_I2S:
		SET_UNIPERIF_I2S_FMT_ALIGN_LEFT(player);
		SET_UNIPERIF_I2S_FMT_PADDING_I2S_MODE(player);
		break;
	case SND_SOC_DAIFMT_LEFT_J:
		SET_UNIPERIF_I2S_FMT_ALIGN_LEFT(player);
		SET_UNIPERIF_I2S_FMT_PADDING_SONY_MODE(player);
		break;
	case SND_SOC_DAIFMT_RIGHT_J:
		SET_UNIPERIF_I2S_FMT_ALIGN_RIGHT(player);
		SET_UNIPERIF_I2S_FMT_PADDING_SONY_MODE(player);
		break;
	default:
		dev_err(player->dev, "format not supported\n");
		return -EINVAL;
	}

	SET_UNIPERIF_I2S_FMT_NO_OF_SAMPLES_TO_READ(player, 0);


	return sti_uniperiph_reset(player);
}
コード例 #5
0
ファイル: kirkwood-i2s.c プロジェクト: Scorpio92/mstar6a918
static int kirkwood_i2s_hw_params(struct snd_pcm_substream *substream,
                                  struct snd_pcm_hw_params *params,
                                  struct snd_soc_dai *dai)
{
    struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai);
    unsigned int i2s_reg, reg;
    unsigned long i2s_value, value;

    if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
        i2s_reg = KIRKWOOD_I2S_PLAYCTL;
        reg = KIRKWOOD_PLAYCTL;
    } else {
        i2s_reg = KIRKWOOD_I2S_RECCTL;
        reg = KIRKWOOD_RECCTL;
    }

    /* set dco conf */
    kirkwood_set_dco(priv->io, params_rate(params));

    i2s_value = readl(priv->io+i2s_reg);
    i2s_value &= ~KIRKWOOD_I2S_CTL_SIZE_MASK;

    value = readl(priv->io+reg);
    value &= ~KIRKWOOD_PLAYCTL_SIZE_MASK;

    /*
     * Size settings in play/rec i2s control regs and play/rec control
     * regs must be the same.
     */
    switch (params_format(params)) {
    case SNDRV_PCM_FORMAT_S16_LE:
        i2s_value |= KIRKWOOD_I2S_CTL_SIZE_16;
        value |= KIRKWOOD_PLAYCTL_SIZE_16_C;
        break;
    /*
     * doesn't work... S20_3LE != kirkwood 20bit format ?
     *
    case SNDRV_PCM_FORMAT_S20_3LE:
    	i2s_value |= KIRKWOOD_I2S_CTL_SIZE_20;
    	value |= KIRKWOOD_PLAYCTL_SIZE_20;
    	break;
    */
    case SNDRV_PCM_FORMAT_S24_LE:
        i2s_value |= KIRKWOOD_I2S_CTL_SIZE_24;
        value |= KIRKWOOD_PLAYCTL_SIZE_24;
        break;
    case SNDRV_PCM_FORMAT_S32_LE:
        i2s_value |= KIRKWOOD_I2S_CTL_SIZE_32;
        value |= KIRKWOOD_PLAYCTL_SIZE_32;
        break;
    default:
        return -EINVAL;
    }

    if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
        value &= ~KIRKWOOD_PLAYCTL_MONO_MASK;
        if (params_channels(params) == 1)
            value |= KIRKWOOD_PLAYCTL_MONO_BOTH;
        else
            value |= KIRKWOOD_PLAYCTL_MONO_OFF;
    }

    writel(i2s_value, priv->io+i2s_reg);
    writel(value, priv->io+reg);

    return 0;
}
コード例 #6
0
ファイル: omap-mcbsp.c プロジェクト: shaqfu786/FreshBake
static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
					 int clk_id, unsigned int freq,
					 int dir)
{
	struct omap_mcbsp_data *mcbsp_data = snd_soc_dai_get_drvdata(cpu_dai);
	struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs;
	int err = 0;

	if (mcbsp_data->active) {
		if (freq == mcbsp_data->in_freq)
			return 0;
		else
			return -EBUSY;
	}

	/* The McBSP signal muxing functions are only available on McBSP1 */
	if (clk_id == OMAP_MCBSP_CLKR_SRC_CLKR ||
	    clk_id == OMAP_MCBSP_CLKR_SRC_CLKX ||
	    clk_id == OMAP_MCBSP_FSR_SRC_FSR ||
	    clk_id == OMAP_MCBSP_FSR_SRC_FSX)
		if (cpu_class_is_omap1() || mcbsp_data->bus_id != 0)
			return -EINVAL;

	mcbsp_data->in_freq = freq;

	switch (clk_id) {
	case OMAP_MCBSP_SYSCLK_CLK:
		regs->srgr2	|= CLKSM;
		break;
	case OMAP_MCBSP_SYSCLK_CLKS_FCLK:
		if (cpu_class_is_omap1()) {
			err = -EINVAL;
			break;
		}
		err = omap2_mcbsp_set_clks_src(mcbsp_data->bus_id,
					       MCBSP_CLKS_PRCM_SRC);
		break;
	case OMAP_MCBSP_SYSCLK_CLKS_EXT:
		if (cpu_class_is_omap1()) {
			err = 0;
			break;
		}
		err = omap2_mcbsp_set_clks_src(mcbsp_data->bus_id,
					       MCBSP_CLKS_PAD_SRC);
		break;

	case OMAP_MCBSP_SYSCLK_CLKX_EXT:
		regs->srgr2	|= CLKSM;
	case OMAP_MCBSP_SYSCLK_CLKR_EXT:
		regs->pcr0	|= SCLKME;
		break;


	case OMAP_MCBSP_CLKR_SRC_CLKR:
		if (cpu_class_is_omap1())
			break;
		omap2_mcbsp1_mux_clkr_src(CLKR_SRC_CLKR);
		break;
	case OMAP_MCBSP_CLKR_SRC_CLKX:
		if (cpu_class_is_omap1())
			break;
		omap2_mcbsp1_mux_clkr_src(CLKR_SRC_CLKX);
		break;
	case OMAP_MCBSP_FSR_SRC_FSR:
		if (cpu_class_is_omap1())
			break;
		omap2_mcbsp1_mux_fsr_src(FSR_SRC_FSR);
		break;
	case OMAP_MCBSP_FSR_SRC_FSX:
		if (cpu_class_is_omap1())
			break;
		omap2_mcbsp1_mux_fsr_src(FSR_SRC_FSX);
		break;
	default:
		err = -ENODEV;
	}

	return err;
}
コード例 #7
0
ファイル: kirkwood-i2s.c プロジェクト: 3null/linux
static int kirkwood_i2s_play_trigger(struct snd_pcm_substream *substream,
				int cmd, struct snd_soc_dai *dai)
{
	struct snd_pcm_runtime *runtime = substream->runtime;
	struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai);
	uint32_t ctl, value;

	ctl = readl(priv->io + KIRKWOOD_PLAYCTL);
	if ((ctl & KIRKWOOD_PLAYCTL_ENABLE_MASK) == 0) {
		unsigned timeout = 5000;
		/*
		 * The Armada510 spec says that if we enter pause mode, the
		 * busy bit must be read back as clear _twice_.  Make sure
		 * we respect that otherwise we get DMA underruns.
		 */
		do {
			value = ctl;
			ctl = readl(priv->io + KIRKWOOD_PLAYCTL);
			if (!((ctl | value) & KIRKWOOD_PLAYCTL_PLAY_BUSY))
				break;
			udelay(1);
		} while (timeout--);

		if ((ctl | value) & KIRKWOOD_PLAYCTL_PLAY_BUSY)
			dev_notice(dai->dev, "timed out waiting for busy to deassert: %08x\n",
				   ctl);
	}

	switch (cmd) {
	case SNDRV_PCM_TRIGGER_START:
		/* configure */
		ctl = priv->ctl_play;
		if (dai->id == 0)
			ctl &= ~KIRKWOOD_PLAYCTL_SPDIF_EN;	/* i2s */
		else
			ctl &= ~KIRKWOOD_PLAYCTL_I2S_EN;	/* spdif */
		ctl = kirkwood_i2s_play_mute(ctl);
		value = ctl & ~KIRKWOOD_PLAYCTL_ENABLE_MASK;
		writel(value, priv->io + KIRKWOOD_PLAYCTL);

		/* enable interrupts */
		if (!runtime->no_period_wakeup) {
			value = readl(priv->io + KIRKWOOD_INT_MASK);
			value |= KIRKWOOD_INT_CAUSE_PLAY_BYTES;
			writel(value, priv->io + KIRKWOOD_INT_MASK);
		}

		/* enable playback */
		writel(ctl, priv->io + KIRKWOOD_PLAYCTL);
		break;

	case SNDRV_PCM_TRIGGER_STOP:
		/* stop audio, disable interrupts */
		ctl |= KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE |
				KIRKWOOD_PLAYCTL_SPDIF_MUTE;
		writel(ctl, priv->io + KIRKWOOD_PLAYCTL);

		value = readl(priv->io + KIRKWOOD_INT_MASK);
		value &= ~KIRKWOOD_INT_CAUSE_PLAY_BYTES;
		writel(value, priv->io + KIRKWOOD_INT_MASK);

		/* disable all playbacks */
		ctl &= ~KIRKWOOD_PLAYCTL_ENABLE_MASK;
		writel(ctl, priv->io + KIRKWOOD_PLAYCTL);
		break;

	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
	case SNDRV_PCM_TRIGGER_SUSPEND:
		ctl |= KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE |
				KIRKWOOD_PLAYCTL_SPDIF_MUTE;
		writel(ctl, priv->io + KIRKWOOD_PLAYCTL);
		break;

	case SNDRV_PCM_TRIGGER_RESUME:
	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
		ctl &= ~(KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE |
				KIRKWOOD_PLAYCTL_SPDIF_MUTE);
		ctl = kirkwood_i2s_play_mute(ctl);
		writel(ctl, priv->io + KIRKWOOD_PLAYCTL);
		break;

	default:
		return -EINVAL;
	}

	return 0;
}
コード例 #8
0
ファイル: davinci-i2s.c プロジェクト: AlexShiLucky/linux
static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
				 struct snd_pcm_hw_params *params,
				 struct snd_soc_dai *dai)
{
	struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(dai);
	struct snd_interval *i = NULL;
	int mcbsp_word_length, master;
	unsigned int rcr, xcr, srgr, clk_div, freq, framesize;
	u32 spcr;
	snd_pcm_format_t fmt;
	unsigned element_cnt = 1;

	/* general line settings */
	spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
		spcr |= DAVINCI_MCBSP_SPCR_RINTM(3) | DAVINCI_MCBSP_SPCR_FREE;
		davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
	} else {
		spcr |= DAVINCI_MCBSP_SPCR_XINTM(3) | DAVINCI_MCBSP_SPCR_FREE;
		davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
	}

	master = dev->fmt & SND_SOC_DAIFMT_MASTER_MASK;
	fmt = params_format(params);
	mcbsp_word_length = asp_word_length[fmt];

	switch (master) {
	case SND_SOC_DAIFMT_CBS_CFS:
		freq = clk_get_rate(dev->clk);
		srgr = DAVINCI_MCBSP_SRGR_FSGM |
		       DAVINCI_MCBSP_SRGR_CLKSM;
		srgr |= DAVINCI_MCBSP_SRGR_FWID(mcbsp_word_length *
						8 - 1);
		if (dev->i2s_accurate_sck) {
			clk_div = 256;
			do {
				framesize = (freq / (--clk_div)) /
				params->rate_num *
					params->rate_den;
			} while (((framesize < 33) || (framesize > 4095)) &&
				 (clk_div));
			clk_div--;
			srgr |= DAVINCI_MCBSP_SRGR_FPER(framesize - 1);
		} else {
			/* symmetric waveforms */
			clk_div = freq / (mcbsp_word_length * 16) /
				  params->rate_num * params->rate_den;
			srgr |= DAVINCI_MCBSP_SRGR_FPER(mcbsp_word_length *
							16 - 1);
		}
		clk_div &= 0xFF;
		srgr |= clk_div;
		break;
	case SND_SOC_DAIFMT_CBM_CFS:
		srgr = DAVINCI_MCBSP_SRGR_FSGM;
		clk_div = dev->clk_div - 1;
		srgr |= DAVINCI_MCBSP_SRGR_FWID(mcbsp_word_length * 8 - 1);
		srgr |= DAVINCI_MCBSP_SRGR_FPER(mcbsp_word_length * 16 - 1);
		clk_div &= 0xFF;
		srgr |= clk_div;
		break;
	case SND_SOC_DAIFMT_CBM_CFM:
		/* Clock and frame sync given from external sources */
		i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS);
		srgr = DAVINCI_MCBSP_SRGR_FSGM;
		srgr |= DAVINCI_MCBSP_SRGR_FWID(snd_interval_value(i) - 1);
		pr_debug("%s - %d  FWID set: re-read srgr = %X\n",
			__func__, __LINE__, snd_interval_value(i) - 1);

		i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_FRAME_BITS);
		srgr |= DAVINCI_MCBSP_SRGR_FPER(snd_interval_value(i) - 1);
		break;
	default:
		return -EINVAL;
	}
	davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, srgr);

	rcr = DAVINCI_MCBSP_RCR_RFIG;
	xcr = DAVINCI_MCBSP_XCR_XFIG;
	if (dev->mode == MOD_DSP_B) {
		rcr |= DAVINCI_MCBSP_RCR_RDATDLY(0);
		xcr |= DAVINCI_MCBSP_XCR_XDATDLY(0);
	} else {
		rcr |= DAVINCI_MCBSP_RCR_RDATDLY(1);
		xcr |= DAVINCI_MCBSP_XCR_XDATDLY(1);
	}
	/* Determine xfer data type */
	fmt = params_format(params);
	if ((fmt > SNDRV_PCM_FORMAT_S32_LE) || !data_type[fmt]) {
		printk(KERN_WARNING "davinci-i2s: unsupported PCM format\n");
		return -EINVAL;
	}

	if (params_channels(params) == 2) {
		element_cnt = 2;
		if (double_fmt[fmt] && dev->enable_channel_combine) {
			element_cnt = 1;
			fmt = double_fmt[fmt];
		}
		switch (master) {
		case SND_SOC_DAIFMT_CBS_CFS:
		case SND_SOC_DAIFMT_CBS_CFM:
			rcr |= DAVINCI_MCBSP_RCR_RFRLEN2(0);
			xcr |= DAVINCI_MCBSP_XCR_XFRLEN2(0);
			rcr |= DAVINCI_MCBSP_RCR_RPHASE;
			xcr |= DAVINCI_MCBSP_XCR_XPHASE;
			break;
		case SND_SOC_DAIFMT_CBM_CFM:
		case SND_SOC_DAIFMT_CBM_CFS:
			rcr |= DAVINCI_MCBSP_RCR_RFRLEN2(element_cnt - 1);
			xcr |= DAVINCI_MCBSP_XCR_XFRLEN2(element_cnt - 1);
			break;
		default:
			return -EINVAL;
		}
	}
	mcbsp_word_length = asp_word_length[fmt];

	switch (master) {
	case SND_SOC_DAIFMT_CBS_CFS:
	case SND_SOC_DAIFMT_CBS_CFM:
		rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(0);
		xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(0);
		break;
	case SND_SOC_DAIFMT_CBM_CFM:
	case SND_SOC_DAIFMT_CBM_CFS:
		rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(element_cnt - 1);
		xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(element_cnt - 1);
		break;
	default:
		return -EINVAL;
	}

	rcr |= DAVINCI_MCBSP_RCR_RWDLEN1(mcbsp_word_length) |
		DAVINCI_MCBSP_RCR_RWDLEN2(mcbsp_word_length);
	xcr |= DAVINCI_MCBSP_XCR_XWDLEN1(mcbsp_word_length) |
		DAVINCI_MCBSP_XCR_XWDLEN2(mcbsp_word_length);

	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
		davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG, xcr);
	else
		davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG, rcr);

	pr_debug("%s - %d  srgr=%X\n", __func__, __LINE__, srgr);
	pr_debug("%s - %d  xcr=%X\n", __func__, __LINE__, xcr);
	pr_debug("%s - %d  rcr=%X\n", __func__, __LINE__, rcr);
	return 0;
}
コード例 #9
0
ファイル: stm32_sai_sub.c プロジェクト: asmalldev/linux
static int stm32_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
{
	struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
	int cr1 = 0, frcr = 0;
	int cr1_mask = 0, frcr_mask = 0;
	int ret;

	dev_dbg(cpu_dai->dev, "fmt %x\n", fmt);

	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
	/* SCK active high for all protocols */
	case SND_SOC_DAIFMT_I2S:
		cr1 |= SAI_XCR1_CKSTR;
		frcr |= SAI_XFRCR_FSOFF | SAI_XFRCR_FSDEF;
		break;
	/* Left justified */
	case SND_SOC_DAIFMT_MSB:
		frcr |= SAI_XFRCR_FSPOL | SAI_XFRCR_FSDEF;
		break;
	/* Right justified */
	case SND_SOC_DAIFMT_LSB:
		frcr |= SAI_XFRCR_FSPOL | SAI_XFRCR_FSDEF;
		break;
	case SND_SOC_DAIFMT_DSP_A:
		frcr |= SAI_XFRCR_FSPOL | SAI_XFRCR_FSOFF;
		break;
	case SND_SOC_DAIFMT_DSP_B:
		frcr |= SAI_XFRCR_FSPOL;
		break;
	default:
		dev_err(cpu_dai->dev, "Unsupported protocol %#x\n",
			fmt & SND_SOC_DAIFMT_FORMAT_MASK);
		return -EINVAL;
	}

	cr1_mask |= SAI_XCR1_PRTCFG_MASK | SAI_XCR1_CKSTR;
	frcr_mask |= SAI_XFRCR_FSPOL | SAI_XFRCR_FSOFF |
		     SAI_XFRCR_FSDEF;

	/* DAI clock strobing. Invert setting previously set */
	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
	case SND_SOC_DAIFMT_NB_NF:
		break;
	case SND_SOC_DAIFMT_IB_NF:
		cr1 ^= SAI_XCR1_CKSTR;
		break;
	case SND_SOC_DAIFMT_NB_IF:
		frcr ^= SAI_XFRCR_FSPOL;
		break;
	case SND_SOC_DAIFMT_IB_IF:
		/* Invert fs & sck */
		cr1 ^= SAI_XCR1_CKSTR;
		frcr ^= SAI_XFRCR_FSPOL;
		break;
	default:
		dev_err(cpu_dai->dev, "Unsupported strobing %#x\n",
			fmt & SND_SOC_DAIFMT_INV_MASK);
		return -EINVAL;
	}
	cr1_mask |= SAI_XCR1_CKSTR;
	frcr_mask |= SAI_XFRCR_FSPOL;

	regmap_update_bits(sai->regmap, STM_SAI_FRCR_REGX, frcr_mask, frcr);

	/* DAI clock master masks */
	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
	case SND_SOC_DAIFMT_CBM_CFM:
		/* codec is master */
		cr1 |= SAI_XCR1_SLAVE;
		sai->master = false;
		break;
	case SND_SOC_DAIFMT_CBS_CFS:
		sai->master = true;
		break;
	default:
		dev_err(cpu_dai->dev, "Unsupported mode %#x\n",
			fmt & SND_SOC_DAIFMT_MASTER_MASK);
		return -EINVAL;
	}
	cr1_mask |= SAI_XCR1_SLAVE;

	ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, cr1_mask, cr1);
	if (ret < 0) {
		dev_err(cpu_dai->dev, "Failed to update CR1 register\n");
		return ret;
	}

	sai->fmt = fmt;

	return 0;
}
コード例 #10
0
ファイル: tegra20_spdif.c プロジェクト: FrozenCow/FIRE-ICE
static int tegra20_spdif_hw_params(struct snd_pcm_substream *substream,
				struct snd_pcm_hw_params *params,
				struct snd_soc_dai *dai)
{
	struct device *dev = dai->dev;
	struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai);
	unsigned int mask = 0, val = 0;
	int ret, srate, spdifclock;
	u32 ch_sta[2] = {0, 0};

	mask |= TEGRA20_SPDIF_CTRL_PACK |
		TEGRA20_SPDIF_CTRL_BIT_MODE_MASK;
	switch (params_format(params)) {
	case SNDRV_PCM_FORMAT_S16_LE:
		val |= TEGRA20_SPDIF_CTRL_PACK |
		       TEGRA20_SPDIF_CTRL_BIT_MODE_16BIT;
		break;
	default:
		return -EINVAL;
	}

	regmap_update_bits(spdif->regmap, TEGRA20_SPDIF_CTRL, mask, val);

	srate = params_rate(params);
	switch (params_rate(params)) {
	case 32000:
		spdifclock = 4096000;
		ch_sta[0] |= TEGRA20_SPDIF_CH_STA_TX_A_SAMP_FREQ_32000;
		ch_sta[1] |= TEGRA20_SPDIF_CH_STA_TX_B_ORIG_SAMP_FREQ_32000;
		break;
	case 44100:
		spdifclock = 5644800;
		ch_sta[0] |= TEGRA20_SPDIF_CH_STA_TX_A_SAMP_FREQ_44100;
		ch_sta[1] |= TEGRA20_SPDIF_CH_STA_TX_B_ORIG_SAMP_FREQ_44100;
		break;
	case 48000:
		spdifclock = 6144000;
		ch_sta[0] |= TEGRA20_SPDIF_CH_STA_TX_A_SAMP_FREQ_48000;
		ch_sta[1] |= TEGRA20_SPDIF_CH_STA_TX_B_ORIG_SAMP_FREQ_48000;
		break;
	case 88200:
		spdifclock = 11289600;
		ch_sta[0] |= TEGRA20_SPDIF_CH_STA_TX_A_SAMP_FREQ_88200;
		ch_sta[1] |= TEGRA20_SPDIF_CH_STA_TX_B_ORIG_SAMP_FREQ_88200;
		break;
	case 96000:
		spdifclock = 12288000;
		ch_sta[0] |= TEGRA20_SPDIF_CH_STA_TX_A_SAMP_FREQ_96000;
		ch_sta[1] |= TEGRA20_SPDIF_CH_STA_TX_B_ORIG_SAMP_FREQ_96000;
		break;
	case 176400:
		spdifclock = 22579200;
		ch_sta[0] |= TEGRA20_SPDIF_CH_STA_TX_A_SAMP_FREQ_176400;
		ch_sta[1] |= TEGRA20_SPDIF_CH_STA_TX_B_ORIG_SAMP_FREQ_176400;
		break;
	case 192000:
		spdifclock = 24576000;
		ch_sta[0] |= TEGRA20_SPDIF_CH_STA_TX_A_SAMP_FREQ_192000;
		ch_sta[1] |= TEGRA20_SPDIF_CH_STA_TX_B_ORIG_SAMP_FREQ_192000;
		break;
	default:
		return -EINVAL;
	}

	ret = clk_set_rate(spdif->clk_spdif_out, spdifclock);
	if (ret) {
		dev_err(dev, "Can't set SPDIF clock rate: %d\n", ret);
		return ret;
	}

	clk_enable(spdif->clk_spdif_out);

	mask = TEGRA20_SPDIF_CH_STA_TX_A_SAMP_FREQ_MASK;
	regmap_update_bits(spdif->regmap, TEGRA20_SPDIF_CH_STA_TX_A, mask, ch_sta[0]);
	mask = TEGRA20_SPDIF_CH_STA_TX_B_ORIG_SAMP_FREQ_MASK;
	regmap_update_bits(spdif->regmap, TEGRA20_SPDIF_CH_STA_TX_B, mask, ch_sta[1]);

	clk_disable(spdif->clk_spdif_out);

	ret = tegra_hdmi_setup_audio_freq_source(srate, SPDIF);
	if (ret) {
		dev_err(dev, "Can't set HDMI audio freq source: %d\n", ret);
		return ret;
	}

	return 0;
}
コード例 #11
0
ファイル: davinci-i2s.c プロジェクト: AlexShiLucky/linux
static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
				   unsigned int fmt)
{
	struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
	unsigned int pcr;
	unsigned int srgr;
	bool inv_fs = false;
	/* Attention srgr is updated by hw_params! */
	srgr = DAVINCI_MCBSP_SRGR_FSGM |
		DAVINCI_MCBSP_SRGR_FPER(DEFAULT_BITPERSAMPLE * 2 - 1) |
		DAVINCI_MCBSP_SRGR_FWID(DEFAULT_BITPERSAMPLE - 1);

	dev->fmt = fmt;
	/* set master/slave audio interface */
	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
	case SND_SOC_DAIFMT_CBS_CFS:
		/* cpu is master */
		pcr = DAVINCI_MCBSP_PCR_FSXM |
			DAVINCI_MCBSP_PCR_FSRM |
			DAVINCI_MCBSP_PCR_CLKXM |
			DAVINCI_MCBSP_PCR_CLKRM;
		break;
	case SND_SOC_DAIFMT_CBM_CFS:
		pcr = DAVINCI_MCBSP_PCR_FSRM | DAVINCI_MCBSP_PCR_FSXM;
		/*
		 * Selection of the clock input pin that is the
		 * input for the Sample Rate Generator.
		 * McBSP FSR and FSX are driven by the Sample Rate
		 * Generator.
		 */
		switch (dev->clk_input_pin) {
		case MCBSP_CLKS:
			pcr |= DAVINCI_MCBSP_PCR_CLKXM |
				DAVINCI_MCBSP_PCR_CLKRM;
			break;
		case MCBSP_CLKR:
			pcr |= DAVINCI_MCBSP_PCR_SCLKME;
			break;
		default:
			dev_err(dev->dev, "bad clk_input_pin\n");
			return -EINVAL;
		}

		break;
	case SND_SOC_DAIFMT_CBM_CFM:
		/* codec is master */
		pcr = 0;
		break;
	default:
		printk(KERN_ERR "%s:bad master\n", __func__);
		return -EINVAL;
	}

	/* interface format */
	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
	case SND_SOC_DAIFMT_I2S:
		/* Davinci doesn't support TRUE I2S, but some codecs will have
		 * the left and right channels contiguous. This allows
		 * dsp_a mode to be used with an inverted normal frame clk.
		 * If your codec is master and does not have contiguous
		 * channels, then you will have sound on only one channel.
		 * Try using a different mode, or codec as slave.
		 *
		 * The TLV320AIC33 is an example of a codec where this works.
		 * It has a variable bit clock frequency allowing it to have
		 * valid data on every bit clock.
		 *
		 * The TLV320AIC23 is an example of a codec where this does not
		 * work. It has a fixed bit clock frequency with progressively
		 * more empty bit clock slots between channels as the sample
		 * rate is lowered.
		 */
		inv_fs = true;
		/* fall through */
	case SND_SOC_DAIFMT_DSP_A:
		dev->mode = MOD_DSP_A;
		break;
	case SND_SOC_DAIFMT_DSP_B:
		dev->mode = MOD_DSP_B;
		break;
	default:
		printk(KERN_ERR "%s:bad format\n", __func__);
		return -EINVAL;
	}

	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
	case SND_SOC_DAIFMT_NB_NF:
		/* CLKRP Receive clock polarity,
		 *	1 - sampled on rising edge of CLKR
		 *	valid on rising edge
		 * CLKXP Transmit clock polarity,
		 *	1 - clocked on falling edge of CLKX
		 *	valid on rising edge
		 * FSRP  Receive frame sync pol, 0 - active high
		 * FSXP  Transmit frame sync pol, 0 - active high
		 */
		pcr |= (DAVINCI_MCBSP_PCR_CLKXP | DAVINCI_MCBSP_PCR_CLKRP);
		break;
	case SND_SOC_DAIFMT_IB_IF:
		/* CLKRP Receive clock polarity,
		 *	0 - sampled on falling edge of CLKR
		 *	valid on falling edge
		 * CLKXP Transmit clock polarity,
		 *	0 - clocked on rising edge of CLKX
		 *	valid on falling edge
		 * FSRP  Receive frame sync pol, 1 - active low
		 * FSXP  Transmit frame sync pol, 1 - active low
		 */
		pcr |= (DAVINCI_MCBSP_PCR_FSXP | DAVINCI_MCBSP_PCR_FSRP);
		break;
	case SND_SOC_DAIFMT_NB_IF:
		/* CLKRP Receive clock polarity,
		 *	1 - sampled on rising edge of CLKR
		 *	valid on rising edge
		 * CLKXP Transmit clock polarity,
		 *	1 - clocked on falling edge of CLKX
		 *	valid on rising edge
		 * FSRP  Receive frame sync pol, 1 - active low
		 * FSXP  Transmit frame sync pol, 1 - active low
		 */
		pcr |= (DAVINCI_MCBSP_PCR_CLKXP | DAVINCI_MCBSP_PCR_CLKRP |
			DAVINCI_MCBSP_PCR_FSXP | DAVINCI_MCBSP_PCR_FSRP);
		break;
	case SND_SOC_DAIFMT_IB_NF:
		/* CLKRP Receive clock polarity,
		 *	0 - sampled on falling edge of CLKR
		 *	valid on falling edge
		 * CLKXP Transmit clock polarity,
		 *	0 - clocked on rising edge of CLKX
		 *	valid on falling edge
		 * FSRP  Receive frame sync pol, 0 - active high
		 * FSXP  Transmit frame sync pol, 0 - active high
		 */
		break;
	default:
		return -EINVAL;
	}
	if (inv_fs == true)
		pcr ^= (DAVINCI_MCBSP_PCR_FSXP | DAVINCI_MCBSP_PCR_FSRP);
	davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, srgr);
	dev->pcr = pcr;
	davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, pcr);
	return 0;
}
コード例 #12
0
ファイル: designware_i2s.c プロジェクト: rochecr/linux
static int dw_i2s_hw_params(struct snd_pcm_substream *substream,
		struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
	struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
	struct i2s_clk_config_data *config = &dev->config;
	u32 ccr, xfer_resolution, ch_reg, irq;
	int ret;

	switch (params_format(params)) {
	case SNDRV_PCM_FORMAT_S16_LE:
		config->data_width = 16;
		ccr = 0x00;
		xfer_resolution = 0x02;
		break;

	case SNDRV_PCM_FORMAT_S24_LE:
		config->data_width = 24;
		ccr = 0x08;
		xfer_resolution = 0x04;
		break;

	case SNDRV_PCM_FORMAT_S32_LE:
		config->data_width = 32;
		ccr = 0x10;
		xfer_resolution = 0x05;
		break;

	default:
		dev_err(dev->dev, "designware-i2s: unsuppted PCM fmt");
		return -EINVAL;
	}

	config->chan_nr = params_channels(params);

	switch (config->chan_nr) {
	case EIGHT_CHANNEL_SUPPORT:
	case SIX_CHANNEL_SUPPORT:
	case FOUR_CHANNEL_SUPPORT:
	case TWO_CHANNEL_SUPPORT:
		break;
	default:
		dev_err(dev->dev, "channel not supported\n");
		return -EINVAL;
	}

	i2s_disable_channels(dev, substream->stream);

	for (ch_reg = 0; ch_reg < (config->chan_nr / 2); ch_reg++) {
		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
			i2s_write_reg(dev->i2s_base, TCR(ch_reg),
				      xfer_resolution);
			i2s_write_reg(dev->i2s_base, TFCR(ch_reg), 0x02);
			irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg));
			i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x30);
			i2s_write_reg(dev->i2s_base, TER(ch_reg), 1);
		} else {
			i2s_write_reg(dev->i2s_base, RCR(ch_reg),
				      xfer_resolution);
			i2s_write_reg(dev->i2s_base, RFCR(ch_reg), 0x07);
			irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg));
			i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x03);
			i2s_write_reg(dev->i2s_base, RER(ch_reg), 1);
		}
	}

	i2s_write_reg(dev->i2s_base, CCR, ccr);

	config->sample_rate = params_rate(params);

	if (dev->i2s_clk_cfg) {
		ret = dev->i2s_clk_cfg(config);
		if (ret < 0) {
			dev_err(dev->dev, "runtime audio clk config fail\n");
			return ret;
		}
	} else {
		u32 bitclk = config->sample_rate * config->data_width * 2;

		ret = clk_set_rate(dev->clk, bitclk);
		if (ret) {
			dev_err(dev->dev, "Can't set I2S clock rate: %d\n",
				ret);
			return ret;
		}
	}

	return 0;
}
コード例 #13
0
static int fsl_ssi_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
				  int clk_id, unsigned int freq, int dir)
{
	struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai);
	struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
	int synchronous = ssi_private->cpu_dai_drv.symmetric_rates, ret;
	u32 pm = 999, div2, psr, stccr, mask, afreq, factor, i;
	unsigned long clkrate, sysrate = 0, baudrate, flags;
	u64 sub, savesub = 100000;

	/*
	 * It should be already enough to divide clock by setting pm.
	 * So we here keep psr and div2 to 0.
	 */
	psr = 0;
	div2 = 0;

	factor = (div2 + 1) * (7 * psr + 1) * 2;

	for (i = 0; i < 255; i++) {
		/* The bclk rate must be smaller than 1/5 sysclk rate */
		if (factor * (i + 1) < 5)
			continue;

		sysrate = freq * factor * (i + 2);
		clkrate = clk_round_rate(ssi_private->clk, sysrate);

		do_div(clkrate, factor);
		afreq = (u32)clkrate / (i + 1);

		if (freq == afreq)
			sub = 0;
		else if (freq / afreq == 1)
			sub = freq - afreq;
		else if (afreq / freq == 1)
			sub = afreq - freq;
		else
			continue;

		/* Calculate the fraction */
		sub *= 100000;
		do_div(sub, freq);

		if (sub < savesub) {
			baudrate = sysrate;
			savesub = sub;
			pm = i;
		}

		/* We are lucky */
		if (savesub == 0)
			break;
	}

	/* No proper pm found if it is still remaining the initial value */
	if (pm == 999) {
		dev_err(cpu_dai->dev, "failed to handle the required sysclk\n");
		return -EINVAL;
	}

	stccr = CCSR_SSI_SxCCR_PM(pm + 1) | (div2 ? CCSR_SSI_SxCCR_DIV2 : 0)
		| (psr ? CCSR_SSI_SxCCR_PSR : 0);
	mask = CCSR_SSI_SxCCR_PM_MASK | CCSR_SSI_SxCCR_DIV2_MASK
		| CCSR_SSI_SxCCR_PSR_MASK;

	if (dir == SND_SOC_CLOCK_OUT || synchronous)
		write_ssi_mask(&ssi->stccr, mask, stccr);
	else
		write_ssi_mask(&ssi->srccr, mask, stccr);

	spin_lock_irqsave(&ssi_private->baudclk_lock, flags);
	if (!ssi_private->baudclk_locked) {
		ret = clk_set_rate(ssi_private->clk, baudrate);
		if (ret) {
			spin_unlock_irqrestore(&ssi_private->baudclk_lock, flags);
			dev_err(cpu_dai->dev, "failed to set baudclk rate\n");
			return -EINVAL;
		}
		ssi_private->baudclk_locked = true;
	}
	spin_unlock_irqrestore(&ssi_private->baudclk_lock, flags);

	return 0;
}
コード例 #14
0
static int fsl_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
{
	struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai);
	struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
	u32 strcr = 0, stcr, srcr, scr, mask;

	scr = read_ssi(&ssi->scr) & ~(CCSR_SSI_SCR_SYN | CCSR_SSI_SCR_NET);

	mask = CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFDIR | CCSR_SSI_STCR_TXDIR
		| CCSR_SSI_STCR_TSCKP | CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TFSL
		| CCSR_SSI_STCR_TEFS;
	stcr = read_ssi(&ssi->stcr) & ~mask;
	srcr = read_ssi(&ssi->srcr) & ~mask;

	/* DAI format */
	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
	case SND_SOC_DAIFMT_I2S:
		scr |= CCSR_SSI_SCR_NET;

		/* Pre-set SSI I2S mode */
		switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
		case SND_SOC_DAIFMT_CBS_CFS:
			ssi_private->i2s_mode = CCSR_SSI_SCR_I2S_MODE_MASTER;
			break;
		case SND_SOC_DAIFMT_CBM_CFM:
			ssi_private->i2s_mode = CCSR_SSI_SCR_I2S_MODE_SLAVE;
			break;
		default:
			dev_err(cpu_dai->dev, "unsupported SND_SOC_DAIFMT_MASTER: %d",
					fmt & SND_SOC_DAIFMT_MASTER_MASK);
			return -EINVAL;
		}
		scr &= ~CCSR_SSI_SCR_I2S_MODE_MASK;
		scr |= ssi_private->i2s_mode;

		/* Data on rising edge of bclk, frame low, 1clk before data */
		strcr |= CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TSCKP
			| CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TEFS;
		break;
	case SND_SOC_DAIFMT_LEFT_J:
		/* Data on rising edge of bclk, frame high */
		strcr |= CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TSCKP;
		break;
	case SND_SOC_DAIFMT_DSP_A:
		/* Data on rising edge of bclk, frame high, 1clk before data */
		strcr |= CCSR_SSI_STCR_TFSL | CCSR_SSI_STCR_TSCKP
			| CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TEFS;
		break;
	case SND_SOC_DAIFMT_DSP_B:
		/* Data on rising edge of bclk, frame high */
		strcr |= CCSR_SSI_STCR_TFSL | CCSR_SSI_STCR_TSCKP
			| CCSR_SSI_STCR_TXBIT0;
		break;
	default:
		dev_err(cpu_dai->dev, "unsupported SND_SOC_DAIFMT: %d",
				fmt & SND_SOC_DAIFMT_FORMAT_MASK);
		return -EINVAL;
	}

	/* DAI clock inversion */
	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
	case SND_SOC_DAIFMT_NB_NF:
		/* Nothing to do for both normal cases */
		break;
	case SND_SOC_DAIFMT_IB_NF:
		/* Invert bit clock */
		strcr ^= CCSR_SSI_STCR_TSCKP;
		break;
	case SND_SOC_DAIFMT_NB_IF:
		/* Invert frame clock */
		strcr ^= CCSR_SSI_STCR_TFSI;
		break;
	case SND_SOC_DAIFMT_IB_IF:
		/* Invert both clocks */
		strcr ^= CCSR_SSI_STCR_TSCKP;
		strcr ^= CCSR_SSI_STCR_TFSI;
		break;
	default:
		dev_err(cpu_dai->dev, "unsupported SND_SOC_DAIFMT_INV: %d",
				fmt & SND_SOC_DAIFMT_INV_MASK);
		return -EINVAL;
	}

	/* DAI clock master masks */
	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
	case SND_SOC_DAIFMT_CBS_CFS:
		scr |= CCSR_SSI_SCR_SYS_CLK_EN;
		strcr |= CCSR_SSI_STCR_TFDIR | CCSR_SSI_STCR_TXDIR;
		break;
	case SND_SOC_DAIFMT_CBM_CFM:
		scr &= ~CCSR_SSI_SCR_SYS_CLK_EN;
		break;
	default:
		dev_err(cpu_dai->dev, "unsupported SND_SOC_DAIFMT_MASTER: %d",
				fmt & SND_SOC_DAIFMT_MASTER_MASK);
		return -EINVAL;
	}

	stcr |= strcr;
	srcr |= strcr;

	if (ssi_private->cpu_dai_drv.symmetric_rates) {
		scr |= CCSR_SSI_SCR_SYN;

		/* Need to clear RXDIR when using SYNC mode */
		srcr &= ~CCSR_SSI_SRCR_RXDIR;
	}

	write_ssi(stcr, &ssi->stcr);
	write_ssi(srcr, &ssi->srcr);
	write_ssi(scr, &ssi->scr);

	return 0;
}
コード例 #15
0
ファイル: omap-mcbsp.c プロジェクト: shaqfu786/FreshBake
static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
				    struct snd_pcm_hw_params *params,
				    struct snd_soc_dai *cpu_dai)
{
	struct omap_mcbsp_data *mcbsp_data = snd_soc_dai_get_drvdata(cpu_dai);
	struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs;
	struct omap_pcm_dma_data *dma_data;
	int dma, bus_id = mcbsp_data->bus_id;
	int wlen, channels, wpf, sync_mode = OMAP_DMA_SYNC_ELEMENT;
	int pkt_size = 0;
	unsigned long port;
	unsigned int format, div, framesize, master;

	dma_data = &omap_mcbsp_dai_dma_params[cpu_dai->id][substream->stream];

	dma = omap_mcbsp_dma_ch_params(bus_id, substream->stream);
	port = omap_mcbsp_dma_reg_params(bus_id, substream->stream);

	switch (params_format(params)) {
	case SNDRV_PCM_FORMAT_S16_LE:
		dma_data->data_type = OMAP_DMA_DATA_TYPE_S16;
		wlen = 16;
		break;
	case SNDRV_PCM_FORMAT_S32_LE:
		dma_data->data_type = OMAP_DMA_DATA_TYPE_S32;
		wlen = 32;
		break;
	default:
		return -EINVAL;
	}
	if (cpu_is_omap34xx() || cpu_is_omap44xx()) {
		dma_data->set_threshold = omap_mcbsp_set_threshold;
		/* TODO: Currently, MODE_ELEMENT == MODE_FRAME */
		if (omap_mcbsp_get_dma_op_mode(bus_id) ==
						MCBSP_DMA_MODE_THRESHOLD) {
			int period_words, max_thrsh;

			period_words = params_period_bytes(params) / (wlen / 8);
			if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
				max_thrsh = omap_mcbsp_get_max_tx_threshold(
							    mcbsp_data->bus_id);
			else
				max_thrsh = omap_mcbsp_get_max_rx_threshold(
							    mcbsp_data->bus_id);
			/*
			 * If the period contains less or equal number of words,
			 * we are using the original threshold mode setup:
			 * McBSP threshold = sDMA frame size = period_size
			 * Otherwise we switch to sDMA packet mode:
			 * McBSP threshold = sDMA packet size
			 * sDMA frame size = period size
			 */
			if (period_words > max_thrsh) {
				int divider = 0;

				/*
				 * Look for the biggest threshold value, which
				 * divides the period size evenly.
				 */
				divider = period_words / max_thrsh;
				if (period_words % max_thrsh)
					divider++;
				while (period_words % divider &&
					divider < period_words)
					divider++;
				if (divider == period_words)
					return -EINVAL;

				pkt_size = period_words / divider;
				sync_mode = OMAP_DMA_SYNC_PACKET;
			} else {
				sync_mode = OMAP_DMA_SYNC_FRAME;
			}
		}
	}

	dma_data->name = substream->stream ? "Audio Capture" : "Audio Playback";
	dma_data->dma_req = dma;
	dma_data->port_addr = port;
	dma_data->sync_mode = sync_mode;
	dma_data->packet_size = pkt_size;

	snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data);

	if (mcbsp_data->configured) {
		/* McBSP already configured by another stream */
		return 0;
	}

	format = mcbsp_data->fmt & SND_SOC_DAIFMT_FORMAT_MASK;
	wpf = channels = params_channels(params);
	if (channels == 2 && (format == SND_SOC_DAIFMT_I2S ||
			      format == SND_SOC_DAIFMT_LEFT_J)) {
		/* Use dual-phase frames */
		regs->rcr2	|= RPHASE;
		regs->xcr2	|= XPHASE;
		/* Set 1 word per (McBSP) frame for phase1 and phase2 */
		wpf--;
		regs->rcr2	|= RFRLEN2(wpf - 1);
		regs->xcr2	|= XFRLEN2(wpf - 1);
	}

	regs->rcr1	|= RFRLEN1(wpf - 1);
	regs->xcr1	|= XFRLEN1(wpf - 1);

	switch (params_format(params)) {
	case SNDRV_PCM_FORMAT_S16_LE:
		/* Set word lengths */
		regs->rcr2	|= RWDLEN2(OMAP_MCBSP_WORD_16);
		regs->rcr1	|= RWDLEN1(OMAP_MCBSP_WORD_16);
		regs->xcr2	|= XWDLEN2(OMAP_MCBSP_WORD_16);
		regs->xcr1	|= XWDLEN1(OMAP_MCBSP_WORD_16);
		break;
	case SNDRV_PCM_FORMAT_S32_LE:
		/* Set word lengths */
		regs->rcr2	|= RWDLEN2(OMAP_MCBSP_WORD_32);
		regs->rcr1	|= RWDLEN1(OMAP_MCBSP_WORD_32);
		regs->xcr2	|= XWDLEN2(OMAP_MCBSP_WORD_32);
		regs->xcr1	|= XWDLEN1(OMAP_MCBSP_WORD_32);
		break;
	default:
		/* Unsupported PCM format */
		return -EINVAL;
	}

	/* In McBSP master modes, FRAME (i.e. sample rate) is generated
	 * by _counting_ BCLKs. Calculate frame size in BCLKs */
	master = mcbsp_data->fmt & SND_SOC_DAIFMT_MASTER_MASK;
	if (master ==	SND_SOC_DAIFMT_CBS_CFS) {
		div = mcbsp_data->clk_div ? mcbsp_data->clk_div : 1;
		framesize = (mcbsp_data->in_freq / div) / params_rate(params);

		if (framesize < wlen * channels) {
			printk(KERN_ERR "%s: not enough bandwidth for desired rate and "
					"channels\n", __func__);
			return -EINVAL;
		}
	} else
		framesize = wlen * channels;

	/* Set FS period and length in terms of bit clock periods */
	switch (format) {
	case SND_SOC_DAIFMT_I2S:
	case SND_SOC_DAIFMT_LEFT_J:
		regs->srgr2	|= FPER(framesize - 1);
		regs->srgr1	|= FWID((framesize >> 1) - 1);
		break;
	case SND_SOC_DAIFMT_DSP_A:
	case SND_SOC_DAIFMT_DSP_B:
		regs->srgr2	|= FPER(framesize - 1);
		regs->srgr1	|= FWID(0);
		break;
	}

	omap_mcbsp_config(bus_id, &mcbsp_data->regs);
	mcbsp_data->wlen = wlen;
	mcbsp_data->configured = 1;

	return 0;
}
コード例 #16
0
ファイル: s6000-i2s.c プロジェクト: nos1609/Chrono_Kernel-1
static int s6000_i2s_dai_probe(struct snd_soc_dai *dai)
{
	struct s6000_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
	struct s6000_snd_platform_data *pdata = dai->dev->platform_data;

	if (!pdata)
		return -EINVAL;

	dai->capture_dma_data = &dev->dma_params;
	dai->playback_dma_data = &dev->dma_params;

	dev->wide = pdata->wide;
	dev->channel_in = pdata->channel_in;
	dev->channel_out = pdata->channel_out;
	dev->lines_in = pdata->lines_in;
	dev->lines_out = pdata->lines_out;

	s6_i2s_write_reg(dev, S6_I2S_MODE,
			 dev->wide ? S6_I2S_WIDE : S6_I2S_DUAL);

	if (dev->wide) {
		int i;

		if (dev->lines_in + dev->lines_out > S6_I2S_NUM_LINES)
			return -EINVAL;

		dev->channel_in = 0;
		dev->channel_out = 1;
		dai->driver->capture.channels_min = 2 * dev->lines_in;
		dai->driver->capture.channels_max = dai->driver->capture.channels_min;
		dai->driver->playback.channels_min = 2 * dev->lines_out;
		dai->driver->playback.channels_max = dai->driver->playback.channels_min;

		for (i = 0; i < dev->lines_out; i++)
			s6_i2s_write_reg(dev, S6_I2S_DATA_CFG(i), S6_I2S_OUT);

		for (; i < S6_I2S_NUM_LINES - dev->lines_in; i++)
			s6_i2s_write_reg(dev, S6_I2S_DATA_CFG(i),
					 S6_I2S_UNUSED);

		for (; i < S6_I2S_NUM_LINES; i++)
			s6_i2s_write_reg(dev, S6_I2S_DATA_CFG(i), S6_I2S_IN);
	} else {
		unsigned int cfg[2] = {S6_I2S_UNUSED, S6_I2S_UNUSED};

		if (dev->lines_in > 1 || dev->lines_out > 1)
			return -EINVAL;

		dai->driver->capture.channels_min = 2 * dev->lines_in;
		dai->driver->capture.channels_max = 8 * dev->lines_in;
		dai->driver->playback.channels_min = 2 * dev->lines_out;
		dai->driver->playback.channels_max = 8 * dev->lines_out;

		if (dev->lines_in)
			cfg[dev->channel_in] = S6_I2S_IN;
		if (dev->lines_out)
			cfg[dev->channel_out] = S6_I2S_OUT;

		s6_i2s_write_reg(dev, S6_I2S_DATA_CFG(0), cfg[0]);
		s6_i2s_write_reg(dev, S6_I2S_DATA_CFG(1), cfg[1]);
	}

	if (dev->lines_out) {
		if (dev->lines_in) {
			if (!dev->dma_params.dma_out)
				return -ENODEV;
		} else {
			dev->dma_params.dma_out = dev->dma_params.dma_in;
			dev->dma_params.dma_in = 0;
		}
	}
	dev->dma_params.sif_in = dev->sifbase + (dev->channel_in ?
					S6_I2S_SIF_PORT1 : S6_I2S_SIF_PORT0);
	dev->dma_params.sif_out = dev->sifbase + (dev->channel_out ?
					S6_I2S_SIF_PORT1 : S6_I2S_SIF_PORT0);
	dev->dma_params.same_rate = pdata->same_rate | pdata->wide;
	return 0;
}
コード例 #17
0
ファイル: omap-mcbsp.c プロジェクト: shaqfu786/FreshBake
/*
 * This must be called before _set_clkdiv and _set_sysclk since McBSP register
 * cache is initialized here
 */
static int omap_mcbsp_dai_set_dai_fmt(struct snd_soc_dai *cpu_dai,
				      unsigned int fmt)
{
	struct omap_mcbsp_data *mcbsp_data = snd_soc_dai_get_drvdata(cpu_dai);
	struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs;
	unsigned int temp_fmt = fmt;

	if (mcbsp_data->configured)
		return 0;

	mcbsp_data->fmt = fmt;
	memset(regs, 0, sizeof(*regs));
	/* Generic McBSP register settings */
	regs->spcr2	|= XINTM(3) | FREE;
	regs->spcr1	|= RINTM(3);
	/* RFIG and XFIG are not defined in 34xx */
	if (!cpu_is_omap34xx() && !cpu_is_omap44xx()) {
		regs->rcr2	|= RFIG;
		regs->xcr2	|= XFIG;
	}
	if (cpu_is_omap2430() || cpu_is_omap34xx() || cpu_is_omap44xx()) {
		regs->xccr = DXENDLY(1) | XDMAEN | XDISABLE;
		regs->rccr = RFULL_CYCLE | RDMAEN | RDISABLE;
	}

	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
	case SND_SOC_DAIFMT_I2S:
		/* 1-bit data delay */
		regs->rcr2	|= RDATDLY(1);
		regs->xcr2	|= XDATDLY(1);
		break;
	case SND_SOC_DAIFMT_LEFT_J:
		/* 0-bit data delay */
		regs->rcr2	|= RDATDLY(0);
		regs->xcr2	|= XDATDLY(0);
		regs->spcr1	|= RJUST(2);
		/* Invert FS polarity configuration */
		temp_fmt ^= SND_SOC_DAIFMT_NB_IF;
		break;
	case SND_SOC_DAIFMT_DSP_A:
		/* 1-bit data delay */
		regs->rcr2      |= RDATDLY(1);
		regs->xcr2      |= XDATDLY(1);
#if 0  //                                                                                                             
		/* Invert FS polarity configuration */
		temp_fmt ^= SND_SOC_DAIFMT_NB_IF;
#endif
		break;
	case SND_SOC_DAIFMT_DSP_B:
		/* 0-bit data delay */
		regs->rcr2      |= RDATDLY(0);
		regs->xcr2      |= XDATDLY(0);
		/* Invert FS polarity configuration */
		temp_fmt ^= SND_SOC_DAIFMT_NB_IF;
		break;
	default:
		/* Unsupported data format */
		return -EINVAL;
	}

	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
	case SND_SOC_DAIFMT_CBS_CFS:
		/* McBSP master. Set FS and bit clocks as outputs */
		regs->pcr0	|= FSXM | FSRM |
				   CLKXM | CLKRM;
		/* Sample rate generator drives the FS */
		regs->srgr2	|= FSGM;
		break;
	case SND_SOC_DAIFMT_CBM_CFM:
		/* McBSP slave */
		break;
	default:
		/* Unsupported master/slave configuration */
		return -EINVAL;
	}

	/* Set bit clock (CLKX/CLKR) and FS polarities */
	switch (temp_fmt & SND_SOC_DAIFMT_INV_MASK) {
	case SND_SOC_DAIFMT_NB_NF:
		/*
		 * Normal BCLK + FS.
		 * FS active low. TX data driven on falling edge of bit clock
		 * and RX data sampled on rising edge of bit clock.
		 */
		regs->pcr0	|= FSXP | FSRP |
				   CLKXP | CLKRP;
		break;
	case SND_SOC_DAIFMT_NB_IF:
		regs->pcr0	|= CLKXP | CLKRP;
		break;
	case SND_SOC_DAIFMT_IB_NF:
		regs->pcr0	|= FSXP | FSRP;
		break;
	case SND_SOC_DAIFMT_IB_IF:
		break;
	default:
		return -EINVAL;
	}

	return 0;
}
コード例 #18
0
ファイル: omap-mcbsp.c プロジェクト: AdrianHuang/linux-3.8.13
static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
				    struct snd_pcm_hw_params *params,
				    struct snd_soc_dai *cpu_dai)
{
	struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
	struct omap_mcbsp_reg_cfg *regs = &mcbsp->cfg_regs;
	struct omap_pcm_dma_data *dma_data;
	int wlen, channels, wpf;
	int pkt_size = 0;
	unsigned int format, div, framesize, master;

	dma_data = snd_soc_dai_get_dma_data(cpu_dai, substream);
	channels = params_channels(params);

	switch (params_format(params)) {
	case SNDRV_PCM_FORMAT_S16_LE:
		wlen = 16;
		break;
	case SNDRV_PCM_FORMAT_S32_LE:
		wlen = 32;
		break;
	default:
		return -EINVAL;
	}
	if (mcbsp->pdata->buffer_size) {
		dma_data->set_threshold = omap_mcbsp_set_threshold;
		if (mcbsp->dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) {
			int period_words, max_thrsh;
			int divider = 0;

			period_words = params_period_bytes(params) / (wlen / 8);
			if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
				max_thrsh = mcbsp->max_tx_thres;
			else
				max_thrsh = mcbsp->max_rx_thres;
			/*
			 * Use sDMA packet mode if McBSP is in threshold mode:
			 * If period words less than the FIFO size the packet
			 * size is set to the number of period words, otherwise
			 * Look for the biggest threshold value which divides
			 * the period size evenly.
			 */
			divider = period_words / max_thrsh;
			if (period_words % max_thrsh)
				divider++;
			while (period_words % divider &&
				divider < period_words)
				divider++;
			if (divider == period_words)
				return -EINVAL;

			pkt_size = period_words / divider;
		} else if (channels > 1) {
			/* Use packet mode for non mono streams */
			pkt_size = channels;
		}
	}

	dma_data->packet_size = pkt_size;

	if (mcbsp->configured) {
		/* McBSP already configured by another stream */
		return 0;
	}

	regs->rcr2	&= ~(RPHASE | RFRLEN2(0x7f) | RWDLEN2(7));
	regs->xcr2	&= ~(RPHASE | XFRLEN2(0x7f) | XWDLEN2(7));
	regs->rcr1	&= ~(RFRLEN1(0x7f) | RWDLEN1(7));
	regs->xcr1	&= ~(XFRLEN1(0x7f) | XWDLEN1(7));
	format = mcbsp->fmt & SND_SOC_DAIFMT_FORMAT_MASK;
	wpf = channels;
	if (channels == 2 && (format == SND_SOC_DAIFMT_I2S ||
			      format == SND_SOC_DAIFMT_LEFT_J)) {
		/* Use dual-phase frames */
		regs->rcr2	|= RPHASE;
		regs->xcr2	|= XPHASE;
		/* Set 1 word per (McBSP) frame for phase1 and phase2 */
		wpf--;
		regs->rcr2	|= RFRLEN2(wpf - 1);
		regs->xcr2	|= XFRLEN2(wpf - 1);
	}

	regs->rcr1	|= RFRLEN1(wpf - 1);
	regs->xcr1	|= XFRLEN1(wpf - 1);

	switch (params_format(params)) {
	case SNDRV_PCM_FORMAT_S16_LE:
		/* Set word lengths */
		regs->rcr2	|= RWDLEN2(OMAP_MCBSP_WORD_16);
		regs->rcr1	|= RWDLEN1(OMAP_MCBSP_WORD_16);
		regs->xcr2	|= XWDLEN2(OMAP_MCBSP_WORD_16);
		regs->xcr1	|= XWDLEN1(OMAP_MCBSP_WORD_16);
		break;
	case SNDRV_PCM_FORMAT_S32_LE:
		/* Set word lengths */
		regs->rcr2	|= RWDLEN2(OMAP_MCBSP_WORD_32);
		regs->rcr1	|= RWDLEN1(OMAP_MCBSP_WORD_32);
		regs->xcr2	|= XWDLEN2(OMAP_MCBSP_WORD_32);
		regs->xcr1	|= XWDLEN1(OMAP_MCBSP_WORD_32);
		break;
	default:
		/* Unsupported PCM format */
		return -EINVAL;
	}

	/* In McBSP master modes, FRAME (i.e. sample rate) is generated
	 * by _counting_ BCLKs. Calculate frame size in BCLKs */
	master = mcbsp->fmt & SND_SOC_DAIFMT_MASTER_MASK;
	if (master ==	SND_SOC_DAIFMT_CBS_CFS) {
		div = mcbsp->clk_div ? mcbsp->clk_div : 1;
		framesize = (mcbsp->in_freq / div) / params_rate(params);

		if (framesize < wlen * channels) {
			printk(KERN_ERR "%s: not enough bandwidth for desired rate and "
					"channels\n", __func__);
			return -EINVAL;
		}
	} else
		framesize = wlen * channels;

	/* Set FS period and length in terms of bit clock periods */
	regs->srgr2	&= ~FPER(0xfff);
	regs->srgr1	&= ~FWID(0xff);
	switch (format) {
	case SND_SOC_DAIFMT_I2S:
	case SND_SOC_DAIFMT_LEFT_J:
		regs->srgr2	|= FPER(framesize - 1);
		regs->srgr1	|= FWID((framesize >> 1) - 1);
		break;
	case SND_SOC_DAIFMT_DSP_A:
	case SND_SOC_DAIFMT_DSP_B:
		regs->srgr2	|= FPER(framesize - 1);
		regs->srgr1	|= FWID(0);
		break;
	}

	omap_mcbsp_config(mcbsp, &mcbsp->cfg_regs);
	mcbsp->wlen = wlen;
	mcbsp->configured = 1;

	return 0;
}
コード例 #19
0
ファイル: kirkwood-i2s.c プロジェクト: 3null/linux
static int kirkwood_i2s_hw_params(struct snd_pcm_substream *substream,
				 struct snd_pcm_hw_params *params,
				 struct snd_soc_dai *dai)
{
	struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai);
	uint32_t ctl_play, ctl_rec;
	unsigned int i2s_reg;
	unsigned long i2s_value;

	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
		i2s_reg = KIRKWOOD_I2S_PLAYCTL;
	} else {
		i2s_reg = KIRKWOOD_I2S_RECCTL;
	}

	kirkwood_set_rate(dai, priv, params_rate(params));

	i2s_value = readl(priv->io+i2s_reg);
	i2s_value &= ~KIRKWOOD_I2S_CTL_SIZE_MASK;

	/*
	 * Size settings in play/rec i2s control regs and play/rec control
	 * regs must be the same.
	 */
	switch (params_format(params)) {
	case SNDRV_PCM_FORMAT_S16_LE:
		i2s_value |= KIRKWOOD_I2S_CTL_SIZE_16;
		ctl_play = KIRKWOOD_PLAYCTL_SIZE_16_C |
			   KIRKWOOD_PLAYCTL_I2S_EN |
			   KIRKWOOD_PLAYCTL_SPDIF_EN;
		ctl_rec = KIRKWOOD_RECCTL_SIZE_16_C |
			  KIRKWOOD_RECCTL_I2S_EN |
			  KIRKWOOD_RECCTL_SPDIF_EN;
		break;
	/*
	 * doesn't work... S20_3LE != kirkwood 20bit format ?
	 *
	case SNDRV_PCM_FORMAT_S20_3LE:
		i2s_value |= KIRKWOOD_I2S_CTL_SIZE_20;
		ctl_play = KIRKWOOD_PLAYCTL_SIZE_20 |
			   KIRKWOOD_PLAYCTL_I2S_EN;
		ctl_rec = KIRKWOOD_RECCTL_SIZE_20 |
			  KIRKWOOD_RECCTL_I2S_EN;
		break;
	*/
	case SNDRV_PCM_FORMAT_S24_LE:
		i2s_value |= KIRKWOOD_I2S_CTL_SIZE_24;
		ctl_play = KIRKWOOD_PLAYCTL_SIZE_24 |
			   KIRKWOOD_PLAYCTL_I2S_EN |
			   KIRKWOOD_PLAYCTL_SPDIF_EN;
		ctl_rec = KIRKWOOD_RECCTL_SIZE_24 |
			  KIRKWOOD_RECCTL_I2S_EN |
			  KIRKWOOD_RECCTL_SPDIF_EN;
		break;
	case SNDRV_PCM_FORMAT_S32_LE:
		i2s_value |= KIRKWOOD_I2S_CTL_SIZE_32;
		ctl_play = KIRKWOOD_PLAYCTL_SIZE_32 |
			   KIRKWOOD_PLAYCTL_I2S_EN;
		ctl_rec = KIRKWOOD_RECCTL_SIZE_32 |
			  KIRKWOOD_RECCTL_I2S_EN;
		break;
	default:
		return -EINVAL;
	}

	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
		if (params_channels(params) == 1)
			ctl_play |= KIRKWOOD_PLAYCTL_MONO_BOTH;
		else
			ctl_play |= KIRKWOOD_PLAYCTL_MONO_OFF;

		priv->ctl_play &= ~(KIRKWOOD_PLAYCTL_MONO_MASK |
				    KIRKWOOD_PLAYCTL_ENABLE_MASK |
				    KIRKWOOD_PLAYCTL_SIZE_MASK);
		priv->ctl_play |= ctl_play;
	} else {
		priv->ctl_rec &= ~(KIRKWOOD_RECCTL_ENABLE_MASK |
				   KIRKWOOD_RECCTL_SIZE_MASK);
		priv->ctl_rec |= ctl_rec;
	}

	writel(i2s_value, priv->io+i2s_reg);

	return 0;
}
コード例 #20
0
ファイル: mxs-saif.c プロジェクト: avagin/linux
/*
 * SAIF DAI format configuration.
 * Should only be called when port is inactive.
 */
static int mxs_saif_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
{
	u32 scr, stat;
	u32 scr0;
	struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai);

	stat = __raw_readl(saif->base + SAIF_STAT);
	if (stat & BM_SAIF_STAT_BUSY) {
		dev_err(cpu_dai->dev, "error: busy\n");
		return -EBUSY;
	}

	/* If SAIF1 is configured as slave, the clk gate needs to be cleared
	 * before the register can be written.
	 */
	if (saif->id != saif->master_id) {
		__raw_writel(BM_SAIF_CTRL_SFTRST,
			saif->base + SAIF_CTRL + MXS_CLR_ADDR);
		__raw_writel(BM_SAIF_CTRL_CLKGATE,
			saif->base + SAIF_CTRL + MXS_CLR_ADDR);
	}

	scr0 = __raw_readl(saif->base + SAIF_CTRL);
	scr0 = scr0 & ~BM_SAIF_CTRL_BITCLK_EDGE & ~BM_SAIF_CTRL_LRCLK_POLARITY \
		& ~BM_SAIF_CTRL_JUSTIFY & ~BM_SAIF_CTRL_DELAY;
	scr = 0;

	/* DAI mode */
	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
	case SND_SOC_DAIFMT_I2S:
		/* data frame low 1clk before data */
		scr |= BM_SAIF_CTRL_DELAY;
		scr &= ~BM_SAIF_CTRL_LRCLK_POLARITY;
		break;
	case SND_SOC_DAIFMT_LEFT_J:
		/* data frame high with data */
		scr &= ~BM_SAIF_CTRL_DELAY;
		scr &= ~BM_SAIF_CTRL_LRCLK_POLARITY;
		scr &= ~BM_SAIF_CTRL_JUSTIFY;
		break;
	default:
		return -EINVAL;
	}

	/* DAI clock inversion */
	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
	case SND_SOC_DAIFMT_IB_IF:
		scr |= BM_SAIF_CTRL_BITCLK_EDGE;
		scr |= BM_SAIF_CTRL_LRCLK_POLARITY;
		break;
	case SND_SOC_DAIFMT_IB_NF:
		scr |= BM_SAIF_CTRL_BITCLK_EDGE;
		scr &= ~BM_SAIF_CTRL_LRCLK_POLARITY;
		break;
	case SND_SOC_DAIFMT_NB_IF:
		scr &= ~BM_SAIF_CTRL_BITCLK_EDGE;
		scr |= BM_SAIF_CTRL_LRCLK_POLARITY;
		break;
	case SND_SOC_DAIFMT_NB_NF:
		scr &= ~BM_SAIF_CTRL_BITCLK_EDGE;
		scr &= ~BM_SAIF_CTRL_LRCLK_POLARITY;
		break;
	}

	/*
	 * Note: We simply just support master mode since SAIF TX can only
	 * work as master.
	 * Here the master is relative to codec side.
	 * Saif internally could be slave when working on EXTMASTER mode.
	 * We just hide this to machine driver.
	 */
	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
	case SND_SOC_DAIFMT_CBS_CFS:
		if (saif->id == saif->master_id)
			scr &= ~BM_SAIF_CTRL_SLAVE_MODE;
		else
			scr |= BM_SAIF_CTRL_SLAVE_MODE;

		__raw_writel(scr | scr0, saif->base + SAIF_CTRL);
		break;
	default:
		return -EINVAL;
	}

	return 0;
}
コード例 #21
0
ファイル: kirkwood-i2s.c プロジェクト: 3null/linux
static int kirkwood_i2s_rec_trigger(struct snd_pcm_substream *substream,
				int cmd, struct snd_soc_dai *dai)
{
	struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai);
	uint32_t ctl, value;

	value = readl(priv->io + KIRKWOOD_RECCTL);

	switch (cmd) {
	case SNDRV_PCM_TRIGGER_START:
		/* configure */
		ctl = priv->ctl_rec;
		if (dai->id == 0)
			ctl &= ~KIRKWOOD_RECCTL_SPDIF_EN;	/* i2s */
		else
			ctl &= ~KIRKWOOD_RECCTL_I2S_EN;		/* spdif */

		value = ctl & ~KIRKWOOD_RECCTL_ENABLE_MASK;
		writel(value, priv->io + KIRKWOOD_RECCTL);

		/* enable interrupts */
		value = readl(priv->io + KIRKWOOD_INT_MASK);
		value |= KIRKWOOD_INT_CAUSE_REC_BYTES;
		writel(value, priv->io + KIRKWOOD_INT_MASK);

		/* enable record */
		writel(ctl, priv->io + KIRKWOOD_RECCTL);
		break;

	case SNDRV_PCM_TRIGGER_STOP:
		/* stop audio, disable interrupts */
		value = readl(priv->io + KIRKWOOD_RECCTL);
		value |= KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE;
		writel(value, priv->io + KIRKWOOD_RECCTL);

		value = readl(priv->io + KIRKWOOD_INT_MASK);
		value &= ~KIRKWOOD_INT_CAUSE_REC_BYTES;
		writel(value, priv->io + KIRKWOOD_INT_MASK);

		/* disable all records */
		value = readl(priv->io + KIRKWOOD_RECCTL);
		value &= ~KIRKWOOD_RECCTL_ENABLE_MASK;
		writel(value, priv->io + KIRKWOOD_RECCTL);
		break;

	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
	case SNDRV_PCM_TRIGGER_SUSPEND:
		value = readl(priv->io + KIRKWOOD_RECCTL);
		value |= KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE;
		writel(value, priv->io + KIRKWOOD_RECCTL);
		break;

	case SNDRV_PCM_TRIGGER_RESUME:
	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
		value = readl(priv->io + KIRKWOOD_RECCTL);
		value &= ~(KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE);
		writel(value, priv->io + KIRKWOOD_RECCTL);
		break;

	default:
		return -EINVAL;
	}

	return 0;
}
コード例 #22
0
ファイル: mxs-saif.c プロジェクト: avagin/linux
/*
 * Should only be called when port is inactive.
 * although can be called multiple times by upper layers.
 */
static int mxs_saif_hw_params(struct snd_pcm_substream *substream,
			     struct snd_pcm_hw_params *params,
			     struct snd_soc_dai *cpu_dai)
{
	struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai);
	struct mxs_saif *master_saif;
	u32 scr, stat;
	int ret;

	master_saif = mxs_saif_get_master(saif);
	if (!master_saif)
		return -EINVAL;

	/* mclk should already be set */
	if (!saif->mclk && saif->mclk_in_use) {
		dev_err(cpu_dai->dev, "set mclk first\n");
		return -EINVAL;
	}

	stat = __raw_readl(saif->base + SAIF_STAT);
	if (!saif->mclk_in_use && (stat & BM_SAIF_STAT_BUSY)) {
		dev_err(cpu_dai->dev, "error: busy\n");
		return -EBUSY;
	}

	/*
	 * Set saif clk based on sample rate.
	 * If mclk is used, we also set mclk, if not, saif->mclk is
	 * default 0, means not used.
	 */
	ret = mxs_saif_set_clk(saif, saif->mclk, params_rate(params));
	if (ret) {
		dev_err(cpu_dai->dev, "unable to get proper clk\n");
		return ret;
	}

	if (saif != master_saif) {
		/*
		* Set an initial clock rate for the saif internal logic to work
		* properly. This is important when working in EXTMASTER mode
		* that uses the other saif's BITCLK&LRCLK but it still needs a
		* basic clock which should be fast enough for the internal
		* logic.
		*/
		clk_enable(saif->clk);
		ret = clk_set_rate(saif->clk, 24000000);
		clk_disable(saif->clk);
		if (ret)
			return ret;

		ret = clk_prepare(master_saif->clk);
		if (ret)
			return ret;
	}

	scr = __raw_readl(saif->base + SAIF_CTRL);

	scr &= ~BM_SAIF_CTRL_WORD_LENGTH;
	scr &= ~BM_SAIF_CTRL_BITCLK_48XFS_ENABLE;
	switch (params_format(params)) {
	case SNDRV_PCM_FORMAT_S16_LE:
		scr |= BF_SAIF_CTRL_WORD_LENGTH(0);
		break;
	case SNDRV_PCM_FORMAT_S20_3LE:
		scr |= BF_SAIF_CTRL_WORD_LENGTH(4);
		scr |= BM_SAIF_CTRL_BITCLK_48XFS_ENABLE;
		break;
	case SNDRV_PCM_FORMAT_S24_LE:
		scr |= BF_SAIF_CTRL_WORD_LENGTH(8);
		scr |= BM_SAIF_CTRL_BITCLK_48XFS_ENABLE;
		break;
	default:
		return -EINVAL;
	}

	/* Tx/Rx config */
	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
		/* enable TX mode */
		scr &= ~BM_SAIF_CTRL_READ_MODE;
	} else {
		/* enable RX mode */
		scr |= BM_SAIF_CTRL_READ_MODE;
	}

	__raw_writel(scr, saif->base + SAIF_CTRL);
	return 0;
}
コード例 #23
0
ファイル: mmp-sspa.c プロジェクト: 3null/linux
/*
 * Set up the sspa dai format. The sspa port must be inactive
 * before calling this function as the physical
 * interface format is changed.
 */
static int mmp_sspa_set_dai_fmt(struct snd_soc_dai *cpu_dai,
				 unsigned int fmt)
{
	struct sspa_priv *sspa_priv = snd_soc_dai_get_drvdata(cpu_dai);
	struct ssp_device *sspa = sspa_priv->sspa;
	u32 sspa_sp, sspa_ctrl;

	/* check if we need to change anything at all */
	if (sspa_priv->dai_fmt == fmt)
		return 0;

	/* we can only change the settings if the port is not in use */
	if ((mmp_sspa_read_reg(sspa, SSPA_TXSP) & SSPA_SP_S_EN) ||
	    (mmp_sspa_read_reg(sspa, SSPA_RXSP) & SSPA_SP_S_EN)) {
		dev_err(&sspa->pdev->dev,
			"can't change hardware dai format: stream is in use\n");
		return -EINVAL;
	}

	/* reset port settings */
	sspa_sp   = SSPA_SP_WEN | SSPA_SP_S_RST | SSPA_SP_FFLUSH;
	sspa_ctrl = 0;

	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
	case SND_SOC_DAIFMT_CBS_CFS:
		sspa_sp |= SSPA_SP_MSL;
		break;
	case SND_SOC_DAIFMT_CBM_CFM:
		break;
	default:
		return -EINVAL;
	}

	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
	case SND_SOC_DAIFMT_NB_NF:
		sspa_sp |= SSPA_SP_FSP;
		break;
	default:
		return -EINVAL;
	}

	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
	case SND_SOC_DAIFMT_I2S:
		sspa_sp |= SSPA_TXSP_FPER(63);
		sspa_sp |= SSPA_SP_FWID(31);
		sspa_ctrl |= SSPA_CTL_XDATDLY(1);
		break;
	default:
		return -EINVAL;
	}

	mmp_sspa_write_reg(sspa, SSPA_TXSP, sspa_sp);
	mmp_sspa_write_reg(sspa, SSPA_RXSP, sspa_sp);

	sspa_sp &= ~(SSPA_SP_S_RST | SSPA_SP_FFLUSH);
	mmp_sspa_write_reg(sspa, SSPA_TXSP, sspa_sp);
	mmp_sspa_write_reg(sspa, SSPA_RXSP, sspa_sp);

	/*
	 * FIXME: hw issue, for the tx serial port,
	 * can not config the master/slave mode;
	 * so must clean this bit.
	 * The master/slave mode has been set in the
	 * rx port.
	 */
	sspa_sp &= ~SSPA_SP_MSL;
	mmp_sspa_write_reg(sspa, SSPA_TXSP, sspa_sp);

	mmp_sspa_write_reg(sspa, SSPA_TXCTL, sspa_ctrl);
	mmp_sspa_write_reg(sspa, SSPA_RXCTL, sspa_ctrl);

	/* Since we are configuring the timings for the format by hand
	 * we have to defer some things until hw_params() where we
	 * know parameters like the sample size.
	 */
	sspa_priv->dai_fmt = fmt;
	return 0;
}
コード例 #24
0
ファイル: mxs-saif.c プロジェクト: avagin/linux
static int mxs_saif_trigger(struct snd_pcm_substream *substream, int cmd,
				struct snd_soc_dai *cpu_dai)
{
	struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai);
	struct mxs_saif *master_saif;
	u32 delay;
	int ret;

	master_saif = mxs_saif_get_master(saif);
	if (!master_saif)
		return -EINVAL;

	switch (cmd) {
	case SNDRV_PCM_TRIGGER_START:
	case SNDRV_PCM_TRIGGER_RESUME:
	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
		if (saif->state == MXS_SAIF_STATE_RUNNING)
			return 0;

		dev_dbg(cpu_dai->dev, "start\n");

		ret = clk_enable(master_saif->clk);
		if (ret) {
			dev_err(saif->dev, "Failed to enable master clock\n");
			return ret;
		}

		/*
		 * If the saif's master is not itself, we also need to enable
		 * itself clk for its internal basic logic to work.
		 */
		if (saif != master_saif) {
			ret = clk_enable(saif->clk);
			if (ret) {
				dev_err(saif->dev, "Failed to enable master clock\n");
				clk_disable(master_saif->clk);
				return ret;
			}

			__raw_writel(BM_SAIF_CTRL_RUN,
				saif->base + SAIF_CTRL + MXS_SET_ADDR);
		}

		if (!master_saif->mclk_in_use)
			__raw_writel(BM_SAIF_CTRL_RUN,
				master_saif->base + SAIF_CTRL + MXS_SET_ADDR);

		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
			/*
			 * write data to saif data register to trigger
			 * the transfer.
			 * For 24-bit format the 32-bit FIFO register stores
			 * only one channel, so we need to write twice.
			 * This is also safe for the other non 24-bit formats.
			 */
			__raw_writel(0, saif->base + SAIF_DATA);
			__raw_writel(0, saif->base + SAIF_DATA);
		} else {
			/*
			 * read data from saif data register to trigger
			 * the receive.
			 * For 24-bit format the 32-bit FIFO register stores
			 * only one channel, so we need to read twice.
			 * This is also safe for the other non 24-bit formats.
			 */
			__raw_readl(saif->base + SAIF_DATA);
			__raw_readl(saif->base + SAIF_DATA);
		}

		master_saif->ongoing = 1;
		saif->state = MXS_SAIF_STATE_RUNNING;

		dev_dbg(saif->dev, "CTRL 0x%x STAT 0x%x\n",
			__raw_readl(saif->base + SAIF_CTRL),
			__raw_readl(saif->base + SAIF_STAT));

		dev_dbg(master_saif->dev, "CTRL 0x%x STAT 0x%x\n",
			__raw_readl(master_saif->base + SAIF_CTRL),
			__raw_readl(master_saif->base + SAIF_STAT));
		break;
	case SNDRV_PCM_TRIGGER_SUSPEND:
	case SNDRV_PCM_TRIGGER_STOP:
	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
		if (saif->state == MXS_SAIF_STATE_STOPPED)
			return 0;

		dev_dbg(cpu_dai->dev, "stop\n");

		/* wait a while for the current sample to complete */
		delay = USEC_PER_SEC / master_saif->cur_rate;

		if (!master_saif->mclk_in_use) {
			__raw_writel(BM_SAIF_CTRL_RUN,
				master_saif->base + SAIF_CTRL + MXS_CLR_ADDR);
			udelay(delay);
		}
		clk_disable(master_saif->clk);

		if (saif != master_saif) {
			__raw_writel(BM_SAIF_CTRL_RUN,
				saif->base + SAIF_CTRL + MXS_CLR_ADDR);
			udelay(delay);
			clk_disable(saif->clk);
		}

		master_saif->ongoing = 0;
		saif->state = MXS_SAIF_STATE_STOPPED;

		break;
	default:
		return -EINVAL;
	}

	return 0;
}
コード例 #25
0
ファイル: kirkwood-i2s.c プロジェクト: Scorpio92/mstar6a918
static int kirkwood_i2s_play_trigger(struct snd_pcm_substream *substream,
                                     int cmd, struct snd_soc_dai *dai)
{
    struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai);
    unsigned long value;

    /*
     * specs says KIRKWOOD_PLAYCTL must be read 2 times before
     * changing it. So read 1 time here and 1 later.
     */
    value = readl(priv->io + KIRKWOOD_PLAYCTL);

    switch (cmd) {
    case SNDRV_PCM_TRIGGER_START:
        /* stop audio, enable interrupts */
        value = readl(priv->io + KIRKWOOD_PLAYCTL);
        value |= KIRKWOOD_PLAYCTL_PAUSE;
        writel(value, priv->io + KIRKWOOD_PLAYCTL);

        value = readl(priv->io + KIRKWOOD_INT_MASK);
        value |= KIRKWOOD_INT_CAUSE_PLAY_BYTES;
        writel(value, priv->io + KIRKWOOD_INT_MASK);

        /* configure audio & enable i2s playback */
        value = readl(priv->io + KIRKWOOD_PLAYCTL);
        value &= ~KIRKWOOD_PLAYCTL_BURST_MASK;
        value &= ~(KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE
                   | KIRKWOOD_PLAYCTL_SPDIF_EN);

        if (priv->burst == 32)
            value |= KIRKWOOD_PLAYCTL_BURST_32;
        else
            value |= KIRKWOOD_PLAYCTL_BURST_128;
        value |= KIRKWOOD_PLAYCTL_I2S_EN;
        writel(value, priv->io + KIRKWOOD_PLAYCTL);
        break;

    case SNDRV_PCM_TRIGGER_STOP:
        /* stop audio, disable interrupts */
        value = readl(priv->io + KIRKWOOD_PLAYCTL);
        value |= KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE;
        writel(value, priv->io + KIRKWOOD_PLAYCTL);

        value = readl(priv->io + KIRKWOOD_INT_MASK);
        value &= ~KIRKWOOD_INT_CAUSE_PLAY_BYTES;
        writel(value, priv->io + KIRKWOOD_INT_MASK);

        /* disable all playbacks */
        value = readl(priv->io + KIRKWOOD_PLAYCTL);
        value &= ~(KIRKWOOD_PLAYCTL_I2S_EN | KIRKWOOD_PLAYCTL_SPDIF_EN);
        writel(value, priv->io + KIRKWOOD_PLAYCTL);
        break;

    case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
    case SNDRV_PCM_TRIGGER_SUSPEND:
        value = readl(priv->io + KIRKWOOD_PLAYCTL);
        value |= KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE;
        writel(value, priv->io + KIRKWOOD_PLAYCTL);
        break;

    case SNDRV_PCM_TRIGGER_RESUME:
    case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
        value = readl(priv->io + KIRKWOOD_PLAYCTL);
        value &= ~(KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE);
        writel(value, priv->io + KIRKWOOD_PLAYCTL);
        break;

    default:
        return -EINVAL;
    }

    return 0;
}
コード例 #26
0
static int tegra_max98095_hw_params(struct snd_pcm_substream *substream,
					struct snd_pcm_hw_params *params)
{
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
	struct snd_soc_dai *codec_dai = rtd->codec_dai;
	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
	struct snd_soc_codec *codec = rtd->codec;
	struct snd_soc_card *card = codec->card;
	struct tegra_max98095 *machine = snd_soc_card_get_drvdata(card);
#ifndef CONFIG_ARCH_TEGRA_2x_SOC
	struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(cpu_dai);
#endif
	unsigned int srate, mclk, sample_size;
	int err;

	switch (params_format(params)) {
	case SNDRV_PCM_FORMAT_S16_LE:
		sample_size = 16;
		break;
	default:
		return -EINVAL;
	}

	srate = params_rate(params);
	switch (srate) {
	case 8000:
	case 16000:
	case 24000:
	case 32000:
	case 48000:
	case 64000:
	case 96000:
		mclk = 12288000;
		break;
	case 11025:
	case 22050:
	case 44100:
	case 88200:
		mclk = 11289600;
		break;
	default:
		mclk = 12000000;
		break;
	}

	err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk);
	if (err < 0) {
		if (!(machine->util_data.set_mclk % mclk))
			mclk = machine->util_data.set_mclk;
		else {
			dev_err(card->dev, "Can't configure clocks\n");
			return err;
		}
	}

	tegra_asoc_utils_lock_clk_rate(&machine->util_data, 1);

	err = snd_soc_dai_set_fmt(codec_dai,
					SND_SOC_DAIFMT_I2S |
					SND_SOC_DAIFMT_NB_NF |
					SND_SOC_DAIFMT_CBS_CFS);
	if (err < 0) {
		dev_err(card->dev, "codec_dai fmt not set\n");
		return err;
	}

	err = snd_soc_dai_set_fmt(cpu_dai,
					SND_SOC_DAIFMT_I2S |
					SND_SOC_DAIFMT_NB_NF |
					SND_SOC_DAIFMT_CBS_CFS);
	if (err < 0) {
		dev_err(card->dev, "cpu_dai fmt not set\n");
		return err;
	}

	err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
					SND_SOC_CLOCK_IN);
	if (err < 0) {
		dev_err(card->dev, "codec_dai clock not set\n");
		return err;
	}

#ifndef CONFIG_ARCH_TEGRA_2x_SOC
	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
		tegra_max98095_set_dam_cif(i2s->dam_ifc, srate,
				params_channels(params), sample_size);
#endif

	return 0;
}
コード例 #27
0
ファイル: rockchip_i2s.c プロジェクト: a2hojsjsjs/linux
static inline struct rk_i2s_dev *to_info(struct snd_soc_dai *dai)
{
	return snd_soc_dai_get_drvdata(dai);
}
コード例 #28
0
static int tegra_max98095_init(struct snd_soc_pcm_runtime *rtd)
{
	struct snd_soc_codec *codec = rtd->codec;
	struct snd_soc_dapm_context *dapm = &codec->dapm;
	struct snd_soc_card *card = codec->card;
	struct tegra_max98095 *machine = snd_soc_card_get_drvdata(card);
	struct tegra_asoc_platform_data *pdata = machine->pdata;
#ifndef CONFIG_ARCH_TEGRA_2x_SOC
	struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(rtd->cpu_dai);
#endif
	int ret;

#ifndef CONFIG_ARCH_TEGRA_2x_SOC
	if (machine->codec_info[BASEBAND].i2s_id != -1)
		i2s->is_dam_used = true;
#endif

	if (machine->init_done)
		return 0;

	machine->init_done = true;

	if (gpio_is_valid(pdata->gpio_spkr_en)) {
		ret = gpio_request(pdata->gpio_spkr_en, "spkr_en");
		if (ret) {
			dev_err(card->dev, "cannot get spkr_en gpio\n");
			return ret;
		}
		machine->gpio_requested |= GPIO_SPKR_EN;

		gpio_direction_output(pdata->gpio_spkr_en, 0);
	}

	if (gpio_is_valid(pdata->gpio_hp_mute)) {
		ret = gpio_request(pdata->gpio_hp_mute, "hp_mute");
		if (ret) {
			dev_err(card->dev, "cannot get hp_mute gpio\n");
			return ret;
		}
		machine->gpio_requested |= GPIO_HP_MUTE;

		gpio_direction_output(pdata->gpio_hp_mute, 0);
	}

	if (gpio_is_valid(pdata->gpio_int_mic_en)) {
		ret = gpio_request(pdata->gpio_int_mic_en, "int_mic_en");
		if (ret) {
			dev_err(card->dev, "cannot get int_mic_en gpio\n");
			return ret;
		}
		machine->gpio_requested |= GPIO_INT_MIC_EN;

		/* Disable int mic; enable signal is active-high */
		gpio_direction_output(pdata->gpio_int_mic_en, 0);
	}

	if (gpio_is_valid(pdata->gpio_ext_mic_en)) {
		ret = gpio_request(pdata->gpio_ext_mic_en, "ext_mic_en");
		if (ret) {
			dev_err(card->dev, "cannot get ext_mic_en gpio\n");
			return ret;
		}
		machine->gpio_requested |= GPIO_EXT_MIC_EN;

		/* Enable ext mic; enable signal is active-low */
		gpio_direction_output(pdata->gpio_ext_mic_en, 0);
	}

	ret = snd_soc_add_controls(codec, tegra_max98095_controls,
				   ARRAY_SIZE(tegra_max98095_controls));
	if (ret < 0)
		return ret;

	snd_soc_dapm_new_controls(dapm, tegra_max98095_dapm_widgets,
			ARRAY_SIZE(tegra_max98095_dapm_widgets));

	snd_soc_dapm_add_routes(dapm, enterprise_audio_map,
			ARRAY_SIZE(enterprise_audio_map));

	ret = snd_soc_jack_new(codec, "Headset Jack", SND_JACK_HEADSET,
			&tegra_max98095_hp_jack);
	if (ret < 0)
		return ret;

#ifdef CONFIG_SWITCH
	snd_soc_jack_notifier_register(&tegra_max98095_hp_jack,
		&headset_switch_nb);
#else /*gpio based headset detection*/
	snd_soc_jack_add_pins(&tegra_max98095_hp_jack,
		ARRAY_SIZE(tegra_max98095_hp_jack_pins),
		tegra_max98095_hp_jack_pins);
#endif

	/* max98095_headset_detect(codec, &tegra_max98095_hp_jack,
		SND_JACK_HEADSET); */

	snd_soc_dapm_nc_pin(dapm, "INA1");
	snd_soc_dapm_nc_pin(dapm, "INA2");
	snd_soc_dapm_nc_pin(dapm, "INB1");
	snd_soc_dapm_nc_pin(dapm, "INB2");
	snd_soc_dapm_sync(dapm);

	return 0;
}
コード例 #29
0
/**
 * This function mainly configures the clock frequency of MCLK (HCKT/HCKR)
 *
 * @Parameters:
 * clk_id: The clock source of HCKT/HCKR
 *	  (Input from outside; output from inside, FSYS or EXTAL)
 * freq: The required clock rate of HCKT/HCKR
 * dir: The clock direction of HCKT/HCKR
 *
 * Note: If the direction is input, we do not care about clk_id.
 */
static int fsl_esai_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
                                   unsigned int freq, int dir)
{
    struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai);
    struct clk *clksrc = esai_priv->extalclk;
    bool tx = clk_id <= ESAI_HCKT_EXTAL;
    bool in = dir == SND_SOC_CLOCK_IN;
    u32 ret, ratio, ecr = 0;
    unsigned long clk_rate;

    /* sck_div can be only bypassed if ETO/ERO=0 and SNC_SOC_CLOCK_OUT */
    esai_priv->sck_div[tx] = true;

    /* Set the direction of HCKT/HCKR pins */
    regmap_update_bits(esai_priv->regmap, REG_ESAI_xCCR(tx),
                       ESAI_xCCR_xHCKD, in ? 0 : ESAI_xCCR_xHCKD);

    if (in)
        goto out;

    switch (clk_id) {
    case ESAI_HCKT_FSYS:
    case ESAI_HCKR_FSYS:
        clksrc = esai_priv->fsysclk;
        break;
    case ESAI_HCKT_EXTAL:
        ecr |= ESAI_ECR_ETI;
    case ESAI_HCKR_EXTAL:
        ecr |= ESAI_ECR_ERI;
        break;
    default:
        return -EINVAL;
    }

    if (IS_ERR(clksrc)) {
        dev_err(dai->dev, "no assigned %s clock\n",
                clk_id % 2 ? "extal" : "fsys");
        return PTR_ERR(clksrc);
    }
    clk_rate = clk_get_rate(clksrc);

    ratio = clk_rate / freq;
    if (ratio * freq > clk_rate)
        ret = ratio * freq - clk_rate;
    else if (ratio * freq < clk_rate)
        ret = clk_rate - ratio * freq;
    else
        ret = 0;

    /* Block if clock source can not be divided into the required rate */
    if (ret != 0 && clk_rate / ret < 1000) {
        dev_err(dai->dev, "failed to derive required HCK%c rate\n",
                tx ? 'T' : 'R');
        return -EINVAL;
    }

    if (ratio == 1) {
        /* Bypass all the dividers if not being needed */
        ecr |= tx ? ESAI_ECR_ETO : ESAI_ECR_ERO;
        goto out;
    }

    ret = fsl_esai_divisor_cal(dai, tx, ratio, false, 0);
    if (ret)
        return ret;

    esai_priv->sck_div[tx] = false;

out:
    esai_priv->hck_rate[tx] = freq;

    regmap_update_bits(esai_priv->regmap, REG_ESAI_ECR,
                       tx ? ESAI_ECR_ETI | ESAI_ECR_ETO :
                       ESAI_ECR_ERI | ESAI_ECR_ERO, ecr);

    return 0;
}
コード例 #30
0
static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream,
				 struct snd_pcm_hw_params *params,
				 struct snd_soc_dai *dai)
{
	struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);

	unsigned int sampling_rate = params_rate(params);
	unsigned int data_length, data_delay, bclk_ratio;
	unsigned int ch1pos, ch2pos, mode, format;
	unsigned int mash = BCM2835_CLK_MASH_1;
	unsigned int divi, divf, target_frequency;
	int clk_src = -1;
	unsigned int master = dev->fmt & SND_SOC_DAIFMT_MASTER_MASK;
	bool bit_master =	(master == SND_SOC_DAIFMT_CBS_CFS
					|| master == SND_SOC_DAIFMT_CBS_CFM);

	bool frame_master =	(master == SND_SOC_DAIFMT_CBS_CFS
					|| master == SND_SOC_DAIFMT_CBM_CFS);
	uint32_t csreg;

	/*
	 * If a stream is already enabled,
	 * the registers are already set properly.
	 */
	regmap_read(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, &csreg);

	if (csreg & (BCM2835_I2S_TXON | BCM2835_I2S_RXON))
		return 0;

	/*
	 * Adjust the data length according to the format.
	 * We prefill the half frame length with an integer
	 * divider of 2400 as explained at the clock settings.
	 * Maybe it is overwritten there, if the Integer mode
	 * does not apply.
	 */
	switch (params_format(params)) {
	case SNDRV_PCM_FORMAT_S16_LE:
		data_length = 16;
		bclk_ratio = 40;
		break;
	case SNDRV_PCM_FORMAT_S32_LE:
		data_length = 32;
		bclk_ratio = 80;
		break;
	default:
		return -EINVAL;
	}

	/* If bclk_ratio already set, use that one. */
	if (dev->bclk_ratio)
		bclk_ratio = dev->bclk_ratio;

	/*
	 * Clock Settings
	 *
	 * The target frequency of the bit clock is
	 *	sampling rate * frame length
	 *
	 * Integer mode:
	 * Sampling rates that are multiples of 8000 kHz
	 * can be driven by the oscillator of 19.2 MHz
	 * with an integer divider as long as the frame length
	 * is an integer divider of 19200000/8000=2400 as set up above.
	 * This is no longer possible if the sampling rate
	 * is too high (e.g. 192 kHz), because the oscillator is too slow.
	 *
	 * MASH mode:
	 * For all other sampling rates, it is not possible to
	 * have an integer divider. Approximate the clock
	 * with the MASH module that induces a slight frequency
	 * variance. To minimize that it is best to have the fastest
	 * clock here. That is PLLD with 500 MHz.
	 */
	target_frequency = sampling_rate * bclk_ratio;
	clk_src = BCM2835_CLK_SRC_OSC;
	mash = BCM2835_CLK_MASH_0;

	if (bcm2835_clk_freq[clk_src] % target_frequency == 0
			&& bit_master && frame_master) {
		divi = bcm2835_clk_freq[clk_src] / target_frequency;
		divf = 0;
	} else {
		uint64_t dividend;

		if (!dev->bclk_ratio) {
			/*
			 * Overwrite bclk_ratio, because the
			 * above trick is not needed or can
			 * not be used.
			 */
			bclk_ratio = 2 * data_length;
		}

		target_frequency = sampling_rate * bclk_ratio;

		clk_src = BCM2835_CLK_SRC_PLLD;
		mash = BCM2835_CLK_MASH_1;

		dividend = bcm2835_clk_freq[clk_src];
		dividend <<= BCM2835_CLK_SHIFT;
		do_div(dividend, target_frequency);
		divi = dividend >> BCM2835_CLK_SHIFT;
		divf = dividend & BCM2835_CLK_DIVF_MASK;
	}

	/* Set clock divider */
	regmap_write(dev->clk_regmap, BCM2835_CLK_PCMDIV_REG, BCM2835_CLK_PASSWD
			| BCM2835_CLK_DIVI(divi)
			| BCM2835_CLK_DIVF(divf));

	/* Setup clock, but don't start it yet */
	regmap_write(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG, BCM2835_CLK_PASSWD
			| BCM2835_CLK_MASH(mash)
			| BCM2835_CLK_SRC(clk_src));

	/* Setup the frame format */
	format = BCM2835_I2S_CHEN;

	if (data_length > 24)
		format |= BCM2835_I2S_CHWEX;

	format |= BCM2835_I2S_CHWID((data_length-8)&0xf);

	switch (dev->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
	case SND_SOC_DAIFMT_I2S:
		data_delay = 1;
		break;
	default:
		/*
		 * TODO
		 * Others are possible but are not implemented at the moment.
		 */
		dev_err(dev->dev, "%s:bad format\n", __func__);
		return -EINVAL;
	}

	ch1pos = data_delay;
	ch2pos = bclk_ratio / 2 + data_delay;

	switch (params_channels(params)) {
	case 2:
		format = BCM2835_I2S_CH1(format) | BCM2835_I2S_CH2(format);
		format |= BCM2835_I2S_CH1(BCM2835_I2S_CHPOS(ch1pos));
		format |= BCM2835_I2S_CH2(BCM2835_I2S_CHPOS(ch2pos));
		break;
	default:
		return -EINVAL;
	}

	/*
	 * Set format for both streams.
	 * We cannot set another frame length
	 * (and therefore word length) anyway,
	 * so the format will be the same.
	 */
	regmap_write(dev->i2s_regmap, BCM2835_I2S_RXC_A_REG, format);
	regmap_write(dev->i2s_regmap, BCM2835_I2S_TXC_A_REG, format);

	/* Setup the I2S mode */
	mode = 0;

	if (data_length <= 16) {
		/*
		 * Use frame packed mode (2 channels per 32 bit word)
		 * We cannot set another frame length in the second stream
		 * (and therefore word length) anyway,
		 * so the format will be the same.
		 */
		mode |= BCM2835_I2S_FTXP | BCM2835_I2S_FRXP;
	}

	mode |= BCM2835_I2S_FLEN(bclk_ratio - 1);
	mode |= BCM2835_I2S_FSLEN(bclk_ratio / 2);

	/* Master or slave? */
	switch (dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) {
	case SND_SOC_DAIFMT_CBS_CFS:
		/* CPU is master */
		break;
	case SND_SOC_DAIFMT_CBM_CFS:
		/*
		 * CODEC is bit clock master
		 * CPU is frame master
		 */
		mode |= BCM2835_I2S_CLKM;
		break;
	case SND_SOC_DAIFMT_CBS_CFM:
		/*
		 * CODEC is frame master
		 * CPU is bit clock master
		 */
		mode |= BCM2835_I2S_FSM;
		break;
	case SND_SOC_DAIFMT_CBM_CFM:
		/* CODEC is master */
		mode |= BCM2835_I2S_CLKM;
		mode |= BCM2835_I2S_FSM;
		break;
	default:
		dev_err(dev->dev, "%s:bad master\n", __func__);
		return -EINVAL;
	}

	/*
	 * Invert clocks?
	 *
	 * The BCM approach seems to be inverted to the classical I2S approach.
	 */
	switch (dev->fmt & SND_SOC_DAIFMT_INV_MASK) {
	case SND_SOC_DAIFMT_NB_NF:
		/* None. Therefore, both for BCM */
		mode |= BCM2835_I2S_CLKI;
		mode |= BCM2835_I2S_FSI;
		break;
	case SND_SOC_DAIFMT_IB_IF:
		/* Both. Therefore, none for BCM */
		break;
	case SND_SOC_DAIFMT_NB_IF:
		/*
		 * Invert only frame sync. Therefore,
		 * invert only bit clock for BCM
		 */
		mode |= BCM2835_I2S_CLKI;
		break;
	case SND_SOC_DAIFMT_IB_NF:
		/*
		 * Invert only bit clock. Therefore,
		 * invert only frame sync for BCM
		 */
		mode |= BCM2835_I2S_FSI;
		break;
	default:
		return -EINVAL;
	}

	regmap_write(dev->i2s_regmap, BCM2835_I2S_MODE_A_REG, mode);

	/* Setup the DMA parameters */
	regmap_update_bits(dev->i2s_regmap, BCM2835_I2S_CS_A_REG,
			BCM2835_I2S_RXTHR(1)
			| BCM2835_I2S_TXTHR(1)
			| BCM2835_I2S_DMAEN, 0xffffffff);

	regmap_update_bits(dev->i2s_regmap, BCM2835_I2S_DREQ_A_REG,
			  BCM2835_I2S_TX_PANIC(0x10)
			| BCM2835_I2S_RX_PANIC(0x30)
			| BCM2835_I2S_TX(0x30)
			| BCM2835_I2S_RX(0x20), 0xffffffff);

	/* Clear FIFOs */
	bcm2835_i2s_clear_fifos(dev, true, true);

	return 0;
}