static int dw_i2s_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) i2s_write_reg(dev->i2s_base, TXFFR, 1); else i2s_write_reg(dev->i2s_base, RXFFR, 1); return 0; }
static inline void i2s_disable_channels(struct dw_i2s_dev *dev, u32 stream) { u32 i = 0; if (stream == SNDRV_PCM_STREAM_PLAYBACK) { for (i = 0; i < 4; i++) i2s_write_reg(dev->i2s_base, TER(i), 0); } else { for (i = 0; i < 4; i++) i2s_write_reg(dev->i2s_base, RER(i), 0); } }
static inline void i2s_clear_irqs(struct dw_i2s_dev *dev, u32 stream) { u32 i = 0; if (stream == SNDRV_PCM_STREAM_PLAYBACK) { for (i = 0; i < 4; i++) i2s_write_reg(dev->i2s_base, TOR(i), 0); } else { for (i = 0; i < 4; i++) i2s_write_reg(dev->i2s_base, ROR(i), 0); } }
static void i2s_start(struct dw_i2s_dev *dev, struct snd_pcm_substream *substream) { i2s_write_reg(dev->i2s_base, IER, 1); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) i2s_write_reg(dev->i2s_base, ITER, 1); else i2s_write_reg(dev->i2s_base, IRER, 1); i2s_write_reg(dev->i2s_base, CER, 1); }
static void i2s_start(struct dw_i2s_dev *dev, struct snd_pcm_substream *substream) { struct i2s_clk_config_data *config = &dev->config; i2s_write_reg(dev->i2s_base, IER, 1); i2s_enable_irqs(dev, substream->stream, config->chan_nr); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) i2s_write_reg(dev->i2s_base, ITER, 1); else i2s_write_reg(dev->i2s_base, IRER, 1); i2s_write_reg(dev->i2s_base, CER, 1); }
static inline void i2s_enable_irqs(struct dw_i2s_dev *dev, u32 stream, int chan_nr) { u32 i, irq; if (stream == SNDRV_PCM_STREAM_PLAYBACK) { for (i = 0; i < (chan_nr / 2); i++) { irq = i2s_read_reg(dev->i2s_base, IMR(i)); i2s_write_reg(dev->i2s_base, IMR(i), irq & ~0x30); } } else { for (i = 0; i < (chan_nr / 2); i++) { irq = i2s_read_reg(dev->i2s_base, IMR(i)); i2s_write_reg(dev->i2s_base, IMR(i), irq & ~0x03); } } }
static void i2s_stop(struct dw_i2s_dev *dev, struct snd_pcm_substream *substream) { i2s_clear_irqs(dev, substream->stream); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) i2s_write_reg(dev->i2s_base, ITER, 0); else i2s_write_reg(dev->i2s_base, IRER, 0); i2s_disable_irqs(dev, substream->stream, 8); if (!dev->active) { i2s_write_reg(dev->i2s_base, CER, 0); i2s_write_reg(dev->i2s_base, IER, 0); } }
static void i2s_stop(struct dw_i2s_dev *dev, struct snd_pcm_substream *substream) { u32 i = 0, irq; i2s_clear_irqs(dev, substream->stream); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { i2s_write_reg(dev->i2s_base, ITER, 0); for (i = 0; i < 4; i++) { irq = i2s_read_reg(dev->i2s_base, IMR(i)); i2s_write_reg(dev->i2s_base, IMR(i), irq | 0x30); } } else { i2s_write_reg(dev->i2s_base, IRER, 0); for (i = 0; i < 4; i++) { irq = i2s_read_reg(dev->i2s_base, IMR(i)); i2s_write_reg(dev->i2s_base, IMR(i), irq | 0x03); } } if (!dev->active) { i2s_write_reg(dev->i2s_base, CER, 0); i2s_write_reg(dev->i2s_base, IER, 0); } }
static void dw_i2s_config(struct dw_i2s_dev *dev, int stream) { u32 ch_reg; struct i2s_clk_config_data *config = &dev->config; i2s_disable_channels(dev, stream); for (ch_reg = 0; ch_reg < (config->chan_nr / 2); ch_reg++) { if (stream == SNDRV_PCM_STREAM_PLAYBACK) { i2s_write_reg(dev->i2s_base, TCR(ch_reg), dev->xfer_resolution); i2s_write_reg(dev->i2s_base, TFCR(ch_reg), dev->fifo_th - 1); i2s_write_reg(dev->i2s_base, TER(ch_reg), 1); } else { i2s_write_reg(dev->i2s_base, RCR(ch_reg), dev->xfer_resolution); i2s_write_reg(dev->i2s_base, RFCR(ch_reg), dev->fifo_th - 1); i2s_write_reg(dev->i2s_base, RER(ch_reg), 1); } } }
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: ch_reg = 3; break; case SIX_CHANNEL_SUPPORT: ch_reg = 2; break; case FOUR_CHANNEL_SUPPORT: ch_reg = 1; break; case TWO_CHANNEL_SUPPORT: ch_reg = 0; break; default: dev_err(dev->dev, "channel not supported\n"); return -EINVAL; } i2s_disable_channels(dev, substream->stream); 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) return -EINVAL; ret = dev->i2s_clk_cfg(config); if (ret < 0) { dev_err(dev->dev, "runtime audio clk config fail\n"); return ret; } 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; int ret; switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: config->data_width = 16; dev->ccr = 0x00; dev->xfer_resolution = 0x02; break; case SNDRV_PCM_FORMAT_S24_LE: config->data_width = 24; dev->ccr = 0x08; dev->xfer_resolution = 0x04; break; case SNDRV_PCM_FORMAT_S32_LE: config->data_width = 32; dev->ccr = 0x10; dev->xfer_resolution = 0x05; break; default: dev_err(dev->dev, "designware-i2s: unsupported 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; } dw_i2s_config(dev, substream->stream); i2s_write_reg(dev->i2s_base, CCR, dev->ccr); config->sample_rate = params_rate(params); if (dev->capability & DW_I2S_MASTER) { 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; }