/*
* pxa-ssp.c -- ALSA Soc Audio Layer
*
* Copyright 2005,2008 Wolfson Microelectronics PLC.
* Author: Liam Girdwood
* Mark Brown <broonie@opensource.wolfsonmicro.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* TODO:
* o Test network mode for > 16bit sample size
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <asm/irq.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/initval.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/pxa2xx-lib.h>
#include <mach/hardware.h>
#include <mach/dma.h>
#include <mach/regs-ssp.h>
#include <mach/audio.h>
#include <mach/ssp.h>
#include "pxa2xx-pcm.h"
#include "pxa-ssp.h"
/*
* SSP audio private data
*/
struct ssp_priv {
struct ssp_device *ssp;
unsigned int sysclk;
int dai_fmt;
#ifdef CONFIG_PM
uint32_t cr0;
uint32_t cr1;
uint32_t to;
uint32_t psp;
#endif
};
static void dump_registers(struct ssp_device *ssp)
{
dev_dbg(&ssp->pdev->dev, "SSCR0 0x%08x SSCR1 0x%08x SSTO 0x%08x\n",
ssp_read_reg(ssp, SSCR0), ssp_read_reg(ssp, SSCR1),
ssp_read_reg(ssp, SSTO));
dev_dbg(&ssp->pdev->dev, "SSPSP 0x%08x SSSR 0x%08x SSACD 0x%08x\n",
ssp_read_reg(ssp, SSPSP), ssp_read_reg(ssp, SSSR),
ssp_read_reg(ssp, SSACD));
}
static void ssp_enable(struct ssp_device *ssp)
{
uint32_t sscr0;
sscr0 = __raw_readl(ssp->mmio_base + SSCR0) | SSCR0_SSE;
__raw_writel(sscr0, ssp->mmio_base + SSCR0);
}
static void ssp_disable(struct ssp_device *ssp)
{
uint32_t sscr0;
sscr0 = __raw_readl(ssp->mmio_base + SSCR0) & ~SSCR0_SSE;
__raw_writel(sscr0, ssp->mmio_base + SSCR0);
}
struct pxa2xx_pcm_dma_data {
struct pxa2xx_pcm_dma_params params;
char name[20];
};
static struct pxa2xx_pcm_dma_params *
ssp_get_dma_params(struct ssp_device *ssp, int width4, int out)
{
struct pxa2xx_pcm_dma_data *dma;
dma = kzalloc(sizeof(struct pxa2xx_pcm_dma_data), GFP_KERNEL);
if (dma == NULL)
return NULL;
snprintf(dma->name, 20, "SSP%d PCM %s %s", ssp->port_id,
width4 ? "32-bit" : "16-bit", out ? "out" : "in");
dma->params.name = dma->name;
dma->params.drcmr = &DRCMR(out ? ssp->drcmr_tx : ssp->drcmr_rx);
dma->params.dcmd = (out ? (DCMD_INCSRCADDR | DCMD_FLOWTRG) :
(DCMD_INCTRGADDR | DCMD_FLOWSRC)) |
(width4 ? DCMD_WIDTH4 : DCMD_WIDTH2) | DCMD_BURST16;
dma->params.dev_addr = ssp->phys_base + SSDR;
return &dma->params;
}
static int pxa_ssp_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
struct ssp_priv *priv = cpu_dai->private_data;
struct ssp_device *ssp = priv->ssp;
int ret = 0;
if (!cpu_dai->active) {
clk_enable(ssp->clk);
ssp_disable(ssp);
}
if (cpu_dai->dma_data) {
kfree(cpu_dai->dma_data);
cpu_dai->dma_data = NULL;
}
return ret;
}
static void pxa_ssp_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
struct ssp_priv *priv = cpu_dai->private_data;
struct ssp_device *ssp = priv->ssp;
if (!cpu_dai->active) {
ssp_disable(ssp);
clk_disable(ssp->clk);
}
if (cpu_dai->dma_data) {
kfree(cpu_dai->dma_data);
cpu_dai->dma_data = NULL;
}
}
#ifdef CONFIG_PM
static int pxa_ssp_suspend(struct snd_soc_dai *cpu_dai)
{
struct ssp_priv *priv = cpu_dai->private_data;
struct ssp_device *ssp = priv->ssp;
if (!cpu_dai->active)
clk_enable(ssp->clk);
priv->cr0 = __raw_readl(ssp->mmio_base + SSCR0);
priv->cr1 = __raw_readl(ssp->mmio_base + SSCR1);
priv->to = __raw_readl(ssp->mmio_base + SSTO);
priv->psp = __raw_readl(ssp->mmio_base + SSPSP);
ssp_di