/*
* 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/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/list.h>
#include <linux/pm_runtime.h>
#include <linux/io.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/initval.h>
#include <sound/soc.h>
#include <sound/pcm_params.h>
#include <sound/sh_fsi.h>
#include <asm/atomic.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 INT_ST 0x0200
#define IEMSK 0x0204
#define IMSK 0x0208
#define MUTE 0x020C
#define CLK_RST 0x0210
#define SOFT_RST 0x0214
#define MREG_START INT_ST
#define MREG_END SOFT_RST
/* DO_FMT */
/* DI_FMT */
#define CR_FMT(param) ((param) << 4)
# define CR_MONO 0x0
# define CR_MONO_D 0x1
# define CR_PCM 0x2
# define CR_I2S 0x3
# define CR_TDM 0x4
# define CR_TDM_D 0x5
/* DOFF_CTL */
/* DIFF_CTL */
#define IRQ_HALF 0x00100000
#define FIFO_CLR 0x00000001
/* DOFF_ST */
#define ERR_OVER 0x00000010
#define ERR_UNDER 0x00000001
/* 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)
#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;
int fifo_max;
int chan;
int byte_offset;
int period_len;
int buffer_len;
int periods;
};
struct fsi_master {
void __iomem *base;
int irq;
struct fsi_priv fsia;
struct fsi_priv fsib;
struct sh_fsi_platform_info *info;
};
static struct fsi_master *master;
/************************************************************************
basic read write function
************************************************************************/
static int __fsi_reg_write(u32 reg, u32 data)
{
/* valid data area is 24bit */
data &= 0x00ffffff;
return ctrl_outl(data, reg);
}
static u32 __fsi_reg_read(u32 reg)
{
return ctrl_inl(reg);
}
static int __fsi_reg_mask_set(u32 reg, u32 mask, u32 data)
{
u32 val = __fsi_reg_read(reg);
val &= ~mask;
val |= data & mask;
return __fsi_reg_write(reg, val);
}
static int fsi_reg_write(struct fsi_priv *fsi, u32 reg, u32 data)
{
if (reg > REG_END)
return -1;
return __fsi_reg_write((u32)(fsi->base + reg), data);
}
static u32 fsi_reg_read(struct fsi_priv *fsi, u32 reg)
{
if (reg > REG_END)
return 0;
return __fsi_reg_read((u32)(fsi->base + reg));
}
static int fsi_reg_mask_set(struct fsi_priv *fsi, u32 reg, u32 mask, u32 data)
{
if (reg > REG_END)
return -1;
return __fsi_reg_mask_set((u32)(fsi->base + reg), mask, data);
}
static int fsi_master_write(u32 reg, u32 data)
{
if ((reg < MREG_START) ||
(reg > MREG_END))
return -1;
return __fsi_reg_write((u32)(master->base + reg), data);
}
static u32 fsi_master_read(u32 reg)
{
if ((reg < MREG_START) ||
(reg > MREG_END))
return 0;
return __fsi_reg_read((u32)(master->base + reg));
}
static int fsi_master_mask_set(u32 reg, u32 mask, u32 data)
{
if ((reg < MREG_START) ||
(reg > MREG_END))
return -1;
return __fsi_reg_mask_set((u32)(master->base + reg), mask, data);
}
/************************************************************************
basic function
************************************************************************/
static struct fsi_priv *fsi_get(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd;
struct fsi_priv *fsi = NULL;
if (!substream || !master)
return NULL;
rtd = substream->private_data;
switch (rtd->dai->cpu_dai->id) {
case 0:
fsi = &master->fsia;
break;
case 1:
fsi = &master->fsib;
break;
}
return fsi;
}
static