/*
* ALSA SoC Synopsys I2S Audio Layer
*
* sound/soc/dwc/designware_i2s.c
*
* Copyright (C) 2010 ST Microelectronics
* Rajeev Kumar <rajeevkumar.linux@gmail.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/reset.h>
#include <linux/slab.h>
#include <linux/pm_runtime.h>
#include <sound/designware_i2s.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/dmaengine_pcm.h>
#include "local.h"
static inline void i2s_write_reg(void __iomem *io_base, int reg, u32 val)
{
writel(val, io_base + reg);
}
static inline u32 i2s_read_reg(void __iomem *io_base, int reg)
{
return readl(io_base + reg);
}
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_read_reg(dev->i2s_base, TOR(i));
} else {
for (i = 0; i < 4; i++)
i2s_read_reg(dev->i2s_base, ROR(i));
}
}
static inline void i2s_disable_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 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 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