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 int dw_configure_dai(struct dw_i2s_dev *dev, struct snd_soc_dai_driver *dw_i2s_dai, unsigned int rates) { /* * Read component parameter registers to extract * the I2S block's configuration. */ u32 comp1 = i2s_read_reg(dev->i2s_base, dev->i2s_reg_comp1); u32 comp2 = i2s_read_reg(dev->i2s_base, dev->i2s_reg_comp2); u32 fifo_depth = 1 << (1 + COMP1_FIFO_DEPTH_GLOBAL(comp1)); u32 idx; if (dev->capability & DWC_I2S_RECORD && dev->quirks & DW_I2S_QUIRK_COMP_PARAM1) comp1 = comp1 & ~BIT(5); if (COMP1_TX_ENABLED(comp1)) { dev_dbg(dev->dev, " designware: play supported\n"); idx = COMP1_TX_WORDSIZE_0(comp1); if (WARN_ON(idx >= ARRAY_SIZE(formats))) return -EINVAL; if (dev->quirks & DW_I2S_QUIRK_16BIT_IDX_OVERRIDE) idx = 1; dw_i2s_dai->playback.channels_min = MIN_CHANNEL_NUM; dw_i2s_dai->playback.channels_max = 1 << (COMP1_TX_CHANNELS(comp1) + 1); dw_i2s_dai->playback.formats = formats[idx]; dw_i2s_dai->playback.rates = rates; } if (COMP1_RX_ENABLED(comp1)) { dev_dbg(dev->dev, "designware: record supported\n"); idx = COMP2_RX_WORDSIZE_0(comp2); if (WARN_ON(idx >= ARRAY_SIZE(formats))) return -EINVAL; if (dev->quirks & DW_I2S_QUIRK_16BIT_IDX_OVERRIDE) idx = 1; dw_i2s_dai->capture.channels_min = MIN_CHANNEL_NUM; dw_i2s_dai->capture.channels_max = 1 << (COMP1_RX_CHANNELS(comp1) + 1); dw_i2s_dai->capture.formats = formats[idx]; dw_i2s_dai->capture.rates = rates; } if (COMP1_MODE_EN(comp1)) { dev_dbg(dev->dev, "designware: i2s master mode supported\n"); dev->capability |= DW_I2S_MASTER; } else { dev_dbg(dev->dev, "designware: i2s slave mode supported\n"); dev->capability |= DW_I2S_SLAVE; } dev->fifo_th = fifo_depth / 2; return 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_read_reg(dev->i2s_base, TOR(i)); } else { for (i = 0; i < 4; i++) i2s_read_reg(dev->i2s_base, ROR(i)); } }
static int dw_configure_dai_by_pd(struct dw_i2s_dev *dev, struct snd_soc_dai_driver *dw_i2s_dai, struct resource *res, const struct i2s_platform_data *pdata) { u32 comp1 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_1); u32 idx = COMP1_APB_DATA_WIDTH(comp1); int ret; if (WARN_ON(idx >= ARRAY_SIZE(bus_widths))) return -EINVAL; ret = dw_configure_dai(dev, dw_i2s_dai, pdata->snd_rates); if (ret < 0) return ret; /* Set DMA slaves info */ dev->play_dma_data.pd.data = pdata->play_dma_data; dev->capture_dma_data.pd.data = pdata->capture_dma_data; dev->play_dma_data.pd.addr = res->start + I2S_TXDMA; dev->capture_dma_data.pd.addr = res->start + I2S_RXDMA; dev->play_dma_data.pd.max_burst = 16; dev->capture_dma_data.pd.max_burst = 16; dev->play_dma_data.pd.addr_width = bus_widths[idx]; dev->capture_dma_data.pd.addr_width = bus_widths[idx]; dev->play_dma_data.pd.filter = pdata->filter; dev->capture_dma_data.pd.filter = pdata->filter; return 0; }
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 int dw_configure_dai_by_dt(struct dw_i2s_dev *dev, struct snd_soc_dai_driver *dw_i2s_dai, struct resource *res) { u32 comp1 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_1); u32 comp2 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_2); u32 fifo_depth = 1 << (1 + COMP1_FIFO_DEPTH_GLOBAL(comp1)); u32 idx = COMP1_APB_DATA_WIDTH(comp1); u32 idx2; int ret; if (WARN_ON(idx >= ARRAY_SIZE(bus_widths))) return -EINVAL; ret = dw_configure_dai(dev, dw_i2s_dai, SNDRV_PCM_RATE_8000_192000); if (ret < 0) return ret; if (COMP1_TX_ENABLED(comp1)) { idx2 = COMP1_TX_WORDSIZE_0(comp1); dev->capability |= DWC_I2S_PLAY; dev->play_dma_data.dt.addr = res->start + I2S_TXDMA; dev->play_dma_data.dt.addr_width = bus_widths[idx]; dev->play_dma_data.dt.chan_name = "TX"; dev->play_dma_data.dt.fifo_size = fifo_depth * (fifo_width[idx2]) >> 8; dev->play_dma_data.dt.maxburst = 16; } if (COMP1_RX_ENABLED(comp1)) { idx2 = COMP2_RX_WORDSIZE_0(comp2); dev->capability |= DWC_I2S_RECORD; dev->capture_dma_data.dt.addr = res->start + I2S_RXDMA; dev->capture_dma_data.dt.addr_width = bus_widths[idx]; dev->capture_dma_data.dt.chan_name = "RX"; dev->capture_dma_data.dt.fifo_size = fifo_depth * (fifo_width[idx2] >> 8); dev->capture_dma_data.dt.maxburst = 16; } return 0; }
static int dw_configure_dai(struct dw_i2s_dev *dev, struct snd_soc_dai_driver *dw_i2s_dai, unsigned int rates) { /* * Read component parameter registers to extract * the I2S block's configuration. */ u32 comp1 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_1); u32 comp2 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_2); u32 idx; if (COMP1_TX_ENABLED(comp1)) { dev_dbg(dev->dev, " designware: play supported\n"); idx = COMP1_TX_WORDSIZE_0(comp1); if (WARN_ON(idx >= ARRAY_SIZE(formats))) return -EINVAL; dw_i2s_dai->playback.channels_min = MIN_CHANNEL_NUM; dw_i2s_dai->playback.channels_max = 1 << (COMP1_TX_CHANNELS(comp1) + 1); dw_i2s_dai->playback.formats = formats[idx]; dw_i2s_dai->playback.rates = rates; } if (COMP1_RX_ENABLED(comp1)) { dev_dbg(dev->dev, "designware: record supported\n"); idx = COMP2_RX_WORDSIZE_0(comp2); if (WARN_ON(idx >= ARRAY_SIZE(formats))) return -EINVAL; dw_i2s_dai->capture.channels_min = MIN_CHANNEL_NUM; dw_i2s_dai->capture.channels_max = 1 << (COMP1_RX_CHANNELS(comp1) + 1); dw_i2s_dai->capture.formats = formats[idx]; dw_i2s_dai->capture.rates = rates; } return 0; }
static irqreturn_t i2s_irq_handler(int irq, void *dev_id) { struct dw_i2s_dev *dev = dev_id; bool irq_valid = false; u32 isr[4]; int i; for (i = 0; i < 4; i++) isr[i] = i2s_read_reg(dev->i2s_base, ISR(i)); i2s_clear_irqs(dev, SNDRV_PCM_STREAM_PLAYBACK); i2s_clear_irqs(dev, SNDRV_PCM_STREAM_CAPTURE); for (i = 0; i < 4; i++) { /* * Check if TX fifo is empty. If empty fill FIFO with samples * NOTE: Only two channels supported */ if ((isr[i] & ISR_TXFE) && (i == 0) && dev->use_pio) { dw_pcm_push_tx(dev); irq_valid = true; } /* * Data available. Retrieve samples from FIFO * NOTE: Only two channels supported */ if ((isr[i] & ISR_RXDA) && (i == 0) && dev->use_pio) { dw_pcm_pop_rx(dev); irq_valid = true; } /* Error Handling: TX */ if (isr[i] & ISR_TXFO) { dev_err(dev->dev, "TX overrun (ch_id=%d)\n", i); irq_valid = true; } /* Error Handling: TX */ if (isr[i] & ISR_RXFO) { dev_err(dev->dev, "RX overrun (ch_id=%d)\n", i); irq_valid = true; } } if (irq_valid) return IRQ_HANDLED; else return IRQ_NONE; }
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; }