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