xref: /openbmc/linux/sound/soc/fsl/fsl_asrc.c (revision ac27ca16)
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
23d2de3f5eSShengjiu Wang #define INIT_RETRY_NUM 50
243117bb31SNicolin Chen 
253117bb31SNicolin Chen #define pair_err(fmt, ...) \
267470704dSShengjiu Wang 	dev_err(&asrc->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__)
273117bb31SNicolin Chen 
283117bb31SNicolin Chen #define pair_dbg(fmt, ...) \
297470704dSShengjiu Wang 	dev_dbg(&asrc->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__)
303117bb31SNicolin Chen 
31d2de3f5eSShengjiu Wang #define pair_warn(fmt, ...) \
32d2de3f5eSShengjiu Wang 	dev_warn(&asrc->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__)
33d2de3f5eSShengjiu Wang 
343117bb31SNicolin Chen /* Corresponding to process_option */
35d281bf5dSS.j. Wang static unsigned int supported_asrc_rate[] = {
36d281bf5dSS.j. Wang 	5512, 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000,
37d281bf5dSS.j. Wang 	64000, 88200, 96000, 128000, 176400, 192000,
383117bb31SNicolin Chen };
393117bb31SNicolin Chen 
40d281bf5dSS.j. Wang static struct snd_pcm_hw_constraint_list fsl_asrc_rate_constraints = {
41d281bf5dSS.j. Wang 	.count = ARRAY_SIZE(supported_asrc_rate),
42d281bf5dSS.j. Wang 	.list = supported_asrc_rate,
433117bb31SNicolin Chen };
443117bb31SNicolin Chen 
4545e039d9SPierre-Louis Bossart /*
463117bb31SNicolin Chen  * The following tables map the relationship between asrc_inclk/asrc_outclk in
473117bb31SNicolin Chen  * fsl_asrc.h and the registers of ASRCSR
483117bb31SNicolin Chen  */
49c05f10f2SShengjiu Wang static unsigned char input_clk_map_imx35[ASRC_CLK_MAP_LEN] = {
503117bb31SNicolin Chen 	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
51c05f10f2SShengjiu Wang 	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
52c05f10f2SShengjiu Wang 	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
533117bb31SNicolin Chen };
543117bb31SNicolin Chen 
55c05f10f2SShengjiu Wang static unsigned char output_clk_map_imx35[ASRC_CLK_MAP_LEN] = {
563117bb31SNicolin Chen 	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
57c05f10f2SShengjiu Wang 	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
58c05f10f2SShengjiu Wang 	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
593117bb31SNicolin Chen };
603117bb31SNicolin Chen 
613117bb31SNicolin Chen /* i.MX53 uses the same map for input and output */
62c05f10f2SShengjiu Wang static unsigned char input_clk_map_imx53[ASRC_CLK_MAP_LEN] = {
633117bb31SNicolin Chen /*	0x0  0x1  0x2  0x3  0x4  0x5  0x6  0x7  0x8  0x9  0xa  0xb  0xc  0xd  0xe  0xf */
643117bb31SNicolin Chen 	0x0, 0x1, 0x2, 0x7, 0x4, 0x5, 0x6, 0x3, 0x8, 0x9, 0xa, 0xb, 0xc, 0xf, 0xe, 0xd,
65c05f10f2SShengjiu Wang 	0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
66c05f10f2SShengjiu Wang 	0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
673117bb31SNicolin Chen };
683117bb31SNicolin Chen 
69c05f10f2SShengjiu Wang static unsigned char output_clk_map_imx53[ASRC_CLK_MAP_LEN] = {
703117bb31SNicolin Chen /*	0x0  0x1  0x2  0x3  0x4  0x5  0x6  0x7  0x8  0x9  0xa  0xb  0xc  0xd  0xe  0xf */
713117bb31SNicolin Chen 	0x8, 0x9, 0xa, 0x7, 0xc, 0x5, 0x6, 0xb, 0x0, 0x1, 0x2, 0x3, 0x4, 0xf, 0xe, 0xd,
72c05f10f2SShengjiu Wang 	0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
73c05f10f2SShengjiu Wang 	0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
743117bb31SNicolin Chen };
753117bb31SNicolin Chen 
7645e039d9SPierre-Louis Bossart /*
77c05f10f2SShengjiu Wang  * i.MX8QM/i.MX8QXP uses the same map for input and output.
78c05f10f2SShengjiu Wang  * clk_map_imx8qm[0] is for i.MX8QM asrc0
79c05f10f2SShengjiu Wang  * clk_map_imx8qm[1] is for i.MX8QM asrc1
80c05f10f2SShengjiu Wang  * clk_map_imx8qxp[0] is for i.MX8QXP asrc0
81c05f10f2SShengjiu Wang  * clk_map_imx8qxp[1] is for i.MX8QXP asrc1
82c05f10f2SShengjiu Wang  */
83c05f10f2SShengjiu Wang static unsigned char clk_map_imx8qm[2][ASRC_CLK_MAP_LEN] = {
84c05f10f2SShengjiu Wang 	{
85c05f10f2SShengjiu Wang 	0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x0,
86c05f10f2SShengjiu Wang 	0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
87c05f10f2SShengjiu Wang 	0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
88c05f10f2SShengjiu Wang 	},
89c05f10f2SShengjiu Wang 	{
90c05f10f2SShengjiu Wang 	0xf, 0xf, 0xf, 0xf, 0xf, 0x7, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x0,
91c05f10f2SShengjiu Wang 	0x0, 0x1, 0x2, 0x3, 0xb, 0xc, 0xf, 0xf, 0xd, 0xe, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
92c05f10f2SShengjiu Wang 	0x4, 0x5, 0x6, 0xf, 0x8, 0x9, 0xa, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
93c05f10f2SShengjiu Wang 	},
94c05f10f2SShengjiu Wang };
95c05f10f2SShengjiu Wang 
96c05f10f2SShengjiu Wang static unsigned char clk_map_imx8qxp[2][ASRC_CLK_MAP_LEN] = {
97c05f10f2SShengjiu Wang 	{
98c05f10f2SShengjiu Wang 	0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x0,
99c05f10f2SShengjiu Wang 	0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0xf, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xf, 0xf,
100c05f10f2SShengjiu Wang 	0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
101c05f10f2SShengjiu Wang 	},
102c05f10f2SShengjiu Wang 	{
103c05f10f2SShengjiu Wang 	0xf, 0xf, 0xf, 0xf, 0xf, 0x7, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x0,
104c05f10f2SShengjiu Wang 	0x0, 0x1, 0x2, 0x3, 0x7, 0x8, 0xf, 0xf, 0x9, 0xa, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
105c05f10f2SShengjiu Wang 	0xf, 0xf, 0x6, 0xf, 0xf, 0xf, 0xa, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
106c05f10f2SShengjiu Wang 	},
107c05f10f2SShengjiu Wang };
1083117bb31SNicolin Chen 
10932038634SShengjiu Wang /*
11032038634SShengjiu Wang  * According to RM, the divider range is 1 ~ 8,
11132038634SShengjiu Wang  * prescaler is power of 2 from 1 ~ 128.
11232038634SShengjiu Wang  */
11332038634SShengjiu Wang static int asrc_clk_divider[DIVIDER_NUM] = {
11432038634SShengjiu Wang 	1,  2,  4,  8,  16,  32,  64,  128,  /* divider = 1 */
11532038634SShengjiu Wang 	2,  4,  8, 16,  32,  64, 128,  256,  /* divider = 2 */
11632038634SShengjiu Wang 	3,  6, 12, 24,  48,  96, 192,  384,  /* divider = 3 */
11732038634SShengjiu Wang 	4,  8, 16, 32,  64, 128, 256,  512,  /* divider = 4 */
11832038634SShengjiu Wang 	5, 10, 20, 40,  80, 160, 320,  640,  /* divider = 5 */
11932038634SShengjiu Wang 	6, 12, 24, 48,  96, 192, 384,  768,  /* divider = 6 */
12032038634SShengjiu Wang 	7, 14, 28, 56, 112, 224, 448,  896,  /* divider = 7 */
12132038634SShengjiu Wang 	8, 16, 32, 64, 128, 256, 512, 1024,  /* divider = 8 */
12232038634SShengjiu Wang };
12332038634SShengjiu Wang 
12432038634SShengjiu Wang /*
12532038634SShengjiu Wang  * Check if the divider is available for internal ratio mode
12632038634SShengjiu Wang  */
fsl_asrc_divider_avail(int clk_rate,int rate,int * div)12732038634SShengjiu Wang static bool fsl_asrc_divider_avail(int clk_rate, int rate, int *div)
12832038634SShengjiu Wang {
12932038634SShengjiu Wang 	u32 rem, i;
13032038634SShengjiu Wang 	u64 n;
13132038634SShengjiu Wang 
13232038634SShengjiu Wang 	if (div)
13332038634SShengjiu Wang 		*div = 0;
13432038634SShengjiu Wang 
13532038634SShengjiu Wang 	if (clk_rate == 0 || rate == 0)
13632038634SShengjiu Wang 		return false;
13732038634SShengjiu Wang 
13832038634SShengjiu Wang 	n = clk_rate;
13932038634SShengjiu Wang 	rem = do_div(n, rate);
14032038634SShengjiu Wang 
14132038634SShengjiu Wang 	if (div)
14232038634SShengjiu Wang 		*div = n;
14332038634SShengjiu Wang 
14432038634SShengjiu Wang 	if (rem != 0)
14532038634SShengjiu Wang 		return false;
14632038634SShengjiu Wang 
14732038634SShengjiu Wang 	for (i = 0; i < DIVIDER_NUM; i++) {
14832038634SShengjiu Wang 		if (n == asrc_clk_divider[i])
14932038634SShengjiu Wang 			break;
15032038634SShengjiu Wang 	}
15132038634SShengjiu Wang 
15232038634SShengjiu Wang 	if (i == DIVIDER_NUM)
15332038634SShengjiu Wang 		return false;
15432038634SShengjiu Wang 
15532038634SShengjiu Wang 	return true;
15632038634SShengjiu Wang }
15732038634SShengjiu Wang 
1583117bb31SNicolin Chen /**
15945e039d9SPierre-Louis Bossart  * fsl_asrc_sel_proc - Select the pre-processing and post-processing options
16045e039d9SPierre-Louis Bossart  * @inrate: input sample rate
16145e039d9SPierre-Louis Bossart  * @outrate: output sample rate
16245e039d9SPierre-Louis Bossart  * @pre_proc: return value for pre-processing option
16345e039d9SPierre-Louis Bossart  * @post_proc: return value for post-processing option
16445e039d9SPierre-Louis Bossart  *
1654aecaa0aSS.j. Wang  * Make sure to exclude following unsupported cases before
1664aecaa0aSS.j. Wang  * calling this function:
1674aecaa0aSS.j. Wang  * 1) inrate > 8.125 * outrate
1684aecaa0aSS.j. Wang  * 2) inrate > 16.125 * outrate
1694aecaa0aSS.j. Wang  *
1704aecaa0aSS.j. Wang  */
fsl_asrc_sel_proc(int inrate,int outrate,int * pre_proc,int * post_proc)1714aecaa0aSS.j. Wang static void fsl_asrc_sel_proc(int inrate, int outrate,
1724aecaa0aSS.j. Wang 			     int *pre_proc, int *post_proc)
1734aecaa0aSS.j. Wang {
1744aecaa0aSS.j. Wang 	bool post_proc_cond2;
1754aecaa0aSS.j. Wang 	bool post_proc_cond0;
1764aecaa0aSS.j. Wang 
1774aecaa0aSS.j. Wang 	/* select pre_proc between [0, 2] */
1784aecaa0aSS.j. Wang 	if (inrate * 8 > 33 * outrate)
1794aecaa0aSS.j. Wang 		*pre_proc = 2;
1804aecaa0aSS.j. Wang 	else if (inrate * 8 > 15 * outrate) {
1814aecaa0aSS.j. Wang 		if (inrate > 152000)
1824aecaa0aSS.j. Wang 			*pre_proc = 2;
1834aecaa0aSS.j. Wang 		else
1844aecaa0aSS.j. Wang 			*pre_proc = 1;
1854aecaa0aSS.j. Wang 	} else if (inrate < 76000)
1864aecaa0aSS.j. Wang 		*pre_proc = 0;
1874aecaa0aSS.j. Wang 	else if (inrate > 152000)
1884aecaa0aSS.j. Wang 		*pre_proc = 2;
1894aecaa0aSS.j. Wang 	else
1904aecaa0aSS.j. Wang 		*pre_proc = 1;
1914aecaa0aSS.j. Wang 
1924aecaa0aSS.j. Wang 	/* Condition for selection of post-processing */
1934aecaa0aSS.j. Wang 	post_proc_cond2 = (inrate * 15 > outrate * 16 && outrate < 56000) ||
1944aecaa0aSS.j. Wang 			  (inrate > 56000 && outrate < 56000);
1954aecaa0aSS.j. Wang 	post_proc_cond0 = inrate * 23 < outrate * 8;
1964aecaa0aSS.j. Wang 
1974aecaa0aSS.j. Wang 	if (post_proc_cond2)
1984aecaa0aSS.j. Wang 		*post_proc = 2;
1994aecaa0aSS.j. Wang 	else if (post_proc_cond0)
2004aecaa0aSS.j. Wang 		*post_proc = 0;
2014aecaa0aSS.j. Wang 	else
2024aecaa0aSS.j. Wang 		*post_proc = 1;
2034aecaa0aSS.j. Wang }
2044aecaa0aSS.j. Wang 
2054aecaa0aSS.j. Wang /**
20645e039d9SPierre-Louis Bossart  * fsl_asrc_request_pair - Request ASRC pair
20745e039d9SPierre-Louis Bossart  * @channels: number of channels
20845e039d9SPierre-Louis Bossart  * @pair: pointer to pair
2093117bb31SNicolin Chen  *
2103117bb31SNicolin Chen  * It assigns pair by the order of A->C->B because allocation of pair B,
2113117bb31SNicolin Chen  * within range [ANCA, ANCA+ANCB-1], depends on the channels of pair A
2123117bb31SNicolin Chen  * while pair A and pair C are comparatively independent.
2133117bb31SNicolin Chen  */
fsl_asrc_request_pair(int channels,struct fsl_asrc_pair * pair)214c16e923dSChenTao static int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair)
2153117bb31SNicolin Chen {
2163117bb31SNicolin Chen 	enum asrc_pair_index index = ASRC_INVALID_PAIR;
2177470704dSShengjiu Wang 	struct fsl_asrc *asrc = pair->asrc;
2187470704dSShengjiu Wang 	struct device *dev = &asrc->pdev->dev;
2193117bb31SNicolin Chen 	unsigned long lock_flags;
2203117bb31SNicolin Chen 	int i, ret = 0;
2213117bb31SNicolin Chen 
2227470704dSShengjiu Wang 	spin_lock_irqsave(&asrc->lock, lock_flags);
2233117bb31SNicolin Chen 
2243117bb31SNicolin Chen 	for (i = ASRC_PAIR_A; i < ASRC_PAIR_MAX_NUM; i++) {
2257470704dSShengjiu Wang 		if (asrc->pair[i] != NULL)
2263117bb31SNicolin Chen 			continue;
2273117bb31SNicolin Chen 
2283117bb31SNicolin Chen 		index = i;
2293117bb31SNicolin Chen 
2303117bb31SNicolin Chen 		if (i != ASRC_PAIR_B)
2313117bb31SNicolin Chen 			break;
2323117bb31SNicolin Chen 	}
2333117bb31SNicolin Chen 
2343117bb31SNicolin Chen 	if (index == ASRC_INVALID_PAIR) {
2353117bb31SNicolin Chen 		dev_err(dev, "all pairs are busy now\n");
2363117bb31SNicolin Chen 		ret = -EBUSY;
2377470704dSShengjiu Wang 	} else if (asrc->channel_avail < channels) {
2383117bb31SNicolin Chen 		dev_err(dev, "can't afford required channels: %d\n", channels);
2393117bb31SNicolin Chen 		ret = -EINVAL;
2403117bb31SNicolin Chen 	} else {
2417470704dSShengjiu Wang 		asrc->channel_avail -= channels;
2427470704dSShengjiu Wang 		asrc->pair[index] = pair;
2433117bb31SNicolin Chen 		pair->channels = channels;
2443117bb31SNicolin Chen 		pair->index = index;
2453117bb31SNicolin Chen 	}
2463117bb31SNicolin Chen 
2477470704dSShengjiu Wang 	spin_unlock_irqrestore(&asrc->lock, lock_flags);
2483117bb31SNicolin Chen 
2493117bb31SNicolin Chen 	return ret;
2503117bb31SNicolin Chen }
2513117bb31SNicolin Chen 
2523117bb31SNicolin Chen /**
25345e039d9SPierre-Louis Bossart  * fsl_asrc_release_pair - Release ASRC pair
25445e039d9SPierre-Louis Bossart  * @pair: pair to release
2553117bb31SNicolin Chen  *
2567470704dSShengjiu Wang  * It clears the resource from asrc and releases the occupied channels.
2573117bb31SNicolin Chen  */
fsl_asrc_release_pair(struct fsl_asrc_pair * pair)258c16e923dSChenTao static void fsl_asrc_release_pair(struct fsl_asrc_pair *pair)
2593117bb31SNicolin Chen {
2607470704dSShengjiu Wang 	struct fsl_asrc *asrc = pair->asrc;
2613117bb31SNicolin Chen 	enum asrc_pair_index index = pair->index;
2623117bb31SNicolin Chen 	unsigned long lock_flags;
2633117bb31SNicolin Chen 
2643117bb31SNicolin Chen 	/* Make sure the pair is disabled */
2657470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCTR,
2663117bb31SNicolin Chen 			   ASRCTR_ASRCEi_MASK(index), 0);
2673117bb31SNicolin Chen 
2687470704dSShengjiu Wang 	spin_lock_irqsave(&asrc->lock, lock_flags);
2693117bb31SNicolin Chen 
2707470704dSShengjiu Wang 	asrc->channel_avail += pair->channels;
2717470704dSShengjiu Wang 	asrc->pair[index] = NULL;
2723117bb31SNicolin Chen 	pair->error = 0;
2733117bb31SNicolin Chen 
2747470704dSShengjiu Wang 	spin_unlock_irqrestore(&asrc->lock, lock_flags);
2753117bb31SNicolin Chen }
2763117bb31SNicolin Chen 
2773117bb31SNicolin Chen /**
27845e039d9SPierre-Louis Bossart  * fsl_asrc_set_watermarks- configure input and output thresholds
27945e039d9SPierre-Louis Bossart  * @pair: pointer to pair
28045e039d9SPierre-Louis Bossart  * @in: input threshold
28145e039d9SPierre-Louis Bossart  * @out: output threshold
2823117bb31SNicolin Chen  */
fsl_asrc_set_watermarks(struct fsl_asrc_pair * pair,u32 in,u32 out)2833117bb31SNicolin Chen static void fsl_asrc_set_watermarks(struct fsl_asrc_pair *pair, u32 in, u32 out)
2843117bb31SNicolin Chen {
2857470704dSShengjiu Wang 	struct fsl_asrc *asrc = pair->asrc;
2863117bb31SNicolin Chen 	enum asrc_pair_index index = pair->index;
2873117bb31SNicolin Chen 
2887470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRMCR(index),
2893117bb31SNicolin Chen 			   ASRMCRi_EXTTHRSHi_MASK |
2903117bb31SNicolin Chen 			   ASRMCRi_INFIFO_THRESHOLD_MASK |
2913117bb31SNicolin Chen 			   ASRMCRi_OUTFIFO_THRESHOLD_MASK,
2923117bb31SNicolin Chen 			   ASRMCRi_EXTTHRSHi |
2933117bb31SNicolin Chen 			   ASRMCRi_INFIFO_THRESHOLD(in) |
2943117bb31SNicolin Chen 			   ASRMCRi_OUTFIFO_THRESHOLD(out));
2953117bb31SNicolin Chen }
2963117bb31SNicolin Chen 
2973117bb31SNicolin Chen /**
29845e039d9SPierre-Louis Bossart  * fsl_asrc_cal_asrck_divisor - Calculate the total divisor between asrck clock rate and sample rate
29945e039d9SPierre-Louis Bossart  * @pair: pointer to pair
30045e039d9SPierre-Louis Bossart  * @div: divider
3013117bb31SNicolin Chen  *
3023117bb31SNicolin Chen  * It follows the formula clk_rate = samplerate * (2 ^ prescaler) * divider
3033117bb31SNicolin Chen  */
fsl_asrc_cal_asrck_divisor(struct fsl_asrc_pair * pair,u32 div)3043117bb31SNicolin Chen static u32 fsl_asrc_cal_asrck_divisor(struct fsl_asrc_pair *pair, u32 div)
3053117bb31SNicolin Chen {
3063117bb31SNicolin Chen 	u32 ps;
3073117bb31SNicolin Chen 
3083117bb31SNicolin Chen 	/* Calculate the divisors: prescaler [2^0, 2^7], divder [1, 8] */
3093117bb31SNicolin Chen 	for (ps = 0; div > 8; ps++)
3103117bb31SNicolin Chen 		div >>= 1;
3113117bb31SNicolin Chen 
3123117bb31SNicolin Chen 	return ((div - 1) << ASRCDRi_AxCPi_WIDTH) | ps;
3133117bb31SNicolin Chen }
3143117bb31SNicolin Chen 
3153117bb31SNicolin Chen /**
31645e039d9SPierre-Louis Bossart  * fsl_asrc_set_ideal_ratio - Calculate and set the ratio for Ideal Ratio mode only
31745e039d9SPierre-Louis Bossart  * @pair: pointer to pair
31845e039d9SPierre-Louis Bossart  * @inrate: input rate
31945e039d9SPierre-Louis Bossart  * @outrate: output rate
3203117bb31SNicolin Chen  *
3213117bb31SNicolin Chen  * The ratio is a 32-bit fixed point value with 26 fractional bits.
3223117bb31SNicolin Chen  */
fsl_asrc_set_ideal_ratio(struct fsl_asrc_pair * pair,int inrate,int outrate)3233117bb31SNicolin Chen static int fsl_asrc_set_ideal_ratio(struct fsl_asrc_pair *pair,
3243117bb31SNicolin Chen 				    int inrate, int outrate)
3253117bb31SNicolin Chen {
3267470704dSShengjiu Wang 	struct fsl_asrc *asrc = pair->asrc;
3273117bb31SNicolin Chen 	enum asrc_pair_index index = pair->index;
3283117bb31SNicolin Chen 	unsigned long ratio;
3293117bb31SNicolin Chen 	int i;
3303117bb31SNicolin Chen 
3313117bb31SNicolin Chen 	if (!outrate) {
3323117bb31SNicolin Chen 		pair_err("output rate should not be zero\n");
3333117bb31SNicolin Chen 		return -EINVAL;
3343117bb31SNicolin Chen 	}
3353117bb31SNicolin Chen 
3363117bb31SNicolin Chen 	/* Calculate the intergal part of the ratio */
3373117bb31SNicolin Chen 	ratio = (inrate / outrate) << IDEAL_RATIO_DECIMAL_DEPTH;
3383117bb31SNicolin Chen 
3393117bb31SNicolin Chen 	/* ... and then the 26 depth decimal part */
3403117bb31SNicolin Chen 	inrate %= outrate;
3413117bb31SNicolin Chen 
3423117bb31SNicolin Chen 	for (i = 1; i <= IDEAL_RATIO_DECIMAL_DEPTH; i++) {
3433117bb31SNicolin Chen 		inrate <<= 1;
3443117bb31SNicolin Chen 
3453117bb31SNicolin Chen 		if (inrate < outrate)
3463117bb31SNicolin Chen 			continue;
3473117bb31SNicolin Chen 
3483117bb31SNicolin Chen 		ratio |= 1 << (IDEAL_RATIO_DECIMAL_DEPTH - i);
3493117bb31SNicolin Chen 		inrate -= outrate;
3503117bb31SNicolin Chen 
3513117bb31SNicolin Chen 		if (!inrate)
3523117bb31SNicolin Chen 			break;
3533117bb31SNicolin Chen 	}
3543117bb31SNicolin Chen 
3557470704dSShengjiu Wang 	regmap_write(asrc->regmap, REG_ASRIDRL(index), ratio);
3567470704dSShengjiu Wang 	regmap_write(asrc->regmap, REG_ASRIDRH(index), ratio >> 24);
3573117bb31SNicolin Chen 
3583117bb31SNicolin Chen 	return 0;
3593117bb31SNicolin Chen }
3603117bb31SNicolin Chen 
3613117bb31SNicolin Chen /**
36245e039d9SPierre-Louis Bossart  * fsl_asrc_config_pair - Configure the assigned ASRC pair
36345e039d9SPierre-Louis Bossart  * @pair: pointer to pair
36445e039d9SPierre-Louis Bossart  * @use_ideal_rate: boolean configuration
3653117bb31SNicolin Chen  *
3663117bb31SNicolin Chen  * It configures those ASRC registers according to a configuration instance
3673117bb31SNicolin Chen  * of struct asrc_config which includes in/output sample rate, width, channel
3683117bb31SNicolin Chen  * and clock settings.
369b39eb1e2SShengjiu Wang  *
370b39eb1e2SShengjiu Wang  * Note:
371b39eb1e2SShengjiu Wang  * The ideal ratio configuration can work with a flexible clock rate setting.
372b39eb1e2SShengjiu Wang  * Using IDEAL_RATIO_RATE gives a faster converting speed but overloads ASRC.
373b39eb1e2SShengjiu Wang  * For a regular audio playback, the clock rate should not be slower than an
374b39eb1e2SShengjiu Wang  * clock rate aligning with the output sample rate; For a use case requiring
375b39eb1e2SShengjiu Wang  * faster conversion, set use_ideal_rate to have the faster speed.
3763117bb31SNicolin Chen  */
fsl_asrc_config_pair(struct fsl_asrc_pair * pair,bool use_ideal_rate)377b39eb1e2SShengjiu Wang static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool use_ideal_rate)
3783117bb31SNicolin Chen {
379be7bd03fSShengjiu Wang 	struct fsl_asrc_pair_priv *pair_priv = pair->private;
380be7bd03fSShengjiu Wang 	struct asrc_config *config = pair_priv->config;
3817470704dSShengjiu Wang 	struct fsl_asrc *asrc = pair->asrc;
382be7bd03fSShengjiu Wang 	struct fsl_asrc_priv *asrc_priv = asrc->private;
3833117bb31SNicolin Chen 	enum asrc_pair_index index = pair->index;
3844bf62571SShengjiu Wang 	enum asrc_word_width input_word_width;
3854bf62571SShengjiu Wang 	enum asrc_word_width output_word_width;
3864e13eb72SNicolin Chen 	u32 inrate, outrate, indiv, outdiv;
38732038634SShengjiu Wang 	u32 clk_index[2], div[2];
388b39eb1e2SShengjiu Wang 	u64 clk_rate;
3893117bb31SNicolin Chen 	int in, out, channels;
3904aecaa0aSS.j. Wang 	int pre_proc, post_proc;
3913117bb31SNicolin Chen 	struct clk *clk;
39232038634SShengjiu Wang 	bool ideal, div_avail;
3933117bb31SNicolin Chen 
3943117bb31SNicolin Chen 	if (!config) {
3953117bb31SNicolin Chen 		pair_err("invalid pair config\n");
3963117bb31SNicolin Chen 		return -EINVAL;
3973117bb31SNicolin Chen 	}
3983117bb31SNicolin Chen 
3993117bb31SNicolin Chen 	/* Validate channels */
4003117bb31SNicolin Chen 	if (config->channel_num < 1 || config->channel_num > 10) {
4013117bb31SNicolin Chen 		pair_err("does not support %d channels\n", config->channel_num);
4023117bb31SNicolin Chen 		return -EINVAL;
4033117bb31SNicolin Chen 	}
4043117bb31SNicolin Chen 
4054bf62571SShengjiu Wang 	switch (snd_pcm_format_width(config->input_format)) {
4064bf62571SShengjiu Wang 	case 8:
4074bf62571SShengjiu Wang 		input_word_width = ASRC_WIDTH_8_BIT;
4084bf62571SShengjiu Wang 		break;
4094bf62571SShengjiu Wang 	case 16:
4104bf62571SShengjiu Wang 		input_word_width = ASRC_WIDTH_16_BIT;
4114bf62571SShengjiu Wang 		break;
4124bf62571SShengjiu Wang 	case 24:
4134bf62571SShengjiu Wang 		input_word_width = ASRC_WIDTH_24_BIT;
4144bf62571SShengjiu Wang 		break;
4154bf62571SShengjiu Wang 	default:
4164bf62571SShengjiu Wang 		pair_err("does not support this input format, %d\n",
4174bf62571SShengjiu Wang 			 config->input_format);
4184bf62571SShengjiu Wang 		return -EINVAL;
4194bf62571SShengjiu Wang 	}
4204bf62571SShengjiu Wang 
4214bf62571SShengjiu Wang 	switch (snd_pcm_format_width(config->output_format)) {
4224bf62571SShengjiu Wang 	case 16:
4234bf62571SShengjiu Wang 		output_word_width = ASRC_WIDTH_16_BIT;
4244bf62571SShengjiu Wang 		break;
4254bf62571SShengjiu Wang 	case 24:
4264bf62571SShengjiu Wang 		output_word_width = ASRC_WIDTH_24_BIT;
4274bf62571SShengjiu Wang 		break;
4284bf62571SShengjiu Wang 	default:
4294bf62571SShengjiu Wang 		pair_err("does not support this output format, %d\n",
4304bf62571SShengjiu Wang 			 config->output_format);
4313117bb31SNicolin Chen 		return -EINVAL;
4323117bb31SNicolin Chen 	}
4333117bb31SNicolin Chen 
4344e13eb72SNicolin Chen 	inrate = config->input_sample_rate;
4354e13eb72SNicolin Chen 	outrate = config->output_sample_rate;
4364e13eb72SNicolin Chen 	ideal = config->inclk == INCLK_NONE;
4374e13eb72SNicolin Chen 
4383117bb31SNicolin Chen 	/* Validate input and output sample rates */
439d281bf5dSS.j. Wang 	for (in = 0; in < ARRAY_SIZE(supported_asrc_rate); in++)
440d281bf5dSS.j. Wang 		if (inrate == supported_asrc_rate[in])
4413117bb31SNicolin Chen 			break;
4423117bb31SNicolin Chen 
443d281bf5dSS.j. Wang 	if (in == ARRAY_SIZE(supported_asrc_rate)) {
4443117bb31SNicolin Chen 		pair_err("unsupported input sample rate: %dHz\n", inrate);
4453117bb31SNicolin Chen 		return -EINVAL;
4463117bb31SNicolin Chen 	}
4473117bb31SNicolin Chen 
4483117bb31SNicolin Chen 	for (out = 0; out < ARRAY_SIZE(supported_asrc_rate); out++)
4493117bb31SNicolin Chen 		if (outrate == supported_asrc_rate[out])
4503117bb31SNicolin Chen 			break;
4513117bb31SNicolin Chen 
4523117bb31SNicolin Chen 	if (out == ARRAY_SIZE(supported_asrc_rate)) {
4533117bb31SNicolin Chen 		pair_err("unsupported output sample rate: %dHz\n", outrate);
4543117bb31SNicolin Chen 		return -EINVAL;
4553117bb31SNicolin Chen 	}
4563117bb31SNicolin Chen 
457d281bf5dSS.j. Wang 	if ((outrate >= 5512 && outrate <= 30000) &&
458b06c58c2SS.j. Wang 	    (outrate > 24 * inrate || inrate > 8 * outrate)) {
459fff6e03cSZidan Wang 		pair_err("exceed supported ratio range [1/24, 8] for \
460fff6e03cSZidan Wang 				inrate/outrate: %d/%d\n", inrate, outrate);
461fff6e03cSZidan Wang 		return -EINVAL;
462fff6e03cSZidan Wang 	}
463fff6e03cSZidan Wang 
4643117bb31SNicolin Chen 	/* Validate input and output clock sources */
465be7bd03fSShengjiu Wang 	clk_index[IN] = asrc_priv->clk_map[IN][config->inclk];
466be7bd03fSShengjiu Wang 	clk_index[OUT] = asrc_priv->clk_map[OUT][config->outclk];
4673117bb31SNicolin Chen 
4683117bb31SNicolin Chen 	/* We only have output clock for ideal ratio mode */
469be7bd03fSShengjiu Wang 	clk = asrc_priv->asrck_clk[clk_index[ideal ? OUT : IN]];
4703117bb31SNicolin Chen 
471b39eb1e2SShengjiu Wang 	clk_rate = clk_get_rate(clk);
47232038634SShengjiu Wang 	div_avail = fsl_asrc_divider_avail(clk_rate, inrate, &div[IN]);
473b39eb1e2SShengjiu Wang 
474b39eb1e2SShengjiu Wang 	/*
475b39eb1e2SShengjiu Wang 	 * The divider range is [1, 1024], defined by the hardware. For non-
476b39eb1e2SShengjiu Wang 	 * ideal ratio configuration, clock rate has to be strictly aligned
477b39eb1e2SShengjiu Wang 	 * with the sample rate. For ideal ratio configuration, clock rates
478b39eb1e2SShengjiu Wang 	 * only result in different converting speeds. So remainder does not
479b39eb1e2SShengjiu Wang 	 * matter, as long as we keep the divider within its valid range.
480b39eb1e2SShengjiu Wang 	 */
48132038634SShengjiu Wang 	if (div[IN] == 0 || (!ideal && !div_avail)) {
4823117bb31SNicolin Chen 		pair_err("failed to support input sample rate %dHz by asrck_%x\n",
4833117bb31SNicolin Chen 				inrate, clk_index[ideal ? OUT : IN]);
4843117bb31SNicolin Chen 		return -EINVAL;
4853117bb31SNicolin Chen 	}
4863117bb31SNicolin Chen 
487b39eb1e2SShengjiu Wang 	div[IN] = min_t(u32, 1024, div[IN]);
488b39eb1e2SShengjiu Wang 
489be7bd03fSShengjiu Wang 	clk = asrc_priv->asrck_clk[clk_index[OUT]];
490b39eb1e2SShengjiu Wang 	clk_rate = clk_get_rate(clk);
491b39eb1e2SShengjiu Wang 	if (ideal && use_ideal_rate)
49232038634SShengjiu Wang 		div_avail = fsl_asrc_divider_avail(clk_rate, IDEAL_RATIO_RATE, &div[OUT]);
4933117bb31SNicolin Chen 	else
49432038634SShengjiu Wang 		div_avail = fsl_asrc_divider_avail(clk_rate, outrate, &div[OUT]);
4953117bb31SNicolin Chen 
496b39eb1e2SShengjiu Wang 	/* Output divider has the same limitation as the input one */
49732038634SShengjiu Wang 	if (div[OUT] == 0 || (!ideal && !div_avail)) {
4983117bb31SNicolin Chen 		pair_err("failed to support output sample rate %dHz by asrck_%x\n",
4993117bb31SNicolin Chen 				outrate, clk_index[OUT]);
5003117bb31SNicolin Chen 		return -EINVAL;
5013117bb31SNicolin Chen 	}
5023117bb31SNicolin Chen 
503b39eb1e2SShengjiu Wang 	div[OUT] = min_t(u32, 1024, div[OUT]);
504b39eb1e2SShengjiu Wang 
5053117bb31SNicolin Chen 	/* Set the channel number */
5063117bb31SNicolin Chen 	channels = config->channel_num;
5073117bb31SNicolin Chen 
508be7bd03fSShengjiu Wang 	if (asrc_priv->soc->channel_bits < 4)
5093117bb31SNicolin Chen 		channels /= 2;
5103117bb31SNicolin Chen 
5113117bb31SNicolin Chen 	/* Update channels for current pair */
5127470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCNCR,
513be7bd03fSShengjiu Wang 			   ASRCNCR_ANCi_MASK(index, asrc_priv->soc->channel_bits),
514be7bd03fSShengjiu Wang 			   ASRCNCR_ANCi(index, channels, asrc_priv->soc->channel_bits));
5153117bb31SNicolin Chen 
5163117bb31SNicolin Chen 	/* Default setting: Automatic selection for processing mode */
5177470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCTR,
5183117bb31SNicolin Chen 			   ASRCTR_ATSi_MASK(index), ASRCTR_ATS(index));
5197470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCTR,
5203117bb31SNicolin Chen 			   ASRCTR_USRi_MASK(index), 0);
5213117bb31SNicolin Chen 
5223117bb31SNicolin Chen 	/* Set the input and output clock sources */
5237470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCSR,
5243117bb31SNicolin Chen 			   ASRCSR_AICSi_MASK(index) | ASRCSR_AOCSi_MASK(index),
5253117bb31SNicolin Chen 			   ASRCSR_AICS(index, clk_index[IN]) |
5263117bb31SNicolin Chen 			   ASRCSR_AOCS(index, clk_index[OUT]));
5273117bb31SNicolin Chen 
5283117bb31SNicolin Chen 	/* Calculate the input clock divisors */
5293117bb31SNicolin Chen 	indiv = fsl_asrc_cal_asrck_divisor(pair, div[IN]);
5303117bb31SNicolin Chen 	outdiv = fsl_asrc_cal_asrck_divisor(pair, div[OUT]);
5313117bb31SNicolin Chen 
5323117bb31SNicolin Chen 	/* Suppose indiv and outdiv includes prescaler, so add its MASK too */
5337470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCDR(index),
5343117bb31SNicolin Chen 			   ASRCDRi_AOCPi_MASK(index) | ASRCDRi_AICPi_MASK(index) |
5353117bb31SNicolin Chen 			   ASRCDRi_AOCDi_MASK(index) | ASRCDRi_AICDi_MASK(index),
5363117bb31SNicolin Chen 			   ASRCDRi_AOCP(index, outdiv) | ASRCDRi_AICP(index, indiv));
5373117bb31SNicolin Chen 
5383117bb31SNicolin Chen 	/* Implement word_width configurations */
5397470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRMCR1(index),
5403117bb31SNicolin Chen 			   ASRMCR1i_OW16_MASK | ASRMCR1i_IWD_MASK,
5414bf62571SShengjiu Wang 			   ASRMCR1i_OW16(output_word_width) |
5424bf62571SShengjiu Wang 			   ASRMCR1i_IWD(input_word_width));
5433117bb31SNicolin Chen 
5443117bb31SNicolin Chen 	/* Enable BUFFER STALL */
5457470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRMCR(index),
5463117bb31SNicolin Chen 			   ASRMCRi_BUFSTALLi_MASK, ASRMCRi_BUFSTALLi);
5473117bb31SNicolin Chen 
5483117bb31SNicolin Chen 	/* Set default thresholds for input and output FIFO */
5493117bb31SNicolin Chen 	fsl_asrc_set_watermarks(pair, ASRC_INPUTFIFO_THRESHOLD,
5503117bb31SNicolin Chen 				ASRC_INPUTFIFO_THRESHOLD);
5513117bb31SNicolin Chen 
5524091fb95SMasahiro Yamada 	/* Configure the following only for Ideal Ratio mode */
5533117bb31SNicolin Chen 	if (!ideal)
5543117bb31SNicolin Chen 		return 0;
5553117bb31SNicolin Chen 
5563117bb31SNicolin Chen 	/* Clear ASTSx bit to use Ideal Ratio mode */
5577470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCTR,
5583117bb31SNicolin Chen 			   ASRCTR_ATSi_MASK(index), 0);
5593117bb31SNicolin Chen 
5603117bb31SNicolin Chen 	/* Enable Ideal Ratio mode */
5617470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCTR,
5623117bb31SNicolin Chen 			   ASRCTR_IDRi_MASK(index) | ASRCTR_USRi_MASK(index),
5633117bb31SNicolin Chen 			   ASRCTR_IDR(index) | ASRCTR_USR(index));
5643117bb31SNicolin Chen 
5654aecaa0aSS.j. Wang 	fsl_asrc_sel_proc(inrate, outrate, &pre_proc, &post_proc);
5664aecaa0aSS.j. Wang 
5673117bb31SNicolin Chen 	/* Apply configurations for pre- and post-processing */
5687470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCFG,
5693117bb31SNicolin Chen 			   ASRCFG_PREMODi_MASK(index) |	ASRCFG_POSTMODi_MASK(index),
5704aecaa0aSS.j. Wang 			   ASRCFG_PREMOD(index, pre_proc) |
5714aecaa0aSS.j. Wang 			   ASRCFG_POSTMOD(index, post_proc));
5723117bb31SNicolin Chen 
5733117bb31SNicolin Chen 	return fsl_asrc_set_ideal_ratio(pair, inrate, outrate);
5743117bb31SNicolin Chen }
5753117bb31SNicolin Chen 
5763117bb31SNicolin Chen /**
57745e039d9SPierre-Louis Bossart  * fsl_asrc_start_pair - Start the assigned ASRC pair
57845e039d9SPierre-Louis Bossart  * @pair: pointer to pair
5793117bb31SNicolin Chen  *
5803117bb31SNicolin Chen  * It enables the assigned pair and makes it stopped at the stall level.
5813117bb31SNicolin Chen  */
fsl_asrc_start_pair(struct fsl_asrc_pair * pair)5823117bb31SNicolin Chen static void fsl_asrc_start_pair(struct fsl_asrc_pair *pair)
5833117bb31SNicolin Chen {
5847470704dSShengjiu Wang 	struct fsl_asrc *asrc = pair->asrc;
5853117bb31SNicolin Chen 	enum asrc_pair_index index = pair->index;
586d2de3f5eSShengjiu Wang 	int reg, retry = INIT_RETRY_NUM, i;
5873117bb31SNicolin Chen 
5883117bb31SNicolin Chen 	/* Enable the current pair */
5897470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCTR,
5903117bb31SNicolin Chen 			   ASRCTR_ASRCEi_MASK(index), ASRCTR_ASRCE(index));
5913117bb31SNicolin Chen 
5923117bb31SNicolin Chen 	/* Wait for status of initialization */
5933117bb31SNicolin Chen 	do {
5943117bb31SNicolin Chen 		udelay(5);
5957470704dSShengjiu Wang 		regmap_read(asrc->regmap, REG_ASRCFG, &reg);
5963117bb31SNicolin Chen 		reg &= ASRCFG_INIRQi_MASK(index);
5973117bb31SNicolin Chen 	} while (!reg && --retry);
5983117bb31SNicolin Chen 
599d2de3f5eSShengjiu Wang 	/* NOTE: Doesn't treat initialization timeout as an error */
600d2de3f5eSShengjiu Wang 	if (!retry)
601d2de3f5eSShengjiu Wang 		pair_warn("initialization isn't finished\n");
602d2de3f5eSShengjiu Wang 
6033117bb31SNicolin Chen 	/* Make the input fifo to ASRC STALL level */
6047470704dSShengjiu Wang 	regmap_read(asrc->regmap, REG_ASRCNCR, &reg);
6053117bb31SNicolin Chen 	for (i = 0; i < pair->channels * 4; i++)
6067470704dSShengjiu Wang 		regmap_write(asrc->regmap, REG_ASRDI(index), 0);
6073117bb31SNicolin Chen 
6083117bb31SNicolin Chen 	/* Enable overload interrupt */
6097470704dSShengjiu Wang 	regmap_write(asrc->regmap, REG_ASRIER, ASRIER_AOLIE);
6103117bb31SNicolin Chen }
6113117bb31SNicolin Chen 
6123117bb31SNicolin Chen /**
61345e039d9SPierre-Louis Bossart  * fsl_asrc_stop_pair - Stop the assigned ASRC pair
61445e039d9SPierre-Louis Bossart  * @pair: pointer to pair
6153117bb31SNicolin Chen  */
fsl_asrc_stop_pair(struct fsl_asrc_pair * pair)6163117bb31SNicolin Chen static void fsl_asrc_stop_pair(struct fsl_asrc_pair *pair)
6173117bb31SNicolin Chen {
6187470704dSShengjiu Wang 	struct fsl_asrc *asrc = pair->asrc;
6193117bb31SNicolin Chen 	enum asrc_pair_index index = pair->index;
6203117bb31SNicolin Chen 
6213117bb31SNicolin Chen 	/* Stop the current pair */
6227470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCTR,
6233117bb31SNicolin Chen 			   ASRCTR_ASRCEi_MASK(index), 0);
6243117bb31SNicolin Chen }
6253117bb31SNicolin Chen 
6263117bb31SNicolin Chen /**
62745e039d9SPierre-Louis Bossart  * fsl_asrc_get_dma_channel- Get DMA channel according to the pair and direction.
62845e039d9SPierre-Louis Bossart  * @pair: pointer to pair
62945e039d9SPierre-Louis Bossart  * @dir: DMA direction
6303117bb31SNicolin Chen  */
fsl_asrc_get_dma_channel(struct fsl_asrc_pair * pair,bool dir)631cff1f8b4Sshengjiu wang static struct dma_chan *fsl_asrc_get_dma_channel(struct fsl_asrc_pair *pair,
632cff1f8b4Sshengjiu wang 						 bool dir)
6333117bb31SNicolin Chen {
6347470704dSShengjiu Wang 	struct fsl_asrc *asrc = pair->asrc;
6353117bb31SNicolin Chen 	enum asrc_pair_index index = pair->index;
6363117bb31SNicolin Chen 	char name[4];
6373117bb31SNicolin Chen 
6383117bb31SNicolin Chen 	sprintf(name, "%cx%c", dir == IN ? 'r' : 't', index + 'a');
6393117bb31SNicolin Chen 
6407470704dSShengjiu Wang 	return dma_request_slave_channel(&asrc->pdev->dev, name);
6413117bb31SNicolin Chen }
6423117bb31SNicolin Chen 
fsl_asrc_dai_startup(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)64353f67a78SS.j. Wang static int fsl_asrc_dai_startup(struct snd_pcm_substream *substream,
64453f67a78SS.j. Wang 				struct snd_soc_dai *dai)
64553f67a78SS.j. Wang {
6467470704dSShengjiu Wang 	struct fsl_asrc *asrc = snd_soc_dai_get_drvdata(dai);
647be7bd03fSShengjiu Wang 	struct fsl_asrc_priv *asrc_priv = asrc->private;
64853f67a78SS.j. Wang 
64953f67a78SS.j. Wang 	/* Odd channel number is not valid for older ASRC (channel_bits==3) */
650be7bd03fSShengjiu Wang 	if (asrc_priv->soc->channel_bits == 3)
65153f67a78SS.j. Wang 		snd_pcm_hw_constraint_step(substream->runtime, 0,
65253f67a78SS.j. Wang 					   SNDRV_PCM_HW_PARAM_CHANNELS, 2);
65353f67a78SS.j. Wang 
654d281bf5dSS.j. Wang 
655d281bf5dSS.j. Wang 	return snd_pcm_hw_constraint_list(substream->runtime, 0,
656d281bf5dSS.j. Wang 			SNDRV_PCM_HW_PARAM_RATE, &fsl_asrc_rate_constraints);
65753f67a78SS.j. Wang }
65853f67a78SS.j. Wang 
659d0250cf4SShengjiu Wang /* Select proper clock source for internal ratio mode */
fsl_asrc_select_clk(struct fsl_asrc_priv * asrc_priv,struct fsl_asrc_pair * pair,int in_rate,int out_rate)660d0250cf4SShengjiu Wang static void fsl_asrc_select_clk(struct fsl_asrc_priv *asrc_priv,
661d0250cf4SShengjiu Wang 				struct fsl_asrc_pair *pair,
662d0250cf4SShengjiu Wang 				int in_rate,
663d0250cf4SShengjiu Wang 				int out_rate)
664d0250cf4SShengjiu Wang {
665d0250cf4SShengjiu Wang 	struct fsl_asrc_pair_priv *pair_priv = pair->private;
666d0250cf4SShengjiu Wang 	struct asrc_config *config = pair_priv->config;
667d0250cf4SShengjiu Wang 	int rate[2], select_clk[2]; /* Array size 2 means IN and OUT */
668d0250cf4SShengjiu Wang 	int clk_rate, clk_index;
669b2967435SPierre-Louis Bossart 	int i, j;
670d0250cf4SShengjiu Wang 
671d0250cf4SShengjiu Wang 	rate[IN] = in_rate;
672d0250cf4SShengjiu Wang 	rate[OUT] = out_rate;
673d0250cf4SShengjiu Wang 
674d0250cf4SShengjiu Wang 	/* Select proper clock source for internal ratio mode */
675d0250cf4SShengjiu Wang 	for (j = 0; j < 2; j++) {
676d0250cf4SShengjiu Wang 		for (i = 0; i < ASRC_CLK_MAP_LEN; i++) {
677d0250cf4SShengjiu Wang 			clk_index = asrc_priv->clk_map[j][i];
678d0250cf4SShengjiu Wang 			clk_rate = clk_get_rate(asrc_priv->asrck_clk[clk_index]);
679d0250cf4SShengjiu Wang 			/* Only match a perfect clock source with no remainder */
68032038634SShengjiu Wang 			if (fsl_asrc_divider_avail(clk_rate, rate[j], NULL))
681d0250cf4SShengjiu Wang 				break;
682d0250cf4SShengjiu Wang 		}
683d0250cf4SShengjiu Wang 
684d0250cf4SShengjiu Wang 		select_clk[j] = i;
685d0250cf4SShengjiu Wang 	}
686d0250cf4SShengjiu Wang 
687d0250cf4SShengjiu Wang 	/* Switch to ideal ratio mode if there is no proper clock source */
688d0250cf4SShengjiu Wang 	if (select_clk[IN] == ASRC_CLK_MAP_LEN || select_clk[OUT] == ASRC_CLK_MAP_LEN) {
689d0250cf4SShengjiu Wang 		select_clk[IN] = INCLK_NONE;
690d0250cf4SShengjiu Wang 		select_clk[OUT] = OUTCLK_ASRCK1_CLK;
691d0250cf4SShengjiu Wang 	}
692d0250cf4SShengjiu Wang 
693d0250cf4SShengjiu Wang 	config->inclk = select_clk[IN];
694d0250cf4SShengjiu Wang 	config->outclk = select_clk[OUT];
695d0250cf4SShengjiu Wang }
696d0250cf4SShengjiu Wang 
fsl_asrc_dai_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_soc_dai * dai)6973117bb31SNicolin Chen static int fsl_asrc_dai_hw_params(struct snd_pcm_substream *substream,
6983117bb31SNicolin Chen 				  struct snd_pcm_hw_params *params,
6993117bb31SNicolin Chen 				  struct snd_soc_dai *dai)
7003117bb31SNicolin Chen {
7017470704dSShengjiu Wang 	struct fsl_asrc *asrc = snd_soc_dai_get_drvdata(dai);
702d0250cf4SShengjiu Wang 	struct fsl_asrc_priv *asrc_priv = asrc->private;
7033117bb31SNicolin Chen 	struct snd_pcm_runtime *runtime = substream->runtime;
7043117bb31SNicolin Chen 	struct fsl_asrc_pair *pair = runtime->private_data;
705be7bd03fSShengjiu Wang 	struct fsl_asrc_pair_priv *pair_priv = pair->private;
7063117bb31SNicolin Chen 	unsigned int channels = params_channels(params);
7073117bb31SNicolin Chen 	unsigned int rate = params_rate(params);
7083117bb31SNicolin Chen 	struct asrc_config config;
7094bf62571SShengjiu Wang 	int ret;
7103117bb31SNicolin Chen 
7113117bb31SNicolin Chen 	ret = fsl_asrc_request_pair(channels, pair);
7123117bb31SNicolin Chen 	if (ret) {
7133117bb31SNicolin Chen 		dev_err(dai->dev, "fail to request asrc pair\n");
7143117bb31SNicolin Chen 		return ret;
7153117bb31SNicolin Chen 	}
7163117bb31SNicolin Chen 
717be7bd03fSShengjiu Wang 	pair_priv->config = &config;
7183117bb31SNicolin Chen 
7193117bb31SNicolin Chen 	config.pair = pair->index;
7203117bb31SNicolin Chen 	config.channel_num = channels;
7213117bb31SNicolin Chen 
7223117bb31SNicolin Chen 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
7234bf62571SShengjiu Wang 		config.input_format   = params_format(params);
7244520af41SShengjiu Wang 		config.output_format  = asrc->asrc_format;
7253117bb31SNicolin Chen 		config.input_sample_rate  = rate;
7267470704dSShengjiu Wang 		config.output_sample_rate = asrc->asrc_rate;
7273117bb31SNicolin Chen 	} else {
7284520af41SShengjiu Wang 		config.input_format   = asrc->asrc_format;
7294bf62571SShengjiu Wang 		config.output_format  = params_format(params);
7307470704dSShengjiu Wang 		config.input_sample_rate  = asrc->asrc_rate;
7313117bb31SNicolin Chen 		config.output_sample_rate = rate;
7323117bb31SNicolin Chen 	}
7333117bb31SNicolin Chen 
734d0250cf4SShengjiu Wang 	fsl_asrc_select_clk(asrc_priv, pair,
735d0250cf4SShengjiu Wang 			    config.input_sample_rate,
736d0250cf4SShengjiu Wang 			    config.output_sample_rate);
737d0250cf4SShengjiu Wang 
738b39eb1e2SShengjiu Wang 	ret = fsl_asrc_config_pair(pair, false);
7393117bb31SNicolin Chen 	if (ret) {
7403117bb31SNicolin Chen 		dev_err(dai->dev, "fail to config asrc pair\n");
7413117bb31SNicolin Chen 		return ret;
7423117bb31SNicolin Chen 	}
7433117bb31SNicolin Chen 
7443117bb31SNicolin Chen 	return 0;
7453117bb31SNicolin Chen }
7463117bb31SNicolin Chen 
fsl_asrc_dai_hw_free(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)7473117bb31SNicolin Chen static int fsl_asrc_dai_hw_free(struct snd_pcm_substream *substream,
7483117bb31SNicolin Chen 				struct snd_soc_dai *dai)
7493117bb31SNicolin Chen {
7503117bb31SNicolin Chen 	struct snd_pcm_runtime *runtime = substream->runtime;
7513117bb31SNicolin Chen 	struct fsl_asrc_pair *pair = runtime->private_data;
7523117bb31SNicolin Chen 
7533117bb31SNicolin Chen 	if (pair)
7543117bb31SNicolin Chen 		fsl_asrc_release_pair(pair);
7553117bb31SNicolin Chen 
7563117bb31SNicolin Chen 	return 0;
7573117bb31SNicolin Chen }
7583117bb31SNicolin Chen 
fsl_asrc_dai_trigger(struct snd_pcm_substream * substream,int cmd,struct snd_soc_dai * dai)7593117bb31SNicolin Chen static int fsl_asrc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
7603117bb31SNicolin Chen 				struct snd_soc_dai *dai)
7613117bb31SNicolin Chen {
7623117bb31SNicolin Chen 	struct snd_pcm_runtime *runtime = substream->runtime;
7633117bb31SNicolin Chen 	struct fsl_asrc_pair *pair = runtime->private_data;
7643117bb31SNicolin Chen 
7653117bb31SNicolin Chen 	switch (cmd) {
7663117bb31SNicolin Chen 	case SNDRV_PCM_TRIGGER_START:
7673117bb31SNicolin Chen 	case SNDRV_PCM_TRIGGER_RESUME:
7683117bb31SNicolin Chen 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
7693117bb31SNicolin Chen 		fsl_asrc_start_pair(pair);
7703117bb31SNicolin Chen 		break;
7713117bb31SNicolin Chen 	case SNDRV_PCM_TRIGGER_STOP:
7723117bb31SNicolin Chen 	case SNDRV_PCM_TRIGGER_SUSPEND:
7733117bb31SNicolin Chen 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
7743117bb31SNicolin Chen 		fsl_asrc_stop_pair(pair);
7753117bb31SNicolin Chen 		break;
7763117bb31SNicolin Chen 	default:
7773117bb31SNicolin Chen 		return -EINVAL;
7783117bb31SNicolin Chen 	}
7793117bb31SNicolin Chen 
7803117bb31SNicolin Chen 	return 0;
7813117bb31SNicolin Chen }
7823117bb31SNicolin Chen 
fsl_asrc_dai_probe(struct snd_soc_dai * dai)7833117bb31SNicolin Chen static int fsl_asrc_dai_probe(struct snd_soc_dai *dai)
7843117bb31SNicolin Chen {
7857470704dSShengjiu Wang 	struct fsl_asrc *asrc = snd_soc_dai_get_drvdata(dai);
7863117bb31SNicolin Chen 
7877470704dSShengjiu Wang 	snd_soc_dai_init_dma_data(dai, &asrc->dma_params_tx,
7887470704dSShengjiu Wang 				  &asrc->dma_params_rx);
7893117bb31SNicolin Chen 
7903117bb31SNicolin Chen 	return 0;
7913117bb31SNicolin Chen }
7923117bb31SNicolin Chen 
793*ac27ca16SKuninori Morimoto static const struct snd_soc_dai_ops fsl_asrc_dai_ops = {
794*ac27ca16SKuninori Morimoto 	.probe		= fsl_asrc_dai_probe,
795*ac27ca16SKuninori Morimoto 	.startup	= fsl_asrc_dai_startup,
796*ac27ca16SKuninori Morimoto 	.hw_params	= fsl_asrc_dai_hw_params,
797*ac27ca16SKuninori Morimoto 	.hw_free	= fsl_asrc_dai_hw_free,
798*ac27ca16SKuninori Morimoto 	.trigger	= fsl_asrc_dai_trigger,
799*ac27ca16SKuninori Morimoto };
800*ac27ca16SKuninori Morimoto 
8013117bb31SNicolin Chen #define FSL_ASRC_FORMATS	(SNDRV_PCM_FMTBIT_S24_LE | \
8023117bb31SNicolin Chen 				 SNDRV_PCM_FMTBIT_S16_LE | \
803109539c9SShengjiu Wang 				 SNDRV_PCM_FMTBIT_S24_3LE)
8043117bb31SNicolin Chen 
8053117bb31SNicolin Chen static struct snd_soc_dai_driver fsl_asrc_dai = {
8063117bb31SNicolin Chen 	.playback = {
8073117bb31SNicolin Chen 		.stream_name = "ASRC-Playback",
8083117bb31SNicolin Chen 		.channels_min = 1,
8093117bb31SNicolin Chen 		.channels_max = 10,
810d281bf5dSS.j. Wang 		.rate_min = 5512,
811d281bf5dSS.j. Wang 		.rate_max = 192000,
812d281bf5dSS.j. Wang 		.rates = SNDRV_PCM_RATE_KNOT,
813109539c9SShengjiu Wang 		.formats = FSL_ASRC_FORMATS |
814109539c9SShengjiu Wang 			   SNDRV_PCM_FMTBIT_S8,
8153117bb31SNicolin Chen 	},
8163117bb31SNicolin Chen 	.capture = {
8173117bb31SNicolin Chen 		.stream_name = "ASRC-Capture",
8183117bb31SNicolin Chen 		.channels_min = 1,
8193117bb31SNicolin Chen 		.channels_max = 10,
820d281bf5dSS.j. Wang 		.rate_min = 5512,
821d281bf5dSS.j. Wang 		.rate_max = 192000,
822d281bf5dSS.j. Wang 		.rates = SNDRV_PCM_RATE_KNOT,
8233117bb31SNicolin Chen 		.formats = FSL_ASRC_FORMATS,
8243117bb31SNicolin Chen 	},
8253117bb31SNicolin Chen 	.ops = &fsl_asrc_dai_ops,
8263117bb31SNicolin Chen };
8273117bb31SNicolin Chen 
fsl_asrc_readable_reg(struct device * dev,unsigned int reg)8283117bb31SNicolin Chen static bool fsl_asrc_readable_reg(struct device *dev, unsigned int reg)
8293117bb31SNicolin Chen {
8303117bb31SNicolin Chen 	switch (reg) {
8313117bb31SNicolin Chen 	case REG_ASRCTR:
8323117bb31SNicolin Chen 	case REG_ASRIER:
8333117bb31SNicolin Chen 	case REG_ASRCNCR:
8343117bb31SNicolin Chen 	case REG_ASRCFG:
8353117bb31SNicolin Chen 	case REG_ASRCSR:
8363117bb31SNicolin Chen 	case REG_ASRCDR1:
8373117bb31SNicolin Chen 	case REG_ASRCDR2:
8383117bb31SNicolin Chen 	case REG_ASRSTR:
8393117bb31SNicolin Chen 	case REG_ASRPM1:
8403117bb31SNicolin Chen 	case REG_ASRPM2:
8413117bb31SNicolin Chen 	case REG_ASRPM3:
8423117bb31SNicolin Chen 	case REG_ASRPM4:
8433117bb31SNicolin Chen 	case REG_ASRPM5:
8443117bb31SNicolin Chen 	case REG_ASRTFR1:
8453117bb31SNicolin Chen 	case REG_ASRCCR:
8463117bb31SNicolin Chen 	case REG_ASRDOA:
8473117bb31SNicolin Chen 	case REG_ASRDOB:
8483117bb31SNicolin Chen 	case REG_ASRDOC:
8493117bb31SNicolin Chen 	case REG_ASRIDRHA:
8503117bb31SNicolin Chen 	case REG_ASRIDRLA:
8513117bb31SNicolin Chen 	case REG_ASRIDRHB:
8523117bb31SNicolin Chen 	case REG_ASRIDRLB:
8533117bb31SNicolin Chen 	case REG_ASRIDRHC:
8543117bb31SNicolin Chen 	case REG_ASRIDRLC:
8553117bb31SNicolin Chen 	case REG_ASR76K:
8563117bb31SNicolin Chen 	case REG_ASR56K:
8573117bb31SNicolin Chen 	case REG_ASRMCRA:
8583117bb31SNicolin Chen 	case REG_ASRFSTA:
8593117bb31SNicolin Chen 	case REG_ASRMCRB:
8603117bb31SNicolin Chen 	case REG_ASRFSTB:
8613117bb31SNicolin Chen 	case REG_ASRMCRC:
8623117bb31SNicolin Chen 	case REG_ASRFSTC:
8633117bb31SNicolin Chen 	case REG_ASRMCR1A:
8643117bb31SNicolin Chen 	case REG_ASRMCR1B:
8653117bb31SNicolin Chen 	case REG_ASRMCR1C:
8663117bb31SNicolin Chen 		return true;
8673117bb31SNicolin Chen 	default:
8683117bb31SNicolin Chen 		return false;
8693117bb31SNicolin Chen 	}
8703117bb31SNicolin Chen }
8713117bb31SNicolin Chen 
fsl_asrc_volatile_reg(struct device * dev,unsigned int reg)8723117bb31SNicolin Chen static bool fsl_asrc_volatile_reg(struct device *dev, unsigned int reg)
8733117bb31SNicolin Chen {
8743117bb31SNicolin Chen 	switch (reg) {
8753117bb31SNicolin Chen 	case REG_ASRSTR:
8763117bb31SNicolin Chen 	case REG_ASRDIA:
8773117bb31SNicolin Chen 	case REG_ASRDIB:
8783117bb31SNicolin Chen 	case REG_ASRDIC:
8793117bb31SNicolin Chen 	case REG_ASRDOA:
8803117bb31SNicolin Chen 	case REG_ASRDOB:
8813117bb31SNicolin Chen 	case REG_ASRDOC:
8823117bb31SNicolin Chen 	case REG_ASRFSTA:
8833117bb31SNicolin Chen 	case REG_ASRFSTB:
8843117bb31SNicolin Chen 	case REG_ASRFSTC:
8853117bb31SNicolin Chen 	case REG_ASRCFG:
8863117bb31SNicolin Chen 		return true;
8873117bb31SNicolin Chen 	default:
8883117bb31SNicolin Chen 		return false;
8893117bb31SNicolin Chen 	}
8903117bb31SNicolin Chen }
8913117bb31SNicolin Chen 
fsl_asrc_writeable_reg(struct device * dev,unsigned int reg)8923117bb31SNicolin Chen static bool fsl_asrc_writeable_reg(struct device *dev, unsigned int reg)
8933117bb31SNicolin Chen {
8943117bb31SNicolin Chen 	switch (reg) {
8953117bb31SNicolin Chen 	case REG_ASRCTR:
8963117bb31SNicolin Chen 	case REG_ASRIER:
8973117bb31SNicolin Chen 	case REG_ASRCNCR:
8983117bb31SNicolin Chen 	case REG_ASRCFG:
8993117bb31SNicolin Chen 	case REG_ASRCSR:
9003117bb31SNicolin Chen 	case REG_ASRCDR1:
9013117bb31SNicolin Chen 	case REG_ASRCDR2:
9023117bb31SNicolin Chen 	case REG_ASRSTR:
9033117bb31SNicolin Chen 	case REG_ASRPM1:
9043117bb31SNicolin Chen 	case REG_ASRPM2:
9053117bb31SNicolin Chen 	case REG_ASRPM3:
9063117bb31SNicolin Chen 	case REG_ASRPM4:
9073117bb31SNicolin Chen 	case REG_ASRPM5:
9083117bb31SNicolin Chen 	case REG_ASRTFR1:
9093117bb31SNicolin Chen 	case REG_ASRCCR:
9103117bb31SNicolin Chen 	case REG_ASRDIA:
9113117bb31SNicolin Chen 	case REG_ASRDIB:
9123117bb31SNicolin Chen 	case REG_ASRDIC:
9133117bb31SNicolin Chen 	case REG_ASRIDRHA:
9143117bb31SNicolin Chen 	case REG_ASRIDRLA:
9153117bb31SNicolin Chen 	case REG_ASRIDRHB:
9163117bb31SNicolin Chen 	case REG_ASRIDRLB:
9173117bb31SNicolin Chen 	case REG_ASRIDRHC:
9183117bb31SNicolin Chen 	case REG_ASRIDRLC:
9193117bb31SNicolin Chen 	case REG_ASR76K:
9203117bb31SNicolin Chen 	case REG_ASR56K:
9213117bb31SNicolin Chen 	case REG_ASRMCRA:
9223117bb31SNicolin Chen 	case REG_ASRMCRB:
9233117bb31SNicolin Chen 	case REG_ASRMCRC:
9243117bb31SNicolin Chen 	case REG_ASRMCR1A:
9253117bb31SNicolin Chen 	case REG_ASRMCR1B:
9263117bb31SNicolin Chen 	case REG_ASRMCR1C:
9273117bb31SNicolin Chen 		return true;
9283117bb31SNicolin Chen 	default:
9293117bb31SNicolin Chen 		return false;
9303117bb31SNicolin Chen 	}
9313117bb31SNicolin Chen }
9323117bb31SNicolin Chen 
93386a570c5SNicolin Chen static struct reg_default fsl_asrc_reg[] = {
93486a570c5SNicolin Chen 	{ REG_ASRCTR, 0x0000 }, { REG_ASRIER, 0x0000 },
93586a570c5SNicolin Chen 	{ REG_ASRCNCR, 0x0000 }, { REG_ASRCFG, 0x0000 },
93686a570c5SNicolin Chen 	{ REG_ASRCSR, 0x0000 }, { REG_ASRCDR1, 0x0000 },
93786a570c5SNicolin Chen 	{ REG_ASRCDR2, 0x0000 }, { REG_ASRSTR, 0x0000 },
93886a570c5SNicolin Chen 	{ REG_ASRRA, 0x0000 }, { REG_ASRRB, 0x0000 },
93986a570c5SNicolin Chen 	{ REG_ASRRC, 0x0000 }, { REG_ASRPM1, 0x0000 },
94086a570c5SNicolin Chen 	{ REG_ASRPM2, 0x0000 }, { REG_ASRPM3, 0x0000 },
94186a570c5SNicolin Chen 	{ REG_ASRPM4, 0x0000 }, { REG_ASRPM5, 0x0000 },
94286a570c5SNicolin Chen 	{ REG_ASRTFR1, 0x0000 }, { REG_ASRCCR, 0x0000 },
94386a570c5SNicolin Chen 	{ REG_ASRDIA, 0x0000 }, { REG_ASRDOA, 0x0000 },
94486a570c5SNicolin Chen 	{ REG_ASRDIB, 0x0000 }, { REG_ASRDOB, 0x0000 },
94586a570c5SNicolin Chen 	{ REG_ASRDIC, 0x0000 }, { REG_ASRDOC, 0x0000 },
94686a570c5SNicolin Chen 	{ REG_ASRIDRHA, 0x0000 }, { REG_ASRIDRLA, 0x0000 },
94786a570c5SNicolin Chen 	{ REG_ASRIDRHB, 0x0000 }, { REG_ASRIDRLB, 0x0000 },
94886a570c5SNicolin Chen 	{ REG_ASRIDRHC, 0x0000 }, { REG_ASRIDRLC, 0x0000 },
94986a570c5SNicolin Chen 	{ REG_ASR76K, 0x0A47 }, { REG_ASR56K, 0x0DF3 },
95086a570c5SNicolin Chen 	{ REG_ASRMCRA, 0x0000 }, { REG_ASRFSTA, 0x0000 },
95186a570c5SNicolin Chen 	{ REG_ASRMCRB, 0x0000 }, { REG_ASRFSTB, 0x0000 },
95286a570c5SNicolin Chen 	{ REG_ASRMCRC, 0x0000 }, { REG_ASRFSTC, 0x0000 },
95386a570c5SNicolin Chen 	{ REG_ASRMCR1A, 0x0000 }, { REG_ASRMCR1B, 0x0000 },
95486a570c5SNicolin Chen 	{ REG_ASRMCR1C, 0x0000 },
95586a570c5SNicolin Chen };
95686a570c5SNicolin Chen 
957bf16d883SXiubo Li static const struct regmap_config fsl_asrc_regmap_config = {
9583117bb31SNicolin Chen 	.reg_bits = 32,
9593117bb31SNicolin Chen 	.reg_stride = 4,
9603117bb31SNicolin Chen 	.val_bits = 32,
9613117bb31SNicolin Chen 
9623117bb31SNicolin Chen 	.max_register = REG_ASRMCR1C,
96386a570c5SNicolin Chen 	.reg_defaults = fsl_asrc_reg,
96486a570c5SNicolin Chen 	.num_reg_defaults = ARRAY_SIZE(fsl_asrc_reg),
9653117bb31SNicolin Chen 	.readable_reg = fsl_asrc_readable_reg,
9663117bb31SNicolin Chen 	.volatile_reg = fsl_asrc_volatile_reg,
9673117bb31SNicolin Chen 	.writeable_reg = fsl_asrc_writeable_reg,
968b4138868SMarek Vasut 	.cache_type = REGCACHE_FLAT,
9693117bb31SNicolin Chen };
9703117bb31SNicolin Chen 
9713117bb31SNicolin Chen /**
97245e039d9SPierre-Louis Bossart  * fsl_asrc_init - Initialize ASRC registers with a default configuration
97345e039d9SPierre-Louis Bossart  * @asrc: ASRC context
9743117bb31SNicolin Chen  */
fsl_asrc_init(struct fsl_asrc * asrc)9757470704dSShengjiu Wang static int fsl_asrc_init(struct fsl_asrc *asrc)
9763117bb31SNicolin Chen {
977f8953043SShengjiu Wang 	unsigned long ipg_rate;
978f8953043SShengjiu Wang 
9793117bb31SNicolin Chen 	/* Halt ASRC internal FP when input FIFO needs data for pair A, B, C */
9807470704dSShengjiu Wang 	regmap_write(asrc->regmap, REG_ASRCTR, ASRCTR_ASRCEN);
9813117bb31SNicolin Chen 
9823117bb31SNicolin Chen 	/* Disable interrupt by default */
9837470704dSShengjiu Wang 	regmap_write(asrc->regmap, REG_ASRIER, 0x0);
9843117bb31SNicolin Chen 
9853117bb31SNicolin Chen 	/* Apply recommended settings for parameters from Reference Manual */
9867470704dSShengjiu Wang 	regmap_write(asrc->regmap, REG_ASRPM1, 0x7fffff);
9877470704dSShengjiu Wang 	regmap_write(asrc->regmap, REG_ASRPM2, 0x255555);
9887470704dSShengjiu Wang 	regmap_write(asrc->regmap, REG_ASRPM3, 0xff7280);
9897470704dSShengjiu Wang 	regmap_write(asrc->regmap, REG_ASRPM4, 0xff7280);
9907470704dSShengjiu Wang 	regmap_write(asrc->regmap, REG_ASRPM5, 0xff7280);
9913117bb31SNicolin Chen 
9923117bb31SNicolin Chen 	/* Base address for task queue FIFO. Set to 0x7C */
9937470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRTFR1,
9943117bb31SNicolin Chen 			   ASRTFR1_TF_BASE_MASK, ASRTFR1_TF_BASE(0xfc));
9953117bb31SNicolin Chen 
996f8953043SShengjiu Wang 	/*
997f8953043SShengjiu Wang 	 * Set the period of the 76KHz and 56KHz sampling clocks based on
998f8953043SShengjiu Wang 	 * the ASRC processing clock.
999f8953043SShengjiu Wang 	 * On iMX6, ipg_clk = 133MHz, REG_ASR76K = 0x06D6, REG_ASR56K = 0x0947
1000f8953043SShengjiu Wang 	 */
1001f8953043SShengjiu Wang 	ipg_rate = clk_get_rate(asrc->ipg_clk);
1002f8953043SShengjiu Wang 	regmap_write(asrc->regmap, REG_ASR76K, ipg_rate / 76000);
1003f8953043SShengjiu Wang 	return regmap_write(asrc->regmap, REG_ASR56K, ipg_rate / 56000);
10043117bb31SNicolin Chen }
10053117bb31SNicolin Chen 
10063117bb31SNicolin Chen /**
100745e039d9SPierre-Louis Bossart  * fsl_asrc_isr- Interrupt handler for ASRC
100845e039d9SPierre-Louis Bossart  * @irq: irq number
100945e039d9SPierre-Louis Bossart  * @dev_id: ASRC context
10103117bb31SNicolin Chen  */
fsl_asrc_isr(int irq,void * dev_id)10113117bb31SNicolin Chen static irqreturn_t fsl_asrc_isr(int irq, void *dev_id)
10123117bb31SNicolin Chen {
10137470704dSShengjiu Wang 	struct fsl_asrc *asrc = (struct fsl_asrc *)dev_id;
10147470704dSShengjiu Wang 	struct device *dev = &asrc->pdev->dev;
10153117bb31SNicolin Chen 	enum asrc_pair_index index;
10163117bb31SNicolin Chen 	u32 status;
10173117bb31SNicolin Chen 
10187470704dSShengjiu Wang 	regmap_read(asrc->regmap, REG_ASRSTR, &status);
10193117bb31SNicolin Chen 
10203117bb31SNicolin Chen 	/* Clean overload error */
10217470704dSShengjiu Wang 	regmap_write(asrc->regmap, REG_ASRSTR, ASRSTR_AOLE);
10223117bb31SNicolin Chen 
10233117bb31SNicolin Chen 	/*
10243117bb31SNicolin Chen 	 * We here use dev_dbg() for all exceptions because ASRC itself does
10253117bb31SNicolin Chen 	 * not care if FIFO overflowed or underrun while a warning in the
10263117bb31SNicolin Chen 	 * interrupt would result a ridged conversion.
10273117bb31SNicolin Chen 	 */
10283117bb31SNicolin Chen 	for (index = ASRC_PAIR_A; index < ASRC_PAIR_MAX_NUM; index++) {
10297470704dSShengjiu Wang 		if (!asrc->pair[index])
10303117bb31SNicolin Chen 			continue;
10313117bb31SNicolin Chen 
10323117bb31SNicolin Chen 		if (status & ASRSTR_ATQOL) {
10337470704dSShengjiu Wang 			asrc->pair[index]->error |= ASRC_TASK_Q_OVERLOAD;
10343117bb31SNicolin Chen 			dev_dbg(dev, "ASRC Task Queue FIFO overload\n");
10353117bb31SNicolin Chen 		}
10363117bb31SNicolin Chen 
10373117bb31SNicolin Chen 		if (status & ASRSTR_AOOL(index)) {
10387470704dSShengjiu Wang 			asrc->pair[index]->error |= ASRC_OUTPUT_TASK_OVERLOAD;
10393117bb31SNicolin Chen 			pair_dbg("Output Task Overload\n");
10403117bb31SNicolin Chen 		}
10413117bb31SNicolin Chen 
10423117bb31SNicolin Chen 		if (status & ASRSTR_AIOL(index)) {
10437470704dSShengjiu Wang 			asrc->pair[index]->error |= ASRC_INPUT_TASK_OVERLOAD;
10443117bb31SNicolin Chen 			pair_dbg("Input Task Overload\n");
10453117bb31SNicolin Chen 		}
10463117bb31SNicolin Chen 
10473117bb31SNicolin Chen 		if (status & ASRSTR_AODO(index)) {
10487470704dSShengjiu Wang 			asrc->pair[index]->error |= ASRC_OUTPUT_BUFFER_OVERFLOW;
10493117bb31SNicolin Chen 			pair_dbg("Output Data Buffer has overflowed\n");
10503117bb31SNicolin Chen 		}
10513117bb31SNicolin Chen 
10523117bb31SNicolin Chen 		if (status & ASRSTR_AIDU(index)) {
10537470704dSShengjiu Wang 			asrc->pair[index]->error |= ASRC_INPUT_BUFFER_UNDERRUN;
10543117bb31SNicolin Chen 			pair_dbg("Input Data Buffer has underflowed\n");
10553117bb31SNicolin Chen 		}
10563117bb31SNicolin Chen 	}
10573117bb31SNicolin Chen 
10583117bb31SNicolin Chen 	return IRQ_HANDLED;
10593117bb31SNicolin Chen }
10603117bb31SNicolin Chen 
fsl_asrc_get_fifo_addr(u8 dir,enum asrc_pair_index index)1061be7bd03fSShengjiu Wang static int fsl_asrc_get_fifo_addr(u8 dir, enum asrc_pair_index index)
1062be7bd03fSShengjiu Wang {
1063be7bd03fSShengjiu Wang 	return REG_ASRDx(dir, index);
1064be7bd03fSShengjiu Wang }
1065be7bd03fSShengjiu Wang 
1066cab04ab5SShengjiu Wang static int fsl_asrc_runtime_resume(struct device *dev);
1067cab04ab5SShengjiu Wang static int fsl_asrc_runtime_suspend(struct device *dev);
1068cab04ab5SShengjiu Wang 
fsl_asrc_probe(struct platform_device * pdev)10693117bb31SNicolin Chen static int fsl_asrc_probe(struct platform_device *pdev)
10703117bb31SNicolin Chen {
10713117bb31SNicolin Chen 	struct device_node *np = pdev->dev.of_node;
1072be7bd03fSShengjiu Wang 	struct fsl_asrc_priv *asrc_priv;
10737470704dSShengjiu Wang 	struct fsl_asrc *asrc;
10743117bb31SNicolin Chen 	struct resource *res;
10753117bb31SNicolin Chen 	void __iomem *regs;
10763117bb31SNicolin Chen 	int irq, ret, i;
1077c4993272SShengjiu Wang 	u32 asrc_fmt = 0;
1078c05f10f2SShengjiu Wang 	u32 map_idx;
10793117bb31SNicolin Chen 	char tmp[16];
10804520af41SShengjiu Wang 	u32 width;
10813117bb31SNicolin Chen 
10827470704dSShengjiu Wang 	asrc = devm_kzalloc(&pdev->dev, sizeof(*asrc), GFP_KERNEL);
10837470704dSShengjiu Wang 	if (!asrc)
10843117bb31SNicolin Chen 		return -ENOMEM;
10853117bb31SNicolin Chen 
1086be7bd03fSShengjiu Wang 	asrc_priv = devm_kzalloc(&pdev->dev, sizeof(*asrc_priv), GFP_KERNEL);
1087be7bd03fSShengjiu Wang 	if (!asrc_priv)
1088be7bd03fSShengjiu Wang 		return -ENOMEM;
1089be7bd03fSShengjiu Wang 
10907470704dSShengjiu Wang 	asrc->pdev = pdev;
1091be7bd03fSShengjiu Wang 	asrc->private = asrc_priv;
10923117bb31SNicolin Chen 
10933117bb31SNicolin Chen 	/* Get the addresses and IRQ */
1094c66d7621SYang Yingliang 	regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
10953117bb31SNicolin Chen 	if (IS_ERR(regs))
10963117bb31SNicolin Chen 		return PTR_ERR(regs);
10973117bb31SNicolin Chen 
10987470704dSShengjiu Wang 	asrc->paddr = res->start;
10993117bb31SNicolin Chen 
1100cab04ab5SShengjiu Wang 	asrc->regmap = devm_regmap_init_mmio(&pdev->dev, regs, &fsl_asrc_regmap_config);
11017470704dSShengjiu Wang 	if (IS_ERR(asrc->regmap)) {
11023117bb31SNicolin Chen 		dev_err(&pdev->dev, "failed to init regmap\n");
11037470704dSShengjiu Wang 		return PTR_ERR(asrc->regmap);
11043117bb31SNicolin Chen 	}
11053117bb31SNicolin Chen 
11063117bb31SNicolin Chen 	irq = platform_get_irq(pdev, 0);
1107cf9441adSStephen Boyd 	if (irq < 0)
11083117bb31SNicolin Chen 		return irq;
11093117bb31SNicolin Chen 
11103117bb31SNicolin Chen 	ret = devm_request_irq(&pdev->dev, irq, fsl_asrc_isr, 0,
11117470704dSShengjiu Wang 			       dev_name(&pdev->dev), asrc);
11123117bb31SNicolin Chen 	if (ret) {
11133117bb31SNicolin Chen 		dev_err(&pdev->dev, "failed to claim irq %u: %d\n", irq, ret);
11143117bb31SNicolin Chen 		return ret;
11153117bb31SNicolin Chen 	}
11163117bb31SNicolin Chen 
11177470704dSShengjiu Wang 	asrc->mem_clk = devm_clk_get(&pdev->dev, "mem");
11187470704dSShengjiu Wang 	if (IS_ERR(asrc->mem_clk)) {
11193117bb31SNicolin Chen 		dev_err(&pdev->dev, "failed to get mem clock\n");
11207470704dSShengjiu Wang 		return PTR_ERR(asrc->mem_clk);
11213117bb31SNicolin Chen 	}
11223117bb31SNicolin Chen 
11237470704dSShengjiu Wang 	asrc->ipg_clk = devm_clk_get(&pdev->dev, "ipg");
11247470704dSShengjiu Wang 	if (IS_ERR(asrc->ipg_clk)) {
11253117bb31SNicolin Chen 		dev_err(&pdev->dev, "failed to get ipg clock\n");
11267470704dSShengjiu Wang 		return PTR_ERR(asrc->ipg_clk);
11273117bb31SNicolin Chen 	}
11283117bb31SNicolin Chen 
11297470704dSShengjiu Wang 	asrc->spba_clk = devm_clk_get(&pdev->dev, "spba");
11307470704dSShengjiu Wang 	if (IS_ERR(asrc->spba_clk))
113113b8a97aSShengjiu Wang 		dev_warn(&pdev->dev, "failed to get spba clock\n");
113213b8a97aSShengjiu Wang 
11333117bb31SNicolin Chen 	for (i = 0; i < ASRC_CLK_MAX_NUM; i++) {
11343117bb31SNicolin Chen 		sprintf(tmp, "asrck_%x", i);
1135be7bd03fSShengjiu Wang 		asrc_priv->asrck_clk[i] = devm_clk_get(&pdev->dev, tmp);
1136be7bd03fSShengjiu Wang 		if (IS_ERR(asrc_priv->asrck_clk[i])) {
11373117bb31SNicolin Chen 			dev_err(&pdev->dev, "failed to get %s clock\n", tmp);
1138be7bd03fSShengjiu Wang 			return PTR_ERR(asrc_priv->asrck_clk[i]);
11393117bb31SNicolin Chen 		}
11403117bb31SNicolin Chen 	}
11413117bb31SNicolin Chen 
1142be7bd03fSShengjiu Wang 	asrc_priv->soc = of_device_get_match_data(&pdev->dev);
1143be7bd03fSShengjiu Wang 	asrc->use_edma = asrc_priv->soc->use_edma;
1144be7bd03fSShengjiu Wang 	asrc->get_dma_channel = fsl_asrc_get_dma_channel;
1145be7bd03fSShengjiu Wang 	asrc->request_pair = fsl_asrc_request_pair;
1146be7bd03fSShengjiu Wang 	asrc->release_pair = fsl_asrc_release_pair;
1147be7bd03fSShengjiu Wang 	asrc->get_fifo_addr = fsl_asrc_get_fifo_addr;
1148be7bd03fSShengjiu Wang 	asrc->pair_priv_size = sizeof(struct fsl_asrc_pair_priv);
1149be7bd03fSShengjiu Wang 
1150f3d8ac8cSFabio Estevam 	if (of_device_is_compatible(np, "fsl,imx35-asrc")) {
1151be7bd03fSShengjiu Wang 		asrc_priv->clk_map[IN] = input_clk_map_imx35;
1152be7bd03fSShengjiu Wang 		asrc_priv->clk_map[OUT] = output_clk_map_imx35;
1153c05f10f2SShengjiu Wang 	} else if (of_device_is_compatible(np, "fsl,imx53-asrc")) {
1154be7bd03fSShengjiu Wang 		asrc_priv->clk_map[IN] = input_clk_map_imx53;
1155be7bd03fSShengjiu Wang 		asrc_priv->clk_map[OUT] = output_clk_map_imx53;
1156c05f10f2SShengjiu Wang 	} else if (of_device_is_compatible(np, "fsl,imx8qm-asrc") ||
1157c05f10f2SShengjiu Wang 		   of_device_is_compatible(np, "fsl,imx8qxp-asrc")) {
1158c05f10f2SShengjiu Wang 		ret = of_property_read_u32(np, "fsl,asrc-clk-map", &map_idx);
1159c05f10f2SShengjiu Wang 		if (ret) {
1160c05f10f2SShengjiu Wang 			dev_err(&pdev->dev, "failed to get clk map index\n");
1161c05f10f2SShengjiu Wang 			return ret;
1162c05f10f2SShengjiu Wang 		}
1163c05f10f2SShengjiu Wang 
1164c05f10f2SShengjiu Wang 		if (map_idx > 1) {
1165c05f10f2SShengjiu Wang 			dev_err(&pdev->dev, "unsupported clk map index\n");
1166c05f10f2SShengjiu Wang 			return -EINVAL;
1167c05f10f2SShengjiu Wang 		}
1168c05f10f2SShengjiu Wang 		if (of_device_is_compatible(np, "fsl,imx8qm-asrc")) {
1169be7bd03fSShengjiu Wang 			asrc_priv->clk_map[IN] = clk_map_imx8qm[map_idx];
1170be7bd03fSShengjiu Wang 			asrc_priv->clk_map[OUT] = clk_map_imx8qm[map_idx];
11713117bb31SNicolin Chen 		} else {
1172be7bd03fSShengjiu Wang 			asrc_priv->clk_map[IN] = clk_map_imx8qxp[map_idx];
1173be7bd03fSShengjiu Wang 			asrc_priv->clk_map[OUT] = clk_map_imx8qxp[map_idx];
1174c05f10f2SShengjiu Wang 		}
11753117bb31SNicolin Chen 	}
11763117bb31SNicolin Chen 
11777470704dSShengjiu Wang 	asrc->channel_avail = 10;
11783117bb31SNicolin Chen 
11793117bb31SNicolin Chen 	ret = of_property_read_u32(np, "fsl,asrc-rate",
11807470704dSShengjiu Wang 				   &asrc->asrc_rate);
11813117bb31SNicolin Chen 	if (ret) {
11823117bb31SNicolin Chen 		dev_err(&pdev->dev, "failed to get output rate\n");
1183c0296950SFabio Estevam 		return ret;
11843117bb31SNicolin Chen 	}
11853117bb31SNicolin Chen 
1186c4993272SShengjiu Wang 	ret = of_property_read_u32(np, "fsl,asrc-format", &asrc_fmt);
1187c4993272SShengjiu Wang 	asrc->asrc_format = (__force snd_pcm_format_t)asrc_fmt;
11883117bb31SNicolin Chen 	if (ret) {
11894520af41SShengjiu Wang 		ret = of_property_read_u32(np, "fsl,asrc-width", &width);
11904520af41SShengjiu Wang 		if (ret) {
11914520af41SShengjiu Wang 			dev_err(&pdev->dev, "failed to decide output format\n");
1192c0296950SFabio Estevam 			return ret;
11933117bb31SNicolin Chen 		}
11943117bb31SNicolin Chen 
11954520af41SShengjiu Wang 		switch (width) {
11964520af41SShengjiu Wang 		case 16:
11974520af41SShengjiu Wang 			asrc->asrc_format = SNDRV_PCM_FORMAT_S16_LE;
11984520af41SShengjiu Wang 			break;
11994520af41SShengjiu Wang 		case 24:
12004520af41SShengjiu Wang 			asrc->asrc_format = SNDRV_PCM_FORMAT_S24_LE;
12014520af41SShengjiu Wang 			break;
12024520af41SShengjiu Wang 		default:
12034520af41SShengjiu Wang 			dev_warn(&pdev->dev,
12044520af41SShengjiu Wang 				 "unsupported width, use default S24_LE\n");
12054520af41SShengjiu Wang 			asrc->asrc_format = SNDRV_PCM_FORMAT_S24_LE;
12064520af41SShengjiu Wang 			break;
12074520af41SShengjiu Wang 		}
12084520af41SShengjiu Wang 	}
12094520af41SShengjiu Wang 
1210c4993272SShengjiu Wang 	if (!(FSL_ASRC_FORMATS & pcm_format_to_bits(asrc->asrc_format))) {
12114520af41SShengjiu Wang 		dev_warn(&pdev->dev, "unsupported width, use default S24_LE\n");
12124520af41SShengjiu Wang 		asrc->asrc_format = SNDRV_PCM_FORMAT_S24_LE;
12133117bb31SNicolin Chen 	}
12143117bb31SNicolin Chen 
12157470704dSShengjiu Wang 	platform_set_drvdata(pdev, asrc);
12167470704dSShengjiu Wang 	spin_lock_init(&asrc->lock);
1217cab04ab5SShengjiu Wang 	pm_runtime_enable(&pdev->dev);
1218cab04ab5SShengjiu Wang 	if (!pm_runtime_enabled(&pdev->dev)) {
1219cab04ab5SShengjiu Wang 		ret = fsl_asrc_runtime_resume(&pdev->dev);
1220cab04ab5SShengjiu Wang 		if (ret)
1221cab04ab5SShengjiu Wang 			goto err_pm_disable;
1222cab04ab5SShengjiu Wang 	}
1223cab04ab5SShengjiu Wang 
1224d0504074SMinghao Chi 	ret = pm_runtime_resume_and_get(&pdev->dev);
1225d0504074SMinghao Chi 	if (ret < 0)
1226cab04ab5SShengjiu Wang 		goto err_pm_get_sync;
1227cab04ab5SShengjiu Wang 
1228cab04ab5SShengjiu Wang 	ret = fsl_asrc_init(asrc);
1229cab04ab5SShengjiu Wang 	if (ret) {
1230cab04ab5SShengjiu Wang 		dev_err(&pdev->dev, "failed to init asrc %d\n", ret);
1231cab04ab5SShengjiu Wang 		goto err_pm_get_sync;
1232cab04ab5SShengjiu Wang 	}
1233cab04ab5SShengjiu Wang 
1234cab04ab5SShengjiu Wang 	ret = pm_runtime_put_sync(&pdev->dev);
12356a564338SMaarten Zanders 	if (ret < 0 && ret != -ENOSYS)
1236cab04ab5SShengjiu Wang 		goto err_pm_get_sync;
12373117bb31SNicolin Chen 
12383117bb31SNicolin Chen 	ret = devm_snd_soc_register_component(&pdev->dev, &fsl_asrc_component,
12393117bb31SNicolin Chen 					      &fsl_asrc_dai, 1);
12403117bb31SNicolin Chen 	if (ret) {
12413117bb31SNicolin Chen 		dev_err(&pdev->dev, "failed to register ASoC DAI\n");
1242cab04ab5SShengjiu Wang 		goto err_pm_get_sync;
1243cab04ab5SShengjiu Wang 	}
1244cab04ab5SShengjiu Wang 
1245cab04ab5SShengjiu Wang 	return 0;
1246cab04ab5SShengjiu Wang 
1247cab04ab5SShengjiu Wang err_pm_get_sync:
1248cab04ab5SShengjiu Wang 	if (!pm_runtime_status_suspended(&pdev->dev))
1249cab04ab5SShengjiu Wang 		fsl_asrc_runtime_suspend(&pdev->dev);
1250cab04ab5SShengjiu Wang err_pm_disable:
1251cab04ab5SShengjiu Wang 	pm_runtime_disable(&pdev->dev);
12523117bb31SNicolin Chen 	return ret;
12533117bb31SNicolin Chen }
12543117bb31SNicolin Chen 
fsl_asrc_remove(struct platform_device * pdev)12557a2d15b9SUwe Kleine-König static void fsl_asrc_remove(struct platform_device *pdev)
1256cab04ab5SShengjiu Wang {
1257cab04ab5SShengjiu Wang 	pm_runtime_disable(&pdev->dev);
1258cab04ab5SShengjiu Wang 	if (!pm_runtime_status_suspended(&pdev->dev))
1259cab04ab5SShengjiu Wang 		fsl_asrc_runtime_suspend(&pdev->dev);
12603117bb31SNicolin Chen }
12613117bb31SNicolin Chen 
fsl_asrc_runtime_resume(struct device * dev)12623117bb31SNicolin Chen static int fsl_asrc_runtime_resume(struct device *dev)
12633117bb31SNicolin Chen {
12647470704dSShengjiu Wang 	struct fsl_asrc *asrc = dev_get_drvdata(dev);
1265be7bd03fSShengjiu Wang 	struct fsl_asrc_priv *asrc_priv = asrc->private;
1266d2de3f5eSShengjiu Wang 	int reg, retry = INIT_RETRY_NUM;
1267b1ade0f2SFabio Estevam 	int i, ret;
1268393dc21dSShengjiu Wang 	u32 asrctr;
12693117bb31SNicolin Chen 
12707470704dSShengjiu Wang 	ret = clk_prepare_enable(asrc->mem_clk);
1271b1ade0f2SFabio Estevam 	if (ret)
1272b1ade0f2SFabio Estevam 		return ret;
12737470704dSShengjiu Wang 	ret = clk_prepare_enable(asrc->ipg_clk);
1274b1ade0f2SFabio Estevam 	if (ret)
1275b1ade0f2SFabio Estevam 		goto disable_mem_clk;
12767470704dSShengjiu Wang 	if (!IS_ERR(asrc->spba_clk)) {
12777470704dSShengjiu Wang 		ret = clk_prepare_enable(asrc->spba_clk);
127813b8a97aSShengjiu Wang 		if (ret)
127913b8a97aSShengjiu Wang 			goto disable_ipg_clk;
128013b8a97aSShengjiu Wang 	}
1281b1ade0f2SFabio Estevam 	for (i = 0; i < ASRC_CLK_MAX_NUM; i++) {
1282be7bd03fSShengjiu Wang 		ret = clk_prepare_enable(asrc_priv->asrck_clk[i]);
1283b1ade0f2SFabio Estevam 		if (ret)
1284b1ade0f2SFabio Estevam 			goto disable_asrck_clk;
1285b1ade0f2SFabio Estevam 	}
12863117bb31SNicolin Chen 
1287393dc21dSShengjiu Wang 	/* Stop all pairs provisionally */
1288393dc21dSShengjiu Wang 	regmap_read(asrc->regmap, REG_ASRCTR, &asrctr);
1289393dc21dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCTR,
1290393dc21dSShengjiu Wang 			   ASRCTR_ASRCEi_ALL_MASK, 0);
1291393dc21dSShengjiu Wang 
1292393dc21dSShengjiu Wang 	/* Restore all registers */
1293393dc21dSShengjiu Wang 	regcache_cache_only(asrc->regmap, false);
1294393dc21dSShengjiu Wang 	regcache_mark_dirty(asrc->regmap);
1295393dc21dSShengjiu Wang 	regcache_sync(asrc->regmap);
1296393dc21dSShengjiu Wang 
1297393dc21dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCFG,
1298393dc21dSShengjiu Wang 			   ASRCFG_NDPRi_ALL_MASK | ASRCFG_POSTMODi_ALL_MASK |
1299393dc21dSShengjiu Wang 			   ASRCFG_PREMODi_ALL_MASK, asrc_priv->regcache_cfg);
1300393dc21dSShengjiu Wang 
1301393dc21dSShengjiu Wang 	/* Restart enabled pairs */
1302393dc21dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCTR,
1303393dc21dSShengjiu Wang 			   ASRCTR_ASRCEi_ALL_MASK, asrctr);
1304393dc21dSShengjiu Wang 
1305d2de3f5eSShengjiu Wang 	/* Wait for status of initialization for all enabled pairs */
1306d2de3f5eSShengjiu Wang 	do {
1307d2de3f5eSShengjiu Wang 		udelay(5);
1308d2de3f5eSShengjiu Wang 		regmap_read(asrc->regmap, REG_ASRCFG, &reg);
1309d2de3f5eSShengjiu Wang 		reg = (reg >> ASRCFG_INIRQi_SHIFT(0)) & 0x7;
1310d2de3f5eSShengjiu Wang 	} while ((reg != ((asrctr >> ASRCTR_ASRCEi_SHIFT(0)) & 0x7)) && --retry);
1311d2de3f5eSShengjiu Wang 
1312d2de3f5eSShengjiu Wang 	/*
1313d2de3f5eSShengjiu Wang 	 * NOTE: Doesn't treat initialization timeout as an error
1314d2de3f5eSShengjiu Wang 	 * Some of the pairs may success, then still can continue.
1315d2de3f5eSShengjiu Wang 	 */
1316d2de3f5eSShengjiu Wang 	if (!retry) {
1317d2de3f5eSShengjiu Wang 		for (i = ASRC_PAIR_A; i < ASRC_PAIR_MAX_NUM; i++) {
1318d2de3f5eSShengjiu Wang 			if ((asrctr & ASRCTR_ASRCEi_MASK(i)) && !(reg & (1 << i)))
1319d2de3f5eSShengjiu Wang 				dev_warn(dev, "Pair %c initialization isn't finished\n", 'A' + i);
1320d2de3f5eSShengjiu Wang 		}
1321d2de3f5eSShengjiu Wang 	}
1322d2de3f5eSShengjiu Wang 
13233117bb31SNicolin Chen 	return 0;
1324b1ade0f2SFabio Estevam 
1325b1ade0f2SFabio Estevam disable_asrck_clk:
1326b1ade0f2SFabio Estevam 	for (i--; i >= 0; i--)
1327be7bd03fSShengjiu Wang 		clk_disable_unprepare(asrc_priv->asrck_clk[i]);
13287470704dSShengjiu Wang 	if (!IS_ERR(asrc->spba_clk))
13297470704dSShengjiu Wang 		clk_disable_unprepare(asrc->spba_clk);
133013b8a97aSShengjiu Wang disable_ipg_clk:
13317470704dSShengjiu Wang 	clk_disable_unprepare(asrc->ipg_clk);
1332b1ade0f2SFabio Estevam disable_mem_clk:
13337470704dSShengjiu Wang 	clk_disable_unprepare(asrc->mem_clk);
1334b1ade0f2SFabio Estevam 	return ret;
13353117bb31SNicolin Chen }
13363117bb31SNicolin Chen 
fsl_asrc_runtime_suspend(struct device * dev)13373117bb31SNicolin Chen static int fsl_asrc_runtime_suspend(struct device *dev)
13383117bb31SNicolin Chen {
13397470704dSShengjiu Wang 	struct fsl_asrc *asrc = dev_get_drvdata(dev);
1340be7bd03fSShengjiu Wang 	struct fsl_asrc_priv *asrc_priv = asrc->private;
13413117bb31SNicolin Chen 	int i;
13423117bb31SNicolin Chen 
1343393dc21dSShengjiu Wang 	regmap_read(asrc->regmap, REG_ASRCFG,
1344393dc21dSShengjiu Wang 		    &asrc_priv->regcache_cfg);
1345393dc21dSShengjiu Wang 
1346393dc21dSShengjiu Wang 	regcache_cache_only(asrc->regmap, true);
1347393dc21dSShengjiu Wang 
13483117bb31SNicolin Chen 	for (i = 0; i < ASRC_CLK_MAX_NUM; i++)
1349be7bd03fSShengjiu Wang 		clk_disable_unprepare(asrc_priv->asrck_clk[i]);
13507470704dSShengjiu Wang 	if (!IS_ERR(asrc->spba_clk))
13517470704dSShengjiu Wang 		clk_disable_unprepare(asrc->spba_clk);
13527470704dSShengjiu Wang 	clk_disable_unprepare(asrc->ipg_clk);
13537470704dSShengjiu Wang 	clk_disable_unprepare(asrc->mem_clk);
13543117bb31SNicolin Chen 
13553117bb31SNicolin Chen 	return 0;
13563117bb31SNicolin Chen }
13573117bb31SNicolin Chen 
13583117bb31SNicolin Chen static const struct dev_pm_ops fsl_asrc_pm = {
13593117bb31SNicolin Chen 	SET_RUNTIME_PM_OPS(fsl_asrc_runtime_suspend, fsl_asrc_runtime_resume, NULL)
1360393dc21dSShengjiu Wang 	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
1361393dc21dSShengjiu Wang 				pm_runtime_force_resume)
13623117bb31SNicolin Chen };
13633117bb31SNicolin Chen 
1364c05f10f2SShengjiu Wang static const struct fsl_asrc_soc_data fsl_asrc_imx35_data = {
1365c05f10f2SShengjiu Wang 	.use_edma = false,
1366c05f10f2SShengjiu Wang 	.channel_bits = 3,
1367c05f10f2SShengjiu Wang };
1368c05f10f2SShengjiu Wang 
1369c05f10f2SShengjiu Wang static const struct fsl_asrc_soc_data fsl_asrc_imx53_data = {
1370c05f10f2SShengjiu Wang 	.use_edma = false,
1371c05f10f2SShengjiu Wang 	.channel_bits = 4,
1372c05f10f2SShengjiu Wang };
1373c05f10f2SShengjiu Wang 
1374c05f10f2SShengjiu Wang static const struct fsl_asrc_soc_data fsl_asrc_imx8qm_data = {
1375c05f10f2SShengjiu Wang 	.use_edma = true,
1376c05f10f2SShengjiu Wang 	.channel_bits = 4,
1377c05f10f2SShengjiu Wang };
1378c05f10f2SShengjiu Wang 
1379c05f10f2SShengjiu Wang static const struct fsl_asrc_soc_data fsl_asrc_imx8qxp_data = {
1380c05f10f2SShengjiu Wang 	.use_edma = true,
1381c05f10f2SShengjiu Wang 	.channel_bits = 4,
1382c05f10f2SShengjiu Wang };
1383c05f10f2SShengjiu Wang 
13843117bb31SNicolin Chen static const struct of_device_id fsl_asrc_ids[] = {
1385c05f10f2SShengjiu Wang 	{ .compatible = "fsl,imx35-asrc", .data = &fsl_asrc_imx35_data },
1386c05f10f2SShengjiu Wang 	{ .compatible = "fsl,imx53-asrc", .data = &fsl_asrc_imx53_data },
1387c05f10f2SShengjiu Wang 	{ .compatible = "fsl,imx8qm-asrc", .data = &fsl_asrc_imx8qm_data },
1388c05f10f2SShengjiu Wang 	{ .compatible = "fsl,imx8qxp-asrc", .data = &fsl_asrc_imx8qxp_data },
13893117bb31SNicolin Chen 	{}
13903117bb31SNicolin Chen };
13913117bb31SNicolin Chen MODULE_DEVICE_TABLE(of, fsl_asrc_ids);
13923117bb31SNicolin Chen 
13933117bb31SNicolin Chen static struct platform_driver fsl_asrc_driver = {
13943117bb31SNicolin Chen 	.probe = fsl_asrc_probe,
13957a2d15b9SUwe Kleine-König 	.remove_new = fsl_asrc_remove,
13963117bb31SNicolin Chen 	.driver = {
13973117bb31SNicolin Chen 		.name = "fsl-asrc",
13983117bb31SNicolin Chen 		.of_match_table = fsl_asrc_ids,
13993117bb31SNicolin Chen 		.pm = &fsl_asrc_pm,
14003117bb31SNicolin Chen 	},
14013117bb31SNicolin Chen };
14023117bb31SNicolin Chen module_platform_driver(fsl_asrc_driver);
14033117bb31SNicolin Chen 
14043117bb31SNicolin Chen MODULE_DESCRIPTION("Freescale ASRC ASoC driver");
14053117bb31SNicolin Chen MODULE_AUTHOR("Nicolin Chen <nicoleotsuka@gmail.com>");
14063117bb31SNicolin Chen MODULE_ALIAS("platform:fsl-asrc");
14073117bb31SNicolin Chen MODULE_LICENSE("GPL v2");
1408