/*
* 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
*
* 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/module.h>
#include <linux/clk.h>
#include <linux/clk-private.h>
#include <linux/bitrev.h>
#include <linux/regmap.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_irq.h>
#include <sound/asoundef.h>
#include <sound/soc.h>
#include <sound/dmaengine_pcm.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)
/* 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
/*
* 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 {
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;
u8 txclk_div[SPDIF_TXRATE_MAX];
u8 txclk_src[SPDIF_TXRATE_MAX];
u8 rxclk_src;
struct clk *txclk[SPDIF_TXRATE_MAX];
struct clk *rxclk;
struct snd_dmaengine_dai_dma_data dma_params_tx;
struct snd_dmaengine_dai_dma_data dma_params_rx;
/* The name space will be allocated dynamically */
char name[0];
};
/* 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");
if (!spdif_priv->dpll_locked) {
/* DPLL unlocked seems no audio stream */
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