/*
* Fifo-attached Serial Interface (FSI) support for SH7724
*
* Copyright (C) 2009 Renesas Solutions Corp.
* Kuninori Morimoto <morimoto.kuninori@renesas.com>
*
* Based on ssi.c
* Copyright (c) 2007 Manuel Lauss <mano@roarinelk.homelinux.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/delay.h>
#include <linux/pm_runtime.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <sound/soc.h>
#include <sound/sh_fsi.h>
#define DO_FMT 0x0000
#define DOFF_CTL 0x0004
#define DOFF_ST 0x0008
#define DI_FMT 0x000C
#define DIFF_CTL 0x0010
#define DIFF_ST 0x0014
#define CKG1 0x0018
#define CKG2 0x001C
#define DIDT 0x0020
#define DODT 0x0024
#define MUTE_ST 0x0028
#define REG_END MUTE_ST
#define CPU_INT_ST 0x01F4
#define CPU_IEMSK 0x01F8
#define CPU_IMSK 0x01FC
#define INT_ST 0x0200
#define IEMSK 0x0204
#define IMSK 0x0208
#define MUTE 0x020C
#define CLK_RST 0x0210
#define SOFT_RST 0x0214
#define FIFO_SZ 0x0218
#define MREG_START CPU_INT_ST
#define MREG_END FIFO_SZ
/* DO_FMT */
/* DI_FMT */
#define CR_MONO (0x0 << 4)
#define CR_MONO_D (0x1 << 4)
#define CR_PCM (0x2 << 4)
#define CR_I2S (0x3 << 4)
#define CR_TDM (0x4 << 4)
#define CR_TDM_D (0x5 << 4)
/* DOFF_CTL */
/* DIFF_CTL */
#define IRQ_HALF 0x00100000
#define FIFO_CLR 0x00000001
/* DOFF_ST */
#define ERR_OVER 0x00000010
#define ERR_UNDER 0x00000001
#define ST_ERR (ERR_OVER | ERR_UNDER)
/* CKG1 */
#define ACKMD_MASK 0x00007000
#define BPFMD_MASK 0x00000700
/* CLK_RST */
#define B_CLK 0x00000010
#define A_CLK 0x00000001
/* INT_ST */
#define INT_B_IN (1 << 12)
#define INT_B_OUT (1 << 8)
#define INT_A_IN (1 << 4)
#define INT_A_OUT (1 << 0)
/* SOFT_RST */
#define PBSR (1 << 12) /* Port B Software Reset */
#define PASR (1 << 8) /* Port A Software Reset */
#define IR (1 << 4) /* Interrupt Reset */
#define FSISR (1 << 0) /* Software Reset */
/* FIFO_SZ */
#define OUT_SZ_MASK 0x7
#define BO_SZ_SHIFT 8
#define AO_SZ_SHIFT 0
#define FSI_RATES SNDRV_PCM_RATE_8000_96000
#define FSI_FMTS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE)
/************************************************************************
struct
************************************************************************/
struct fsi_priv {
void __iomem *base;
struct snd_pcm_substream *substream;
struct fsi_master *master;
int fifo_max;
int chan;
int byte_offset;
int period_len;
int buffer_len;
int periods;
};
struct fsi_core {
int ver;
u32 int_st;
u32 iemsk;
u32 imsk;
};
struct fsi_master {
void __iomem *base;
int irq;
struct fsi_priv fsia;
struct fsi_priv fsib;
struct fsi_core *core;
struct sh_fsi_platform_info *info;
spinlock_t lock;
};
/************************************************************************
basic read write function
************************************************************************/
static void __fsi_reg_write(u32 reg, u32 data)
{
/* valid data area is 24bit */
data &= 0x00ffffff;
__raw_writel(data, reg);
}
static u32 __fsi_reg_read(u32 reg)
{
return __raw_readl(reg);
}
static void __fsi_reg_mask_set(u32 reg, u32 mask, u32 data)
{
u32 val = __fsi_reg_read(reg);
val &= ~mask;
val |= data & mask;
__fsi_reg_write(reg, val);
}
static void fsi_reg_write(struct fsi_priv *fsi, u32 reg, u32 data)
{
if (reg > REG_END) {
pr_err("fsi: register access err (%s)\n", __func__);
return;
}
__fsi_reg_write((u32)(fsi->base + reg), data);
}
static u32 fsi_reg_read(struct fsi_priv *fsi, u32 reg)
{
if (reg > REG_END) {
pr_err("fsi: register access err (%s)\n", __func__);
return 0;
}
return __fsi_reg_read((u32)(fsi->base<