/** * 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; }
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; }
/** * 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; }
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); }
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; }
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; }
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; }
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; }
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; }
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; }
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; }
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; }
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; }
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; }
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; }
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; }
/* * 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; }
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; }
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; }
/* * 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; }
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; }
/* * 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; }
/* * 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; }
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; }
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; }
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; }
static inline struct rk_i2s_dev *to_info(struct snd_soc_dai *dai) { return snd_soc_dai_get_drvdata(dai); }
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; }
/** * 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; }
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; }