162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * pxa-ssp.c -- ALSA Soc Audio Layer 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2005,2008 Wolfson Microelectronics PLC. 662306a36Sopenharmony_ci * Author: Liam Girdwood 762306a36Sopenharmony_ci * Mark Brown <broonie@opensource.wolfsonmicro.com> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * TODO: 1062306a36Sopenharmony_ci * o Test network mode for > 16bit sample size 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/init.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci#include <linux/platform_device.h> 1762306a36Sopenharmony_ci#include <linux/clk.h> 1862306a36Sopenharmony_ci#include <linux/io.h> 1962306a36Sopenharmony_ci#include <linux/pxa2xx_ssp.h> 2062306a36Sopenharmony_ci#include <linux/of.h> 2162306a36Sopenharmony_ci#include <linux/dmaengine.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include <asm/irq.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include <sound/core.h> 2662306a36Sopenharmony_ci#include <sound/pcm.h> 2762306a36Sopenharmony_ci#include <sound/initval.h> 2862306a36Sopenharmony_ci#include <sound/pcm_params.h> 2962306a36Sopenharmony_ci#include <sound/soc.h> 3062306a36Sopenharmony_ci#include <sound/pxa2xx-lib.h> 3162306a36Sopenharmony_ci#include <sound/dmaengine_pcm.h> 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#include "pxa-ssp.h" 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* 3662306a36Sopenharmony_ci * SSP audio private data 3762306a36Sopenharmony_ci */ 3862306a36Sopenharmony_cistruct ssp_priv { 3962306a36Sopenharmony_ci struct ssp_device *ssp; 4062306a36Sopenharmony_ci struct clk *extclk; 4162306a36Sopenharmony_ci unsigned long ssp_clk; 4262306a36Sopenharmony_ci unsigned int sysclk; 4362306a36Sopenharmony_ci unsigned int dai_fmt; 4462306a36Sopenharmony_ci unsigned int configured_dai_fmt; 4562306a36Sopenharmony_ci#ifdef CONFIG_PM 4662306a36Sopenharmony_ci uint32_t cr0; 4762306a36Sopenharmony_ci uint32_t cr1; 4862306a36Sopenharmony_ci uint32_t to; 4962306a36Sopenharmony_ci uint32_t psp; 5062306a36Sopenharmony_ci#endif 5162306a36Sopenharmony_ci}; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic void dump_registers(struct ssp_device *ssp) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci dev_dbg(ssp->dev, "SSCR0 0x%08x SSCR1 0x%08x SSTO 0x%08x\n", 5662306a36Sopenharmony_ci pxa_ssp_read_reg(ssp, SSCR0), pxa_ssp_read_reg(ssp, SSCR1), 5762306a36Sopenharmony_ci pxa_ssp_read_reg(ssp, SSTO)); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci dev_dbg(ssp->dev, "SSPSP 0x%08x SSSR 0x%08x SSACD 0x%08x\n", 6062306a36Sopenharmony_ci pxa_ssp_read_reg(ssp, SSPSP), pxa_ssp_read_reg(ssp, SSSR), 6162306a36Sopenharmony_ci pxa_ssp_read_reg(ssp, SSACD)); 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic void pxa_ssp_set_dma_params(struct ssp_device *ssp, int width4, 6562306a36Sopenharmony_ci int out, struct snd_dmaengine_dai_dma_data *dma) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci dma->addr_width = width4 ? DMA_SLAVE_BUSWIDTH_4_BYTES : 6862306a36Sopenharmony_ci DMA_SLAVE_BUSWIDTH_2_BYTES; 6962306a36Sopenharmony_ci dma->maxburst = 16; 7062306a36Sopenharmony_ci dma->addr = ssp->phys_base + SSDR; 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic int pxa_ssp_startup(struct snd_pcm_substream *substream, 7462306a36Sopenharmony_ci struct snd_soc_dai *cpu_dai) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); 7762306a36Sopenharmony_ci struct ssp_device *ssp = priv->ssp; 7862306a36Sopenharmony_ci struct snd_dmaengine_dai_dma_data *dma; 7962306a36Sopenharmony_ci int ret = 0; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci if (!snd_soc_dai_active(cpu_dai)) { 8262306a36Sopenharmony_ci clk_prepare_enable(ssp->clk); 8362306a36Sopenharmony_ci pxa_ssp_disable(ssp); 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci clk_prepare_enable(priv->extclk); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci dma = kzalloc(sizeof(struct snd_dmaengine_dai_dma_data), GFP_KERNEL); 8962306a36Sopenharmony_ci if (!dma) 9062306a36Sopenharmony_ci return -ENOMEM; 9162306a36Sopenharmony_ci dma->chan_name = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 9262306a36Sopenharmony_ci "tx" : "rx"; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci snd_soc_dai_set_dma_data(cpu_dai, substream, dma); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci return ret; 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic void pxa_ssp_shutdown(struct snd_pcm_substream *substream, 10062306a36Sopenharmony_ci struct snd_soc_dai *cpu_dai) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); 10362306a36Sopenharmony_ci struct ssp_device *ssp = priv->ssp; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (!snd_soc_dai_active(cpu_dai)) { 10662306a36Sopenharmony_ci pxa_ssp_disable(ssp); 10762306a36Sopenharmony_ci clk_disable_unprepare(ssp->clk); 10862306a36Sopenharmony_ci } 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci clk_disable_unprepare(priv->extclk); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci kfree(snd_soc_dai_get_dma_data(cpu_dai, substream)); 11362306a36Sopenharmony_ci snd_soc_dai_set_dma_data(cpu_dai, substream, NULL); 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci#ifdef CONFIG_PM 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic int pxa_ssp_suspend(struct snd_soc_component *component) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci struct ssp_priv *priv = snd_soc_component_get_drvdata(component); 12162306a36Sopenharmony_ci struct ssp_device *ssp = priv->ssp; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (!snd_soc_component_active(component)) 12462306a36Sopenharmony_ci clk_prepare_enable(ssp->clk); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci priv->cr0 = __raw_readl(ssp->mmio_base + SSCR0); 12762306a36Sopenharmony_ci priv->cr1 = __raw_readl(ssp->mmio_base + SSCR1); 12862306a36Sopenharmony_ci priv->to = __raw_readl(ssp->mmio_base + SSTO); 12962306a36Sopenharmony_ci priv->psp = __raw_readl(ssp->mmio_base + SSPSP); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci pxa_ssp_disable(ssp); 13262306a36Sopenharmony_ci clk_disable_unprepare(ssp->clk); 13362306a36Sopenharmony_ci return 0; 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic int pxa_ssp_resume(struct snd_soc_component *component) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci struct ssp_priv *priv = snd_soc_component_get_drvdata(component); 13962306a36Sopenharmony_ci struct ssp_device *ssp = priv->ssp; 14062306a36Sopenharmony_ci uint32_t sssr = SSSR_ROR | SSSR_TUR | SSSR_BCE; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci clk_prepare_enable(ssp->clk); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci __raw_writel(sssr, ssp->mmio_base + SSSR); 14562306a36Sopenharmony_ci __raw_writel(priv->cr0 & ~SSCR0_SSE, ssp->mmio_base + SSCR0); 14662306a36Sopenharmony_ci __raw_writel(priv->cr1, ssp->mmio_base + SSCR1); 14762306a36Sopenharmony_ci __raw_writel(priv->to, ssp->mmio_base + SSTO); 14862306a36Sopenharmony_ci __raw_writel(priv->psp, ssp->mmio_base + SSPSP); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci if (snd_soc_component_active(component)) 15162306a36Sopenharmony_ci pxa_ssp_enable(ssp); 15262306a36Sopenharmony_ci else 15362306a36Sopenharmony_ci clk_disable_unprepare(ssp->clk); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci return 0; 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci#else 15962306a36Sopenharmony_ci#define pxa_ssp_suspend NULL 16062306a36Sopenharmony_ci#define pxa_ssp_resume NULL 16162306a36Sopenharmony_ci#endif 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci/* 16462306a36Sopenharmony_ci * ssp_set_clkdiv - set SSP clock divider 16562306a36Sopenharmony_ci * @div: serial clock rate divider 16662306a36Sopenharmony_ci */ 16762306a36Sopenharmony_cistatic void pxa_ssp_set_scr(struct ssp_device *ssp, u32 div) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci u32 sscr0 = pxa_ssp_read_reg(ssp, SSCR0); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci if (ssp->type == PXA25x_SSP) { 17262306a36Sopenharmony_ci sscr0 &= ~0x0000ff00; 17362306a36Sopenharmony_ci sscr0 |= ((div - 2)/2) << 8; /* 2..512 */ 17462306a36Sopenharmony_ci } else { 17562306a36Sopenharmony_ci sscr0 &= ~0x000fff00; 17662306a36Sopenharmony_ci sscr0 |= (div - 1) << 8; /* 1..4096 */ 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci pxa_ssp_write_reg(ssp, SSCR0, sscr0); 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci/* 18262306a36Sopenharmony_ci * Set the SSP ports SYSCLK. 18362306a36Sopenharmony_ci */ 18462306a36Sopenharmony_cistatic int pxa_ssp_set_dai_sysclk(struct snd_soc_dai *cpu_dai, 18562306a36Sopenharmony_ci int clk_id, unsigned int freq, int dir) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); 18862306a36Sopenharmony_ci struct ssp_device *ssp = priv->ssp; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci u32 sscr0 = pxa_ssp_read_reg(ssp, SSCR0) & 19162306a36Sopenharmony_ci ~(SSCR0_ECS | SSCR0_NCS | SSCR0_MOD | SSCR0_ACS); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci if (priv->extclk) { 19462306a36Sopenharmony_ci int ret; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci /* 19762306a36Sopenharmony_ci * For DT based boards, if an extclk is given, use it 19862306a36Sopenharmony_ci * here and configure PXA_SSP_CLK_EXT. 19962306a36Sopenharmony_ci */ 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci ret = clk_set_rate(priv->extclk, freq); 20262306a36Sopenharmony_ci if (ret < 0) 20362306a36Sopenharmony_ci return ret; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci clk_id = PXA_SSP_CLK_EXT; 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci dev_dbg(ssp->dev, 20962306a36Sopenharmony_ci "pxa_ssp_set_dai_sysclk id: %d, clk_id %d, freq %u\n", 21062306a36Sopenharmony_ci cpu_dai->id, clk_id, freq); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci switch (clk_id) { 21362306a36Sopenharmony_ci case PXA_SSP_CLK_NET_PLL: 21462306a36Sopenharmony_ci sscr0 |= SSCR0_MOD; 21562306a36Sopenharmony_ci break; 21662306a36Sopenharmony_ci case PXA_SSP_CLK_PLL: 21762306a36Sopenharmony_ci /* Internal PLL is fixed */ 21862306a36Sopenharmony_ci if (ssp->type == PXA25x_SSP) 21962306a36Sopenharmony_ci priv->sysclk = 1843200; 22062306a36Sopenharmony_ci else 22162306a36Sopenharmony_ci priv->sysclk = 13000000; 22262306a36Sopenharmony_ci break; 22362306a36Sopenharmony_ci case PXA_SSP_CLK_EXT: 22462306a36Sopenharmony_ci priv->sysclk = freq; 22562306a36Sopenharmony_ci sscr0 |= SSCR0_ECS; 22662306a36Sopenharmony_ci break; 22762306a36Sopenharmony_ci case PXA_SSP_CLK_NET: 22862306a36Sopenharmony_ci priv->sysclk = freq; 22962306a36Sopenharmony_ci sscr0 |= SSCR0_NCS | SSCR0_MOD; 23062306a36Sopenharmony_ci break; 23162306a36Sopenharmony_ci case PXA_SSP_CLK_AUDIO: 23262306a36Sopenharmony_ci priv->sysclk = 0; 23362306a36Sopenharmony_ci pxa_ssp_set_scr(ssp, 1); 23462306a36Sopenharmony_ci sscr0 |= SSCR0_ACS; 23562306a36Sopenharmony_ci break; 23662306a36Sopenharmony_ci default: 23762306a36Sopenharmony_ci return -ENODEV; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci /* The SSP clock must be disabled when changing SSP clock mode 24162306a36Sopenharmony_ci * on PXA2xx. On PXA3xx it must be enabled when doing so. */ 24262306a36Sopenharmony_ci if (ssp->type != PXA3xx_SSP) 24362306a36Sopenharmony_ci clk_disable_unprepare(ssp->clk); 24462306a36Sopenharmony_ci pxa_ssp_write_reg(ssp, SSCR0, sscr0); 24562306a36Sopenharmony_ci if (ssp->type != PXA3xx_SSP) 24662306a36Sopenharmony_ci clk_prepare_enable(ssp->clk); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci return 0; 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci/* 25262306a36Sopenharmony_ci * Configure the PLL frequency pxa27x and (afaik - pxa320 only) 25362306a36Sopenharmony_ci */ 25462306a36Sopenharmony_cistatic int pxa_ssp_set_pll(struct ssp_priv *priv, unsigned int freq) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci struct ssp_device *ssp = priv->ssp; 25762306a36Sopenharmony_ci u32 ssacd = pxa_ssp_read_reg(ssp, SSACD) & ~0x70; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci if (ssp->type == PXA3xx_SSP) 26062306a36Sopenharmony_ci pxa_ssp_write_reg(ssp, SSACDD, 0); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci switch (freq) { 26362306a36Sopenharmony_ci case 5622000: 26462306a36Sopenharmony_ci break; 26562306a36Sopenharmony_ci case 11345000: 26662306a36Sopenharmony_ci ssacd |= (0x1 << 4); 26762306a36Sopenharmony_ci break; 26862306a36Sopenharmony_ci case 12235000: 26962306a36Sopenharmony_ci ssacd |= (0x2 << 4); 27062306a36Sopenharmony_ci break; 27162306a36Sopenharmony_ci case 14857000: 27262306a36Sopenharmony_ci ssacd |= (0x3 << 4); 27362306a36Sopenharmony_ci break; 27462306a36Sopenharmony_ci case 32842000: 27562306a36Sopenharmony_ci ssacd |= (0x4 << 4); 27662306a36Sopenharmony_ci break; 27762306a36Sopenharmony_ci case 48000000: 27862306a36Sopenharmony_ci ssacd |= (0x5 << 4); 27962306a36Sopenharmony_ci break; 28062306a36Sopenharmony_ci case 0: 28162306a36Sopenharmony_ci /* Disable */ 28262306a36Sopenharmony_ci break; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci default: 28562306a36Sopenharmony_ci /* PXA3xx has a clock ditherer which can be used to generate 28662306a36Sopenharmony_ci * a wider range of frequencies - calculate a value for it. 28762306a36Sopenharmony_ci */ 28862306a36Sopenharmony_ci if (ssp->type == PXA3xx_SSP) { 28962306a36Sopenharmony_ci u32 val; 29062306a36Sopenharmony_ci u64 tmp = 19968; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci tmp *= 1000000; 29362306a36Sopenharmony_ci do_div(tmp, freq); 29462306a36Sopenharmony_ci val = tmp; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci val = (val << 16) | 64; 29762306a36Sopenharmony_ci pxa_ssp_write_reg(ssp, SSACDD, val); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci ssacd |= (0x6 << 4); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci dev_dbg(ssp->dev, 30262306a36Sopenharmony_ci "Using SSACDD %x to supply %uHz\n", 30362306a36Sopenharmony_ci val, freq); 30462306a36Sopenharmony_ci break; 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci return -EINVAL; 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci pxa_ssp_write_reg(ssp, SSACD, ssacd); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci return 0; 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci/* 31662306a36Sopenharmony_ci * Set the active slots in TDM/Network mode 31762306a36Sopenharmony_ci */ 31862306a36Sopenharmony_cistatic int pxa_ssp_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, 31962306a36Sopenharmony_ci unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); 32262306a36Sopenharmony_ci struct ssp_device *ssp = priv->ssp; 32362306a36Sopenharmony_ci u32 sscr0; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci sscr0 = pxa_ssp_read_reg(ssp, SSCR0); 32662306a36Sopenharmony_ci sscr0 &= ~(SSCR0_MOD | SSCR0_SlotsPerFrm(8) | SSCR0_EDSS | SSCR0_DSS); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci /* set slot width */ 32962306a36Sopenharmony_ci if (slot_width > 16) 33062306a36Sopenharmony_ci sscr0 |= SSCR0_EDSS | SSCR0_DataSize(slot_width - 16); 33162306a36Sopenharmony_ci else 33262306a36Sopenharmony_ci sscr0 |= SSCR0_DataSize(slot_width); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci if (slots > 1) { 33562306a36Sopenharmony_ci /* enable network mode */ 33662306a36Sopenharmony_ci sscr0 |= SSCR0_MOD; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci /* set number of active slots */ 33962306a36Sopenharmony_ci sscr0 |= SSCR0_SlotsPerFrm(slots); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci /* set active slot mask */ 34262306a36Sopenharmony_ci pxa_ssp_write_reg(ssp, SSTSA, tx_mask); 34362306a36Sopenharmony_ci pxa_ssp_write_reg(ssp, SSRSA, rx_mask); 34462306a36Sopenharmony_ci } 34562306a36Sopenharmony_ci pxa_ssp_write_reg(ssp, SSCR0, sscr0); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci return 0; 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci/* 35162306a36Sopenharmony_ci * Tristate the SSP DAI lines 35262306a36Sopenharmony_ci */ 35362306a36Sopenharmony_cistatic int pxa_ssp_set_dai_tristate(struct snd_soc_dai *cpu_dai, 35462306a36Sopenharmony_ci int tristate) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); 35762306a36Sopenharmony_ci struct ssp_device *ssp = priv->ssp; 35862306a36Sopenharmony_ci u32 sscr1; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci sscr1 = pxa_ssp_read_reg(ssp, SSCR1); 36162306a36Sopenharmony_ci if (tristate) 36262306a36Sopenharmony_ci sscr1 &= ~SSCR1_TTE; 36362306a36Sopenharmony_ci else 36462306a36Sopenharmony_ci sscr1 |= SSCR1_TTE; 36562306a36Sopenharmony_ci pxa_ssp_write_reg(ssp, SSCR1, sscr1); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci return 0; 36862306a36Sopenharmony_ci} 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_cistatic int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai, 37162306a36Sopenharmony_ci unsigned int fmt) 37262306a36Sopenharmony_ci{ 37362306a36Sopenharmony_ci struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { 37662306a36Sopenharmony_ci case SND_SOC_DAIFMT_BC_FC: 37762306a36Sopenharmony_ci case SND_SOC_DAIFMT_BC_FP: 37862306a36Sopenharmony_ci case SND_SOC_DAIFMT_BP_FP: 37962306a36Sopenharmony_ci break; 38062306a36Sopenharmony_ci default: 38162306a36Sopenharmony_ci return -EINVAL; 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 38562306a36Sopenharmony_ci case SND_SOC_DAIFMT_NB_NF: 38662306a36Sopenharmony_ci case SND_SOC_DAIFMT_NB_IF: 38762306a36Sopenharmony_ci case SND_SOC_DAIFMT_IB_IF: 38862306a36Sopenharmony_ci case SND_SOC_DAIFMT_IB_NF: 38962306a36Sopenharmony_ci break; 39062306a36Sopenharmony_ci default: 39162306a36Sopenharmony_ci return -EINVAL; 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 39562306a36Sopenharmony_ci case SND_SOC_DAIFMT_I2S: 39662306a36Sopenharmony_ci case SND_SOC_DAIFMT_DSP_A: 39762306a36Sopenharmony_ci case SND_SOC_DAIFMT_DSP_B: 39862306a36Sopenharmony_ci break; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci default: 40162306a36Sopenharmony_ci return -EINVAL; 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci /* Settings will be applied in hw_params() */ 40562306a36Sopenharmony_ci priv->dai_fmt = fmt; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci return 0; 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci/* 41162306a36Sopenharmony_ci * Set up the SSP DAI format. 41262306a36Sopenharmony_ci * The SSP Port must be inactive before calling this function as the 41362306a36Sopenharmony_ci * physical interface format is changed. 41462306a36Sopenharmony_ci */ 41562306a36Sopenharmony_cistatic int pxa_ssp_configure_dai_fmt(struct ssp_priv *priv) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci struct ssp_device *ssp = priv->ssp; 41862306a36Sopenharmony_ci u32 sscr0, sscr1, sspsp, scfr; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci /* check if we need to change anything at all */ 42162306a36Sopenharmony_ci if (priv->configured_dai_fmt == priv->dai_fmt) 42262306a36Sopenharmony_ci return 0; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci /* reset port settings */ 42562306a36Sopenharmony_ci sscr0 = pxa_ssp_read_reg(ssp, SSCR0) & 42662306a36Sopenharmony_ci ~(SSCR0_PSP | SSCR0_MOD); 42762306a36Sopenharmony_ci sscr1 = pxa_ssp_read_reg(ssp, SSCR1) & 42862306a36Sopenharmony_ci ~(SSCR1_SCLKDIR | SSCR1_SFRMDIR | SSCR1_SCFR | 42962306a36Sopenharmony_ci SSCR1_RWOT | SSCR1_TRAIL | SSCR1_TFT | SSCR1_RFT); 43062306a36Sopenharmony_ci sspsp = pxa_ssp_read_reg(ssp, SSPSP) & 43162306a36Sopenharmony_ci ~(SSPSP_SFRMP | SSPSP_SCMODE(3)); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci sscr1 |= SSCR1_RxTresh(8) | SSCR1_TxTresh(7); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci switch (priv->dai_fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { 43662306a36Sopenharmony_ci case SND_SOC_DAIFMT_BC_FC: 43762306a36Sopenharmony_ci sscr1 |= SSCR1_SCLKDIR | SSCR1_SFRMDIR | SSCR1_SCFR; 43862306a36Sopenharmony_ci break; 43962306a36Sopenharmony_ci case SND_SOC_DAIFMT_BC_FP: 44062306a36Sopenharmony_ci sscr1 |= SSCR1_SCLKDIR | SSCR1_SCFR; 44162306a36Sopenharmony_ci break; 44262306a36Sopenharmony_ci case SND_SOC_DAIFMT_BP_FP: 44362306a36Sopenharmony_ci break; 44462306a36Sopenharmony_ci default: 44562306a36Sopenharmony_ci return -EINVAL; 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci switch (priv->dai_fmt & SND_SOC_DAIFMT_INV_MASK) { 44962306a36Sopenharmony_ci case SND_SOC_DAIFMT_NB_NF: 45062306a36Sopenharmony_ci sspsp |= SSPSP_SFRMP; 45162306a36Sopenharmony_ci break; 45262306a36Sopenharmony_ci case SND_SOC_DAIFMT_NB_IF: 45362306a36Sopenharmony_ci break; 45462306a36Sopenharmony_ci case SND_SOC_DAIFMT_IB_IF: 45562306a36Sopenharmony_ci sspsp |= SSPSP_SCMODE(2); 45662306a36Sopenharmony_ci break; 45762306a36Sopenharmony_ci case SND_SOC_DAIFMT_IB_NF: 45862306a36Sopenharmony_ci sspsp |= SSPSP_SCMODE(2) | SSPSP_SFRMP; 45962306a36Sopenharmony_ci break; 46062306a36Sopenharmony_ci default: 46162306a36Sopenharmony_ci return -EINVAL; 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci switch (priv->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 46562306a36Sopenharmony_ci case SND_SOC_DAIFMT_I2S: 46662306a36Sopenharmony_ci sscr0 |= SSCR0_PSP; 46762306a36Sopenharmony_ci sscr1 |= SSCR1_RWOT | SSCR1_TRAIL; 46862306a36Sopenharmony_ci /* See hw_params() */ 46962306a36Sopenharmony_ci break; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci case SND_SOC_DAIFMT_DSP_A: 47262306a36Sopenharmony_ci sspsp |= SSPSP_FSRT; 47362306a36Sopenharmony_ci fallthrough; 47462306a36Sopenharmony_ci case SND_SOC_DAIFMT_DSP_B: 47562306a36Sopenharmony_ci sscr0 |= SSCR0_MOD | SSCR0_PSP; 47662306a36Sopenharmony_ci sscr1 |= SSCR1_TRAIL | SSCR1_RWOT; 47762306a36Sopenharmony_ci break; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci default: 48062306a36Sopenharmony_ci return -EINVAL; 48162306a36Sopenharmony_ci } 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci pxa_ssp_write_reg(ssp, SSCR0, sscr0); 48462306a36Sopenharmony_ci pxa_ssp_write_reg(ssp, SSCR1, sscr1); 48562306a36Sopenharmony_ci pxa_ssp_write_reg(ssp, SSPSP, sspsp); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci switch (priv->dai_fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { 48862306a36Sopenharmony_ci case SND_SOC_DAIFMT_BC_FC: 48962306a36Sopenharmony_ci case SND_SOC_DAIFMT_BC_FP: 49062306a36Sopenharmony_ci scfr = pxa_ssp_read_reg(ssp, SSCR1) | SSCR1_SCFR; 49162306a36Sopenharmony_ci pxa_ssp_write_reg(ssp, SSCR1, scfr); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci while (pxa_ssp_read_reg(ssp, SSSR) & SSSR_BSY) 49462306a36Sopenharmony_ci cpu_relax(); 49562306a36Sopenharmony_ci break; 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci dump_registers(ssp); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci /* Since we are configuring the timings for the format by hand 50162306a36Sopenharmony_ci * we have to defer some things until hw_params() where we 50262306a36Sopenharmony_ci * know parameters like the sample size. 50362306a36Sopenharmony_ci */ 50462306a36Sopenharmony_ci priv->configured_dai_fmt = priv->dai_fmt; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci return 0; 50762306a36Sopenharmony_ci} 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_cistruct pxa_ssp_clock_mode { 51062306a36Sopenharmony_ci int rate; 51162306a36Sopenharmony_ci int pll; 51262306a36Sopenharmony_ci u8 acds; 51362306a36Sopenharmony_ci u8 scdb; 51462306a36Sopenharmony_ci}; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_cistatic const struct pxa_ssp_clock_mode pxa_ssp_clock_modes[] = { 51762306a36Sopenharmony_ci { .rate = 8000, .pll = 32842000, .acds = SSACD_ACDS_32, .scdb = SSACD_SCDB_4X }, 51862306a36Sopenharmony_ci { .rate = 11025, .pll = 5622000, .acds = SSACD_ACDS_4, .scdb = SSACD_SCDB_4X }, 51962306a36Sopenharmony_ci { .rate = 16000, .pll = 32842000, .acds = SSACD_ACDS_16, .scdb = SSACD_SCDB_4X }, 52062306a36Sopenharmony_ci { .rate = 22050, .pll = 5622000, .acds = SSACD_ACDS_2, .scdb = SSACD_SCDB_4X }, 52162306a36Sopenharmony_ci { .rate = 44100, .pll = 11345000, .acds = SSACD_ACDS_2, .scdb = SSACD_SCDB_4X }, 52262306a36Sopenharmony_ci { .rate = 48000, .pll = 12235000, .acds = SSACD_ACDS_2, .scdb = SSACD_SCDB_4X }, 52362306a36Sopenharmony_ci { .rate = 96000, .pll = 12235000, .acds = SSACD_ACDS_4, .scdb = SSACD_SCDB_1X }, 52462306a36Sopenharmony_ci {} 52562306a36Sopenharmony_ci}; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci/* 52862306a36Sopenharmony_ci * Set the SSP audio DMA parameters and sample size. 52962306a36Sopenharmony_ci * Can be called multiple times by oss emulation. 53062306a36Sopenharmony_ci */ 53162306a36Sopenharmony_cistatic int pxa_ssp_hw_params(struct snd_pcm_substream *substream, 53262306a36Sopenharmony_ci struct snd_pcm_hw_params *params, 53362306a36Sopenharmony_ci struct snd_soc_dai *cpu_dai) 53462306a36Sopenharmony_ci{ 53562306a36Sopenharmony_ci struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); 53662306a36Sopenharmony_ci struct ssp_device *ssp = priv->ssp; 53762306a36Sopenharmony_ci int chn = params_channels(params); 53862306a36Sopenharmony_ci u32 sscr0, sspsp; 53962306a36Sopenharmony_ci int width = snd_pcm_format_physical_width(params_format(params)); 54062306a36Sopenharmony_ci int ttsa = pxa_ssp_read_reg(ssp, SSTSA) & 0xf; 54162306a36Sopenharmony_ci struct snd_dmaengine_dai_dma_data *dma_data; 54262306a36Sopenharmony_ci int rate = params_rate(params); 54362306a36Sopenharmony_ci int bclk = rate * chn * (width / 8); 54462306a36Sopenharmony_ci int ret; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci dma_data = snd_soc_dai_get_dma_data(cpu_dai, substream); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci /* Network mode with one active slot (ttsa == 1) can be used 54962306a36Sopenharmony_ci * to force 16-bit frame width on the wire (for S16_LE), even 55062306a36Sopenharmony_ci * with two channels. Use 16-bit DMA transfers for this case. 55162306a36Sopenharmony_ci */ 55262306a36Sopenharmony_ci pxa_ssp_set_dma_params(ssp, 55362306a36Sopenharmony_ci ((chn == 2) && (ttsa != 1)) || (width == 32), 55462306a36Sopenharmony_ci substream->stream == SNDRV_PCM_STREAM_PLAYBACK, dma_data); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci /* we can only change the settings if the port is not in use */ 55762306a36Sopenharmony_ci if (pxa_ssp_read_reg(ssp, SSCR0) & SSCR0_SSE) 55862306a36Sopenharmony_ci return 0; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci ret = pxa_ssp_configure_dai_fmt(priv); 56162306a36Sopenharmony_ci if (ret < 0) 56262306a36Sopenharmony_ci return ret; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci /* clear selected SSP bits */ 56562306a36Sopenharmony_ci sscr0 = pxa_ssp_read_reg(ssp, SSCR0) & ~(SSCR0_DSS | SSCR0_EDSS); 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci /* bit size */ 56862306a36Sopenharmony_ci switch (params_format(params)) { 56962306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_LE: 57062306a36Sopenharmony_ci if (ssp->type == PXA3xx_SSP) 57162306a36Sopenharmony_ci sscr0 |= SSCR0_FPCKE; 57262306a36Sopenharmony_ci sscr0 |= SSCR0_DataSize(16); 57362306a36Sopenharmony_ci break; 57462306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S24_LE: 57562306a36Sopenharmony_ci sscr0 |= (SSCR0_EDSS | SSCR0_DataSize(8)); 57662306a36Sopenharmony_ci break; 57762306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S32_LE: 57862306a36Sopenharmony_ci sscr0 |= (SSCR0_EDSS | SSCR0_DataSize(16)); 57962306a36Sopenharmony_ci break; 58062306a36Sopenharmony_ci } 58162306a36Sopenharmony_ci pxa_ssp_write_reg(ssp, SSCR0, sscr0); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci if (sscr0 & SSCR0_ACS) { 58462306a36Sopenharmony_ci ret = pxa_ssp_set_pll(priv, bclk); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci /* 58762306a36Sopenharmony_ci * If we were able to generate the bclk directly, 58862306a36Sopenharmony_ci * all is fine. Otherwise, look up the closest rate 58962306a36Sopenharmony_ci * from the table and also set the dividers. 59062306a36Sopenharmony_ci */ 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci if (ret < 0) { 59362306a36Sopenharmony_ci const struct pxa_ssp_clock_mode *m; 59462306a36Sopenharmony_ci int ssacd; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci for (m = pxa_ssp_clock_modes; m->rate; m++) { 59762306a36Sopenharmony_ci if (m->rate == rate) 59862306a36Sopenharmony_ci break; 59962306a36Sopenharmony_ci } 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci if (!m->rate) 60262306a36Sopenharmony_ci return -EINVAL; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci ret = pxa_ssp_set_pll(priv, bclk); 60562306a36Sopenharmony_ci if (ret < 0) 60662306a36Sopenharmony_ci return ret; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci ssacd = pxa_ssp_read_reg(ssp, SSACD); 60962306a36Sopenharmony_ci ssacd &= ~(SSACD_ACDS(7) | SSACD_SCDB_1X); 61062306a36Sopenharmony_ci ssacd |= SSACD_ACDS(m->acds); 61162306a36Sopenharmony_ci ssacd |= m->scdb; 61262306a36Sopenharmony_ci pxa_ssp_write_reg(ssp, SSACD, ssacd); 61362306a36Sopenharmony_ci } 61462306a36Sopenharmony_ci } else if (sscr0 & SSCR0_ECS) { 61562306a36Sopenharmony_ci /* 61662306a36Sopenharmony_ci * For setups with external clocking, the PLL and its diviers 61762306a36Sopenharmony_ci * are not active. Instead, the SCR bits in SSCR0 can be used 61862306a36Sopenharmony_ci * to divide the clock. 61962306a36Sopenharmony_ci */ 62062306a36Sopenharmony_ci pxa_ssp_set_scr(ssp, bclk / rate); 62162306a36Sopenharmony_ci } 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci switch (priv->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 62462306a36Sopenharmony_ci case SND_SOC_DAIFMT_I2S: 62562306a36Sopenharmony_ci sspsp = pxa_ssp_read_reg(ssp, SSPSP); 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci if (((priv->sysclk / bclk) == 64) && (width == 16)) { 62862306a36Sopenharmony_ci /* This is a special case where the bitclk is 64fs 62962306a36Sopenharmony_ci * and we're not dealing with 2*32 bits of audio 63062306a36Sopenharmony_ci * samples. 63162306a36Sopenharmony_ci * 63262306a36Sopenharmony_ci * The SSP values used for that are all found out by 63362306a36Sopenharmony_ci * trying and failing a lot; some of the registers 63462306a36Sopenharmony_ci * needed for that mode are only available on PXA3xx. 63562306a36Sopenharmony_ci */ 63662306a36Sopenharmony_ci if (ssp->type != PXA3xx_SSP) 63762306a36Sopenharmony_ci return -EINVAL; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci sspsp |= SSPSP_SFRMWDTH(width * 2); 64062306a36Sopenharmony_ci sspsp |= SSPSP_SFRMDLY(width * 4); 64162306a36Sopenharmony_ci sspsp |= SSPSP_EDMYSTOP(3); 64262306a36Sopenharmony_ci sspsp |= SSPSP_DMYSTOP(3); 64362306a36Sopenharmony_ci sspsp |= SSPSP_DMYSTRT(1); 64462306a36Sopenharmony_ci } else { 64562306a36Sopenharmony_ci /* The frame width is the width the LRCLK is 64662306a36Sopenharmony_ci * asserted for; the delay is expressed in 64762306a36Sopenharmony_ci * half cycle units. We need the extra cycle 64862306a36Sopenharmony_ci * because the data starts clocking out one BCLK 64962306a36Sopenharmony_ci * after LRCLK changes polarity. 65062306a36Sopenharmony_ci */ 65162306a36Sopenharmony_ci sspsp |= SSPSP_SFRMWDTH(width + 1); 65262306a36Sopenharmony_ci sspsp |= SSPSP_SFRMDLY((width + 1) * 2); 65362306a36Sopenharmony_ci sspsp |= SSPSP_DMYSTRT(1); 65462306a36Sopenharmony_ci } 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci pxa_ssp_write_reg(ssp, SSPSP, sspsp); 65762306a36Sopenharmony_ci break; 65862306a36Sopenharmony_ci default: 65962306a36Sopenharmony_ci break; 66062306a36Sopenharmony_ci } 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci /* When we use a network mode, we always require TDM slots 66362306a36Sopenharmony_ci * - complain loudly and fail if they've not been set up yet. 66462306a36Sopenharmony_ci */ 66562306a36Sopenharmony_ci if ((sscr0 & SSCR0_MOD) && !ttsa) { 66662306a36Sopenharmony_ci dev_err(ssp->dev, "No TDM timeslot configured\n"); 66762306a36Sopenharmony_ci return -EINVAL; 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci dump_registers(ssp); 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci return 0; 67362306a36Sopenharmony_ci} 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_cistatic void pxa_ssp_set_running_bit(struct snd_pcm_substream *substream, 67662306a36Sopenharmony_ci struct ssp_device *ssp, int value) 67762306a36Sopenharmony_ci{ 67862306a36Sopenharmony_ci uint32_t sscr0 = pxa_ssp_read_reg(ssp, SSCR0); 67962306a36Sopenharmony_ci uint32_t sscr1 = pxa_ssp_read_reg(ssp, SSCR1); 68062306a36Sopenharmony_ci uint32_t sspsp = pxa_ssp_read_reg(ssp, SSPSP); 68162306a36Sopenharmony_ci uint32_t sssr = pxa_ssp_read_reg(ssp, SSSR); 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci if (value && (sscr0 & SSCR0_SSE)) 68462306a36Sopenharmony_ci pxa_ssp_write_reg(ssp, SSCR0, sscr0 & ~SSCR0_SSE); 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 68762306a36Sopenharmony_ci if (value) 68862306a36Sopenharmony_ci sscr1 |= SSCR1_TSRE; 68962306a36Sopenharmony_ci else 69062306a36Sopenharmony_ci sscr1 &= ~SSCR1_TSRE; 69162306a36Sopenharmony_ci } else { 69262306a36Sopenharmony_ci if (value) 69362306a36Sopenharmony_ci sscr1 |= SSCR1_RSRE; 69462306a36Sopenharmony_ci else 69562306a36Sopenharmony_ci sscr1 &= ~SSCR1_RSRE; 69662306a36Sopenharmony_ci } 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci pxa_ssp_write_reg(ssp, SSCR1, sscr1); 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci if (value) { 70162306a36Sopenharmony_ci pxa_ssp_write_reg(ssp, SSSR, sssr); 70262306a36Sopenharmony_ci pxa_ssp_write_reg(ssp, SSPSP, sspsp); 70362306a36Sopenharmony_ci pxa_ssp_write_reg(ssp, SSCR0, sscr0 | SSCR0_SSE); 70462306a36Sopenharmony_ci } 70562306a36Sopenharmony_ci} 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_cistatic int pxa_ssp_trigger(struct snd_pcm_substream *substream, int cmd, 70862306a36Sopenharmony_ci struct snd_soc_dai *cpu_dai) 70962306a36Sopenharmony_ci{ 71062306a36Sopenharmony_ci int ret = 0; 71162306a36Sopenharmony_ci struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); 71262306a36Sopenharmony_ci struct ssp_device *ssp = priv->ssp; 71362306a36Sopenharmony_ci int val; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci switch (cmd) { 71662306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 71762306a36Sopenharmony_ci pxa_ssp_enable(ssp); 71862306a36Sopenharmony_ci break; 71962306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 72062306a36Sopenharmony_ci pxa_ssp_set_running_bit(substream, ssp, 1); 72162306a36Sopenharmony_ci val = pxa_ssp_read_reg(ssp, SSSR); 72262306a36Sopenharmony_ci pxa_ssp_write_reg(ssp, SSSR, val); 72362306a36Sopenharmony_ci break; 72462306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 72562306a36Sopenharmony_ci pxa_ssp_set_running_bit(substream, ssp, 1); 72662306a36Sopenharmony_ci break; 72762306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 72862306a36Sopenharmony_ci pxa_ssp_set_running_bit(substream, ssp, 0); 72962306a36Sopenharmony_ci break; 73062306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 73162306a36Sopenharmony_ci pxa_ssp_disable(ssp); 73262306a36Sopenharmony_ci break; 73362306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 73462306a36Sopenharmony_ci pxa_ssp_set_running_bit(substream, ssp, 0); 73562306a36Sopenharmony_ci break; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci default: 73862306a36Sopenharmony_ci ret = -EINVAL; 73962306a36Sopenharmony_ci } 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci dump_registers(ssp); 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci return ret; 74462306a36Sopenharmony_ci} 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_cistatic int pxa_ssp_probe(struct snd_soc_dai *dai) 74762306a36Sopenharmony_ci{ 74862306a36Sopenharmony_ci struct device *dev = dai->dev; 74962306a36Sopenharmony_ci struct ssp_priv *priv; 75062306a36Sopenharmony_ci int ret; 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci priv = kzalloc(sizeof(struct ssp_priv), GFP_KERNEL); 75362306a36Sopenharmony_ci if (!priv) 75462306a36Sopenharmony_ci return -ENOMEM; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci if (dev->of_node) { 75762306a36Sopenharmony_ci struct device_node *ssp_handle; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci ssp_handle = of_parse_phandle(dev->of_node, "port", 0); 76062306a36Sopenharmony_ci if (!ssp_handle) { 76162306a36Sopenharmony_ci dev_err(dev, "unable to get 'port' phandle\n"); 76262306a36Sopenharmony_ci ret = -ENODEV; 76362306a36Sopenharmony_ci goto err_priv; 76462306a36Sopenharmony_ci } 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci priv->ssp = pxa_ssp_request_of(ssp_handle, "SoC audio"); 76762306a36Sopenharmony_ci if (priv->ssp == NULL) { 76862306a36Sopenharmony_ci ret = -ENODEV; 76962306a36Sopenharmony_ci goto err_priv; 77062306a36Sopenharmony_ci } 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci priv->extclk = devm_clk_get(dev, "extclk"); 77362306a36Sopenharmony_ci if (IS_ERR(priv->extclk)) { 77462306a36Sopenharmony_ci ret = PTR_ERR(priv->extclk); 77562306a36Sopenharmony_ci if (ret == -EPROBE_DEFER) 77662306a36Sopenharmony_ci goto err_priv; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci priv->extclk = NULL; 77962306a36Sopenharmony_ci } 78062306a36Sopenharmony_ci } else { 78162306a36Sopenharmony_ci priv->ssp = pxa_ssp_request(dai->id + 1, "SoC audio"); 78262306a36Sopenharmony_ci if (priv->ssp == NULL) { 78362306a36Sopenharmony_ci ret = -ENODEV; 78462306a36Sopenharmony_ci goto err_priv; 78562306a36Sopenharmony_ci } 78662306a36Sopenharmony_ci } 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci priv->dai_fmt = (unsigned int) -1; 78962306a36Sopenharmony_ci snd_soc_dai_set_drvdata(dai, priv); 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci return 0; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_cierr_priv: 79462306a36Sopenharmony_ci kfree(priv); 79562306a36Sopenharmony_ci return ret; 79662306a36Sopenharmony_ci} 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_cistatic int pxa_ssp_remove(struct snd_soc_dai *dai) 79962306a36Sopenharmony_ci{ 80062306a36Sopenharmony_ci struct ssp_priv *priv = snd_soc_dai_get_drvdata(dai); 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci pxa_ssp_free(priv->ssp); 80362306a36Sopenharmony_ci kfree(priv); 80462306a36Sopenharmony_ci return 0; 80562306a36Sopenharmony_ci} 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci#define PXA_SSP_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ 80862306a36Sopenharmony_ci SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \ 80962306a36Sopenharmony_ci SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ 81062306a36Sopenharmony_ci SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | \ 81162306a36Sopenharmony_ci SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci#define PXA_SSP_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE) 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_cistatic const struct snd_soc_dai_ops pxa_ssp_dai_ops = { 81662306a36Sopenharmony_ci .probe = pxa_ssp_probe, 81762306a36Sopenharmony_ci .remove = pxa_ssp_remove, 81862306a36Sopenharmony_ci .startup = pxa_ssp_startup, 81962306a36Sopenharmony_ci .shutdown = pxa_ssp_shutdown, 82062306a36Sopenharmony_ci .trigger = pxa_ssp_trigger, 82162306a36Sopenharmony_ci .hw_params = pxa_ssp_hw_params, 82262306a36Sopenharmony_ci .set_sysclk = pxa_ssp_set_dai_sysclk, 82362306a36Sopenharmony_ci .set_fmt = pxa_ssp_set_dai_fmt, 82462306a36Sopenharmony_ci .set_tdm_slot = pxa_ssp_set_dai_tdm_slot, 82562306a36Sopenharmony_ci .set_tristate = pxa_ssp_set_dai_tristate, 82662306a36Sopenharmony_ci}; 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_cistatic struct snd_soc_dai_driver pxa_ssp_dai = { 82962306a36Sopenharmony_ci .playback = { 83062306a36Sopenharmony_ci .channels_min = 1, 83162306a36Sopenharmony_ci .channels_max = 8, 83262306a36Sopenharmony_ci .rates = PXA_SSP_RATES, 83362306a36Sopenharmony_ci .formats = PXA_SSP_FORMATS, 83462306a36Sopenharmony_ci }, 83562306a36Sopenharmony_ci .capture = { 83662306a36Sopenharmony_ci .channels_min = 1, 83762306a36Sopenharmony_ci .channels_max = 8, 83862306a36Sopenharmony_ci .rates = PXA_SSP_RATES, 83962306a36Sopenharmony_ci .formats = PXA_SSP_FORMATS, 84062306a36Sopenharmony_ci }, 84162306a36Sopenharmony_ci .ops = &pxa_ssp_dai_ops, 84262306a36Sopenharmony_ci}; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_cistatic const struct snd_soc_component_driver pxa_ssp_component = { 84562306a36Sopenharmony_ci .name = "pxa-ssp", 84662306a36Sopenharmony_ci .pcm_construct = pxa2xx_soc_pcm_new, 84762306a36Sopenharmony_ci .open = pxa2xx_soc_pcm_open, 84862306a36Sopenharmony_ci .close = pxa2xx_soc_pcm_close, 84962306a36Sopenharmony_ci .hw_params = pxa2xx_soc_pcm_hw_params, 85062306a36Sopenharmony_ci .prepare = pxa2xx_soc_pcm_prepare, 85162306a36Sopenharmony_ci .trigger = pxa2xx_soc_pcm_trigger, 85262306a36Sopenharmony_ci .pointer = pxa2xx_soc_pcm_pointer, 85362306a36Sopenharmony_ci .suspend = pxa_ssp_suspend, 85462306a36Sopenharmony_ci .resume = pxa_ssp_resume, 85562306a36Sopenharmony_ci .legacy_dai_naming = 1, 85662306a36Sopenharmony_ci}; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci#ifdef CONFIG_OF 85962306a36Sopenharmony_cistatic const struct of_device_id pxa_ssp_of_ids[] = { 86062306a36Sopenharmony_ci { .compatible = "mrvl,pxa-ssp-dai" }, 86162306a36Sopenharmony_ci {} 86262306a36Sopenharmony_ci}; 86362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, pxa_ssp_of_ids); 86462306a36Sopenharmony_ci#endif 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_cistatic int asoc_ssp_probe(struct platform_device *pdev) 86762306a36Sopenharmony_ci{ 86862306a36Sopenharmony_ci return devm_snd_soc_register_component(&pdev->dev, &pxa_ssp_component, 86962306a36Sopenharmony_ci &pxa_ssp_dai, 1); 87062306a36Sopenharmony_ci} 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_cistatic struct platform_driver asoc_ssp_driver = { 87362306a36Sopenharmony_ci .driver = { 87462306a36Sopenharmony_ci .name = "pxa-ssp-dai", 87562306a36Sopenharmony_ci .of_match_table = of_match_ptr(pxa_ssp_of_ids), 87662306a36Sopenharmony_ci }, 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci .probe = asoc_ssp_probe, 87962306a36Sopenharmony_ci}; 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_cimodule_platform_driver(asoc_ssp_driver); 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci/* Module information */ 88462306a36Sopenharmony_ciMODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 88562306a36Sopenharmony_ciMODULE_DESCRIPTION("PXA SSP/PCM SoC Interface"); 88662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 88762306a36Sopenharmony_ciMODULE_ALIAS("platform:pxa-ssp-dai"); 888