// SPDX-License-Identifier: GPL-2.0
//
// Freescale S/PDIF ALSA SoC Digital Audio Interface (DAI) driver
//
// Copyright (C) 2013 Freescale Semiconductor, Inc.
//
// Based on stmp3xxx_spdif_dai.c
// Vladimir Barinov <vbarinov@embeddedalley.com>
// Copyright 2008 SigmaTel, Inc
// Copyright 2008 Embedded Alley Solutions, Inc
#include <linux/bitrev.h>
#include <linux/clk.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_irq.h>
#include <linux/regmap.h>
#include <linux/pm_runtime.h>
#include <sound/asoundef.h>
#include <sound/dmaengine_pcm.h>
#include <sound/soc.h>
#include "fsl_spdif.h"
#include "imx-pcm.h"
#define FSL_SPDIF_TXFIFO_WML 0x8
#define FSL_SPDIF_RXFIFO_WML 0x8
#define INTR_FOR_PLAYBACK (INT_TXFIFO_RESYNC)
#define INTR_FOR_CAPTURE (INT_SYM_ERR | INT_BIT_ERR | INT_URX_FUL |\
INT_URX_OV | INT_QRX_FUL | INT_QRX_OV |\
INT_UQ_SYNC | INT_UQ_ERR | INT_RXFIFO_RESYNC |\
INT_LOSS_LOCK | INT_DPLL_LOCKED)
#define SIE_INTR_FOR(tx) (tx ? INTR_FOR_PLAYBACK : INTR_FOR_CAPTURE)
/* Index list for the values that has if (DPLL Locked) condition */
static u8 srpc_dpll_locked[] = { 0x0, 0x1, 0x2, 0x3, 0x4, 0xa, 0xb };
#define SRPC_NODPLL_START1 0x5
#define SRPC_NODPLL_START2 0xc
#define DEFAULT_RXCLK_SRC 1
/**
* struct fsl_spdif_soc_data: soc specific data
*
* @imx: for imx platform
* @shared_root_clock: flag of sharing a clock source with others;
* so the driver shouldn't set root clock rate
* @interrupts: interrupt number
* @tx_burst: tx maxburst size
* @rx_burst: rx maxburst size
* @tx_formats: tx supported data format
*/
struct fsl_spdif_soc_data {
bool imx;
bool shared_root_clock;
u32 interrupts;
u32 tx_burst;
u32 rx_burst;
u64 tx_formats;
};
/*
* SPDIF control structure
* Defines channel status, subcode and Q sub
*/
struct spdif_mixer_control {
/* spinlock to access control data */
spinlock_t ctl_lock;
/* IEC958 channel tx status bit */
unsigned char ch_status[4];
/* User bits */
unsigned char subcode[2 * SPDIF_UBITS_SIZE];
/* Q subcode part of user bits */
unsigned char qsub[2 * SPDIF_QSUB_SIZE];
/* Buffer offset for U/Q */
u32 upos;
u32 qpos;
/* Ready buffer index of the two buffers */
u32 ready_buf;
};
/**
* struct fsl_spdif_priv - Freescale SPDIF private data
* @soc: SPDIF soc data
* @fsl_spdif_control: SPDIF control data
* @cpu_dai_drv: cpu dai driver
* @pdev: platform device pointer
* @regmap: regmap handler
* @dpll_locked: dpll lock flag
* @txrate: the best rates for playback
* @txclk_df: STC_TXCLK_DF dividers value for playback
* @sysclk_df: STC_SYSCLK_DF dividers value for playback
* @txclk_src: STC_TXCLK_SRC values for playback
* @rxclk_src: SRPC_CLKSRC_SEL values for capture
* @txclk: tx clock sources for playback
* @rxclk: rx clock sources for capture
* @coreclk: core clock for register access via DMA
* @sysclk: system clock for rx clock rate measurement
* @spbaclk: SPBA clock (optional, depending on SoC design)
* @dma_params_tx: DMA parameters for transmit channel
* @dma_params_rx: DMA parameters for receive channel
* @regcache_srpc: regcache for SRPC
*/
struct fsl_spdif_priv {
const struct fsl_spdif_soc_data *soc;
struct spdif_mixer_control fsl_spdif_control;
struct snd_soc_dai_driver cpu_dai_drv;
struct platform_device *pdev;
struct regmap *regmap;
bool dpll_locked;
u32 txrate[SPDIF_TXRATE_MAX];
u8 txclk_df[SPDIF_TXRATE_MAX];
u16 sysclk_df[SPDIF_TXRATE_MAX];
u8 txclk_src[SPDIF_TXRATE_MAX];
u8 rxclk_src;
struct clk *txclk[SPDIF_TXRATE_MAX];
struct clk *rxclk;
struct clk *coreclk;
struct clk *sysclk;
struct clk *spbaclk;
struct snd_dmaengine_dai_dma_data dma_params_tx;
struct snd_dmaengine_dai_dma_data dma_params_rx;
/* regcache for SRPC */
u32 regcache_srpc;
};
static struct fsl_spdif_soc_data fsl_spdif_vf610 = {
.imx = false,
.shared_root_clock = false,
.interrupts = 1,
.tx_burst = FSL_SPDIF_TXFIFO_WML,
.rx_burst = FSL_SPDIF_RXFIFO_WML,
.tx_formats = FSL_SPDIF_FORMATS_PLAYBACK,
};
static struct fsl_spdif_soc_data fsl_spdif_imx35 = {
.imx = true,
.shared_root_clock = false,
.interrupts = 1,
.tx_burst = FSL_SPDIF_TXFIFO_WML,
.rx_burst = FSL_SPDIF_RXFIFO_WML,
.tx_formats = FSL_SPDIF_FORMATS_PLAYBACK,
};
static struct fsl_spdif_soc_data fsl_spdif_imx6sx = {
.imx = true,
.shared_root_clock = true,
.interrupts = 1,
.tx_burst = FSL_SPDIF_TXFIFO_WML,
.rx_burst = FSL_SPDIF_RXFIFO_WML,
.tx_formats = FSL_SPDIF_FORMATS_PLAYBACK,
};
static struct fsl_spdif_soc_data fsl_spdif_imx8qm = {
.imx = true,
.shared_root_clock = true,
.interrupts = 2,
.tx_burst = 2, /* Applied for EDMA */
.rx_burst = 2, /* Applied for EDMA */
.tx_formats = SNDRV_PCM_FMTBIT_S24_LE, /* Applied for EDMA */
};
/* Check if clk is a root clock that does not share clock source with others */
static inline bool fsl_spdif_can_set_clk_rate(struct fsl_spdif_priv *spdif, int clk)
{
return (clk == STC_TXCLK_SPDIF_ROOT) && !spdif->soc->shared_root_clock;
}
/* DPLL locked and lock loss interrupt handler */
static void spdif_irq_dpll_lock(struct fsl_spdif_priv *spdif_priv)
{
struct regmap *regmap = spdif_priv->regmap;
struct platform_device *pdev = spdif_priv->pdev;
u32 locked;
regmap_read(regmap, REG_SPDIF_SRPC, &locked);
locked &= SRPC_DPLL_LOCKED;
dev_dbg(&pdev->dev, "isr: Rx dpll %s \n",
locked ? "locked" : "loss lock");
spdif_priv->dpll_locked = locked ? true : false;
}
/* Receiver found illegal symbol interrupt handler */
static void spdif_irq_sym_error(struct fsl_spdif_priv *spdif_priv)
{
struct regmap *regmap = spdif_priv->regmap;
struct platform_device *pdev = spdif_priv->pdev;
dev_dbg(&pdev->dev, "isr: receiver found illegal symbol\n");
/* Clear illegal symbol if DPLL unlocked since no audio stream */
if (!spdif_priv->dpll_locked)
regmap_update_bits(regmap, REG_SPDIF_SIE, INT_SYM_ERR, 0);
}
/* U/Q Channel receive register full */
static void spdif_irq_uqrx_full(struct fsl_spdif_priv *spdif_priv, char name)
{
struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
struct regmap *regmap = spdif_priv->regmap;
struct platform_device *pdev = spdif_priv->pdev;
u32 *pos, size, val, reg;
switch (name) {
case 'U':
pos = &ctrl->upos;
size = SPDIF_UBITS_SIZE;
reg = REG_SPDIF_SRU;
break;
case 'Q':
pos = &ctrl->qpos;
size = SPDIF_QSUB_SIZE;
reg = REG_SPDIF_SRQ;
break;
default:
dev_err(&pdev->dev, "unsupported channel name\n");
return;
}
dev_dbg(&pdev->dev, "isr: %c Channel receive register full\n", name);
if (*pos >= size * 2) {
*pos = 0;
} else if (unlikely((*pos % size) + 3 > size)) {
dev_err(&pdev->dev, "User bit receive buffer overflow\n");
return;
}
regmap_read(regmap, reg, &val);
ctrl->subcode[*pos++] = val >> 16;
ctrl->subcode[*pos++] = val >> 8;
ctrl->subcode[*pos++] = val;
}
/* U/Q Channel sync found */
static void spdif_irq_uq_sync(struct fsl_spdif_priv *spdif_priv)
{
struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
struct platform_device *pdev = spdif_priv->pdev;
dev_dbg(&pdev->dev, "isr: U/Q Channel sync found\n");
/* U/Q buffer reset */
if (ctrl->qpos == 0)
return;
/* Set ready to this buffer */
ctrl->ready_buf = (ctrl->qpos - 1) / SPDIF_QSUB_SIZE + 1;
}
/* U/Q Channel framing error */
static void spdif_irq_uq_err(struct fsl_spdif_priv *spdif_priv)
{
struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
struct regmap *regmap = spdif_priv->regmap;
struct platform_device *pdev = spdif_priv->pdev;
u32 val;
dev_dbg(&pdev->d
|