xref: /linux/sound/soc/fsl/fsl_asrc.c (revision c49932726de24405d45516b3f8ad2735714fdf05)
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>
14c6547c2eSSascha Hauer #include <linux/dma/imx-dma.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
2232038634SShengjiu Wang #define DIVIDER_NUM  64
233117bb31SNicolin Chen 
243117bb31SNicolin Chen #define pair_err(fmt, ...) \
257470704dSShengjiu Wang 	dev_err(&asrc->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__)
263117bb31SNicolin Chen 
273117bb31SNicolin Chen #define pair_dbg(fmt, ...) \
287470704dSShengjiu Wang 	dev_dbg(&asrc->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__)
293117bb31SNicolin Chen 
303117bb31SNicolin Chen /* Corresponding to process_option */
31d281bf5dSS.j. Wang static unsigned int supported_asrc_rate[] = {
32d281bf5dSS.j. Wang 	5512, 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000,
33d281bf5dSS.j. Wang 	64000, 88200, 96000, 128000, 176400, 192000,
343117bb31SNicolin Chen };
353117bb31SNicolin Chen 
36d281bf5dSS.j. Wang static struct snd_pcm_hw_constraint_list fsl_asrc_rate_constraints = {
37d281bf5dSS.j. Wang 	.count = ARRAY_SIZE(supported_asrc_rate),
38d281bf5dSS.j. Wang 	.list = supported_asrc_rate,
393117bb31SNicolin Chen };
403117bb31SNicolin Chen 
4145e039d9SPierre-Louis Bossart /*
423117bb31SNicolin Chen  * The following tables map the relationship between asrc_inclk/asrc_outclk in
433117bb31SNicolin Chen  * fsl_asrc.h and the registers of ASRCSR
443117bb31SNicolin Chen  */
45c05f10f2SShengjiu Wang static unsigned char input_clk_map_imx35[ASRC_CLK_MAP_LEN] = {
463117bb31SNicolin Chen 	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
47c05f10f2SShengjiu Wang 	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
48c05f10f2SShengjiu Wang 	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
493117bb31SNicolin Chen };
503117bb31SNicolin Chen 
51c05f10f2SShengjiu Wang static unsigned char output_clk_map_imx35[ASRC_CLK_MAP_LEN] = {
523117bb31SNicolin Chen 	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
53c05f10f2SShengjiu Wang 	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
54c05f10f2SShengjiu Wang 	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
553117bb31SNicolin Chen };
563117bb31SNicolin Chen 
573117bb31SNicolin Chen /* i.MX53 uses the same map for input and output */
58c05f10f2SShengjiu Wang static unsigned char input_clk_map_imx53[ASRC_CLK_MAP_LEN] = {
593117bb31SNicolin Chen /*	0x0  0x1  0x2  0x3  0x4  0x5  0x6  0x7  0x8  0x9  0xa  0xb  0xc  0xd  0xe  0xf */
603117bb31SNicolin Chen 	0x0, 0x1, 0x2, 0x7, 0x4, 0x5, 0x6, 0x3, 0x8, 0x9, 0xa, 0xb, 0xc, 0xf, 0xe, 0xd,
61c05f10f2SShengjiu Wang 	0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
62c05f10f2SShengjiu Wang 	0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
633117bb31SNicolin Chen };
643117bb31SNicolin Chen 
65c05f10f2SShengjiu Wang static unsigned char output_clk_map_imx53[ASRC_CLK_MAP_LEN] = {
663117bb31SNicolin Chen /*	0x0  0x1  0x2  0x3  0x4  0x5  0x6  0x7  0x8  0x9  0xa  0xb  0xc  0xd  0xe  0xf */
673117bb31SNicolin Chen 	0x8, 0x9, 0xa, 0x7, 0xc, 0x5, 0x6, 0xb, 0x0, 0x1, 0x2, 0x3, 0x4, 0xf, 0xe, 0xd,
68c05f10f2SShengjiu Wang 	0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
69c05f10f2SShengjiu Wang 	0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
703117bb31SNicolin Chen };
713117bb31SNicolin Chen 
7245e039d9SPierre-Louis Bossart /*
73c05f10f2SShengjiu Wang  * i.MX8QM/i.MX8QXP uses the same map for input and output.
74c05f10f2SShengjiu Wang  * clk_map_imx8qm[0] is for i.MX8QM asrc0
75c05f10f2SShengjiu Wang  * clk_map_imx8qm[1] is for i.MX8QM asrc1
76c05f10f2SShengjiu Wang  * clk_map_imx8qxp[0] is for i.MX8QXP asrc0
77c05f10f2SShengjiu Wang  * clk_map_imx8qxp[1] is for i.MX8QXP asrc1
78c05f10f2SShengjiu Wang  */
79c05f10f2SShengjiu Wang static unsigned char clk_map_imx8qm[2][ASRC_CLK_MAP_LEN] = {
80c05f10f2SShengjiu Wang 	{
81c05f10f2SShengjiu Wang 	0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x0,
82c05f10f2SShengjiu Wang 	0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
83c05f10f2SShengjiu Wang 	0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
84c05f10f2SShengjiu Wang 	},
85c05f10f2SShengjiu Wang 	{
86c05f10f2SShengjiu Wang 	0xf, 0xf, 0xf, 0xf, 0xf, 0x7, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x0,
87c05f10f2SShengjiu Wang 	0x0, 0x1, 0x2, 0x3, 0xb, 0xc, 0xf, 0xf, 0xd, 0xe, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
88c05f10f2SShengjiu Wang 	0x4, 0x5, 0x6, 0xf, 0x8, 0x9, 0xa, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
89c05f10f2SShengjiu Wang 	},
90c05f10f2SShengjiu Wang };
91c05f10f2SShengjiu Wang 
92c05f10f2SShengjiu Wang static unsigned char clk_map_imx8qxp[2][ASRC_CLK_MAP_LEN] = {
93c05f10f2SShengjiu Wang 	{
94c05f10f2SShengjiu Wang 	0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x0,
95c05f10f2SShengjiu Wang 	0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0xf, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xf, 0xf,
96c05f10f2SShengjiu Wang 	0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
97c05f10f2SShengjiu Wang 	},
98c05f10f2SShengjiu Wang 	{
99c05f10f2SShengjiu Wang 	0xf, 0xf, 0xf, 0xf, 0xf, 0x7, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x0,
100c05f10f2SShengjiu Wang 	0x0, 0x1, 0x2, 0x3, 0x7, 0x8, 0xf, 0xf, 0x9, 0xa, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
101c05f10f2SShengjiu Wang 	0xf, 0xf, 0x6, 0xf, 0xf, 0xf, 0xa, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
102c05f10f2SShengjiu Wang 	},
103c05f10f2SShengjiu Wang };
1043117bb31SNicolin Chen 
10532038634SShengjiu Wang /*
10632038634SShengjiu Wang  * According to RM, the divider range is 1 ~ 8,
10732038634SShengjiu Wang  * prescaler is power of 2 from 1 ~ 128.
10832038634SShengjiu Wang  */
10932038634SShengjiu Wang static int asrc_clk_divider[DIVIDER_NUM] = {
11032038634SShengjiu Wang 	1,  2,  4,  8,  16,  32,  64,  128,  /* divider = 1 */
11132038634SShengjiu Wang 	2,  4,  8, 16,  32,  64, 128,  256,  /* divider = 2 */
11232038634SShengjiu Wang 	3,  6, 12, 24,  48,  96, 192,  384,  /* divider = 3 */
11332038634SShengjiu Wang 	4,  8, 16, 32,  64, 128, 256,  512,  /* divider = 4 */
11432038634SShengjiu Wang 	5, 10, 20, 40,  80, 160, 320,  640,  /* divider = 5 */
11532038634SShengjiu Wang 	6, 12, 24, 48,  96, 192, 384,  768,  /* divider = 6 */
11632038634SShengjiu Wang 	7, 14, 28, 56, 112, 224, 448,  896,  /* divider = 7 */
11732038634SShengjiu Wang 	8, 16, 32, 64, 128, 256, 512, 1024,  /* divider = 8 */
11832038634SShengjiu Wang };
11932038634SShengjiu Wang 
12032038634SShengjiu Wang /*
12132038634SShengjiu Wang  * Check if the divider is available for internal ratio mode
12232038634SShengjiu Wang  */
12332038634SShengjiu Wang static bool fsl_asrc_divider_avail(int clk_rate, int rate, int *div)
12432038634SShengjiu Wang {
12532038634SShengjiu Wang 	u32 rem, i;
12632038634SShengjiu Wang 	u64 n;
12732038634SShengjiu Wang 
12832038634SShengjiu Wang 	if (div)
12932038634SShengjiu Wang 		*div = 0;
13032038634SShengjiu Wang 
13132038634SShengjiu Wang 	if (clk_rate == 0 || rate == 0)
13232038634SShengjiu Wang 		return false;
13332038634SShengjiu Wang 
13432038634SShengjiu Wang 	n = clk_rate;
13532038634SShengjiu Wang 	rem = do_div(n, rate);
13632038634SShengjiu Wang 
13732038634SShengjiu Wang 	if (div)
13832038634SShengjiu Wang 		*div = n;
13932038634SShengjiu Wang 
14032038634SShengjiu Wang 	if (rem != 0)
14132038634SShengjiu Wang 		return false;
14232038634SShengjiu Wang 
14332038634SShengjiu Wang 	for (i = 0; i < DIVIDER_NUM; i++) {
14432038634SShengjiu Wang 		if (n == asrc_clk_divider[i])
14532038634SShengjiu Wang 			break;
14632038634SShengjiu Wang 	}
14732038634SShengjiu Wang 
14832038634SShengjiu Wang 	if (i == DIVIDER_NUM)
14932038634SShengjiu Wang 		return false;
15032038634SShengjiu Wang 
15132038634SShengjiu Wang 	return true;
15232038634SShengjiu Wang }
15332038634SShengjiu Wang 
1543117bb31SNicolin Chen /**
15545e039d9SPierre-Louis Bossart  * fsl_asrc_sel_proc - Select the pre-processing and post-processing options
15645e039d9SPierre-Louis Bossart  * @inrate: input sample rate
15745e039d9SPierre-Louis Bossart  * @outrate: output sample rate
15845e039d9SPierre-Louis Bossart  * @pre_proc: return value for pre-processing option
15945e039d9SPierre-Louis Bossart  * @post_proc: return value for post-processing option
16045e039d9SPierre-Louis Bossart  *
1614aecaa0aSS.j. Wang  * Make sure to exclude following unsupported cases before
1624aecaa0aSS.j. Wang  * calling this function:
1634aecaa0aSS.j. Wang  * 1) inrate > 8.125 * outrate
1644aecaa0aSS.j. Wang  * 2) inrate > 16.125 * outrate
1654aecaa0aSS.j. Wang  *
1664aecaa0aSS.j. Wang  */
1674aecaa0aSS.j. Wang static void fsl_asrc_sel_proc(int inrate, int outrate,
1684aecaa0aSS.j. Wang 			     int *pre_proc, int *post_proc)
1694aecaa0aSS.j. Wang {
1704aecaa0aSS.j. Wang 	bool post_proc_cond2;
1714aecaa0aSS.j. Wang 	bool post_proc_cond0;
1724aecaa0aSS.j. Wang 
1734aecaa0aSS.j. Wang 	/* select pre_proc between [0, 2] */
1744aecaa0aSS.j. Wang 	if (inrate * 8 > 33 * outrate)
1754aecaa0aSS.j. Wang 		*pre_proc = 2;
1764aecaa0aSS.j. Wang 	else if (inrate * 8 > 15 * outrate) {
1774aecaa0aSS.j. Wang 		if (inrate > 152000)
1784aecaa0aSS.j. Wang 			*pre_proc = 2;
1794aecaa0aSS.j. Wang 		else
1804aecaa0aSS.j. Wang 			*pre_proc = 1;
1814aecaa0aSS.j. Wang 	} else if (inrate < 76000)
1824aecaa0aSS.j. Wang 		*pre_proc = 0;
1834aecaa0aSS.j. Wang 	else if (inrate > 152000)
1844aecaa0aSS.j. Wang 		*pre_proc = 2;
1854aecaa0aSS.j. Wang 	else
1864aecaa0aSS.j. Wang 		*pre_proc = 1;
1874aecaa0aSS.j. Wang 
1884aecaa0aSS.j. Wang 	/* Condition for selection of post-processing */
1894aecaa0aSS.j. Wang 	post_proc_cond2 = (inrate * 15 > outrate * 16 && outrate < 56000) ||
1904aecaa0aSS.j. Wang 			  (inrate > 56000 && outrate < 56000);
1914aecaa0aSS.j. Wang 	post_proc_cond0 = inrate * 23 < outrate * 8;
1924aecaa0aSS.j. Wang 
1934aecaa0aSS.j. Wang 	if (post_proc_cond2)
1944aecaa0aSS.j. Wang 		*post_proc = 2;
1954aecaa0aSS.j. Wang 	else if (post_proc_cond0)
1964aecaa0aSS.j. Wang 		*post_proc = 0;
1974aecaa0aSS.j. Wang 	else
1984aecaa0aSS.j. Wang 		*post_proc = 1;
1994aecaa0aSS.j. Wang }
2004aecaa0aSS.j. Wang 
2014aecaa0aSS.j. Wang /**
20245e039d9SPierre-Louis Bossart  * fsl_asrc_request_pair - Request ASRC pair
20345e039d9SPierre-Louis Bossart  * @channels: number of channels
20445e039d9SPierre-Louis Bossart  * @pair: pointer to pair
2053117bb31SNicolin Chen  *
2063117bb31SNicolin Chen  * It assigns pair by the order of A->C->B because allocation of pair B,
2073117bb31SNicolin Chen  * within range [ANCA, ANCA+ANCB-1], depends on the channels of pair A
2083117bb31SNicolin Chen  * while pair A and pair C are comparatively independent.
2093117bb31SNicolin Chen  */
210c16e923dSChenTao static int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair)
2113117bb31SNicolin Chen {
2123117bb31SNicolin Chen 	enum asrc_pair_index index = ASRC_INVALID_PAIR;
2137470704dSShengjiu Wang 	struct fsl_asrc *asrc = pair->asrc;
2147470704dSShengjiu Wang 	struct device *dev = &asrc->pdev->dev;
2153117bb31SNicolin Chen 	unsigned long lock_flags;
2163117bb31SNicolin Chen 	int i, ret = 0;
2173117bb31SNicolin Chen 
2187470704dSShengjiu Wang 	spin_lock_irqsave(&asrc->lock, lock_flags);
2193117bb31SNicolin Chen 
2203117bb31SNicolin Chen 	for (i = ASRC_PAIR_A; i < ASRC_PAIR_MAX_NUM; i++) {
2217470704dSShengjiu Wang 		if (asrc->pair[i] != NULL)
2223117bb31SNicolin Chen 			continue;
2233117bb31SNicolin Chen 
2243117bb31SNicolin Chen 		index = i;
2253117bb31SNicolin Chen 
2263117bb31SNicolin Chen 		if (i != ASRC_PAIR_B)
2273117bb31SNicolin Chen 			break;
2283117bb31SNicolin Chen 	}
2293117bb31SNicolin Chen 
2303117bb31SNicolin Chen 	if (index == ASRC_INVALID_PAIR) {
2313117bb31SNicolin Chen 		dev_err(dev, "all pairs are busy now\n");
2323117bb31SNicolin Chen 		ret = -EBUSY;
2337470704dSShengjiu Wang 	} else if (asrc->channel_avail < channels) {
2343117bb31SNicolin Chen 		dev_err(dev, "can't afford required channels: %d\n", channels);
2353117bb31SNicolin Chen 		ret = -EINVAL;
2363117bb31SNicolin Chen 	} else {
2377470704dSShengjiu Wang 		asrc->channel_avail -= channels;
2387470704dSShengjiu Wang 		asrc->pair[index] = pair;
2393117bb31SNicolin Chen 		pair->channels = channels;
2403117bb31SNicolin Chen 		pair->index = index;
2413117bb31SNicolin Chen 	}
2423117bb31SNicolin Chen 
2437470704dSShengjiu Wang 	spin_unlock_irqrestore(&asrc->lock, lock_flags);
2443117bb31SNicolin Chen 
2453117bb31SNicolin Chen 	return ret;
2463117bb31SNicolin Chen }
2473117bb31SNicolin Chen 
2483117bb31SNicolin Chen /**
24945e039d9SPierre-Louis Bossart  * fsl_asrc_release_pair - Release ASRC pair
25045e039d9SPierre-Louis Bossart  * @pair: pair to release
2513117bb31SNicolin Chen  *
2527470704dSShengjiu Wang  * It clears the resource from asrc and releases the occupied channels.
2533117bb31SNicolin Chen  */
254c16e923dSChenTao static void fsl_asrc_release_pair(struct fsl_asrc_pair *pair)
2553117bb31SNicolin Chen {
2567470704dSShengjiu Wang 	struct fsl_asrc *asrc = pair->asrc;
2573117bb31SNicolin Chen 	enum asrc_pair_index index = pair->index;
2583117bb31SNicolin Chen 	unsigned long lock_flags;
2593117bb31SNicolin Chen 
2603117bb31SNicolin Chen 	/* Make sure the pair is disabled */
2617470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCTR,
2623117bb31SNicolin Chen 			   ASRCTR_ASRCEi_MASK(index), 0);
2633117bb31SNicolin Chen 
2647470704dSShengjiu Wang 	spin_lock_irqsave(&asrc->lock, lock_flags);
2653117bb31SNicolin Chen 
2667470704dSShengjiu Wang 	asrc->channel_avail += pair->channels;
2677470704dSShengjiu Wang 	asrc->pair[index] = NULL;
2683117bb31SNicolin Chen 	pair->error = 0;
2693117bb31SNicolin Chen 
2707470704dSShengjiu Wang 	spin_unlock_irqrestore(&asrc->lock, lock_flags);
2713117bb31SNicolin Chen }
2723117bb31SNicolin Chen 
2733117bb31SNicolin Chen /**
27445e039d9SPierre-Louis Bossart  * fsl_asrc_set_watermarks- configure input and output thresholds
27545e039d9SPierre-Louis Bossart  * @pair: pointer to pair
27645e039d9SPierre-Louis Bossart  * @in: input threshold
27745e039d9SPierre-Louis Bossart  * @out: output threshold
2783117bb31SNicolin Chen  */
2793117bb31SNicolin Chen static void fsl_asrc_set_watermarks(struct fsl_asrc_pair *pair, u32 in, u32 out)
2803117bb31SNicolin Chen {
2817470704dSShengjiu Wang 	struct fsl_asrc *asrc = pair->asrc;
2823117bb31SNicolin Chen 	enum asrc_pair_index index = pair->index;
2833117bb31SNicolin Chen 
2847470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRMCR(index),
2853117bb31SNicolin Chen 			   ASRMCRi_EXTTHRSHi_MASK |
2863117bb31SNicolin Chen 			   ASRMCRi_INFIFO_THRESHOLD_MASK |
2873117bb31SNicolin Chen 			   ASRMCRi_OUTFIFO_THRESHOLD_MASK,
2883117bb31SNicolin Chen 			   ASRMCRi_EXTTHRSHi |
2893117bb31SNicolin Chen 			   ASRMCRi_INFIFO_THRESHOLD(in) |
2903117bb31SNicolin Chen 			   ASRMCRi_OUTFIFO_THRESHOLD(out));
2913117bb31SNicolin Chen }
2923117bb31SNicolin Chen 
2933117bb31SNicolin Chen /**
29445e039d9SPierre-Louis Bossart  * fsl_asrc_cal_asrck_divisor - Calculate the total divisor between asrck clock rate and sample rate
29545e039d9SPierre-Louis Bossart  * @pair: pointer to pair
29645e039d9SPierre-Louis Bossart  * @div: divider
2973117bb31SNicolin Chen  *
2983117bb31SNicolin Chen  * It follows the formula clk_rate = samplerate * (2 ^ prescaler) * divider
2993117bb31SNicolin Chen  */
3003117bb31SNicolin Chen static u32 fsl_asrc_cal_asrck_divisor(struct fsl_asrc_pair *pair, u32 div)
3013117bb31SNicolin Chen {
3023117bb31SNicolin Chen 	u32 ps;
3033117bb31SNicolin Chen 
3043117bb31SNicolin Chen 	/* Calculate the divisors: prescaler [2^0, 2^7], divder [1, 8] */
3053117bb31SNicolin Chen 	for (ps = 0; div > 8; ps++)
3063117bb31SNicolin Chen 		div >>= 1;
3073117bb31SNicolin Chen 
3083117bb31SNicolin Chen 	return ((div - 1) << ASRCDRi_AxCPi_WIDTH) | ps;
3093117bb31SNicolin Chen }
3103117bb31SNicolin Chen 
3113117bb31SNicolin Chen /**
31245e039d9SPierre-Louis Bossart  * fsl_asrc_set_ideal_ratio - Calculate and set the ratio for Ideal Ratio mode only
31345e039d9SPierre-Louis Bossart  * @pair: pointer to pair
31445e039d9SPierre-Louis Bossart  * @inrate: input rate
31545e039d9SPierre-Louis Bossart  * @outrate: output rate
3163117bb31SNicolin Chen  *
3173117bb31SNicolin Chen  * The ratio is a 32-bit fixed point value with 26 fractional bits.
3183117bb31SNicolin Chen  */
3193117bb31SNicolin Chen static int fsl_asrc_set_ideal_ratio(struct fsl_asrc_pair *pair,
3203117bb31SNicolin Chen 				    int inrate, int outrate)
3213117bb31SNicolin Chen {
3227470704dSShengjiu Wang 	struct fsl_asrc *asrc = pair->asrc;
3233117bb31SNicolin Chen 	enum asrc_pair_index index = pair->index;
3243117bb31SNicolin Chen 	unsigned long ratio;
3253117bb31SNicolin Chen 	int i;
3263117bb31SNicolin Chen 
3273117bb31SNicolin Chen 	if (!outrate) {
3283117bb31SNicolin Chen 		pair_err("output rate should not be zero\n");
3293117bb31SNicolin Chen 		return -EINVAL;
3303117bb31SNicolin Chen 	}
3313117bb31SNicolin Chen 
3323117bb31SNicolin Chen 	/* Calculate the intergal part of the ratio */
3333117bb31SNicolin Chen 	ratio = (inrate / outrate) << IDEAL_RATIO_DECIMAL_DEPTH;
3343117bb31SNicolin Chen 
3353117bb31SNicolin Chen 	/* ... and then the 26 depth decimal part */
3363117bb31SNicolin Chen 	inrate %= outrate;
3373117bb31SNicolin Chen 
3383117bb31SNicolin Chen 	for (i = 1; i <= IDEAL_RATIO_DECIMAL_DEPTH; i++) {
3393117bb31SNicolin Chen 		inrate <<= 1;
3403117bb31SNicolin Chen 
3413117bb31SNicolin Chen 		if (inrate < outrate)
3423117bb31SNicolin Chen 			continue;
3433117bb31SNicolin Chen 
3443117bb31SNicolin Chen 		ratio |= 1 << (IDEAL_RATIO_DECIMAL_DEPTH - i);
3453117bb31SNicolin Chen 		inrate -= outrate;
3463117bb31SNicolin Chen 
3473117bb31SNicolin Chen 		if (!inrate)
3483117bb31SNicolin Chen 			break;
3493117bb31SNicolin Chen 	}
3503117bb31SNicolin Chen 
3517470704dSShengjiu Wang 	regmap_write(asrc->regmap, REG_ASRIDRL(index), ratio);
3527470704dSShengjiu Wang 	regmap_write(asrc->regmap, REG_ASRIDRH(index), ratio >> 24);
3533117bb31SNicolin Chen 
3543117bb31SNicolin Chen 	return 0;
3553117bb31SNicolin Chen }
3563117bb31SNicolin Chen 
3573117bb31SNicolin Chen /**
35845e039d9SPierre-Louis Bossart  * fsl_asrc_config_pair - Configure the assigned ASRC pair
35945e039d9SPierre-Louis Bossart  * @pair: pointer to pair
36045e039d9SPierre-Louis Bossart  * @use_ideal_rate: boolean configuration
3613117bb31SNicolin Chen  *
3623117bb31SNicolin Chen  * It configures those ASRC registers according to a configuration instance
3633117bb31SNicolin Chen  * of struct asrc_config which includes in/output sample rate, width, channel
3643117bb31SNicolin Chen  * and clock settings.
365b39eb1e2SShengjiu Wang  *
366b39eb1e2SShengjiu Wang  * Note:
367b39eb1e2SShengjiu Wang  * The ideal ratio configuration can work with a flexible clock rate setting.
368b39eb1e2SShengjiu Wang  * Using IDEAL_RATIO_RATE gives a faster converting speed but overloads ASRC.
369b39eb1e2SShengjiu Wang  * For a regular audio playback, the clock rate should not be slower than an
370b39eb1e2SShengjiu Wang  * clock rate aligning with the output sample rate; For a use case requiring
371b39eb1e2SShengjiu Wang  * faster conversion, set use_ideal_rate to have the faster speed.
3723117bb31SNicolin Chen  */
373b39eb1e2SShengjiu Wang static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool use_ideal_rate)
3743117bb31SNicolin Chen {
375be7bd03fSShengjiu Wang 	struct fsl_asrc_pair_priv *pair_priv = pair->private;
376be7bd03fSShengjiu Wang 	struct asrc_config *config = pair_priv->config;
3777470704dSShengjiu Wang 	struct fsl_asrc *asrc = pair->asrc;
378be7bd03fSShengjiu Wang 	struct fsl_asrc_priv *asrc_priv = asrc->private;
3793117bb31SNicolin Chen 	enum asrc_pair_index index = pair->index;
3804bf62571SShengjiu Wang 	enum asrc_word_width input_word_width;
3814bf62571SShengjiu Wang 	enum asrc_word_width output_word_width;
3824e13eb72SNicolin Chen 	u32 inrate, outrate, indiv, outdiv;
38332038634SShengjiu Wang 	u32 clk_index[2], div[2];
384b39eb1e2SShengjiu Wang 	u64 clk_rate;
3853117bb31SNicolin Chen 	int in, out, channels;
3864aecaa0aSS.j. Wang 	int pre_proc, post_proc;
3873117bb31SNicolin Chen 	struct clk *clk;
38832038634SShengjiu Wang 	bool ideal, div_avail;
3893117bb31SNicolin Chen 
3903117bb31SNicolin Chen 	if (!config) {
3913117bb31SNicolin Chen 		pair_err("invalid pair config\n");
3923117bb31SNicolin Chen 		return -EINVAL;
3933117bb31SNicolin Chen 	}
3943117bb31SNicolin Chen 
3953117bb31SNicolin Chen 	/* Validate channels */
3963117bb31SNicolin Chen 	if (config->channel_num < 1 || config->channel_num > 10) {
3973117bb31SNicolin Chen 		pair_err("does not support %d channels\n", config->channel_num);
3983117bb31SNicolin Chen 		return -EINVAL;
3993117bb31SNicolin Chen 	}
4003117bb31SNicolin Chen 
4014bf62571SShengjiu Wang 	switch (snd_pcm_format_width(config->input_format)) {
4024bf62571SShengjiu Wang 	case 8:
4034bf62571SShengjiu Wang 		input_word_width = ASRC_WIDTH_8_BIT;
4044bf62571SShengjiu Wang 		break;
4054bf62571SShengjiu Wang 	case 16:
4064bf62571SShengjiu Wang 		input_word_width = ASRC_WIDTH_16_BIT;
4074bf62571SShengjiu Wang 		break;
4084bf62571SShengjiu Wang 	case 24:
4094bf62571SShengjiu Wang 		input_word_width = ASRC_WIDTH_24_BIT;
4104bf62571SShengjiu Wang 		break;
4114bf62571SShengjiu Wang 	default:
4124bf62571SShengjiu Wang 		pair_err("does not support this input format, %d\n",
4134bf62571SShengjiu Wang 			 config->input_format);
4144bf62571SShengjiu Wang 		return -EINVAL;
4154bf62571SShengjiu Wang 	}
4164bf62571SShengjiu Wang 
4174bf62571SShengjiu Wang 	switch (snd_pcm_format_width(config->output_format)) {
4184bf62571SShengjiu Wang 	case 16:
4194bf62571SShengjiu Wang 		output_word_width = ASRC_WIDTH_16_BIT;
4204bf62571SShengjiu Wang 		break;
4214bf62571SShengjiu Wang 	case 24:
4224bf62571SShengjiu Wang 		output_word_width = ASRC_WIDTH_24_BIT;
4234bf62571SShengjiu Wang 		break;
4244bf62571SShengjiu Wang 	default:
4254bf62571SShengjiu Wang 		pair_err("does not support this output format, %d\n",
4264bf62571SShengjiu Wang 			 config->output_format);
4273117bb31SNicolin Chen 		return -EINVAL;
4283117bb31SNicolin Chen 	}
4293117bb31SNicolin Chen 
4304e13eb72SNicolin Chen 	inrate = config->input_sample_rate;
4314e13eb72SNicolin Chen 	outrate = config->output_sample_rate;
4324e13eb72SNicolin Chen 	ideal = config->inclk == INCLK_NONE;
4334e13eb72SNicolin Chen 
4343117bb31SNicolin Chen 	/* Validate input and output sample rates */
435d281bf5dSS.j. Wang 	for (in = 0; in < ARRAY_SIZE(supported_asrc_rate); in++)
436d281bf5dSS.j. Wang 		if (inrate == supported_asrc_rate[in])
4373117bb31SNicolin Chen 			break;
4383117bb31SNicolin Chen 
439d281bf5dSS.j. Wang 	if (in == ARRAY_SIZE(supported_asrc_rate)) {
4403117bb31SNicolin Chen 		pair_err("unsupported input sample rate: %dHz\n", inrate);
4413117bb31SNicolin Chen 		return -EINVAL;
4423117bb31SNicolin Chen 	}
4433117bb31SNicolin Chen 
4443117bb31SNicolin Chen 	for (out = 0; out < ARRAY_SIZE(supported_asrc_rate); out++)
4453117bb31SNicolin Chen 		if (outrate == supported_asrc_rate[out])
4463117bb31SNicolin Chen 			break;
4473117bb31SNicolin Chen 
4483117bb31SNicolin Chen 	if (out == ARRAY_SIZE(supported_asrc_rate)) {
4493117bb31SNicolin Chen 		pair_err("unsupported output sample rate: %dHz\n", outrate);
4503117bb31SNicolin Chen 		return -EINVAL;
4513117bb31SNicolin Chen 	}
4523117bb31SNicolin Chen 
453d281bf5dSS.j. Wang 	if ((outrate >= 5512 && outrate <= 30000) &&
454b06c58c2SS.j. Wang 	    (outrate > 24 * inrate || inrate > 8 * outrate)) {
455fff6e03cSZidan Wang 		pair_err("exceed supported ratio range [1/24, 8] for \
456fff6e03cSZidan Wang 				inrate/outrate: %d/%d\n", inrate, outrate);
457fff6e03cSZidan Wang 		return -EINVAL;
458fff6e03cSZidan Wang 	}
459fff6e03cSZidan Wang 
4603117bb31SNicolin Chen 	/* Validate input and output clock sources */
461be7bd03fSShengjiu Wang 	clk_index[IN] = asrc_priv->clk_map[IN][config->inclk];
462be7bd03fSShengjiu Wang 	clk_index[OUT] = asrc_priv->clk_map[OUT][config->outclk];
4633117bb31SNicolin Chen 
4643117bb31SNicolin Chen 	/* We only have output clock for ideal ratio mode */
465be7bd03fSShengjiu Wang 	clk = asrc_priv->asrck_clk[clk_index[ideal ? OUT : IN]];
4663117bb31SNicolin Chen 
467b39eb1e2SShengjiu Wang 	clk_rate = clk_get_rate(clk);
46832038634SShengjiu Wang 	div_avail = fsl_asrc_divider_avail(clk_rate, inrate, &div[IN]);
469b39eb1e2SShengjiu Wang 
470b39eb1e2SShengjiu Wang 	/*
471b39eb1e2SShengjiu Wang 	 * The divider range is [1, 1024], defined by the hardware. For non-
472b39eb1e2SShengjiu Wang 	 * ideal ratio configuration, clock rate has to be strictly aligned
473b39eb1e2SShengjiu Wang 	 * with the sample rate. For ideal ratio configuration, clock rates
474b39eb1e2SShengjiu Wang 	 * only result in different converting speeds. So remainder does not
475b39eb1e2SShengjiu Wang 	 * matter, as long as we keep the divider within its valid range.
476b39eb1e2SShengjiu Wang 	 */
47732038634SShengjiu Wang 	if (div[IN] == 0 || (!ideal && !div_avail)) {
4783117bb31SNicolin Chen 		pair_err("failed to support input sample rate %dHz by asrck_%x\n",
4793117bb31SNicolin Chen 				inrate, clk_index[ideal ? OUT : IN]);
4803117bb31SNicolin Chen 		return -EINVAL;
4813117bb31SNicolin Chen 	}
4823117bb31SNicolin Chen 
483b39eb1e2SShengjiu Wang 	div[IN] = min_t(u32, 1024, div[IN]);
484b39eb1e2SShengjiu Wang 
485be7bd03fSShengjiu Wang 	clk = asrc_priv->asrck_clk[clk_index[OUT]];
486b39eb1e2SShengjiu Wang 	clk_rate = clk_get_rate(clk);
487b39eb1e2SShengjiu Wang 	if (ideal && use_ideal_rate)
48832038634SShengjiu Wang 		div_avail = fsl_asrc_divider_avail(clk_rate, IDEAL_RATIO_RATE, &div[OUT]);
4893117bb31SNicolin Chen 	else
49032038634SShengjiu Wang 		div_avail = fsl_asrc_divider_avail(clk_rate, outrate, &div[OUT]);
4913117bb31SNicolin Chen 
492b39eb1e2SShengjiu Wang 	/* Output divider has the same limitation as the input one */
49332038634SShengjiu Wang 	if (div[OUT] == 0 || (!ideal && !div_avail)) {
4943117bb31SNicolin Chen 		pair_err("failed to support output sample rate %dHz by asrck_%x\n",
4953117bb31SNicolin Chen 				outrate, clk_index[OUT]);
4963117bb31SNicolin Chen 		return -EINVAL;
4973117bb31SNicolin Chen 	}
4983117bb31SNicolin Chen 
499b39eb1e2SShengjiu Wang 	div[OUT] = min_t(u32, 1024, div[OUT]);
500b39eb1e2SShengjiu Wang 
5013117bb31SNicolin Chen 	/* Set the channel number */
5023117bb31SNicolin Chen 	channels = config->channel_num;
5033117bb31SNicolin Chen 
504be7bd03fSShengjiu Wang 	if (asrc_priv->soc->channel_bits < 4)
5053117bb31SNicolin Chen 		channels /= 2;
5063117bb31SNicolin Chen 
5073117bb31SNicolin Chen 	/* Update channels for current pair */
5087470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCNCR,
509be7bd03fSShengjiu Wang 			   ASRCNCR_ANCi_MASK(index, asrc_priv->soc->channel_bits),
510be7bd03fSShengjiu Wang 			   ASRCNCR_ANCi(index, channels, asrc_priv->soc->channel_bits));
5113117bb31SNicolin Chen 
5123117bb31SNicolin Chen 	/* Default setting: Automatic selection for processing mode */
5137470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCTR,
5143117bb31SNicolin Chen 			   ASRCTR_ATSi_MASK(index), ASRCTR_ATS(index));
5157470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCTR,
5163117bb31SNicolin Chen 			   ASRCTR_USRi_MASK(index), 0);
5173117bb31SNicolin Chen 
5183117bb31SNicolin Chen 	/* Set the input and output clock sources */
5197470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCSR,
5203117bb31SNicolin Chen 			   ASRCSR_AICSi_MASK(index) | ASRCSR_AOCSi_MASK(index),
5213117bb31SNicolin Chen 			   ASRCSR_AICS(index, clk_index[IN]) |
5223117bb31SNicolin Chen 			   ASRCSR_AOCS(index, clk_index[OUT]));
5233117bb31SNicolin Chen 
5243117bb31SNicolin Chen 	/* Calculate the input clock divisors */
5253117bb31SNicolin Chen 	indiv = fsl_asrc_cal_asrck_divisor(pair, div[IN]);
5263117bb31SNicolin Chen 	outdiv = fsl_asrc_cal_asrck_divisor(pair, div[OUT]);
5273117bb31SNicolin Chen 
5283117bb31SNicolin Chen 	/* Suppose indiv and outdiv includes prescaler, so add its MASK too */
5297470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCDR(index),
5303117bb31SNicolin Chen 			   ASRCDRi_AOCPi_MASK(index) | ASRCDRi_AICPi_MASK(index) |
5313117bb31SNicolin Chen 			   ASRCDRi_AOCDi_MASK(index) | ASRCDRi_AICDi_MASK(index),
5323117bb31SNicolin Chen 			   ASRCDRi_AOCP(index, outdiv) | ASRCDRi_AICP(index, indiv));
5333117bb31SNicolin Chen 
5343117bb31SNicolin Chen 	/* Implement word_width configurations */
5357470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRMCR1(index),
5363117bb31SNicolin Chen 			   ASRMCR1i_OW16_MASK | ASRMCR1i_IWD_MASK,
5374bf62571SShengjiu Wang 			   ASRMCR1i_OW16(output_word_width) |
5384bf62571SShengjiu Wang 			   ASRMCR1i_IWD(input_word_width));
5393117bb31SNicolin Chen 
5403117bb31SNicolin Chen 	/* Enable BUFFER STALL */
5417470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRMCR(index),
5423117bb31SNicolin Chen 			   ASRMCRi_BUFSTALLi_MASK, ASRMCRi_BUFSTALLi);
5433117bb31SNicolin Chen 
5443117bb31SNicolin Chen 	/* Set default thresholds for input and output FIFO */
5453117bb31SNicolin Chen 	fsl_asrc_set_watermarks(pair, ASRC_INPUTFIFO_THRESHOLD,
5463117bb31SNicolin Chen 				ASRC_INPUTFIFO_THRESHOLD);
5473117bb31SNicolin Chen 
5484091fb95SMasahiro Yamada 	/* Configure the following only for Ideal Ratio mode */
5493117bb31SNicolin Chen 	if (!ideal)
5503117bb31SNicolin Chen 		return 0;
5513117bb31SNicolin Chen 
5523117bb31SNicolin Chen 	/* Clear ASTSx bit to use Ideal Ratio mode */
5537470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCTR,
5543117bb31SNicolin Chen 			   ASRCTR_ATSi_MASK(index), 0);
5553117bb31SNicolin Chen 
5563117bb31SNicolin Chen 	/* Enable Ideal Ratio mode */
5577470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCTR,
5583117bb31SNicolin Chen 			   ASRCTR_IDRi_MASK(index) | ASRCTR_USRi_MASK(index),
5593117bb31SNicolin Chen 			   ASRCTR_IDR(index) | ASRCTR_USR(index));
5603117bb31SNicolin Chen 
5614aecaa0aSS.j. Wang 	fsl_asrc_sel_proc(inrate, outrate, &pre_proc, &post_proc);
5624aecaa0aSS.j. Wang 
5633117bb31SNicolin Chen 	/* Apply configurations for pre- and post-processing */
5647470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCFG,
5653117bb31SNicolin Chen 			   ASRCFG_PREMODi_MASK(index) |	ASRCFG_POSTMODi_MASK(index),
5664aecaa0aSS.j. Wang 			   ASRCFG_PREMOD(index, pre_proc) |
5674aecaa0aSS.j. Wang 			   ASRCFG_POSTMOD(index, post_proc));
5683117bb31SNicolin Chen 
5693117bb31SNicolin Chen 	return fsl_asrc_set_ideal_ratio(pair, inrate, outrate);
5703117bb31SNicolin Chen }
5713117bb31SNicolin Chen 
5723117bb31SNicolin Chen /**
57345e039d9SPierre-Louis Bossart  * fsl_asrc_start_pair - Start the assigned ASRC pair
57445e039d9SPierre-Louis Bossart  * @pair: pointer to pair
5753117bb31SNicolin Chen  *
5763117bb31SNicolin Chen  * It enables the assigned pair and makes it stopped at the stall level.
5773117bb31SNicolin Chen  */
5783117bb31SNicolin Chen static void fsl_asrc_start_pair(struct fsl_asrc_pair *pair)
5793117bb31SNicolin Chen {
5807470704dSShengjiu Wang 	struct fsl_asrc *asrc = pair->asrc;
5813117bb31SNicolin Chen 	enum asrc_pair_index index = pair->index;
5823117bb31SNicolin Chen 	int reg, retry = 10, i;
5833117bb31SNicolin Chen 
5843117bb31SNicolin Chen 	/* Enable the current pair */
5857470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCTR,
5863117bb31SNicolin Chen 			   ASRCTR_ASRCEi_MASK(index), ASRCTR_ASRCE(index));
5873117bb31SNicolin Chen 
5883117bb31SNicolin Chen 	/* Wait for status of initialization */
5893117bb31SNicolin Chen 	do {
5903117bb31SNicolin Chen 		udelay(5);
5917470704dSShengjiu Wang 		regmap_read(asrc->regmap, REG_ASRCFG, &reg);
5923117bb31SNicolin Chen 		reg &= ASRCFG_INIRQi_MASK(index);
5933117bb31SNicolin Chen 	} while (!reg && --retry);
5943117bb31SNicolin Chen 
5953117bb31SNicolin Chen 	/* Make the input fifo to ASRC STALL level */
5967470704dSShengjiu Wang 	regmap_read(asrc->regmap, REG_ASRCNCR, &reg);
5973117bb31SNicolin Chen 	for (i = 0; i < pair->channels * 4; i++)
5987470704dSShengjiu Wang 		regmap_write(asrc->regmap, REG_ASRDI(index), 0);
5993117bb31SNicolin Chen 
6003117bb31SNicolin Chen 	/* Enable overload interrupt */
6017470704dSShengjiu Wang 	regmap_write(asrc->regmap, REG_ASRIER, ASRIER_AOLIE);
6023117bb31SNicolin Chen }
6033117bb31SNicolin Chen 
6043117bb31SNicolin Chen /**
60545e039d9SPierre-Louis Bossart  * fsl_asrc_stop_pair - Stop the assigned ASRC pair
60645e039d9SPierre-Louis Bossart  * @pair: pointer to pair
6073117bb31SNicolin Chen  */
6083117bb31SNicolin Chen static void fsl_asrc_stop_pair(struct fsl_asrc_pair *pair)
6093117bb31SNicolin Chen {
6107470704dSShengjiu Wang 	struct fsl_asrc *asrc = pair->asrc;
6113117bb31SNicolin Chen 	enum asrc_pair_index index = pair->index;
6123117bb31SNicolin Chen 
6133117bb31SNicolin Chen 	/* Stop the current pair */
6147470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCTR,
6153117bb31SNicolin Chen 			   ASRCTR_ASRCEi_MASK(index), 0);
6163117bb31SNicolin Chen }
6173117bb31SNicolin Chen 
6183117bb31SNicolin Chen /**
61945e039d9SPierre-Louis Bossart  * fsl_asrc_get_dma_channel- Get DMA channel according to the pair and direction.
62045e039d9SPierre-Louis Bossart  * @pair: pointer to pair
62145e039d9SPierre-Louis Bossart  * @dir: DMA direction
6223117bb31SNicolin Chen  */
623cff1f8b4Sshengjiu wang static struct dma_chan *fsl_asrc_get_dma_channel(struct fsl_asrc_pair *pair,
624cff1f8b4Sshengjiu wang 						 bool dir)
6253117bb31SNicolin Chen {
6267470704dSShengjiu Wang 	struct fsl_asrc *asrc = pair->asrc;
6273117bb31SNicolin Chen 	enum asrc_pair_index index = pair->index;
6283117bb31SNicolin Chen 	char name[4];
6293117bb31SNicolin Chen 
6303117bb31SNicolin Chen 	sprintf(name, "%cx%c", dir == IN ? 'r' : 't', index + 'a');
6313117bb31SNicolin Chen 
6327470704dSShengjiu Wang 	return dma_request_slave_channel(&asrc->pdev->dev, name);
6333117bb31SNicolin Chen }
6343117bb31SNicolin Chen 
63553f67a78SS.j. Wang static int fsl_asrc_dai_startup(struct snd_pcm_substream *substream,
63653f67a78SS.j. Wang 				struct snd_soc_dai *dai)
63753f67a78SS.j. Wang {
6387470704dSShengjiu Wang 	struct fsl_asrc *asrc = snd_soc_dai_get_drvdata(dai);
639be7bd03fSShengjiu Wang 	struct fsl_asrc_priv *asrc_priv = asrc->private;
64053f67a78SS.j. Wang 
64153f67a78SS.j. Wang 	/* Odd channel number is not valid for older ASRC (channel_bits==3) */
642be7bd03fSShengjiu Wang 	if (asrc_priv->soc->channel_bits == 3)
64353f67a78SS.j. Wang 		snd_pcm_hw_constraint_step(substream->runtime, 0,
64453f67a78SS.j. Wang 					   SNDRV_PCM_HW_PARAM_CHANNELS, 2);
64553f67a78SS.j. Wang 
646d281bf5dSS.j. Wang 
647d281bf5dSS.j. Wang 	return snd_pcm_hw_constraint_list(substream->runtime, 0,
648d281bf5dSS.j. Wang 			SNDRV_PCM_HW_PARAM_RATE, &fsl_asrc_rate_constraints);
64953f67a78SS.j. Wang }
65053f67a78SS.j. Wang 
651d0250cf4SShengjiu Wang /* Select proper clock source for internal ratio mode */
652d0250cf4SShengjiu Wang static void fsl_asrc_select_clk(struct fsl_asrc_priv *asrc_priv,
653d0250cf4SShengjiu Wang 				struct fsl_asrc_pair *pair,
654d0250cf4SShengjiu Wang 				int in_rate,
655d0250cf4SShengjiu Wang 				int out_rate)
656d0250cf4SShengjiu Wang {
657d0250cf4SShengjiu Wang 	struct fsl_asrc_pair_priv *pair_priv = pair->private;
658d0250cf4SShengjiu Wang 	struct asrc_config *config = pair_priv->config;
659d0250cf4SShengjiu Wang 	int rate[2], select_clk[2]; /* Array size 2 means IN and OUT */
660d0250cf4SShengjiu Wang 	int clk_rate, clk_index;
661b2967435SPierre-Louis Bossart 	int i, j;
662d0250cf4SShengjiu Wang 
663d0250cf4SShengjiu Wang 	rate[IN] = in_rate;
664d0250cf4SShengjiu Wang 	rate[OUT] = out_rate;
665d0250cf4SShengjiu Wang 
666d0250cf4SShengjiu Wang 	/* Select proper clock source for internal ratio mode */
667d0250cf4SShengjiu Wang 	for (j = 0; j < 2; j++) {
668d0250cf4SShengjiu Wang 		for (i = 0; i < ASRC_CLK_MAP_LEN; i++) {
669d0250cf4SShengjiu Wang 			clk_index = asrc_priv->clk_map[j][i];
670d0250cf4SShengjiu Wang 			clk_rate = clk_get_rate(asrc_priv->asrck_clk[clk_index]);
671d0250cf4SShengjiu Wang 			/* Only match a perfect clock source with no remainder */
67232038634SShengjiu Wang 			if (fsl_asrc_divider_avail(clk_rate, rate[j], NULL))
673d0250cf4SShengjiu Wang 				break;
674d0250cf4SShengjiu Wang 		}
675d0250cf4SShengjiu Wang 
676d0250cf4SShengjiu Wang 		select_clk[j] = i;
677d0250cf4SShengjiu Wang 	}
678d0250cf4SShengjiu Wang 
679d0250cf4SShengjiu Wang 	/* Switch to ideal ratio mode if there is no proper clock source */
680d0250cf4SShengjiu Wang 	if (select_clk[IN] == ASRC_CLK_MAP_LEN || select_clk[OUT] == ASRC_CLK_MAP_LEN) {
681d0250cf4SShengjiu Wang 		select_clk[IN] = INCLK_NONE;
682d0250cf4SShengjiu Wang 		select_clk[OUT] = OUTCLK_ASRCK1_CLK;
683d0250cf4SShengjiu Wang 	}
684d0250cf4SShengjiu Wang 
685d0250cf4SShengjiu Wang 	config->inclk = select_clk[IN];
686d0250cf4SShengjiu Wang 	config->outclk = select_clk[OUT];
687d0250cf4SShengjiu Wang }
688d0250cf4SShengjiu Wang 
6893117bb31SNicolin Chen static int fsl_asrc_dai_hw_params(struct snd_pcm_substream *substream,
6903117bb31SNicolin Chen 				  struct snd_pcm_hw_params *params,
6913117bb31SNicolin Chen 				  struct snd_soc_dai *dai)
6923117bb31SNicolin Chen {
6937470704dSShengjiu Wang 	struct fsl_asrc *asrc = snd_soc_dai_get_drvdata(dai);
694d0250cf4SShengjiu Wang 	struct fsl_asrc_priv *asrc_priv = asrc->private;
6953117bb31SNicolin Chen 	struct snd_pcm_runtime *runtime = substream->runtime;
6963117bb31SNicolin Chen 	struct fsl_asrc_pair *pair = runtime->private_data;
697be7bd03fSShengjiu Wang 	struct fsl_asrc_pair_priv *pair_priv = pair->private;
6983117bb31SNicolin Chen 	unsigned int channels = params_channels(params);
6993117bb31SNicolin Chen 	unsigned int rate = params_rate(params);
7003117bb31SNicolin Chen 	struct asrc_config config;
7014bf62571SShengjiu Wang 	int ret;
7023117bb31SNicolin Chen 
7033117bb31SNicolin Chen 	ret = fsl_asrc_request_pair(channels, pair);
7043117bb31SNicolin Chen 	if (ret) {
7053117bb31SNicolin Chen 		dev_err(dai->dev, "fail to request asrc pair\n");
7063117bb31SNicolin Chen 		return ret;
7073117bb31SNicolin Chen 	}
7083117bb31SNicolin Chen 
709be7bd03fSShengjiu Wang 	pair_priv->config = &config;
7103117bb31SNicolin Chen 
7113117bb31SNicolin Chen 	config.pair = pair->index;
7123117bb31SNicolin Chen 	config.channel_num = channels;
7133117bb31SNicolin Chen 
7143117bb31SNicolin Chen 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
7154bf62571SShengjiu Wang 		config.input_format   = params_format(params);
7164520af41SShengjiu Wang 		config.output_format  = asrc->asrc_format;
7173117bb31SNicolin Chen 		config.input_sample_rate  = rate;
7187470704dSShengjiu Wang 		config.output_sample_rate = asrc->asrc_rate;
7193117bb31SNicolin Chen 	} else {
7204520af41SShengjiu Wang 		config.input_format   = asrc->asrc_format;
7214bf62571SShengjiu Wang 		config.output_format  = params_format(params);
7227470704dSShengjiu Wang 		config.input_sample_rate  = asrc->asrc_rate;
7233117bb31SNicolin Chen 		config.output_sample_rate = rate;
7243117bb31SNicolin Chen 	}
7253117bb31SNicolin Chen 
726d0250cf4SShengjiu Wang 	fsl_asrc_select_clk(asrc_priv, pair,
727d0250cf4SShengjiu Wang 			    config.input_sample_rate,
728d0250cf4SShengjiu Wang 			    config.output_sample_rate);
729d0250cf4SShengjiu Wang 
730b39eb1e2SShengjiu Wang 	ret = fsl_asrc_config_pair(pair, false);
7313117bb31SNicolin Chen 	if (ret) {
7323117bb31SNicolin Chen 		dev_err(dai->dev, "fail to config asrc pair\n");
7333117bb31SNicolin Chen 		return ret;
7343117bb31SNicolin Chen 	}
7353117bb31SNicolin Chen 
7363117bb31SNicolin Chen 	return 0;
7373117bb31SNicolin Chen }
7383117bb31SNicolin Chen 
7393117bb31SNicolin Chen static int fsl_asrc_dai_hw_free(struct snd_pcm_substream *substream,
7403117bb31SNicolin Chen 				struct snd_soc_dai *dai)
7413117bb31SNicolin Chen {
7423117bb31SNicolin Chen 	struct snd_pcm_runtime *runtime = substream->runtime;
7433117bb31SNicolin Chen 	struct fsl_asrc_pair *pair = runtime->private_data;
7443117bb31SNicolin Chen 
7453117bb31SNicolin Chen 	if (pair)
7463117bb31SNicolin Chen 		fsl_asrc_release_pair(pair);
7473117bb31SNicolin Chen 
7483117bb31SNicolin Chen 	return 0;
7493117bb31SNicolin Chen }
7503117bb31SNicolin Chen 
7513117bb31SNicolin Chen static int fsl_asrc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
7523117bb31SNicolin Chen 				struct snd_soc_dai *dai)
7533117bb31SNicolin Chen {
7543117bb31SNicolin Chen 	struct snd_pcm_runtime *runtime = substream->runtime;
7553117bb31SNicolin Chen 	struct fsl_asrc_pair *pair = runtime->private_data;
7563117bb31SNicolin Chen 
7573117bb31SNicolin Chen 	switch (cmd) {
7583117bb31SNicolin Chen 	case SNDRV_PCM_TRIGGER_START:
7593117bb31SNicolin Chen 	case SNDRV_PCM_TRIGGER_RESUME:
7603117bb31SNicolin Chen 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
7613117bb31SNicolin Chen 		fsl_asrc_start_pair(pair);
7623117bb31SNicolin Chen 		break;
7633117bb31SNicolin Chen 	case SNDRV_PCM_TRIGGER_STOP:
7643117bb31SNicolin Chen 	case SNDRV_PCM_TRIGGER_SUSPEND:
7653117bb31SNicolin Chen 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
7663117bb31SNicolin Chen 		fsl_asrc_stop_pair(pair);
7673117bb31SNicolin Chen 		break;
7683117bb31SNicolin Chen 	default:
7693117bb31SNicolin Chen 		return -EINVAL;
7703117bb31SNicolin Chen 	}
7713117bb31SNicolin Chen 
7723117bb31SNicolin Chen 	return 0;
7733117bb31SNicolin Chen }
7743117bb31SNicolin Chen 
77529a22ebfSGustavo A. R. Silva static const struct snd_soc_dai_ops fsl_asrc_dai_ops = {
77653f67a78SS.j. Wang 	.startup      = fsl_asrc_dai_startup,
7773117bb31SNicolin Chen 	.hw_params    = fsl_asrc_dai_hw_params,
7783117bb31SNicolin Chen 	.hw_free      = fsl_asrc_dai_hw_free,
7793117bb31SNicolin Chen 	.trigger      = fsl_asrc_dai_trigger,
7803117bb31SNicolin Chen };
7813117bb31SNicolin Chen 
7823117bb31SNicolin Chen static int fsl_asrc_dai_probe(struct snd_soc_dai *dai)
7833117bb31SNicolin Chen {
7847470704dSShengjiu Wang 	struct fsl_asrc *asrc = snd_soc_dai_get_drvdata(dai);
7853117bb31SNicolin Chen 
7867470704dSShengjiu Wang 	snd_soc_dai_init_dma_data(dai, &asrc->dma_params_tx,
7877470704dSShengjiu Wang 				  &asrc->dma_params_rx);
7883117bb31SNicolin Chen 
7893117bb31SNicolin Chen 	return 0;
7903117bb31SNicolin Chen }
7913117bb31SNicolin Chen 
7923117bb31SNicolin Chen #define FSL_ASRC_FORMATS	(SNDRV_PCM_FMTBIT_S24_LE | \
7933117bb31SNicolin Chen 				 SNDRV_PCM_FMTBIT_S16_LE | \
794109539c9SShengjiu Wang 				 SNDRV_PCM_FMTBIT_S24_3LE)
7953117bb31SNicolin Chen 
7963117bb31SNicolin Chen static struct snd_soc_dai_driver fsl_asrc_dai = {
7973117bb31SNicolin Chen 	.probe = fsl_asrc_dai_probe,
7983117bb31SNicolin Chen 	.playback = {
7993117bb31SNicolin Chen 		.stream_name = "ASRC-Playback",
8003117bb31SNicolin Chen 		.channels_min = 1,
8013117bb31SNicolin Chen 		.channels_max = 10,
802d281bf5dSS.j. Wang 		.rate_min = 5512,
803d281bf5dSS.j. Wang 		.rate_max = 192000,
804d281bf5dSS.j. Wang 		.rates = SNDRV_PCM_RATE_KNOT,
805109539c9SShengjiu Wang 		.formats = FSL_ASRC_FORMATS |
806109539c9SShengjiu Wang 			   SNDRV_PCM_FMTBIT_S8,
8073117bb31SNicolin Chen 	},
8083117bb31SNicolin Chen 	.capture = {
8093117bb31SNicolin Chen 		.stream_name = "ASRC-Capture",
8103117bb31SNicolin Chen 		.channels_min = 1,
8113117bb31SNicolin Chen 		.channels_max = 10,
812d281bf5dSS.j. Wang 		.rate_min = 5512,
813d281bf5dSS.j. Wang 		.rate_max = 192000,
814d281bf5dSS.j. Wang 		.rates = SNDRV_PCM_RATE_KNOT,
8153117bb31SNicolin Chen 		.formats = FSL_ASRC_FORMATS,
8163117bb31SNicolin Chen 	},
8173117bb31SNicolin Chen 	.ops = &fsl_asrc_dai_ops,
8183117bb31SNicolin Chen };
8193117bb31SNicolin Chen 
8203117bb31SNicolin Chen static bool fsl_asrc_readable_reg(struct device *dev, unsigned int reg)
8213117bb31SNicolin Chen {
8223117bb31SNicolin Chen 	switch (reg) {
8233117bb31SNicolin Chen 	case REG_ASRCTR:
8243117bb31SNicolin Chen 	case REG_ASRIER:
8253117bb31SNicolin Chen 	case REG_ASRCNCR:
8263117bb31SNicolin Chen 	case REG_ASRCFG:
8273117bb31SNicolin Chen 	case REG_ASRCSR:
8283117bb31SNicolin Chen 	case REG_ASRCDR1:
8293117bb31SNicolin Chen 	case REG_ASRCDR2:
8303117bb31SNicolin Chen 	case REG_ASRSTR:
8313117bb31SNicolin Chen 	case REG_ASRPM1:
8323117bb31SNicolin Chen 	case REG_ASRPM2:
8333117bb31SNicolin Chen 	case REG_ASRPM3:
8343117bb31SNicolin Chen 	case REG_ASRPM4:
8353117bb31SNicolin Chen 	case REG_ASRPM5:
8363117bb31SNicolin Chen 	case REG_ASRTFR1:
8373117bb31SNicolin Chen 	case REG_ASRCCR:
8383117bb31SNicolin Chen 	case REG_ASRDOA:
8393117bb31SNicolin Chen 	case REG_ASRDOB:
8403117bb31SNicolin Chen 	case REG_ASRDOC:
8413117bb31SNicolin Chen 	case REG_ASRIDRHA:
8423117bb31SNicolin Chen 	case REG_ASRIDRLA:
8433117bb31SNicolin Chen 	case REG_ASRIDRHB:
8443117bb31SNicolin Chen 	case REG_ASRIDRLB:
8453117bb31SNicolin Chen 	case REG_ASRIDRHC:
8463117bb31SNicolin Chen 	case REG_ASRIDRLC:
8473117bb31SNicolin Chen 	case REG_ASR76K:
8483117bb31SNicolin Chen 	case REG_ASR56K:
8493117bb31SNicolin Chen 	case REG_ASRMCRA:
8503117bb31SNicolin Chen 	case REG_ASRFSTA:
8513117bb31SNicolin Chen 	case REG_ASRMCRB:
8523117bb31SNicolin Chen 	case REG_ASRFSTB:
8533117bb31SNicolin Chen 	case REG_ASRMCRC:
8543117bb31SNicolin Chen 	case REG_ASRFSTC:
8553117bb31SNicolin Chen 	case REG_ASRMCR1A:
8563117bb31SNicolin Chen 	case REG_ASRMCR1B:
8573117bb31SNicolin Chen 	case REG_ASRMCR1C:
8583117bb31SNicolin Chen 		return true;
8593117bb31SNicolin Chen 	default:
8603117bb31SNicolin Chen 		return false;
8613117bb31SNicolin Chen 	}
8623117bb31SNicolin Chen }
8633117bb31SNicolin Chen 
8643117bb31SNicolin Chen static bool fsl_asrc_volatile_reg(struct device *dev, unsigned int reg)
8653117bb31SNicolin Chen {
8663117bb31SNicolin Chen 	switch (reg) {
8673117bb31SNicolin Chen 	case REG_ASRSTR:
8683117bb31SNicolin Chen 	case REG_ASRDIA:
8693117bb31SNicolin Chen 	case REG_ASRDIB:
8703117bb31SNicolin Chen 	case REG_ASRDIC:
8713117bb31SNicolin Chen 	case REG_ASRDOA:
8723117bb31SNicolin Chen 	case REG_ASRDOB:
8733117bb31SNicolin Chen 	case REG_ASRDOC:
8743117bb31SNicolin Chen 	case REG_ASRFSTA:
8753117bb31SNicolin Chen 	case REG_ASRFSTB:
8763117bb31SNicolin Chen 	case REG_ASRFSTC:
8773117bb31SNicolin Chen 	case REG_ASRCFG:
8783117bb31SNicolin Chen 		return true;
8793117bb31SNicolin Chen 	default:
8803117bb31SNicolin Chen 		return false;
8813117bb31SNicolin Chen 	}
8823117bb31SNicolin Chen }
8833117bb31SNicolin Chen 
8843117bb31SNicolin Chen static bool fsl_asrc_writeable_reg(struct device *dev, unsigned int reg)
8853117bb31SNicolin Chen {
8863117bb31SNicolin Chen 	switch (reg) {
8873117bb31SNicolin Chen 	case REG_ASRCTR:
8883117bb31SNicolin Chen 	case REG_ASRIER:
8893117bb31SNicolin Chen 	case REG_ASRCNCR:
8903117bb31SNicolin Chen 	case REG_ASRCFG:
8913117bb31SNicolin Chen 	case REG_ASRCSR:
8923117bb31SNicolin Chen 	case REG_ASRCDR1:
8933117bb31SNicolin Chen 	case REG_ASRCDR2:
8943117bb31SNicolin Chen 	case REG_ASRSTR:
8953117bb31SNicolin Chen 	case REG_ASRPM1:
8963117bb31SNicolin Chen 	case REG_ASRPM2:
8973117bb31SNicolin Chen 	case REG_ASRPM3:
8983117bb31SNicolin Chen 	case REG_ASRPM4:
8993117bb31SNicolin Chen 	case REG_ASRPM5:
9003117bb31SNicolin Chen 	case REG_ASRTFR1:
9013117bb31SNicolin Chen 	case REG_ASRCCR:
9023117bb31SNicolin Chen 	case REG_ASRDIA:
9033117bb31SNicolin Chen 	case REG_ASRDIB:
9043117bb31SNicolin Chen 	case REG_ASRDIC:
9053117bb31SNicolin Chen 	case REG_ASRIDRHA:
9063117bb31SNicolin Chen 	case REG_ASRIDRLA:
9073117bb31SNicolin Chen 	case REG_ASRIDRHB:
9083117bb31SNicolin Chen 	case REG_ASRIDRLB:
9093117bb31SNicolin Chen 	case REG_ASRIDRHC:
9103117bb31SNicolin Chen 	case REG_ASRIDRLC:
9113117bb31SNicolin Chen 	case REG_ASR76K:
9123117bb31SNicolin Chen 	case REG_ASR56K:
9133117bb31SNicolin Chen 	case REG_ASRMCRA:
9143117bb31SNicolin Chen 	case REG_ASRMCRB:
9153117bb31SNicolin Chen 	case REG_ASRMCRC:
9163117bb31SNicolin Chen 	case REG_ASRMCR1A:
9173117bb31SNicolin Chen 	case REG_ASRMCR1B:
9183117bb31SNicolin Chen 	case REG_ASRMCR1C:
9193117bb31SNicolin Chen 		return true;
9203117bb31SNicolin Chen 	default:
9213117bb31SNicolin Chen 		return false;
9223117bb31SNicolin Chen 	}
9233117bb31SNicolin Chen }
9243117bb31SNicolin Chen 
92586a570c5SNicolin Chen static struct reg_default fsl_asrc_reg[] = {
92686a570c5SNicolin Chen 	{ REG_ASRCTR, 0x0000 }, { REG_ASRIER, 0x0000 },
92786a570c5SNicolin Chen 	{ REG_ASRCNCR, 0x0000 }, { REG_ASRCFG, 0x0000 },
92886a570c5SNicolin Chen 	{ REG_ASRCSR, 0x0000 }, { REG_ASRCDR1, 0x0000 },
92986a570c5SNicolin Chen 	{ REG_ASRCDR2, 0x0000 }, { REG_ASRSTR, 0x0000 },
93086a570c5SNicolin Chen 	{ REG_ASRRA, 0x0000 }, { REG_ASRRB, 0x0000 },
93186a570c5SNicolin Chen 	{ REG_ASRRC, 0x0000 }, { REG_ASRPM1, 0x0000 },
93286a570c5SNicolin Chen 	{ REG_ASRPM2, 0x0000 }, { REG_ASRPM3, 0x0000 },
93386a570c5SNicolin Chen 	{ REG_ASRPM4, 0x0000 }, { REG_ASRPM5, 0x0000 },
93486a570c5SNicolin Chen 	{ REG_ASRTFR1, 0x0000 }, { REG_ASRCCR, 0x0000 },
93586a570c5SNicolin Chen 	{ REG_ASRDIA, 0x0000 }, { REG_ASRDOA, 0x0000 },
93686a570c5SNicolin Chen 	{ REG_ASRDIB, 0x0000 }, { REG_ASRDOB, 0x0000 },
93786a570c5SNicolin Chen 	{ REG_ASRDIC, 0x0000 }, { REG_ASRDOC, 0x0000 },
93886a570c5SNicolin Chen 	{ REG_ASRIDRHA, 0x0000 }, { REG_ASRIDRLA, 0x0000 },
93986a570c5SNicolin Chen 	{ REG_ASRIDRHB, 0x0000 }, { REG_ASRIDRLB, 0x0000 },
94086a570c5SNicolin Chen 	{ REG_ASRIDRHC, 0x0000 }, { REG_ASRIDRLC, 0x0000 },
94186a570c5SNicolin Chen 	{ REG_ASR76K, 0x0A47 }, { REG_ASR56K, 0x0DF3 },
94286a570c5SNicolin Chen 	{ REG_ASRMCRA, 0x0000 }, { REG_ASRFSTA, 0x0000 },
94386a570c5SNicolin Chen 	{ REG_ASRMCRB, 0x0000 }, { REG_ASRFSTB, 0x0000 },
94486a570c5SNicolin Chen 	{ REG_ASRMCRC, 0x0000 }, { REG_ASRFSTC, 0x0000 },
94586a570c5SNicolin Chen 	{ REG_ASRMCR1A, 0x0000 }, { REG_ASRMCR1B, 0x0000 },
94686a570c5SNicolin Chen 	{ REG_ASRMCR1C, 0x0000 },
94786a570c5SNicolin Chen };
94886a570c5SNicolin Chen 
949bf16d883SXiubo Li static const struct regmap_config fsl_asrc_regmap_config = {
9503117bb31SNicolin Chen 	.reg_bits = 32,
9513117bb31SNicolin Chen 	.reg_stride = 4,
9523117bb31SNicolin Chen 	.val_bits = 32,
9533117bb31SNicolin Chen 
9543117bb31SNicolin Chen 	.max_register = REG_ASRMCR1C,
95586a570c5SNicolin Chen 	.reg_defaults = fsl_asrc_reg,
95686a570c5SNicolin Chen 	.num_reg_defaults = ARRAY_SIZE(fsl_asrc_reg),
9573117bb31SNicolin Chen 	.readable_reg = fsl_asrc_readable_reg,
9583117bb31SNicolin Chen 	.volatile_reg = fsl_asrc_volatile_reg,
9593117bb31SNicolin Chen 	.writeable_reg = fsl_asrc_writeable_reg,
960b4138868SMarek Vasut 	.cache_type = REGCACHE_FLAT,
9613117bb31SNicolin Chen };
9623117bb31SNicolin Chen 
9633117bb31SNicolin Chen /**
96445e039d9SPierre-Louis Bossart  * fsl_asrc_init - Initialize ASRC registers with a default configuration
96545e039d9SPierre-Louis Bossart  * @asrc: ASRC context
9663117bb31SNicolin Chen  */
9677470704dSShengjiu Wang static int fsl_asrc_init(struct fsl_asrc *asrc)
9683117bb31SNicolin Chen {
969f8953043SShengjiu Wang 	unsigned long ipg_rate;
970f8953043SShengjiu Wang 
9713117bb31SNicolin Chen 	/* Halt ASRC internal FP when input FIFO needs data for pair A, B, C */
9727470704dSShengjiu Wang 	regmap_write(asrc->regmap, REG_ASRCTR, ASRCTR_ASRCEN);
9733117bb31SNicolin Chen 
9743117bb31SNicolin Chen 	/* Disable interrupt by default */
9757470704dSShengjiu Wang 	regmap_write(asrc->regmap, REG_ASRIER, 0x0);
9763117bb31SNicolin Chen 
9773117bb31SNicolin Chen 	/* Apply recommended settings for parameters from Reference Manual */
9787470704dSShengjiu Wang 	regmap_write(asrc->regmap, REG_ASRPM1, 0x7fffff);
9797470704dSShengjiu Wang 	regmap_write(asrc->regmap, REG_ASRPM2, 0x255555);
9807470704dSShengjiu Wang 	regmap_write(asrc->regmap, REG_ASRPM3, 0xff7280);
9817470704dSShengjiu Wang 	regmap_write(asrc->regmap, REG_ASRPM4, 0xff7280);
9827470704dSShengjiu Wang 	regmap_write(asrc->regmap, REG_ASRPM5, 0xff7280);
9833117bb31SNicolin Chen 
9843117bb31SNicolin Chen 	/* Base address for task queue FIFO. Set to 0x7C */
9857470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRTFR1,
9863117bb31SNicolin Chen 			   ASRTFR1_TF_BASE_MASK, ASRTFR1_TF_BASE(0xfc));
9873117bb31SNicolin Chen 
988f8953043SShengjiu Wang 	/*
989f8953043SShengjiu Wang 	 * Set the period of the 76KHz and 56KHz sampling clocks based on
990f8953043SShengjiu Wang 	 * the ASRC processing clock.
991f8953043SShengjiu Wang 	 * On iMX6, ipg_clk = 133MHz, REG_ASR76K = 0x06D6, REG_ASR56K = 0x0947
992f8953043SShengjiu Wang 	 */
993f8953043SShengjiu Wang 	ipg_rate = clk_get_rate(asrc->ipg_clk);
994f8953043SShengjiu Wang 	regmap_write(asrc->regmap, REG_ASR76K, ipg_rate / 76000);
995f8953043SShengjiu Wang 	return regmap_write(asrc->regmap, REG_ASR56K, ipg_rate / 56000);
9963117bb31SNicolin Chen }
9973117bb31SNicolin Chen 
9983117bb31SNicolin Chen /**
99945e039d9SPierre-Louis Bossart  * fsl_asrc_isr- Interrupt handler for ASRC
100045e039d9SPierre-Louis Bossart  * @irq: irq number
100145e039d9SPierre-Louis Bossart  * @dev_id: ASRC context
10023117bb31SNicolin Chen  */
10033117bb31SNicolin Chen static irqreturn_t fsl_asrc_isr(int irq, void *dev_id)
10043117bb31SNicolin Chen {
10057470704dSShengjiu Wang 	struct fsl_asrc *asrc = (struct fsl_asrc *)dev_id;
10067470704dSShengjiu Wang 	struct device *dev = &asrc->pdev->dev;
10073117bb31SNicolin Chen 	enum asrc_pair_index index;
10083117bb31SNicolin Chen 	u32 status;
10093117bb31SNicolin Chen 
10107470704dSShengjiu Wang 	regmap_read(asrc->regmap, REG_ASRSTR, &status);
10113117bb31SNicolin Chen 
10123117bb31SNicolin Chen 	/* Clean overload error */
10137470704dSShengjiu Wang 	regmap_write(asrc->regmap, REG_ASRSTR, ASRSTR_AOLE);
10143117bb31SNicolin Chen 
10153117bb31SNicolin Chen 	/*
10163117bb31SNicolin Chen 	 * We here use dev_dbg() for all exceptions because ASRC itself does
10173117bb31SNicolin Chen 	 * not care if FIFO overflowed or underrun while a warning in the
10183117bb31SNicolin Chen 	 * interrupt would result a ridged conversion.
10193117bb31SNicolin Chen 	 */
10203117bb31SNicolin Chen 	for (index = ASRC_PAIR_A; index < ASRC_PAIR_MAX_NUM; index++) {
10217470704dSShengjiu Wang 		if (!asrc->pair[index])
10223117bb31SNicolin Chen 			continue;
10233117bb31SNicolin Chen 
10243117bb31SNicolin Chen 		if (status & ASRSTR_ATQOL) {
10257470704dSShengjiu Wang 			asrc->pair[index]->error |= ASRC_TASK_Q_OVERLOAD;
10263117bb31SNicolin Chen 			dev_dbg(dev, "ASRC Task Queue FIFO overload\n");
10273117bb31SNicolin Chen 		}
10283117bb31SNicolin Chen 
10293117bb31SNicolin Chen 		if (status & ASRSTR_AOOL(index)) {
10307470704dSShengjiu Wang 			asrc->pair[index]->error |= ASRC_OUTPUT_TASK_OVERLOAD;
10313117bb31SNicolin Chen 			pair_dbg("Output Task Overload\n");
10323117bb31SNicolin Chen 		}
10333117bb31SNicolin Chen 
10343117bb31SNicolin Chen 		if (status & ASRSTR_AIOL(index)) {
10357470704dSShengjiu Wang 			asrc->pair[index]->error |= ASRC_INPUT_TASK_OVERLOAD;
10363117bb31SNicolin Chen 			pair_dbg("Input Task Overload\n");
10373117bb31SNicolin Chen 		}
10383117bb31SNicolin Chen 
10393117bb31SNicolin Chen 		if (status & ASRSTR_AODO(index)) {
10407470704dSShengjiu Wang 			asrc->pair[index]->error |= ASRC_OUTPUT_BUFFER_OVERFLOW;
10413117bb31SNicolin Chen 			pair_dbg("Output Data Buffer has overflowed\n");
10423117bb31SNicolin Chen 		}
10433117bb31SNicolin Chen 
10443117bb31SNicolin Chen 		if (status & ASRSTR_AIDU(index)) {
10457470704dSShengjiu Wang 			asrc->pair[index]->error |= ASRC_INPUT_BUFFER_UNDERRUN;
10463117bb31SNicolin Chen 			pair_dbg("Input Data Buffer has underflowed\n");
10473117bb31SNicolin Chen 		}
10483117bb31SNicolin Chen 	}
10493117bb31SNicolin Chen 
10503117bb31SNicolin Chen 	return IRQ_HANDLED;
10513117bb31SNicolin Chen }
10523117bb31SNicolin Chen 
1053be7bd03fSShengjiu Wang static int fsl_asrc_get_fifo_addr(u8 dir, enum asrc_pair_index index)
1054be7bd03fSShengjiu Wang {
1055be7bd03fSShengjiu Wang 	return REG_ASRDx(dir, index);
1056be7bd03fSShengjiu Wang }
1057be7bd03fSShengjiu Wang 
1058cab04ab5SShengjiu Wang static int fsl_asrc_runtime_resume(struct device *dev);
1059cab04ab5SShengjiu Wang static int fsl_asrc_runtime_suspend(struct device *dev);
1060cab04ab5SShengjiu Wang 
10613117bb31SNicolin Chen static int fsl_asrc_probe(struct platform_device *pdev)
10623117bb31SNicolin Chen {
10633117bb31SNicolin Chen 	struct device_node *np = pdev->dev.of_node;
1064be7bd03fSShengjiu Wang 	struct fsl_asrc_priv *asrc_priv;
10657470704dSShengjiu Wang 	struct fsl_asrc *asrc;
10663117bb31SNicolin Chen 	struct resource *res;
10673117bb31SNicolin Chen 	void __iomem *regs;
10683117bb31SNicolin Chen 	int irq, ret, i;
1069*c4993272SShengjiu Wang 	u32 asrc_fmt = 0;
1070c05f10f2SShengjiu Wang 	u32 map_idx;
10713117bb31SNicolin Chen 	char tmp[16];
10724520af41SShengjiu Wang 	u32 width;
10733117bb31SNicolin Chen 
10747470704dSShengjiu Wang 	asrc = devm_kzalloc(&pdev->dev, sizeof(*asrc), GFP_KERNEL);
10757470704dSShengjiu Wang 	if (!asrc)
10763117bb31SNicolin Chen 		return -ENOMEM;
10773117bb31SNicolin Chen 
1078be7bd03fSShengjiu Wang 	asrc_priv = devm_kzalloc(&pdev->dev, sizeof(*asrc_priv), GFP_KERNEL);
1079be7bd03fSShengjiu Wang 	if (!asrc_priv)
1080be7bd03fSShengjiu Wang 		return -ENOMEM;
1081be7bd03fSShengjiu Wang 
10827470704dSShengjiu Wang 	asrc->pdev = pdev;
1083be7bd03fSShengjiu Wang 	asrc->private = asrc_priv;
10843117bb31SNicolin Chen 
10853117bb31SNicolin Chen 	/* Get the addresses and IRQ */
1086c66d7621SYang Yingliang 	regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
10873117bb31SNicolin Chen 	if (IS_ERR(regs))
10883117bb31SNicolin Chen 		return PTR_ERR(regs);
10893117bb31SNicolin Chen 
10907470704dSShengjiu Wang 	asrc->paddr = res->start;
10913117bb31SNicolin Chen 
1092cab04ab5SShengjiu Wang 	asrc->regmap = devm_regmap_init_mmio(&pdev->dev, regs, &fsl_asrc_regmap_config);
10937470704dSShengjiu Wang 	if (IS_ERR(asrc->regmap)) {
10943117bb31SNicolin Chen 		dev_err(&pdev->dev, "failed to init regmap\n");
10957470704dSShengjiu Wang 		return PTR_ERR(asrc->regmap);
10963117bb31SNicolin Chen 	}
10973117bb31SNicolin Chen 
10983117bb31SNicolin Chen 	irq = platform_get_irq(pdev, 0);
1099cf9441adSStephen Boyd 	if (irq < 0)
11003117bb31SNicolin Chen 		return irq;
11013117bb31SNicolin Chen 
11023117bb31SNicolin Chen 	ret = devm_request_irq(&pdev->dev, irq, fsl_asrc_isr, 0,
11037470704dSShengjiu Wang 			       dev_name(&pdev->dev), asrc);
11043117bb31SNicolin Chen 	if (ret) {
11053117bb31SNicolin Chen 		dev_err(&pdev->dev, "failed to claim irq %u: %d\n", irq, ret);
11063117bb31SNicolin Chen 		return ret;
11073117bb31SNicolin Chen 	}
11083117bb31SNicolin Chen 
11097470704dSShengjiu Wang 	asrc->mem_clk = devm_clk_get(&pdev->dev, "mem");
11107470704dSShengjiu Wang 	if (IS_ERR(asrc->mem_clk)) {
11113117bb31SNicolin Chen 		dev_err(&pdev->dev, "failed to get mem clock\n");
11127470704dSShengjiu Wang 		return PTR_ERR(asrc->mem_clk);
11133117bb31SNicolin Chen 	}
11143117bb31SNicolin Chen 
11157470704dSShengjiu Wang 	asrc->ipg_clk = devm_clk_get(&pdev->dev, "ipg");
11167470704dSShengjiu Wang 	if (IS_ERR(asrc->ipg_clk)) {
11173117bb31SNicolin Chen 		dev_err(&pdev->dev, "failed to get ipg clock\n");
11187470704dSShengjiu Wang 		return PTR_ERR(asrc->ipg_clk);
11193117bb31SNicolin Chen 	}
11203117bb31SNicolin Chen 
11217470704dSShengjiu Wang 	asrc->spba_clk = devm_clk_get(&pdev->dev, "spba");
11227470704dSShengjiu Wang 	if (IS_ERR(asrc->spba_clk))
112313b8a97aSShengjiu Wang 		dev_warn(&pdev->dev, "failed to get spba clock\n");
112413b8a97aSShengjiu Wang 
11253117bb31SNicolin Chen 	for (i = 0; i < ASRC_CLK_MAX_NUM; i++) {
11263117bb31SNicolin Chen 		sprintf(tmp, "asrck_%x", i);
1127be7bd03fSShengjiu Wang 		asrc_priv->asrck_clk[i] = devm_clk_get(&pdev->dev, tmp);
1128be7bd03fSShengjiu Wang 		if (IS_ERR(asrc_priv->asrck_clk[i])) {
11293117bb31SNicolin Chen 			dev_err(&pdev->dev, "failed to get %s clock\n", tmp);
1130be7bd03fSShengjiu Wang 			return PTR_ERR(asrc_priv->asrck_clk[i]);
11313117bb31SNicolin Chen 		}
11323117bb31SNicolin Chen 	}
11333117bb31SNicolin Chen 
1134be7bd03fSShengjiu Wang 	asrc_priv->soc = of_device_get_match_data(&pdev->dev);
1135be7bd03fSShengjiu Wang 	asrc->use_edma = asrc_priv->soc->use_edma;
1136be7bd03fSShengjiu Wang 	asrc->get_dma_channel = fsl_asrc_get_dma_channel;
1137be7bd03fSShengjiu Wang 	asrc->request_pair = fsl_asrc_request_pair;
1138be7bd03fSShengjiu Wang 	asrc->release_pair = fsl_asrc_release_pair;
1139be7bd03fSShengjiu Wang 	asrc->get_fifo_addr = fsl_asrc_get_fifo_addr;
1140be7bd03fSShengjiu Wang 	asrc->pair_priv_size = sizeof(struct fsl_asrc_pair_priv);
1141be7bd03fSShengjiu Wang 
1142f3d8ac8cSFabio Estevam 	if (of_device_is_compatible(np, "fsl,imx35-asrc")) {
1143be7bd03fSShengjiu Wang 		asrc_priv->clk_map[IN] = input_clk_map_imx35;
1144be7bd03fSShengjiu Wang 		asrc_priv->clk_map[OUT] = output_clk_map_imx35;
1145c05f10f2SShengjiu Wang 	} else if (of_device_is_compatible(np, "fsl,imx53-asrc")) {
1146be7bd03fSShengjiu Wang 		asrc_priv->clk_map[IN] = input_clk_map_imx53;
1147be7bd03fSShengjiu Wang 		asrc_priv->clk_map[OUT] = output_clk_map_imx53;
1148c05f10f2SShengjiu Wang 	} else if (of_device_is_compatible(np, "fsl,imx8qm-asrc") ||
1149c05f10f2SShengjiu Wang 		   of_device_is_compatible(np, "fsl,imx8qxp-asrc")) {
1150c05f10f2SShengjiu Wang 		ret = of_property_read_u32(np, "fsl,asrc-clk-map", &map_idx);
1151c05f10f2SShengjiu Wang 		if (ret) {
1152c05f10f2SShengjiu Wang 			dev_err(&pdev->dev, "failed to get clk map index\n");
1153c05f10f2SShengjiu Wang 			return ret;
1154c05f10f2SShengjiu Wang 		}
1155c05f10f2SShengjiu Wang 
1156c05f10f2SShengjiu Wang 		if (map_idx > 1) {
1157c05f10f2SShengjiu Wang 			dev_err(&pdev->dev, "unsupported clk map index\n");
1158c05f10f2SShengjiu Wang 			return -EINVAL;
1159c05f10f2SShengjiu Wang 		}
1160c05f10f2SShengjiu Wang 		if (of_device_is_compatible(np, "fsl,imx8qm-asrc")) {
1161be7bd03fSShengjiu Wang 			asrc_priv->clk_map[IN] = clk_map_imx8qm[map_idx];
1162be7bd03fSShengjiu Wang 			asrc_priv->clk_map[OUT] = clk_map_imx8qm[map_idx];
11633117bb31SNicolin Chen 		} else {
1164be7bd03fSShengjiu Wang 			asrc_priv->clk_map[IN] = clk_map_imx8qxp[map_idx];
1165be7bd03fSShengjiu Wang 			asrc_priv->clk_map[OUT] = clk_map_imx8qxp[map_idx];
1166c05f10f2SShengjiu Wang 		}
11673117bb31SNicolin Chen 	}
11683117bb31SNicolin Chen 
11697470704dSShengjiu Wang 	asrc->channel_avail = 10;
11703117bb31SNicolin Chen 
11713117bb31SNicolin Chen 	ret = of_property_read_u32(np, "fsl,asrc-rate",
11727470704dSShengjiu Wang 				   &asrc->asrc_rate);
11733117bb31SNicolin Chen 	if (ret) {
11743117bb31SNicolin Chen 		dev_err(&pdev->dev, "failed to get output rate\n");
1175c0296950SFabio Estevam 		return ret;
11763117bb31SNicolin Chen 	}
11773117bb31SNicolin Chen 
1178*c4993272SShengjiu Wang 	ret = of_property_read_u32(np, "fsl,asrc-format", &asrc_fmt);
1179*c4993272SShengjiu Wang 	asrc->asrc_format = (__force snd_pcm_format_t)asrc_fmt;
11803117bb31SNicolin Chen 	if (ret) {
11814520af41SShengjiu Wang 		ret = of_property_read_u32(np, "fsl,asrc-width", &width);
11824520af41SShengjiu Wang 		if (ret) {
11834520af41SShengjiu Wang 			dev_err(&pdev->dev, "failed to decide output format\n");
1184c0296950SFabio Estevam 			return ret;
11853117bb31SNicolin Chen 		}
11863117bb31SNicolin Chen 
11874520af41SShengjiu Wang 		switch (width) {
11884520af41SShengjiu Wang 		case 16:
11894520af41SShengjiu Wang 			asrc->asrc_format = SNDRV_PCM_FORMAT_S16_LE;
11904520af41SShengjiu Wang 			break;
11914520af41SShengjiu Wang 		case 24:
11924520af41SShengjiu Wang 			asrc->asrc_format = SNDRV_PCM_FORMAT_S24_LE;
11934520af41SShengjiu Wang 			break;
11944520af41SShengjiu Wang 		default:
11954520af41SShengjiu Wang 			dev_warn(&pdev->dev,
11964520af41SShengjiu Wang 				 "unsupported width, use default S24_LE\n");
11974520af41SShengjiu Wang 			asrc->asrc_format = SNDRV_PCM_FORMAT_S24_LE;
11984520af41SShengjiu Wang 			break;
11994520af41SShengjiu Wang 		}
12004520af41SShengjiu Wang 	}
12014520af41SShengjiu Wang 
1202*c4993272SShengjiu Wang 	if (!(FSL_ASRC_FORMATS & pcm_format_to_bits(asrc->asrc_format))) {
12034520af41SShengjiu Wang 		dev_warn(&pdev->dev, "unsupported width, use default S24_LE\n");
12044520af41SShengjiu Wang 		asrc->asrc_format = SNDRV_PCM_FORMAT_S24_LE;
12053117bb31SNicolin Chen 	}
12063117bb31SNicolin Chen 
12077470704dSShengjiu Wang 	platform_set_drvdata(pdev, asrc);
12087470704dSShengjiu Wang 	spin_lock_init(&asrc->lock);
1209cab04ab5SShengjiu Wang 	pm_runtime_enable(&pdev->dev);
1210cab04ab5SShengjiu Wang 	if (!pm_runtime_enabled(&pdev->dev)) {
1211cab04ab5SShengjiu Wang 		ret = fsl_asrc_runtime_resume(&pdev->dev);
1212cab04ab5SShengjiu Wang 		if (ret)
1213cab04ab5SShengjiu Wang 			goto err_pm_disable;
1214cab04ab5SShengjiu Wang 	}
1215cab04ab5SShengjiu Wang 
1216d0504074SMinghao Chi 	ret = pm_runtime_resume_and_get(&pdev->dev);
1217d0504074SMinghao Chi 	if (ret < 0)
1218cab04ab5SShengjiu Wang 		goto err_pm_get_sync;
1219cab04ab5SShengjiu Wang 
1220cab04ab5SShengjiu Wang 	ret = fsl_asrc_init(asrc);
1221cab04ab5SShengjiu Wang 	if (ret) {
1222cab04ab5SShengjiu Wang 		dev_err(&pdev->dev, "failed to init asrc %d\n", ret);
1223cab04ab5SShengjiu Wang 		goto err_pm_get_sync;
1224cab04ab5SShengjiu Wang 	}
1225cab04ab5SShengjiu Wang 
1226cab04ab5SShengjiu Wang 	ret = pm_runtime_put_sync(&pdev->dev);
1227cab04ab5SShengjiu Wang 	if (ret < 0)
1228cab04ab5SShengjiu Wang 		goto err_pm_get_sync;
12293117bb31SNicolin Chen 
12303117bb31SNicolin Chen 	ret = devm_snd_soc_register_component(&pdev->dev, &fsl_asrc_component,
12313117bb31SNicolin Chen 					      &fsl_asrc_dai, 1);
12323117bb31SNicolin Chen 	if (ret) {
12333117bb31SNicolin Chen 		dev_err(&pdev->dev, "failed to register ASoC DAI\n");
1234cab04ab5SShengjiu Wang 		goto err_pm_get_sync;
1235cab04ab5SShengjiu Wang 	}
1236cab04ab5SShengjiu Wang 
1237cab04ab5SShengjiu Wang 	return 0;
1238cab04ab5SShengjiu Wang 
1239cab04ab5SShengjiu Wang err_pm_get_sync:
1240cab04ab5SShengjiu Wang 	if (!pm_runtime_status_suspended(&pdev->dev))
1241cab04ab5SShengjiu Wang 		fsl_asrc_runtime_suspend(&pdev->dev);
1242cab04ab5SShengjiu Wang err_pm_disable:
1243cab04ab5SShengjiu Wang 	pm_runtime_disable(&pdev->dev);
12443117bb31SNicolin Chen 	return ret;
12453117bb31SNicolin Chen }
12463117bb31SNicolin Chen 
1247cab04ab5SShengjiu Wang static int fsl_asrc_remove(struct platform_device *pdev)
1248cab04ab5SShengjiu Wang {
1249cab04ab5SShengjiu Wang 	pm_runtime_disable(&pdev->dev);
1250cab04ab5SShengjiu Wang 	if (!pm_runtime_status_suspended(&pdev->dev))
1251cab04ab5SShengjiu Wang 		fsl_asrc_runtime_suspend(&pdev->dev);
1252cab04ab5SShengjiu Wang 
12533117bb31SNicolin Chen 	return 0;
12543117bb31SNicolin Chen }
12553117bb31SNicolin Chen 
12563117bb31SNicolin Chen static int fsl_asrc_runtime_resume(struct device *dev)
12573117bb31SNicolin Chen {
12587470704dSShengjiu Wang 	struct fsl_asrc *asrc = dev_get_drvdata(dev);
1259be7bd03fSShengjiu Wang 	struct fsl_asrc_priv *asrc_priv = asrc->private;
1260b1ade0f2SFabio Estevam 	int i, ret;
1261393dc21dSShengjiu Wang 	u32 asrctr;
12623117bb31SNicolin Chen 
12637470704dSShengjiu Wang 	ret = clk_prepare_enable(asrc->mem_clk);
1264b1ade0f2SFabio Estevam 	if (ret)
1265b1ade0f2SFabio Estevam 		return ret;
12667470704dSShengjiu Wang 	ret = clk_prepare_enable(asrc->ipg_clk);
1267b1ade0f2SFabio Estevam 	if (ret)
1268b1ade0f2SFabio Estevam 		goto disable_mem_clk;
12697470704dSShengjiu Wang 	if (!IS_ERR(asrc->spba_clk)) {
12707470704dSShengjiu Wang 		ret = clk_prepare_enable(asrc->spba_clk);
127113b8a97aSShengjiu Wang 		if (ret)
127213b8a97aSShengjiu Wang 			goto disable_ipg_clk;
127313b8a97aSShengjiu Wang 	}
1274b1ade0f2SFabio Estevam 	for (i = 0; i < ASRC_CLK_MAX_NUM; i++) {
1275be7bd03fSShengjiu Wang 		ret = clk_prepare_enable(asrc_priv->asrck_clk[i]);
1276b1ade0f2SFabio Estevam 		if (ret)
1277b1ade0f2SFabio Estevam 			goto disable_asrck_clk;
1278b1ade0f2SFabio Estevam 	}
12793117bb31SNicolin Chen 
1280393dc21dSShengjiu Wang 	/* Stop all pairs provisionally */
1281393dc21dSShengjiu Wang 	regmap_read(asrc->regmap, REG_ASRCTR, &asrctr);
1282393dc21dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCTR,
1283393dc21dSShengjiu Wang 			   ASRCTR_ASRCEi_ALL_MASK, 0);
1284393dc21dSShengjiu Wang 
1285393dc21dSShengjiu Wang 	/* Restore all registers */
1286393dc21dSShengjiu Wang 	regcache_cache_only(asrc->regmap, false);
1287393dc21dSShengjiu Wang 	regcache_mark_dirty(asrc->regmap);
1288393dc21dSShengjiu Wang 	regcache_sync(asrc->regmap);
1289393dc21dSShengjiu Wang 
1290393dc21dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCFG,
1291393dc21dSShengjiu Wang 			   ASRCFG_NDPRi_ALL_MASK | ASRCFG_POSTMODi_ALL_MASK |
1292393dc21dSShengjiu Wang 			   ASRCFG_PREMODi_ALL_MASK, asrc_priv->regcache_cfg);
1293393dc21dSShengjiu Wang 
1294393dc21dSShengjiu Wang 	/* Restart enabled pairs */
1295393dc21dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCTR,
1296393dc21dSShengjiu Wang 			   ASRCTR_ASRCEi_ALL_MASK, asrctr);
1297393dc21dSShengjiu Wang 
12983117bb31SNicolin Chen 	return 0;
1299b1ade0f2SFabio Estevam 
1300b1ade0f2SFabio Estevam disable_asrck_clk:
1301b1ade0f2SFabio Estevam 	for (i--; i >= 0; i--)
1302be7bd03fSShengjiu Wang 		clk_disable_unprepare(asrc_priv->asrck_clk[i]);
13037470704dSShengjiu Wang 	if (!IS_ERR(asrc->spba_clk))
13047470704dSShengjiu Wang 		clk_disable_unprepare(asrc->spba_clk);
130513b8a97aSShengjiu Wang disable_ipg_clk:
13067470704dSShengjiu Wang 	clk_disable_unprepare(asrc->ipg_clk);
1307b1ade0f2SFabio Estevam disable_mem_clk:
13087470704dSShengjiu Wang 	clk_disable_unprepare(asrc->mem_clk);
1309b1ade0f2SFabio Estevam 	return ret;
13103117bb31SNicolin Chen }
13113117bb31SNicolin Chen 
13123117bb31SNicolin Chen static int fsl_asrc_runtime_suspend(struct device *dev)
13133117bb31SNicolin Chen {
13147470704dSShengjiu Wang 	struct fsl_asrc *asrc = dev_get_drvdata(dev);
1315be7bd03fSShengjiu Wang 	struct fsl_asrc_priv *asrc_priv = asrc->private;
13163117bb31SNicolin Chen 	int i;
13173117bb31SNicolin Chen 
1318393dc21dSShengjiu Wang 	regmap_read(asrc->regmap, REG_ASRCFG,
1319393dc21dSShengjiu Wang 		    &asrc_priv->regcache_cfg);
1320393dc21dSShengjiu Wang 
1321393dc21dSShengjiu Wang 	regcache_cache_only(asrc->regmap, true);
1322393dc21dSShengjiu Wang 
13233117bb31SNicolin Chen 	for (i = 0; i < ASRC_CLK_MAX_NUM; i++)
1324be7bd03fSShengjiu Wang 		clk_disable_unprepare(asrc_priv->asrck_clk[i]);
13257470704dSShengjiu Wang 	if (!IS_ERR(asrc->spba_clk))
13267470704dSShengjiu Wang 		clk_disable_unprepare(asrc->spba_clk);
13277470704dSShengjiu Wang 	clk_disable_unprepare(asrc->ipg_clk);
13287470704dSShengjiu Wang 	clk_disable_unprepare(asrc->mem_clk);
13293117bb31SNicolin Chen 
13303117bb31SNicolin Chen 	return 0;
13313117bb31SNicolin Chen }
13323117bb31SNicolin Chen 
13333117bb31SNicolin Chen static const struct dev_pm_ops fsl_asrc_pm = {
13343117bb31SNicolin Chen 	SET_RUNTIME_PM_OPS(fsl_asrc_runtime_suspend, fsl_asrc_runtime_resume, NULL)
1335393dc21dSShengjiu Wang 	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
1336393dc21dSShengjiu Wang 				pm_runtime_force_resume)
13373117bb31SNicolin Chen };
13383117bb31SNicolin Chen 
1339c05f10f2SShengjiu Wang static const struct fsl_asrc_soc_data fsl_asrc_imx35_data = {
1340c05f10f2SShengjiu Wang 	.use_edma = false,
1341c05f10f2SShengjiu Wang 	.channel_bits = 3,
1342c05f10f2SShengjiu Wang };
1343c05f10f2SShengjiu Wang 
1344c05f10f2SShengjiu Wang static const struct fsl_asrc_soc_data fsl_asrc_imx53_data = {
1345c05f10f2SShengjiu Wang 	.use_edma = false,
1346c05f10f2SShengjiu Wang 	.channel_bits = 4,
1347c05f10f2SShengjiu Wang };
1348c05f10f2SShengjiu Wang 
1349c05f10f2SShengjiu Wang static const struct fsl_asrc_soc_data fsl_asrc_imx8qm_data = {
1350c05f10f2SShengjiu Wang 	.use_edma = true,
1351c05f10f2SShengjiu Wang 	.channel_bits = 4,
1352c05f10f2SShengjiu Wang };
1353c05f10f2SShengjiu Wang 
1354c05f10f2SShengjiu Wang static const struct fsl_asrc_soc_data fsl_asrc_imx8qxp_data = {
1355c05f10f2SShengjiu Wang 	.use_edma = true,
1356c05f10f2SShengjiu Wang 	.channel_bits = 4,
1357c05f10f2SShengjiu Wang };
1358c05f10f2SShengjiu Wang 
13593117bb31SNicolin Chen static const struct of_device_id fsl_asrc_ids[] = {
1360c05f10f2SShengjiu Wang 	{ .compatible = "fsl,imx35-asrc", .data = &fsl_asrc_imx35_data },
1361c05f10f2SShengjiu Wang 	{ .compatible = "fsl,imx53-asrc", .data = &fsl_asrc_imx53_data },
1362c05f10f2SShengjiu Wang 	{ .compatible = "fsl,imx8qm-asrc", .data = &fsl_asrc_imx8qm_data },
1363c05f10f2SShengjiu Wang 	{ .compatible = "fsl,imx8qxp-asrc", .data = &fsl_asrc_imx8qxp_data },
13643117bb31SNicolin Chen 	{}
13653117bb31SNicolin Chen };
13663117bb31SNicolin Chen MODULE_DEVICE_TABLE(of, fsl_asrc_ids);
13673117bb31SNicolin Chen 
13683117bb31SNicolin Chen static struct platform_driver fsl_asrc_driver = {
13693117bb31SNicolin Chen 	.probe = fsl_asrc_probe,
1370cab04ab5SShengjiu Wang 	.remove = fsl_asrc_remove,
13713117bb31SNicolin Chen 	.driver = {
13723117bb31SNicolin Chen 		.name = "fsl-asrc",
13733117bb31SNicolin Chen 		.of_match_table = fsl_asrc_ids,
13743117bb31SNicolin Chen 		.pm = &fsl_asrc_pm,
13753117bb31SNicolin Chen 	},
13763117bb31SNicolin Chen };
13773117bb31SNicolin Chen module_platform_driver(fsl_asrc_driver);
13783117bb31SNicolin Chen 
13793117bb31SNicolin Chen MODULE_DESCRIPTION("Freescale ASRC ASoC driver");
13803117bb31SNicolin Chen MODULE_AUTHOR("Nicolin Chen <nicoleotsuka@gmail.com>");
13813117bb31SNicolin Chen MODULE_ALIAS("platform:fsl-asrc");
13823117bb31SNicolin Chen MODULE_LICENSE("GPL v2");
1383