162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * atmel_ssc_dai.c -- ALSA SoC ATMEL SSC Audio Layer Platform driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2005 SAN People 662306a36Sopenharmony_ci * Copyright (C) 2008 Atmel 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Author: Sedji Gaouaou <sedji.gaouaou@atmel.com> 962306a36Sopenharmony_ci * ATMEL CORP. 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Based on at91-ssc.c by 1262306a36Sopenharmony_ci * Frank Mandarino <fmandarino@endrelia.com> 1362306a36Sopenharmony_ci * Based on pxa2xx Platform drivers by 1462306a36Sopenharmony_ci * Liam Girdwood <lrg@slimlogic.co.uk> 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <linux/init.h> 1862306a36Sopenharmony_ci#include <linux/module.h> 1962306a36Sopenharmony_ci#include <linux/interrupt.h> 2062306a36Sopenharmony_ci#include <linux/device.h> 2162306a36Sopenharmony_ci#include <linux/delay.h> 2262306a36Sopenharmony_ci#include <linux/clk.h> 2362306a36Sopenharmony_ci#include <linux/atmel_pdc.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include <linux/atmel-ssc.h> 2662306a36Sopenharmony_ci#include <sound/core.h> 2762306a36Sopenharmony_ci#include <sound/pcm.h> 2862306a36Sopenharmony_ci#include <sound/pcm_params.h> 2962306a36Sopenharmony_ci#include <sound/initval.h> 3062306a36Sopenharmony_ci#include <sound/soc.h> 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#include "atmel-pcm.h" 3362306a36Sopenharmony_ci#include "atmel_ssc_dai.h" 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define NUM_SSC_DEVICES 3 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* 3962306a36Sopenharmony_ci * SSC PDC registers required by the PCM DMA engine. 4062306a36Sopenharmony_ci */ 4162306a36Sopenharmony_cistatic struct atmel_pdc_regs pdc_tx_reg = { 4262306a36Sopenharmony_ci .xpr = ATMEL_PDC_TPR, 4362306a36Sopenharmony_ci .xcr = ATMEL_PDC_TCR, 4462306a36Sopenharmony_ci .xnpr = ATMEL_PDC_TNPR, 4562306a36Sopenharmony_ci .xncr = ATMEL_PDC_TNCR, 4662306a36Sopenharmony_ci}; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic struct atmel_pdc_regs pdc_rx_reg = { 4962306a36Sopenharmony_ci .xpr = ATMEL_PDC_RPR, 5062306a36Sopenharmony_ci .xcr = ATMEL_PDC_RCR, 5162306a36Sopenharmony_ci .xnpr = ATMEL_PDC_RNPR, 5262306a36Sopenharmony_ci .xncr = ATMEL_PDC_RNCR, 5362306a36Sopenharmony_ci}; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci/* 5662306a36Sopenharmony_ci * SSC & PDC status bits for transmit and receive. 5762306a36Sopenharmony_ci */ 5862306a36Sopenharmony_cistatic struct atmel_ssc_mask ssc_tx_mask = { 5962306a36Sopenharmony_ci .ssc_enable = SSC_BIT(CR_TXEN), 6062306a36Sopenharmony_ci .ssc_disable = SSC_BIT(CR_TXDIS), 6162306a36Sopenharmony_ci .ssc_endx = SSC_BIT(SR_ENDTX), 6262306a36Sopenharmony_ci .ssc_endbuf = SSC_BIT(SR_TXBUFE), 6362306a36Sopenharmony_ci .ssc_error = SSC_BIT(SR_OVRUN), 6462306a36Sopenharmony_ci .pdc_enable = ATMEL_PDC_TXTEN, 6562306a36Sopenharmony_ci .pdc_disable = ATMEL_PDC_TXTDIS, 6662306a36Sopenharmony_ci}; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic struct atmel_ssc_mask ssc_rx_mask = { 6962306a36Sopenharmony_ci .ssc_enable = SSC_BIT(CR_RXEN), 7062306a36Sopenharmony_ci .ssc_disable = SSC_BIT(CR_RXDIS), 7162306a36Sopenharmony_ci .ssc_endx = SSC_BIT(SR_ENDRX), 7262306a36Sopenharmony_ci .ssc_endbuf = SSC_BIT(SR_RXBUFF), 7362306a36Sopenharmony_ci .ssc_error = SSC_BIT(SR_OVRUN), 7462306a36Sopenharmony_ci .pdc_enable = ATMEL_PDC_RXTEN, 7562306a36Sopenharmony_ci .pdc_disable = ATMEL_PDC_RXTDIS, 7662306a36Sopenharmony_ci}; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci/* 8062306a36Sopenharmony_ci * DMA parameters. 8162306a36Sopenharmony_ci */ 8262306a36Sopenharmony_cistatic struct atmel_pcm_dma_params ssc_dma_params[NUM_SSC_DEVICES][2] = { 8362306a36Sopenharmony_ci {{ 8462306a36Sopenharmony_ci .name = "SSC0 PCM out", 8562306a36Sopenharmony_ci .pdc = &pdc_tx_reg, 8662306a36Sopenharmony_ci .mask = &ssc_tx_mask, 8762306a36Sopenharmony_ci }, 8862306a36Sopenharmony_ci { 8962306a36Sopenharmony_ci .name = "SSC0 PCM in", 9062306a36Sopenharmony_ci .pdc = &pdc_rx_reg, 9162306a36Sopenharmony_ci .mask = &ssc_rx_mask, 9262306a36Sopenharmony_ci } }, 9362306a36Sopenharmony_ci {{ 9462306a36Sopenharmony_ci .name = "SSC1 PCM out", 9562306a36Sopenharmony_ci .pdc = &pdc_tx_reg, 9662306a36Sopenharmony_ci .mask = &ssc_tx_mask, 9762306a36Sopenharmony_ci }, 9862306a36Sopenharmony_ci { 9962306a36Sopenharmony_ci .name = "SSC1 PCM in", 10062306a36Sopenharmony_ci .pdc = &pdc_rx_reg, 10162306a36Sopenharmony_ci .mask = &ssc_rx_mask, 10262306a36Sopenharmony_ci } }, 10362306a36Sopenharmony_ci {{ 10462306a36Sopenharmony_ci .name = "SSC2 PCM out", 10562306a36Sopenharmony_ci .pdc = &pdc_tx_reg, 10662306a36Sopenharmony_ci .mask = &ssc_tx_mask, 10762306a36Sopenharmony_ci }, 10862306a36Sopenharmony_ci { 10962306a36Sopenharmony_ci .name = "SSC2 PCM in", 11062306a36Sopenharmony_ci .pdc = &pdc_rx_reg, 11162306a36Sopenharmony_ci .mask = &ssc_rx_mask, 11262306a36Sopenharmony_ci } }, 11362306a36Sopenharmony_ci}; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic struct atmel_ssc_info ssc_info[NUM_SSC_DEVICES] = { 11762306a36Sopenharmony_ci { 11862306a36Sopenharmony_ci .name = "ssc0", 11962306a36Sopenharmony_ci .dir_mask = SSC_DIR_MASK_UNUSED, 12062306a36Sopenharmony_ci .initialized = 0, 12162306a36Sopenharmony_ci }, 12262306a36Sopenharmony_ci { 12362306a36Sopenharmony_ci .name = "ssc1", 12462306a36Sopenharmony_ci .dir_mask = SSC_DIR_MASK_UNUSED, 12562306a36Sopenharmony_ci .initialized = 0, 12662306a36Sopenharmony_ci }, 12762306a36Sopenharmony_ci { 12862306a36Sopenharmony_ci .name = "ssc2", 12962306a36Sopenharmony_ci .dir_mask = SSC_DIR_MASK_UNUSED, 13062306a36Sopenharmony_ci .initialized = 0, 13162306a36Sopenharmony_ci }, 13262306a36Sopenharmony_ci}; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci/* 13662306a36Sopenharmony_ci * SSC interrupt handler. Passes PDC interrupts to the DMA 13762306a36Sopenharmony_ci * interrupt handler in the PCM driver. 13862306a36Sopenharmony_ci */ 13962306a36Sopenharmony_cistatic irqreturn_t atmel_ssc_interrupt(int irq, void *dev_id) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci struct atmel_ssc_info *ssc_p = dev_id; 14262306a36Sopenharmony_ci struct atmel_pcm_dma_params *dma_params; 14362306a36Sopenharmony_ci u32 ssc_sr; 14462306a36Sopenharmony_ci u32 ssc_substream_mask; 14562306a36Sopenharmony_ci int i; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci ssc_sr = (unsigned long)ssc_readl(ssc_p->ssc->regs, SR) 14862306a36Sopenharmony_ci & (unsigned long)ssc_readl(ssc_p->ssc->regs, IMR); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci /* 15162306a36Sopenharmony_ci * Loop through the substreams attached to this SSC. If 15262306a36Sopenharmony_ci * a DMA-related interrupt occurred on that substream, call 15362306a36Sopenharmony_ci * the DMA interrupt handler function, if one has been 15462306a36Sopenharmony_ci * registered in the dma_params structure by the PCM driver. 15562306a36Sopenharmony_ci */ 15662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ssc_p->dma_params); i++) { 15762306a36Sopenharmony_ci dma_params = ssc_p->dma_params[i]; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci if ((dma_params != NULL) && 16062306a36Sopenharmony_ci (dma_params->dma_intr_handler != NULL)) { 16162306a36Sopenharmony_ci ssc_substream_mask = (dma_params->mask->ssc_endx | 16262306a36Sopenharmony_ci dma_params->mask->ssc_endbuf); 16362306a36Sopenharmony_ci if (ssc_sr & ssc_substream_mask) { 16462306a36Sopenharmony_ci dma_params->dma_intr_handler(ssc_sr, 16562306a36Sopenharmony_ci dma_params-> 16662306a36Sopenharmony_ci substream); 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci return IRQ_HANDLED; 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci/* 17562306a36Sopenharmony_ci * When the bit clock is input, limit the maximum rate according to the 17662306a36Sopenharmony_ci * Serial Clock Ratio Considerations section from the SSC documentation: 17762306a36Sopenharmony_ci * 17862306a36Sopenharmony_ci * The Transmitter and the Receiver can be programmed to operate 17962306a36Sopenharmony_ci * with the clock signals provided on either the TK or RK pins. 18062306a36Sopenharmony_ci * This allows the SSC to support many slave-mode data transfers. 18162306a36Sopenharmony_ci * In this case, the maximum clock speed allowed on the RK pin is: 18262306a36Sopenharmony_ci * - Peripheral clock divided by 2 if Receiver Frame Synchro is input 18362306a36Sopenharmony_ci * - Peripheral clock divided by 3 if Receiver Frame Synchro is output 18462306a36Sopenharmony_ci * In addition, the maximum clock speed allowed on the TK pin is: 18562306a36Sopenharmony_ci * - Peripheral clock divided by 6 if Transmit Frame Synchro is input 18662306a36Sopenharmony_ci * - Peripheral clock divided by 2 if Transmit Frame Synchro is output 18762306a36Sopenharmony_ci * 18862306a36Sopenharmony_ci * When the bit clock is output, limit the rate according to the 18962306a36Sopenharmony_ci * SSC divider restrictions. 19062306a36Sopenharmony_ci */ 19162306a36Sopenharmony_cistatic int atmel_ssc_hw_rule_rate(struct snd_pcm_hw_params *params, 19262306a36Sopenharmony_ci struct snd_pcm_hw_rule *rule) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci struct atmel_ssc_info *ssc_p = rule->private; 19562306a36Sopenharmony_ci struct ssc_device *ssc = ssc_p->ssc; 19662306a36Sopenharmony_ci struct snd_interval *i = hw_param_interval(params, rule->var); 19762306a36Sopenharmony_ci struct snd_interval t; 19862306a36Sopenharmony_ci struct snd_ratnum r = { 19962306a36Sopenharmony_ci .den_min = 1, 20062306a36Sopenharmony_ci .den_max = 4095, 20162306a36Sopenharmony_ci .den_step = 1, 20262306a36Sopenharmony_ci }; 20362306a36Sopenharmony_ci unsigned int num = 0, den = 0; 20462306a36Sopenharmony_ci int frame_size; 20562306a36Sopenharmony_ci int mck_div = 2; 20662306a36Sopenharmony_ci int ret; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci frame_size = snd_soc_params_to_frame_size(params); 20962306a36Sopenharmony_ci if (frame_size < 0) 21062306a36Sopenharmony_ci return frame_size; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci switch (ssc_p->daifmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { 21362306a36Sopenharmony_ci case SND_SOC_DAIFMT_BC_FP: 21462306a36Sopenharmony_ci if ((ssc_p->dir_mask & SSC_DIR_MASK_CAPTURE) 21562306a36Sopenharmony_ci && ssc->clk_from_rk_pin) 21662306a36Sopenharmony_ci /* Receiver Frame Synchro (i.e. capture) 21762306a36Sopenharmony_ci * is output (format is _CFS) and the RK pin 21862306a36Sopenharmony_ci * is used for input (format is _CBM_). 21962306a36Sopenharmony_ci */ 22062306a36Sopenharmony_ci mck_div = 3; 22162306a36Sopenharmony_ci break; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci case SND_SOC_DAIFMT_BC_FC: 22462306a36Sopenharmony_ci if ((ssc_p->dir_mask & SSC_DIR_MASK_PLAYBACK) 22562306a36Sopenharmony_ci && !ssc->clk_from_rk_pin) 22662306a36Sopenharmony_ci /* Transmit Frame Synchro (i.e. playback) 22762306a36Sopenharmony_ci * is input (format is _CFM) and the TK pin 22862306a36Sopenharmony_ci * is used for input (format _CBM_ but not 22962306a36Sopenharmony_ci * using the RK pin). 23062306a36Sopenharmony_ci */ 23162306a36Sopenharmony_ci mck_div = 6; 23262306a36Sopenharmony_ci break; 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci switch (ssc_p->daifmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { 23662306a36Sopenharmony_ci case SND_SOC_DAIFMT_BP_FP: 23762306a36Sopenharmony_ci r.num = ssc_p->mck_rate / mck_div / frame_size; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci ret = snd_interval_ratnum(i, 1, &r, &num, &den); 24062306a36Sopenharmony_ci if (ret >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) { 24162306a36Sopenharmony_ci params->rate_num = num; 24262306a36Sopenharmony_ci params->rate_den = den; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci break; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci case SND_SOC_DAIFMT_BC_FP: 24762306a36Sopenharmony_ci case SND_SOC_DAIFMT_BC_FC: 24862306a36Sopenharmony_ci t.min = 8000; 24962306a36Sopenharmony_ci t.max = ssc_p->mck_rate / mck_div / frame_size; 25062306a36Sopenharmony_ci t.openmin = t.openmax = 0; 25162306a36Sopenharmony_ci t.integer = 0; 25262306a36Sopenharmony_ci ret = snd_interval_refine(i, &t); 25362306a36Sopenharmony_ci break; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci default: 25662306a36Sopenharmony_ci ret = -EINVAL; 25762306a36Sopenharmony_ci break; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci return ret; 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci/*-------------------------------------------------------------------------*\ 26462306a36Sopenharmony_ci * DAI functions 26562306a36Sopenharmony_ci\*-------------------------------------------------------------------------*/ 26662306a36Sopenharmony_ci/* 26762306a36Sopenharmony_ci * Startup. Only that one substream allowed in each direction. 26862306a36Sopenharmony_ci */ 26962306a36Sopenharmony_cistatic int atmel_ssc_startup(struct snd_pcm_substream *substream, 27062306a36Sopenharmony_ci struct snd_soc_dai *dai) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dai->dev); 27362306a36Sopenharmony_ci struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id]; 27462306a36Sopenharmony_ci struct atmel_pcm_dma_params *dma_params; 27562306a36Sopenharmony_ci int dir, dir_mask; 27662306a36Sopenharmony_ci int ret; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci pr_debug("atmel_ssc_startup: SSC_SR=0x%x\n", 27962306a36Sopenharmony_ci ssc_readl(ssc_p->ssc->regs, SR)); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci /* Enable PMC peripheral clock for this SSC */ 28262306a36Sopenharmony_ci pr_debug("atmel_ssc_dai: Starting clock\n"); 28362306a36Sopenharmony_ci ret = clk_enable(ssc_p->ssc->clk); 28462306a36Sopenharmony_ci if (ret) 28562306a36Sopenharmony_ci return ret; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci ssc_p->mck_rate = clk_get_rate(ssc_p->ssc->clk); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci /* Reset the SSC unless initialized to keep it in a clean state */ 29062306a36Sopenharmony_ci if (!ssc_p->initialized) 29162306a36Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST)); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 29462306a36Sopenharmony_ci dir = 0; 29562306a36Sopenharmony_ci dir_mask = SSC_DIR_MASK_PLAYBACK; 29662306a36Sopenharmony_ci } else { 29762306a36Sopenharmony_ci dir = 1; 29862306a36Sopenharmony_ci dir_mask = SSC_DIR_MASK_CAPTURE; 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci ret = snd_pcm_hw_rule_add(substream->runtime, 0, 30262306a36Sopenharmony_ci SNDRV_PCM_HW_PARAM_RATE, 30362306a36Sopenharmony_ci atmel_ssc_hw_rule_rate, 30462306a36Sopenharmony_ci ssc_p, 30562306a36Sopenharmony_ci SNDRV_PCM_HW_PARAM_FRAME_BITS, 30662306a36Sopenharmony_ci SNDRV_PCM_HW_PARAM_CHANNELS, -1); 30762306a36Sopenharmony_ci if (ret < 0) { 30862306a36Sopenharmony_ci dev_err(dai->dev, "Failed to specify rate rule: %d\n", ret); 30962306a36Sopenharmony_ci return ret; 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci dma_params = &ssc_dma_params[pdev->id][dir]; 31362306a36Sopenharmony_ci dma_params->ssc = ssc_p->ssc; 31462306a36Sopenharmony_ci dma_params->substream = substream; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci ssc_p->dma_params[dir] = dma_params; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci snd_soc_dai_set_dma_data(dai, substream, dma_params); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci if (ssc_p->dir_mask & dir_mask) 32162306a36Sopenharmony_ci return -EBUSY; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci ssc_p->dir_mask |= dir_mask; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci return 0; 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci/* 32962306a36Sopenharmony_ci * Shutdown. Clear DMA parameters and shutdown the SSC if there 33062306a36Sopenharmony_ci * are no other substreams open. 33162306a36Sopenharmony_ci */ 33262306a36Sopenharmony_cistatic void atmel_ssc_shutdown(struct snd_pcm_substream *substream, 33362306a36Sopenharmony_ci struct snd_soc_dai *dai) 33462306a36Sopenharmony_ci{ 33562306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dai->dev); 33662306a36Sopenharmony_ci struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id]; 33762306a36Sopenharmony_ci struct atmel_pcm_dma_params *dma_params; 33862306a36Sopenharmony_ci int dir, dir_mask; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 34162306a36Sopenharmony_ci dir = 0; 34262306a36Sopenharmony_ci else 34362306a36Sopenharmony_ci dir = 1; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci dma_params = ssc_p->dma_params[dir]; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci if (dma_params != NULL) { 34862306a36Sopenharmony_ci dma_params->ssc = NULL; 34962306a36Sopenharmony_ci dma_params->substream = NULL; 35062306a36Sopenharmony_ci ssc_p->dma_params[dir] = NULL; 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci dir_mask = 1 << dir; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci ssc_p->dir_mask &= ~dir_mask; 35662306a36Sopenharmony_ci if (!ssc_p->dir_mask) { 35762306a36Sopenharmony_ci if (ssc_p->initialized) { 35862306a36Sopenharmony_ci free_irq(ssc_p->ssc->irq, ssc_p); 35962306a36Sopenharmony_ci ssc_p->initialized = 0; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci /* Reset the SSC */ 36362306a36Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST)); 36462306a36Sopenharmony_ci /* Clear the SSC dividers */ 36562306a36Sopenharmony_ci ssc_p->cmr_div = ssc_p->tcmr_period = ssc_p->rcmr_period = 0; 36662306a36Sopenharmony_ci ssc_p->forced_divider = 0; 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci /* Shutdown the SSC clock. */ 37062306a36Sopenharmony_ci pr_debug("atmel_ssc_dai: Stopping clock\n"); 37162306a36Sopenharmony_ci clk_disable(ssc_p->ssc->clk); 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci/* 37662306a36Sopenharmony_ci * Record the DAI format for use in hw_params(). 37762306a36Sopenharmony_ci */ 37862306a36Sopenharmony_cistatic int atmel_ssc_set_dai_fmt(struct snd_soc_dai *cpu_dai, 37962306a36Sopenharmony_ci unsigned int fmt) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(cpu_dai->dev); 38262306a36Sopenharmony_ci struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id]; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci ssc_p->daifmt = fmt; 38562306a36Sopenharmony_ci return 0; 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci/* 38962306a36Sopenharmony_ci * Record SSC clock dividers for use in hw_params(). 39062306a36Sopenharmony_ci */ 39162306a36Sopenharmony_cistatic int atmel_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai, 39262306a36Sopenharmony_ci int div_id, int div) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(cpu_dai->dev); 39562306a36Sopenharmony_ci struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id]; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci switch (div_id) { 39862306a36Sopenharmony_ci case ATMEL_SSC_CMR_DIV: 39962306a36Sopenharmony_ci /* 40062306a36Sopenharmony_ci * The same master clock divider is used for both 40162306a36Sopenharmony_ci * transmit and receive, so if a value has already 40262306a36Sopenharmony_ci * been set, it must match this value. 40362306a36Sopenharmony_ci */ 40462306a36Sopenharmony_ci if (ssc_p->dir_mask != 40562306a36Sopenharmony_ci (SSC_DIR_MASK_PLAYBACK | SSC_DIR_MASK_CAPTURE)) 40662306a36Sopenharmony_ci ssc_p->cmr_div = div; 40762306a36Sopenharmony_ci else if (ssc_p->cmr_div == 0) 40862306a36Sopenharmony_ci ssc_p->cmr_div = div; 40962306a36Sopenharmony_ci else 41062306a36Sopenharmony_ci if (div != ssc_p->cmr_div) 41162306a36Sopenharmony_ci return -EBUSY; 41262306a36Sopenharmony_ci ssc_p->forced_divider |= BIT(ATMEL_SSC_CMR_DIV); 41362306a36Sopenharmony_ci break; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci case ATMEL_SSC_TCMR_PERIOD: 41662306a36Sopenharmony_ci ssc_p->tcmr_period = div; 41762306a36Sopenharmony_ci ssc_p->forced_divider |= BIT(ATMEL_SSC_TCMR_PERIOD); 41862306a36Sopenharmony_ci break; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci case ATMEL_SSC_RCMR_PERIOD: 42162306a36Sopenharmony_ci ssc_p->rcmr_period = div; 42262306a36Sopenharmony_ci ssc_p->forced_divider |= BIT(ATMEL_SSC_RCMR_PERIOD); 42362306a36Sopenharmony_ci break; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci default: 42662306a36Sopenharmony_ci return -EINVAL; 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci return 0; 43062306a36Sopenharmony_ci} 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci/* Is the cpu-dai master of the frame clock? */ 43362306a36Sopenharmony_cistatic int atmel_ssc_cfs(struct atmel_ssc_info *ssc_p) 43462306a36Sopenharmony_ci{ 43562306a36Sopenharmony_ci switch (ssc_p->daifmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { 43662306a36Sopenharmony_ci case SND_SOC_DAIFMT_BC_FP: 43762306a36Sopenharmony_ci case SND_SOC_DAIFMT_BP_FP: 43862306a36Sopenharmony_ci return 1; 43962306a36Sopenharmony_ci } 44062306a36Sopenharmony_ci return 0; 44162306a36Sopenharmony_ci} 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci/* Is the cpu-dai master of the bit clock? */ 44462306a36Sopenharmony_cistatic int atmel_ssc_cbs(struct atmel_ssc_info *ssc_p) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci switch (ssc_p->daifmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { 44762306a36Sopenharmony_ci case SND_SOC_DAIFMT_BP_FC: 44862306a36Sopenharmony_ci case SND_SOC_DAIFMT_BP_FP: 44962306a36Sopenharmony_ci return 1; 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci return 0; 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci/* 45562306a36Sopenharmony_ci * Configure the SSC. 45662306a36Sopenharmony_ci */ 45762306a36Sopenharmony_cistatic int atmel_ssc_hw_params(struct snd_pcm_substream *substream, 45862306a36Sopenharmony_ci struct snd_pcm_hw_params *params, 45962306a36Sopenharmony_ci struct snd_soc_dai *dai) 46062306a36Sopenharmony_ci{ 46162306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dai->dev); 46262306a36Sopenharmony_ci int id = pdev->id; 46362306a36Sopenharmony_ci struct atmel_ssc_info *ssc_p = &ssc_info[id]; 46462306a36Sopenharmony_ci struct ssc_device *ssc = ssc_p->ssc; 46562306a36Sopenharmony_ci struct atmel_pcm_dma_params *dma_params; 46662306a36Sopenharmony_ci int dir, channels, bits; 46762306a36Sopenharmony_ci u32 tfmr, rfmr, tcmr, rcmr; 46862306a36Sopenharmony_ci int ret; 46962306a36Sopenharmony_ci int fslen, fslen_ext, fs_osync, fs_edge; 47062306a36Sopenharmony_ci u32 cmr_div; 47162306a36Sopenharmony_ci u32 tcmr_period; 47262306a36Sopenharmony_ci u32 rcmr_period; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci /* 47562306a36Sopenharmony_ci * Currently, there is only one set of dma params for 47662306a36Sopenharmony_ci * each direction. If more are added, this code will 47762306a36Sopenharmony_ci * have to be changed to select the proper set. 47862306a36Sopenharmony_ci */ 47962306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 48062306a36Sopenharmony_ci dir = 0; 48162306a36Sopenharmony_ci else 48262306a36Sopenharmony_ci dir = 1; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci /* 48562306a36Sopenharmony_ci * If the cpu dai should provide BCLK, but noone has provided the 48662306a36Sopenharmony_ci * divider needed for that to work, fall back to something sensible. 48762306a36Sopenharmony_ci */ 48862306a36Sopenharmony_ci cmr_div = ssc_p->cmr_div; 48962306a36Sopenharmony_ci if (!(ssc_p->forced_divider & BIT(ATMEL_SSC_CMR_DIV)) && 49062306a36Sopenharmony_ci atmel_ssc_cbs(ssc_p)) { 49162306a36Sopenharmony_ci int bclk_rate = snd_soc_params_to_bclk(params); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci if (bclk_rate < 0) { 49462306a36Sopenharmony_ci dev_err(dai->dev, "unable to calculate cmr_div: %d\n", 49562306a36Sopenharmony_ci bclk_rate); 49662306a36Sopenharmony_ci return bclk_rate; 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci cmr_div = DIV_ROUND_CLOSEST(ssc_p->mck_rate, 2 * bclk_rate); 50062306a36Sopenharmony_ci } 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci /* 50362306a36Sopenharmony_ci * If the cpu dai should provide LRCLK, but noone has provided the 50462306a36Sopenharmony_ci * dividers needed for that to work, fall back to something sensible. 50562306a36Sopenharmony_ci */ 50662306a36Sopenharmony_ci tcmr_period = ssc_p->tcmr_period; 50762306a36Sopenharmony_ci rcmr_period = ssc_p->rcmr_period; 50862306a36Sopenharmony_ci if (atmel_ssc_cfs(ssc_p)) { 50962306a36Sopenharmony_ci int frame_size = snd_soc_params_to_frame_size(params); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci if (frame_size < 0) { 51262306a36Sopenharmony_ci dev_err(dai->dev, 51362306a36Sopenharmony_ci "unable to calculate tx/rx cmr_period: %d\n", 51462306a36Sopenharmony_ci frame_size); 51562306a36Sopenharmony_ci return frame_size; 51662306a36Sopenharmony_ci } 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci if (!(ssc_p->forced_divider & BIT(ATMEL_SSC_TCMR_PERIOD))) 51962306a36Sopenharmony_ci tcmr_period = frame_size / 2 - 1; 52062306a36Sopenharmony_ci if (!(ssc_p->forced_divider & BIT(ATMEL_SSC_RCMR_PERIOD))) 52162306a36Sopenharmony_ci rcmr_period = frame_size / 2 - 1; 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci dma_params = ssc_p->dma_params[dir]; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci channels = params_channels(params); 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci /* 52962306a36Sopenharmony_ci * Determine sample size in bits and the PDC increment. 53062306a36Sopenharmony_ci */ 53162306a36Sopenharmony_ci switch (params_format(params)) { 53262306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S8: 53362306a36Sopenharmony_ci bits = 8; 53462306a36Sopenharmony_ci dma_params->pdc_xfer_size = 1; 53562306a36Sopenharmony_ci break; 53662306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_LE: 53762306a36Sopenharmony_ci bits = 16; 53862306a36Sopenharmony_ci dma_params->pdc_xfer_size = 2; 53962306a36Sopenharmony_ci break; 54062306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S24_LE: 54162306a36Sopenharmony_ci bits = 24; 54262306a36Sopenharmony_ci dma_params->pdc_xfer_size = 4; 54362306a36Sopenharmony_ci break; 54462306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S32_LE: 54562306a36Sopenharmony_ci bits = 32; 54662306a36Sopenharmony_ci dma_params->pdc_xfer_size = 4; 54762306a36Sopenharmony_ci break; 54862306a36Sopenharmony_ci default: 54962306a36Sopenharmony_ci printk(KERN_WARNING "atmel_ssc_dai: unsupported PCM format"); 55062306a36Sopenharmony_ci return -EINVAL; 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci /* 55462306a36Sopenharmony_ci * Compute SSC register settings. 55562306a36Sopenharmony_ci */ 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci fslen_ext = (bits - 1) / 16; 55862306a36Sopenharmony_ci fslen = (bits - 1) % 16; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci switch (ssc_p->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) { 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci case SND_SOC_DAIFMT_LEFT_J: 56362306a36Sopenharmony_ci fs_osync = SSC_FSOS_POSITIVE; 56462306a36Sopenharmony_ci fs_edge = SSC_START_RISING_RF; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci rcmr = SSC_BF(RCMR_STTDLY, 0); 56762306a36Sopenharmony_ci tcmr = SSC_BF(TCMR_STTDLY, 0); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci break; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci case SND_SOC_DAIFMT_I2S: 57262306a36Sopenharmony_ci fs_osync = SSC_FSOS_NEGATIVE; 57362306a36Sopenharmony_ci fs_edge = SSC_START_FALLING_RF; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci rcmr = SSC_BF(RCMR_STTDLY, 1); 57662306a36Sopenharmony_ci tcmr = SSC_BF(TCMR_STTDLY, 1); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci break; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci case SND_SOC_DAIFMT_DSP_A: 58162306a36Sopenharmony_ci /* 58262306a36Sopenharmony_ci * DSP/PCM Mode A format 58362306a36Sopenharmony_ci * 58462306a36Sopenharmony_ci * Data is transferred on first BCLK after LRC pulse rising 58562306a36Sopenharmony_ci * edge.If stereo, the right channel data is contiguous with 58662306a36Sopenharmony_ci * the left channel data. 58762306a36Sopenharmony_ci */ 58862306a36Sopenharmony_ci fs_osync = SSC_FSOS_POSITIVE; 58962306a36Sopenharmony_ci fs_edge = SSC_START_RISING_RF; 59062306a36Sopenharmony_ci fslen = fslen_ext = 0; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci rcmr = SSC_BF(RCMR_STTDLY, 1); 59362306a36Sopenharmony_ci tcmr = SSC_BF(TCMR_STTDLY, 1); 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci break; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci default: 59862306a36Sopenharmony_ci printk(KERN_WARNING "atmel_ssc_dai: unsupported DAI format 0x%x\n", 59962306a36Sopenharmony_ci ssc_p->daifmt); 60062306a36Sopenharmony_ci return -EINVAL; 60162306a36Sopenharmony_ci } 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci if (!atmel_ssc_cfs(ssc_p)) { 60462306a36Sopenharmony_ci fslen = fslen_ext = 0; 60562306a36Sopenharmony_ci rcmr_period = tcmr_period = 0; 60662306a36Sopenharmony_ci fs_osync = SSC_FSOS_NONE; 60762306a36Sopenharmony_ci } 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci rcmr |= SSC_BF(RCMR_START, fs_edge); 61062306a36Sopenharmony_ci tcmr |= SSC_BF(TCMR_START, fs_edge); 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci if (atmel_ssc_cbs(ssc_p)) { 61362306a36Sopenharmony_ci /* 61462306a36Sopenharmony_ci * SSC provides BCLK 61562306a36Sopenharmony_ci * 61662306a36Sopenharmony_ci * The SSC transmit and receive clocks are generated from the 61762306a36Sopenharmony_ci * MCK divider, and the BCLK signal is output 61862306a36Sopenharmony_ci * on the SSC TK line. 61962306a36Sopenharmony_ci */ 62062306a36Sopenharmony_ci rcmr |= SSC_BF(RCMR_CKS, SSC_CKS_DIV) 62162306a36Sopenharmony_ci | SSC_BF(RCMR_CKO, SSC_CKO_NONE); 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci tcmr |= SSC_BF(TCMR_CKS, SSC_CKS_DIV) 62462306a36Sopenharmony_ci | SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS); 62562306a36Sopenharmony_ci } else { 62662306a36Sopenharmony_ci rcmr |= SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ? 62762306a36Sopenharmony_ci SSC_CKS_PIN : SSC_CKS_CLOCK) 62862306a36Sopenharmony_ci | SSC_BF(RCMR_CKO, SSC_CKO_NONE); 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci tcmr |= SSC_BF(TCMR_CKS, ssc->clk_from_rk_pin ? 63162306a36Sopenharmony_ci SSC_CKS_CLOCK : SSC_CKS_PIN) 63262306a36Sopenharmony_ci | SSC_BF(TCMR_CKO, SSC_CKO_NONE); 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci rcmr |= SSC_BF(RCMR_PERIOD, rcmr_period) 63662306a36Sopenharmony_ci | SSC_BF(RCMR_CKI, SSC_CKI_RISING); 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci tcmr |= SSC_BF(TCMR_PERIOD, tcmr_period) 63962306a36Sopenharmony_ci | SSC_BF(TCMR_CKI, SSC_CKI_FALLING); 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci rfmr = SSC_BF(RFMR_FSLEN_EXT, fslen_ext) 64262306a36Sopenharmony_ci | SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE) 64362306a36Sopenharmony_ci | SSC_BF(RFMR_FSOS, fs_osync) 64462306a36Sopenharmony_ci | SSC_BF(RFMR_FSLEN, fslen) 64562306a36Sopenharmony_ci | SSC_BF(RFMR_DATNB, (channels - 1)) 64662306a36Sopenharmony_ci | SSC_BIT(RFMR_MSBF) 64762306a36Sopenharmony_ci | SSC_BF(RFMR_LOOP, 0) 64862306a36Sopenharmony_ci | SSC_BF(RFMR_DATLEN, (bits - 1)); 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci tfmr = SSC_BF(TFMR_FSLEN_EXT, fslen_ext) 65162306a36Sopenharmony_ci | SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE) 65262306a36Sopenharmony_ci | SSC_BF(TFMR_FSDEN, 0) 65362306a36Sopenharmony_ci | SSC_BF(TFMR_FSOS, fs_osync) 65462306a36Sopenharmony_ci | SSC_BF(TFMR_FSLEN, fslen) 65562306a36Sopenharmony_ci | SSC_BF(TFMR_DATNB, (channels - 1)) 65662306a36Sopenharmony_ci | SSC_BIT(TFMR_MSBF) 65762306a36Sopenharmony_ci | SSC_BF(TFMR_DATDEF, 0) 65862306a36Sopenharmony_ci | SSC_BF(TFMR_DATLEN, (bits - 1)); 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci if (fslen_ext && !ssc->pdata->has_fslen_ext) { 66162306a36Sopenharmony_ci dev_err(dai->dev, "sample size %d is too large for SSC device\n", 66262306a36Sopenharmony_ci bits); 66362306a36Sopenharmony_ci return -EINVAL; 66462306a36Sopenharmony_ci } 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci pr_debug("atmel_ssc_hw_params: " 66762306a36Sopenharmony_ci "RCMR=%08x RFMR=%08x TCMR=%08x TFMR=%08x\n", 66862306a36Sopenharmony_ci rcmr, rfmr, tcmr, tfmr); 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci if (!ssc_p->initialized) { 67162306a36Sopenharmony_ci if (!ssc_p->ssc->pdata->use_dma) { 67262306a36Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, PDC_RPR, 0); 67362306a36Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, PDC_RCR, 0); 67462306a36Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, PDC_RNPR, 0); 67562306a36Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, PDC_RNCR, 0); 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, PDC_TPR, 0); 67862306a36Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, PDC_TCR, 0); 67962306a36Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, PDC_TNPR, 0); 68062306a36Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, PDC_TNCR, 0); 68162306a36Sopenharmony_ci } 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci ret = request_irq(ssc_p->ssc->irq, atmel_ssc_interrupt, 0, 68462306a36Sopenharmony_ci ssc_p->name, ssc_p); 68562306a36Sopenharmony_ci if (ret < 0) { 68662306a36Sopenharmony_ci printk(KERN_WARNING 68762306a36Sopenharmony_ci "atmel_ssc_dai: request_irq failure\n"); 68862306a36Sopenharmony_ci pr_debug("Atmel_ssc_dai: Stopping clock\n"); 68962306a36Sopenharmony_ci clk_disable(ssc_p->ssc->clk); 69062306a36Sopenharmony_ci return ret; 69162306a36Sopenharmony_ci } 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci ssc_p->initialized = 1; 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci /* set SSC clock mode register */ 69762306a36Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, CMR, cmr_div); 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci /* set receive clock mode and format */ 70062306a36Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, RCMR, rcmr); 70162306a36Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, RFMR, rfmr); 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci /* set transmit clock mode and format */ 70462306a36Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, TCMR, tcmr); 70562306a36Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, TFMR, tfmr); 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci pr_debug("atmel_ssc_dai,hw_params: SSC initialized\n"); 70862306a36Sopenharmony_ci return 0; 70962306a36Sopenharmony_ci} 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_cistatic int atmel_ssc_prepare(struct snd_pcm_substream *substream, 71362306a36Sopenharmony_ci struct snd_soc_dai *dai) 71462306a36Sopenharmony_ci{ 71562306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dai->dev); 71662306a36Sopenharmony_ci struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id]; 71762306a36Sopenharmony_ci struct atmel_pcm_dma_params *dma_params; 71862306a36Sopenharmony_ci int dir; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 72162306a36Sopenharmony_ci dir = 0; 72262306a36Sopenharmony_ci else 72362306a36Sopenharmony_ci dir = 1; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci dma_params = ssc_p->dma_params[dir]; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, CR, dma_params->mask->ssc_disable); 72862306a36Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, IDR, dma_params->mask->ssc_error); 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci pr_debug("%s enabled SSC_SR=0x%08x\n", 73162306a36Sopenharmony_ci dir ? "receive" : "transmit", 73262306a36Sopenharmony_ci ssc_readl(ssc_p->ssc->regs, SR)); 73362306a36Sopenharmony_ci return 0; 73462306a36Sopenharmony_ci} 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_cistatic int atmel_ssc_trigger(struct snd_pcm_substream *substream, 73762306a36Sopenharmony_ci int cmd, struct snd_soc_dai *dai) 73862306a36Sopenharmony_ci{ 73962306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dai->dev); 74062306a36Sopenharmony_ci struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id]; 74162306a36Sopenharmony_ci struct atmel_pcm_dma_params *dma_params; 74262306a36Sopenharmony_ci int dir; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 74562306a36Sopenharmony_ci dir = 0; 74662306a36Sopenharmony_ci else 74762306a36Sopenharmony_ci dir = 1; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci dma_params = ssc_p->dma_params[dir]; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci switch (cmd) { 75262306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 75362306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 75462306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 75562306a36Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, CR, dma_params->mask->ssc_enable); 75662306a36Sopenharmony_ci break; 75762306a36Sopenharmony_ci default: 75862306a36Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, CR, dma_params->mask->ssc_disable); 75962306a36Sopenharmony_ci break; 76062306a36Sopenharmony_ci } 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci return 0; 76362306a36Sopenharmony_ci} 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_cistatic int atmel_ssc_suspend(struct snd_soc_component *component) 76662306a36Sopenharmony_ci{ 76762306a36Sopenharmony_ci struct atmel_ssc_info *ssc_p; 76862306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(component->dev); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci if (!snd_soc_component_active(component)) 77162306a36Sopenharmony_ci return 0; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci ssc_p = &ssc_info[pdev->id]; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci /* Save the status register before disabling transmit and receive */ 77662306a36Sopenharmony_ci ssc_p->ssc_state.ssc_sr = ssc_readl(ssc_p->ssc->regs, SR); 77762306a36Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_TXDIS) | SSC_BIT(CR_RXDIS)); 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci /* Save the current interrupt mask, then disable unmasked interrupts */ 78062306a36Sopenharmony_ci ssc_p->ssc_state.ssc_imr = ssc_readl(ssc_p->ssc->regs, IMR); 78162306a36Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, IDR, ssc_p->ssc_state.ssc_imr); 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci ssc_p->ssc_state.ssc_cmr = ssc_readl(ssc_p->ssc->regs, CMR); 78462306a36Sopenharmony_ci ssc_p->ssc_state.ssc_rcmr = ssc_readl(ssc_p->ssc->regs, RCMR); 78562306a36Sopenharmony_ci ssc_p->ssc_state.ssc_rfmr = ssc_readl(ssc_p->ssc->regs, RFMR); 78662306a36Sopenharmony_ci ssc_p->ssc_state.ssc_tcmr = ssc_readl(ssc_p->ssc->regs, TCMR); 78762306a36Sopenharmony_ci ssc_p->ssc_state.ssc_tfmr = ssc_readl(ssc_p->ssc->regs, TFMR); 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci return 0; 79062306a36Sopenharmony_ci} 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_cistatic int atmel_ssc_resume(struct snd_soc_component *component) 79362306a36Sopenharmony_ci{ 79462306a36Sopenharmony_ci struct atmel_ssc_info *ssc_p; 79562306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(component->dev); 79662306a36Sopenharmony_ci u32 cr; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci if (!snd_soc_component_active(component)) 79962306a36Sopenharmony_ci return 0; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci ssc_p = &ssc_info[pdev->id]; 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci /* restore SSC register settings */ 80462306a36Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, TFMR, ssc_p->ssc_state.ssc_tfmr); 80562306a36Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, TCMR, ssc_p->ssc_state.ssc_tcmr); 80662306a36Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, RFMR, ssc_p->ssc_state.ssc_rfmr); 80762306a36Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, RCMR, ssc_p->ssc_state.ssc_rcmr); 80862306a36Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, CMR, ssc_p->ssc_state.ssc_cmr); 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci /* re-enable interrupts */ 81162306a36Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, IER, ssc_p->ssc_state.ssc_imr); 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci /* Re-enable receive and transmit as appropriate */ 81462306a36Sopenharmony_ci cr = 0; 81562306a36Sopenharmony_ci cr |= 81662306a36Sopenharmony_ci (ssc_p->ssc_state.ssc_sr & SSC_BIT(SR_RXEN)) ? SSC_BIT(CR_RXEN) : 0; 81762306a36Sopenharmony_ci cr |= 81862306a36Sopenharmony_ci (ssc_p->ssc_state.ssc_sr & SSC_BIT(SR_TXEN)) ? SSC_BIT(CR_TXEN) : 0; 81962306a36Sopenharmony_ci ssc_writel(ssc_p->ssc->regs, CR, cr); 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci return 0; 82262306a36Sopenharmony_ci} 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci#define ATMEL_SSC_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\ 82562306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_cistatic const struct snd_soc_dai_ops atmel_ssc_dai_ops = { 82862306a36Sopenharmony_ci .startup = atmel_ssc_startup, 82962306a36Sopenharmony_ci .shutdown = atmel_ssc_shutdown, 83062306a36Sopenharmony_ci .prepare = atmel_ssc_prepare, 83162306a36Sopenharmony_ci .trigger = atmel_ssc_trigger, 83262306a36Sopenharmony_ci .hw_params = atmel_ssc_hw_params, 83362306a36Sopenharmony_ci .set_fmt = atmel_ssc_set_dai_fmt, 83462306a36Sopenharmony_ci .set_clkdiv = atmel_ssc_set_dai_clkdiv, 83562306a36Sopenharmony_ci}; 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_cistatic struct snd_soc_dai_driver atmel_ssc_dai = { 83862306a36Sopenharmony_ci .playback = { 83962306a36Sopenharmony_ci .channels_min = 1, 84062306a36Sopenharmony_ci .channels_max = 2, 84162306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS, 84262306a36Sopenharmony_ci .rate_min = 8000, 84362306a36Sopenharmony_ci .rate_max = 384000, 84462306a36Sopenharmony_ci .formats = ATMEL_SSC_FORMATS,}, 84562306a36Sopenharmony_ci .capture = { 84662306a36Sopenharmony_ci .channels_min = 1, 84762306a36Sopenharmony_ci .channels_max = 2, 84862306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS, 84962306a36Sopenharmony_ci .rate_min = 8000, 85062306a36Sopenharmony_ci .rate_max = 384000, 85162306a36Sopenharmony_ci .formats = ATMEL_SSC_FORMATS,}, 85262306a36Sopenharmony_ci .ops = &atmel_ssc_dai_ops, 85362306a36Sopenharmony_ci}; 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_cistatic const struct snd_soc_component_driver atmel_ssc_component = { 85662306a36Sopenharmony_ci .name = "atmel-ssc", 85762306a36Sopenharmony_ci .suspend = pm_ptr(atmel_ssc_suspend), 85862306a36Sopenharmony_ci .resume = pm_ptr(atmel_ssc_resume), 85962306a36Sopenharmony_ci .legacy_dai_naming = 1, 86062306a36Sopenharmony_ci}; 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_cistatic int asoc_ssc_init(struct device *dev) 86362306a36Sopenharmony_ci{ 86462306a36Sopenharmony_ci struct ssc_device *ssc = dev_get_drvdata(dev); 86562306a36Sopenharmony_ci int ret; 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci ret = devm_snd_soc_register_component(dev, &atmel_ssc_component, 86862306a36Sopenharmony_ci &atmel_ssc_dai, 1); 86962306a36Sopenharmony_ci if (ret) { 87062306a36Sopenharmony_ci dev_err(dev, "Could not register DAI: %d\n", ret); 87162306a36Sopenharmony_ci return ret; 87262306a36Sopenharmony_ci } 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci if (ssc->pdata->use_dma) 87562306a36Sopenharmony_ci ret = atmel_pcm_dma_platform_register(dev); 87662306a36Sopenharmony_ci else 87762306a36Sopenharmony_ci ret = atmel_pcm_pdc_platform_register(dev); 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci if (ret) { 88062306a36Sopenharmony_ci dev_err(dev, "Could not register PCM: %d\n", ret); 88162306a36Sopenharmony_ci return ret; 88262306a36Sopenharmony_ci } 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci return 0; 88562306a36Sopenharmony_ci} 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci/** 88862306a36Sopenharmony_ci * atmel_ssc_set_audio - Allocate the specified SSC for audio use. 88962306a36Sopenharmony_ci * @ssc_id: SSD ID in [0, NUM_SSC_DEVICES[ 89062306a36Sopenharmony_ci */ 89162306a36Sopenharmony_ciint atmel_ssc_set_audio(int ssc_id) 89262306a36Sopenharmony_ci{ 89362306a36Sopenharmony_ci struct ssc_device *ssc; 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci /* If we can grab the SSC briefly to parent the DAI device off it */ 89662306a36Sopenharmony_ci ssc = ssc_request(ssc_id); 89762306a36Sopenharmony_ci if (IS_ERR(ssc)) { 89862306a36Sopenharmony_ci pr_err("Unable to parent ASoC SSC DAI on SSC: %ld\n", 89962306a36Sopenharmony_ci PTR_ERR(ssc)); 90062306a36Sopenharmony_ci return PTR_ERR(ssc); 90162306a36Sopenharmony_ci } else { 90262306a36Sopenharmony_ci ssc_info[ssc_id].ssc = ssc; 90362306a36Sopenharmony_ci } 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci return asoc_ssc_init(&ssc->pdev->dev); 90662306a36Sopenharmony_ci} 90762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(atmel_ssc_set_audio); 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_civoid atmel_ssc_put_audio(int ssc_id) 91062306a36Sopenharmony_ci{ 91162306a36Sopenharmony_ci struct ssc_device *ssc = ssc_info[ssc_id].ssc; 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci ssc_free(ssc); 91462306a36Sopenharmony_ci} 91562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(atmel_ssc_put_audio); 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci/* Module information */ 91862306a36Sopenharmony_ciMODULE_AUTHOR("Sedji Gaouaou, sedji.gaouaou@atmel.com, www.atmel.com"); 91962306a36Sopenharmony_ciMODULE_DESCRIPTION("ATMEL SSC ASoC Interface"); 92062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 921