18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * pxa-ssp.c -- ALSA Soc Audio Layer 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2005,2008 Wolfson Microelectronics PLC. 68c2ecf20Sopenharmony_ci * Author: Liam Girdwood 78c2ecf20Sopenharmony_ci * Mark Brown <broonie@opensource.wolfsonmicro.com> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * TODO: 108c2ecf20Sopenharmony_ci * o Test network mode for > 16bit sample size 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/init.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 178c2ecf20Sopenharmony_ci#include <linux/clk.h> 188c2ecf20Sopenharmony_ci#include <linux/io.h> 198c2ecf20Sopenharmony_ci#include <linux/pxa2xx_ssp.h> 208c2ecf20Sopenharmony_ci#include <linux/of.h> 218c2ecf20Sopenharmony_ci#include <linux/dmaengine.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include <asm/irq.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include <sound/core.h> 268c2ecf20Sopenharmony_ci#include <sound/pcm.h> 278c2ecf20Sopenharmony_ci#include <sound/initval.h> 288c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 298c2ecf20Sopenharmony_ci#include <sound/soc.h> 308c2ecf20Sopenharmony_ci#include <sound/pxa2xx-lib.h> 318c2ecf20Sopenharmony_ci#include <sound/dmaengine_pcm.h> 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#include "pxa-ssp.h" 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/* 368c2ecf20Sopenharmony_ci * SSP audio private data 378c2ecf20Sopenharmony_ci */ 388c2ecf20Sopenharmony_cistruct ssp_priv { 398c2ecf20Sopenharmony_ci struct ssp_device *ssp; 408c2ecf20Sopenharmony_ci struct clk *extclk; 418c2ecf20Sopenharmony_ci unsigned long ssp_clk; 428c2ecf20Sopenharmony_ci unsigned int sysclk; 438c2ecf20Sopenharmony_ci unsigned int dai_fmt; 448c2ecf20Sopenharmony_ci unsigned int configured_dai_fmt; 458c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 468c2ecf20Sopenharmony_ci uint32_t cr0; 478c2ecf20Sopenharmony_ci uint32_t cr1; 488c2ecf20Sopenharmony_ci uint32_t to; 498c2ecf20Sopenharmony_ci uint32_t psp; 508c2ecf20Sopenharmony_ci#endif 518c2ecf20Sopenharmony_ci}; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic void dump_registers(struct ssp_device *ssp) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci dev_dbg(ssp->dev, "SSCR0 0x%08x SSCR1 0x%08x SSTO 0x%08x\n", 568c2ecf20Sopenharmony_ci pxa_ssp_read_reg(ssp, SSCR0), pxa_ssp_read_reg(ssp, SSCR1), 578c2ecf20Sopenharmony_ci pxa_ssp_read_reg(ssp, SSTO)); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci dev_dbg(ssp->dev, "SSPSP 0x%08x SSSR 0x%08x SSACD 0x%08x\n", 608c2ecf20Sopenharmony_ci pxa_ssp_read_reg(ssp, SSPSP), pxa_ssp_read_reg(ssp, SSSR), 618c2ecf20Sopenharmony_ci pxa_ssp_read_reg(ssp, SSACD)); 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic void pxa_ssp_enable(struct ssp_device *ssp) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci uint32_t sscr0; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci sscr0 = __raw_readl(ssp->mmio_base + SSCR0) | SSCR0_SSE; 698c2ecf20Sopenharmony_ci __raw_writel(sscr0, ssp->mmio_base + SSCR0); 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic void pxa_ssp_disable(struct ssp_device *ssp) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci uint32_t sscr0; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci sscr0 = __raw_readl(ssp->mmio_base + SSCR0) & ~SSCR0_SSE; 778c2ecf20Sopenharmony_ci __raw_writel(sscr0, ssp->mmio_base + SSCR0); 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic void pxa_ssp_set_dma_params(struct ssp_device *ssp, int width4, 818c2ecf20Sopenharmony_ci int out, struct snd_dmaengine_dai_dma_data *dma) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci dma->addr_width = width4 ? DMA_SLAVE_BUSWIDTH_4_BYTES : 848c2ecf20Sopenharmony_ci DMA_SLAVE_BUSWIDTH_2_BYTES; 858c2ecf20Sopenharmony_ci dma->maxburst = 16; 868c2ecf20Sopenharmony_ci dma->addr = ssp->phys_base + SSDR; 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic int pxa_ssp_startup(struct snd_pcm_substream *substream, 908c2ecf20Sopenharmony_ci struct snd_soc_dai *cpu_dai) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); 938c2ecf20Sopenharmony_ci struct ssp_device *ssp = priv->ssp; 948c2ecf20Sopenharmony_ci struct snd_dmaengine_dai_dma_data *dma; 958c2ecf20Sopenharmony_ci int ret = 0; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci if (!snd_soc_dai_active(cpu_dai)) { 988c2ecf20Sopenharmony_ci clk_prepare_enable(ssp->clk); 998c2ecf20Sopenharmony_ci pxa_ssp_disable(ssp); 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci if (priv->extclk) 1038c2ecf20Sopenharmony_ci clk_prepare_enable(priv->extclk); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci dma = kzalloc(sizeof(struct snd_dmaengine_dai_dma_data), GFP_KERNEL); 1068c2ecf20Sopenharmony_ci if (!dma) 1078c2ecf20Sopenharmony_ci return -ENOMEM; 1088c2ecf20Sopenharmony_ci dma->chan_name = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 1098c2ecf20Sopenharmony_ci "tx" : "rx"; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci snd_soc_dai_set_dma_data(cpu_dai, substream, dma); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci return ret; 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic void pxa_ssp_shutdown(struct snd_pcm_substream *substream, 1178c2ecf20Sopenharmony_ci struct snd_soc_dai *cpu_dai) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); 1208c2ecf20Sopenharmony_ci struct ssp_device *ssp = priv->ssp; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci if (!snd_soc_dai_active(cpu_dai)) { 1238c2ecf20Sopenharmony_ci pxa_ssp_disable(ssp); 1248c2ecf20Sopenharmony_ci clk_disable_unprepare(ssp->clk); 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci if (priv->extclk) 1288c2ecf20Sopenharmony_ci clk_disable_unprepare(priv->extclk); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci kfree(snd_soc_dai_get_dma_data(cpu_dai, substream)); 1318c2ecf20Sopenharmony_ci snd_soc_dai_set_dma_data(cpu_dai, substream, NULL); 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic int pxa_ssp_suspend(struct snd_soc_component *component) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci struct ssp_priv *priv = snd_soc_component_get_drvdata(component); 1398c2ecf20Sopenharmony_ci struct ssp_device *ssp = priv->ssp; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci if (!snd_soc_component_active(component)) 1428c2ecf20Sopenharmony_ci clk_prepare_enable(ssp->clk); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci priv->cr0 = __raw_readl(ssp->mmio_base + SSCR0); 1458c2ecf20Sopenharmony_ci priv->cr1 = __raw_readl(ssp->mmio_base + SSCR1); 1468c2ecf20Sopenharmony_ci priv->to = __raw_readl(ssp->mmio_base + SSTO); 1478c2ecf20Sopenharmony_ci priv->psp = __raw_readl(ssp->mmio_base + SSPSP); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci pxa_ssp_disable(ssp); 1508c2ecf20Sopenharmony_ci clk_disable_unprepare(ssp->clk); 1518c2ecf20Sopenharmony_ci return 0; 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic int pxa_ssp_resume(struct snd_soc_component *component) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci struct ssp_priv *priv = snd_soc_component_get_drvdata(component); 1578c2ecf20Sopenharmony_ci struct ssp_device *ssp = priv->ssp; 1588c2ecf20Sopenharmony_ci uint32_t sssr = SSSR_ROR | SSSR_TUR | SSSR_BCE; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci clk_prepare_enable(ssp->clk); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci __raw_writel(sssr, ssp->mmio_base + SSSR); 1638c2ecf20Sopenharmony_ci __raw_writel(priv->cr0 & ~SSCR0_SSE, ssp->mmio_base + SSCR0); 1648c2ecf20Sopenharmony_ci __raw_writel(priv->cr1, ssp->mmio_base + SSCR1); 1658c2ecf20Sopenharmony_ci __raw_writel(priv->to, ssp->mmio_base + SSTO); 1668c2ecf20Sopenharmony_ci __raw_writel(priv->psp, ssp->mmio_base + SSPSP); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci if (snd_soc_component_active(component)) 1698c2ecf20Sopenharmony_ci pxa_ssp_enable(ssp); 1708c2ecf20Sopenharmony_ci else 1718c2ecf20Sopenharmony_ci clk_disable_unprepare(ssp->clk); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci return 0; 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci#else 1778c2ecf20Sopenharmony_ci#define pxa_ssp_suspend NULL 1788c2ecf20Sopenharmony_ci#define pxa_ssp_resume NULL 1798c2ecf20Sopenharmony_ci#endif 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci/* 1828c2ecf20Sopenharmony_ci * ssp_set_clkdiv - set SSP clock divider 1838c2ecf20Sopenharmony_ci * @div: serial clock rate divider 1848c2ecf20Sopenharmony_ci */ 1858c2ecf20Sopenharmony_cistatic void pxa_ssp_set_scr(struct ssp_device *ssp, u32 div) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci u32 sscr0 = pxa_ssp_read_reg(ssp, SSCR0); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci if (ssp->type == PXA25x_SSP) { 1908c2ecf20Sopenharmony_ci sscr0 &= ~0x0000ff00; 1918c2ecf20Sopenharmony_ci sscr0 |= ((div - 2)/2) << 8; /* 2..512 */ 1928c2ecf20Sopenharmony_ci } else { 1938c2ecf20Sopenharmony_ci sscr0 &= ~0x000fff00; 1948c2ecf20Sopenharmony_ci sscr0 |= (div - 1) << 8; /* 1..4096 */ 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci pxa_ssp_write_reg(ssp, SSCR0, sscr0); 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci/* 2008c2ecf20Sopenharmony_ci * Set the SSP ports SYSCLK. 2018c2ecf20Sopenharmony_ci */ 2028c2ecf20Sopenharmony_cistatic int pxa_ssp_set_dai_sysclk(struct snd_soc_dai *cpu_dai, 2038c2ecf20Sopenharmony_ci int clk_id, unsigned int freq, int dir) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); 2068c2ecf20Sopenharmony_ci struct ssp_device *ssp = priv->ssp; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci u32 sscr0 = pxa_ssp_read_reg(ssp, SSCR0) & 2098c2ecf20Sopenharmony_ci ~(SSCR0_ECS | SSCR0_NCS | SSCR0_MOD | SSCR0_ACS); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci if (priv->extclk) { 2128c2ecf20Sopenharmony_ci int ret; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci /* 2158c2ecf20Sopenharmony_ci * For DT based boards, if an extclk is given, use it 2168c2ecf20Sopenharmony_ci * here and configure PXA_SSP_CLK_EXT. 2178c2ecf20Sopenharmony_ci */ 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci ret = clk_set_rate(priv->extclk, freq); 2208c2ecf20Sopenharmony_ci if (ret < 0) 2218c2ecf20Sopenharmony_ci return ret; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci clk_id = PXA_SSP_CLK_EXT; 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci dev_dbg(ssp->dev, 2278c2ecf20Sopenharmony_ci "pxa_ssp_set_dai_sysclk id: %d, clk_id %d, freq %u\n", 2288c2ecf20Sopenharmony_ci cpu_dai->id, clk_id, freq); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci switch (clk_id) { 2318c2ecf20Sopenharmony_ci case PXA_SSP_CLK_NET_PLL: 2328c2ecf20Sopenharmony_ci sscr0 |= SSCR0_MOD; 2338c2ecf20Sopenharmony_ci break; 2348c2ecf20Sopenharmony_ci case PXA_SSP_CLK_PLL: 2358c2ecf20Sopenharmony_ci /* Internal PLL is fixed */ 2368c2ecf20Sopenharmony_ci if (ssp->type == PXA25x_SSP) 2378c2ecf20Sopenharmony_ci priv->sysclk = 1843200; 2388c2ecf20Sopenharmony_ci else 2398c2ecf20Sopenharmony_ci priv->sysclk = 13000000; 2408c2ecf20Sopenharmony_ci break; 2418c2ecf20Sopenharmony_ci case PXA_SSP_CLK_EXT: 2428c2ecf20Sopenharmony_ci priv->sysclk = freq; 2438c2ecf20Sopenharmony_ci sscr0 |= SSCR0_ECS; 2448c2ecf20Sopenharmony_ci break; 2458c2ecf20Sopenharmony_ci case PXA_SSP_CLK_NET: 2468c2ecf20Sopenharmony_ci priv->sysclk = freq; 2478c2ecf20Sopenharmony_ci sscr0 |= SSCR0_NCS | SSCR0_MOD; 2488c2ecf20Sopenharmony_ci break; 2498c2ecf20Sopenharmony_ci case PXA_SSP_CLK_AUDIO: 2508c2ecf20Sopenharmony_ci priv->sysclk = 0; 2518c2ecf20Sopenharmony_ci pxa_ssp_set_scr(ssp, 1); 2528c2ecf20Sopenharmony_ci sscr0 |= SSCR0_ACS; 2538c2ecf20Sopenharmony_ci break; 2548c2ecf20Sopenharmony_ci default: 2558c2ecf20Sopenharmony_ci return -ENODEV; 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci /* The SSP clock must be disabled when changing SSP clock mode 2598c2ecf20Sopenharmony_ci * on PXA2xx. On PXA3xx it must be enabled when doing so. */ 2608c2ecf20Sopenharmony_ci if (ssp->type != PXA3xx_SSP) 2618c2ecf20Sopenharmony_ci clk_disable_unprepare(ssp->clk); 2628c2ecf20Sopenharmony_ci pxa_ssp_write_reg(ssp, SSCR0, sscr0); 2638c2ecf20Sopenharmony_ci if (ssp->type != PXA3xx_SSP) 2648c2ecf20Sopenharmony_ci clk_prepare_enable(ssp->clk); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci return 0; 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci/* 2708c2ecf20Sopenharmony_ci * Configure the PLL frequency pxa27x and (afaik - pxa320 only) 2718c2ecf20Sopenharmony_ci */ 2728c2ecf20Sopenharmony_cistatic int pxa_ssp_set_pll(struct ssp_priv *priv, unsigned int freq) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci struct ssp_device *ssp = priv->ssp; 2758c2ecf20Sopenharmony_ci u32 ssacd = pxa_ssp_read_reg(ssp, SSACD) & ~0x70; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci if (ssp->type == PXA3xx_SSP) 2788c2ecf20Sopenharmony_ci pxa_ssp_write_reg(ssp, SSACDD, 0); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci switch (freq) { 2818c2ecf20Sopenharmony_ci case 5622000: 2828c2ecf20Sopenharmony_ci break; 2838c2ecf20Sopenharmony_ci case 11345000: 2848c2ecf20Sopenharmony_ci ssacd |= (0x1 << 4); 2858c2ecf20Sopenharmony_ci break; 2868c2ecf20Sopenharmony_ci case 12235000: 2878c2ecf20Sopenharmony_ci ssacd |= (0x2 << 4); 2888c2ecf20Sopenharmony_ci break; 2898c2ecf20Sopenharmony_ci case 14857000: 2908c2ecf20Sopenharmony_ci ssacd |= (0x3 << 4); 2918c2ecf20Sopenharmony_ci break; 2928c2ecf20Sopenharmony_ci case 32842000: 2938c2ecf20Sopenharmony_ci ssacd |= (0x4 << 4); 2948c2ecf20Sopenharmony_ci break; 2958c2ecf20Sopenharmony_ci case 48000000: 2968c2ecf20Sopenharmony_ci ssacd |= (0x5 << 4); 2978c2ecf20Sopenharmony_ci break; 2988c2ecf20Sopenharmony_ci case 0: 2998c2ecf20Sopenharmony_ci /* Disable */ 3008c2ecf20Sopenharmony_ci break; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci default: 3038c2ecf20Sopenharmony_ci /* PXA3xx has a clock ditherer which can be used to generate 3048c2ecf20Sopenharmony_ci * a wider range of frequencies - calculate a value for it. 3058c2ecf20Sopenharmony_ci */ 3068c2ecf20Sopenharmony_ci if (ssp->type == PXA3xx_SSP) { 3078c2ecf20Sopenharmony_ci u32 val; 3088c2ecf20Sopenharmony_ci u64 tmp = 19968; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci tmp *= 1000000; 3118c2ecf20Sopenharmony_ci do_div(tmp, freq); 3128c2ecf20Sopenharmony_ci val = tmp; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci val = (val << 16) | 64; 3158c2ecf20Sopenharmony_ci pxa_ssp_write_reg(ssp, SSACDD, val); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci ssacd |= (0x6 << 4); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci dev_dbg(ssp->dev, 3208c2ecf20Sopenharmony_ci "Using SSACDD %x to supply %uHz\n", 3218c2ecf20Sopenharmony_ci val, freq); 3228c2ecf20Sopenharmony_ci break; 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci return -EINVAL; 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci pxa_ssp_write_reg(ssp, SSACD, ssacd); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci return 0; 3318c2ecf20Sopenharmony_ci} 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci/* 3348c2ecf20Sopenharmony_ci * Set the active slots in TDM/Network mode 3358c2ecf20Sopenharmony_ci */ 3368c2ecf20Sopenharmony_cistatic int pxa_ssp_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, 3378c2ecf20Sopenharmony_ci unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); 3408c2ecf20Sopenharmony_ci struct ssp_device *ssp = priv->ssp; 3418c2ecf20Sopenharmony_ci u32 sscr0; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci sscr0 = pxa_ssp_read_reg(ssp, SSCR0); 3448c2ecf20Sopenharmony_ci sscr0 &= ~(SSCR0_MOD | SSCR0_SlotsPerFrm(8) | SSCR0_EDSS | SSCR0_DSS); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci /* set slot width */ 3478c2ecf20Sopenharmony_ci if (slot_width > 16) 3488c2ecf20Sopenharmony_ci sscr0 |= SSCR0_EDSS | SSCR0_DataSize(slot_width - 16); 3498c2ecf20Sopenharmony_ci else 3508c2ecf20Sopenharmony_ci sscr0 |= SSCR0_DataSize(slot_width); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci if (slots > 1) { 3538c2ecf20Sopenharmony_ci /* enable network mode */ 3548c2ecf20Sopenharmony_ci sscr0 |= SSCR0_MOD; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci /* set number of active slots */ 3578c2ecf20Sopenharmony_ci sscr0 |= SSCR0_SlotsPerFrm(slots); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci /* set active slot mask */ 3608c2ecf20Sopenharmony_ci pxa_ssp_write_reg(ssp, SSTSA, tx_mask); 3618c2ecf20Sopenharmony_ci pxa_ssp_write_reg(ssp, SSRSA, rx_mask); 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci pxa_ssp_write_reg(ssp, SSCR0, sscr0); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci return 0; 3668c2ecf20Sopenharmony_ci} 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci/* 3698c2ecf20Sopenharmony_ci * Tristate the SSP DAI lines 3708c2ecf20Sopenharmony_ci */ 3718c2ecf20Sopenharmony_cistatic int pxa_ssp_set_dai_tristate(struct snd_soc_dai *cpu_dai, 3728c2ecf20Sopenharmony_ci int tristate) 3738c2ecf20Sopenharmony_ci{ 3748c2ecf20Sopenharmony_ci struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); 3758c2ecf20Sopenharmony_ci struct ssp_device *ssp = priv->ssp; 3768c2ecf20Sopenharmony_ci u32 sscr1; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci sscr1 = pxa_ssp_read_reg(ssp, SSCR1); 3798c2ecf20Sopenharmony_ci if (tristate) 3808c2ecf20Sopenharmony_ci sscr1 &= ~SSCR1_TTE; 3818c2ecf20Sopenharmony_ci else 3828c2ecf20Sopenharmony_ci sscr1 |= SSCR1_TTE; 3838c2ecf20Sopenharmony_ci pxa_ssp_write_reg(ssp, SSCR1, sscr1); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci return 0; 3868c2ecf20Sopenharmony_ci} 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_cistatic int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai, 3898c2ecf20Sopenharmony_ci unsigned int fmt) 3908c2ecf20Sopenharmony_ci{ 3918c2ecf20Sopenharmony_ci struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 3948c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBM_CFM: 3958c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBM_CFS: 3968c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBS_CFS: 3978c2ecf20Sopenharmony_ci break; 3988c2ecf20Sopenharmony_ci default: 3998c2ecf20Sopenharmony_ci return -EINVAL; 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 4038c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_NB_NF: 4048c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_NB_IF: 4058c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_IB_IF: 4068c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_IB_NF: 4078c2ecf20Sopenharmony_ci break; 4088c2ecf20Sopenharmony_ci default: 4098c2ecf20Sopenharmony_ci return -EINVAL; 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 4138c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_I2S: 4148c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_DSP_A: 4158c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_DSP_B: 4168c2ecf20Sopenharmony_ci break; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci default: 4198c2ecf20Sopenharmony_ci return -EINVAL; 4208c2ecf20Sopenharmony_ci } 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci /* Settings will be applied in hw_params() */ 4238c2ecf20Sopenharmony_ci priv->dai_fmt = fmt; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci return 0; 4268c2ecf20Sopenharmony_ci} 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci/* 4298c2ecf20Sopenharmony_ci * Set up the SSP DAI format. 4308c2ecf20Sopenharmony_ci * The SSP Port must be inactive before calling this function as the 4318c2ecf20Sopenharmony_ci * physical interface format is changed. 4328c2ecf20Sopenharmony_ci */ 4338c2ecf20Sopenharmony_cistatic int pxa_ssp_configure_dai_fmt(struct ssp_priv *priv) 4348c2ecf20Sopenharmony_ci{ 4358c2ecf20Sopenharmony_ci struct ssp_device *ssp = priv->ssp; 4368c2ecf20Sopenharmony_ci u32 sscr0, sscr1, sspsp, scfr; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci /* check if we need to change anything at all */ 4398c2ecf20Sopenharmony_ci if (priv->configured_dai_fmt == priv->dai_fmt) 4408c2ecf20Sopenharmony_ci return 0; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci /* reset port settings */ 4438c2ecf20Sopenharmony_ci sscr0 = pxa_ssp_read_reg(ssp, SSCR0) & 4448c2ecf20Sopenharmony_ci ~(SSCR0_PSP | SSCR0_MOD); 4458c2ecf20Sopenharmony_ci sscr1 = pxa_ssp_read_reg(ssp, SSCR1) & 4468c2ecf20Sopenharmony_ci ~(SSCR1_SCLKDIR | SSCR1_SFRMDIR | SSCR1_SCFR | 4478c2ecf20Sopenharmony_ci SSCR1_RWOT | SSCR1_TRAIL | SSCR1_TFT | SSCR1_RFT); 4488c2ecf20Sopenharmony_ci sspsp = pxa_ssp_read_reg(ssp, SSPSP) & 4498c2ecf20Sopenharmony_ci ~(SSPSP_SFRMP | SSPSP_SCMODE(3)); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci sscr1 |= SSCR1_RxTresh(8) | SSCR1_TxTresh(7); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci switch (priv->dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) { 4548c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBM_CFM: 4558c2ecf20Sopenharmony_ci sscr1 |= SSCR1_SCLKDIR | SSCR1_SFRMDIR | SSCR1_SCFR; 4568c2ecf20Sopenharmony_ci break; 4578c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBM_CFS: 4588c2ecf20Sopenharmony_ci sscr1 |= SSCR1_SCLKDIR | SSCR1_SCFR; 4598c2ecf20Sopenharmony_ci break; 4608c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBS_CFS: 4618c2ecf20Sopenharmony_ci break; 4628c2ecf20Sopenharmony_ci default: 4638c2ecf20Sopenharmony_ci return -EINVAL; 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci switch (priv->dai_fmt & SND_SOC_DAIFMT_INV_MASK) { 4678c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_NB_NF: 4688c2ecf20Sopenharmony_ci sspsp |= SSPSP_SFRMP; 4698c2ecf20Sopenharmony_ci break; 4708c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_NB_IF: 4718c2ecf20Sopenharmony_ci break; 4728c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_IB_IF: 4738c2ecf20Sopenharmony_ci sspsp |= SSPSP_SCMODE(2); 4748c2ecf20Sopenharmony_ci break; 4758c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_IB_NF: 4768c2ecf20Sopenharmony_ci sspsp |= SSPSP_SCMODE(2) | SSPSP_SFRMP; 4778c2ecf20Sopenharmony_ci break; 4788c2ecf20Sopenharmony_ci default: 4798c2ecf20Sopenharmony_ci return -EINVAL; 4808c2ecf20Sopenharmony_ci } 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci switch (priv->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 4838c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_I2S: 4848c2ecf20Sopenharmony_ci sscr0 |= SSCR0_PSP; 4858c2ecf20Sopenharmony_ci sscr1 |= SSCR1_RWOT | SSCR1_TRAIL; 4868c2ecf20Sopenharmony_ci /* See hw_params() */ 4878c2ecf20Sopenharmony_ci break; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_DSP_A: 4908c2ecf20Sopenharmony_ci sspsp |= SSPSP_FSRT; 4918c2ecf20Sopenharmony_ci fallthrough; 4928c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_DSP_B: 4938c2ecf20Sopenharmony_ci sscr0 |= SSCR0_MOD | SSCR0_PSP; 4948c2ecf20Sopenharmony_ci sscr1 |= SSCR1_TRAIL | SSCR1_RWOT; 4958c2ecf20Sopenharmony_ci break; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci default: 4988c2ecf20Sopenharmony_ci return -EINVAL; 4998c2ecf20Sopenharmony_ci } 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci pxa_ssp_write_reg(ssp, SSCR0, sscr0); 5028c2ecf20Sopenharmony_ci pxa_ssp_write_reg(ssp, SSCR1, sscr1); 5038c2ecf20Sopenharmony_ci pxa_ssp_write_reg(ssp, SSPSP, sspsp); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci switch (priv->dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) { 5068c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBM_CFM: 5078c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBM_CFS: 5088c2ecf20Sopenharmony_ci scfr = pxa_ssp_read_reg(ssp, SSCR1) | SSCR1_SCFR; 5098c2ecf20Sopenharmony_ci pxa_ssp_write_reg(ssp, SSCR1, scfr); 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci while (pxa_ssp_read_reg(ssp, SSSR) & SSSR_BSY) 5128c2ecf20Sopenharmony_ci cpu_relax(); 5138c2ecf20Sopenharmony_ci break; 5148c2ecf20Sopenharmony_ci } 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci dump_registers(ssp); 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci /* Since we are configuring the timings for the format by hand 5198c2ecf20Sopenharmony_ci * we have to defer some things until hw_params() where we 5208c2ecf20Sopenharmony_ci * know parameters like the sample size. 5218c2ecf20Sopenharmony_ci */ 5228c2ecf20Sopenharmony_ci priv->configured_dai_fmt = priv->dai_fmt; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci return 0; 5258c2ecf20Sopenharmony_ci} 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_cistruct pxa_ssp_clock_mode { 5288c2ecf20Sopenharmony_ci int rate; 5298c2ecf20Sopenharmony_ci int pll; 5308c2ecf20Sopenharmony_ci u8 acds; 5318c2ecf20Sopenharmony_ci u8 scdb; 5328c2ecf20Sopenharmony_ci}; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_cistatic const struct pxa_ssp_clock_mode pxa_ssp_clock_modes[] = { 5358c2ecf20Sopenharmony_ci { .rate = 8000, .pll = 32842000, .acds = SSACD_ACDS_32, .scdb = SSACD_SCDB_4X }, 5368c2ecf20Sopenharmony_ci { .rate = 11025, .pll = 5622000, .acds = SSACD_ACDS_4, .scdb = SSACD_SCDB_4X }, 5378c2ecf20Sopenharmony_ci { .rate = 16000, .pll = 32842000, .acds = SSACD_ACDS_16, .scdb = SSACD_SCDB_4X }, 5388c2ecf20Sopenharmony_ci { .rate = 22050, .pll = 5622000, .acds = SSACD_ACDS_2, .scdb = SSACD_SCDB_4X }, 5398c2ecf20Sopenharmony_ci { .rate = 44100, .pll = 11345000, .acds = SSACD_ACDS_2, .scdb = SSACD_SCDB_4X }, 5408c2ecf20Sopenharmony_ci { .rate = 48000, .pll = 12235000, .acds = SSACD_ACDS_2, .scdb = SSACD_SCDB_4X }, 5418c2ecf20Sopenharmony_ci { .rate = 96000, .pll = 12235000, .acds = SSACD_ACDS_4, .scdb = SSACD_SCDB_1X }, 5428c2ecf20Sopenharmony_ci {} 5438c2ecf20Sopenharmony_ci}; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci/* 5468c2ecf20Sopenharmony_ci * Set the SSP audio DMA parameters and sample size. 5478c2ecf20Sopenharmony_ci * Can be called multiple times by oss emulation. 5488c2ecf20Sopenharmony_ci */ 5498c2ecf20Sopenharmony_cistatic int pxa_ssp_hw_params(struct snd_pcm_substream *substream, 5508c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params, 5518c2ecf20Sopenharmony_ci struct snd_soc_dai *cpu_dai) 5528c2ecf20Sopenharmony_ci{ 5538c2ecf20Sopenharmony_ci struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); 5548c2ecf20Sopenharmony_ci struct ssp_device *ssp = priv->ssp; 5558c2ecf20Sopenharmony_ci int chn = params_channels(params); 5568c2ecf20Sopenharmony_ci u32 sscr0, sspsp; 5578c2ecf20Sopenharmony_ci int width = snd_pcm_format_physical_width(params_format(params)); 5588c2ecf20Sopenharmony_ci int ttsa = pxa_ssp_read_reg(ssp, SSTSA) & 0xf; 5598c2ecf20Sopenharmony_ci struct snd_dmaengine_dai_dma_data *dma_data; 5608c2ecf20Sopenharmony_ci int rate = params_rate(params); 5618c2ecf20Sopenharmony_ci int bclk = rate * chn * (width / 8); 5628c2ecf20Sopenharmony_ci int ret; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci dma_data = snd_soc_dai_get_dma_data(cpu_dai, substream); 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci /* Network mode with one active slot (ttsa == 1) can be used 5678c2ecf20Sopenharmony_ci * to force 16-bit frame width on the wire (for S16_LE), even 5688c2ecf20Sopenharmony_ci * with two channels. Use 16-bit DMA transfers for this case. 5698c2ecf20Sopenharmony_ci */ 5708c2ecf20Sopenharmony_ci pxa_ssp_set_dma_params(ssp, 5718c2ecf20Sopenharmony_ci ((chn == 2) && (ttsa != 1)) || (width == 32), 5728c2ecf20Sopenharmony_ci substream->stream == SNDRV_PCM_STREAM_PLAYBACK, dma_data); 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci /* we can only change the settings if the port is not in use */ 5758c2ecf20Sopenharmony_ci if (pxa_ssp_read_reg(ssp, SSCR0) & SSCR0_SSE) 5768c2ecf20Sopenharmony_ci return 0; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci ret = pxa_ssp_configure_dai_fmt(priv); 5798c2ecf20Sopenharmony_ci if (ret < 0) 5808c2ecf20Sopenharmony_ci return ret; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci /* clear selected SSP bits */ 5838c2ecf20Sopenharmony_ci sscr0 = pxa_ssp_read_reg(ssp, SSCR0) & ~(SSCR0_DSS | SSCR0_EDSS); 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci /* bit size */ 5868c2ecf20Sopenharmony_ci switch (params_format(params)) { 5878c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_LE: 5888c2ecf20Sopenharmony_ci if (ssp->type == PXA3xx_SSP) 5898c2ecf20Sopenharmony_ci sscr0 |= SSCR0_FPCKE; 5908c2ecf20Sopenharmony_ci sscr0 |= SSCR0_DataSize(16); 5918c2ecf20Sopenharmony_ci break; 5928c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S24_LE: 5938c2ecf20Sopenharmony_ci sscr0 |= (SSCR0_EDSS | SSCR0_DataSize(8)); 5948c2ecf20Sopenharmony_ci break; 5958c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S32_LE: 5968c2ecf20Sopenharmony_ci sscr0 |= (SSCR0_EDSS | SSCR0_DataSize(16)); 5978c2ecf20Sopenharmony_ci break; 5988c2ecf20Sopenharmony_ci } 5998c2ecf20Sopenharmony_ci pxa_ssp_write_reg(ssp, SSCR0, sscr0); 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci if (sscr0 & SSCR0_ACS) { 6028c2ecf20Sopenharmony_ci ret = pxa_ssp_set_pll(priv, bclk); 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci /* 6058c2ecf20Sopenharmony_ci * If we were able to generate the bclk directly, 6068c2ecf20Sopenharmony_ci * all is fine. Otherwise, look up the closest rate 6078c2ecf20Sopenharmony_ci * from the table and also set the dividers. 6088c2ecf20Sopenharmony_ci */ 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci if (ret < 0) { 6118c2ecf20Sopenharmony_ci const struct pxa_ssp_clock_mode *m; 6128c2ecf20Sopenharmony_ci int ssacd, acds; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci for (m = pxa_ssp_clock_modes; m->rate; m++) { 6158c2ecf20Sopenharmony_ci if (m->rate == rate) 6168c2ecf20Sopenharmony_ci break; 6178c2ecf20Sopenharmony_ci } 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci if (!m->rate) 6208c2ecf20Sopenharmony_ci return -EINVAL; 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci acds = m->acds; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci /* The values in the table are for 16 bits */ 6258c2ecf20Sopenharmony_ci if (width == 32) 6268c2ecf20Sopenharmony_ci acds--; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci ret = pxa_ssp_set_pll(priv, bclk); 6298c2ecf20Sopenharmony_ci if (ret < 0) 6308c2ecf20Sopenharmony_ci return ret; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci ssacd = pxa_ssp_read_reg(ssp, SSACD); 6338c2ecf20Sopenharmony_ci ssacd &= ~(SSACD_ACDS(7) | SSACD_SCDB_1X); 6348c2ecf20Sopenharmony_ci ssacd |= SSACD_ACDS(m->acds); 6358c2ecf20Sopenharmony_ci ssacd |= m->scdb; 6368c2ecf20Sopenharmony_ci pxa_ssp_write_reg(ssp, SSACD, ssacd); 6378c2ecf20Sopenharmony_ci } 6388c2ecf20Sopenharmony_ci } else if (sscr0 & SSCR0_ECS) { 6398c2ecf20Sopenharmony_ci /* 6408c2ecf20Sopenharmony_ci * For setups with external clocking, the PLL and its diviers 6418c2ecf20Sopenharmony_ci * are not active. Instead, the SCR bits in SSCR0 can be used 6428c2ecf20Sopenharmony_ci * to divide the clock. 6438c2ecf20Sopenharmony_ci */ 6448c2ecf20Sopenharmony_ci pxa_ssp_set_scr(ssp, bclk / rate); 6458c2ecf20Sopenharmony_ci } 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci switch (priv->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 6488c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_I2S: 6498c2ecf20Sopenharmony_ci sspsp = pxa_ssp_read_reg(ssp, SSPSP); 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci if (((priv->sysclk / bclk) == 64) && (width == 16)) { 6528c2ecf20Sopenharmony_ci /* This is a special case where the bitclk is 64fs 6538c2ecf20Sopenharmony_ci * and we're not dealing with 2*32 bits of audio 6548c2ecf20Sopenharmony_ci * samples. 6558c2ecf20Sopenharmony_ci * 6568c2ecf20Sopenharmony_ci * The SSP values used for that are all found out by 6578c2ecf20Sopenharmony_ci * trying and failing a lot; some of the registers 6588c2ecf20Sopenharmony_ci * needed for that mode are only available on PXA3xx. 6598c2ecf20Sopenharmony_ci */ 6608c2ecf20Sopenharmony_ci if (ssp->type != PXA3xx_SSP) 6618c2ecf20Sopenharmony_ci return -EINVAL; 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci sspsp |= SSPSP_SFRMWDTH(width * 2); 6648c2ecf20Sopenharmony_ci sspsp |= SSPSP_SFRMDLY(width * 4); 6658c2ecf20Sopenharmony_ci sspsp |= SSPSP_EDMYSTOP(3); 6668c2ecf20Sopenharmony_ci sspsp |= SSPSP_DMYSTOP(3); 6678c2ecf20Sopenharmony_ci sspsp |= SSPSP_DMYSTRT(1); 6688c2ecf20Sopenharmony_ci } else { 6698c2ecf20Sopenharmony_ci /* The frame width is the width the LRCLK is 6708c2ecf20Sopenharmony_ci * asserted for; the delay is expressed in 6718c2ecf20Sopenharmony_ci * half cycle units. We need the extra cycle 6728c2ecf20Sopenharmony_ci * because the data starts clocking out one BCLK 6738c2ecf20Sopenharmony_ci * after LRCLK changes polarity. 6748c2ecf20Sopenharmony_ci */ 6758c2ecf20Sopenharmony_ci sspsp |= SSPSP_SFRMWDTH(width + 1); 6768c2ecf20Sopenharmony_ci sspsp |= SSPSP_SFRMDLY((width + 1) * 2); 6778c2ecf20Sopenharmony_ci sspsp |= SSPSP_DMYSTRT(1); 6788c2ecf20Sopenharmony_ci } 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci pxa_ssp_write_reg(ssp, SSPSP, sspsp); 6818c2ecf20Sopenharmony_ci break; 6828c2ecf20Sopenharmony_ci default: 6838c2ecf20Sopenharmony_ci break; 6848c2ecf20Sopenharmony_ci } 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci /* When we use a network mode, we always require TDM slots 6878c2ecf20Sopenharmony_ci * - complain loudly and fail if they've not been set up yet. 6888c2ecf20Sopenharmony_ci */ 6898c2ecf20Sopenharmony_ci if ((sscr0 & SSCR0_MOD) && !ttsa) { 6908c2ecf20Sopenharmony_ci dev_err(ssp->dev, "No TDM timeslot configured\n"); 6918c2ecf20Sopenharmony_ci return -EINVAL; 6928c2ecf20Sopenharmony_ci } 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci dump_registers(ssp); 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci return 0; 6978c2ecf20Sopenharmony_ci} 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_cistatic void pxa_ssp_set_running_bit(struct snd_pcm_substream *substream, 7008c2ecf20Sopenharmony_ci struct ssp_device *ssp, int value) 7018c2ecf20Sopenharmony_ci{ 7028c2ecf20Sopenharmony_ci uint32_t sscr0 = pxa_ssp_read_reg(ssp, SSCR0); 7038c2ecf20Sopenharmony_ci uint32_t sscr1 = pxa_ssp_read_reg(ssp, SSCR1); 7048c2ecf20Sopenharmony_ci uint32_t sspsp = pxa_ssp_read_reg(ssp, SSPSP); 7058c2ecf20Sopenharmony_ci uint32_t sssr = pxa_ssp_read_reg(ssp, SSSR); 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci if (value && (sscr0 & SSCR0_SSE)) 7088c2ecf20Sopenharmony_ci pxa_ssp_write_reg(ssp, SSCR0, sscr0 & ~SSCR0_SSE); 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 7118c2ecf20Sopenharmony_ci if (value) 7128c2ecf20Sopenharmony_ci sscr1 |= SSCR1_TSRE; 7138c2ecf20Sopenharmony_ci else 7148c2ecf20Sopenharmony_ci sscr1 &= ~SSCR1_TSRE; 7158c2ecf20Sopenharmony_ci } else { 7168c2ecf20Sopenharmony_ci if (value) 7178c2ecf20Sopenharmony_ci sscr1 |= SSCR1_RSRE; 7188c2ecf20Sopenharmony_ci else 7198c2ecf20Sopenharmony_ci sscr1 &= ~SSCR1_RSRE; 7208c2ecf20Sopenharmony_ci } 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci pxa_ssp_write_reg(ssp, SSCR1, sscr1); 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci if (value) { 7258c2ecf20Sopenharmony_ci pxa_ssp_write_reg(ssp, SSSR, sssr); 7268c2ecf20Sopenharmony_ci pxa_ssp_write_reg(ssp, SSPSP, sspsp); 7278c2ecf20Sopenharmony_ci pxa_ssp_write_reg(ssp, SSCR0, sscr0 | SSCR0_SSE); 7288c2ecf20Sopenharmony_ci } 7298c2ecf20Sopenharmony_ci} 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_cistatic int pxa_ssp_trigger(struct snd_pcm_substream *substream, int cmd, 7328c2ecf20Sopenharmony_ci struct snd_soc_dai *cpu_dai) 7338c2ecf20Sopenharmony_ci{ 7348c2ecf20Sopenharmony_ci int ret = 0; 7358c2ecf20Sopenharmony_ci struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); 7368c2ecf20Sopenharmony_ci struct ssp_device *ssp = priv->ssp; 7378c2ecf20Sopenharmony_ci int val; 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci switch (cmd) { 7408c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 7418c2ecf20Sopenharmony_ci pxa_ssp_enable(ssp); 7428c2ecf20Sopenharmony_ci break; 7438c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 7448c2ecf20Sopenharmony_ci pxa_ssp_set_running_bit(substream, ssp, 1); 7458c2ecf20Sopenharmony_ci val = pxa_ssp_read_reg(ssp, SSSR); 7468c2ecf20Sopenharmony_ci pxa_ssp_write_reg(ssp, SSSR, val); 7478c2ecf20Sopenharmony_ci break; 7488c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 7498c2ecf20Sopenharmony_ci pxa_ssp_set_running_bit(substream, ssp, 1); 7508c2ecf20Sopenharmony_ci break; 7518c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 7528c2ecf20Sopenharmony_ci pxa_ssp_set_running_bit(substream, ssp, 0); 7538c2ecf20Sopenharmony_ci break; 7548c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 7558c2ecf20Sopenharmony_ci pxa_ssp_disable(ssp); 7568c2ecf20Sopenharmony_ci break; 7578c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 7588c2ecf20Sopenharmony_ci pxa_ssp_set_running_bit(substream, ssp, 0); 7598c2ecf20Sopenharmony_ci break; 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci default: 7628c2ecf20Sopenharmony_ci ret = -EINVAL; 7638c2ecf20Sopenharmony_ci } 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci dump_registers(ssp); 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci return ret; 7688c2ecf20Sopenharmony_ci} 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_cistatic int pxa_ssp_probe(struct snd_soc_dai *dai) 7718c2ecf20Sopenharmony_ci{ 7728c2ecf20Sopenharmony_ci struct device *dev = dai->dev; 7738c2ecf20Sopenharmony_ci struct ssp_priv *priv; 7748c2ecf20Sopenharmony_ci int ret; 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci priv = kzalloc(sizeof(struct ssp_priv), GFP_KERNEL); 7778c2ecf20Sopenharmony_ci if (!priv) 7788c2ecf20Sopenharmony_ci return -ENOMEM; 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci if (dev->of_node) { 7818c2ecf20Sopenharmony_ci struct device_node *ssp_handle; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci ssp_handle = of_parse_phandle(dev->of_node, "port", 0); 7848c2ecf20Sopenharmony_ci if (!ssp_handle) { 7858c2ecf20Sopenharmony_ci dev_err(dev, "unable to get 'port' phandle\n"); 7868c2ecf20Sopenharmony_ci ret = -ENODEV; 7878c2ecf20Sopenharmony_ci goto err_priv; 7888c2ecf20Sopenharmony_ci } 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci priv->ssp = pxa_ssp_request_of(ssp_handle, "SoC audio"); 7918c2ecf20Sopenharmony_ci if (priv->ssp == NULL) { 7928c2ecf20Sopenharmony_ci ret = -ENODEV; 7938c2ecf20Sopenharmony_ci goto err_priv; 7948c2ecf20Sopenharmony_ci } 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci priv->extclk = devm_clk_get(dev, "extclk"); 7978c2ecf20Sopenharmony_ci if (IS_ERR(priv->extclk)) { 7988c2ecf20Sopenharmony_ci ret = PTR_ERR(priv->extclk); 7998c2ecf20Sopenharmony_ci if (ret == -EPROBE_DEFER) 8008c2ecf20Sopenharmony_ci goto err_priv; 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci priv->extclk = NULL; 8038c2ecf20Sopenharmony_ci } 8048c2ecf20Sopenharmony_ci } else { 8058c2ecf20Sopenharmony_ci priv->ssp = pxa_ssp_request(dai->id + 1, "SoC audio"); 8068c2ecf20Sopenharmony_ci if (priv->ssp == NULL) { 8078c2ecf20Sopenharmony_ci ret = -ENODEV; 8088c2ecf20Sopenharmony_ci goto err_priv; 8098c2ecf20Sopenharmony_ci } 8108c2ecf20Sopenharmony_ci } 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci priv->dai_fmt = (unsigned int) -1; 8138c2ecf20Sopenharmony_ci snd_soc_dai_set_drvdata(dai, priv); 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci return 0; 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_cierr_priv: 8188c2ecf20Sopenharmony_ci kfree(priv); 8198c2ecf20Sopenharmony_ci return ret; 8208c2ecf20Sopenharmony_ci} 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_cistatic int pxa_ssp_remove(struct snd_soc_dai *dai) 8238c2ecf20Sopenharmony_ci{ 8248c2ecf20Sopenharmony_ci struct ssp_priv *priv = snd_soc_dai_get_drvdata(dai); 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci pxa_ssp_free(priv->ssp); 8278c2ecf20Sopenharmony_ci kfree(priv); 8288c2ecf20Sopenharmony_ci return 0; 8298c2ecf20Sopenharmony_ci} 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci#define PXA_SSP_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ 8328c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \ 8338c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ 8348c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | \ 8358c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci#define PXA_SSP_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE) 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops pxa_ssp_dai_ops = { 8408c2ecf20Sopenharmony_ci .startup = pxa_ssp_startup, 8418c2ecf20Sopenharmony_ci .shutdown = pxa_ssp_shutdown, 8428c2ecf20Sopenharmony_ci .trigger = pxa_ssp_trigger, 8438c2ecf20Sopenharmony_ci .hw_params = pxa_ssp_hw_params, 8448c2ecf20Sopenharmony_ci .set_sysclk = pxa_ssp_set_dai_sysclk, 8458c2ecf20Sopenharmony_ci .set_fmt = pxa_ssp_set_dai_fmt, 8468c2ecf20Sopenharmony_ci .set_tdm_slot = pxa_ssp_set_dai_tdm_slot, 8478c2ecf20Sopenharmony_ci .set_tristate = pxa_ssp_set_dai_tristate, 8488c2ecf20Sopenharmony_ci}; 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_cistatic struct snd_soc_dai_driver pxa_ssp_dai = { 8518c2ecf20Sopenharmony_ci .probe = pxa_ssp_probe, 8528c2ecf20Sopenharmony_ci .remove = pxa_ssp_remove, 8538c2ecf20Sopenharmony_ci .playback = { 8548c2ecf20Sopenharmony_ci .channels_min = 1, 8558c2ecf20Sopenharmony_ci .channels_max = 8, 8568c2ecf20Sopenharmony_ci .rates = PXA_SSP_RATES, 8578c2ecf20Sopenharmony_ci .formats = PXA_SSP_FORMATS, 8588c2ecf20Sopenharmony_ci }, 8598c2ecf20Sopenharmony_ci .capture = { 8608c2ecf20Sopenharmony_ci .channels_min = 1, 8618c2ecf20Sopenharmony_ci .channels_max = 8, 8628c2ecf20Sopenharmony_ci .rates = PXA_SSP_RATES, 8638c2ecf20Sopenharmony_ci .formats = PXA_SSP_FORMATS, 8648c2ecf20Sopenharmony_ci }, 8658c2ecf20Sopenharmony_ci .ops = &pxa_ssp_dai_ops, 8668c2ecf20Sopenharmony_ci}; 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver pxa_ssp_component = { 8698c2ecf20Sopenharmony_ci .name = "pxa-ssp", 8708c2ecf20Sopenharmony_ci .pcm_construct = pxa2xx_soc_pcm_new, 8718c2ecf20Sopenharmony_ci .pcm_destruct = pxa2xx_soc_pcm_free, 8728c2ecf20Sopenharmony_ci .open = pxa2xx_soc_pcm_open, 8738c2ecf20Sopenharmony_ci .close = pxa2xx_soc_pcm_close, 8748c2ecf20Sopenharmony_ci .hw_params = pxa2xx_soc_pcm_hw_params, 8758c2ecf20Sopenharmony_ci .hw_free = pxa2xx_soc_pcm_hw_free, 8768c2ecf20Sopenharmony_ci .prepare = pxa2xx_soc_pcm_prepare, 8778c2ecf20Sopenharmony_ci .trigger = pxa2xx_soc_pcm_trigger, 8788c2ecf20Sopenharmony_ci .pointer = pxa2xx_soc_pcm_pointer, 8798c2ecf20Sopenharmony_ci .mmap = pxa2xx_soc_pcm_mmap, 8808c2ecf20Sopenharmony_ci .suspend = pxa_ssp_suspend, 8818c2ecf20Sopenharmony_ci .resume = pxa_ssp_resume, 8828c2ecf20Sopenharmony_ci}; 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 8858c2ecf20Sopenharmony_cistatic const struct of_device_id pxa_ssp_of_ids[] = { 8868c2ecf20Sopenharmony_ci { .compatible = "mrvl,pxa-ssp-dai" }, 8878c2ecf20Sopenharmony_ci {} 8888c2ecf20Sopenharmony_ci}; 8898c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, pxa_ssp_of_ids); 8908c2ecf20Sopenharmony_ci#endif 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_cistatic int asoc_ssp_probe(struct platform_device *pdev) 8938c2ecf20Sopenharmony_ci{ 8948c2ecf20Sopenharmony_ci return devm_snd_soc_register_component(&pdev->dev, &pxa_ssp_component, 8958c2ecf20Sopenharmony_ci &pxa_ssp_dai, 1); 8968c2ecf20Sopenharmony_ci} 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_cistatic struct platform_driver asoc_ssp_driver = { 8998c2ecf20Sopenharmony_ci .driver = { 9008c2ecf20Sopenharmony_ci .name = "pxa-ssp-dai", 9018c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(pxa_ssp_of_ids), 9028c2ecf20Sopenharmony_ci }, 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci .probe = asoc_ssp_probe, 9058c2ecf20Sopenharmony_ci}; 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_cimodule_platform_driver(asoc_ssp_driver); 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci/* Module information */ 9108c2ecf20Sopenharmony_ciMODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 9118c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("PXA SSP/PCM SoC Interface"); 9128c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 9138c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:pxa-ssp-dai"); 914