// SPDX-License-Identifier: GPL-2.0
//
// Freescale ESAI ALSA SoC Digital Audio Interface (DAI) driver
//
// Copyright (C) 2014 Freescale Semiconductor, Inc.
#include <linux/clk.h>
#include <linux/dmaengine.h>
#include <linux/module.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/pm_runtime.h>
#include <sound/dmaengine_pcm.h>
#include <sound/pcm_params.h>
#include "fsl_esai.h"
#include "imx-pcm.h"
#define FSL_ESAI_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE)
/**
* struct fsl_esai_soc_data - soc specific data
* @reset_at_xrun: flags for enable reset operaton
*/
struct fsl_esai_soc_data {
bool reset_at_xrun;
};
/**
* struct fsl_esai - ESAI private data
* @dma_params_rx: DMA parameters for receive channel
* @dma_params_tx: DMA parameters for transmit channel
* @pdev: platform device pointer
* @regmap: regmap handler
* @coreclk: clock source to access register
* @extalclk: esai clock source to derive HCK, SCK and FS
* @fsysclk: system clock source to derive HCK, SCK and FS
* @spbaclk: SPBA clock (optional, depending on SoC design)
* @work: work to handle the reset operation
* @soc: soc specific data
* @lock: spin lock between hw_reset() and trigger()
* @fifo_depth: depth of tx/rx FIFO
* @slot_width: width of each DAI slot
* @slots: number of slots
* @tx_mask: slot mask for TX
* @rx_mask: slot mask for RX
* @channels: channel num for tx or rx
* @hck_rate: clock rate of desired HCKx clock
* @sck_rate: clock rate of desired SCKx clock
* @hck_dir: the direction of HCKx pads
* @sck_div: if using PSR/PM dividers for SCKx clock
* @consumer_mode: if fully using DAI clock consumer mode
* @synchronous: if using tx/rx synchronous mode
* @name: driver name
*/
struct fsl_esai {
struct snd_dmaengine_dai_dma_data dma_params_rx;
struct snd_dmaengine_dai_dma_data dma_params_tx;
struct platform_device *pdev;
struct regmap *regmap;
struct clk *coreclk;
struct clk *extalclk;
struct clk *fsysclk;
struct clk *spbaclk;
struct work_struct work;
const struct fsl_esai_soc_data *soc;
spinlock_t lock; /* Protect hw_reset and trigger */
u32 fifo_depth;
u32 slot_width;
u32 slots;
u32 tx_mask;
u32 rx_mask;
u32 channels[2];
u32 hck_rate[2];
u32 sck_rate[2];
bool hck_dir[2];
bool sck_div[2];
bool consumer_mode;
bool synchronous;
char name[32];
};
static struct fsl_esai_soc_data fsl_esai_vf610 = {
.reset_at_xrun = true,
};
static struct fsl_esai_soc_data fsl_esai_imx35 = {
.reset_at_xrun = true,
};
static struct fsl_esai_soc_data fsl_esai_imx6ull = {
.reset_at_xrun = false,
};
static irqreturn_t esai_isr(int irq, void *devid)
{
struct fsl_esai *esai_priv = (struct fsl_esai *)devid;
struct platform_device *pdev = esai_priv->pdev;
u32 esr;
u32 saisr;
regmap_read(esai_priv->regmap, REG_ESAI_ESR, &esr);
regmap_read(esai_priv->regmap, REG_ESAI_SAISR, &saisr);
if ((saisr & (ESAI_SAISR_TUE | ESAI_SAISR_ROE)) &&
esai_priv->soc->reset_at_xrun) {
dev_dbg(&pdev->dev, "reset module for xrun\n");
regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR,
ESAI_xCR_xEIE_MASK, 0);
regmap_update_bits(esai_priv->regmap, REG_ESAI_RCR,
ESAI_xCR_xEIE_MASK, 0);
schedule_work(&esai_priv->work);
}
if (esr & ESAI_ESR_TINIT_MASK)
dev_dbg(&pdev->dev, "isr: Transmission Initialized\n");
if (esr & ESAI_ESR_RFF_MASK)
dev_warn(&pdev->dev, "isr: Receiving overrun\n");
if (esr & ESAI_ESR_TFE_MASK)
dev_warn(&pdev->dev, "isr: Transmission underrun\n");
if (esr & ESAI_ESR_TLS_MASK)
dev_dbg(&pdev->dev, "isr: Just transmitted the last slot\n");
if (esr & ESAI_ESR_TDE_MASK)
dev_dbg(&pdev->dev, "isr: Transmission data exception\n");
if (esr & ESAI_ESR_TED_MASK)
dev_dbg(&pdev->dev, "isr: Transmitting even slots\n");
if (esr & ESAI_ESR_TD_MASK)
dev_dbg(&