On Fri, Apr 9, 2010 at 7:16 AM, Mark Brown <broonie@xxxxxxxxxxxxxxxxxxxxxxxxxxx> wrote: > On Wed, Mar 31, 2010 at 08:46:42AM -0400, Haojian Zhuang wrote: >> From b52a0d2687e82f81d38198cb450b6a39a9e53851 Mon Sep 17 00:00:00 2001 >> From: Haojian Zhuang <haojian.zhuang@xxxxxxxxxxx> >> Date: Wed, 31 Mar 2010 15:08:55 -0400 >> Subject: [PATCH] [ARM] pxa: add namespace on ssp >> >> In order to prevent code ambiguous, add namespace on functions in ssp driver. > > Liam acked these on IRC so I was going to apply them (the interface for > the sysclk configuration should be fixed, but that can be done as a > followup patch) but I can't seem to find a branch that these apply to - > they don't apply cleanly to either Eric's ssp_cleanup branch, the ASoC > for-2.6.35 branch, nor a merge of the two. > Hi Mark, Excuse me for late response. Now patches are refreshed and they're still based on Eric's ssp-cleanup branch. Since these patches are relied on some ssp's fix. Do you want to immigrate these patches into ASoC's tree? Would you pull eric's ssp-cleanup branch into ASoC tree? Thanks Haojian
From b52a0d2687e82f81d38198cb450b6a39a9e53851 Mon Sep 17 00:00:00 2001 From: Haojian Zhuang <haojian.zhuang@xxxxxxxxxxx> Date: Wed, 31 Mar 2010 15:08:55 -0400 Subject: [PATCH] [ARM] pxa: add namespace on ssp In order to prevent code ambiguous, add namespace on functions in ssp driver. Signed-off-by: Haojian Zhuang <haojian.zhuang@xxxxxxxxxxx> --- arch/arm/plat-pxa/include/plat/ssp.h | 12 ++-- arch/arm/plat-pxa/ssp.c | 22 +++--- drivers/spi/pxa2xx_spi.c | 8 +- sound/soc/pxa/pxa-ssp.c | 132 +++++++++++++++++----------------- 4 files changed, 87 insertions(+), 87 deletions(-) diff --git a/arch/arm/plat-pxa/include/plat/ssp.h b/arch/arm/plat-pxa/include/plat/ssp.h index d16d79a..fe43150 100644 --- a/arch/arm/plat-pxa/include/plat/ssp.h +++ b/arch/arm/plat-pxa/include/plat/ssp.h @@ -159,28 +159,28 @@ struct ssp_device { }; /** - * ssp_write_reg - Write to a SSP register + * pxa_ssp_write_reg - Write to a SSP register * * @dev: SSP device to access * @reg: Register to write to * @val: Value to be written. */ -static inline void ssp_write_reg(struct ssp_device *dev, u32 reg, u32 val) +static inline void pxa_ssp_write_reg(struct ssp_device *dev, u32 reg, u32 val) { __raw_writel(val, dev->mmio_base + reg); } /** - * ssp_read_reg - Read from a SSP register + * pxa_ssp_read_reg - Read from a SSP register * * @dev: SSP device to access * @reg: Register to read from */ -static inline u32 ssp_read_reg(struct ssp_device *dev, u32 reg) +static inline u32 pxa_ssp_read_reg(struct ssp_device *dev, u32 reg) { return __raw_readl(dev->mmio_base + reg); } -struct ssp_device *ssp_request(int port, const char *label); -void ssp_free(struct ssp_device *); +struct ssp_device *pxa_ssp_request(int port, const char *label); +void pxa_ssp_free(struct ssp_device *); #endif /* __ASM_ARCH_SSP_H */ diff --git a/arch/arm/plat-pxa/ssp.c b/arch/arm/plat-pxa/ssp.c index cfebcd8..511805e 100644 --- a/arch/arm/plat-pxa/ssp.c +++ b/arch/arm/plat-pxa/ssp.c @@ -37,7 +37,7 @@ static DEFINE_MUTEX(ssp_lock); static LIST_HEAD(ssp_list); -struct ssp_device *ssp_request(int port, const char *label) +struct ssp_device *pxa_ssp_request(int port, const char *label) { struct ssp_device *ssp = NULL; @@ -58,9 +58,9 @@ struct ssp_device *ssp_request(int port, const char *label) return ssp; } -EXPORT_SYMBOL(ssp_request); +EXPORT_SYMBOL(pxa_ssp_request); -void ssp_free(struct ssp_device *ssp) +void pxa_ssp_free(struct ssp_device *ssp) { mutex_lock(&ssp_lock); if (ssp->use_count) { @@ -70,9 +70,9 @@ void ssp_free(struct ssp_device *ssp) dev_err(&ssp->pdev->dev, "device already free\n"); mutex_unlock(&ssp_lock); } -EXPORT_SYMBOL(ssp_free); +EXPORT_SYMBOL(pxa_ssp_free); -static int __devinit ssp_probe(struct platform_device *pdev) +static int __devinit pxa_ssp_probe(struct platform_device *pdev) { const struct platform_device_id *id = platform_get_device_id(pdev); struct resource *res; @@ -164,7 +164,7 @@ err_free: return ret; } -static int __devexit ssp_remove(struct platform_device *pdev) +static int __devexit pxa_ssp_remove(struct platform_device *pdev) { struct resource *res; struct ssp_device *ssp; @@ -196,9 +196,9 @@ static const struct platform_device_id ssp_id_table[] = { { }, }; -static struct platform_driver ssp_driver = { - .probe = ssp_probe, - .remove = __devexit_p(ssp_remove), +static struct platform_driver pxa_ssp_driver = { + .probe = pxa_ssp_probe, + .remove = __devexit_p(pxa_ssp_remove), .driver = { .owner = THIS_MODULE, .name = "pxa2xx-ssp", @@ -208,12 +208,12 @@ static struct platform_driver ssp_driver = { static int __init pxa_ssp_init(void) { - return platform_driver_register(&ssp_driver); + return platform_driver_register(&pxa_ssp_driver); } static void __exit pxa_ssp_exit(void) { - platform_driver_unregister(&ssp_driver); + platform_driver_unregister(&pxa_ssp_driver); } arch_initcall(pxa_ssp_init); diff --git a/drivers/spi/pxa2xx_spi.c b/drivers/spi/pxa2xx_spi.c index 4a64da7..2fd7d5a 100644 --- a/drivers/spi/pxa2xx_spi.c +++ b/drivers/spi/pxa2xx_spi.c @@ -1464,7 +1464,7 @@ static int __init pxa2xx_spi_probe(struct platform_device *pdev) platform_info = dev->platform_data; - ssp = ssp_request(pdev->id, pdev->name); + ssp = pxa_ssp_request(pdev->id, pdev->name); if (ssp == NULL) { dev_err(&pdev->dev, "failed to request SSP%d\n", pdev->id); return -ENODEV; @@ -1474,7 +1474,7 @@ static int __init pxa2xx_spi_probe(struct platform_device *pdev) master = spi_alloc_master(dev, sizeof(struct driver_data) + 16); if (!master) { dev_err(&pdev->dev, "cannot alloc spi_master\n"); - ssp_free(ssp); + pxa_ssp_free(ssp); return -ENOMEM; } drv_data = spi_master_get_devdata(master); @@ -1603,7 +1603,7 @@ out_error_irq_alloc: out_error_master_alloc: spi_master_put(master); - ssp_free(ssp); + pxa_ssp_free(ssp); return status; } @@ -1647,7 +1647,7 @@ static int pxa2xx_spi_remove(struct platform_device *pdev) free_irq(ssp->irq, drv_data); /* Release SSP */ - ssp_free(ssp); + pxa_ssp_free(ssp); /* Disconnect from the SPI framework */ spi_unregister_master(drv_data->master); diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c index 4ca9245..ed54bef 100644 --- a/sound/soc/pxa/pxa-ssp.c +++ b/sound/soc/pxa/pxa-ssp.c @@ -55,15 +55,15 @@ struct ssp_priv { 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)); + pxa_ssp_read_reg(ssp, SSCR0), pxa_ssp_read_reg(ssp, SSCR1), + pxa_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)); + pxa_ssp_read_reg(ssp, SSPSP), pxa_ssp_read_reg(ssp, SSSR), + pxa_ssp_read_reg(ssp, SSACD)); } -static void ssp_enable(struct ssp_device *ssp) +static void pxa_ssp_enable(struct ssp_device *ssp) { uint32_t sscr0; @@ -71,7 +71,7 @@ static void ssp_enable(struct ssp_device *ssp) __raw_writel(sscr0, ssp->mmio_base + SSCR0); } -static void ssp_disable(struct ssp_device *ssp) +static void pxa_ssp_disable(struct ssp_device *ssp) { uint32_t sscr0; @@ -85,7 +85,7 @@ struct pxa2xx_pcm_dma_data { }; static struct pxa2xx_pcm_dma_params * -ssp_get_dma_params(struct ssp_device *ssp, int width4, int out) +pxa_ssp_get_dma_params(struct ssp_device *ssp, int width4, int out) { struct pxa2xx_pcm_dma_data *dma; @@ -117,7 +117,7 @@ static int pxa_ssp_startup(struct snd_pcm_substream *substream, if (!cpu_dai->active) { clk_enable(ssp->clk); - ssp_disable(ssp); + pxa_ssp_disable(ssp); } if (cpu_dai->dma_data) { @@ -136,7 +136,7 @@ static void pxa_ssp_shutdown(struct snd_pcm_substream *substream, struct ssp_device *ssp = priv->ssp; if (!cpu_dai->active) { - ssp_disable(ssp); + pxa_ssp_disable(ssp); clk_disable(ssp->clk); } @@ -161,7 +161,7 @@ static int pxa_ssp_suspend(struct snd_soc_dai *cpu_dai) priv->to = __raw_readl(ssp->mmio_base + SSTO); priv->psp = __raw_readl(ssp->mmio_base + SSPSP); - ssp_disable(ssp); + pxa_ssp_disable(ssp); clk_disable(ssp->clk); return 0; } @@ -181,7 +181,7 @@ static int pxa_ssp_resume(struct snd_soc_dai *cpu_dai) __raw_writel(priv->psp, ssp->mmio_base + SSPSP); if (cpu_dai->active) - ssp_enable(ssp); + pxa_ssp_enable(ssp); else clk_disable(ssp->clk); @@ -197,9 +197,9 @@ static int pxa_ssp_resume(struct snd_soc_dai *cpu_dai) * ssp_set_clkdiv - set SSP clock divider * @div: serial clock rate divider */ -static void ssp_set_scr(struct ssp_device *ssp, u32 div) +static void pxa_ssp_set_scr(struct ssp_device *ssp, u32 div) { - u32 sscr0 = ssp_read_reg(ssp, SSCR0); + u32 sscr0 = pxa_ssp_read_reg(ssp, SSCR0); if (cpu_is_pxa25x() && ssp->type == PXA25x_SSP) { sscr0 &= ~0x0000ff00; @@ -208,15 +208,15 @@ static void ssp_set_scr(struct ssp_device *ssp, u32 div) sscr0 &= ~0x000fff00; sscr0 |= (div - 1) << 8; /* 1..4096 */ } - ssp_write_reg(ssp, SSCR0, sscr0); + pxa_ssp_write_reg(ssp, SSCR0, sscr0); } /** - * ssp_get_clkdiv - get SSP clock divider + * pxa_ssp_get_clkdiv - get SSP clock divider */ -static u32 ssp_get_scr(struct ssp_device *ssp) +static u32 pxa_ssp_get_scr(struct ssp_device *ssp) { - u32 sscr0 = ssp_read_reg(ssp, SSCR0); + u32 sscr0 = pxa_ssp_read_reg(ssp, SSCR0); u32 div; if (cpu_is_pxa25x() && ssp->type == PXA25x_SSP) @@ -236,7 +236,7 @@ static int pxa_ssp_set_dai_sysclk(struct snd_soc_dai *cpu_dai, struct ssp_device *ssp = priv->ssp; int val; - u32 sscr0 = ssp_read_reg(ssp, SSCR0) & + u32 sscr0 = pxa_ssp_read_reg(ssp, SSCR0) & ~(SSCR0_ECS | SSCR0_NCS | SSCR0_MOD | SSCR0_ACS); dev_dbg(&ssp->pdev->dev, @@ -264,7 +264,7 @@ static int pxa_ssp_set_dai_sysclk(struct snd_soc_dai *cpu_dai, break; case PXA_SSP_CLK_AUDIO: priv->sysclk = 0; - ssp_set_scr(ssp, 1); + pxa_ssp_set_scr(ssp, 1); sscr0 |= SSCR0_ACS; break; default: @@ -275,8 +275,8 @@ static int pxa_ssp_set_dai_sysclk(struct snd_soc_dai *cpu_dai, * on PXA2xx. On PXA3xx it must be enabled when doing so. */ if (!cpu_is_pxa3xx()) clk_disable(ssp->clk); - val = ssp_read_reg(ssp, SSCR0) | sscr0; - ssp_write_reg(ssp, SSCR0, val); + val = pxa_ssp_read_reg(ssp, SSCR0) | sscr0; + pxa_ssp_write_reg(ssp, SSCR0, val); if (!cpu_is_pxa3xx()) clk_enable(ssp->clk); @@ -295,11 +295,11 @@ static int pxa_ssp_set_dai_clkdiv(struct snd_soc_dai *cpu_dai, switch (div_id) { case PXA_SSP_AUDIO_DIV_ACDS: - val = (ssp_read_reg(ssp, SSACD) & ~0x7) | SSACD_ACDS(div); - ssp_write_reg(ssp, SSACD, val); + val = (pxa_ssp_read_reg(ssp, SSACD) & ~0x7) | SSACD_ACDS(div); + pxa_ssp_write_reg(ssp, SSACD, val); break; case PXA_SSP_AUDIO_DIV_SCDB: - val = ssp_read_reg(ssp, SSACD); + val = pxa_ssp_read_reg(ssp, SSACD); val &= ~SSACD_SCDB; #if defined(CONFIG_PXA3xx) if (cpu_is_pxa3xx()) @@ -322,10 +322,10 @@ static int pxa_ssp_set_dai_clkdiv(struct snd_soc_dai *cpu_dai, default: return -EINVAL; } - ssp_write_reg(ssp, SSACD, val); + pxa_ssp_write_reg(ssp, SSACD, val); break; case PXA_SSP_DIV_SCR: - ssp_set_scr(ssp, div); + pxa_ssp_set_scr(ssp, div); break; default: return -ENODEV; @@ -342,11 +342,11 @@ static int pxa_ssp_set_dai_pll(struct snd_soc_dai *cpu_dai, int pll_id, { struct ssp_priv *priv = cpu_dai->private_data; struct ssp_device *ssp = priv->ssp; - u32 ssacd = ssp_read_reg(ssp, SSACD) & ~0x70; + u32 ssacd = pxa_ssp_read_reg(ssp, SSACD) & ~0x70; #if defined(CONFIG_PXA3xx) if (cpu_is_pxa3xx()) - ssp_write_reg(ssp, SSACDD, 0); + pxa_ssp_write_reg(ssp, SSACDD, 0); #endif switch (freq_out) { @@ -384,7 +384,7 @@ static int pxa_ssp_set_dai_pll(struct snd_soc_dai *cpu_dai, int pll_id, val = tmp; val = (val << 16) | 64; - ssp_write_reg(ssp, SSACDD, val); + pxa_ssp_write_reg(ssp, SSACDD, val); ssacd |= (0x6 << 4); @@ -398,7 +398,7 @@ static int pxa_ssp_set_dai_pll(struct snd_soc_dai *cpu_dai, int pll_id, return -EINVAL; } - ssp_write_reg(ssp, SSACD, ssacd); + pxa_ssp_write_reg(ssp, SSACD, ssacd); return 0; } @@ -413,7 +413,7 @@ static int pxa_ssp_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, struct ssp_device *ssp = priv->ssp; u32 sscr0; - sscr0 = ssp_read_reg(ssp, SSCR0); + sscr0 = pxa_ssp_read_reg(ssp, SSCR0); sscr0 &= ~(SSCR0_MOD | SSCR0_SlotsPerFrm(8) | SSCR0_EDSS | SSCR0_DSS); /* set slot width */ @@ -430,10 +430,10 @@ static int pxa_ssp_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, sscr0 |= SSCR0_SlotsPerFrm(slots); /* set active slot mask */ - ssp_write_reg(ssp, SSTSA, tx_mask); - ssp_write_reg(ssp, SSRSA, rx_mask); + pxa_ssp_write_reg(ssp, SSTSA, tx_mask); + pxa_ssp_write_reg(ssp, SSRSA, rx_mask); } - ssp_write_reg(ssp, SSCR0, sscr0); + pxa_ssp_write_reg(ssp, SSCR0, sscr0); return 0; } @@ -448,12 +448,12 @@ static int pxa_ssp_set_dai_tristate(struct snd_soc_dai *cpu_dai, struct ssp_device *ssp = priv->ssp; u32 sscr1; - sscr1 = ssp_read_reg(ssp, SSCR1); + sscr1 = pxa_ssp_read_reg(ssp, SSCR1); if (tristate) sscr1 &= ~SSCR1_TTE; else sscr1 |= SSCR1_TTE; - ssp_write_reg(ssp, SSCR1, sscr1); + pxa_ssp_write_reg(ssp, SSCR1, sscr1); return 0; } @@ -477,14 +477,14 @@ static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai, return 0; /* we can only change the settings if the port is not in use */ - if (ssp_read_reg(ssp, SSCR0) & SSCR0_SSE) { + if (pxa_ssp_read_reg(ssp, SSCR0) & SSCR0_SSE) { dev_err(&ssp->pdev->dev, "can't change hardware dai format: stream is in use"); return -EINVAL; } /* reset port settings */ - sscr0 = ssp_read_reg(ssp, SSCR0) & + sscr0 = pxa_ssp_read_reg(ssp, SSCR0) & (SSCR0_ECS | SSCR0_NCS | SSCR0_MOD | SSCR0_ACS); sscr1 = SSCR1_RxTresh(8) | SSCR1_TxTresh(7); sspsp = 0; @@ -536,9 +536,9 @@ static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai, return -EINVAL; } - ssp_write_reg(ssp, SSCR0, sscr0); - ssp_write_reg(ssp, SSCR1, sscr1); - ssp_write_reg(ssp, SSPSP, sspsp); + pxa_ssp_write_reg(ssp, SSCR0, sscr0); + pxa_ssp_write_reg(ssp, SSCR1, sscr1); + pxa_ssp_write_reg(ssp, SSPSP, sspsp); dump_registers(ssp); @@ -567,7 +567,7 @@ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream, u32 sscr0; u32 sspsp; int width = snd_pcm_format_physical_width(params_format(params)); - int ttsa = ssp_read_reg(ssp, SSTSA) & 0xf; + int ttsa = pxa_ssp_read_reg(ssp, SSTSA) & 0xf; /* generate correct DMA params */ if (cpu_dai->dma_data) @@ -577,20 +577,20 @@ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream, * to force 16-bit frame width on the wire (for S16_LE), even * with two channels. Use 16-bit DMA transfers for this case. */ - cpu_dai->dma_data = ssp_get_dma_params(ssp, + cpu_dai->dma_data = pxa_ssp_get_dma_params(ssp, ((chn == 2) && (ttsa != 1)) || (width == 32), substream->stream == SNDRV_PCM_STREAM_PLAYBACK); /* we can only change the settings if the port is not in use */ - if (ssp_read_reg(ssp, SSCR0) & SSCR0_SSE) + if (pxa_ssp_read_reg(ssp, SSCR0) & SSCR0_SSE) return 0; /* clear selected SSP bits */ - sscr0 = ssp_read_reg(ssp, SSCR0) & ~(SSCR0_DSS | SSCR0_EDSS); - ssp_write_reg(ssp, SSCR0, sscr0); + sscr0 = pxa_ssp_read_reg(ssp, SSCR0) & ~(SSCR0_DSS | SSCR0_EDSS); + pxa_ssp_write_reg(ssp, SSCR0, sscr0); /* bit size */ - sscr0 = ssp_read_reg(ssp, SSCR0); + sscr0 = pxa_ssp_read_reg(ssp, SSCR0); switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: #ifdef CONFIG_PXA3xx @@ -606,13 +606,13 @@ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream, sscr0 |= (SSCR0_EDSS | SSCR0_DataSize(16)); break; } - ssp_write_reg(ssp, SSCR0, sscr0); + pxa_ssp_write_reg(ssp, SSCR0, sscr0); switch (priv->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: - sspsp = ssp_read_reg(ssp, SSPSP); + sspsp = pxa_ssp_read_reg(ssp, SSPSP); - if ((ssp_get_scr(ssp) == 4) && (width == 16)) { + if ((pxa_ssp_get_scr(ssp) == 4) && (width == 16)) { /* This is a special case where the bitclk is 64fs * and we're not dealing with 2*32 bits of audio * samples. @@ -646,7 +646,7 @@ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream, sspsp |= SSPSP_DMYSTRT(1); } - ssp_write_reg(ssp, SSPSP, sspsp); + pxa_ssp_write_reg(ssp, SSPSP, sspsp); break; default: break; @@ -677,45 +677,45 @@ static int pxa_ssp_trigger(struct snd_pcm_substream *substream, int cmd, switch (cmd) { case SNDRV_PCM_TRIGGER_RESUME: - ssp_enable(ssp); + pxa_ssp_enable(ssp); break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - val = ssp_read_reg(ssp, SSCR1); + val = pxa_ssp_read_reg(ssp, SSCR1); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) val |= SSCR1_TSRE; else val |= SSCR1_RSRE; - ssp_write_reg(ssp, SSCR1, val); - val = ssp_read_reg(ssp, SSSR); - ssp_write_reg(ssp, SSSR, val); + pxa_ssp_write_reg(ssp, SSCR1, val); + val = pxa_ssp_read_reg(ssp, SSSR); + pxa_ssp_write_reg(ssp, SSSR, val); break; case SNDRV_PCM_TRIGGER_START: - val = ssp_read_reg(ssp, SSCR1); + val = pxa_ssp_read_reg(ssp, SSCR1); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) val |= SSCR1_TSRE; else val |= SSCR1_RSRE; - ssp_write_reg(ssp, SSCR1, val); - ssp_enable(ssp); + pxa_ssp_write_reg(ssp, SSCR1, val); + pxa_ssp_enable(ssp); break; case SNDRV_PCM_TRIGGER_STOP: - val = ssp_read_reg(ssp, SSCR1); + val = pxa_ssp_read_reg(ssp, SSCR1); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) val &= ~SSCR1_TSRE; else val &= ~SSCR1_RSRE; - ssp_write_reg(ssp, SSCR1, val); + pxa_ssp_write_reg(ssp, SSCR1, val); break; case SNDRV_PCM_TRIGGER_SUSPEND: - ssp_disable(ssp); + pxa_ssp_disable(ssp); break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - val = ssp_read_reg(ssp, SSCR1); + val = pxa_ssp_read_reg(ssp, SSCR1); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) val &= ~SSCR1_TSRE; else val &= ~SSCR1_RSRE; - ssp_write_reg(ssp, SSCR1, val); + pxa_ssp_write_reg(ssp, SSCR1, val); break; default: @@ -737,7 +737,7 @@ static int pxa_ssp_probe(struct platform_device *pdev, if (!priv) return -ENOMEM; - priv->ssp = ssp_request(dai->id + 1, "SoC audio"); + priv->ssp = pxa_ssp_request(dai->id + 1, "SoC audio"); if (priv->ssp == NULL) { ret = -ENODEV; goto err_priv; @@ -757,7 +757,7 @@ static void pxa_ssp_remove(struct platform_device *pdev, struct snd_soc_dai *dai) { struct ssp_priv *priv = dai->private_data; - ssp_free(priv->ssp); + pxa_ssp_free(priv->ssp); } #define PXA_SSP_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ -- 1.5.6.5
From 74dd48ef72989fec0ad706c192784d487f219cc5 Mon Sep 17 00:00:00 2001 From: Haojian Zhuang <haojian.zhuang@xxxxxxxxxxx> Date: Wed, 31 Mar 2010 15:28:37 -0400 Subject: [PATCH] ASoC: split pxa ssp for reusing code Since basic SSP features are shared between PXA2xx and PXA168, the difference is focused on clock generating. Now split ssp code into two parts. One is for general ssp feature. The other is for pxa2xx parts. Update the SSP timing configuration in hw_params(). The SSP timing is verified on I2S and Left J interface of PXA168 with 16-bit sample stream. Signed-off-by: Haojian Zhuang <haojian.zhuang@xxxxxxxxxxx> --- sound/soc/pxa/Kconfig | 8 +- sound/soc/pxa/Makefile | 4 +- sound/soc/pxa/magician.c | 38 ++-- sound/soc/pxa/pxa-ssp.c | 728 +++++++++++--------------------------------- sound/soc/pxa/pxa-ssp.h | 52 ++-- sound/soc/pxa/pxa2xx-ssp.c | 318 +++++++++++++++++++ sound/soc/pxa/pxa2xx-ssp.h | 48 +++ sound/soc/pxa/raumfeld.c | 14 +- sound/soc/pxa/zylonite.c | 6 +- 9 files changed, 603 insertions(+), 613 deletions(-) create mode 100644 sound/soc/pxa/pxa2xx-ssp.c create mode 100644 sound/soc/pxa/pxa2xx-ssp.h diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig index 78e6121..7be1d5f 100644 --- a/sound/soc/pxa/Kconfig +++ b/sound/soc/pxa/Kconfig @@ -21,7 +21,7 @@ config SND_PXA2XX_SOC_AC97 config SND_PXA2XX_SOC_I2S tristate -config SND_PXA_SOC_SSP +config SND_PXA2XX_SOC_SSP tristate select PXA_SSP @@ -113,7 +113,7 @@ config SND_SOC_ZYLONITE tristate "SoC Audio support for Marvell Zylonite" depends on SND_PXA2XX_SOC && MACH_ZYLONITE select SND_PXA2XX_SOC_AC97 - select SND_PXA_SOC_SSP + select SND_PXA2XX_SOC_SSP select SND_SOC_WM9713 help Say Y if you want to add support for SoC audio on the @@ -122,7 +122,7 @@ config SND_SOC_ZYLONITE config SND_SOC_RAUMFELD tristate "SoC Audio support Raumfeld audio adapter" depends on SND_PXA2XX_SOC && (MACH_RAUMFELD_SPEAKER || MACH_RAUMFELD_CONNECTOR) - select SND_PXA_SOC_SSP + select SND_PXA2XX_SOC_SSP select SND_SOC_CS4270 select SND_SOC_AK4104 help @@ -132,7 +132,7 @@ config SND_PXA2XX_SOC_MAGICIAN tristate "SoC Audio support for HTC Magician" depends on SND_PXA2XX_SOC && MACH_MAGICIAN select SND_PXA2XX_SOC_I2S - select SND_PXA_SOC_SSP + select SND_PXA2XX_SOC_SSP select SND_SOC_UDA1380 help Say Y if you want to add support for SoC audio on the diff --git a/sound/soc/pxa/Makefile b/sound/soc/pxa/Makefile index f3e08fd..33c1579 100644 --- a/sound/soc/pxa/Makefile +++ b/sound/soc/pxa/Makefile @@ -2,12 +2,12 @@ snd-soc-pxa2xx-objs := pxa2xx-pcm.o snd-soc-pxa2xx-ac97-objs := pxa2xx-ac97.o snd-soc-pxa2xx-i2s-objs := pxa2xx-i2s.o -snd-soc-pxa-ssp-objs := pxa-ssp.o +snd-soc-pxa2xx-ssp-objs := pxa-ssp.o pxa2xx-ssp.o obj-$(CONFIG_SND_PXA2XX_SOC) += snd-soc-pxa2xx.o obj-$(CONFIG_SND_PXA2XX_SOC_AC97) += snd-soc-pxa2xx-ac97.o obj-$(CONFIG_SND_PXA2XX_SOC_I2S) += snd-soc-pxa2xx-i2s.o -obj-$(CONFIG_SND_PXA_SOC_SSP) += snd-soc-pxa-ssp.o +obj-$(CONFIG_SND_PXA2XX_SOC_SSP) += snd-soc-pxa2xx-ssp.o # PXA Machine Support snd-soc-corgi-objs := corgi.o diff --git a/sound/soc/pxa/magician.c b/sound/soc/pxa/magician.c index 4c8d99a..da6ff83 100644 --- a/sound/soc/pxa/magician.c +++ b/sound/soc/pxa/magician.c @@ -34,7 +34,7 @@ #include "../codecs/uda1380.h" #include "pxa2xx-pcm.h" #include "pxa2xx-i2s.h" -#include "pxa-ssp.h" +#include "pxa2xx-ssp.h" #define MAGICIAN_MIC 0 #define MAGICIAN_MIC_EXT 1 @@ -89,7 +89,7 @@ static int magician_playback_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; unsigned int acps, acds, width, rate; - unsigned int div4 = PXA_SSP_CLK_SCDB_4; + unsigned int div4 = PXA2XX_SSP_CLK_SCDB_4; int ret = 0; rate = params_rate(params); @@ -106,11 +106,11 @@ static int magician_playback_hw_params(struct snd_pcm_substream *substream, switch (width) { case 16: /* 513156 Hz ~= _2_ * 8000 Hz * 32 (+0.23%) */ - acds = PXA_SSP_CLK_AUDIO_DIV_16; + acds = PXA2XX_SSP_CLK_AUDIO_DIV_16; break; default: /* 32 */ /* 1026312 Hz ~= _2_ * 8000 Hz * 64 (+0.23%) */ - acds = PXA_SSP_CLK_AUDIO_DIV_8; + acds = PXA2XX_SSP_CLK_AUDIO_DIV_8; } break; case 11025: @@ -118,11 +118,11 @@ static int magician_playback_hw_params(struct snd_pcm_substream *substream, switch (width) { case 16: /* 351375 Hz ~= 11025 Hz * 32 (-0.41%) */ - acds = PXA_SSP_CLK_AUDIO_DIV_4; + acds = PXA2XX_SSP_CLK_AUDIO_DIV_4; break; default: /* 32 */ /* 702750 Hz ~= 11025 Hz * 64 (-0.41%) */ - acds = PXA_SSP_CLK_AUDIO_DIV_2; + acds = PXA2XX_SSP_CLK_AUDIO_DIV_2; } break; case 22050: @@ -130,11 +130,11 @@ static int magician_playback_hw_params(struct snd_pcm_substream *substream, switch (width) { case 16: /* 702750 Hz ~= 22050 Hz * 32 (-0.41%) */ - acds = PXA_SSP_CLK_AUDIO_DIV_2; + acds = PXA2XX_SSP_CLK_AUDIO_DIV_2; break; default: /* 32 */ /* 1405500 Hz ~= 22050 Hz * 64 (-0.41%) */ - acds = PXA_SSP_CLK_AUDIO_DIV_1; + acds = PXA2XX_SSP_CLK_AUDIO_DIV_1; } break; case 44100: @@ -142,11 +142,11 @@ static int magician_playback_hw_params(struct snd_pcm_substream *substream, switch (width) { case 16: /* 1405500 Hz ~= 44100 Hz * 32 (-0.41%) */ - acds = PXA_SSP_CLK_AUDIO_DIV_2; + acds = PXA2XX_SSP_CLK_AUDIO_DIV_2; break; default: /* 32 */ /* 2811000 Hz ~= 44100 Hz * 64 (-0.41%) */ - acds = PXA_SSP_CLK_AUDIO_DIV_1; + acds = PXA2XX_SSP_CLK_AUDIO_DIV_1; } break; case 48000: @@ -154,11 +154,11 @@ static int magician_playback_hw_params(struct snd_pcm_substream *substream, switch (width) { case 16: /* 1529375 Hz ~= 48000 Hz * 32 (-0.44%) */ - acds = PXA_SSP_CLK_AUDIO_DIV_2; + acds = PXA2XX_SSP_CLK_AUDIO_DIV_2; break; default: /* 32 */ /* 3058750 Hz ~= 48000 Hz * 64 (-0.44%) */ - acds = PXA_SSP_CLK_AUDIO_DIV_1; + acds = PXA2XX_SSP_CLK_AUDIO_DIV_1; } break; case 96000: @@ -167,12 +167,12 @@ static int magician_playback_hw_params(struct snd_pcm_substream *substream, switch (width) { case 16: /* 3058750 Hz ~= 96000 Hz * 32 (-0.44%) */ - acds = PXA_SSP_CLK_AUDIO_DIV_1; + acds = PXA2XX_SSP_CLK_AUDIO_DIV_1; break; default: /* 32 */ /* 6117500 Hz ~= 96000 Hz * 64 (-0.44%) */ - acds = PXA_SSP_CLK_AUDIO_DIV_2; - div4 = PXA_SSP_CLK_SCDB_1; + acds = PXA2XX_SSP_CLK_AUDIO_DIV_2; + div4 = PXA2XX_SSP_CLK_SCDB_1; break; } break; @@ -195,20 +195,20 @@ static int magician_playback_hw_params(struct snd_pcm_substream *substream, return ret; /* set audio clock as clock source */ - ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_AUDIO, 0, + ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_SSP_CLK_AUDIO, 0, SND_SOC_CLOCK_OUT); if (ret < 0) return ret; /* set the SSP audio system clock ACDS divider */ ret = snd_soc_dai_set_clkdiv(cpu_dai, - PXA_SSP_AUDIO_DIV_ACDS, acds); + PXA2XX_SSP_AUDIO_DIV_ACDS, acds); if (ret < 0) return ret; /* set the SSP audio system clock SCDB divider4 */ ret = snd_soc_dai_set_clkdiv(cpu_dai, - PXA_SSP_AUDIO_DIV_SCDB, div4); + PXA2XX_SSP_AUDIO_DIV_SCDB, div4); if (ret < 0) return ret; @@ -427,7 +427,7 @@ static struct snd_soc_dai_link magician_dai[] = { { .name = "uda1380", .stream_name = "UDA1380 Playback", - .cpu_dai = &pxa_ssp_dai[PXA_DAI_SSP1], + .cpu_dai = &pxa2xx_ssp_dai[PXA2XX_DAI_SSP1], .codec_dai = &uda1380_dai[UDA1380_DAI_PLAYBACK], .init = magician_uda1380_init, .ops = &magician_playback_ops, diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c index ed54bef..bd5e800 100644 --- a/sound/soc/pxa/pxa-ssp.c +++ b/sound/soc/pxa/pxa-ssp.c @@ -5,6 +5,9 @@ * Author: Liam Girdwood * Mark Brown <broonie@xxxxxxxxxxxxxxxxxxxxxxxxxxx> * + * Copyright 2010 Marvell International Ltd. + * Haojian Zhuang <haojian.zhuang@xxxxxxxxxxx> + * * 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 @@ -14,44 +17,20 @@ * 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/soc.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/audio.h> #include <plat/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", @@ -63,7 +42,7 @@ static void dump_registers(struct ssp_device *ssp) pxa_ssp_read_reg(ssp, SSACD)); } -static void pxa_ssp_enable(struct ssp_device *ssp) +void pxa_ssp_enable(struct ssp_device *ssp) { uint32_t sscr0; @@ -71,7 +50,7 @@ static void pxa_ssp_enable(struct ssp_device *ssp) __raw_writel(sscr0, ssp->mmio_base + SSCR0); } -static void pxa_ssp_disable(struct ssp_device *ssp) +void pxa_ssp_disable(struct ssp_device *ssp) { uint32_t sscr0; @@ -84,7 +63,7 @@ struct pxa2xx_pcm_dma_data { char name[20]; }; -static struct pxa2xx_pcm_dma_params * +struct pxa2xx_pcm_dma_params * pxa_ssp_get_dma_params(struct ssp_device *ssp, int width4, int out) { struct pxa2xx_pcm_dma_data *dma; @@ -105,6 +84,102 @@ pxa_ssp_get_dma_params(struct ssp_device *ssp, int width4, int out) return &dma->params; } +EXPORT_SYMBOL_GPL(pxa_ssp_get_dma_params); + +/* + * Set up the SSP DAI format. + * The SSP Port must be inactive before calling this function as the + * physical interface format is changed. + */ +static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai, + unsigned int fmt) +{ + struct ssp_priv *priv = cpu_dai->private_data; + struct ssp_device *ssp = priv->ssp; + u32 sscr0; + u32 sscr1; + u32 sspsp; + + /* check if we need to change anything at all */ + if (priv->dai_fmt == fmt) + return 0; + + pxa_ssp_disable(ssp); + + /* reset port settings */ + sscr0 = pxa_ssp_read_reg(ssp, SSCR0) & + (SSCR0_ECS | SSCR0_NCS | SSCR0_MOD | SSCR0_ACS); + sscr1 = SSCR1_RxTresh(8) | SSCR1_TxTresh(7); + sspsp = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + sscr1 |= SSCR1_SCLKDIR | SSCR1_SFRMDIR; + break; + case SND_SOC_DAIFMT_CBM_CFS: + sscr1 |= SSCR1_SCLKDIR; + break; + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + sspsp |= SSPSP_SFRMP; + break; + case SND_SOC_DAIFMT_NB_IF: + break; + case SND_SOC_DAIFMT_IB_IF: + sspsp |= SSPSP_SCMODE(2); + break; + case SND_SOC_DAIFMT_IB_NF: + sspsp |= SSPSP_SCMODE(2) | SSPSP_SFRMP; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + sscr0 |= SSCR0_PSP; + sscr1 |= SSCR1_RWOT | SSCR1_TRAIL; + /* See hw_params() */ + break; + + case SND_SOC_DAIFMT_LEFT_J: + sscr0 |= SSCR0_PSP; + sscr1 |= SSCR1_RWOT | SSCR1_TRAIL; + /* See hw_params() */ + break; + + case SND_SOC_DAIFMT_DSP_A: + sspsp |= SSPSP_FSRT; + case SND_SOC_DAIFMT_DSP_B: + sscr0 |= SSCR0_MOD | SSCR0_PSP; + sscr1 |= SSCR1_TRAIL | SSCR1_RWOT; + break; + + default: + return -EINVAL; + } + + pxa_ssp_write_reg(ssp, SSCR0, sscr0); + pxa_ssp_write_reg(ssp, SSCR1, sscr1); + pxa_ssp_write_reg(ssp, SSPSP, sspsp); + pxa_ssp_enable(ssp); + + /* Since we are configuring the timings for the format by hand + * we have to defer some things until hw_params() where we + * know parameters like the sample size. + */ + priv->dai_fmt = fmt; + + dump_registers(ssp); + + return 0; +} static int pxa_ssp_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) @@ -193,212 +268,88 @@ static int pxa_ssp_resume(struct snd_soc_dai *cpu_dai) #define pxa_ssp_resume NULL #endif -/** - * ssp_set_clkdiv - set SSP clock divider - * @div: serial clock rate divider - */ -static void pxa_ssp_set_scr(struct ssp_device *ssp, u32 div) -{ - u32 sscr0 = pxa_ssp_read_reg(ssp, SSCR0); - - if (cpu_is_pxa25x() && ssp->type == PXA25x_SSP) { - sscr0 &= ~0x0000ff00; - sscr0 |= ((div - 2)/2) << 8; /* 2..512 */ - } else { - sscr0 &= ~0x000fff00; - sscr0 |= (div - 1) << 8; /* 1..4096 */ - } - pxa_ssp_write_reg(ssp, SSCR0, sscr0); -} - -/** - * pxa_ssp_get_clkdiv - get SSP clock divider - */ -static u32 pxa_ssp_get_scr(struct ssp_device *ssp) -{ - u32 sscr0 = pxa_ssp_read_reg(ssp, SSCR0); - u32 div; - - if (cpu_is_pxa25x() && ssp->type == PXA25x_SSP) - div = ((sscr0 >> 8) & 0xff) * 2 + 2; - else - div = ((sscr0 >> 8) & 0xfff) + 1; - return div; -} - /* - * Set the SSP ports SYSCLK. + * While configuring SSP timing, two formula on SSP clocks should be followed. + * + * 1) (frame delay + frame width) <= (start delay + dummy start + * + data size + dummy stop) + * + * 2) 1 <= frame width < (dummy start + data size + dummy stop) */ -static int pxa_ssp_set_dai_sysclk(struct snd_soc_dai *cpu_dai, - int clk_id, unsigned int freq, int dir) +static int pxa_ssp_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + 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 val; + int width = snd_pcm_format_physical_width(params_format(params)); + int channels = params_channels(params); + int dma_32b = 0, stream_out = 0, data_size; + u32 sscr0, sspsp; - u32 sscr0 = pxa_ssp_read_reg(ssp, SSCR0) & - ~(SSCR0_ECS | SSCR0_NCS | SSCR0_MOD | SSCR0_ACS); + /* generate correct DMA params */ + kfree(cpu_dai->dma_data); - dev_dbg(&ssp->pdev->dev, - "pxa_ssp_set_dai_sysclk id: %d, clk_id %d, freq %u\n", - cpu_dai->id, clk_id, freq); + if ((width == 32) || (channels == 1)) + dma_32b = 1; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + stream_out = 1; + cpu_dai->dma_data = pxa_ssp_get_dma_params(ssp, dma_32b, stream_out); - switch (clk_id) { - case PXA_SSP_CLK_NET_PLL: - sscr0 |= SSCR0_MOD; - break; - case PXA_SSP_CLK_PLL: - /* Internal PLL is fixed */ - if (cpu_is_pxa25x()) - priv->sysclk = 1843200; - else - priv->sysclk = 13000000; - break; - case PXA_SSP_CLK_EXT: - priv->sysclk = freq; - sscr0 |= SSCR0_ECS; - break; - case PXA_SSP_CLK_NET: - priv->sysclk = freq; - sscr0 |= SSCR0_NCS | SSCR0_MOD; + /* clear selected SSP bits */ + sscr0 = pxa_ssp_read_reg(ssp, SSCR0) & ~(SSCR0_DSS | SSCR0_EDSS); + + /* data_size should only be 16-bit or 32-bit because of DMA. + * 24-bit data is treated as 32-bit data. + */ + data_size = width * channels; + switch (data_size) { + case 16: + sscr0 |= SSCR0_DataSize(16); break; - case PXA_SSP_CLK_AUDIO: - priv->sysclk = 0; - pxa_ssp_set_scr(ssp, 1); - sscr0 |= SSCR0_ACS; + case 32: + sscr0 |= (SSCR0_EDSS | SSCR0_DataSize(16)); break; default: - return -ENODEV; + dev_err(&ssp->pdev->dev, "Invalid data size:%d\n", data_size); + return -EINVAL; } - /* The SSP clock must be disabled when changing SSP clock mode - * on PXA2xx. On PXA3xx it must be enabled when doing so. */ - if (!cpu_is_pxa3xx()) - clk_disable(ssp->clk); - val = pxa_ssp_read_reg(ssp, SSCR0) | sscr0; - pxa_ssp_write_reg(ssp, SSCR0, val); - if (!cpu_is_pxa3xx()) - clk_enable(ssp->clk); - - return 0; -} - -/* - * Set the SSP clock dividers. - */ -static int pxa_ssp_set_dai_clkdiv(struct snd_soc_dai *cpu_dai, - int div_id, int div) -{ - struct ssp_priv *priv = cpu_dai->private_data; - struct ssp_device *ssp = priv->ssp; - int val; - - switch (div_id) { - case PXA_SSP_AUDIO_DIV_ACDS: - val = (pxa_ssp_read_reg(ssp, SSACD) & ~0x7) | SSACD_ACDS(div); - pxa_ssp_write_reg(ssp, SSACD, val); - break; - case PXA_SSP_AUDIO_DIV_SCDB: - val = pxa_ssp_read_reg(ssp, SSACD); - val &= ~SSACD_SCDB; -#if defined(CONFIG_PXA3xx) - if (cpu_is_pxa3xx()) - val &= ~SSACD_SCDX8; -#endif - switch (div) { - case PXA_SSP_CLK_SCDB_1: - val |= SSACD_SCDB; - break; - case PXA_SSP_CLK_SCDB_4: - break; -#if defined(CONFIG_PXA3xx) - case PXA_SSP_CLK_SCDB_8: - if (cpu_is_pxa3xx()) - val |= SSACD_SCDX8; - else - return -EINVAL; - break; -#endif - default: - return -EINVAL; + pxa_ssp_disable(ssp); + sspsp = pxa_ssp_read_reg(ssp, SSPSP); + sspsp &= ~SSPSP_TIMING_MASK; + switch (priv->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + if (channels == 1) { + sspsp |= SSPSP_DMYSTRT(1); + sspsp |= SSPSP_EDMYSTOP(((width - 1) >> 2) & 7); + sspsp |= SSPSP_DMYSTOP((width - 1) & 3); + sspsp |= SSPSP_SFRMDLY(width << 1); + sspsp |= SSPSP_SFRMWDTH(width); + } else { + /* channels == 2 */ + sspsp |= SSPSP_SFRMWDTH(width); + /* Don't set any timing while FSRT is set */ + sspsp |= SSPSP_FSRT; } - pxa_ssp_write_reg(ssp, SSACD, val); break; - case PXA_SSP_DIV_SCR: - pxa_ssp_set_scr(ssp, div); + case SND_SOC_DAIFMT_LEFT_J: + if (channels == 1) { + sspsp |= SSPSP_EDMYSTOP((width >> 2) & 7); + sspsp |= SSPSP_DMYSTOP(width & 3); + } + sspsp |= SSPSP_SFRMWDTH(width); break; - default: - return -ENODEV; } - return 0; -} - -/* - * Configure the PLL frequency pxa27x and (afaik - pxa320 only) - */ -static int pxa_ssp_set_dai_pll(struct snd_soc_dai *cpu_dai, int pll_id, - int source, unsigned int freq_in, unsigned int freq_out) -{ - struct ssp_priv *priv = cpu_dai->private_data; - struct ssp_device *ssp = priv->ssp; - u32 ssacd = pxa_ssp_read_reg(ssp, SSACD) & ~0x70; - -#if defined(CONFIG_PXA3xx) - if (cpu_is_pxa3xx()) - pxa_ssp_write_reg(ssp, SSACDD, 0); -#endif - - switch (freq_out) { - case 5622000: - break; - case 11345000: - ssacd |= (0x1 << 4); - break; - case 12235000: - ssacd |= (0x2 << 4); - break; - case 14857000: - ssacd |= (0x3 << 4); - break; - case 32842000: - ssacd |= (0x4 << 4); - break; - case 48000000: - ssacd |= (0x5 << 4); - break; - case 0: - /* Disable */ - break; - - default: -#ifdef CONFIG_PXA3xx - /* PXA3xx has a clock ditherer which can be used to generate - * a wider range of frequencies - calculate a value for it. - */ - if (cpu_is_pxa3xx()) { - u32 val; - u64 tmp = 19968; - tmp *= 1000000; - do_div(tmp, freq_out); - val = tmp; - - val = (val << 16) | 64; - pxa_ssp_write_reg(ssp, SSACDD, val); - - ssacd |= (0x6 << 4); - - dev_dbg(&ssp->pdev->dev, - "Using SSACDD %x to supply %uHz\n", - val, freq_out); - break; - } -#endif - - return -EINVAL; - } + /* update SSP register at the same time */ + pxa_ssp_write_reg(ssp, SSCR0, sscr0); + pxa_ssp_write_reg(ssp, SSPSP, sspsp); + pxa_ssp_enable(ssp); - pxa_ssp_write_reg(ssp, SSACD, ssacd); + dump_registers(ssp); return 0; } @@ -458,213 +409,6 @@ static int pxa_ssp_set_dai_tristate(struct snd_soc_dai *cpu_dai, return 0; } -/* - * Set up the SSP DAI format. - * The SSP Port must be inactive before calling this function as the - * physical interface format is changed. - */ -static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai, - unsigned int fmt) -{ - struct ssp_priv *priv = cpu_dai->private_data; - struct ssp_device *ssp = priv->ssp; - u32 sscr0; - u32 sscr1; - u32 sspsp; - - /* check if we need to change anything at all */ - if (priv->dai_fmt == fmt) - return 0; - - /* we can only change the settings if the port is not in use */ - if (pxa_ssp_read_reg(ssp, SSCR0) & SSCR0_SSE) { - dev_err(&ssp->pdev->dev, - "can't change hardware dai format: stream is in use"); - return -EINVAL; - } - - /* reset port settings */ - sscr0 = pxa_ssp_read_reg(ssp, SSCR0) & - (SSCR0_ECS | SSCR0_NCS | SSCR0_MOD | SSCR0_ACS); - sscr1 = SSCR1_RxTresh(8) | SSCR1_TxTresh(7); - sspsp = 0; - - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: - sscr1 |= SSCR1_SCLKDIR | SSCR1_SFRMDIR; - break; - case SND_SOC_DAIFMT_CBM_CFS: - sscr1 |= SSCR1_SCLKDIR; - break; - case SND_SOC_DAIFMT_CBS_CFS: - break; - default: - return -EINVAL; - } - - switch (fmt & SND_SOC_DAIFMT_INV_MASK) { - case SND_SOC_DAIFMT_NB_NF: - sspsp |= SSPSP_SFRMP; - break; - case SND_SOC_DAIFMT_NB_IF: - break; - case SND_SOC_DAIFMT_IB_IF: - sspsp |= SSPSP_SCMODE(2); - break; - case SND_SOC_DAIFMT_IB_NF: - sspsp |= SSPSP_SCMODE(2) | SSPSP_SFRMP; - break; - default: - return -EINVAL; - } - - switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { - case SND_SOC_DAIFMT_I2S: - sscr0 |= SSCR0_PSP; - sscr1 |= SSCR1_RWOT | SSCR1_TRAIL; - /* See hw_params() */ - break; - - case SND_SOC_DAIFMT_DSP_A: - sspsp |= SSPSP_FSRT; - case SND_SOC_DAIFMT_DSP_B: - sscr0 |= SSCR0_MOD | SSCR0_PSP; - sscr1 |= SSCR1_TRAIL | SSCR1_RWOT; - break; - - default: - return -EINVAL; - } - - pxa_ssp_write_reg(ssp, SSCR0, sscr0); - pxa_ssp_write_reg(ssp, SSCR1, sscr1); - pxa_ssp_write_reg(ssp, SSPSP, sspsp); - - dump_registers(ssp); - - /* Since we are configuring the timings for the format by hand - * we have to defer some things until hw_params() where we - * know parameters like the sample size. - */ - priv->dai_fmt = fmt; - - return 0; -} - -/* - * Set the SSP audio DMA parameters and sample size. - * Can be called multiple times by oss emulation. - */ -static int pxa_ssp_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - 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 chn = params_channels(params); - u32 sscr0; - u32 sspsp; - int width = snd_pcm_format_physical_width(params_format(params)); - int ttsa = pxa_ssp_read_reg(ssp, SSTSA) & 0xf; - - /* generate correct DMA params */ - if (cpu_dai->dma_data) - kfree(cpu_dai->dma_data); - - /* Network mode with one active slot (ttsa == 1) can be used - * to force 16-bit frame width on the wire (for S16_LE), even - * with two channels. Use 16-bit DMA transfers for this case. - */ - cpu_dai->dma_data = pxa_ssp_get_dma_params(ssp, - ((chn == 2) && (ttsa != 1)) || (width == 32), - substream->stream == SNDRV_PCM_STREAM_PLAYBACK); - - /* we can only change the settings if the port is not in use */ - if (pxa_ssp_read_reg(ssp, SSCR0) & SSCR0_SSE) - return 0; - - /* clear selected SSP bits */ - sscr0 = pxa_ssp_read_reg(ssp, SSCR0) & ~(SSCR0_DSS | SSCR0_EDSS); - pxa_ssp_write_reg(ssp, SSCR0, sscr0); - - /* bit size */ - sscr0 = pxa_ssp_read_reg(ssp, SSCR0); - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: -#ifdef CONFIG_PXA3xx - if (cpu_is_pxa3xx()) - sscr0 |= SSCR0_FPCKE; -#endif - sscr0 |= SSCR0_DataSize(16); - break; - case SNDRV_PCM_FORMAT_S24_LE: - sscr0 |= (SSCR0_EDSS | SSCR0_DataSize(8)); - break; - case SNDRV_PCM_FORMAT_S32_LE: - sscr0 |= (SSCR0_EDSS | SSCR0_DataSize(16)); - break; - } - pxa_ssp_write_reg(ssp, SSCR0, sscr0); - - switch (priv->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) { - case SND_SOC_DAIFMT_I2S: - sspsp = pxa_ssp_read_reg(ssp, SSPSP); - - if ((pxa_ssp_get_scr(ssp) == 4) && (width == 16)) { - /* This is a special case where the bitclk is 64fs - * and we're not dealing with 2*32 bits of audio - * samples. - * - * The SSP values used for that are all found out by - * trying and failing a lot; some of the registers - * needed for that mode are only available on PXA3xx. - */ - -#ifdef CONFIG_PXA3xx - if (!cpu_is_pxa3xx()) - return -EINVAL; - - sspsp |= SSPSP_SFRMWDTH(width * 2); - sspsp |= SSPSP_SFRMDLY(width * 4); - sspsp |= SSPSP_EDMYSTOP(3); - sspsp |= SSPSP_DMYSTOP(3); - sspsp |= SSPSP_DMYSTRT(1); -#else - return -EINVAL; -#endif - } else { - /* The frame width is the width the LRCLK is - * asserted for; the delay is expressed in - * half cycle units. We need the extra cycle - * because the data starts clocking out one BCLK - * after LRCLK changes polarity. - */ - sspsp |= SSPSP_SFRMWDTH(width + 1); - sspsp |= SSPSP_SFRMDLY((width + 1) * 2); - sspsp |= SSPSP_DMYSTRT(1); - } - - pxa_ssp_write_reg(ssp, SSPSP, sspsp); - break; - default: - break; - } - - /* When we use a network mode, we always require TDM slots - * - complain loudly and fail if they've not been set up yet. - */ - if ((sscr0 & SSCR0_MOD) && !ttsa) { - dev_err(&ssp->pdev->dev, "No TDM timeslot configured\n"); - return -EINVAL; - } - - dump_registers(ssp); - - return 0; -} - static int pxa_ssp_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { @@ -760,128 +504,22 @@ static void pxa_ssp_remove(struct platform_device *pdev, pxa_ssp_free(priv->ssp); } -#define PXA_SSP_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ - SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \ - SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \ - SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) - -#define PXA_SSP_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ - SNDRV_PCM_FMTBIT_S24_LE | \ - SNDRV_PCM_FMTBIT_S32_LE) - -static struct snd_soc_dai_ops pxa_ssp_dai_ops = { - .startup = pxa_ssp_startup, - .shutdown = pxa_ssp_shutdown, - .trigger = pxa_ssp_trigger, - .hw_params = pxa_ssp_hw_params, - .set_sysclk = pxa_ssp_set_dai_sysclk, - .set_clkdiv = pxa_ssp_set_dai_clkdiv, - .set_pll = pxa_ssp_set_dai_pll, - .set_fmt = pxa_ssp_set_dai_fmt, - .set_tdm_slot = pxa_ssp_set_dai_tdm_slot, - .set_tristate = pxa_ssp_set_dai_tristate, -}; - -struct snd_soc_dai pxa_ssp_dai[] = { - { - .name = "pxa2xx-ssp1", - .id = 0, - .probe = pxa_ssp_probe, - .remove = pxa_ssp_remove, - .suspend = pxa_ssp_suspend, - .resume = pxa_ssp_resume, - .playback = { - .channels_min = 1, - .channels_max = 8, - .rates = PXA_SSP_RATES, - .formats = PXA_SSP_FORMATS, - }, - .capture = { - .channels_min = 1, - .channels_max = 8, - .rates = PXA_SSP_RATES, - .formats = PXA_SSP_FORMATS, - }, - .ops = &pxa_ssp_dai_ops, - }, - { .name = "pxa2xx-ssp2", - .id = 1, - .probe = pxa_ssp_probe, - .remove = pxa_ssp_remove, - .suspend = pxa_ssp_suspend, - .resume = pxa_ssp_resume, - .playback = { - .channels_min = 1, - .channels_max = 8, - .rates = PXA_SSP_RATES, - .formats = PXA_SSP_FORMATS, - }, - .capture = { - .channels_min = 1, - .channels_max = 8, - .rates = PXA_SSP_RATES, - .formats = PXA_SSP_FORMATS, - }, - .ops = &pxa_ssp_dai_ops, - }, - { - .name = "pxa2xx-ssp3", - .id = 2, - .probe = pxa_ssp_probe, - .remove = pxa_ssp_remove, - .suspend = pxa_ssp_suspend, - .resume = pxa_ssp_resume, - .playback = { - .channels_min = 1, - .channels_max = 8, - .rates = PXA_SSP_RATES, - .formats = PXA_SSP_FORMATS, - }, - .capture = { - .channels_min = 1, - .channels_max = 8, - .rates = PXA_SSP_RATES, - .formats = PXA_SSP_FORMATS, - }, - .ops = &pxa_ssp_dai_ops, - }, - { - .name = "pxa2xx-ssp4", - .id = 3, - .probe = pxa_ssp_probe, - .remove = pxa_ssp_remove, - .suspend = pxa_ssp_suspend, - .resume = pxa_ssp_resume, - .playback = { - .channels_min = 1, - .channels_max = 8, - .rates = PXA_SSP_RATES, - .formats = PXA_SSP_FORMATS, - }, - .capture = { - .channels_min = 1, - .channels_max = 8, - .rates = PXA_SSP_RATES, - .formats = PXA_SSP_FORMATS, - }, - .ops = &pxa_ssp_dai_ops, - }, -}; -EXPORT_SYMBOL_GPL(pxa_ssp_dai); - -static int __init pxa_ssp_init(void) -{ - return snd_soc_register_dais(pxa_ssp_dai, ARRAY_SIZE(pxa_ssp_dai)); -} -module_init(pxa_ssp_init); - -static void __exit pxa_ssp_exit(void) +int pxa_ssp_register_dai(struct snd_soc_dai *dai) { - snd_soc_unregister_dais(pxa_ssp_dai, ARRAY_SIZE(pxa_ssp_dai)); + struct snd_soc_dai_ops *ops = dai->ops; + + ops->startup = pxa_ssp_startup; + ops->shutdown = pxa_ssp_shutdown; + ops->trigger = pxa_ssp_trigger; + ops->hw_params = pxa_ssp_hw_params; + ops->set_fmt = pxa_ssp_set_dai_fmt; + ops->set_tdm_slot = pxa_ssp_set_dai_tdm_slot; + ops->set_tristate = pxa_ssp_set_dai_tristate; + + dai->probe = pxa_ssp_probe; + dai->remove = pxa_ssp_remove; + dai->suspend = pxa_ssp_suspend; + dai->resume = pxa_ssp_resume; + + return snd_soc_register_dai(dai); } -module_exit(pxa_ssp_exit); - -/* Module information */ -MODULE_AUTHOR("Mark Brown <broonie@xxxxxxxxxxxxxxxxxxxxxxxxxxx>"); -MODULE_DESCRIPTION("PXA SSP/PCM SoC Interface"); -MODULE_LICENSE("GPL"); diff --git a/sound/soc/pxa/pxa-ssp.h b/sound/soc/pxa/pxa-ssp.h index 91deadd..af4a09b 100644 --- a/sound/soc/pxa/pxa-ssp.h +++ b/sound/soc/pxa/pxa-ssp.h @@ -9,39 +9,25 @@ #ifndef _PXA_SSP_H #define _PXA_SSP_H -/* pxa DAI SSP IDs */ -#define PXA_DAI_SSP1 0 -#define PXA_DAI_SSP2 1 -#define PXA_DAI_SSP3 2 -#define PXA_DAI_SSP4 3 - -/* SSP clock sources */ -#define PXA_SSP_CLK_PLL 0 -#define PXA_SSP_CLK_EXT 1 -#define PXA_SSP_CLK_NET 2 -#define PXA_SSP_CLK_AUDIO 3 -#define PXA_SSP_CLK_NET_PLL 4 - -/* SSP audio dividers */ -#define PXA_SSP_AUDIO_DIV_ACDS 0 -#define PXA_SSP_AUDIO_DIV_SCDB 1 -#define PXA_SSP_DIV_SCR 2 - -/* SSP ACDS audio dividers values */ -#define PXA_SSP_CLK_AUDIO_DIV_1 0 -#define PXA_SSP_CLK_AUDIO_DIV_2 1 -#define PXA_SSP_CLK_AUDIO_DIV_4 2 -#define PXA_SSP_CLK_AUDIO_DIV_8 3 -#define PXA_SSP_CLK_AUDIO_DIV_16 4 -#define PXA_SSP_CLK_AUDIO_DIV_32 5 - -/* SSP divider bypass */ -#define PXA_SSP_CLK_SCDB_4 0 -#define PXA_SSP_CLK_SCDB_1 1 -#define PXA_SSP_CLK_SCDB_8 2 - -#define PXA_SSP_PLL_OUT 0 +/* + * SSP audio 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 +}; -extern struct snd_soc_dai pxa_ssp_dai[4]; +extern void pxa_ssp_enable(struct ssp_device *ssp); +extern void pxa_ssp_disable(struct ssp_device *ssp); +extern struct pxa2xx_pcm_dma_params * +pxa_ssp_get_dma_params(struct ssp_device *ssp, int width4, int out); +extern int pxa_ssp_register_dai(struct snd_soc_dai *dai); #endif diff --git a/sound/soc/pxa/pxa2xx-ssp.c b/sound/soc/pxa/pxa2xx-ssp.c new file mode 100644 index 0000000..eff5334 --- /dev/null +++ b/sound/soc/pxa/pxa2xx-ssp.c @@ -0,0 +1,318 @@ +/* + * pxa2xx-ssp.c -- ALSA Soc Audio Layer + * + * Copyright 2005,2008 Wolfson Microelectronics PLC. + * Author: Liam Girdwood + * Mark Brown <broonie@xxxxxxxxxxxxxxxxxxxxxxxxxxx> + * + * Copyright 2010 Marvell International Ltd. + * Haojian Zhuang <haojian.zhuang@xxxxxxxxxxx> + * + * 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 <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/pxa2xx-lib.h> + +#include <mach/hardware.h> +#include <mach/dma.h> +#include <plat/ssp.h> + +#include "pxa2xx-pcm.h" +#include "pxa2xx-ssp.h" +#include "pxa-ssp.h" + +/** + * pxa_ssp_set_clkdiv - set SSP clock divider + * @div: serial clock rate divider + */ +static void pxa_ssp_set_scr(struct ssp_device *ssp, u32 div) +{ + u32 sscr0 = pxa_ssp_read_reg(ssp, SSCR0); + + if (cpu_is_pxa25x() && ssp->type == PXA25x_SSP) { + sscr0 &= ~0x0000ff00; + sscr0 |= ((div - 2)/2) << 8; /* 2..512 */ + } else { + sscr0 &= ~0x000fff00; + sscr0 |= (div - 1) << 8; /* 1..4096 */ + } + pxa_ssp_write_reg(ssp, SSCR0, sscr0); +} + +/** + * pxa_ssp_get_clkdiv - get SSP clock divider + */ +static u32 pxa_ssp_get_scr(struct ssp_device *ssp) +{ + u32 sscr0 = pxa_ssp_read_reg(ssp, SSCR0); + u32 div; + + if (cpu_is_pxa25x() && ssp->type == PXA25x_SSP) + div = ((sscr0 >> 8) & 0xff) * 2 + 2; + else + div = ((sscr0 >> 8) & 0xfff) + 1; + return div; +} + +/* + * Set the SSP ports SYSCLK. + */ +static int pxa2xx_ssp_set_dai_sysclk(struct snd_soc_dai *cpu_dai, + int clk_id, unsigned int freq, int dir) +{ + struct ssp_priv *priv = cpu_dai->private_data; + struct ssp_device *ssp = priv->ssp; + int val; + + u32 sscr0 = pxa_ssp_read_reg(ssp, SSCR0) & + ~(SSCR0_ECS | SSCR0_NCS | SSCR0_MOD | SSCR0_ACS); + + dev_dbg(&ssp->pdev->dev, + "pxa_ssp_set_dai_sysclk id: %d, clk_id %d, freq %u\n", + cpu_dai->id, clk_id, freq); + + switch (clk_id) { + case PXA2XX_SSP_CLK_NET_PLL: + sscr0 |= SSCR0_MOD; + break; + case PXA2XX_SSP_CLK_PLL: + /* Internal PLL is fixed */ + if (cpu_is_pxa25x()) + priv->sysclk = 1843200; + else + priv->sysclk = 13000000; + break; + case PXA2XX_SSP_CLK_EXT: + priv->sysclk = freq; + sscr0 |= SSCR0_ECS; + break; + case PXA2XX_SSP_CLK_NET: + priv->sysclk = freq; + sscr0 |= SSCR0_NCS | SSCR0_MOD; + break; + case PXA2XX_SSP_CLK_AUDIO: + priv->sysclk = 0; + pxa_ssp_set_scr(ssp, 1); + sscr0 |= SSCR0_ACS; + break; + default: + return -ENODEV; + } + + /* The SSP clock must be disabled when changing SSP clock mode + * on PXA2xx. On PXA3xx it must be enabled when doing so. */ + if (!cpu_is_pxa3xx()) + clk_disable(ssp->clk); + val = pxa_ssp_read_reg(ssp, SSCR0) | sscr0; + pxa_ssp_write_reg(ssp, SSCR0, val); + if (!cpu_is_pxa3xx()) + clk_enable(ssp->clk); + + return 0; +} + +/* + * Set the SSP clock dividers. + */ +static int pxa2xx_ssp_set_dai_clkdiv(struct snd_soc_dai *cpu_dai, + int div_id, int div) +{ + struct ssp_priv *priv = cpu_dai->private_data; + struct ssp_device *ssp = priv->ssp; + int val; + + switch (div_id) { + case PXA2XX_SSP_AUDIO_DIV_ACDS: + val = (pxa_ssp_read_reg(ssp, SSACD) & ~0x7) | SSACD_ACDS(div); + pxa_ssp_write_reg(ssp, SSACD, val); + break; + case PXA2XX_SSP_AUDIO_DIV_SCDB: + val = pxa_ssp_read_reg(ssp, SSACD); + val &= ~SSACD_SCDB; +#if defined(CONFIG_PXA3xx) + if (cpu_is_pxa3xx()) + val &= ~SSACD_SCDX8; +#endif + switch (div) { + case PXA2XX_SSP_CLK_SCDB_1: + val |= SSACD_SCDB; + break; + case PXA2XX_SSP_CLK_SCDB_4: + break; +#if defined(CONFIG_PXA3xx) + case PXA2XX_SSP_CLK_SCDB_8: + if (cpu_is_pxa3xx()) + val |= SSACD_SCDX8; + else + return -EINVAL; + break; +#endif + default: + return -EINVAL; + } + pxa_ssp_write_reg(ssp, SSACD, val); + break; + case PXA2XX_SSP_DIV_SCR: + pxa_ssp_set_scr(ssp, div); + break; + default: + return -ENODEV; + } + + return 0; +} + +/* + * Configure the PLL frequency pxa27x and (afaik - pxa320 only) + */ +static int pxa2xx_ssp_set_dai_pll(struct snd_soc_dai *cpu_dai, int pll_id, + int source, unsigned int freq_in, + unsigned int freq_out) +{ + struct ssp_priv *priv = cpu_dai->private_data; + struct ssp_device *ssp = priv->ssp; + u32 ssacd = pxa_ssp_read_reg(ssp, SSACD) & ~0x70; + +#if defined(CONFIG_PXA3xx) + if (cpu_is_pxa3xx()) + pxa_ssp_write_reg(ssp, SSACDD, 0); +#endif + + switch (freq_out) { + case 5622000: + break; + case 11345000: + ssacd |= (0x1 << 4); + break; + case 12235000: + ssacd |= (0x2 << 4); + break; + case 14857000: + ssacd |= (0x3 << 4); + break; + case 32842000: + ssacd |= (0x4 << 4); + break; + case 48000000: + ssacd |= (0x5 << 4); + break; + case 0: + /* Disable */ + break; + + default: +#ifdef CONFIG_PXA3xx + /* PXA3xx has a clock ditherer which can be used to generate + * a wider range of frequencies - calculate a value for it. + */ + if (cpu_is_pxa3xx()) { + u32 val; + u64 tmp = 19968; + tmp *= 1000000; + do_div(tmp, freq_out); + val = tmp; + + val = (val << 16) | 64; + pxa_ssp_write_reg(ssp, SSACDD, val); + + ssacd |= (0x6 << 4); + + dev_dbg(&ssp->pdev->dev, + "Using SSACDD %x to supply %uHz\n", + val, freq_out); + break; + } +#endif + + return -EINVAL; + } + + pxa_ssp_write_reg(ssp, SSACD, ssacd); + + return 0; +} + +static struct snd_soc_dai_ops pxa2xx_ssp_dai_ops = { + .set_sysclk = pxa2xx_ssp_set_dai_sysclk, + .set_clkdiv = pxa2xx_ssp_set_dai_clkdiv, + .set_pll = pxa2xx_ssp_set_dai_pll, +}; + +#define PXA2XX_SSP_RATES SNDRV_PCM_RATE_8000_96000 +#define PXA2XX_SSP_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +#define PXA2XX_SSP_DAI(_id) \ +{ \ + .name = "pxa2xx-ssp", \ + .id = _id, \ + .playback = { \ + .channels_min = 1, \ + .channels_max = 8, \ + .rates = PXA2XX_SSP_RATES, \ + .formats = PXA2XX_SSP_FORMATS, \ + }, \ + .capture = { \ + .channels_min = 1, \ + .channels_max = 8, \ + .rates = PXA2XX_SSP_RATES, \ + .formats = PXA2XX_SSP_FORMATS, \ + }, \ + .ops = &pxa2xx_ssp_dai_ops, \ +} + +struct snd_soc_dai pxa2xx_ssp_dai[] = { + PXA2XX_SSP_DAI(PXA2XX_DAI_SSP1), + PXA2XX_SSP_DAI(PXA2XX_DAI_SSP2), + PXA2XX_SSP_DAI(PXA2XX_DAI_SSP3), + PXA2XX_SSP_DAI(PXA2XX_DAI_SSP4), +}; +EXPORT_SYMBOL_GPL(pxa2xx_ssp_dai); + +static int __init pxa2xx_ssp_init(void) +{ + struct snd_soc_dai *dai; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(pxa2xx_ssp_dai); i++) { + dai = &pxa2xx_ssp_dai[i]; + ret = pxa_ssp_register_dai(dai); + if (ret) + return ret; + } + return ret; +} +module_init(pxa2xx_ssp_init); + +static void __exit pxa2xx_ssp_exit(void) +{ + struct snd_soc_dai *dai = NULL; + int i; + + for (i = 0; i < ARRAY_SIZE(pxa2xx_ssp_dai); i++) { + dai = &pxa2xx_ssp_dai[i]; + snd_soc_unregister_dai(dai); + } +} +module_exit(pxa2xx_ssp_exit); + +/* Module information */ +MODULE_AUTHOR("Mark Brown <broonie@xxxxxxxxxxxxxxxxxxxxxxxxxxx>"); +MODULE_DESCRIPTION("PXA2xx SSP/PCM SoC Interface"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/pxa/pxa2xx-ssp.h b/sound/soc/pxa/pxa2xx-ssp.h new file mode 100644 index 0000000..c24e0b5 --- /dev/null +++ b/sound/soc/pxa/pxa2xx-ssp.h @@ -0,0 +1,48 @@ + +/* + * ASoC PXA SSP port support + * + * 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. + */ + +#ifndef __PXA2XX_SOC_SSP_H +#define __PXA2XX_SOC_SSP_H + +/* pxa DAI SSP IDs */ +#define PXA2XX_DAI_SSP1 0 +#define PXA2XX_DAI_SSP2 1 +#define PXA2XX_DAI_SSP3 2 +#define PXA2XX_DAI_SSP4 3 + +/* SSP clock sources */ +#define PXA2XX_SSP_CLK_PLL 0 +#define PXA2XX_SSP_CLK_EXT 1 +#define PXA2XX_SSP_CLK_NET 2 +#define PXA2XX_SSP_CLK_AUDIO 3 +#define PXA2XX_SSP_CLK_NET_PLL 4 + +/* SSP audio dividers */ +#define PXA2XX_SSP_AUDIO_DIV_ACDS 0 +#define PXA2XX_SSP_AUDIO_DIV_SCDB 1 +#define PXA2XX_SSP_DIV_SCR 2 + +/* SSP ACDS audio dividers values */ +#define PXA2XX_SSP_CLK_AUDIO_DIV_1 0 +#define PXA2XX_SSP_CLK_AUDIO_DIV_2 1 +#define PXA2XX_SSP_CLK_AUDIO_DIV_4 2 +#define PXA2XX_SSP_CLK_AUDIO_DIV_8 3 +#define PXA2XX_SSP_CLK_AUDIO_DIV_16 4 +#define PXA2XX_SSP_CLK_AUDIO_DIV_32 5 + +/* SSP divider bypass */ +#define PXA2XX_SSP_CLK_SCDB_4 0 +#define PXA2XX_SSP_CLK_SCDB_1 1 +#define PXA2XX_SSP_CLK_SCDB_8 2 + +#define PXA2XX_SSP_PLL_OUT 0 + +extern struct snd_soc_dai pxa2xx_ssp_dai[]; + +#endif /* __PXA2XX_SOC_SSP_H */ diff --git a/sound/soc/pxa/raumfeld.c b/sound/soc/pxa/raumfeld.c index 7e3f416..e66dc12 100644 --- a/sound/soc/pxa/raumfeld.c +++ b/sound/soc/pxa/raumfeld.c @@ -29,7 +29,7 @@ #include "../codecs/cs4270.h" #include "../codecs/ak4104.h" #include "pxa2xx-pcm.h" -#include "pxa-ssp.h" +#include "pxa2xx-ssp.h" #define GPIO_SPDIF_RESET (38) #define GPIO_MCLK_RESET (111) @@ -138,11 +138,11 @@ static int raumfeld_cs4270_hw_params(struct snd_pcm_substream *substream, if (ret < 0) return ret; - ret = snd_soc_dai_set_clkdiv(cpu_dai, PXA_SSP_DIV_SCR, 4); + ret = snd_soc_dai_set_clkdiv(cpu_dai, PXA2XX_SSP_DIV_SCR, 4); if (ret < 0) return ret; - ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_EXT, clk, 1); + ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_SSP_CLK_EXT, clk, 1); if (ret < 0) return ret; @@ -170,7 +170,7 @@ static int raumfeld_line_resume(struct platform_device *pdev) static struct snd_soc_dai_link raumfeld_line_dai = { .name = "CS4270", .stream_name = "CS4270", - .cpu_dai = &pxa_ssp_dai[PXA_DAI_SSP1], + .cpu_dai = &pxa2xx_ssp_dai[PXA2XX_DAI_SSP1], .codec_dai = &cs4270_dai, .ops = &raumfeld_cs4270_ops, }; @@ -232,11 +232,11 @@ static int raumfeld_ak4104_hw_params(struct snd_pcm_substream *substream, if (ret < 0) return ret; - ret = snd_soc_dai_set_clkdiv(cpu_dai, PXA_SSP_DIV_SCR, 4); + ret = snd_soc_dai_set_clkdiv(cpu_dai, PXA2XX_SSP_DIV_SCR, 4); if (ret < 0) return ret; - ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_EXT, clk, 1); + ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_SSP_CLK_EXT, clk, 1); if (ret < 0) return ret; @@ -250,7 +250,7 @@ static struct snd_soc_ops raumfeld_ak4104_ops = { static struct snd_soc_dai_link raumfeld_spdif_dai = { .name = "ak4104", .stream_name = "Playback", - .cpu_dai = &pxa_ssp_dai[PXA_DAI_SSP2], + .cpu_dai = &pxa2xx_ssp_dai[PXA2XX_DAI_SSP2], .codec_dai = &ak4104_dai, .ops = &raumfeld_ak4104_ops, }; diff --git a/sound/soc/pxa/zylonite.c b/sound/soc/pxa/zylonite.c index dd678ae..c65e0db 100644 --- a/sound/soc/pxa/zylonite.c +++ b/sound/soc/pxa/zylonite.c @@ -25,7 +25,7 @@ #include "../codecs/wm9713.h" #include "pxa2xx-pcm.h" #include "pxa2xx-ac97.h" -#include "pxa-ssp.h" +#include "pxa2xx-ssp.h" /* * There is a physical switch SW15 on the board which changes the MCLK @@ -125,7 +125,7 @@ static int zylonite_voice_hw_params(struct snd_pcm_substream *substream, /* Add 1 to the width for the leading clock cycle */ pll_out = rate * (width + 1) * 8; - ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_AUDIO, 0, 1); + ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_SSP_CLK_AUDIO, 0, 1); if (ret < 0) return ret; @@ -176,7 +176,7 @@ static struct snd_soc_dai_link zylonite_dai[] = { { .name = "WM9713 Voice", .stream_name = "WM9713 Voice", - .cpu_dai = &pxa_ssp_dai[PXA_DAI_SSP3], + .cpu_dai = &pxa2xx_ssp_dai[PXA2XX_DAI_SSP3], .codec_dai = &wm9713_dai[WM9713_DAI_PCM_VOICE], .ops = &zylonite_voice_ops, }, -- 1.5.6.5
From 0539606a09ecc94f45dcf4c793ba46c1f10738b1 Mon Sep 17 00:00:00 2001 From: Haojian Zhuang <haojian.zhuang@xxxxxxxxxxx> Date: Wed, 17 Mar 2010 17:31:04 -0400 Subject: [PATCH] ASoC: support pxa168 ssp in ASoC Support pxa168 ssp in ASoC. The SSP bit clock mechanism is totally different from pxa2xx series. Signed-off-by: Haojian Zhuang <haojian.zhuang@xxxxxxxxxxx> --- arch/arm/mach-mmp/include/mach/regs-mpmu.h | 48 ++++++ sound/soc/pxa/Kconfig | 6 +- sound/soc/pxa/Makefile | 2 + sound/soc/pxa/pxa-ssp.h | 1 + sound/soc/pxa/pxa168-ssp.c | 233 ++++++++++++++++++++++++++++ sound/soc/pxa/pxa168-ssp.h | 27 ++++ 6 files changed, 316 insertions(+), 1 deletions(-) create mode 100644 arch/arm/mach-mmp/include/mach/regs-mpmu.h create mode 100644 sound/soc/pxa/pxa168-ssp.c create mode 100644 sound/soc/pxa/pxa168-ssp.h diff --git a/arch/arm/mach-mmp/include/mach/regs-mpmu.h b/arch/arm/mach-mmp/include/mach/regs-mpmu.h new file mode 100644 index 0000000..0d57236 --- /dev/null +++ b/arch/arm/mach-mmp/include/mach/regs-mpmu.h @@ -0,0 +1,48 @@ +/* + * linux/arch/arm/mach-mmp/include/mach/regs-mpmu.h + * + * Main Power Management Unit + * + * 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. + */ + +#ifndef __ASM_MACH_REGS_MPMU_H +#define __ASM_MACH_REGS_MPMU_H + +#include <mach/addr-map.h> + +#define MPMU_VIRT_BASE (APB_VIRT_BASE + 0x50000) +#define MPMU_REG(off) (MPMU_VIRT_BASE + (off)) + +#define MPMU_CPCR MPMU_REG(0x0000) +#define MPMU_FCCR MPMU_REG(0x0008) +#define MPMU_POCR MPMU_REG(0x000c) +#define MPMU_POSR MPMU_REG(0x0010) +#define MPMU_SUCCR MPMU_REG(0x0014) +#define MPMU_VRCR MPMU_REG(0x0018) +#define MPMU_OHCR MPMU_REG(0x001c) +#define MPMU_GPCR MPMU_REG(0x0030) +#define MPMU_PLL2CR MPMU_REG(0x0034) +#define MPMU_SCCR MPMU_REG(0x0038) +#define MPMU_CWUCRM MPMU_REG(0x004c) +#define MPMU_PLL1_REG1 MPMU_REG(0x0050) +#define MPMU_PLL1_REG2 MPMU_REG(0x0054) +#define MPMU_PLL1_SSC MPMU_REG(0x0058) +#define MPMU_PLL2_REG1 MPMU_REG(0x0060) +#define MPMU_PLL2_REG2 MPMU_REG(0x0064) +#define MPMU_PLL2_SSC MPMU_REG(0x0068) +#define MPMU_TS MPMU_REG(0x0080) +#define MPMU_WDTPCR MPMU_REG(0x0200) +#define MPMU_APCR MPMU_REG(0x1000) +#define MPMU_APSR MPMU_REG(0x1004) +#define MPMU_APRR MPMU_REG(0x1020) +#define MPMU_ACGR MPMU_REG(0x1024) +#define MPMU_ARSR MPMU_REG(0x1028) +#define MPMU_AWUCRS MPMU_REG(0x1048) +#define MPMU_AWUCRM MPMU_REG(0x104c) +#define MPMU_ASYSDR MPMU_REG(0x1050) +#define MPMU_ASSPDR MPMU_REG(0x1054) + +#endif /* __ASM_MACH_REGS_APMU_H */ diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig index 7be1d5f..286d52a 100644 --- a/sound/soc/pxa/Kconfig +++ b/sound/soc/pxa/Kconfig @@ -1,6 +1,6 @@ config SND_PXA2XX_SOC tristate "SoC Audio for the Intel PXA2xx chip" - depends on ARCH_PXA + depends on ARCH_PXA || ARCH_MMP select SND_PXA2XX_LIB help Say Y or M if you want to add support for codecs attached to @@ -25,6 +25,10 @@ config SND_PXA2XX_SOC_SSP tristate select PXA_SSP +config SND_PXA168_SOC_SSP + tristate + select PXA_SSP + config SND_PXA2XX_SOC_CORGI tristate "SoC Audio support for Sharp Zaurus SL-C7x0" depends on SND_PXA2XX_SOC && PXA_SHARP_C7xx diff --git a/sound/soc/pxa/Makefile b/sound/soc/pxa/Makefile index 33c1579..a74e6c9 100644 --- a/sound/soc/pxa/Makefile +++ b/sound/soc/pxa/Makefile @@ -3,11 +3,13 @@ snd-soc-pxa2xx-objs := pxa2xx-pcm.o snd-soc-pxa2xx-ac97-objs := pxa2xx-ac97.o snd-soc-pxa2xx-i2s-objs := pxa2xx-i2s.o snd-soc-pxa2xx-ssp-objs := pxa-ssp.o pxa2xx-ssp.o +snd-soc-pxa168-ssp-objs := pxa-ssp.o pxa168-ssp.o obj-$(CONFIG_SND_PXA2XX_SOC) += snd-soc-pxa2xx.o obj-$(CONFIG_SND_PXA2XX_SOC_AC97) += snd-soc-pxa2xx-ac97.o obj-$(CONFIG_SND_PXA2XX_SOC_I2S) += snd-soc-pxa2xx-i2s.o obj-$(CONFIG_SND_PXA2XX_SOC_SSP) += snd-soc-pxa2xx-ssp.o +obj-$(CONFIG_SND_PXA168_SOC_SSP) += snd-soc-pxa168-ssp.o # PXA Machine Support snd-soc-corgi-objs := corgi.o diff --git a/sound/soc/pxa/pxa-ssp.h b/sound/soc/pxa/pxa-ssp.h index af4a09b..852660a 100644 --- a/sound/soc/pxa/pxa-ssp.h +++ b/sound/soc/pxa/pxa-ssp.h @@ -15,6 +15,7 @@ struct ssp_priv { struct ssp_device *ssp; unsigned int sysclk; + unsigned int sysclk_idx; int dai_fmt; #ifdef CONFIG_PM uint32_t cr0; diff --git a/sound/soc/pxa/pxa168-ssp.c b/sound/soc/pxa/pxa168-ssp.c new file mode 100644 index 0000000..487e899 --- /dev/null +++ b/sound/soc/pxa/pxa168-ssp.c @@ -0,0 +1,233 @@ +/* + * pxa168-ssp.c -- ALSA Soc Audio Layer + * + * Copyright 2009-2010 Marvell International Ltd. + * Haojian Zhuang <haojian.zhuang@xxxxxxxxxxx> + * + * 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 + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/clk.h> + +#include <sound/core.h> +#include <sound/soc.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/pxa2xx-lib.h> + +#include <mach/hardware.h> +#include <mach/dma.h> +#include <mach/regs-apbc.h> +#include <mach/regs-apmu.h> +#include <mach/regs-mpmu.h> +#include <plat/ssp.h> + +#include "pxa2xx-pcm.h" +#include "pxa168-ssp.h" +#include "pxa-ssp.h" + +struct pxa168_ssp_mclk { + unsigned int rate; + unsigned int format; + unsigned int channel; + unsigned int mclk; + unsigned int mclk_denom; + unsigned int mclk_num; + unsigned int bclk; + unsigned int bclk_denom; + unsigned int bclk_num; +}; + +/* + * This table is used while CPU is clock master. + * MCLK = 312MHz * ASYSCLK_DENOM / ASYSCLK_NUM + * BCLK = MCLK * SSPSCLK_DENOM / SSPSCLK_NUM + */ +static const struct pxa168_ssp_mclk mclk_conf[] = { + /* rate, fmt, chn, mclk, den, num, bclk, den, num */ + {96000, 16, 2, 12288000, 64, 1625, 6144000, 1, 2}, + {96000, 16, 1, 12288000, 64, 1625, 1536000, 1, 8}, + {88200, 16, 2, 11289600, 294, 8125, 5644800, 1, 2}, + {88200, 16, 1, 11289600, 294, 8125, 1411200, 1, 8}, + {48000, 16, 2, 12288000, 64, 1625, 3072000, 1, 4}, + {48000, 16, 1, 12288000, 64, 1625, 768000, 1, 16}, + {44100, 16, 2, 11289600, 294, 8125, 2822400, 1, 4}, + {44100, 16, 1, 11289600, 294, 8125, 705600, 1, 16}, + {32000, 16, 2, 12288000, 64, 1625, 2048000, 1, 6}, + {32000, 16, 1, 12288000, 64, 1625, 512000, 1, 24}, + {22050, 16, 2, 11289600, 294, 8125, 1411200, 1, 8}, + {22050, 16, 1, 11289600, 294, 8125, 352800, 1, 32}, + {16000, 16, 2, 12288000, 64, 1625, 1024000, 1, 12}, + {16000, 16, 1, 12288000, 64, 1625, 256000, 1, 48}, + {11025, 16, 2, 11289600, 294, 8125, 705600, 1, 16}, + {11025, 16, 1, 11289600, 294, 8125, 176400, 1, 64}, + { 8000, 16, 2, 12288000, 64, 1625, 512000, 1, 24}, + { 8000, 16, 1, 12288000, 64, 1625, 128000, 1, 96}, +}; + +int pxa168_query_mclk(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + 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; + unsigned int rate, width, channel; + int i, ret = -EINVAL; + + rate = params_rate(params); + width = snd_pcm_format_physical_width(params_format(params)); + channel = params_channels(params); + + for (i = 0; i < ARRAY_SIZE(mclk_conf); i++) { + if ((mclk_conf[i].rate == rate) + && (mclk_conf[i].format == width) + && (mclk_conf[i].channel == channel)) { + /* save both mclk and mclk index into ssp_priv */ + priv->sysclk = mclk_conf[i].mclk; + priv->sysclk_idx = i; + ret = priv->sysclk; + break; + } + } + return ret; +} +EXPORT_SYMBOL_GPL(pxa168_query_mclk); + +/* + * Set the SSP ports SYSCLK only from Audio SYSCLK. + */ +static int pxa168_ssp_set_dai_sysclk(struct snd_soc_dai *cpu_dai, int clk_id, + unsigned int freq, int dir) +{ + struct ssp_priv *priv = cpu_dai->private_data; + struct ssp_device *ssp = priv->ssp; + unsigned int sscr0, data, asysdr, asspdr; + + dev_dbg(&ssp->pdev->dev, "%s id: %d, clk_id %d, freq %u\n", + __func__, cpu_dai->id, clk_id, freq); + + /* freq is the index of mclk_conf table */ + if ((freq < 0) || (priv->sysclk_idx >= ARRAY_SIZE(mclk_conf))) { + dev_warn(&ssp->pdev->dev, "Wrong frequency on SYSCLK\n"); + return -EINVAL; + } + asysdr = (mclk_conf[priv->sysclk_idx].mclk_num << 16) + | mclk_conf[priv->sysclk_idx].mclk_denom; + asspdr = 0; + /* If ASYSCLK is supplied by pxa168, ASSPDR should be configured. */ + if (dir == SND_SOC_CLOCK_OUT) + asspdr = (mclk_conf[priv->sysclk_idx].bclk_num << 16) + | mclk_conf[priv->sysclk_idx].bclk_denom; + + pxa_ssp_disable(ssp); + clk_disable(ssp->clk); /* SSP port internal clock */ + + /* clear ECS, NCS, MOD, ACS */ + sscr0 = pxa_ssp_read_reg(ssp, SSCR0); + data = sscr0 & ~(SSCR0_ECS | SSCR0_NCS | SSCR0_MOD | SSCR0_ACS); + if (sscr0 != data) + pxa_ssp_write_reg(ssp, SSCR0, data); + + /* update divider register in MPMU */ + __raw_writel(asysdr, MPMU_ASYSDR); + __raw_writel(asspdr, MPMU_ASSPDR); + + clk_enable(ssp->clk); /* SSP port internal clock */ + pxa_ssp_enable(ssp); + return 0; +} + +static int pxa168_ssp_hw_free(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; + + pxa_ssp_disable(ssp); + /* update divider register in MPMU */ + __raw_writel(0, MPMU_ASYSDR); + __raw_writel(0, MPMU_ASSPDR); + return 0; +} + +static struct snd_soc_dai_ops pxa168_ssp_dai_ops = { + .hw_free = pxa168_ssp_hw_free, + .set_sysclk = pxa168_ssp_set_dai_sysclk, +}; + +#define PXA168_SSP_RATES SNDRV_PCM_RATE_8000_96000 +#define PXA168_SSP_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +#define PXA168_SSP_DAI(_id) \ +{ \ + .name = "pxa168-ssp", \ + .id = _id, \ + .playback = { \ + .channels_min = 1, \ + .channels_max = 2, \ + .rates = PXA168_SSP_RATES, \ + .formats = PXA168_SSP_FORMATS, \ + }, \ + .capture = { \ + .channels_min = 1, \ + .channels_max = 2, \ + .rates = PXA168_SSP_RATES, \ + .formats = PXA168_SSP_FORMATS, \ + }, \ + .ops = &pxa168_ssp_dai_ops, \ +} + +struct snd_soc_dai pxa168_ssp_dai[] = { + PXA168_SSP_DAI(PXA168_DAI_SSP1), + PXA168_SSP_DAI(PXA168_DAI_SSP2), + PXA168_SSP_DAI(PXA168_DAI_SSP3), + PXA168_SSP_DAI(PXA168_DAI_SSP4), + PXA168_SSP_DAI(PXA168_DAI_SSP5), +}; +EXPORT_SYMBOL_GPL(pxa168_ssp_dai); + +static int __init pxa168_ssp_init(void) +{ + struct snd_soc_dai *dai; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(pxa168_ssp_dai); i++) { + dai = &pxa168_ssp_dai[i]; + ret = pxa_ssp_register_dai(dai); + if (ret) + return ret; + } + return ret; +} +module_init(pxa168_ssp_init); + +static void __exit pxa168_ssp_exit(void) +{ + struct snd_soc_dai *dai = NULL; + int i; + + for (i = 0; i < ARRAY_SIZE(pxa168_ssp_dai); i++) { + dai = &pxa168_ssp_dai[i]; + snd_soc_unregister_dai(dai); + } +} +module_exit(pxa168_ssp_exit); + +/* Module information */ +MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@xxxxxxxxxxx>"); +MODULE_DESCRIPTION("PXA168 SSP SoC Interface"); +MODULE_LICENSE("GPL"); + diff --git a/sound/soc/pxa/pxa168-ssp.h b/sound/soc/pxa/pxa168-ssp.h new file mode 100644 index 0000000..c7e23c7 --- /dev/null +++ b/sound/soc/pxa/pxa168-ssp.h @@ -0,0 +1,27 @@ +/* + * ASoC PXA168 SSP port support + * + * 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. + */ + +#ifndef _PXA168_SSP_H +#define _PXA168_SSP_H + +/* pxa DAI SSP IDs */ +#define PXA168_DAI_SSP1 0 +#define PXA168_DAI_SSP2 1 +#define PXA168_DAI_SSP3 2 +#define PXA168_DAI_SSP4 3 +#define PXA168_DAI_SSP5 4 + +/* PXA168 SSP SYSCLK source */ +#define PXA168_ASYSCLK_MASTER 0 /* ASYSCLK master -- pxa168 */ +#define PXA168_ASYSCLK_SLAVE 1 /* ASYSCLK slave -- pxa168 */ + +extern struct snd_soc_dai pxa168_ssp_dai[]; + +extern int pxa168_query_mclk(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params); +#endif -- 1.5.6.5
From 5d94b20b89185ae5a69c7ba5d1e998d568064def Mon Sep 17 00:00:00 2001 From: Haojian Zhuang <haojian.zhuang@xxxxxxxxxxx> Date: Fri, 19 Mar 2010 18:13:32 -0400 Subject: [PATCH] ASoC: enable wm8753 in aspenite Enable wm8753 codec in aspenite via ssp interface. Signed-off-by: Haojian Zhuang <haojian.zhuang@xxxxxxxxxxx> --- sound/soc/pxa/Kconfig | 9 ++ sound/soc/pxa/Makefile | 2 + sound/soc/pxa/aspenite.c | 190 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 201 insertions(+), 0 deletions(-) create mode 100644 sound/soc/pxa/aspenite.c diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig index 286d52a..0f10ee2 100644 --- a/sound/soc/pxa/Kconfig +++ b/sound/soc/pxa/Kconfig @@ -113,6 +113,15 @@ config SND_PXA2XX_SOC_PALM27X Say Y if you want to add support for SoC audio on Palm T|X, T5, E2 or LifeDrive handheld computer. +config SND_SOC_ASPENITE + tristate "SoC Audio support for Marvell Aspenite" + depends on SND_PXA2XX_SOC && MACH_ASPENITE + select SND_PXA168_SOC_SSP + select SND_SOC_WM8753 + help + Say Y if you want to add support for SoC audio on the + Marvell Aspenite reference platform. + config SND_SOC_ZYLONITE tristate "SoC Audio support for Marvell Zylonite" depends on SND_PXA2XX_SOC && MACH_ZYLONITE diff --git a/sound/soc/pxa/Makefile b/sound/soc/pxa/Makefile index a74e6c9..74054aa 100644 --- a/sound/soc/pxa/Makefile +++ b/sound/soc/pxa/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_SND_PXA2XX_SOC_SSP) += snd-soc-pxa2xx-ssp.o obj-$(CONFIG_SND_PXA168_SOC_SSP) += snd-soc-pxa168-ssp.o # PXA Machine Support +snd-soc-aspenite-objs := aspenite.o snd-soc-corgi-objs := corgi.o snd-soc-poodle-objs := poodle.o snd-soc-tosa-objs := tosa.o @@ -38,6 +39,7 @@ obj-$(CONFIG_SND_PXA2XX_SOC_EM_X270) += snd-soc-em-x270.o obj-$(CONFIG_SND_PXA2XX_SOC_PALM27X) += snd-soc-palm27x.o obj-$(CONFIG_SND_PXA2XX_SOC_MAGICIAN) += snd-soc-magician.o obj-$(CONFIG_SND_PXA2XX_SOC_MIOA701) += snd-soc-mioa701.o +obj-$(CONFIG_SND_SOC_ASPENITE) += snd-soc-aspenite.o obj-$(CONFIG_SND_SOC_ZYLONITE) += snd-soc-zylonite.o obj-$(CONFIG_SND_PXA2XX_SOC_IMOTE2) += snd-soc-imote2.o obj-$(CONFIG_SND_SOC_RAUMFELD) += snd-soc-raumfeld.o diff --git a/sound/soc/pxa/aspenite.c b/sound/soc/pxa/aspenite.c new file mode 100644 index 0000000..f82acdc --- /dev/null +++ b/sound/soc/pxa/aspenite.c @@ -0,0 +1,190 @@ +/* + * aspenite.c -- SoC audio for Aspenite + * + * Copyright (C) 2009-2010 Marvell International Ltd. + * Haojian Zhuang <haojian.zhuang@xxxxxxxxxxx> + * + * 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/kernel.h> +#include <linux/module.h> +#include <linux/device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> + +#include <plat/ssp.h> + +#include "../codecs/wm8753.h" +#include "pxa2xx-pcm.h" +#include "pxa168-ssp.h" + +static struct snd_soc_card aspenite; + +/* aspenite machine dapm widgets */ +static const struct snd_soc_dapm_widget aspenite_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_SPK("Headset Speaker L", NULL), + SND_SOC_DAPM_SPK("Headset Speaker R", NULL), + SND_SOC_DAPM_LINE("Line LIN", NULL), + SND_SOC_DAPM_LINE("Line RIN", NULL), +}; + +/* aspenite machine audio map */ +static const struct snd_soc_dapm_route audio_map[] = { + /* Headphone connected to LOUT1/ROUT1 */ + {"Headphone", NULL, "LOUT1"}, + {"Headphone", NULL, "ROUT1"}, + + /* Speaker connected to LOUT2/OUT4 & OUT3/ROUT2 */ + {"Headset Speaker L", NULL, "LOUT2"}, + {"Headset Speaker L", NULL, "OUT4"}, + {"Headset Speaker R", NULL, "OUT3"}, + {"Headset Speaker R", NULL, "ROUT2"}, + + /* Line connected to LINE1/LINE2 */ + {"Line LIN", NULL, "LINE1"}, + {"Line RIN", NULL, "LINE2"}, + + /* Mic */ + {"MIC1", NULL, "Mic Bias"}, + {"Mic Bias", NULL, "Headset Mic"}, + + /* Connect the ALC pins */ + {"ACIN", NULL, "ACOP"}, +}; + +static const struct snd_kcontrol_new wm8753_aspenite_controls[] = { + SOC_DAPM_PIN_SWITCH("Headset Speaker L"), + SOC_DAPM_PIN_SWITCH("Headset Speaker R"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Line LIN"), + SOC_DAPM_PIN_SWITCH("Line RIN"), +}; + +static int aspenite_hifi_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; + int mclk, ret; + + mclk = pxa168_query_mclk(substream, params); + if (mclk < 0) + return mclk; + + /* set codec DAI configuration */ + ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S + | SND_SOC_DAIFMT_NB_NF + | SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) + return ret; + + /* set cpu DAI configuration */ + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S + | SND_SOC_DAIFMT_NB_NF + | SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) + return ret; + + /* set the codec system clock for DAC and ADC */ + ret = snd_soc_dai_set_sysclk(codec_dai, WM8753_MCLK, mclk, + SND_SOC_CLOCK_IN); + if (ret < 0) + return ret; + + /* set cpu system clock */ + ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk, + SND_SOC_CLOCK_OUT); + if (ret < 0) + return ret; + + return 0; +} + +static struct snd_soc_ops aspenite_hifi_ops = { + .hw_params = aspenite_hifi_hw_params, +}; + +static int aspenite_wm8753_init(struct snd_soc_codec *codec) +{ + int ret; + + /* set up NC codec pins */ + snd_soc_dapm_nc_pin(codec, "MONO1"); + snd_soc_dapm_nc_pin(codec, "MONO2"); + snd_soc_dapm_nc_pin(codec, "RXP"); + snd_soc_dapm_nc_pin(codec, "RXN"); + + snd_soc_dapm_new_controls(codec, aspenite_dapm_widgets, + ARRAY_SIZE(aspenite_dapm_widgets)); + ret = snd_soc_add_controls(codec, wm8753_aspenite_controls, + ARRAY_SIZE(wm8753_aspenite_controls)); + if (ret < 0) + return ret; + + snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + + return 0; +} + +static struct snd_soc_dai_link aspenite_dai[] = { + { + .name = "WM8753 HiFi", + .stream_name = "WM8753 HiFi", + .cpu_dai = &pxa168_ssp_dai[PXA168_DAI_SSP1], + .codec_dai = &wm8753_dai[0], + .init = aspenite_wm8753_init, + .ops = &aspenite_hifi_ops, + }, +}; + +static struct snd_soc_card aspenite = { + .name = "Aspenite", + .platform = &pxa2xx_soc_platform, + .dai_link = aspenite_dai, + .num_links = ARRAY_SIZE(aspenite_dai), +}; + +static struct snd_soc_device aspenite_snd_devdata = { + .card = &aspenite, + .codec_dev = &soc_codec_dev_wm8753, +}; + +static struct platform_device *aspenite_snd_device; + +static int __init aspenite_init(void) +{ + int ret; + + aspenite_snd_device = platform_device_alloc("soc-audio", -1); + if (!aspenite_snd_device) + return -ENOMEM; + + platform_set_drvdata(aspenite_snd_device, &aspenite_snd_devdata); + aspenite_snd_devdata.dev = &aspenite_snd_device->dev; + + ret = platform_device_add(aspenite_snd_device); + if (ret) + platform_device_put(aspenite_snd_device); + return ret; +} +module_init(aspenite_init); + +static void __exit aspenite_exit(void) +{ + platform_device_unregister(aspenite_snd_device); +} +module_exit(aspenite_exit); + +MODULE_DESCRIPTION("ALSA SoC WM8753 Aspenite"); +MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@xxxxxxxxxxx>"); +MODULE_LICENSE("GPL"); -- 1.5.6.5
_______________________________________________ Alsa-devel mailing list Alsa-devel@xxxxxxxxxxxxxxxx http://mailman.alsa-project.org/mailman/listinfo/alsa-devel