xref: /linux/sound/soc/fsl/fsl_asrc.c (revision cab04ab5900fea6655f2a49d1f94c37200b63a59)
12ba28053SFabio Estevam // SPDX-License-Identifier: GPL-2.0
22ba28053SFabio Estevam //
32ba28053SFabio Estevam // Freescale ASRC ALSA SoC Digital Audio Interface (DAI) driver
42ba28053SFabio Estevam //
52ba28053SFabio Estevam // Copyright (C) 2014 Freescale Semiconductor, Inc.
62ba28053SFabio Estevam //
72ba28053SFabio Estevam // Author: Nicolin Chen <nicoleotsuka@gmail.com>
83117bb31SNicolin Chen 
93117bb31SNicolin Chen #include <linux/clk.h>
103117bb31SNicolin Chen #include <linux/delay.h>
113117bb31SNicolin Chen #include <linux/dma-mapping.h>
123117bb31SNicolin Chen #include <linux/module.h>
133117bb31SNicolin Chen #include <linux/of_platform.h>
143117bb31SNicolin Chen #include <linux/platform_data/dma-imx.h>
153117bb31SNicolin Chen #include <linux/pm_runtime.h>
163117bb31SNicolin Chen #include <sound/dmaengine_pcm.h>
173117bb31SNicolin Chen #include <sound/pcm_params.h>
183117bb31SNicolin Chen 
193117bb31SNicolin Chen #include "fsl_asrc.h"
203117bb31SNicolin Chen 
213117bb31SNicolin Chen #define IDEAL_RATIO_DECIMAL_DEPTH 26
223117bb31SNicolin Chen 
233117bb31SNicolin Chen #define pair_err(fmt, ...) \
247470704dSShengjiu Wang 	dev_err(&asrc->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__)
253117bb31SNicolin Chen 
263117bb31SNicolin Chen #define pair_dbg(fmt, ...) \
277470704dSShengjiu Wang 	dev_dbg(&asrc->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__)
283117bb31SNicolin Chen 
293117bb31SNicolin Chen /* Corresponding to process_option */
30d281bf5dSS.j. Wang static unsigned int supported_asrc_rate[] = {
31d281bf5dSS.j. Wang 	5512, 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000,
32d281bf5dSS.j. Wang 	64000, 88200, 96000, 128000, 176400, 192000,
333117bb31SNicolin Chen };
343117bb31SNicolin Chen 
35d281bf5dSS.j. Wang static struct snd_pcm_hw_constraint_list fsl_asrc_rate_constraints = {
36d281bf5dSS.j. Wang 	.count = ARRAY_SIZE(supported_asrc_rate),
37d281bf5dSS.j. Wang 	.list = supported_asrc_rate,
383117bb31SNicolin Chen };
393117bb31SNicolin Chen 
4045e039d9SPierre-Louis Bossart /*
413117bb31SNicolin Chen  * The following tables map the relationship between asrc_inclk/asrc_outclk in
423117bb31SNicolin Chen  * fsl_asrc.h and the registers of ASRCSR
433117bb31SNicolin Chen  */
44c05f10f2SShengjiu Wang static unsigned char input_clk_map_imx35[ASRC_CLK_MAP_LEN] = {
453117bb31SNicolin Chen 	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
46c05f10f2SShengjiu Wang 	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
47c05f10f2SShengjiu Wang 	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
483117bb31SNicolin Chen };
493117bb31SNicolin Chen 
50c05f10f2SShengjiu Wang static unsigned char output_clk_map_imx35[ASRC_CLK_MAP_LEN] = {
513117bb31SNicolin Chen 	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
52c05f10f2SShengjiu Wang 	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
53c05f10f2SShengjiu Wang 	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
543117bb31SNicolin Chen };
553117bb31SNicolin Chen 
563117bb31SNicolin Chen /* i.MX53 uses the same map for input and output */
57c05f10f2SShengjiu Wang static unsigned char input_clk_map_imx53[ASRC_CLK_MAP_LEN] = {
583117bb31SNicolin Chen /*	0x0  0x1  0x2  0x3  0x4  0x5  0x6  0x7  0x8  0x9  0xa  0xb  0xc  0xd  0xe  0xf */
593117bb31SNicolin Chen 	0x0, 0x1, 0x2, 0x7, 0x4, 0x5, 0x6, 0x3, 0x8, 0x9, 0xa, 0xb, 0xc, 0xf, 0xe, 0xd,
60c05f10f2SShengjiu Wang 	0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
61c05f10f2SShengjiu Wang 	0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
623117bb31SNicolin Chen };
633117bb31SNicolin Chen 
64c05f10f2SShengjiu Wang static unsigned char output_clk_map_imx53[ASRC_CLK_MAP_LEN] = {
653117bb31SNicolin Chen /*	0x0  0x1  0x2  0x3  0x4  0x5  0x6  0x7  0x8  0x9  0xa  0xb  0xc  0xd  0xe  0xf */
663117bb31SNicolin Chen 	0x8, 0x9, 0xa, 0x7, 0xc, 0x5, 0x6, 0xb, 0x0, 0x1, 0x2, 0x3, 0x4, 0xf, 0xe, 0xd,
67c05f10f2SShengjiu Wang 	0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
68c05f10f2SShengjiu Wang 	0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
693117bb31SNicolin Chen };
703117bb31SNicolin Chen 
7145e039d9SPierre-Louis Bossart /*
72c05f10f2SShengjiu Wang  * i.MX8QM/i.MX8QXP uses the same map for input and output.
73c05f10f2SShengjiu Wang  * clk_map_imx8qm[0] is for i.MX8QM asrc0
74c05f10f2SShengjiu Wang  * clk_map_imx8qm[1] is for i.MX8QM asrc1
75c05f10f2SShengjiu Wang  * clk_map_imx8qxp[0] is for i.MX8QXP asrc0
76c05f10f2SShengjiu Wang  * clk_map_imx8qxp[1] is for i.MX8QXP asrc1
77c05f10f2SShengjiu Wang  */
78c05f10f2SShengjiu Wang static unsigned char clk_map_imx8qm[2][ASRC_CLK_MAP_LEN] = {
79c05f10f2SShengjiu Wang 	{
80c05f10f2SShengjiu Wang 	0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x0,
81c05f10f2SShengjiu Wang 	0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
82c05f10f2SShengjiu Wang 	0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
83c05f10f2SShengjiu Wang 	},
84c05f10f2SShengjiu Wang 	{
85c05f10f2SShengjiu Wang 	0xf, 0xf, 0xf, 0xf, 0xf, 0x7, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x0,
86c05f10f2SShengjiu Wang 	0x0, 0x1, 0x2, 0x3, 0xb, 0xc, 0xf, 0xf, 0xd, 0xe, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
87c05f10f2SShengjiu Wang 	0x4, 0x5, 0x6, 0xf, 0x8, 0x9, 0xa, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
88c05f10f2SShengjiu Wang 	},
89c05f10f2SShengjiu Wang };
90c05f10f2SShengjiu Wang 
91c05f10f2SShengjiu Wang static unsigned char clk_map_imx8qxp[2][ASRC_CLK_MAP_LEN] = {
92c05f10f2SShengjiu Wang 	{
93c05f10f2SShengjiu Wang 	0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x0,
94c05f10f2SShengjiu Wang 	0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0xf, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xf, 0xf,
95c05f10f2SShengjiu Wang 	0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
96c05f10f2SShengjiu Wang 	},
97c05f10f2SShengjiu Wang 	{
98c05f10f2SShengjiu Wang 	0xf, 0xf, 0xf, 0xf, 0xf, 0x7, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x0,
99c05f10f2SShengjiu Wang 	0x0, 0x1, 0x2, 0x3, 0x7, 0x8, 0xf, 0xf, 0x9, 0xa, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
100c05f10f2SShengjiu Wang 	0xf, 0xf, 0x6, 0xf, 0xf, 0xf, 0xa, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
101c05f10f2SShengjiu Wang 	},
102c05f10f2SShengjiu Wang };
1033117bb31SNicolin Chen 
1043117bb31SNicolin Chen /**
10545e039d9SPierre-Louis Bossart  * fsl_asrc_sel_proc - Select the pre-processing and post-processing options
10645e039d9SPierre-Louis Bossart  * @inrate: input sample rate
10745e039d9SPierre-Louis Bossart  * @outrate: output sample rate
10845e039d9SPierre-Louis Bossart  * @pre_proc: return value for pre-processing option
10945e039d9SPierre-Louis Bossart  * @post_proc: return value for post-processing option
11045e039d9SPierre-Louis Bossart  *
1114aecaa0aSS.j. Wang  * Make sure to exclude following unsupported cases before
1124aecaa0aSS.j. Wang  * calling this function:
1134aecaa0aSS.j. Wang  * 1) inrate > 8.125 * outrate
1144aecaa0aSS.j. Wang  * 2) inrate > 16.125 * outrate
1154aecaa0aSS.j. Wang  *
1164aecaa0aSS.j. Wang  */
1174aecaa0aSS.j. Wang static void fsl_asrc_sel_proc(int inrate, int outrate,
1184aecaa0aSS.j. Wang 			     int *pre_proc, int *post_proc)
1194aecaa0aSS.j. Wang {
1204aecaa0aSS.j. Wang 	bool post_proc_cond2;
1214aecaa0aSS.j. Wang 	bool post_proc_cond0;
1224aecaa0aSS.j. Wang 
1234aecaa0aSS.j. Wang 	/* select pre_proc between [0, 2] */
1244aecaa0aSS.j. Wang 	if (inrate * 8 > 33 * outrate)
1254aecaa0aSS.j. Wang 		*pre_proc = 2;
1264aecaa0aSS.j. Wang 	else if (inrate * 8 > 15 * outrate) {
1274aecaa0aSS.j. Wang 		if (inrate > 152000)
1284aecaa0aSS.j. Wang 			*pre_proc = 2;
1294aecaa0aSS.j. Wang 		else
1304aecaa0aSS.j. Wang 			*pre_proc = 1;
1314aecaa0aSS.j. Wang 	} else if (inrate < 76000)
1324aecaa0aSS.j. Wang 		*pre_proc = 0;
1334aecaa0aSS.j. Wang 	else if (inrate > 152000)
1344aecaa0aSS.j. Wang 		*pre_proc = 2;
1354aecaa0aSS.j. Wang 	else
1364aecaa0aSS.j. Wang 		*pre_proc = 1;
1374aecaa0aSS.j. Wang 
1384aecaa0aSS.j. Wang 	/* Condition for selection of post-processing */
1394aecaa0aSS.j. Wang 	post_proc_cond2 = (inrate * 15 > outrate * 16 && outrate < 56000) ||
1404aecaa0aSS.j. Wang 			  (inrate > 56000 && outrate < 56000);
1414aecaa0aSS.j. Wang 	post_proc_cond0 = inrate * 23 < outrate * 8;
1424aecaa0aSS.j. Wang 
1434aecaa0aSS.j. Wang 	if (post_proc_cond2)
1444aecaa0aSS.j. Wang 		*post_proc = 2;
1454aecaa0aSS.j. Wang 	else if (post_proc_cond0)
1464aecaa0aSS.j. Wang 		*post_proc = 0;
1474aecaa0aSS.j. Wang 	else
1484aecaa0aSS.j. Wang 		*post_proc = 1;
1494aecaa0aSS.j. Wang }
1504aecaa0aSS.j. Wang 
1514aecaa0aSS.j. Wang /**
15245e039d9SPierre-Louis Bossart  * fsl_asrc_request_pair - Request ASRC pair
15345e039d9SPierre-Louis Bossart  * @channels: number of channels
15445e039d9SPierre-Louis Bossart  * @pair: pointer to pair
1553117bb31SNicolin Chen  *
1563117bb31SNicolin Chen  * It assigns pair by the order of A->C->B because allocation of pair B,
1573117bb31SNicolin Chen  * within range [ANCA, ANCA+ANCB-1], depends on the channels of pair A
1583117bb31SNicolin Chen  * while pair A and pair C are comparatively independent.
1593117bb31SNicolin Chen  */
160c16e923dSChenTao static int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair)
1613117bb31SNicolin Chen {
1623117bb31SNicolin Chen 	enum asrc_pair_index index = ASRC_INVALID_PAIR;
1637470704dSShengjiu Wang 	struct fsl_asrc *asrc = pair->asrc;
1647470704dSShengjiu Wang 	struct device *dev = &asrc->pdev->dev;
1653117bb31SNicolin Chen 	unsigned long lock_flags;
1663117bb31SNicolin Chen 	int i, ret = 0;
1673117bb31SNicolin Chen 
1687470704dSShengjiu Wang 	spin_lock_irqsave(&asrc->lock, lock_flags);
1693117bb31SNicolin Chen 
1703117bb31SNicolin Chen 	for (i = ASRC_PAIR_A; i < ASRC_PAIR_MAX_NUM; i++) {
1717470704dSShengjiu Wang 		if (asrc->pair[i] != NULL)
1723117bb31SNicolin Chen 			continue;
1733117bb31SNicolin Chen 
1743117bb31SNicolin Chen 		index = i;
1753117bb31SNicolin Chen 
1763117bb31SNicolin Chen 		if (i != ASRC_PAIR_B)
1773117bb31SNicolin Chen 			break;
1783117bb31SNicolin Chen 	}
1793117bb31SNicolin Chen 
1803117bb31SNicolin Chen 	if (index == ASRC_INVALID_PAIR) {
1813117bb31SNicolin Chen 		dev_err(dev, "all pairs are busy now\n");
1823117bb31SNicolin Chen 		ret = -EBUSY;
1837470704dSShengjiu Wang 	} else if (asrc->channel_avail < channels) {
1843117bb31SNicolin Chen 		dev_err(dev, "can't afford required channels: %d\n", channels);
1853117bb31SNicolin Chen 		ret = -EINVAL;
1863117bb31SNicolin Chen 	} else {
1877470704dSShengjiu Wang 		asrc->channel_avail -= channels;
1887470704dSShengjiu Wang 		asrc->pair[index] = pair;
1893117bb31SNicolin Chen 		pair->channels = channels;
1903117bb31SNicolin Chen 		pair->index = index;
1913117bb31SNicolin Chen 	}
1923117bb31SNicolin Chen 
1937470704dSShengjiu Wang 	spin_unlock_irqrestore(&asrc->lock, lock_flags);
1943117bb31SNicolin Chen 
1953117bb31SNicolin Chen 	return ret;
1963117bb31SNicolin Chen }
1973117bb31SNicolin Chen 
1983117bb31SNicolin Chen /**
19945e039d9SPierre-Louis Bossart  * fsl_asrc_release_pair - Release ASRC pair
20045e039d9SPierre-Louis Bossart  * @pair: pair to release
2013117bb31SNicolin Chen  *
2027470704dSShengjiu Wang  * It clears the resource from asrc and releases the occupied channels.
2033117bb31SNicolin Chen  */
204c16e923dSChenTao static void fsl_asrc_release_pair(struct fsl_asrc_pair *pair)
2053117bb31SNicolin Chen {
2067470704dSShengjiu Wang 	struct fsl_asrc *asrc = pair->asrc;
2073117bb31SNicolin Chen 	enum asrc_pair_index index = pair->index;
2083117bb31SNicolin Chen 	unsigned long lock_flags;
2093117bb31SNicolin Chen 
2103117bb31SNicolin Chen 	/* Make sure the pair is disabled */
2117470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCTR,
2123117bb31SNicolin Chen 			   ASRCTR_ASRCEi_MASK(index), 0);
2133117bb31SNicolin Chen 
2147470704dSShengjiu Wang 	spin_lock_irqsave(&asrc->lock, lock_flags);
2153117bb31SNicolin Chen 
2167470704dSShengjiu Wang 	asrc->channel_avail += pair->channels;
2177470704dSShengjiu Wang 	asrc->pair[index] = NULL;
2183117bb31SNicolin Chen 	pair->error = 0;
2193117bb31SNicolin Chen 
2207470704dSShengjiu Wang 	spin_unlock_irqrestore(&asrc->lock, lock_flags);
2213117bb31SNicolin Chen }
2223117bb31SNicolin Chen 
2233117bb31SNicolin Chen /**
22445e039d9SPierre-Louis Bossart  * fsl_asrc_set_watermarks- configure input and output thresholds
22545e039d9SPierre-Louis Bossart  * @pair: pointer to pair
22645e039d9SPierre-Louis Bossart  * @in: input threshold
22745e039d9SPierre-Louis Bossart  * @out: output threshold
2283117bb31SNicolin Chen  */
2293117bb31SNicolin Chen static void fsl_asrc_set_watermarks(struct fsl_asrc_pair *pair, u32 in, u32 out)
2303117bb31SNicolin Chen {
2317470704dSShengjiu Wang 	struct fsl_asrc *asrc = pair->asrc;
2323117bb31SNicolin Chen 	enum asrc_pair_index index = pair->index;
2333117bb31SNicolin Chen 
2347470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRMCR(index),
2353117bb31SNicolin Chen 			   ASRMCRi_EXTTHRSHi_MASK |
2363117bb31SNicolin Chen 			   ASRMCRi_INFIFO_THRESHOLD_MASK |
2373117bb31SNicolin Chen 			   ASRMCRi_OUTFIFO_THRESHOLD_MASK,
2383117bb31SNicolin Chen 			   ASRMCRi_EXTTHRSHi |
2393117bb31SNicolin Chen 			   ASRMCRi_INFIFO_THRESHOLD(in) |
2403117bb31SNicolin Chen 			   ASRMCRi_OUTFIFO_THRESHOLD(out));
2413117bb31SNicolin Chen }
2423117bb31SNicolin Chen 
2433117bb31SNicolin Chen /**
24445e039d9SPierre-Louis Bossart  * fsl_asrc_cal_asrck_divisor - Calculate the total divisor between asrck clock rate and sample rate
24545e039d9SPierre-Louis Bossart  * @pair: pointer to pair
24645e039d9SPierre-Louis Bossart  * @div: divider
2473117bb31SNicolin Chen  *
2483117bb31SNicolin Chen  * It follows the formula clk_rate = samplerate * (2 ^ prescaler) * divider
2493117bb31SNicolin Chen  */
2503117bb31SNicolin Chen static u32 fsl_asrc_cal_asrck_divisor(struct fsl_asrc_pair *pair, u32 div)
2513117bb31SNicolin Chen {
2523117bb31SNicolin Chen 	u32 ps;
2533117bb31SNicolin Chen 
2543117bb31SNicolin Chen 	/* Calculate the divisors: prescaler [2^0, 2^7], divder [1, 8] */
2553117bb31SNicolin Chen 	for (ps = 0; div > 8; ps++)
2563117bb31SNicolin Chen 		div >>= 1;
2573117bb31SNicolin Chen 
2583117bb31SNicolin Chen 	return ((div - 1) << ASRCDRi_AxCPi_WIDTH) | ps;
2593117bb31SNicolin Chen }
2603117bb31SNicolin Chen 
2613117bb31SNicolin Chen /**
26245e039d9SPierre-Louis Bossart  * fsl_asrc_set_ideal_ratio - Calculate and set the ratio for Ideal Ratio mode only
26345e039d9SPierre-Louis Bossart  * @pair: pointer to pair
26445e039d9SPierre-Louis Bossart  * @inrate: input rate
26545e039d9SPierre-Louis Bossart  * @outrate: output rate
2663117bb31SNicolin Chen  *
2673117bb31SNicolin Chen  * The ratio is a 32-bit fixed point value with 26 fractional bits.
2683117bb31SNicolin Chen  */
2693117bb31SNicolin Chen static int fsl_asrc_set_ideal_ratio(struct fsl_asrc_pair *pair,
2703117bb31SNicolin Chen 				    int inrate, int outrate)
2713117bb31SNicolin Chen {
2727470704dSShengjiu Wang 	struct fsl_asrc *asrc = pair->asrc;
2733117bb31SNicolin Chen 	enum asrc_pair_index index = pair->index;
2743117bb31SNicolin Chen 	unsigned long ratio;
2753117bb31SNicolin Chen 	int i;
2763117bb31SNicolin Chen 
2773117bb31SNicolin Chen 	if (!outrate) {
2783117bb31SNicolin Chen 		pair_err("output rate should not be zero\n");
2793117bb31SNicolin Chen 		return -EINVAL;
2803117bb31SNicolin Chen 	}
2813117bb31SNicolin Chen 
2823117bb31SNicolin Chen 	/* Calculate the intergal part of the ratio */
2833117bb31SNicolin Chen 	ratio = (inrate / outrate) << IDEAL_RATIO_DECIMAL_DEPTH;
2843117bb31SNicolin Chen 
2853117bb31SNicolin Chen 	/* ... and then the 26 depth decimal part */
2863117bb31SNicolin Chen 	inrate %= outrate;
2873117bb31SNicolin Chen 
2883117bb31SNicolin Chen 	for (i = 1; i <= IDEAL_RATIO_DECIMAL_DEPTH; i++) {
2893117bb31SNicolin Chen 		inrate <<= 1;
2903117bb31SNicolin Chen 
2913117bb31SNicolin Chen 		if (inrate < outrate)
2923117bb31SNicolin Chen 			continue;
2933117bb31SNicolin Chen 
2943117bb31SNicolin Chen 		ratio |= 1 << (IDEAL_RATIO_DECIMAL_DEPTH - i);
2953117bb31SNicolin Chen 		inrate -= outrate;
2963117bb31SNicolin Chen 
2973117bb31SNicolin Chen 		if (!inrate)
2983117bb31SNicolin Chen 			break;
2993117bb31SNicolin Chen 	}
3003117bb31SNicolin Chen 
3017470704dSShengjiu Wang 	regmap_write(asrc->regmap, REG_ASRIDRL(index), ratio);
3027470704dSShengjiu Wang 	regmap_write(asrc->regmap, REG_ASRIDRH(index), ratio >> 24);
3033117bb31SNicolin Chen 
3043117bb31SNicolin Chen 	return 0;
3053117bb31SNicolin Chen }
3063117bb31SNicolin Chen 
3073117bb31SNicolin Chen /**
30845e039d9SPierre-Louis Bossart  * fsl_asrc_config_pair - Configure the assigned ASRC pair
30945e039d9SPierre-Louis Bossart  * @pair: pointer to pair
31045e039d9SPierre-Louis Bossart  * @use_ideal_rate: boolean configuration
3113117bb31SNicolin Chen  *
3123117bb31SNicolin Chen  * It configures those ASRC registers according to a configuration instance
3133117bb31SNicolin Chen  * of struct asrc_config which includes in/output sample rate, width, channel
3143117bb31SNicolin Chen  * and clock settings.
315b39eb1e2SShengjiu Wang  *
316b39eb1e2SShengjiu Wang  * Note:
317b39eb1e2SShengjiu Wang  * The ideal ratio configuration can work with a flexible clock rate setting.
318b39eb1e2SShengjiu Wang  * Using IDEAL_RATIO_RATE gives a faster converting speed but overloads ASRC.
319b39eb1e2SShengjiu Wang  * For a regular audio playback, the clock rate should not be slower than an
320b39eb1e2SShengjiu Wang  * clock rate aligning with the output sample rate; For a use case requiring
321b39eb1e2SShengjiu Wang  * faster conversion, set use_ideal_rate to have the faster speed.
3223117bb31SNicolin Chen  */
323b39eb1e2SShengjiu Wang static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool use_ideal_rate)
3243117bb31SNicolin Chen {
325be7bd03fSShengjiu Wang 	struct fsl_asrc_pair_priv *pair_priv = pair->private;
326be7bd03fSShengjiu Wang 	struct asrc_config *config = pair_priv->config;
3277470704dSShengjiu Wang 	struct fsl_asrc *asrc = pair->asrc;
328be7bd03fSShengjiu Wang 	struct fsl_asrc_priv *asrc_priv = asrc->private;
3293117bb31SNicolin Chen 	enum asrc_pair_index index = pair->index;
3304bf62571SShengjiu Wang 	enum asrc_word_width input_word_width;
3314bf62571SShengjiu Wang 	enum asrc_word_width output_word_width;
3324e13eb72SNicolin Chen 	u32 inrate, outrate, indiv, outdiv;
333b39eb1e2SShengjiu Wang 	u32 clk_index[2], div[2], rem[2];
334b39eb1e2SShengjiu Wang 	u64 clk_rate;
3353117bb31SNicolin Chen 	int in, out, channels;
3364aecaa0aSS.j. Wang 	int pre_proc, post_proc;
3373117bb31SNicolin Chen 	struct clk *clk;
3384e13eb72SNicolin Chen 	bool ideal;
3393117bb31SNicolin Chen 
3403117bb31SNicolin Chen 	if (!config) {
3413117bb31SNicolin Chen 		pair_err("invalid pair config\n");
3423117bb31SNicolin Chen 		return -EINVAL;
3433117bb31SNicolin Chen 	}
3443117bb31SNicolin Chen 
3453117bb31SNicolin Chen 	/* Validate channels */
3463117bb31SNicolin Chen 	if (config->channel_num < 1 || config->channel_num > 10) {
3473117bb31SNicolin Chen 		pair_err("does not support %d channels\n", config->channel_num);
3483117bb31SNicolin Chen 		return -EINVAL;
3493117bb31SNicolin Chen 	}
3503117bb31SNicolin Chen 
3514bf62571SShengjiu Wang 	switch (snd_pcm_format_width(config->input_format)) {
3524bf62571SShengjiu Wang 	case 8:
3534bf62571SShengjiu Wang 		input_word_width = ASRC_WIDTH_8_BIT;
3544bf62571SShengjiu Wang 		break;
3554bf62571SShengjiu Wang 	case 16:
3564bf62571SShengjiu Wang 		input_word_width = ASRC_WIDTH_16_BIT;
3574bf62571SShengjiu Wang 		break;
3584bf62571SShengjiu Wang 	case 24:
3594bf62571SShengjiu Wang 		input_word_width = ASRC_WIDTH_24_BIT;
3604bf62571SShengjiu Wang 		break;
3614bf62571SShengjiu Wang 	default:
3624bf62571SShengjiu Wang 		pair_err("does not support this input format, %d\n",
3634bf62571SShengjiu Wang 			 config->input_format);
3644bf62571SShengjiu Wang 		return -EINVAL;
3654bf62571SShengjiu Wang 	}
3664bf62571SShengjiu Wang 
3674bf62571SShengjiu Wang 	switch (snd_pcm_format_width(config->output_format)) {
3684bf62571SShengjiu Wang 	case 16:
3694bf62571SShengjiu Wang 		output_word_width = ASRC_WIDTH_16_BIT;
3704bf62571SShengjiu Wang 		break;
3714bf62571SShengjiu Wang 	case 24:
3724bf62571SShengjiu Wang 		output_word_width = ASRC_WIDTH_24_BIT;
3734bf62571SShengjiu Wang 		break;
3744bf62571SShengjiu Wang 	default:
3754bf62571SShengjiu Wang 		pair_err("does not support this output format, %d\n",
3764bf62571SShengjiu Wang 			 config->output_format);
3773117bb31SNicolin Chen 		return -EINVAL;
3783117bb31SNicolin Chen 	}
3793117bb31SNicolin Chen 
3804e13eb72SNicolin Chen 	inrate = config->input_sample_rate;
3814e13eb72SNicolin Chen 	outrate = config->output_sample_rate;
3824e13eb72SNicolin Chen 	ideal = config->inclk == INCLK_NONE;
3834e13eb72SNicolin Chen 
3843117bb31SNicolin Chen 	/* Validate input and output sample rates */
385d281bf5dSS.j. Wang 	for (in = 0; in < ARRAY_SIZE(supported_asrc_rate); in++)
386d281bf5dSS.j. Wang 		if (inrate == supported_asrc_rate[in])
3873117bb31SNicolin Chen 			break;
3883117bb31SNicolin Chen 
389d281bf5dSS.j. Wang 	if (in == ARRAY_SIZE(supported_asrc_rate)) {
3903117bb31SNicolin Chen 		pair_err("unsupported input sample rate: %dHz\n", inrate);
3913117bb31SNicolin Chen 		return -EINVAL;
3923117bb31SNicolin Chen 	}
3933117bb31SNicolin Chen 
3943117bb31SNicolin Chen 	for (out = 0; out < ARRAY_SIZE(supported_asrc_rate); out++)
3953117bb31SNicolin Chen 		if (outrate == supported_asrc_rate[out])
3963117bb31SNicolin Chen 			break;
3973117bb31SNicolin Chen 
3983117bb31SNicolin Chen 	if (out == ARRAY_SIZE(supported_asrc_rate)) {
3993117bb31SNicolin Chen 		pair_err("unsupported output sample rate: %dHz\n", outrate);
4003117bb31SNicolin Chen 		return -EINVAL;
4013117bb31SNicolin Chen 	}
4023117bb31SNicolin Chen 
403d281bf5dSS.j. Wang 	if ((outrate >= 5512 && outrate <= 30000) &&
404b06c58c2SS.j. Wang 	    (outrate > 24 * inrate || inrate > 8 * outrate)) {
405fff6e03cSZidan Wang 		pair_err("exceed supported ratio range [1/24, 8] for \
406fff6e03cSZidan Wang 				inrate/outrate: %d/%d\n", inrate, outrate);
407fff6e03cSZidan Wang 		return -EINVAL;
408fff6e03cSZidan Wang 	}
409fff6e03cSZidan Wang 
4103117bb31SNicolin Chen 	/* Validate input and output clock sources */
411be7bd03fSShengjiu Wang 	clk_index[IN] = asrc_priv->clk_map[IN][config->inclk];
412be7bd03fSShengjiu Wang 	clk_index[OUT] = asrc_priv->clk_map[OUT][config->outclk];
4133117bb31SNicolin Chen 
4143117bb31SNicolin Chen 	/* We only have output clock for ideal ratio mode */
415be7bd03fSShengjiu Wang 	clk = asrc_priv->asrck_clk[clk_index[ideal ? OUT : IN]];
4163117bb31SNicolin Chen 
417b39eb1e2SShengjiu Wang 	clk_rate = clk_get_rate(clk);
418b39eb1e2SShengjiu Wang 	rem[IN] = do_div(clk_rate, inrate);
419b39eb1e2SShengjiu Wang 	div[IN] = (u32)clk_rate;
420b39eb1e2SShengjiu Wang 
421b39eb1e2SShengjiu Wang 	/*
422b39eb1e2SShengjiu Wang 	 * The divider range is [1, 1024], defined by the hardware. For non-
423b39eb1e2SShengjiu Wang 	 * ideal ratio configuration, clock rate has to be strictly aligned
424b39eb1e2SShengjiu Wang 	 * with the sample rate. For ideal ratio configuration, clock rates
425b39eb1e2SShengjiu Wang 	 * only result in different converting speeds. So remainder does not
426b39eb1e2SShengjiu Wang 	 * matter, as long as we keep the divider within its valid range.
427b39eb1e2SShengjiu Wang 	 */
428b39eb1e2SShengjiu Wang 	if (div[IN] == 0 || (!ideal && (div[IN] > 1024 || rem[IN] != 0))) {
4293117bb31SNicolin Chen 		pair_err("failed to support input sample rate %dHz by asrck_%x\n",
4303117bb31SNicolin Chen 				inrate, clk_index[ideal ? OUT : IN]);
4313117bb31SNicolin Chen 		return -EINVAL;
4323117bb31SNicolin Chen 	}
4333117bb31SNicolin Chen 
434b39eb1e2SShengjiu Wang 	div[IN] = min_t(u32, 1024, div[IN]);
435b39eb1e2SShengjiu Wang 
436be7bd03fSShengjiu Wang 	clk = asrc_priv->asrck_clk[clk_index[OUT]];
437b39eb1e2SShengjiu Wang 	clk_rate = clk_get_rate(clk);
438b39eb1e2SShengjiu Wang 	if (ideal && use_ideal_rate)
439b39eb1e2SShengjiu Wang 		rem[OUT] = do_div(clk_rate, IDEAL_RATIO_RATE);
4403117bb31SNicolin Chen 	else
441b39eb1e2SShengjiu Wang 		rem[OUT] = do_div(clk_rate, outrate);
442b39eb1e2SShengjiu Wang 	div[OUT] = clk_rate;
4433117bb31SNicolin Chen 
444b39eb1e2SShengjiu Wang 	/* Output divider has the same limitation as the input one */
445b39eb1e2SShengjiu Wang 	if (div[OUT] == 0 || (!ideal && (div[OUT] > 1024 || rem[OUT] != 0))) {
4463117bb31SNicolin Chen 		pair_err("failed to support output sample rate %dHz by asrck_%x\n",
4473117bb31SNicolin Chen 				outrate, clk_index[OUT]);
4483117bb31SNicolin Chen 		return -EINVAL;
4493117bb31SNicolin Chen 	}
4503117bb31SNicolin Chen 
451b39eb1e2SShengjiu Wang 	div[OUT] = min_t(u32, 1024, div[OUT]);
452b39eb1e2SShengjiu Wang 
4533117bb31SNicolin Chen 	/* Set the channel number */
4543117bb31SNicolin Chen 	channels = config->channel_num;
4553117bb31SNicolin Chen 
456be7bd03fSShengjiu Wang 	if (asrc_priv->soc->channel_bits < 4)
4573117bb31SNicolin Chen 		channels /= 2;
4583117bb31SNicolin Chen 
4593117bb31SNicolin Chen 	/* Update channels for current pair */
4607470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCNCR,
461be7bd03fSShengjiu Wang 			   ASRCNCR_ANCi_MASK(index, asrc_priv->soc->channel_bits),
462be7bd03fSShengjiu Wang 			   ASRCNCR_ANCi(index, channels, asrc_priv->soc->channel_bits));
4633117bb31SNicolin Chen 
4643117bb31SNicolin Chen 	/* Default setting: Automatic selection for processing mode */
4657470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCTR,
4663117bb31SNicolin Chen 			   ASRCTR_ATSi_MASK(index), ASRCTR_ATS(index));
4677470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCTR,
4683117bb31SNicolin Chen 			   ASRCTR_USRi_MASK(index), 0);
4693117bb31SNicolin Chen 
4703117bb31SNicolin Chen 	/* Set the input and output clock sources */
4717470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCSR,
4723117bb31SNicolin Chen 			   ASRCSR_AICSi_MASK(index) | ASRCSR_AOCSi_MASK(index),
4733117bb31SNicolin Chen 			   ASRCSR_AICS(index, clk_index[IN]) |
4743117bb31SNicolin Chen 			   ASRCSR_AOCS(index, clk_index[OUT]));
4753117bb31SNicolin Chen 
4763117bb31SNicolin Chen 	/* Calculate the input clock divisors */
4773117bb31SNicolin Chen 	indiv = fsl_asrc_cal_asrck_divisor(pair, div[IN]);
4783117bb31SNicolin Chen 	outdiv = fsl_asrc_cal_asrck_divisor(pair, div[OUT]);
4793117bb31SNicolin Chen 
4803117bb31SNicolin Chen 	/* Suppose indiv and outdiv includes prescaler, so add its MASK too */
4817470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCDR(index),
4823117bb31SNicolin Chen 			   ASRCDRi_AOCPi_MASK(index) | ASRCDRi_AICPi_MASK(index) |
4833117bb31SNicolin Chen 			   ASRCDRi_AOCDi_MASK(index) | ASRCDRi_AICDi_MASK(index),
4843117bb31SNicolin Chen 			   ASRCDRi_AOCP(index, outdiv) | ASRCDRi_AICP(index, indiv));
4853117bb31SNicolin Chen 
4863117bb31SNicolin Chen 	/* Implement word_width configurations */
4877470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRMCR1(index),
4883117bb31SNicolin Chen 			   ASRMCR1i_OW16_MASK | ASRMCR1i_IWD_MASK,
4894bf62571SShengjiu Wang 			   ASRMCR1i_OW16(output_word_width) |
4904bf62571SShengjiu Wang 			   ASRMCR1i_IWD(input_word_width));
4913117bb31SNicolin Chen 
4923117bb31SNicolin Chen 	/* Enable BUFFER STALL */
4937470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRMCR(index),
4943117bb31SNicolin Chen 			   ASRMCRi_BUFSTALLi_MASK, ASRMCRi_BUFSTALLi);
4953117bb31SNicolin Chen 
4963117bb31SNicolin Chen 	/* Set default thresholds for input and output FIFO */
4973117bb31SNicolin Chen 	fsl_asrc_set_watermarks(pair, ASRC_INPUTFIFO_THRESHOLD,
4983117bb31SNicolin Chen 				ASRC_INPUTFIFO_THRESHOLD);
4993117bb31SNicolin Chen 
5004091fb95SMasahiro Yamada 	/* Configure the following only for Ideal Ratio mode */
5013117bb31SNicolin Chen 	if (!ideal)
5023117bb31SNicolin Chen 		return 0;
5033117bb31SNicolin Chen 
5043117bb31SNicolin Chen 	/* Clear ASTSx bit to use Ideal Ratio mode */
5057470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCTR,
5063117bb31SNicolin Chen 			   ASRCTR_ATSi_MASK(index), 0);
5073117bb31SNicolin Chen 
5083117bb31SNicolin Chen 	/* Enable Ideal Ratio mode */
5097470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCTR,
5103117bb31SNicolin Chen 			   ASRCTR_IDRi_MASK(index) | ASRCTR_USRi_MASK(index),
5113117bb31SNicolin Chen 			   ASRCTR_IDR(index) | ASRCTR_USR(index));
5123117bb31SNicolin Chen 
5134aecaa0aSS.j. Wang 	fsl_asrc_sel_proc(inrate, outrate, &pre_proc, &post_proc);
5144aecaa0aSS.j. Wang 
5153117bb31SNicolin Chen 	/* Apply configurations for pre- and post-processing */
5167470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCFG,
5173117bb31SNicolin Chen 			   ASRCFG_PREMODi_MASK(index) |	ASRCFG_POSTMODi_MASK(index),
5184aecaa0aSS.j. Wang 			   ASRCFG_PREMOD(index, pre_proc) |
5194aecaa0aSS.j. Wang 			   ASRCFG_POSTMOD(index, post_proc));
5203117bb31SNicolin Chen 
5213117bb31SNicolin Chen 	return fsl_asrc_set_ideal_ratio(pair, inrate, outrate);
5223117bb31SNicolin Chen }
5233117bb31SNicolin Chen 
5243117bb31SNicolin Chen /**
52545e039d9SPierre-Louis Bossart  * fsl_asrc_start_pair - Start the assigned ASRC pair
52645e039d9SPierre-Louis Bossart  * @pair: pointer to pair
5273117bb31SNicolin Chen  *
5283117bb31SNicolin Chen  * It enables the assigned pair and makes it stopped at the stall level.
5293117bb31SNicolin Chen  */
5303117bb31SNicolin Chen static void fsl_asrc_start_pair(struct fsl_asrc_pair *pair)
5313117bb31SNicolin Chen {
5327470704dSShengjiu Wang 	struct fsl_asrc *asrc = pair->asrc;
5333117bb31SNicolin Chen 	enum asrc_pair_index index = pair->index;
5343117bb31SNicolin Chen 	int reg, retry = 10, i;
5353117bb31SNicolin Chen 
5363117bb31SNicolin Chen 	/* Enable the current pair */
5377470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCTR,
5383117bb31SNicolin Chen 			   ASRCTR_ASRCEi_MASK(index), ASRCTR_ASRCE(index));
5393117bb31SNicolin Chen 
5403117bb31SNicolin Chen 	/* Wait for status of initialization */
5413117bb31SNicolin Chen 	do {
5423117bb31SNicolin Chen 		udelay(5);
5437470704dSShengjiu Wang 		regmap_read(asrc->regmap, REG_ASRCFG, &reg);
5443117bb31SNicolin Chen 		reg &= ASRCFG_INIRQi_MASK(index);
5453117bb31SNicolin Chen 	} while (!reg && --retry);
5463117bb31SNicolin Chen 
5473117bb31SNicolin Chen 	/* Make the input fifo to ASRC STALL level */
5487470704dSShengjiu Wang 	regmap_read(asrc->regmap, REG_ASRCNCR, &reg);
5493117bb31SNicolin Chen 	for (i = 0; i < pair->channels * 4; i++)
5507470704dSShengjiu Wang 		regmap_write(asrc->regmap, REG_ASRDI(index), 0);
5513117bb31SNicolin Chen 
5523117bb31SNicolin Chen 	/* Enable overload interrupt */
5537470704dSShengjiu Wang 	regmap_write(asrc->regmap, REG_ASRIER, ASRIER_AOLIE);
5543117bb31SNicolin Chen }
5553117bb31SNicolin Chen 
5563117bb31SNicolin Chen /**
55745e039d9SPierre-Louis Bossart  * fsl_asrc_stop_pair - Stop the assigned ASRC pair
55845e039d9SPierre-Louis Bossart  * @pair: pointer to pair
5593117bb31SNicolin Chen  */
5603117bb31SNicolin Chen static void fsl_asrc_stop_pair(struct fsl_asrc_pair *pair)
5613117bb31SNicolin Chen {
5627470704dSShengjiu Wang 	struct fsl_asrc *asrc = pair->asrc;
5633117bb31SNicolin Chen 	enum asrc_pair_index index = pair->index;
5643117bb31SNicolin Chen 
5653117bb31SNicolin Chen 	/* Stop the current pair */
5667470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCTR,
5673117bb31SNicolin Chen 			   ASRCTR_ASRCEi_MASK(index), 0);
5683117bb31SNicolin Chen }
5693117bb31SNicolin Chen 
5703117bb31SNicolin Chen /**
57145e039d9SPierre-Louis Bossart  * fsl_asrc_get_dma_channel- Get DMA channel according to the pair and direction.
57245e039d9SPierre-Louis Bossart  * @pair: pointer to pair
57345e039d9SPierre-Louis Bossart  * @dir: DMA direction
5743117bb31SNicolin Chen  */
575cff1f8b4Sshengjiu wang static struct dma_chan *fsl_asrc_get_dma_channel(struct fsl_asrc_pair *pair,
576cff1f8b4Sshengjiu wang 						 bool dir)
5773117bb31SNicolin Chen {
5787470704dSShengjiu Wang 	struct fsl_asrc *asrc = pair->asrc;
5793117bb31SNicolin Chen 	enum asrc_pair_index index = pair->index;
5803117bb31SNicolin Chen 	char name[4];
5813117bb31SNicolin Chen 
5823117bb31SNicolin Chen 	sprintf(name, "%cx%c", dir == IN ? 'r' : 't', index + 'a');
5833117bb31SNicolin Chen 
5847470704dSShengjiu Wang 	return dma_request_slave_channel(&asrc->pdev->dev, name);
5853117bb31SNicolin Chen }
5863117bb31SNicolin Chen 
58753f67a78SS.j. Wang static int fsl_asrc_dai_startup(struct snd_pcm_substream *substream,
58853f67a78SS.j. Wang 				struct snd_soc_dai *dai)
58953f67a78SS.j. Wang {
5907470704dSShengjiu Wang 	struct fsl_asrc *asrc = snd_soc_dai_get_drvdata(dai);
591be7bd03fSShengjiu Wang 	struct fsl_asrc_priv *asrc_priv = asrc->private;
59253f67a78SS.j. Wang 
59353f67a78SS.j. Wang 	/* Odd channel number is not valid for older ASRC (channel_bits==3) */
594be7bd03fSShengjiu Wang 	if (asrc_priv->soc->channel_bits == 3)
59553f67a78SS.j. Wang 		snd_pcm_hw_constraint_step(substream->runtime, 0,
59653f67a78SS.j. Wang 					   SNDRV_PCM_HW_PARAM_CHANNELS, 2);
59753f67a78SS.j. Wang 
598d281bf5dSS.j. Wang 
599d281bf5dSS.j. Wang 	return snd_pcm_hw_constraint_list(substream->runtime, 0,
600d281bf5dSS.j. Wang 			SNDRV_PCM_HW_PARAM_RATE, &fsl_asrc_rate_constraints);
60153f67a78SS.j. Wang }
60253f67a78SS.j. Wang 
603d0250cf4SShengjiu Wang /* Select proper clock source for internal ratio mode */
604d0250cf4SShengjiu Wang static void fsl_asrc_select_clk(struct fsl_asrc_priv *asrc_priv,
605d0250cf4SShengjiu Wang 				struct fsl_asrc_pair *pair,
606d0250cf4SShengjiu Wang 				int in_rate,
607d0250cf4SShengjiu Wang 				int out_rate)
608d0250cf4SShengjiu Wang {
609d0250cf4SShengjiu Wang 	struct fsl_asrc_pair_priv *pair_priv = pair->private;
610d0250cf4SShengjiu Wang 	struct asrc_config *config = pair_priv->config;
611d0250cf4SShengjiu Wang 	int rate[2], select_clk[2]; /* Array size 2 means IN and OUT */
612d0250cf4SShengjiu Wang 	int clk_rate, clk_index;
613b2967435SPierre-Louis Bossart 	int i, j;
614d0250cf4SShengjiu Wang 
615d0250cf4SShengjiu Wang 	rate[IN] = in_rate;
616d0250cf4SShengjiu Wang 	rate[OUT] = out_rate;
617d0250cf4SShengjiu Wang 
618d0250cf4SShengjiu Wang 	/* Select proper clock source for internal ratio mode */
619d0250cf4SShengjiu Wang 	for (j = 0; j < 2; j++) {
620d0250cf4SShengjiu Wang 		for (i = 0; i < ASRC_CLK_MAP_LEN; i++) {
621d0250cf4SShengjiu Wang 			clk_index = asrc_priv->clk_map[j][i];
622d0250cf4SShengjiu Wang 			clk_rate = clk_get_rate(asrc_priv->asrck_clk[clk_index]);
623d0250cf4SShengjiu Wang 			/* Only match a perfect clock source with no remainder */
624d0250cf4SShengjiu Wang 			if (clk_rate != 0 && (clk_rate / rate[j]) <= 1024 &&
625d0250cf4SShengjiu Wang 			    (clk_rate % rate[j]) == 0)
626d0250cf4SShengjiu Wang 				break;
627d0250cf4SShengjiu Wang 		}
628d0250cf4SShengjiu Wang 
629d0250cf4SShengjiu Wang 		select_clk[j] = i;
630d0250cf4SShengjiu Wang 	}
631d0250cf4SShengjiu Wang 
632d0250cf4SShengjiu Wang 	/* Switch to ideal ratio mode if there is no proper clock source */
633d0250cf4SShengjiu Wang 	if (select_clk[IN] == ASRC_CLK_MAP_LEN || select_clk[OUT] == ASRC_CLK_MAP_LEN) {
634d0250cf4SShengjiu Wang 		select_clk[IN] = INCLK_NONE;
635d0250cf4SShengjiu Wang 		select_clk[OUT] = OUTCLK_ASRCK1_CLK;
636d0250cf4SShengjiu Wang 	}
637d0250cf4SShengjiu Wang 
638d0250cf4SShengjiu Wang 	config->inclk = select_clk[IN];
639d0250cf4SShengjiu Wang 	config->outclk = select_clk[OUT];
640d0250cf4SShengjiu Wang }
641d0250cf4SShengjiu Wang 
6423117bb31SNicolin Chen static int fsl_asrc_dai_hw_params(struct snd_pcm_substream *substream,
6433117bb31SNicolin Chen 				  struct snd_pcm_hw_params *params,
6443117bb31SNicolin Chen 				  struct snd_soc_dai *dai)
6453117bb31SNicolin Chen {
6467470704dSShengjiu Wang 	struct fsl_asrc *asrc = snd_soc_dai_get_drvdata(dai);
647d0250cf4SShengjiu Wang 	struct fsl_asrc_priv *asrc_priv = asrc->private;
6483117bb31SNicolin Chen 	struct snd_pcm_runtime *runtime = substream->runtime;
6493117bb31SNicolin Chen 	struct fsl_asrc_pair *pair = runtime->private_data;
650be7bd03fSShengjiu Wang 	struct fsl_asrc_pair_priv *pair_priv = pair->private;
6513117bb31SNicolin Chen 	unsigned int channels = params_channels(params);
6523117bb31SNicolin Chen 	unsigned int rate = params_rate(params);
6533117bb31SNicolin Chen 	struct asrc_config config;
6544bf62571SShengjiu Wang 	int ret;
6553117bb31SNicolin Chen 
6563117bb31SNicolin Chen 	ret = fsl_asrc_request_pair(channels, pair);
6573117bb31SNicolin Chen 	if (ret) {
6583117bb31SNicolin Chen 		dev_err(dai->dev, "fail to request asrc pair\n");
6593117bb31SNicolin Chen 		return ret;
6603117bb31SNicolin Chen 	}
6613117bb31SNicolin Chen 
662be7bd03fSShengjiu Wang 	pair_priv->config = &config;
6633117bb31SNicolin Chen 
6643117bb31SNicolin Chen 	config.pair = pair->index;
6653117bb31SNicolin Chen 	config.channel_num = channels;
6663117bb31SNicolin Chen 
6673117bb31SNicolin Chen 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
6684bf62571SShengjiu Wang 		config.input_format   = params_format(params);
6694520af41SShengjiu Wang 		config.output_format  = asrc->asrc_format;
6703117bb31SNicolin Chen 		config.input_sample_rate  = rate;
6717470704dSShengjiu Wang 		config.output_sample_rate = asrc->asrc_rate;
6723117bb31SNicolin Chen 	} else {
6734520af41SShengjiu Wang 		config.input_format   = asrc->asrc_format;
6744bf62571SShengjiu Wang 		config.output_format  = params_format(params);
6757470704dSShengjiu Wang 		config.input_sample_rate  = asrc->asrc_rate;
6763117bb31SNicolin Chen 		config.output_sample_rate = rate;
6773117bb31SNicolin Chen 	}
6783117bb31SNicolin Chen 
679d0250cf4SShengjiu Wang 	fsl_asrc_select_clk(asrc_priv, pair,
680d0250cf4SShengjiu Wang 			    config.input_sample_rate,
681d0250cf4SShengjiu Wang 			    config.output_sample_rate);
682d0250cf4SShengjiu Wang 
683b39eb1e2SShengjiu Wang 	ret = fsl_asrc_config_pair(pair, false);
6843117bb31SNicolin Chen 	if (ret) {
6853117bb31SNicolin Chen 		dev_err(dai->dev, "fail to config asrc pair\n");
6863117bb31SNicolin Chen 		return ret;
6873117bb31SNicolin Chen 	}
6883117bb31SNicolin Chen 
6893117bb31SNicolin Chen 	return 0;
6903117bb31SNicolin Chen }
6913117bb31SNicolin Chen 
6923117bb31SNicolin Chen static int fsl_asrc_dai_hw_free(struct snd_pcm_substream *substream,
6933117bb31SNicolin Chen 				struct snd_soc_dai *dai)
6943117bb31SNicolin Chen {
6953117bb31SNicolin Chen 	struct snd_pcm_runtime *runtime = substream->runtime;
6963117bb31SNicolin Chen 	struct fsl_asrc_pair *pair = runtime->private_data;
6973117bb31SNicolin Chen 
6983117bb31SNicolin Chen 	if (pair)
6993117bb31SNicolin Chen 		fsl_asrc_release_pair(pair);
7003117bb31SNicolin Chen 
7013117bb31SNicolin Chen 	return 0;
7023117bb31SNicolin Chen }
7033117bb31SNicolin Chen 
7043117bb31SNicolin Chen static int fsl_asrc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
7053117bb31SNicolin Chen 				struct snd_soc_dai *dai)
7063117bb31SNicolin Chen {
7073117bb31SNicolin Chen 	struct snd_pcm_runtime *runtime = substream->runtime;
7083117bb31SNicolin Chen 	struct fsl_asrc_pair *pair = runtime->private_data;
7093117bb31SNicolin Chen 
7103117bb31SNicolin Chen 	switch (cmd) {
7113117bb31SNicolin Chen 	case SNDRV_PCM_TRIGGER_START:
7123117bb31SNicolin Chen 	case SNDRV_PCM_TRIGGER_RESUME:
7133117bb31SNicolin Chen 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
7143117bb31SNicolin Chen 		fsl_asrc_start_pair(pair);
7153117bb31SNicolin Chen 		break;
7163117bb31SNicolin Chen 	case SNDRV_PCM_TRIGGER_STOP:
7173117bb31SNicolin Chen 	case SNDRV_PCM_TRIGGER_SUSPEND:
7183117bb31SNicolin Chen 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
7193117bb31SNicolin Chen 		fsl_asrc_stop_pair(pair);
7203117bb31SNicolin Chen 		break;
7213117bb31SNicolin Chen 	default:
7223117bb31SNicolin Chen 		return -EINVAL;
7233117bb31SNicolin Chen 	}
7243117bb31SNicolin Chen 
7253117bb31SNicolin Chen 	return 0;
7263117bb31SNicolin Chen }
7273117bb31SNicolin Chen 
72829a22ebfSGustavo A. R. Silva static const struct snd_soc_dai_ops fsl_asrc_dai_ops = {
72953f67a78SS.j. Wang 	.startup      = fsl_asrc_dai_startup,
7303117bb31SNicolin Chen 	.hw_params    = fsl_asrc_dai_hw_params,
7313117bb31SNicolin Chen 	.hw_free      = fsl_asrc_dai_hw_free,
7323117bb31SNicolin Chen 	.trigger      = fsl_asrc_dai_trigger,
7333117bb31SNicolin Chen };
7343117bb31SNicolin Chen 
7353117bb31SNicolin Chen static int fsl_asrc_dai_probe(struct snd_soc_dai *dai)
7363117bb31SNicolin Chen {
7377470704dSShengjiu Wang 	struct fsl_asrc *asrc = snd_soc_dai_get_drvdata(dai);
7383117bb31SNicolin Chen 
7397470704dSShengjiu Wang 	snd_soc_dai_init_dma_data(dai, &asrc->dma_params_tx,
7407470704dSShengjiu Wang 				  &asrc->dma_params_rx);
7413117bb31SNicolin Chen 
7423117bb31SNicolin Chen 	return 0;
7433117bb31SNicolin Chen }
7443117bb31SNicolin Chen 
7453117bb31SNicolin Chen #define FSL_ASRC_FORMATS	(SNDRV_PCM_FMTBIT_S24_LE | \
7463117bb31SNicolin Chen 				 SNDRV_PCM_FMTBIT_S16_LE | \
747109539c9SShengjiu Wang 				 SNDRV_PCM_FMTBIT_S24_3LE)
7483117bb31SNicolin Chen 
7493117bb31SNicolin Chen static struct snd_soc_dai_driver fsl_asrc_dai = {
7503117bb31SNicolin Chen 	.probe = fsl_asrc_dai_probe,
7513117bb31SNicolin Chen 	.playback = {
7523117bb31SNicolin Chen 		.stream_name = "ASRC-Playback",
7533117bb31SNicolin Chen 		.channels_min = 1,
7543117bb31SNicolin Chen 		.channels_max = 10,
755d281bf5dSS.j. Wang 		.rate_min = 5512,
756d281bf5dSS.j. Wang 		.rate_max = 192000,
757d281bf5dSS.j. Wang 		.rates = SNDRV_PCM_RATE_KNOT,
758109539c9SShengjiu Wang 		.formats = FSL_ASRC_FORMATS |
759109539c9SShengjiu Wang 			   SNDRV_PCM_FMTBIT_S8,
7603117bb31SNicolin Chen 	},
7613117bb31SNicolin Chen 	.capture = {
7623117bb31SNicolin Chen 		.stream_name = "ASRC-Capture",
7633117bb31SNicolin Chen 		.channels_min = 1,
7643117bb31SNicolin Chen 		.channels_max = 10,
765d281bf5dSS.j. Wang 		.rate_min = 5512,
766d281bf5dSS.j. Wang 		.rate_max = 192000,
767d281bf5dSS.j. Wang 		.rates = SNDRV_PCM_RATE_KNOT,
7683117bb31SNicolin Chen 		.formats = FSL_ASRC_FORMATS,
7693117bb31SNicolin Chen 	},
7703117bb31SNicolin Chen 	.ops = &fsl_asrc_dai_ops,
7713117bb31SNicolin Chen };
7723117bb31SNicolin Chen 
7733117bb31SNicolin Chen static bool fsl_asrc_readable_reg(struct device *dev, unsigned int reg)
7743117bb31SNicolin Chen {
7753117bb31SNicolin Chen 	switch (reg) {
7763117bb31SNicolin Chen 	case REG_ASRCTR:
7773117bb31SNicolin Chen 	case REG_ASRIER:
7783117bb31SNicolin Chen 	case REG_ASRCNCR:
7793117bb31SNicolin Chen 	case REG_ASRCFG:
7803117bb31SNicolin Chen 	case REG_ASRCSR:
7813117bb31SNicolin Chen 	case REG_ASRCDR1:
7823117bb31SNicolin Chen 	case REG_ASRCDR2:
7833117bb31SNicolin Chen 	case REG_ASRSTR:
7843117bb31SNicolin Chen 	case REG_ASRPM1:
7853117bb31SNicolin Chen 	case REG_ASRPM2:
7863117bb31SNicolin Chen 	case REG_ASRPM3:
7873117bb31SNicolin Chen 	case REG_ASRPM4:
7883117bb31SNicolin Chen 	case REG_ASRPM5:
7893117bb31SNicolin Chen 	case REG_ASRTFR1:
7903117bb31SNicolin Chen 	case REG_ASRCCR:
7913117bb31SNicolin Chen 	case REG_ASRDOA:
7923117bb31SNicolin Chen 	case REG_ASRDOB:
7933117bb31SNicolin Chen 	case REG_ASRDOC:
7943117bb31SNicolin Chen 	case REG_ASRIDRHA:
7953117bb31SNicolin Chen 	case REG_ASRIDRLA:
7963117bb31SNicolin Chen 	case REG_ASRIDRHB:
7973117bb31SNicolin Chen 	case REG_ASRIDRLB:
7983117bb31SNicolin Chen 	case REG_ASRIDRHC:
7993117bb31SNicolin Chen 	case REG_ASRIDRLC:
8003117bb31SNicolin Chen 	case REG_ASR76K:
8013117bb31SNicolin Chen 	case REG_ASR56K:
8023117bb31SNicolin Chen 	case REG_ASRMCRA:
8033117bb31SNicolin Chen 	case REG_ASRFSTA:
8043117bb31SNicolin Chen 	case REG_ASRMCRB:
8053117bb31SNicolin Chen 	case REG_ASRFSTB:
8063117bb31SNicolin Chen 	case REG_ASRMCRC:
8073117bb31SNicolin Chen 	case REG_ASRFSTC:
8083117bb31SNicolin Chen 	case REG_ASRMCR1A:
8093117bb31SNicolin Chen 	case REG_ASRMCR1B:
8103117bb31SNicolin Chen 	case REG_ASRMCR1C:
8113117bb31SNicolin Chen 		return true;
8123117bb31SNicolin Chen 	default:
8133117bb31SNicolin Chen 		return false;
8143117bb31SNicolin Chen 	}
8153117bb31SNicolin Chen }
8163117bb31SNicolin Chen 
8173117bb31SNicolin Chen static bool fsl_asrc_volatile_reg(struct device *dev, unsigned int reg)
8183117bb31SNicolin Chen {
8193117bb31SNicolin Chen 	switch (reg) {
8203117bb31SNicolin Chen 	case REG_ASRSTR:
8213117bb31SNicolin Chen 	case REG_ASRDIA:
8223117bb31SNicolin Chen 	case REG_ASRDIB:
8233117bb31SNicolin Chen 	case REG_ASRDIC:
8243117bb31SNicolin Chen 	case REG_ASRDOA:
8253117bb31SNicolin Chen 	case REG_ASRDOB:
8263117bb31SNicolin Chen 	case REG_ASRDOC:
8273117bb31SNicolin Chen 	case REG_ASRFSTA:
8283117bb31SNicolin Chen 	case REG_ASRFSTB:
8293117bb31SNicolin Chen 	case REG_ASRFSTC:
8303117bb31SNicolin Chen 	case REG_ASRCFG:
8313117bb31SNicolin Chen 		return true;
8323117bb31SNicolin Chen 	default:
8333117bb31SNicolin Chen 		return false;
8343117bb31SNicolin Chen 	}
8353117bb31SNicolin Chen }
8363117bb31SNicolin Chen 
8373117bb31SNicolin Chen static bool fsl_asrc_writeable_reg(struct device *dev, unsigned int reg)
8383117bb31SNicolin Chen {
8393117bb31SNicolin Chen 	switch (reg) {
8403117bb31SNicolin Chen 	case REG_ASRCTR:
8413117bb31SNicolin Chen 	case REG_ASRIER:
8423117bb31SNicolin Chen 	case REG_ASRCNCR:
8433117bb31SNicolin Chen 	case REG_ASRCFG:
8443117bb31SNicolin Chen 	case REG_ASRCSR:
8453117bb31SNicolin Chen 	case REG_ASRCDR1:
8463117bb31SNicolin Chen 	case REG_ASRCDR2:
8473117bb31SNicolin Chen 	case REG_ASRSTR:
8483117bb31SNicolin Chen 	case REG_ASRPM1:
8493117bb31SNicolin Chen 	case REG_ASRPM2:
8503117bb31SNicolin Chen 	case REG_ASRPM3:
8513117bb31SNicolin Chen 	case REG_ASRPM4:
8523117bb31SNicolin Chen 	case REG_ASRPM5:
8533117bb31SNicolin Chen 	case REG_ASRTFR1:
8543117bb31SNicolin Chen 	case REG_ASRCCR:
8553117bb31SNicolin Chen 	case REG_ASRDIA:
8563117bb31SNicolin Chen 	case REG_ASRDIB:
8573117bb31SNicolin Chen 	case REG_ASRDIC:
8583117bb31SNicolin Chen 	case REG_ASRIDRHA:
8593117bb31SNicolin Chen 	case REG_ASRIDRLA:
8603117bb31SNicolin Chen 	case REG_ASRIDRHB:
8613117bb31SNicolin Chen 	case REG_ASRIDRLB:
8623117bb31SNicolin Chen 	case REG_ASRIDRHC:
8633117bb31SNicolin Chen 	case REG_ASRIDRLC:
8643117bb31SNicolin Chen 	case REG_ASR76K:
8653117bb31SNicolin Chen 	case REG_ASR56K:
8663117bb31SNicolin Chen 	case REG_ASRMCRA:
8673117bb31SNicolin Chen 	case REG_ASRMCRB:
8683117bb31SNicolin Chen 	case REG_ASRMCRC:
8693117bb31SNicolin Chen 	case REG_ASRMCR1A:
8703117bb31SNicolin Chen 	case REG_ASRMCR1B:
8713117bb31SNicolin Chen 	case REG_ASRMCR1C:
8723117bb31SNicolin Chen 		return true;
8733117bb31SNicolin Chen 	default:
8743117bb31SNicolin Chen 		return false;
8753117bb31SNicolin Chen 	}
8763117bb31SNicolin Chen }
8773117bb31SNicolin Chen 
87886a570c5SNicolin Chen static struct reg_default fsl_asrc_reg[] = {
87986a570c5SNicolin Chen 	{ REG_ASRCTR, 0x0000 }, { REG_ASRIER, 0x0000 },
88086a570c5SNicolin Chen 	{ REG_ASRCNCR, 0x0000 }, { REG_ASRCFG, 0x0000 },
88186a570c5SNicolin Chen 	{ REG_ASRCSR, 0x0000 }, { REG_ASRCDR1, 0x0000 },
88286a570c5SNicolin Chen 	{ REG_ASRCDR2, 0x0000 }, { REG_ASRSTR, 0x0000 },
88386a570c5SNicolin Chen 	{ REG_ASRRA, 0x0000 }, { REG_ASRRB, 0x0000 },
88486a570c5SNicolin Chen 	{ REG_ASRRC, 0x0000 }, { REG_ASRPM1, 0x0000 },
88586a570c5SNicolin Chen 	{ REG_ASRPM2, 0x0000 }, { REG_ASRPM3, 0x0000 },
88686a570c5SNicolin Chen 	{ REG_ASRPM4, 0x0000 }, { REG_ASRPM5, 0x0000 },
88786a570c5SNicolin Chen 	{ REG_ASRTFR1, 0x0000 }, { REG_ASRCCR, 0x0000 },
88886a570c5SNicolin Chen 	{ REG_ASRDIA, 0x0000 }, { REG_ASRDOA, 0x0000 },
88986a570c5SNicolin Chen 	{ REG_ASRDIB, 0x0000 }, { REG_ASRDOB, 0x0000 },
89086a570c5SNicolin Chen 	{ REG_ASRDIC, 0x0000 }, { REG_ASRDOC, 0x0000 },
89186a570c5SNicolin Chen 	{ REG_ASRIDRHA, 0x0000 }, { REG_ASRIDRLA, 0x0000 },
89286a570c5SNicolin Chen 	{ REG_ASRIDRHB, 0x0000 }, { REG_ASRIDRLB, 0x0000 },
89386a570c5SNicolin Chen 	{ REG_ASRIDRHC, 0x0000 }, { REG_ASRIDRLC, 0x0000 },
89486a570c5SNicolin Chen 	{ REG_ASR76K, 0x0A47 }, { REG_ASR56K, 0x0DF3 },
89586a570c5SNicolin Chen 	{ REG_ASRMCRA, 0x0000 }, { REG_ASRFSTA, 0x0000 },
89686a570c5SNicolin Chen 	{ REG_ASRMCRB, 0x0000 }, { REG_ASRFSTB, 0x0000 },
89786a570c5SNicolin Chen 	{ REG_ASRMCRC, 0x0000 }, { REG_ASRFSTC, 0x0000 },
89886a570c5SNicolin Chen 	{ REG_ASRMCR1A, 0x0000 }, { REG_ASRMCR1B, 0x0000 },
89986a570c5SNicolin Chen 	{ REG_ASRMCR1C, 0x0000 },
90086a570c5SNicolin Chen };
90186a570c5SNicolin Chen 
902bf16d883SXiubo Li static const struct regmap_config fsl_asrc_regmap_config = {
9033117bb31SNicolin Chen 	.reg_bits = 32,
9043117bb31SNicolin Chen 	.reg_stride = 4,
9053117bb31SNicolin Chen 	.val_bits = 32,
9063117bb31SNicolin Chen 
9073117bb31SNicolin Chen 	.max_register = REG_ASRMCR1C,
90886a570c5SNicolin Chen 	.reg_defaults = fsl_asrc_reg,
90986a570c5SNicolin Chen 	.num_reg_defaults = ARRAY_SIZE(fsl_asrc_reg),
9103117bb31SNicolin Chen 	.readable_reg = fsl_asrc_readable_reg,
9113117bb31SNicolin Chen 	.volatile_reg = fsl_asrc_volatile_reg,
9123117bb31SNicolin Chen 	.writeable_reg = fsl_asrc_writeable_reg,
913b4138868SMarek Vasut 	.cache_type = REGCACHE_FLAT,
9143117bb31SNicolin Chen };
9153117bb31SNicolin Chen 
9163117bb31SNicolin Chen /**
91745e039d9SPierre-Louis Bossart  * fsl_asrc_init - Initialize ASRC registers with a default configuration
91845e039d9SPierre-Louis Bossart  * @asrc: ASRC context
9193117bb31SNicolin Chen  */
9207470704dSShengjiu Wang static int fsl_asrc_init(struct fsl_asrc *asrc)
9213117bb31SNicolin Chen {
922f8953043SShengjiu Wang 	unsigned long ipg_rate;
923f8953043SShengjiu Wang 
9243117bb31SNicolin Chen 	/* Halt ASRC internal FP when input FIFO needs data for pair A, B, C */
9257470704dSShengjiu Wang 	regmap_write(asrc->regmap, REG_ASRCTR, ASRCTR_ASRCEN);
9263117bb31SNicolin Chen 
9273117bb31SNicolin Chen 	/* Disable interrupt by default */
9287470704dSShengjiu Wang 	regmap_write(asrc->regmap, REG_ASRIER, 0x0);
9293117bb31SNicolin Chen 
9303117bb31SNicolin Chen 	/* Apply recommended settings for parameters from Reference Manual */
9317470704dSShengjiu Wang 	regmap_write(asrc->regmap, REG_ASRPM1, 0x7fffff);
9327470704dSShengjiu Wang 	regmap_write(asrc->regmap, REG_ASRPM2, 0x255555);
9337470704dSShengjiu Wang 	regmap_write(asrc->regmap, REG_ASRPM3, 0xff7280);
9347470704dSShengjiu Wang 	regmap_write(asrc->regmap, REG_ASRPM4, 0xff7280);
9357470704dSShengjiu Wang 	regmap_write(asrc->regmap, REG_ASRPM5, 0xff7280);
9363117bb31SNicolin Chen 
9373117bb31SNicolin Chen 	/* Base address for task queue FIFO. Set to 0x7C */
9387470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRTFR1,
9393117bb31SNicolin Chen 			   ASRTFR1_TF_BASE_MASK, ASRTFR1_TF_BASE(0xfc));
9403117bb31SNicolin Chen 
941f8953043SShengjiu Wang 	/*
942f8953043SShengjiu Wang 	 * Set the period of the 76KHz and 56KHz sampling clocks based on
943f8953043SShengjiu Wang 	 * the ASRC processing clock.
944f8953043SShengjiu Wang 	 * On iMX6, ipg_clk = 133MHz, REG_ASR76K = 0x06D6, REG_ASR56K = 0x0947
945f8953043SShengjiu Wang 	 */
946f8953043SShengjiu Wang 	ipg_rate = clk_get_rate(asrc->ipg_clk);
947f8953043SShengjiu Wang 	regmap_write(asrc->regmap, REG_ASR76K, ipg_rate / 76000);
948f8953043SShengjiu Wang 	return regmap_write(asrc->regmap, REG_ASR56K, ipg_rate / 56000);
9493117bb31SNicolin Chen }
9503117bb31SNicolin Chen 
9513117bb31SNicolin Chen /**
95245e039d9SPierre-Louis Bossart  * fsl_asrc_isr- Interrupt handler for ASRC
95345e039d9SPierre-Louis Bossart  * @irq: irq number
95445e039d9SPierre-Louis Bossart  * @dev_id: ASRC context
9553117bb31SNicolin Chen  */
9563117bb31SNicolin Chen static irqreturn_t fsl_asrc_isr(int irq, void *dev_id)
9573117bb31SNicolin Chen {
9587470704dSShengjiu Wang 	struct fsl_asrc *asrc = (struct fsl_asrc *)dev_id;
9597470704dSShengjiu Wang 	struct device *dev = &asrc->pdev->dev;
9603117bb31SNicolin Chen 	enum asrc_pair_index index;
9613117bb31SNicolin Chen 	u32 status;
9623117bb31SNicolin Chen 
9637470704dSShengjiu Wang 	regmap_read(asrc->regmap, REG_ASRSTR, &status);
9643117bb31SNicolin Chen 
9653117bb31SNicolin Chen 	/* Clean overload error */
9667470704dSShengjiu Wang 	regmap_write(asrc->regmap, REG_ASRSTR, ASRSTR_AOLE);
9673117bb31SNicolin Chen 
9683117bb31SNicolin Chen 	/*
9693117bb31SNicolin Chen 	 * We here use dev_dbg() for all exceptions because ASRC itself does
9703117bb31SNicolin Chen 	 * not care if FIFO overflowed or underrun while a warning in the
9713117bb31SNicolin Chen 	 * interrupt would result a ridged conversion.
9723117bb31SNicolin Chen 	 */
9733117bb31SNicolin Chen 	for (index = ASRC_PAIR_A; index < ASRC_PAIR_MAX_NUM; index++) {
9747470704dSShengjiu Wang 		if (!asrc->pair[index])
9753117bb31SNicolin Chen 			continue;
9763117bb31SNicolin Chen 
9773117bb31SNicolin Chen 		if (status & ASRSTR_ATQOL) {
9787470704dSShengjiu Wang 			asrc->pair[index]->error |= ASRC_TASK_Q_OVERLOAD;
9793117bb31SNicolin Chen 			dev_dbg(dev, "ASRC Task Queue FIFO overload\n");
9803117bb31SNicolin Chen 		}
9813117bb31SNicolin Chen 
9823117bb31SNicolin Chen 		if (status & ASRSTR_AOOL(index)) {
9837470704dSShengjiu Wang 			asrc->pair[index]->error |= ASRC_OUTPUT_TASK_OVERLOAD;
9843117bb31SNicolin Chen 			pair_dbg("Output Task Overload\n");
9853117bb31SNicolin Chen 		}
9863117bb31SNicolin Chen 
9873117bb31SNicolin Chen 		if (status & ASRSTR_AIOL(index)) {
9887470704dSShengjiu Wang 			asrc->pair[index]->error |= ASRC_INPUT_TASK_OVERLOAD;
9893117bb31SNicolin Chen 			pair_dbg("Input Task Overload\n");
9903117bb31SNicolin Chen 		}
9913117bb31SNicolin Chen 
9923117bb31SNicolin Chen 		if (status & ASRSTR_AODO(index)) {
9937470704dSShengjiu Wang 			asrc->pair[index]->error |= ASRC_OUTPUT_BUFFER_OVERFLOW;
9943117bb31SNicolin Chen 			pair_dbg("Output Data Buffer has overflowed\n");
9953117bb31SNicolin Chen 		}
9963117bb31SNicolin Chen 
9973117bb31SNicolin Chen 		if (status & ASRSTR_AIDU(index)) {
9987470704dSShengjiu Wang 			asrc->pair[index]->error |= ASRC_INPUT_BUFFER_UNDERRUN;
9993117bb31SNicolin Chen 			pair_dbg("Input Data Buffer has underflowed\n");
10003117bb31SNicolin Chen 		}
10013117bb31SNicolin Chen 	}
10023117bb31SNicolin Chen 
10033117bb31SNicolin Chen 	return IRQ_HANDLED;
10043117bb31SNicolin Chen }
10053117bb31SNicolin Chen 
1006be7bd03fSShengjiu Wang static int fsl_asrc_get_fifo_addr(u8 dir, enum asrc_pair_index index)
1007be7bd03fSShengjiu Wang {
1008be7bd03fSShengjiu Wang 	return REG_ASRDx(dir, index);
1009be7bd03fSShengjiu Wang }
1010be7bd03fSShengjiu Wang 
1011*cab04ab5SShengjiu Wang static int fsl_asrc_runtime_resume(struct device *dev);
1012*cab04ab5SShengjiu Wang static int fsl_asrc_runtime_suspend(struct device *dev);
1013*cab04ab5SShengjiu Wang 
10143117bb31SNicolin Chen static int fsl_asrc_probe(struct platform_device *pdev)
10153117bb31SNicolin Chen {
10163117bb31SNicolin Chen 	struct device_node *np = pdev->dev.of_node;
1017be7bd03fSShengjiu Wang 	struct fsl_asrc_priv *asrc_priv;
10187470704dSShengjiu Wang 	struct fsl_asrc *asrc;
10193117bb31SNicolin Chen 	struct resource *res;
10203117bb31SNicolin Chen 	void __iomem *regs;
10213117bb31SNicolin Chen 	int irq, ret, i;
1022c05f10f2SShengjiu Wang 	u32 map_idx;
10233117bb31SNicolin Chen 	char tmp[16];
10244520af41SShengjiu Wang 	u32 width;
10253117bb31SNicolin Chen 
10267470704dSShengjiu Wang 	asrc = devm_kzalloc(&pdev->dev, sizeof(*asrc), GFP_KERNEL);
10277470704dSShengjiu Wang 	if (!asrc)
10283117bb31SNicolin Chen 		return -ENOMEM;
10293117bb31SNicolin Chen 
1030be7bd03fSShengjiu Wang 	asrc_priv = devm_kzalloc(&pdev->dev, sizeof(*asrc_priv), GFP_KERNEL);
1031be7bd03fSShengjiu Wang 	if (!asrc_priv)
1032be7bd03fSShengjiu Wang 		return -ENOMEM;
1033be7bd03fSShengjiu Wang 
10347470704dSShengjiu Wang 	asrc->pdev = pdev;
1035be7bd03fSShengjiu Wang 	asrc->private = asrc_priv;
10363117bb31SNicolin Chen 
10373117bb31SNicolin Chen 	/* Get the addresses and IRQ */
10383117bb31SNicolin Chen 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
10393117bb31SNicolin Chen 	regs = devm_ioremap_resource(&pdev->dev, res);
10403117bb31SNicolin Chen 	if (IS_ERR(regs))
10413117bb31SNicolin Chen 		return PTR_ERR(regs);
10423117bb31SNicolin Chen 
10437470704dSShengjiu Wang 	asrc->paddr = res->start;
10443117bb31SNicolin Chen 
1045*cab04ab5SShengjiu Wang 	asrc->regmap = devm_regmap_init_mmio(&pdev->dev, regs, &fsl_asrc_regmap_config);
10467470704dSShengjiu Wang 	if (IS_ERR(asrc->regmap)) {
10473117bb31SNicolin Chen 		dev_err(&pdev->dev, "failed to init regmap\n");
10487470704dSShengjiu Wang 		return PTR_ERR(asrc->regmap);
10493117bb31SNicolin Chen 	}
10503117bb31SNicolin Chen 
10513117bb31SNicolin Chen 	irq = platform_get_irq(pdev, 0);
1052cf9441adSStephen Boyd 	if (irq < 0)
10533117bb31SNicolin Chen 		return irq;
10543117bb31SNicolin Chen 
10553117bb31SNicolin Chen 	ret = devm_request_irq(&pdev->dev, irq, fsl_asrc_isr, 0,
10567470704dSShengjiu Wang 			       dev_name(&pdev->dev), asrc);
10573117bb31SNicolin Chen 	if (ret) {
10583117bb31SNicolin Chen 		dev_err(&pdev->dev, "failed to claim irq %u: %d\n", irq, ret);
10593117bb31SNicolin Chen 		return ret;
10603117bb31SNicolin Chen 	}
10613117bb31SNicolin Chen 
10627470704dSShengjiu Wang 	asrc->mem_clk = devm_clk_get(&pdev->dev, "mem");
10637470704dSShengjiu Wang 	if (IS_ERR(asrc->mem_clk)) {
10643117bb31SNicolin Chen 		dev_err(&pdev->dev, "failed to get mem clock\n");
10657470704dSShengjiu Wang 		return PTR_ERR(asrc->mem_clk);
10663117bb31SNicolin Chen 	}
10673117bb31SNicolin Chen 
10687470704dSShengjiu Wang 	asrc->ipg_clk = devm_clk_get(&pdev->dev, "ipg");
10697470704dSShengjiu Wang 	if (IS_ERR(asrc->ipg_clk)) {
10703117bb31SNicolin Chen 		dev_err(&pdev->dev, "failed to get ipg clock\n");
10717470704dSShengjiu Wang 		return PTR_ERR(asrc->ipg_clk);
10723117bb31SNicolin Chen 	}
10733117bb31SNicolin Chen 
10747470704dSShengjiu Wang 	asrc->spba_clk = devm_clk_get(&pdev->dev, "spba");
10757470704dSShengjiu Wang 	if (IS_ERR(asrc->spba_clk))
107613b8a97aSShengjiu Wang 		dev_warn(&pdev->dev, "failed to get spba clock\n");
107713b8a97aSShengjiu Wang 
10783117bb31SNicolin Chen 	for (i = 0; i < ASRC_CLK_MAX_NUM; i++) {
10793117bb31SNicolin Chen 		sprintf(tmp, "asrck_%x", i);
1080be7bd03fSShengjiu Wang 		asrc_priv->asrck_clk[i] = devm_clk_get(&pdev->dev, tmp);
1081be7bd03fSShengjiu Wang 		if (IS_ERR(asrc_priv->asrck_clk[i])) {
10823117bb31SNicolin Chen 			dev_err(&pdev->dev, "failed to get %s clock\n", tmp);
1083be7bd03fSShengjiu Wang 			return PTR_ERR(asrc_priv->asrck_clk[i]);
10843117bb31SNicolin Chen 		}
10853117bb31SNicolin Chen 	}
10863117bb31SNicolin Chen 
1087be7bd03fSShengjiu Wang 	asrc_priv->soc = of_device_get_match_data(&pdev->dev);
1088be7bd03fSShengjiu Wang 	asrc->use_edma = asrc_priv->soc->use_edma;
1089be7bd03fSShengjiu Wang 	asrc->get_dma_channel = fsl_asrc_get_dma_channel;
1090be7bd03fSShengjiu Wang 	asrc->request_pair = fsl_asrc_request_pair;
1091be7bd03fSShengjiu Wang 	asrc->release_pair = fsl_asrc_release_pair;
1092be7bd03fSShengjiu Wang 	asrc->get_fifo_addr = fsl_asrc_get_fifo_addr;
1093be7bd03fSShengjiu Wang 	asrc->pair_priv_size = sizeof(struct fsl_asrc_pair_priv);
1094be7bd03fSShengjiu Wang 
1095f3d8ac8cSFabio Estevam 	if (of_device_is_compatible(np, "fsl,imx35-asrc")) {
1096be7bd03fSShengjiu Wang 		asrc_priv->clk_map[IN] = input_clk_map_imx35;
1097be7bd03fSShengjiu Wang 		asrc_priv->clk_map[OUT] = output_clk_map_imx35;
1098c05f10f2SShengjiu Wang 	} else if (of_device_is_compatible(np, "fsl,imx53-asrc")) {
1099be7bd03fSShengjiu Wang 		asrc_priv->clk_map[IN] = input_clk_map_imx53;
1100be7bd03fSShengjiu Wang 		asrc_priv->clk_map[OUT] = output_clk_map_imx53;
1101c05f10f2SShengjiu Wang 	} else if (of_device_is_compatible(np, "fsl,imx8qm-asrc") ||
1102c05f10f2SShengjiu Wang 		   of_device_is_compatible(np, "fsl,imx8qxp-asrc")) {
1103c05f10f2SShengjiu Wang 		ret = of_property_read_u32(np, "fsl,asrc-clk-map", &map_idx);
1104c05f10f2SShengjiu Wang 		if (ret) {
1105c05f10f2SShengjiu Wang 			dev_err(&pdev->dev, "failed to get clk map index\n");
1106c05f10f2SShengjiu Wang 			return ret;
1107c05f10f2SShengjiu Wang 		}
1108c05f10f2SShengjiu Wang 
1109c05f10f2SShengjiu Wang 		if (map_idx > 1) {
1110c05f10f2SShengjiu Wang 			dev_err(&pdev->dev, "unsupported clk map index\n");
1111c05f10f2SShengjiu Wang 			return -EINVAL;
1112c05f10f2SShengjiu Wang 		}
1113c05f10f2SShengjiu Wang 		if (of_device_is_compatible(np, "fsl,imx8qm-asrc")) {
1114be7bd03fSShengjiu Wang 			asrc_priv->clk_map[IN] = clk_map_imx8qm[map_idx];
1115be7bd03fSShengjiu Wang 			asrc_priv->clk_map[OUT] = clk_map_imx8qm[map_idx];
11163117bb31SNicolin Chen 		} else {
1117be7bd03fSShengjiu Wang 			asrc_priv->clk_map[IN] = clk_map_imx8qxp[map_idx];
1118be7bd03fSShengjiu Wang 			asrc_priv->clk_map[OUT] = clk_map_imx8qxp[map_idx];
1119c05f10f2SShengjiu Wang 		}
11203117bb31SNicolin Chen 	}
11213117bb31SNicolin Chen 
11227470704dSShengjiu Wang 	asrc->channel_avail = 10;
11233117bb31SNicolin Chen 
11243117bb31SNicolin Chen 	ret = of_property_read_u32(np, "fsl,asrc-rate",
11257470704dSShengjiu Wang 				   &asrc->asrc_rate);
11263117bb31SNicolin Chen 	if (ret) {
11273117bb31SNicolin Chen 		dev_err(&pdev->dev, "failed to get output rate\n");
1128c0296950SFabio Estevam 		return ret;
11293117bb31SNicolin Chen 	}
11303117bb31SNicolin Chen 
11314520af41SShengjiu Wang 	ret = of_property_read_u32(np, "fsl,asrc-format", &asrc->asrc_format);
11323117bb31SNicolin Chen 	if (ret) {
11334520af41SShengjiu Wang 		ret = of_property_read_u32(np, "fsl,asrc-width", &width);
11344520af41SShengjiu Wang 		if (ret) {
11354520af41SShengjiu Wang 			dev_err(&pdev->dev, "failed to decide output format\n");
1136c0296950SFabio Estevam 			return ret;
11373117bb31SNicolin Chen 		}
11383117bb31SNicolin Chen 
11394520af41SShengjiu Wang 		switch (width) {
11404520af41SShengjiu Wang 		case 16:
11414520af41SShengjiu Wang 			asrc->asrc_format = SNDRV_PCM_FORMAT_S16_LE;
11424520af41SShengjiu Wang 			break;
11434520af41SShengjiu Wang 		case 24:
11444520af41SShengjiu Wang 			asrc->asrc_format = SNDRV_PCM_FORMAT_S24_LE;
11454520af41SShengjiu Wang 			break;
11464520af41SShengjiu Wang 		default:
11474520af41SShengjiu Wang 			dev_warn(&pdev->dev,
11484520af41SShengjiu Wang 				 "unsupported width, use default S24_LE\n");
11494520af41SShengjiu Wang 			asrc->asrc_format = SNDRV_PCM_FORMAT_S24_LE;
11504520af41SShengjiu Wang 			break;
11514520af41SShengjiu Wang 		}
11524520af41SShengjiu Wang 	}
11534520af41SShengjiu Wang 
11544520af41SShengjiu Wang 	if (!(FSL_ASRC_FORMATS & (1ULL << asrc->asrc_format))) {
11554520af41SShengjiu Wang 		dev_warn(&pdev->dev, "unsupported width, use default S24_LE\n");
11564520af41SShengjiu Wang 		asrc->asrc_format = SNDRV_PCM_FORMAT_S24_LE;
11573117bb31SNicolin Chen 	}
11583117bb31SNicolin Chen 
11597470704dSShengjiu Wang 	platform_set_drvdata(pdev, asrc);
11607470704dSShengjiu Wang 	spin_lock_init(&asrc->lock);
1161*cab04ab5SShengjiu Wang 	pm_runtime_enable(&pdev->dev);
1162*cab04ab5SShengjiu Wang 	if (!pm_runtime_enabled(&pdev->dev)) {
1163*cab04ab5SShengjiu Wang 		ret = fsl_asrc_runtime_resume(&pdev->dev);
1164*cab04ab5SShengjiu Wang 		if (ret)
1165*cab04ab5SShengjiu Wang 			goto err_pm_disable;
1166*cab04ab5SShengjiu Wang 	}
1167*cab04ab5SShengjiu Wang 
1168*cab04ab5SShengjiu Wang 	ret = pm_runtime_get_sync(&pdev->dev);
1169*cab04ab5SShengjiu Wang 	if (ret < 0) {
1170*cab04ab5SShengjiu Wang 		pm_runtime_put_noidle(&pdev->dev);
1171*cab04ab5SShengjiu Wang 		goto err_pm_get_sync;
1172*cab04ab5SShengjiu Wang 	}
1173*cab04ab5SShengjiu Wang 
1174*cab04ab5SShengjiu Wang 	ret = fsl_asrc_init(asrc);
1175*cab04ab5SShengjiu Wang 	if (ret) {
1176*cab04ab5SShengjiu Wang 		dev_err(&pdev->dev, "failed to init asrc %d\n", ret);
1177*cab04ab5SShengjiu Wang 		goto err_pm_get_sync;
1178*cab04ab5SShengjiu Wang 	}
1179*cab04ab5SShengjiu Wang 
1180*cab04ab5SShengjiu Wang 	ret = pm_runtime_put_sync(&pdev->dev);
1181*cab04ab5SShengjiu Wang 	if (ret < 0)
1182*cab04ab5SShengjiu Wang 		goto err_pm_get_sync;
11833117bb31SNicolin Chen 
11843117bb31SNicolin Chen 	ret = devm_snd_soc_register_component(&pdev->dev, &fsl_asrc_component,
11853117bb31SNicolin Chen 					      &fsl_asrc_dai, 1);
11863117bb31SNicolin Chen 	if (ret) {
11873117bb31SNicolin Chen 		dev_err(&pdev->dev, "failed to register ASoC DAI\n");
1188*cab04ab5SShengjiu Wang 		goto err_pm_get_sync;
1189*cab04ab5SShengjiu Wang 	}
1190*cab04ab5SShengjiu Wang 
1191*cab04ab5SShengjiu Wang 	return 0;
1192*cab04ab5SShengjiu Wang 
1193*cab04ab5SShengjiu Wang err_pm_get_sync:
1194*cab04ab5SShengjiu Wang 	if (!pm_runtime_status_suspended(&pdev->dev))
1195*cab04ab5SShengjiu Wang 		fsl_asrc_runtime_suspend(&pdev->dev);
1196*cab04ab5SShengjiu Wang err_pm_disable:
1197*cab04ab5SShengjiu Wang 	pm_runtime_disable(&pdev->dev);
11983117bb31SNicolin Chen 	return ret;
11993117bb31SNicolin Chen }
12003117bb31SNicolin Chen 
1201*cab04ab5SShengjiu Wang static int fsl_asrc_remove(struct platform_device *pdev)
1202*cab04ab5SShengjiu Wang {
1203*cab04ab5SShengjiu Wang 	pm_runtime_disable(&pdev->dev);
1204*cab04ab5SShengjiu Wang 	if (!pm_runtime_status_suspended(&pdev->dev))
1205*cab04ab5SShengjiu Wang 		fsl_asrc_runtime_suspend(&pdev->dev);
1206*cab04ab5SShengjiu Wang 
12073117bb31SNicolin Chen 	return 0;
12083117bb31SNicolin Chen }
12093117bb31SNicolin Chen 
12103117bb31SNicolin Chen static int fsl_asrc_runtime_resume(struct device *dev)
12113117bb31SNicolin Chen {
12127470704dSShengjiu Wang 	struct fsl_asrc *asrc = dev_get_drvdata(dev);
1213be7bd03fSShengjiu Wang 	struct fsl_asrc_priv *asrc_priv = asrc->private;
1214b1ade0f2SFabio Estevam 	int i, ret;
1215393dc21dSShengjiu Wang 	u32 asrctr;
12163117bb31SNicolin Chen 
12177470704dSShengjiu Wang 	ret = clk_prepare_enable(asrc->mem_clk);
1218b1ade0f2SFabio Estevam 	if (ret)
1219b1ade0f2SFabio Estevam 		return ret;
12207470704dSShengjiu Wang 	ret = clk_prepare_enable(asrc->ipg_clk);
1221b1ade0f2SFabio Estevam 	if (ret)
1222b1ade0f2SFabio Estevam 		goto disable_mem_clk;
12237470704dSShengjiu Wang 	if (!IS_ERR(asrc->spba_clk)) {
12247470704dSShengjiu Wang 		ret = clk_prepare_enable(asrc->spba_clk);
122513b8a97aSShengjiu Wang 		if (ret)
122613b8a97aSShengjiu Wang 			goto disable_ipg_clk;
122713b8a97aSShengjiu Wang 	}
1228b1ade0f2SFabio Estevam 	for (i = 0; i < ASRC_CLK_MAX_NUM; i++) {
1229be7bd03fSShengjiu Wang 		ret = clk_prepare_enable(asrc_priv->asrck_clk[i]);
1230b1ade0f2SFabio Estevam 		if (ret)
1231b1ade0f2SFabio Estevam 			goto disable_asrck_clk;
1232b1ade0f2SFabio Estevam 	}
12333117bb31SNicolin Chen 
1234393dc21dSShengjiu Wang 	/* Stop all pairs provisionally */
1235393dc21dSShengjiu Wang 	regmap_read(asrc->regmap, REG_ASRCTR, &asrctr);
1236393dc21dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCTR,
1237393dc21dSShengjiu Wang 			   ASRCTR_ASRCEi_ALL_MASK, 0);
1238393dc21dSShengjiu Wang 
1239393dc21dSShengjiu Wang 	/* Restore all registers */
1240393dc21dSShengjiu Wang 	regcache_cache_only(asrc->regmap, false);
1241393dc21dSShengjiu Wang 	regcache_mark_dirty(asrc->regmap);
1242393dc21dSShengjiu Wang 	regcache_sync(asrc->regmap);
1243393dc21dSShengjiu Wang 
1244393dc21dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCFG,
1245393dc21dSShengjiu Wang 			   ASRCFG_NDPRi_ALL_MASK | ASRCFG_POSTMODi_ALL_MASK |
1246393dc21dSShengjiu Wang 			   ASRCFG_PREMODi_ALL_MASK, asrc_priv->regcache_cfg);
1247393dc21dSShengjiu Wang 
1248393dc21dSShengjiu Wang 	/* Restart enabled pairs */
1249393dc21dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCTR,
1250393dc21dSShengjiu Wang 			   ASRCTR_ASRCEi_ALL_MASK, asrctr);
1251393dc21dSShengjiu Wang 
12523117bb31SNicolin Chen 	return 0;
1253b1ade0f2SFabio Estevam 
1254b1ade0f2SFabio Estevam disable_asrck_clk:
1255b1ade0f2SFabio Estevam 	for (i--; i >= 0; i--)
1256be7bd03fSShengjiu Wang 		clk_disable_unprepare(asrc_priv->asrck_clk[i]);
12577470704dSShengjiu Wang 	if (!IS_ERR(asrc->spba_clk))
12587470704dSShengjiu Wang 		clk_disable_unprepare(asrc->spba_clk);
125913b8a97aSShengjiu Wang disable_ipg_clk:
12607470704dSShengjiu Wang 	clk_disable_unprepare(asrc->ipg_clk);
1261b1ade0f2SFabio Estevam disable_mem_clk:
12627470704dSShengjiu Wang 	clk_disable_unprepare(asrc->mem_clk);
1263b1ade0f2SFabio Estevam 	return ret;
12643117bb31SNicolin Chen }
12653117bb31SNicolin Chen 
12663117bb31SNicolin Chen static int fsl_asrc_runtime_suspend(struct device *dev)
12673117bb31SNicolin Chen {
12687470704dSShengjiu Wang 	struct fsl_asrc *asrc = dev_get_drvdata(dev);
1269be7bd03fSShengjiu Wang 	struct fsl_asrc_priv *asrc_priv = asrc->private;
12703117bb31SNicolin Chen 	int i;
12713117bb31SNicolin Chen 
1272393dc21dSShengjiu Wang 	regmap_read(asrc->regmap, REG_ASRCFG,
1273393dc21dSShengjiu Wang 		    &asrc_priv->regcache_cfg);
1274393dc21dSShengjiu Wang 
1275393dc21dSShengjiu Wang 	regcache_cache_only(asrc->regmap, true);
1276393dc21dSShengjiu Wang 
12773117bb31SNicolin Chen 	for (i = 0; i < ASRC_CLK_MAX_NUM; i++)
1278be7bd03fSShengjiu Wang 		clk_disable_unprepare(asrc_priv->asrck_clk[i]);
12797470704dSShengjiu Wang 	if (!IS_ERR(asrc->spba_clk))
12807470704dSShengjiu Wang 		clk_disable_unprepare(asrc->spba_clk);
12817470704dSShengjiu Wang 	clk_disable_unprepare(asrc->ipg_clk);
12827470704dSShengjiu Wang 	clk_disable_unprepare(asrc->mem_clk);
12833117bb31SNicolin Chen 
12843117bb31SNicolin Chen 	return 0;
12853117bb31SNicolin Chen }
12863117bb31SNicolin Chen 
12873117bb31SNicolin Chen static const struct dev_pm_ops fsl_asrc_pm = {
12883117bb31SNicolin Chen 	SET_RUNTIME_PM_OPS(fsl_asrc_runtime_suspend, fsl_asrc_runtime_resume, NULL)
1289393dc21dSShengjiu Wang 	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
1290393dc21dSShengjiu Wang 				pm_runtime_force_resume)
12913117bb31SNicolin Chen };
12923117bb31SNicolin Chen 
1293c05f10f2SShengjiu Wang static const struct fsl_asrc_soc_data fsl_asrc_imx35_data = {
1294c05f10f2SShengjiu Wang 	.use_edma = false,
1295c05f10f2SShengjiu Wang 	.channel_bits = 3,
1296c05f10f2SShengjiu Wang };
1297c05f10f2SShengjiu Wang 
1298c05f10f2SShengjiu Wang static const struct fsl_asrc_soc_data fsl_asrc_imx53_data = {
1299c05f10f2SShengjiu Wang 	.use_edma = false,
1300c05f10f2SShengjiu Wang 	.channel_bits = 4,
1301c05f10f2SShengjiu Wang };
1302c05f10f2SShengjiu Wang 
1303c05f10f2SShengjiu Wang static const struct fsl_asrc_soc_data fsl_asrc_imx8qm_data = {
1304c05f10f2SShengjiu Wang 	.use_edma = true,
1305c05f10f2SShengjiu Wang 	.channel_bits = 4,
1306c05f10f2SShengjiu Wang };
1307c05f10f2SShengjiu Wang 
1308c05f10f2SShengjiu Wang static const struct fsl_asrc_soc_data fsl_asrc_imx8qxp_data = {
1309c05f10f2SShengjiu Wang 	.use_edma = true,
1310c05f10f2SShengjiu Wang 	.channel_bits = 4,
1311c05f10f2SShengjiu Wang };
1312c05f10f2SShengjiu Wang 
13133117bb31SNicolin Chen static const struct of_device_id fsl_asrc_ids[] = {
1314c05f10f2SShengjiu Wang 	{ .compatible = "fsl,imx35-asrc", .data = &fsl_asrc_imx35_data },
1315c05f10f2SShengjiu Wang 	{ .compatible = "fsl,imx53-asrc", .data = &fsl_asrc_imx53_data },
1316c05f10f2SShengjiu Wang 	{ .compatible = "fsl,imx8qm-asrc", .data = &fsl_asrc_imx8qm_data },
1317c05f10f2SShengjiu Wang 	{ .compatible = "fsl,imx8qxp-asrc", .data = &fsl_asrc_imx8qxp_data },
13183117bb31SNicolin Chen 	{}
13193117bb31SNicolin Chen };
13203117bb31SNicolin Chen MODULE_DEVICE_TABLE(of, fsl_asrc_ids);
13213117bb31SNicolin Chen 
13223117bb31SNicolin Chen static struct platform_driver fsl_asrc_driver = {
13233117bb31SNicolin Chen 	.probe = fsl_asrc_probe,
1324*cab04ab5SShengjiu Wang 	.remove = fsl_asrc_remove,
13253117bb31SNicolin Chen 	.driver = {
13263117bb31SNicolin Chen 		.name = "fsl-asrc",
13273117bb31SNicolin Chen 		.of_match_table = fsl_asrc_ids,
13283117bb31SNicolin Chen 		.pm = &fsl_asrc_pm,
13293117bb31SNicolin Chen 	},
13303117bb31SNicolin Chen };
13313117bb31SNicolin Chen module_platform_driver(fsl_asrc_driver);
13323117bb31SNicolin Chen 
13333117bb31SNicolin Chen MODULE_DESCRIPTION("Freescale ASRC ASoC driver");
13343117bb31SNicolin Chen MODULE_AUTHOR("Nicolin Chen <nicoleotsuka@gmail.com>");
13353117bb31SNicolin Chen MODULE_ALIAS("platform:fsl-asrc");
13363117bb31SNicolin Chen MODULE_LICENSE("GPL v2");
1337