18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * atmel_ssc_dai.c -- ALSA SoC ATMEL SSC Audio Layer Platform driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2005 SAN People 68c2ecf20Sopenharmony_ci * Copyright (C) 2008 Atmel 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Author: Sedji Gaouaou <sedji.gaouaou@atmel.com> 98c2ecf20Sopenharmony_ci * ATMEL CORP. 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Based on at91-ssc.c by 128c2ecf20Sopenharmony_ci * Frank Mandarino <fmandarino@endrelia.com> 138c2ecf20Sopenharmony_ci * Based on pxa2xx Platform drivers by 148c2ecf20Sopenharmony_ci * Liam Girdwood <lrg@slimlogic.co.uk> 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <linux/init.h> 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 208c2ecf20Sopenharmony_ci#include <linux/device.h> 218c2ecf20Sopenharmony_ci#include <linux/delay.h> 228c2ecf20Sopenharmony_ci#include <linux/clk.h> 238c2ecf20Sopenharmony_ci#include <linux/atmel_pdc.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include <linux/atmel-ssc.h> 268c2ecf20Sopenharmony_ci#include <sound/core.h> 278c2ecf20Sopenharmony_ci#include <sound/pcm.h> 288c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 298c2ecf20Sopenharmony_ci#include <sound/initval.h> 308c2ecf20Sopenharmony_ci#include <sound/soc.h> 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#include "atmel-pcm.h" 338c2ecf20Sopenharmony_ci#include "atmel_ssc_dai.h" 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define NUM_SSC_DEVICES 3 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/* 398c2ecf20Sopenharmony_ci * SSC PDC registers required by the PCM DMA engine. 408c2ecf20Sopenharmony_ci */ 418c2ecf20Sopenharmony_cistatic struct atmel_pdc_regs pdc_tx_reg = { 428c2ecf20Sopenharmony_ci .xpr = ATMEL_PDC_TPR, 438c2ecf20Sopenharmony_ci .xcr = ATMEL_PDC_TCR, 448c2ecf20Sopenharmony_ci .xnpr = ATMEL_PDC_TNPR, 458c2ecf20Sopenharmony_ci .xncr = ATMEL_PDC_TNCR, 468c2ecf20Sopenharmony_ci}; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic struct atmel_pdc_regs pdc_rx_reg = { 498c2ecf20Sopenharmony_ci .xpr = ATMEL_PDC_RPR, 508c2ecf20Sopenharmony_ci .xcr = ATMEL_PDC_RCR, 518c2ecf20Sopenharmony_ci .xnpr = ATMEL_PDC_RNPR, 528c2ecf20Sopenharmony_ci .xncr = ATMEL_PDC_RNCR, 538c2ecf20Sopenharmony_ci}; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/* 568c2ecf20Sopenharmony_ci * SSC & PDC status bits for transmit and receive. 578c2ecf20Sopenharmony_ci */ 588c2ecf20Sopenharmony_cistatic struct atmel_ssc_mask ssc_tx_mask = { 598c2ecf20Sopenharmony_ci .ssc_enable = SSC_BIT(CR_TXEN), 608c2ecf20Sopenharmony_ci .ssc_disable = SSC_BIT(CR_TXDIS), 618c2ecf20Sopenharmony_ci .ssc_endx = SSC_BIT(SR_ENDTX), 628c2ecf20Sopenharmony_ci .ssc_endbuf = SSC_BIT(SR_TXBUFE), 638c2ecf20Sopenharmony_ci .ssc_error = SSC_BIT(SR_OVRUN), 648c2ecf20Sopenharmony_ci .pdc_enable = ATMEL_PDC_TXTEN, 658c2ecf20Sopenharmony_ci .pdc_disable = ATMEL_PDC_TXTDIS, 668c2ecf20Sopenharmony_ci}; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic struct atmel_ssc_mask ssc_rx_mask = { 698c2ecf20Sopenharmony_ci .ssc_enable = SSC_BIT(CR_RXEN), 708c2ecf20Sopenharmony_ci .ssc_disable = SSC_BIT(CR_RXDIS), 718c2ecf20Sopenharmony_ci .ssc_endx = SSC_BIT(SR_ENDRX), 728c2ecf20Sopenharmony_ci .ssc_endbuf = SSC_BIT(SR_RXBUFF), 738c2ecf20Sopenharmony_ci .ssc_error = SSC_BIT(SR_OVRUN), 748c2ecf20Sopenharmony_ci .pdc_enable = ATMEL_PDC_RXTEN, 758c2ecf20Sopenharmony_ci .pdc_disable = ATMEL_PDC_RXTDIS, 768c2ecf20Sopenharmony_ci}; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci/* 808c2ecf20Sopenharmony_ci * DMA parameters. 818c2ecf20Sopenharmony_ci */ 828c2ecf20Sopenharmony_cistatic struct atmel_pcm_dma_params ssc_dma_params[NUM_SSC_DEVICES][2] = { 838c2ecf20Sopenharmony_ci {{ 848c2ecf20Sopenharmony_ci .name = "SSC0 PCM out", 858c2ecf20Sopenharmony_ci .pdc = &pdc_tx_reg, 868c2ecf20Sopenharmony_ci .mask = &ssc_tx_mask, 878c2ecf20Sopenharmony_ci }, 888c2ecf20Sopenharmony_ci { 898c2ecf20Sopenharmony_ci .name = "SSC0 PCM in", 908c2ecf20Sopenharmony_ci .pdc = &pdc_rx_reg, 918c2ecf20Sopenharmony_ci .mask = &ssc_rx_mask, 928c2ecf20Sopenharmony_ci } }, 938c2ecf20Sopenharmony_ci {{ 948c2ecf20Sopenharmony_ci .name = "SSC1 PCM out", 958c2ecf20Sopenharmony_ci .pdc = &pdc_tx_reg, 968c2ecf20Sopenharmony_ci .mask = &ssc_tx_mask, 978c2ecf20Sopenharmony_ci }, 988c2ecf20Sopenharmony_ci { 998c2ecf20Sopenharmony_ci .name = "SSC1 PCM in", 1008c2ecf20Sopenharmony_ci .pdc = &pdc_rx_reg, 1018c2ecf20Sopenharmony_ci .mask = &ssc_rx_mask, 1028c2ecf20Sopenharmony_ci } }, 1038c2ecf20Sopenharmony_ci {{ 1048c2ecf20Sopenharmony_ci .name = "SSC2 PCM out", 1058c2ecf20Sopenharmony_ci .pdc = &pdc_tx_reg, 1068c2ecf20Sopenharmony_ci .mask = &ssc_tx_mask, 1078c2ecf20Sopenharmony_ci }, 1088c2ecf20Sopenharmony_ci { 1098c2ecf20Sopenharmony_ci .name = "SSC2 PCM in", 1108c2ecf20Sopenharmony_ci .pdc = &pdc_rx_reg, 1118c2ecf20Sopenharmony_ci .mask = &ssc_rx_mask, 1128c2ecf20Sopenharmony_ci } }, 1138c2ecf20Sopenharmony_ci}; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic struct atmel_ssc_info ssc_info[NUM_SSC_DEVICES] = { 1178c2ecf20Sopenharmony_ci { 1188c2ecf20Sopenharmony_ci .name = "ssc0", 1198c2ecf20Sopenharmony_ci .dir_mask = SSC_DIR_MASK_UNUSED, 1208c2ecf20Sopenharmony_ci .initialized = 0, 1218c2ecf20Sopenharmony_ci }, 1228c2ecf20Sopenharmony_ci { 1238c2ecf20Sopenharmony_ci .name = "ssc1", 1248c2ecf20Sopenharmony_ci .dir_mask = SSC_DIR_MASK_UNUSED, 1258c2ecf20Sopenharmony_ci .initialized = 0, 1268c2ecf20Sopenharmony_ci }, 1278c2ecf20Sopenharmony_ci { 1288c2ecf20Sopenharmony_ci .name = "ssc2", 1298c2ecf20Sopenharmony_ci .dir_mask = SSC_DIR_MASK_UNUSED, 1308c2ecf20Sopenharmony_ci .initialized = 0, 1318c2ecf20Sopenharmony_ci }, 1328c2ecf20Sopenharmony_ci}; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci/* 1368c2ecf20Sopenharmony_ci * SSC interrupt handler. Passes PDC interrupts to the DMA 1378c2ecf20Sopenharmony_ci * interrupt handler in the PCM driver. 1388c2ecf20Sopenharmony_ci */ 1398c2ecf20Sopenharmony_cistatic irqreturn_t atmel_ssc_interrupt(int irq, void *dev_id) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci struct atmel_ssc_info *ssc_p = dev_id; 1428c2ecf20Sopenharmony_ci struct atmel_pcm_dma_params *dma_params; 1438c2ecf20Sopenharmony_ci u32 ssc_sr; 1448c2ecf20Sopenharmony_ci u32 ssc_substream_mask; 1458c2ecf20Sopenharmony_ci int i; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci ssc_sr = (unsigned long)ssc_readl(ssc_p->ssc->regs, SR) 1488c2ecf20Sopenharmony_ci & (unsigned long)ssc_readl(ssc_p->ssc->regs, IMR); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci /* 1518c2ecf20Sopenharmony_ci * Loop through the substreams attached to this SSC. If 1528c2ecf20Sopenharmony_ci * a DMA-related interrupt occurred on that substream, call 1538c2ecf20Sopenharmony_ci * the DMA interrupt handler function, if one has been 1548c2ecf20Sopenharmony_ci * registered in the dma_params structure by the PCM driver. 1558c2ecf20Sopenharmony_ci */ 1568c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ssc_p->dma_params); i++) { 1578c2ecf20Sopenharmony_ci dma_params = ssc_p->dma_params[i]; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci if ((dma_params != NULL) && 1608c2ecf20Sopenharmony_ci (dma_params->dma_intr_handler != NULL)) { 1618c2ecf20Sopenharmony_ci ssc_substream_mask = (dma_params->mask->ssc_endx | 1628c2ecf20Sopenharmony_ci dma_params->mask->ssc_endbuf); 1638c2ecf20Sopenharmony_ci if (ssc_sr & ssc_substream_mask) { 1648c2ecf20Sopenharmony_ci dma_params->dma_intr_handler(ssc_sr, 1658c2ecf20Sopenharmony_ci dma_params-> 1668c2ecf20Sopenharmony_ci substream); 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci/* 1758c2ecf20Sopenharmony_ci * When the bit clock is input, limit the maximum rate according to the 1768c2ecf20Sopenharmony_ci * Serial Clock Ratio Considerations section from the SSC documentation: 1778c2ecf20Sopenharmony_ci * 1788c2ecf20Sopenharmony_ci * The Transmitter and the Receiver can be programmed to operate 1798c2ecf20Sopenharmony_ci * with the clock signals provided on either the TK or RK pins. 1808c2ecf20Sopenharmony_ci * This allows the SSC to support many slave-mode data transfers. 1818c2ecf20Sopenharmony_ci * In this case, the maximum clock speed allowed on the RK pin is: 1828c2ecf20Sopenharmony_ci * - Peripheral clock divided by 2 if Receiver Frame Synchro is input 1838c2ecf20Sopenharmony_ci * - Peripheral clock divided by 3 if Receiver Frame Synchro is output 1848c2ecf20Sopenharmony_ci * In addition, the maximum clock speed allowed on the TK pin is: 1858c2ecf20Sopenharmony_ci * - Peripheral clock divided by 6 if Transmit Frame Synchro is input 1868c2ecf20Sopenharmony_ci * - Peripheral clock divided by 2 if Transmit Frame Synchro is output 1878c2ecf20Sopenharmony_ci * 1888c2ecf20Sopenharmony_ci * When the bit clock is output, limit the rate according to the 1898c2ecf20Sopenharmony_ci * SSC divider restrictions. 1908c2ecf20Sopenharmony_ci */ 1918c2ecf20Sopenharmony_cistatic int atmel_ssc_hw_rule_rate(struct snd_pcm_hw_params *params, 1928c2ecf20Sopenharmony_ci struct snd_pcm_hw_rule *rule) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci struct atmel_ssc_info *ssc_p = rule->private; 1958c2ecf20Sopenharmony_ci struct ssc_device *ssc = ssc_p->ssc; 1968c2ecf20Sopenharmony_ci struct snd_interval *i = hw_param_interval(params, rule->var); 1978c2ecf20Sopenharmony_ci struct snd_interval t; 1988c2ecf20Sopenharmony_ci struct snd_ratnum r = { 1998c2ecf20Sopenharmony_ci .den_min = 1, 2008c2ecf20Sopenharmony_ci .den_max = 4095, 2018c2ecf20Sopenharmony_ci .den_step = 1, 2028c2ecf20Sopenharmony_ci }; 2038c2ecf20Sopenharmony_ci unsigned int num = 0, den = 0; 2048c2ecf20Sopenharmony_ci int frame_size; 2058c2ecf20Sopenharmony_ci int mck_div = 2; 2068c2ecf20Sopenharmony_ci int ret; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci frame_size = snd_soc_params_to_frame_size(params); 2098c2ecf20Sopenharmony_ci if (frame_size < 0) 2108c2ecf20Sopenharmony_ci return frame_size; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci switch (ssc_p->daifmt & SND_SOC_DAIFMT_MASTER_MASK) { 2138c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBM_CFS: 2148c2ecf20Sopenharmony_ci if ((ssc_p->dir_mask & SSC_DIR_MASK_CAPTURE) 2158c2ecf20Sopenharmony_ci && ssc->clk_from_rk_pin) 2168c2ecf20Sopenharmony_ci /* Receiver Frame Synchro (i.e. capture) 2178c2ecf20Sopenharmony_ci * is output (format is _CFS) and the RK pin 2188c2ecf20Sopenharmony_ci * is used for input (format is _CBM_). 2198c2ecf20Sopenharmony_ci */ 2208c2ecf20Sopenharmony_ci mck_div = 3; 2218c2ecf20Sopenharmony_ci break; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBM_CFM: 2248c2ecf20Sopenharmony_ci if ((ssc_p->dir_mask & SSC_DIR_MASK_PLAYBACK) 2258c2ecf20Sopenharmony_ci && !ssc->clk_from_rk_pin) 2268c2ecf20Sopenharmony_ci /* Transmit Frame Synchro (i.e. playback) 2278c2ecf20Sopenharmony_ci * is input (format is _CFM) and the TK pin 2288c2ecf20Sopenharmony_ci * is used for input (format _CBM_ but not 2298c2ecf20Sopenharmony_ci * using the RK pin). 2308c2ecf20Sopenharmony_ci */ 2318c2ecf20Sopenharmony_ci mck_div = 6; 2328c2ecf20Sopenharmony_ci break; 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci switch (ssc_p->daifmt & SND_SOC_DAIFMT_MASTER_MASK) { 2368c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBS_CFS: 2378c2ecf20Sopenharmony_ci r.num = ssc_p->mck_rate / mck_div / frame_size; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci ret = snd_interval_ratnum(i, 1, &r, &num, &den); 2408c2ecf20Sopenharmony_ci if (ret >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) { 2418c2ecf20Sopenharmony_ci params->rate_num = num; 2428c2ecf20Sopenharmony_ci params->rate_den = den; 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci break; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBM_CFS: 2478c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBM_CFM: 2488c2ecf20Sopenharmony_ci t.min = 8000; 2498c2ecf20Sopenharmony_ci t.max = ssc_p->mck_rate / mck_div / frame_size; 2508c2ecf20Sopenharmony_ci t.openmin = t.openmax = 0; 2518c2ecf20Sopenharmony_ci t.integer = 0; 2528c2ecf20Sopenharmony_ci ret = snd_interval_refine(i, &t); 2538c2ecf20Sopenharmony_ci break; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci default: 2568c2ecf20Sopenharmony_ci ret = -EINVAL; 2578c2ecf20Sopenharmony_ci break; 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci return ret; 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*\ 2648c2ecf20Sopenharmony_ci * DAI functions 2658c2ecf20Sopenharmony_ci\*-------------------------------------------------------------------------*/ 2668c2ecf20Sopenharmony_ci/* 2678c2ecf20Sopenharmony_ci * Startup. Only that one substream allowed in each direction. 2688c2ecf20Sopenharmony_ci */ 2698c2ecf20Sopenharmony_cistatic int atmel_ssc_startup(struct snd_pcm_substream *substream, 2708c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dai->dev); 2738c2ecf20Sopenharmony_ci struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id]; 2748c2ecf20Sopenharmony_ci struct atmel_pcm_dma_params *dma_params; 2758c2ecf20Sopenharmony_ci int dir, dir_mask; 2768c2ecf20Sopenharmony_ci int ret; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci pr_debug("atmel_ssc_startup: SSC_SR=0x%x\n", 2798c2ecf20Sopenharmony_ci ssc_readl(ssc_p->ssc->regs, SR)); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci /* Enable PMC peripheral clock for this SSC */ 2828c2ecf20Sopenharmony_ci pr_debug("atmel_ssc_dai: Starting clock\n"); 2838c2ecf20Sopenharmony_ci ret = clk_enable(ssc_p->ssc->clk); 2848c2ecf20Sopenharmony_ci if (ret) 2858c2ecf20Sopenharmony_ci return ret; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci ssc_p->mck_rate = clk_get_rate(ssc_p->ssc->clk); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci /* Reset the SSC unless initialized to keep it in a clean state */ 2908c2ecf20Sopenharmony_ci if (!ssc_p->initialized) 2918c2ecf20Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST)); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 2948c2ecf20Sopenharmony_ci dir = 0; 2958c2ecf20Sopenharmony_ci dir_mask = SSC_DIR_MASK_PLAYBACK; 2968c2ecf20Sopenharmony_ci } else { 2978c2ecf20Sopenharmony_ci dir = 1; 2988c2ecf20Sopenharmony_ci dir_mask = SSC_DIR_MASK_CAPTURE; 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci ret = snd_pcm_hw_rule_add(substream->runtime, 0, 3028c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_RATE, 3038c2ecf20Sopenharmony_ci atmel_ssc_hw_rule_rate, 3048c2ecf20Sopenharmony_ci ssc_p, 3058c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_FRAME_BITS, 3068c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_CHANNELS, -1); 3078c2ecf20Sopenharmony_ci if (ret < 0) { 3088c2ecf20Sopenharmony_ci dev_err(dai->dev, "Failed to specify rate rule: %d\n", ret); 3098c2ecf20Sopenharmony_ci return ret; 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci dma_params = &ssc_dma_params[pdev->id][dir]; 3138c2ecf20Sopenharmony_ci dma_params->ssc = ssc_p->ssc; 3148c2ecf20Sopenharmony_ci dma_params->substream = substream; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci ssc_p->dma_params[dir] = dma_params; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci snd_soc_dai_set_dma_data(dai, substream, dma_params); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci if (ssc_p->dir_mask & dir_mask) 3218c2ecf20Sopenharmony_ci return -EBUSY; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci ssc_p->dir_mask |= dir_mask; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci return 0; 3268c2ecf20Sopenharmony_ci} 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci/* 3298c2ecf20Sopenharmony_ci * Shutdown. Clear DMA parameters and shutdown the SSC if there 3308c2ecf20Sopenharmony_ci * are no other substreams open. 3318c2ecf20Sopenharmony_ci */ 3328c2ecf20Sopenharmony_cistatic void atmel_ssc_shutdown(struct snd_pcm_substream *substream, 3338c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 3348c2ecf20Sopenharmony_ci{ 3358c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dai->dev); 3368c2ecf20Sopenharmony_ci struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id]; 3378c2ecf20Sopenharmony_ci struct atmel_pcm_dma_params *dma_params; 3388c2ecf20Sopenharmony_ci int dir, dir_mask; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 3418c2ecf20Sopenharmony_ci dir = 0; 3428c2ecf20Sopenharmony_ci else 3438c2ecf20Sopenharmony_ci dir = 1; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci dma_params = ssc_p->dma_params[dir]; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci if (dma_params != NULL) { 3488c2ecf20Sopenharmony_ci dma_params->ssc = NULL; 3498c2ecf20Sopenharmony_ci dma_params->substream = NULL; 3508c2ecf20Sopenharmony_ci ssc_p->dma_params[dir] = NULL; 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci dir_mask = 1 << dir; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci ssc_p->dir_mask &= ~dir_mask; 3568c2ecf20Sopenharmony_ci if (!ssc_p->dir_mask) { 3578c2ecf20Sopenharmony_ci if (ssc_p->initialized) { 3588c2ecf20Sopenharmony_ci free_irq(ssc_p->ssc->irq, ssc_p); 3598c2ecf20Sopenharmony_ci ssc_p->initialized = 0; 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci /* Reset the SSC */ 3638c2ecf20Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST)); 3648c2ecf20Sopenharmony_ci /* Clear the SSC dividers */ 3658c2ecf20Sopenharmony_ci ssc_p->cmr_div = ssc_p->tcmr_period = ssc_p->rcmr_period = 0; 3668c2ecf20Sopenharmony_ci ssc_p->forced_divider = 0; 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci /* Shutdown the SSC clock. */ 3708c2ecf20Sopenharmony_ci pr_debug("atmel_ssc_dai: Stopping clock\n"); 3718c2ecf20Sopenharmony_ci clk_disable(ssc_p->ssc->clk); 3728c2ecf20Sopenharmony_ci} 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci/* 3768c2ecf20Sopenharmony_ci * Record the DAI format for use in hw_params(). 3778c2ecf20Sopenharmony_ci */ 3788c2ecf20Sopenharmony_cistatic int atmel_ssc_set_dai_fmt(struct snd_soc_dai *cpu_dai, 3798c2ecf20Sopenharmony_ci unsigned int fmt) 3808c2ecf20Sopenharmony_ci{ 3818c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(cpu_dai->dev); 3828c2ecf20Sopenharmony_ci struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id]; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci ssc_p->daifmt = fmt; 3858c2ecf20Sopenharmony_ci return 0; 3868c2ecf20Sopenharmony_ci} 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci/* 3898c2ecf20Sopenharmony_ci * Record SSC clock dividers for use in hw_params(). 3908c2ecf20Sopenharmony_ci */ 3918c2ecf20Sopenharmony_cistatic int atmel_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai, 3928c2ecf20Sopenharmony_ci int div_id, int div) 3938c2ecf20Sopenharmony_ci{ 3948c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(cpu_dai->dev); 3958c2ecf20Sopenharmony_ci struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id]; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci switch (div_id) { 3988c2ecf20Sopenharmony_ci case ATMEL_SSC_CMR_DIV: 3998c2ecf20Sopenharmony_ci /* 4008c2ecf20Sopenharmony_ci * The same master clock divider is used for both 4018c2ecf20Sopenharmony_ci * transmit and receive, so if a value has already 4028c2ecf20Sopenharmony_ci * been set, it must match this value. 4038c2ecf20Sopenharmony_ci */ 4048c2ecf20Sopenharmony_ci if (ssc_p->dir_mask != 4058c2ecf20Sopenharmony_ci (SSC_DIR_MASK_PLAYBACK | SSC_DIR_MASK_CAPTURE)) 4068c2ecf20Sopenharmony_ci ssc_p->cmr_div = div; 4078c2ecf20Sopenharmony_ci else if (ssc_p->cmr_div == 0) 4088c2ecf20Sopenharmony_ci ssc_p->cmr_div = div; 4098c2ecf20Sopenharmony_ci else 4108c2ecf20Sopenharmony_ci if (div != ssc_p->cmr_div) 4118c2ecf20Sopenharmony_ci return -EBUSY; 4128c2ecf20Sopenharmony_ci ssc_p->forced_divider |= BIT(ATMEL_SSC_CMR_DIV); 4138c2ecf20Sopenharmony_ci break; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci case ATMEL_SSC_TCMR_PERIOD: 4168c2ecf20Sopenharmony_ci ssc_p->tcmr_period = div; 4178c2ecf20Sopenharmony_ci ssc_p->forced_divider |= BIT(ATMEL_SSC_TCMR_PERIOD); 4188c2ecf20Sopenharmony_ci break; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci case ATMEL_SSC_RCMR_PERIOD: 4218c2ecf20Sopenharmony_ci ssc_p->rcmr_period = div; 4228c2ecf20Sopenharmony_ci ssc_p->forced_divider |= BIT(ATMEL_SSC_RCMR_PERIOD); 4238c2ecf20Sopenharmony_ci break; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci default: 4268c2ecf20Sopenharmony_ci return -EINVAL; 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci return 0; 4308c2ecf20Sopenharmony_ci} 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci/* Is the cpu-dai master of the frame clock? */ 4338c2ecf20Sopenharmony_cistatic int atmel_ssc_cfs(struct atmel_ssc_info *ssc_p) 4348c2ecf20Sopenharmony_ci{ 4358c2ecf20Sopenharmony_ci switch (ssc_p->daifmt & SND_SOC_DAIFMT_MASTER_MASK) { 4368c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBM_CFS: 4378c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBS_CFS: 4388c2ecf20Sopenharmony_ci return 1; 4398c2ecf20Sopenharmony_ci } 4408c2ecf20Sopenharmony_ci return 0; 4418c2ecf20Sopenharmony_ci} 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci/* Is the cpu-dai master of the bit clock? */ 4448c2ecf20Sopenharmony_cistatic int atmel_ssc_cbs(struct atmel_ssc_info *ssc_p) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci switch (ssc_p->daifmt & SND_SOC_DAIFMT_MASTER_MASK) { 4478c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBS_CFM: 4488c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBS_CFS: 4498c2ecf20Sopenharmony_ci return 1; 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci return 0; 4528c2ecf20Sopenharmony_ci} 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci/* 4558c2ecf20Sopenharmony_ci * Configure the SSC. 4568c2ecf20Sopenharmony_ci */ 4578c2ecf20Sopenharmony_cistatic int atmel_ssc_hw_params(struct snd_pcm_substream *substream, 4588c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params, 4598c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 4608c2ecf20Sopenharmony_ci{ 4618c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dai->dev); 4628c2ecf20Sopenharmony_ci int id = pdev->id; 4638c2ecf20Sopenharmony_ci struct atmel_ssc_info *ssc_p = &ssc_info[id]; 4648c2ecf20Sopenharmony_ci struct ssc_device *ssc = ssc_p->ssc; 4658c2ecf20Sopenharmony_ci struct atmel_pcm_dma_params *dma_params; 4668c2ecf20Sopenharmony_ci int dir, channels, bits; 4678c2ecf20Sopenharmony_ci u32 tfmr, rfmr, tcmr, rcmr; 4688c2ecf20Sopenharmony_ci int ret; 4698c2ecf20Sopenharmony_ci int fslen, fslen_ext, fs_osync, fs_edge; 4708c2ecf20Sopenharmony_ci u32 cmr_div; 4718c2ecf20Sopenharmony_ci u32 tcmr_period; 4728c2ecf20Sopenharmony_ci u32 rcmr_period; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci /* 4758c2ecf20Sopenharmony_ci * Currently, there is only one set of dma params for 4768c2ecf20Sopenharmony_ci * each direction. If more are added, this code will 4778c2ecf20Sopenharmony_ci * have to be changed to select the proper set. 4788c2ecf20Sopenharmony_ci */ 4798c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 4808c2ecf20Sopenharmony_ci dir = 0; 4818c2ecf20Sopenharmony_ci else 4828c2ecf20Sopenharmony_ci dir = 1; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci /* 4858c2ecf20Sopenharmony_ci * If the cpu dai should provide BCLK, but noone has provided the 4868c2ecf20Sopenharmony_ci * divider needed for that to work, fall back to something sensible. 4878c2ecf20Sopenharmony_ci */ 4888c2ecf20Sopenharmony_ci cmr_div = ssc_p->cmr_div; 4898c2ecf20Sopenharmony_ci if (!(ssc_p->forced_divider & BIT(ATMEL_SSC_CMR_DIV)) && 4908c2ecf20Sopenharmony_ci atmel_ssc_cbs(ssc_p)) { 4918c2ecf20Sopenharmony_ci int bclk_rate = snd_soc_params_to_bclk(params); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci if (bclk_rate < 0) { 4948c2ecf20Sopenharmony_ci dev_err(dai->dev, "unable to calculate cmr_div: %d\n", 4958c2ecf20Sopenharmony_ci bclk_rate); 4968c2ecf20Sopenharmony_ci return bclk_rate; 4978c2ecf20Sopenharmony_ci } 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci cmr_div = DIV_ROUND_CLOSEST(ssc_p->mck_rate, 2 * bclk_rate); 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci /* 5038c2ecf20Sopenharmony_ci * If the cpu dai should provide LRCLK, but noone has provided the 5048c2ecf20Sopenharmony_ci * dividers needed for that to work, fall back to something sensible. 5058c2ecf20Sopenharmony_ci */ 5068c2ecf20Sopenharmony_ci tcmr_period = ssc_p->tcmr_period; 5078c2ecf20Sopenharmony_ci rcmr_period = ssc_p->rcmr_period; 5088c2ecf20Sopenharmony_ci if (atmel_ssc_cfs(ssc_p)) { 5098c2ecf20Sopenharmony_ci int frame_size = snd_soc_params_to_frame_size(params); 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci if (frame_size < 0) { 5128c2ecf20Sopenharmony_ci dev_err(dai->dev, 5138c2ecf20Sopenharmony_ci "unable to calculate tx/rx cmr_period: %d\n", 5148c2ecf20Sopenharmony_ci frame_size); 5158c2ecf20Sopenharmony_ci return frame_size; 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci if (!(ssc_p->forced_divider & BIT(ATMEL_SSC_TCMR_PERIOD))) 5198c2ecf20Sopenharmony_ci tcmr_period = frame_size / 2 - 1; 5208c2ecf20Sopenharmony_ci if (!(ssc_p->forced_divider & BIT(ATMEL_SSC_RCMR_PERIOD))) 5218c2ecf20Sopenharmony_ci rcmr_period = frame_size / 2 - 1; 5228c2ecf20Sopenharmony_ci } 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci dma_params = ssc_p->dma_params[dir]; 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci channels = params_channels(params); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci /* 5298c2ecf20Sopenharmony_ci * Determine sample size in bits and the PDC increment. 5308c2ecf20Sopenharmony_ci */ 5318c2ecf20Sopenharmony_ci switch (params_format(params)) { 5328c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S8: 5338c2ecf20Sopenharmony_ci bits = 8; 5348c2ecf20Sopenharmony_ci dma_params->pdc_xfer_size = 1; 5358c2ecf20Sopenharmony_ci break; 5368c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_LE: 5378c2ecf20Sopenharmony_ci bits = 16; 5388c2ecf20Sopenharmony_ci dma_params->pdc_xfer_size = 2; 5398c2ecf20Sopenharmony_ci break; 5408c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S24_LE: 5418c2ecf20Sopenharmony_ci bits = 24; 5428c2ecf20Sopenharmony_ci dma_params->pdc_xfer_size = 4; 5438c2ecf20Sopenharmony_ci break; 5448c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S32_LE: 5458c2ecf20Sopenharmony_ci bits = 32; 5468c2ecf20Sopenharmony_ci dma_params->pdc_xfer_size = 4; 5478c2ecf20Sopenharmony_ci break; 5488c2ecf20Sopenharmony_ci default: 5498c2ecf20Sopenharmony_ci printk(KERN_WARNING "atmel_ssc_dai: unsupported PCM format"); 5508c2ecf20Sopenharmony_ci return -EINVAL; 5518c2ecf20Sopenharmony_ci } 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci /* 5548c2ecf20Sopenharmony_ci * Compute SSC register settings. 5558c2ecf20Sopenharmony_ci */ 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci fslen_ext = (bits - 1) / 16; 5588c2ecf20Sopenharmony_ci fslen = (bits - 1) % 16; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci switch (ssc_p->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) { 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_LEFT_J: 5638c2ecf20Sopenharmony_ci fs_osync = SSC_FSOS_POSITIVE; 5648c2ecf20Sopenharmony_ci fs_edge = SSC_START_RISING_RF; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci rcmr = SSC_BF(RCMR_STTDLY, 0); 5678c2ecf20Sopenharmony_ci tcmr = SSC_BF(TCMR_STTDLY, 0); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci break; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_I2S: 5728c2ecf20Sopenharmony_ci fs_osync = SSC_FSOS_NEGATIVE; 5738c2ecf20Sopenharmony_ci fs_edge = SSC_START_FALLING_RF; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci rcmr = SSC_BF(RCMR_STTDLY, 1); 5768c2ecf20Sopenharmony_ci tcmr = SSC_BF(TCMR_STTDLY, 1); 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci break; 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_DSP_A: 5818c2ecf20Sopenharmony_ci /* 5828c2ecf20Sopenharmony_ci * DSP/PCM Mode A format 5838c2ecf20Sopenharmony_ci * 5848c2ecf20Sopenharmony_ci * Data is transferred on first BCLK after LRC pulse rising 5858c2ecf20Sopenharmony_ci * edge.If stereo, the right channel data is contiguous with 5868c2ecf20Sopenharmony_ci * the left channel data. 5878c2ecf20Sopenharmony_ci */ 5888c2ecf20Sopenharmony_ci fs_osync = SSC_FSOS_POSITIVE; 5898c2ecf20Sopenharmony_ci fs_edge = SSC_START_RISING_RF; 5908c2ecf20Sopenharmony_ci fslen = fslen_ext = 0; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci rcmr = SSC_BF(RCMR_STTDLY, 1); 5938c2ecf20Sopenharmony_ci tcmr = SSC_BF(TCMR_STTDLY, 1); 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci break; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci default: 5988c2ecf20Sopenharmony_ci printk(KERN_WARNING "atmel_ssc_dai: unsupported DAI format 0x%x\n", 5998c2ecf20Sopenharmony_ci ssc_p->daifmt); 6008c2ecf20Sopenharmony_ci return -EINVAL; 6018c2ecf20Sopenharmony_ci } 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci if (!atmel_ssc_cfs(ssc_p)) { 6048c2ecf20Sopenharmony_ci fslen = fslen_ext = 0; 6058c2ecf20Sopenharmony_ci rcmr_period = tcmr_period = 0; 6068c2ecf20Sopenharmony_ci fs_osync = SSC_FSOS_NONE; 6078c2ecf20Sopenharmony_ci } 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci rcmr |= SSC_BF(RCMR_START, fs_edge); 6108c2ecf20Sopenharmony_ci tcmr |= SSC_BF(TCMR_START, fs_edge); 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci if (atmel_ssc_cbs(ssc_p)) { 6138c2ecf20Sopenharmony_ci /* 6148c2ecf20Sopenharmony_ci * SSC provides BCLK 6158c2ecf20Sopenharmony_ci * 6168c2ecf20Sopenharmony_ci * The SSC transmit and receive clocks are generated from the 6178c2ecf20Sopenharmony_ci * MCK divider, and the BCLK signal is output 6188c2ecf20Sopenharmony_ci * on the SSC TK line. 6198c2ecf20Sopenharmony_ci */ 6208c2ecf20Sopenharmony_ci rcmr |= SSC_BF(RCMR_CKS, SSC_CKS_DIV) 6218c2ecf20Sopenharmony_ci | SSC_BF(RCMR_CKO, SSC_CKO_NONE); 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci tcmr |= SSC_BF(TCMR_CKS, SSC_CKS_DIV) 6248c2ecf20Sopenharmony_ci | SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS); 6258c2ecf20Sopenharmony_ci } else { 6268c2ecf20Sopenharmony_ci rcmr |= SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ? 6278c2ecf20Sopenharmony_ci SSC_CKS_PIN : SSC_CKS_CLOCK) 6288c2ecf20Sopenharmony_ci | SSC_BF(RCMR_CKO, SSC_CKO_NONE); 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci tcmr |= SSC_BF(TCMR_CKS, ssc->clk_from_rk_pin ? 6318c2ecf20Sopenharmony_ci SSC_CKS_CLOCK : SSC_CKS_PIN) 6328c2ecf20Sopenharmony_ci | SSC_BF(TCMR_CKO, SSC_CKO_NONE); 6338c2ecf20Sopenharmony_ci } 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci rcmr |= SSC_BF(RCMR_PERIOD, rcmr_period) 6368c2ecf20Sopenharmony_ci | SSC_BF(RCMR_CKI, SSC_CKI_RISING); 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci tcmr |= SSC_BF(TCMR_PERIOD, tcmr_period) 6398c2ecf20Sopenharmony_ci | SSC_BF(TCMR_CKI, SSC_CKI_FALLING); 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci rfmr = SSC_BF(RFMR_FSLEN_EXT, fslen_ext) 6428c2ecf20Sopenharmony_ci | SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE) 6438c2ecf20Sopenharmony_ci | SSC_BF(RFMR_FSOS, fs_osync) 6448c2ecf20Sopenharmony_ci | SSC_BF(RFMR_FSLEN, fslen) 6458c2ecf20Sopenharmony_ci | SSC_BF(RFMR_DATNB, (channels - 1)) 6468c2ecf20Sopenharmony_ci | SSC_BIT(RFMR_MSBF) 6478c2ecf20Sopenharmony_ci | SSC_BF(RFMR_LOOP, 0) 6488c2ecf20Sopenharmony_ci | SSC_BF(RFMR_DATLEN, (bits - 1)); 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci tfmr = SSC_BF(TFMR_FSLEN_EXT, fslen_ext) 6518c2ecf20Sopenharmony_ci | SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE) 6528c2ecf20Sopenharmony_ci | SSC_BF(TFMR_FSDEN, 0) 6538c2ecf20Sopenharmony_ci | SSC_BF(TFMR_FSOS, fs_osync) 6548c2ecf20Sopenharmony_ci | SSC_BF(TFMR_FSLEN, fslen) 6558c2ecf20Sopenharmony_ci | SSC_BF(TFMR_DATNB, (channels - 1)) 6568c2ecf20Sopenharmony_ci | SSC_BIT(TFMR_MSBF) 6578c2ecf20Sopenharmony_ci | SSC_BF(TFMR_DATDEF, 0) 6588c2ecf20Sopenharmony_ci | SSC_BF(TFMR_DATLEN, (bits - 1)); 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci if (fslen_ext && !ssc->pdata->has_fslen_ext) { 6618c2ecf20Sopenharmony_ci dev_err(dai->dev, "sample size %d is too large for SSC device\n", 6628c2ecf20Sopenharmony_ci bits); 6638c2ecf20Sopenharmony_ci return -EINVAL; 6648c2ecf20Sopenharmony_ci } 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci pr_debug("atmel_ssc_hw_params: " 6678c2ecf20Sopenharmony_ci "RCMR=%08x RFMR=%08x TCMR=%08x TFMR=%08x\n", 6688c2ecf20Sopenharmony_ci rcmr, rfmr, tcmr, tfmr); 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci if (!ssc_p->initialized) { 6718c2ecf20Sopenharmony_ci if (!ssc_p->ssc->pdata->use_dma) { 6728c2ecf20Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, PDC_RPR, 0); 6738c2ecf20Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, PDC_RCR, 0); 6748c2ecf20Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, PDC_RNPR, 0); 6758c2ecf20Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, PDC_RNCR, 0); 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, PDC_TPR, 0); 6788c2ecf20Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, PDC_TCR, 0); 6798c2ecf20Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, PDC_TNPR, 0); 6808c2ecf20Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, PDC_TNCR, 0); 6818c2ecf20Sopenharmony_ci } 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci ret = request_irq(ssc_p->ssc->irq, atmel_ssc_interrupt, 0, 6848c2ecf20Sopenharmony_ci ssc_p->name, ssc_p); 6858c2ecf20Sopenharmony_ci if (ret < 0) { 6868c2ecf20Sopenharmony_ci printk(KERN_WARNING 6878c2ecf20Sopenharmony_ci "atmel_ssc_dai: request_irq failure\n"); 6888c2ecf20Sopenharmony_ci pr_debug("Atmel_ssc_dai: Stopping clock\n"); 6898c2ecf20Sopenharmony_ci clk_disable(ssc_p->ssc->clk); 6908c2ecf20Sopenharmony_ci return ret; 6918c2ecf20Sopenharmony_ci } 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci ssc_p->initialized = 1; 6948c2ecf20Sopenharmony_ci } 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci /* set SSC clock mode register */ 6978c2ecf20Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, CMR, cmr_div); 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci /* set receive clock mode and format */ 7008c2ecf20Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, RCMR, rcmr); 7018c2ecf20Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, RFMR, rfmr); 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci /* set transmit clock mode and format */ 7048c2ecf20Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, TCMR, tcmr); 7058c2ecf20Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, TFMR, tfmr); 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci pr_debug("atmel_ssc_dai,hw_params: SSC initialized\n"); 7088c2ecf20Sopenharmony_ci return 0; 7098c2ecf20Sopenharmony_ci} 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_cistatic int atmel_ssc_prepare(struct snd_pcm_substream *substream, 7138c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 7148c2ecf20Sopenharmony_ci{ 7158c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dai->dev); 7168c2ecf20Sopenharmony_ci struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id]; 7178c2ecf20Sopenharmony_ci struct atmel_pcm_dma_params *dma_params; 7188c2ecf20Sopenharmony_ci int dir; 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 7218c2ecf20Sopenharmony_ci dir = 0; 7228c2ecf20Sopenharmony_ci else 7238c2ecf20Sopenharmony_ci dir = 1; 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci dma_params = ssc_p->dma_params[dir]; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, CR, dma_params->mask->ssc_disable); 7288c2ecf20Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, IDR, dma_params->mask->ssc_error); 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci pr_debug("%s enabled SSC_SR=0x%08x\n", 7318c2ecf20Sopenharmony_ci dir ? "receive" : "transmit", 7328c2ecf20Sopenharmony_ci ssc_readl(ssc_p->ssc->regs, SR)); 7338c2ecf20Sopenharmony_ci return 0; 7348c2ecf20Sopenharmony_ci} 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_cistatic int atmel_ssc_trigger(struct snd_pcm_substream *substream, 7378c2ecf20Sopenharmony_ci int cmd, struct snd_soc_dai *dai) 7388c2ecf20Sopenharmony_ci{ 7398c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dai->dev); 7408c2ecf20Sopenharmony_ci struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id]; 7418c2ecf20Sopenharmony_ci struct atmel_pcm_dma_params *dma_params; 7428c2ecf20Sopenharmony_ci int dir; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 7458c2ecf20Sopenharmony_ci dir = 0; 7468c2ecf20Sopenharmony_ci else 7478c2ecf20Sopenharmony_ci dir = 1; 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci dma_params = ssc_p->dma_params[dir]; 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci switch (cmd) { 7528c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 7538c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 7548c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 7558c2ecf20Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, CR, dma_params->mask->ssc_enable); 7568c2ecf20Sopenharmony_ci break; 7578c2ecf20Sopenharmony_ci default: 7588c2ecf20Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, CR, dma_params->mask->ssc_disable); 7598c2ecf20Sopenharmony_ci break; 7608c2ecf20Sopenharmony_ci } 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci return 0; 7638c2ecf20Sopenharmony_ci} 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 7668c2ecf20Sopenharmony_cistatic int atmel_ssc_suspend(struct snd_soc_component *component) 7678c2ecf20Sopenharmony_ci{ 7688c2ecf20Sopenharmony_ci struct atmel_ssc_info *ssc_p; 7698c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(component->dev); 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci if (!snd_soc_component_active(component)) 7728c2ecf20Sopenharmony_ci return 0; 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci ssc_p = &ssc_info[pdev->id]; 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci /* Save the status register before disabling transmit and receive */ 7778c2ecf20Sopenharmony_ci ssc_p->ssc_state.ssc_sr = ssc_readl(ssc_p->ssc->regs, SR); 7788c2ecf20Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_TXDIS) | SSC_BIT(CR_RXDIS)); 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci /* Save the current interrupt mask, then disable unmasked interrupts */ 7818c2ecf20Sopenharmony_ci ssc_p->ssc_state.ssc_imr = ssc_readl(ssc_p->ssc->regs, IMR); 7828c2ecf20Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, IDR, ssc_p->ssc_state.ssc_imr); 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci ssc_p->ssc_state.ssc_cmr = ssc_readl(ssc_p->ssc->regs, CMR); 7858c2ecf20Sopenharmony_ci ssc_p->ssc_state.ssc_rcmr = ssc_readl(ssc_p->ssc->regs, RCMR); 7868c2ecf20Sopenharmony_ci ssc_p->ssc_state.ssc_rfmr = ssc_readl(ssc_p->ssc->regs, RFMR); 7878c2ecf20Sopenharmony_ci ssc_p->ssc_state.ssc_tcmr = ssc_readl(ssc_p->ssc->regs, TCMR); 7888c2ecf20Sopenharmony_ci ssc_p->ssc_state.ssc_tfmr = ssc_readl(ssc_p->ssc->regs, TFMR); 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci return 0; 7918c2ecf20Sopenharmony_ci} 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_cistatic int atmel_ssc_resume(struct snd_soc_component *component) 7948c2ecf20Sopenharmony_ci{ 7958c2ecf20Sopenharmony_ci struct atmel_ssc_info *ssc_p; 7968c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(component->dev); 7978c2ecf20Sopenharmony_ci u32 cr; 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci if (!snd_soc_component_active(component)) 8008c2ecf20Sopenharmony_ci return 0; 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci ssc_p = &ssc_info[pdev->id]; 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci /* restore SSC register settings */ 8058c2ecf20Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, TFMR, ssc_p->ssc_state.ssc_tfmr); 8068c2ecf20Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, TCMR, ssc_p->ssc_state.ssc_tcmr); 8078c2ecf20Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, RFMR, ssc_p->ssc_state.ssc_rfmr); 8088c2ecf20Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, RCMR, ssc_p->ssc_state.ssc_rcmr); 8098c2ecf20Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, CMR, ssc_p->ssc_state.ssc_cmr); 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci /* re-enable interrupts */ 8128c2ecf20Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, IER, ssc_p->ssc_state.ssc_imr); 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci /* Re-enable receive and transmit as appropriate */ 8158c2ecf20Sopenharmony_ci cr = 0; 8168c2ecf20Sopenharmony_ci cr |= 8178c2ecf20Sopenharmony_ci (ssc_p->ssc_state.ssc_sr & SSC_BIT(SR_RXEN)) ? SSC_BIT(CR_RXEN) : 0; 8188c2ecf20Sopenharmony_ci cr |= 8198c2ecf20Sopenharmony_ci (ssc_p->ssc_state.ssc_sr & SSC_BIT(SR_TXEN)) ? SSC_BIT(CR_TXEN) : 0; 8208c2ecf20Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, CR, cr); 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci return 0; 8238c2ecf20Sopenharmony_ci} 8248c2ecf20Sopenharmony_ci#else /* CONFIG_PM */ 8258c2ecf20Sopenharmony_ci# define atmel_ssc_suspend NULL 8268c2ecf20Sopenharmony_ci# define atmel_ssc_resume NULL 8278c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */ 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci#define ATMEL_SSC_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\ 8308c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops atmel_ssc_dai_ops = { 8338c2ecf20Sopenharmony_ci .startup = atmel_ssc_startup, 8348c2ecf20Sopenharmony_ci .shutdown = atmel_ssc_shutdown, 8358c2ecf20Sopenharmony_ci .prepare = atmel_ssc_prepare, 8368c2ecf20Sopenharmony_ci .trigger = atmel_ssc_trigger, 8378c2ecf20Sopenharmony_ci .hw_params = atmel_ssc_hw_params, 8388c2ecf20Sopenharmony_ci .set_fmt = atmel_ssc_set_dai_fmt, 8398c2ecf20Sopenharmony_ci .set_clkdiv = atmel_ssc_set_dai_clkdiv, 8408c2ecf20Sopenharmony_ci}; 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_cistatic struct snd_soc_dai_driver atmel_ssc_dai = { 8438c2ecf20Sopenharmony_ci .playback = { 8448c2ecf20Sopenharmony_ci .channels_min = 1, 8458c2ecf20Sopenharmony_ci .channels_max = 2, 8468c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS, 8478c2ecf20Sopenharmony_ci .rate_min = 8000, 8488c2ecf20Sopenharmony_ci .rate_max = 384000, 8498c2ecf20Sopenharmony_ci .formats = ATMEL_SSC_FORMATS,}, 8508c2ecf20Sopenharmony_ci .capture = { 8518c2ecf20Sopenharmony_ci .channels_min = 1, 8528c2ecf20Sopenharmony_ci .channels_max = 2, 8538c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS, 8548c2ecf20Sopenharmony_ci .rate_min = 8000, 8558c2ecf20Sopenharmony_ci .rate_max = 384000, 8568c2ecf20Sopenharmony_ci .formats = ATMEL_SSC_FORMATS,}, 8578c2ecf20Sopenharmony_ci .ops = &atmel_ssc_dai_ops, 8588c2ecf20Sopenharmony_ci}; 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver atmel_ssc_component = { 8618c2ecf20Sopenharmony_ci .name = "atmel-ssc", 8628c2ecf20Sopenharmony_ci .suspend = atmel_ssc_suspend, 8638c2ecf20Sopenharmony_ci .resume = atmel_ssc_resume, 8648c2ecf20Sopenharmony_ci}; 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_cistatic int asoc_ssc_init(struct device *dev) 8678c2ecf20Sopenharmony_ci{ 8688c2ecf20Sopenharmony_ci struct ssc_device *ssc = dev_get_drvdata(dev); 8698c2ecf20Sopenharmony_ci int ret; 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci ret = devm_snd_soc_register_component(dev, &atmel_ssc_component, 8728c2ecf20Sopenharmony_ci &atmel_ssc_dai, 1); 8738c2ecf20Sopenharmony_ci if (ret) { 8748c2ecf20Sopenharmony_ci dev_err(dev, "Could not register DAI: %d\n", ret); 8758c2ecf20Sopenharmony_ci return ret; 8768c2ecf20Sopenharmony_ci } 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci if (ssc->pdata->use_dma) 8798c2ecf20Sopenharmony_ci ret = atmel_pcm_dma_platform_register(dev); 8808c2ecf20Sopenharmony_ci else 8818c2ecf20Sopenharmony_ci ret = atmel_pcm_pdc_platform_register(dev); 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci if (ret) { 8848c2ecf20Sopenharmony_ci dev_err(dev, "Could not register PCM: %d\n", ret); 8858c2ecf20Sopenharmony_ci return ret; 8868c2ecf20Sopenharmony_ci } 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci return 0; 8898c2ecf20Sopenharmony_ci} 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci/** 8928c2ecf20Sopenharmony_ci * atmel_ssc_set_audio - Allocate the specified SSC for audio use. 8938c2ecf20Sopenharmony_ci * @ssc_id: SSD ID in [0, NUM_SSC_DEVICES[ 8948c2ecf20Sopenharmony_ci */ 8958c2ecf20Sopenharmony_ciint atmel_ssc_set_audio(int ssc_id) 8968c2ecf20Sopenharmony_ci{ 8978c2ecf20Sopenharmony_ci struct ssc_device *ssc; 8988c2ecf20Sopenharmony_ci int ret; 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci /* If we can grab the SSC briefly to parent the DAI device off it */ 9018c2ecf20Sopenharmony_ci ssc = ssc_request(ssc_id); 9028c2ecf20Sopenharmony_ci if (IS_ERR(ssc)) { 9038c2ecf20Sopenharmony_ci pr_err("Unable to parent ASoC SSC DAI on SSC: %ld\n", 9048c2ecf20Sopenharmony_ci PTR_ERR(ssc)); 9058c2ecf20Sopenharmony_ci return PTR_ERR(ssc); 9068c2ecf20Sopenharmony_ci } else { 9078c2ecf20Sopenharmony_ci ssc_info[ssc_id].ssc = ssc; 9088c2ecf20Sopenharmony_ci } 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci ret = asoc_ssc_init(&ssc->pdev->dev); 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci return ret; 9138c2ecf20Sopenharmony_ci} 9148c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(atmel_ssc_set_audio); 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_civoid atmel_ssc_put_audio(int ssc_id) 9178c2ecf20Sopenharmony_ci{ 9188c2ecf20Sopenharmony_ci struct ssc_device *ssc = ssc_info[ssc_id].ssc; 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci ssc_free(ssc); 9218c2ecf20Sopenharmony_ci} 9228c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(atmel_ssc_put_audio); 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci/* Module information */ 9258c2ecf20Sopenharmony_ciMODULE_AUTHOR("Sedji Gaouaou, sedji.gaouaou@atmel.com, www.atmel.com"); 9268c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ATMEL SSC ASoC Interface"); 9278c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 928