xref: /openbmc/linux/sound/soc/fsl/fsl_asrc.c (revision 4520af41fd21863d026d53c7e1eb987509cb3c24)
12ba28053SFabio Estevam // SPDX-License-Identifier: GPL-2.0
22ba28053SFabio Estevam //
32ba28053SFabio Estevam // Freescale ASRC ALSA SoC Digital Audio Interface (DAI) driver
42ba28053SFabio Estevam //
52ba28053SFabio Estevam // Copyright (C) 2014 Freescale Semiconductor, Inc.
62ba28053SFabio Estevam //
72ba28053SFabio Estevam // Author: Nicolin Chen <nicoleotsuka@gmail.com>
83117bb31SNicolin Chen 
93117bb31SNicolin Chen #include <linux/clk.h>
103117bb31SNicolin Chen #include <linux/delay.h>
113117bb31SNicolin Chen #include <linux/dma-mapping.h>
123117bb31SNicolin Chen #include <linux/module.h>
133117bb31SNicolin Chen #include <linux/of_platform.h>
143117bb31SNicolin Chen #include <linux/platform_data/dma-imx.h>
153117bb31SNicolin Chen #include <linux/pm_runtime.h>
163117bb31SNicolin Chen #include <sound/dmaengine_pcm.h>
173117bb31SNicolin Chen #include <sound/pcm_params.h>
183117bb31SNicolin Chen 
193117bb31SNicolin Chen #include "fsl_asrc.h"
203117bb31SNicolin Chen 
213117bb31SNicolin Chen #define IDEAL_RATIO_DECIMAL_DEPTH 26
223117bb31SNicolin Chen 
233117bb31SNicolin Chen #define pair_err(fmt, ...) \
247470704dSShengjiu Wang 	dev_err(&asrc->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__)
253117bb31SNicolin Chen 
263117bb31SNicolin Chen #define pair_dbg(fmt, ...) \
277470704dSShengjiu Wang 	dev_dbg(&asrc->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__)
283117bb31SNicolin Chen 
293117bb31SNicolin Chen /* Corresponding to process_option */
30d281bf5dSS.j. Wang static unsigned int supported_asrc_rate[] = {
31d281bf5dSS.j. Wang 	5512, 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000,
32d281bf5dSS.j. Wang 	64000, 88200, 96000, 128000, 176400, 192000,
333117bb31SNicolin Chen };
343117bb31SNicolin Chen 
35d281bf5dSS.j. Wang static struct snd_pcm_hw_constraint_list fsl_asrc_rate_constraints = {
36d281bf5dSS.j. Wang 	.count = ARRAY_SIZE(supported_asrc_rate),
37d281bf5dSS.j. Wang 	.list = supported_asrc_rate,
383117bb31SNicolin Chen };
393117bb31SNicolin Chen 
403117bb31SNicolin Chen /**
413117bb31SNicolin Chen  * The following tables map the relationship between asrc_inclk/asrc_outclk in
423117bb31SNicolin Chen  * fsl_asrc.h and the registers of ASRCSR
433117bb31SNicolin Chen  */
44c05f10f2SShengjiu Wang static unsigned char input_clk_map_imx35[ASRC_CLK_MAP_LEN] = {
453117bb31SNicolin Chen 	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
46c05f10f2SShengjiu Wang 	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
47c05f10f2SShengjiu Wang 	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
483117bb31SNicolin Chen };
493117bb31SNicolin Chen 
50c05f10f2SShengjiu Wang static unsigned char output_clk_map_imx35[ASRC_CLK_MAP_LEN] = {
513117bb31SNicolin Chen 	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
52c05f10f2SShengjiu Wang 	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
53c05f10f2SShengjiu Wang 	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
543117bb31SNicolin Chen };
553117bb31SNicolin Chen 
563117bb31SNicolin Chen /* i.MX53 uses the same map for input and output */
57c05f10f2SShengjiu Wang static unsigned char input_clk_map_imx53[ASRC_CLK_MAP_LEN] = {
583117bb31SNicolin Chen /*	0x0  0x1  0x2  0x3  0x4  0x5  0x6  0x7  0x8  0x9  0xa  0xb  0xc  0xd  0xe  0xf */
593117bb31SNicolin Chen 	0x0, 0x1, 0x2, 0x7, 0x4, 0x5, 0x6, 0x3, 0x8, 0x9, 0xa, 0xb, 0xc, 0xf, 0xe, 0xd,
60c05f10f2SShengjiu Wang 	0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
61c05f10f2SShengjiu Wang 	0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
623117bb31SNicolin Chen };
633117bb31SNicolin Chen 
64c05f10f2SShengjiu Wang static unsigned char output_clk_map_imx53[ASRC_CLK_MAP_LEN] = {
653117bb31SNicolin Chen /*	0x0  0x1  0x2  0x3  0x4  0x5  0x6  0x7  0x8  0x9  0xa  0xb  0xc  0xd  0xe  0xf */
663117bb31SNicolin Chen 	0x8, 0x9, 0xa, 0x7, 0xc, 0x5, 0x6, 0xb, 0x0, 0x1, 0x2, 0x3, 0x4, 0xf, 0xe, 0xd,
67c05f10f2SShengjiu Wang 	0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
68c05f10f2SShengjiu Wang 	0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7,
693117bb31SNicolin Chen };
703117bb31SNicolin Chen 
71c05f10f2SShengjiu Wang /**
72c05f10f2SShengjiu Wang  * i.MX8QM/i.MX8QXP uses the same map for input and output.
73c05f10f2SShengjiu Wang  * clk_map_imx8qm[0] is for i.MX8QM asrc0
74c05f10f2SShengjiu Wang  * clk_map_imx8qm[1] is for i.MX8QM asrc1
75c05f10f2SShengjiu Wang  * clk_map_imx8qxp[0] is for i.MX8QXP asrc0
76c05f10f2SShengjiu Wang  * clk_map_imx8qxp[1] is for i.MX8QXP asrc1
77c05f10f2SShengjiu Wang  */
78c05f10f2SShengjiu Wang static unsigned char clk_map_imx8qm[2][ASRC_CLK_MAP_LEN] = {
79c05f10f2SShengjiu Wang 	{
80c05f10f2SShengjiu Wang 	0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x0,
81c05f10f2SShengjiu Wang 	0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
82c05f10f2SShengjiu Wang 	0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
83c05f10f2SShengjiu Wang 	},
84c05f10f2SShengjiu Wang 	{
85c05f10f2SShengjiu Wang 	0xf, 0xf, 0xf, 0xf, 0xf, 0x7, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x0,
86c05f10f2SShengjiu Wang 	0x0, 0x1, 0x2, 0x3, 0xb, 0xc, 0xf, 0xf, 0xd, 0xe, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
87c05f10f2SShengjiu Wang 	0x4, 0x5, 0x6, 0xf, 0x8, 0x9, 0xa, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
88c05f10f2SShengjiu Wang 	},
89c05f10f2SShengjiu Wang };
90c05f10f2SShengjiu Wang 
91c05f10f2SShengjiu Wang static unsigned char clk_map_imx8qxp[2][ASRC_CLK_MAP_LEN] = {
92c05f10f2SShengjiu Wang 	{
93c05f10f2SShengjiu Wang 	0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x0,
94c05f10f2SShengjiu Wang 	0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0xf, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xf, 0xf,
95c05f10f2SShengjiu Wang 	0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
96c05f10f2SShengjiu Wang 	},
97c05f10f2SShengjiu Wang 	{
98c05f10f2SShengjiu Wang 	0xf, 0xf, 0xf, 0xf, 0xf, 0x7, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x0,
99c05f10f2SShengjiu Wang 	0x0, 0x1, 0x2, 0x3, 0x7, 0x8, 0xf, 0xf, 0x9, 0xa, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
100c05f10f2SShengjiu Wang 	0xf, 0xf, 0x6, 0xf, 0xf, 0xf, 0xa, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf,
101c05f10f2SShengjiu Wang 	},
102c05f10f2SShengjiu Wang };
1033117bb31SNicolin Chen 
1043117bb31SNicolin Chen /**
1054aecaa0aSS.j. Wang  * Select the pre-processing and post-processing options
1064aecaa0aSS.j. Wang  * Make sure to exclude following unsupported cases before
1074aecaa0aSS.j. Wang  * calling this function:
1084aecaa0aSS.j. Wang  * 1) inrate > 8.125 * outrate
1094aecaa0aSS.j. Wang  * 2) inrate > 16.125 * outrate
1104aecaa0aSS.j. Wang  *
1114aecaa0aSS.j. Wang  * inrate: input sample rate
1124aecaa0aSS.j. Wang  * outrate: output sample rate
1134aecaa0aSS.j. Wang  * pre_proc: return value for pre-processing option
1144aecaa0aSS.j. Wang  * post_proc: return value for post-processing option
1154aecaa0aSS.j. Wang  */
1164aecaa0aSS.j. Wang static void fsl_asrc_sel_proc(int inrate, int outrate,
1174aecaa0aSS.j. Wang 			     int *pre_proc, int *post_proc)
1184aecaa0aSS.j. Wang {
1194aecaa0aSS.j. Wang 	bool post_proc_cond2;
1204aecaa0aSS.j. Wang 	bool post_proc_cond0;
1214aecaa0aSS.j. Wang 
1224aecaa0aSS.j. Wang 	/* select pre_proc between [0, 2] */
1234aecaa0aSS.j. Wang 	if (inrate * 8 > 33 * outrate)
1244aecaa0aSS.j. Wang 		*pre_proc = 2;
1254aecaa0aSS.j. Wang 	else if (inrate * 8 > 15 * outrate) {
1264aecaa0aSS.j. Wang 		if (inrate > 152000)
1274aecaa0aSS.j. Wang 			*pre_proc = 2;
1284aecaa0aSS.j. Wang 		else
1294aecaa0aSS.j. Wang 			*pre_proc = 1;
1304aecaa0aSS.j. Wang 	} else if (inrate < 76000)
1314aecaa0aSS.j. Wang 		*pre_proc = 0;
1324aecaa0aSS.j. Wang 	else if (inrate > 152000)
1334aecaa0aSS.j. Wang 		*pre_proc = 2;
1344aecaa0aSS.j. Wang 	else
1354aecaa0aSS.j. Wang 		*pre_proc = 1;
1364aecaa0aSS.j. Wang 
1374aecaa0aSS.j. Wang 	/* Condition for selection of post-processing */
1384aecaa0aSS.j. Wang 	post_proc_cond2 = (inrate * 15 > outrate * 16 && outrate < 56000) ||
1394aecaa0aSS.j. Wang 			  (inrate > 56000 && outrate < 56000);
1404aecaa0aSS.j. Wang 	post_proc_cond0 = inrate * 23 < outrate * 8;
1414aecaa0aSS.j. Wang 
1424aecaa0aSS.j. Wang 	if (post_proc_cond2)
1434aecaa0aSS.j. Wang 		*post_proc = 2;
1444aecaa0aSS.j. Wang 	else if (post_proc_cond0)
1454aecaa0aSS.j. Wang 		*post_proc = 0;
1464aecaa0aSS.j. Wang 	else
1474aecaa0aSS.j. Wang 		*post_proc = 1;
1484aecaa0aSS.j. Wang }
1494aecaa0aSS.j. Wang 
1504aecaa0aSS.j. Wang /**
1513117bb31SNicolin Chen  * Request ASRC pair
1523117bb31SNicolin Chen  *
1533117bb31SNicolin Chen  * It assigns pair by the order of A->C->B because allocation of pair B,
1543117bb31SNicolin Chen  * within range [ANCA, ANCA+ANCB-1], depends on the channels of pair A
1553117bb31SNicolin Chen  * while pair A and pair C are comparatively independent.
1563117bb31SNicolin Chen  */
157703df441SShengjiu Wang int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair)
1583117bb31SNicolin Chen {
1593117bb31SNicolin Chen 	enum asrc_pair_index index = ASRC_INVALID_PAIR;
1607470704dSShengjiu Wang 	struct fsl_asrc *asrc = pair->asrc;
1617470704dSShengjiu Wang 	struct device *dev = &asrc->pdev->dev;
1623117bb31SNicolin Chen 	unsigned long lock_flags;
1633117bb31SNicolin Chen 	int i, ret = 0;
1643117bb31SNicolin Chen 
1657470704dSShengjiu Wang 	spin_lock_irqsave(&asrc->lock, lock_flags);
1663117bb31SNicolin Chen 
1673117bb31SNicolin Chen 	for (i = ASRC_PAIR_A; i < ASRC_PAIR_MAX_NUM; i++) {
1687470704dSShengjiu Wang 		if (asrc->pair[i] != NULL)
1693117bb31SNicolin Chen 			continue;
1703117bb31SNicolin Chen 
1713117bb31SNicolin Chen 		index = i;
1723117bb31SNicolin Chen 
1733117bb31SNicolin Chen 		if (i != ASRC_PAIR_B)
1743117bb31SNicolin Chen 			break;
1753117bb31SNicolin Chen 	}
1763117bb31SNicolin Chen 
1773117bb31SNicolin Chen 	if (index == ASRC_INVALID_PAIR) {
1783117bb31SNicolin Chen 		dev_err(dev, "all pairs are busy now\n");
1793117bb31SNicolin Chen 		ret = -EBUSY;
1807470704dSShengjiu Wang 	} else if (asrc->channel_avail < channels) {
1813117bb31SNicolin Chen 		dev_err(dev, "can't afford required channels: %d\n", channels);
1823117bb31SNicolin Chen 		ret = -EINVAL;
1833117bb31SNicolin Chen 	} else {
1847470704dSShengjiu Wang 		asrc->channel_avail -= channels;
1857470704dSShengjiu Wang 		asrc->pair[index] = pair;
1863117bb31SNicolin Chen 		pair->channels = channels;
1873117bb31SNicolin Chen 		pair->index = index;
1883117bb31SNicolin Chen 	}
1893117bb31SNicolin Chen 
1907470704dSShengjiu Wang 	spin_unlock_irqrestore(&asrc->lock, lock_flags);
1913117bb31SNicolin Chen 
1923117bb31SNicolin Chen 	return ret;
1933117bb31SNicolin Chen }
1943117bb31SNicolin Chen 
1953117bb31SNicolin Chen /**
1963117bb31SNicolin Chen  * Release ASRC pair
1973117bb31SNicolin Chen  *
1987470704dSShengjiu Wang  * It clears the resource from asrc and releases the occupied channels.
1993117bb31SNicolin Chen  */
200703df441SShengjiu Wang void fsl_asrc_release_pair(struct fsl_asrc_pair *pair)
2013117bb31SNicolin Chen {
2027470704dSShengjiu Wang 	struct fsl_asrc *asrc = pair->asrc;
2033117bb31SNicolin Chen 	enum asrc_pair_index index = pair->index;
2043117bb31SNicolin Chen 	unsigned long lock_flags;
2053117bb31SNicolin Chen 
2063117bb31SNicolin Chen 	/* Make sure the pair is disabled */
2077470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCTR,
2083117bb31SNicolin Chen 			   ASRCTR_ASRCEi_MASK(index), 0);
2093117bb31SNicolin Chen 
2107470704dSShengjiu Wang 	spin_lock_irqsave(&asrc->lock, lock_flags);
2113117bb31SNicolin Chen 
2127470704dSShengjiu Wang 	asrc->channel_avail += pair->channels;
2137470704dSShengjiu Wang 	asrc->pair[index] = NULL;
2143117bb31SNicolin Chen 	pair->error = 0;
2153117bb31SNicolin Chen 
2167470704dSShengjiu Wang 	spin_unlock_irqrestore(&asrc->lock, lock_flags);
2173117bb31SNicolin Chen }
2183117bb31SNicolin Chen 
2193117bb31SNicolin Chen /**
2203117bb31SNicolin Chen  * Configure input and output thresholds
2213117bb31SNicolin Chen  */
2223117bb31SNicolin Chen static void fsl_asrc_set_watermarks(struct fsl_asrc_pair *pair, u32 in, u32 out)
2233117bb31SNicolin Chen {
2247470704dSShengjiu Wang 	struct fsl_asrc *asrc = pair->asrc;
2253117bb31SNicolin Chen 	enum asrc_pair_index index = pair->index;
2263117bb31SNicolin Chen 
2277470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRMCR(index),
2283117bb31SNicolin Chen 			   ASRMCRi_EXTTHRSHi_MASK |
2293117bb31SNicolin Chen 			   ASRMCRi_INFIFO_THRESHOLD_MASK |
2303117bb31SNicolin Chen 			   ASRMCRi_OUTFIFO_THRESHOLD_MASK,
2313117bb31SNicolin Chen 			   ASRMCRi_EXTTHRSHi |
2323117bb31SNicolin Chen 			   ASRMCRi_INFIFO_THRESHOLD(in) |
2333117bb31SNicolin Chen 			   ASRMCRi_OUTFIFO_THRESHOLD(out));
2343117bb31SNicolin Chen }
2353117bb31SNicolin Chen 
2363117bb31SNicolin Chen /**
2373117bb31SNicolin Chen  * Calculate the total divisor between asrck clock rate and sample rate
2383117bb31SNicolin Chen  *
2393117bb31SNicolin Chen  * It follows the formula clk_rate = samplerate * (2 ^ prescaler) * divider
2403117bb31SNicolin Chen  */
2413117bb31SNicolin Chen static u32 fsl_asrc_cal_asrck_divisor(struct fsl_asrc_pair *pair, u32 div)
2423117bb31SNicolin Chen {
2433117bb31SNicolin Chen 	u32 ps;
2443117bb31SNicolin Chen 
2453117bb31SNicolin Chen 	/* Calculate the divisors: prescaler [2^0, 2^7], divder [1, 8] */
2463117bb31SNicolin Chen 	for (ps = 0; div > 8; ps++)
2473117bb31SNicolin Chen 		div >>= 1;
2483117bb31SNicolin Chen 
2493117bb31SNicolin Chen 	return ((div - 1) << ASRCDRi_AxCPi_WIDTH) | ps;
2503117bb31SNicolin Chen }
2513117bb31SNicolin Chen 
2523117bb31SNicolin Chen /**
2533117bb31SNicolin Chen  * Calculate and set the ratio for Ideal Ratio mode only
2543117bb31SNicolin Chen  *
2553117bb31SNicolin Chen  * The ratio is a 32-bit fixed point value with 26 fractional bits.
2563117bb31SNicolin Chen  */
2573117bb31SNicolin Chen static int fsl_asrc_set_ideal_ratio(struct fsl_asrc_pair *pair,
2583117bb31SNicolin Chen 				    int inrate, int outrate)
2593117bb31SNicolin Chen {
2607470704dSShengjiu Wang 	struct fsl_asrc *asrc = pair->asrc;
2613117bb31SNicolin Chen 	enum asrc_pair_index index = pair->index;
2623117bb31SNicolin Chen 	unsigned long ratio;
2633117bb31SNicolin Chen 	int i;
2643117bb31SNicolin Chen 
2653117bb31SNicolin Chen 	if (!outrate) {
2663117bb31SNicolin Chen 		pair_err("output rate should not be zero\n");
2673117bb31SNicolin Chen 		return -EINVAL;
2683117bb31SNicolin Chen 	}
2693117bb31SNicolin Chen 
2703117bb31SNicolin Chen 	/* Calculate the intergal part of the ratio */
2713117bb31SNicolin Chen 	ratio = (inrate / outrate) << IDEAL_RATIO_DECIMAL_DEPTH;
2723117bb31SNicolin Chen 
2733117bb31SNicolin Chen 	/* ... and then the 26 depth decimal part */
2743117bb31SNicolin Chen 	inrate %= outrate;
2753117bb31SNicolin Chen 
2763117bb31SNicolin Chen 	for (i = 1; i <= IDEAL_RATIO_DECIMAL_DEPTH; i++) {
2773117bb31SNicolin Chen 		inrate <<= 1;
2783117bb31SNicolin Chen 
2793117bb31SNicolin Chen 		if (inrate < outrate)
2803117bb31SNicolin Chen 			continue;
2813117bb31SNicolin Chen 
2823117bb31SNicolin Chen 		ratio |= 1 << (IDEAL_RATIO_DECIMAL_DEPTH - i);
2833117bb31SNicolin Chen 		inrate -= outrate;
2843117bb31SNicolin Chen 
2853117bb31SNicolin Chen 		if (!inrate)
2863117bb31SNicolin Chen 			break;
2873117bb31SNicolin Chen 	}
2883117bb31SNicolin Chen 
2897470704dSShengjiu Wang 	regmap_write(asrc->regmap, REG_ASRIDRL(index), ratio);
2907470704dSShengjiu Wang 	regmap_write(asrc->regmap, REG_ASRIDRH(index), ratio >> 24);
2913117bb31SNicolin Chen 
2923117bb31SNicolin Chen 	return 0;
2933117bb31SNicolin Chen }
2943117bb31SNicolin Chen 
2953117bb31SNicolin Chen /**
2963117bb31SNicolin Chen  * Configure the assigned ASRC pair
2973117bb31SNicolin Chen  *
2983117bb31SNicolin Chen  * It configures those ASRC registers according to a configuration instance
2993117bb31SNicolin Chen  * of struct asrc_config which includes in/output sample rate, width, channel
3003117bb31SNicolin Chen  * and clock settings.
301b39eb1e2SShengjiu Wang  *
302b39eb1e2SShengjiu Wang  * Note:
303b39eb1e2SShengjiu Wang  * The ideal ratio configuration can work with a flexible clock rate setting.
304b39eb1e2SShengjiu Wang  * Using IDEAL_RATIO_RATE gives a faster converting speed but overloads ASRC.
305b39eb1e2SShengjiu Wang  * For a regular audio playback, the clock rate should not be slower than an
306b39eb1e2SShengjiu Wang  * clock rate aligning with the output sample rate; For a use case requiring
307b39eb1e2SShengjiu Wang  * faster conversion, set use_ideal_rate to have the faster speed.
3083117bb31SNicolin Chen  */
309b39eb1e2SShengjiu Wang static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool use_ideal_rate)
3103117bb31SNicolin Chen {
3113117bb31SNicolin Chen 	struct asrc_config *config = pair->config;
3127470704dSShengjiu Wang 	struct fsl_asrc *asrc = pair->asrc;
3133117bb31SNicolin Chen 	enum asrc_pair_index index = pair->index;
3144bf62571SShengjiu Wang 	enum asrc_word_width input_word_width;
3154bf62571SShengjiu Wang 	enum asrc_word_width output_word_width;
3164e13eb72SNicolin Chen 	u32 inrate, outrate, indiv, outdiv;
317b39eb1e2SShengjiu Wang 	u32 clk_index[2], div[2], rem[2];
318b39eb1e2SShengjiu Wang 	u64 clk_rate;
3193117bb31SNicolin Chen 	int in, out, channels;
3204aecaa0aSS.j. Wang 	int pre_proc, post_proc;
3213117bb31SNicolin Chen 	struct clk *clk;
3224e13eb72SNicolin Chen 	bool ideal;
3233117bb31SNicolin Chen 
3243117bb31SNicolin Chen 	if (!config) {
3253117bb31SNicolin Chen 		pair_err("invalid pair config\n");
3263117bb31SNicolin Chen 		return -EINVAL;
3273117bb31SNicolin Chen 	}
3283117bb31SNicolin Chen 
3293117bb31SNicolin Chen 	/* Validate channels */
3303117bb31SNicolin Chen 	if (config->channel_num < 1 || config->channel_num > 10) {
3313117bb31SNicolin Chen 		pair_err("does not support %d channels\n", config->channel_num);
3323117bb31SNicolin Chen 		return -EINVAL;
3333117bb31SNicolin Chen 	}
3343117bb31SNicolin Chen 
3354bf62571SShengjiu Wang 	switch (snd_pcm_format_width(config->input_format)) {
3364bf62571SShengjiu Wang 	case 8:
3374bf62571SShengjiu Wang 		input_word_width = ASRC_WIDTH_8_BIT;
3384bf62571SShengjiu Wang 		break;
3394bf62571SShengjiu Wang 	case 16:
3404bf62571SShengjiu Wang 		input_word_width = ASRC_WIDTH_16_BIT;
3414bf62571SShengjiu Wang 		break;
3424bf62571SShengjiu Wang 	case 24:
3434bf62571SShengjiu Wang 		input_word_width = ASRC_WIDTH_24_BIT;
3444bf62571SShengjiu Wang 		break;
3454bf62571SShengjiu Wang 	default:
3464bf62571SShengjiu Wang 		pair_err("does not support this input format, %d\n",
3474bf62571SShengjiu Wang 			 config->input_format);
3484bf62571SShengjiu Wang 		return -EINVAL;
3494bf62571SShengjiu Wang 	}
3504bf62571SShengjiu Wang 
3514bf62571SShengjiu Wang 	switch (snd_pcm_format_width(config->output_format)) {
3524bf62571SShengjiu Wang 	case 16:
3534bf62571SShengjiu Wang 		output_word_width = ASRC_WIDTH_16_BIT;
3544bf62571SShengjiu Wang 		break;
3554bf62571SShengjiu Wang 	case 24:
3564bf62571SShengjiu Wang 		output_word_width = ASRC_WIDTH_24_BIT;
3574bf62571SShengjiu Wang 		break;
3584bf62571SShengjiu Wang 	default:
3594bf62571SShengjiu Wang 		pair_err("does not support this output format, %d\n",
3604bf62571SShengjiu Wang 			 config->output_format);
3613117bb31SNicolin Chen 		return -EINVAL;
3623117bb31SNicolin Chen 	}
3633117bb31SNicolin Chen 
3644e13eb72SNicolin Chen 	inrate = config->input_sample_rate;
3654e13eb72SNicolin Chen 	outrate = config->output_sample_rate;
3664e13eb72SNicolin Chen 	ideal = config->inclk == INCLK_NONE;
3674e13eb72SNicolin Chen 
3683117bb31SNicolin Chen 	/* Validate input and output sample rates */
369d281bf5dSS.j. Wang 	for (in = 0; in < ARRAY_SIZE(supported_asrc_rate); in++)
370d281bf5dSS.j. Wang 		if (inrate == supported_asrc_rate[in])
3713117bb31SNicolin Chen 			break;
3723117bb31SNicolin Chen 
373d281bf5dSS.j. Wang 	if (in == ARRAY_SIZE(supported_asrc_rate)) {
3743117bb31SNicolin Chen 		pair_err("unsupported input sample rate: %dHz\n", inrate);
3753117bb31SNicolin Chen 		return -EINVAL;
3763117bb31SNicolin Chen 	}
3773117bb31SNicolin Chen 
3783117bb31SNicolin Chen 	for (out = 0; out < ARRAY_SIZE(supported_asrc_rate); out++)
3793117bb31SNicolin Chen 		if (outrate == supported_asrc_rate[out])
3803117bb31SNicolin Chen 			break;
3813117bb31SNicolin Chen 
3823117bb31SNicolin Chen 	if (out == ARRAY_SIZE(supported_asrc_rate)) {
3833117bb31SNicolin Chen 		pair_err("unsupported output sample rate: %dHz\n", outrate);
3843117bb31SNicolin Chen 		return -EINVAL;
3853117bb31SNicolin Chen 	}
3863117bb31SNicolin Chen 
387d281bf5dSS.j. Wang 	if ((outrate >= 5512 && outrate <= 30000) &&
388b06c58c2SS.j. Wang 	    (outrate > 24 * inrate || inrate > 8 * outrate)) {
389fff6e03cSZidan Wang 		pair_err("exceed supported ratio range [1/24, 8] for \
390fff6e03cSZidan Wang 				inrate/outrate: %d/%d\n", inrate, outrate);
391fff6e03cSZidan Wang 		return -EINVAL;
392fff6e03cSZidan Wang 	}
393fff6e03cSZidan Wang 
3943117bb31SNicolin Chen 	/* Validate input and output clock sources */
3957470704dSShengjiu Wang 	clk_index[IN] = asrc->clk_map[IN][config->inclk];
3967470704dSShengjiu Wang 	clk_index[OUT] = asrc->clk_map[OUT][config->outclk];
3973117bb31SNicolin Chen 
3983117bb31SNicolin Chen 	/* We only have output clock for ideal ratio mode */
3997470704dSShengjiu Wang 	clk = asrc->asrck_clk[clk_index[ideal ? OUT : IN]];
4003117bb31SNicolin Chen 
401b39eb1e2SShengjiu Wang 	clk_rate = clk_get_rate(clk);
402b39eb1e2SShengjiu Wang 	rem[IN] = do_div(clk_rate, inrate);
403b39eb1e2SShengjiu Wang 	div[IN] = (u32)clk_rate;
404b39eb1e2SShengjiu Wang 
405b39eb1e2SShengjiu Wang 	/*
406b39eb1e2SShengjiu Wang 	 * The divider range is [1, 1024], defined by the hardware. For non-
407b39eb1e2SShengjiu Wang 	 * ideal ratio configuration, clock rate has to be strictly aligned
408b39eb1e2SShengjiu Wang 	 * with the sample rate. For ideal ratio configuration, clock rates
409b39eb1e2SShengjiu Wang 	 * only result in different converting speeds. So remainder does not
410b39eb1e2SShengjiu Wang 	 * matter, as long as we keep the divider within its valid range.
411b39eb1e2SShengjiu Wang 	 */
412b39eb1e2SShengjiu Wang 	if (div[IN] == 0 || (!ideal && (div[IN] > 1024 || rem[IN] != 0))) {
4133117bb31SNicolin Chen 		pair_err("failed to support input sample rate %dHz by asrck_%x\n",
4143117bb31SNicolin Chen 				inrate, clk_index[ideal ? OUT : IN]);
4153117bb31SNicolin Chen 		return -EINVAL;
4163117bb31SNicolin Chen 	}
4173117bb31SNicolin Chen 
418b39eb1e2SShengjiu Wang 	div[IN] = min_t(u32, 1024, div[IN]);
419b39eb1e2SShengjiu Wang 
4207470704dSShengjiu Wang 	clk = asrc->asrck_clk[clk_index[OUT]];
421b39eb1e2SShengjiu Wang 	clk_rate = clk_get_rate(clk);
422b39eb1e2SShengjiu Wang 	if (ideal && use_ideal_rate)
423b39eb1e2SShengjiu Wang 		rem[OUT] = do_div(clk_rate, IDEAL_RATIO_RATE);
4243117bb31SNicolin Chen 	else
425b39eb1e2SShengjiu Wang 		rem[OUT] = do_div(clk_rate, outrate);
426b39eb1e2SShengjiu Wang 	div[OUT] = clk_rate;
4273117bb31SNicolin Chen 
428b39eb1e2SShengjiu Wang 	/* Output divider has the same limitation as the input one */
429b39eb1e2SShengjiu Wang 	if (div[OUT] == 0 || (!ideal && (div[OUT] > 1024 || rem[OUT] != 0))) {
4303117bb31SNicolin Chen 		pair_err("failed to support output sample rate %dHz by asrck_%x\n",
4313117bb31SNicolin Chen 				outrate, clk_index[OUT]);
4323117bb31SNicolin Chen 		return -EINVAL;
4333117bb31SNicolin Chen 	}
4343117bb31SNicolin Chen 
435b39eb1e2SShengjiu Wang 	div[OUT] = min_t(u32, 1024, div[OUT]);
436b39eb1e2SShengjiu Wang 
4373117bb31SNicolin Chen 	/* Set the channel number */
4383117bb31SNicolin Chen 	channels = config->channel_num;
4393117bb31SNicolin Chen 
4407470704dSShengjiu Wang 	if (asrc->soc->channel_bits < 4)
4413117bb31SNicolin Chen 		channels /= 2;
4423117bb31SNicolin Chen 
4433117bb31SNicolin Chen 	/* Update channels for current pair */
4447470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCNCR,
4457470704dSShengjiu Wang 			   ASRCNCR_ANCi_MASK(index, asrc->soc->channel_bits),
4467470704dSShengjiu Wang 			   ASRCNCR_ANCi(index, channels, asrc->soc->channel_bits));
4473117bb31SNicolin Chen 
4483117bb31SNicolin Chen 	/* Default setting: Automatic selection for processing mode */
4497470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCTR,
4503117bb31SNicolin Chen 			   ASRCTR_ATSi_MASK(index), ASRCTR_ATS(index));
4517470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCTR,
4523117bb31SNicolin Chen 			   ASRCTR_USRi_MASK(index), 0);
4533117bb31SNicolin Chen 
4543117bb31SNicolin Chen 	/* Set the input and output clock sources */
4557470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCSR,
4563117bb31SNicolin Chen 			   ASRCSR_AICSi_MASK(index) | ASRCSR_AOCSi_MASK(index),
4573117bb31SNicolin Chen 			   ASRCSR_AICS(index, clk_index[IN]) |
4583117bb31SNicolin Chen 			   ASRCSR_AOCS(index, clk_index[OUT]));
4593117bb31SNicolin Chen 
4603117bb31SNicolin Chen 	/* Calculate the input clock divisors */
4613117bb31SNicolin Chen 	indiv = fsl_asrc_cal_asrck_divisor(pair, div[IN]);
4623117bb31SNicolin Chen 	outdiv = fsl_asrc_cal_asrck_divisor(pair, div[OUT]);
4633117bb31SNicolin Chen 
4643117bb31SNicolin Chen 	/* Suppose indiv and outdiv includes prescaler, so add its MASK too */
4657470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCDR(index),
4663117bb31SNicolin Chen 			   ASRCDRi_AOCPi_MASK(index) | ASRCDRi_AICPi_MASK(index) |
4673117bb31SNicolin Chen 			   ASRCDRi_AOCDi_MASK(index) | ASRCDRi_AICDi_MASK(index),
4683117bb31SNicolin Chen 			   ASRCDRi_AOCP(index, outdiv) | ASRCDRi_AICP(index, indiv));
4693117bb31SNicolin Chen 
4703117bb31SNicolin Chen 	/* Implement word_width configurations */
4717470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRMCR1(index),
4723117bb31SNicolin Chen 			   ASRMCR1i_OW16_MASK | ASRMCR1i_IWD_MASK,
4734bf62571SShengjiu Wang 			   ASRMCR1i_OW16(output_word_width) |
4744bf62571SShengjiu Wang 			   ASRMCR1i_IWD(input_word_width));
4753117bb31SNicolin Chen 
4763117bb31SNicolin Chen 	/* Enable BUFFER STALL */
4777470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRMCR(index),
4783117bb31SNicolin Chen 			   ASRMCRi_BUFSTALLi_MASK, ASRMCRi_BUFSTALLi);
4793117bb31SNicolin Chen 
4803117bb31SNicolin Chen 	/* Set default thresholds for input and output FIFO */
4813117bb31SNicolin Chen 	fsl_asrc_set_watermarks(pair, ASRC_INPUTFIFO_THRESHOLD,
4823117bb31SNicolin Chen 				ASRC_INPUTFIFO_THRESHOLD);
4833117bb31SNicolin Chen 
4844091fb95SMasahiro Yamada 	/* Configure the following only for Ideal Ratio mode */
4853117bb31SNicolin Chen 	if (!ideal)
4863117bb31SNicolin Chen 		return 0;
4873117bb31SNicolin Chen 
4883117bb31SNicolin Chen 	/* Clear ASTSx bit to use Ideal Ratio mode */
4897470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCTR,
4903117bb31SNicolin Chen 			   ASRCTR_ATSi_MASK(index), 0);
4913117bb31SNicolin Chen 
4923117bb31SNicolin Chen 	/* Enable Ideal Ratio mode */
4937470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCTR,
4943117bb31SNicolin Chen 			   ASRCTR_IDRi_MASK(index) | ASRCTR_USRi_MASK(index),
4953117bb31SNicolin Chen 			   ASRCTR_IDR(index) | ASRCTR_USR(index));
4963117bb31SNicolin Chen 
4974aecaa0aSS.j. Wang 	fsl_asrc_sel_proc(inrate, outrate, &pre_proc, &post_proc);
4984aecaa0aSS.j. Wang 
4993117bb31SNicolin Chen 	/* Apply configurations for pre- and post-processing */
5007470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCFG,
5013117bb31SNicolin Chen 			   ASRCFG_PREMODi_MASK(index) |	ASRCFG_POSTMODi_MASK(index),
5024aecaa0aSS.j. Wang 			   ASRCFG_PREMOD(index, pre_proc) |
5034aecaa0aSS.j. Wang 			   ASRCFG_POSTMOD(index, post_proc));
5043117bb31SNicolin Chen 
5053117bb31SNicolin Chen 	return fsl_asrc_set_ideal_ratio(pair, inrate, outrate);
5063117bb31SNicolin Chen }
5073117bb31SNicolin Chen 
5083117bb31SNicolin Chen /**
5093117bb31SNicolin Chen  * Start the assigned ASRC pair
5103117bb31SNicolin Chen  *
5113117bb31SNicolin Chen  * It enables the assigned pair and makes it stopped at the stall level.
5123117bb31SNicolin Chen  */
5133117bb31SNicolin Chen static void fsl_asrc_start_pair(struct fsl_asrc_pair *pair)
5143117bb31SNicolin Chen {
5157470704dSShengjiu Wang 	struct fsl_asrc *asrc = pair->asrc;
5163117bb31SNicolin Chen 	enum asrc_pair_index index = pair->index;
5173117bb31SNicolin Chen 	int reg, retry = 10, i;
5183117bb31SNicolin Chen 
5193117bb31SNicolin Chen 	/* Enable the current pair */
5207470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCTR,
5213117bb31SNicolin Chen 			   ASRCTR_ASRCEi_MASK(index), ASRCTR_ASRCE(index));
5223117bb31SNicolin Chen 
5233117bb31SNicolin Chen 	/* Wait for status of initialization */
5243117bb31SNicolin Chen 	do {
5253117bb31SNicolin Chen 		udelay(5);
5267470704dSShengjiu Wang 		regmap_read(asrc->regmap, REG_ASRCFG, &reg);
5273117bb31SNicolin Chen 		reg &= ASRCFG_INIRQi_MASK(index);
5283117bb31SNicolin Chen 	} while (!reg && --retry);
5293117bb31SNicolin Chen 
5303117bb31SNicolin Chen 	/* Make the input fifo to ASRC STALL level */
5317470704dSShengjiu Wang 	regmap_read(asrc->regmap, REG_ASRCNCR, &reg);
5323117bb31SNicolin Chen 	for (i = 0; i < pair->channels * 4; i++)
5337470704dSShengjiu Wang 		regmap_write(asrc->regmap, REG_ASRDI(index), 0);
5343117bb31SNicolin Chen 
5353117bb31SNicolin Chen 	/* Enable overload interrupt */
5367470704dSShengjiu Wang 	regmap_write(asrc->regmap, REG_ASRIER, ASRIER_AOLIE);
5373117bb31SNicolin Chen }
5383117bb31SNicolin Chen 
5393117bb31SNicolin Chen /**
5403117bb31SNicolin Chen  * Stop the assigned ASRC pair
5413117bb31SNicolin Chen  */
5423117bb31SNicolin Chen static void fsl_asrc_stop_pair(struct fsl_asrc_pair *pair)
5433117bb31SNicolin Chen {
5447470704dSShengjiu Wang 	struct fsl_asrc *asrc = pair->asrc;
5453117bb31SNicolin Chen 	enum asrc_pair_index index = pair->index;
5463117bb31SNicolin Chen 
5473117bb31SNicolin Chen 	/* Stop the current pair */
5487470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCTR,
5493117bb31SNicolin Chen 			   ASRCTR_ASRCEi_MASK(index), 0);
5503117bb31SNicolin Chen }
5513117bb31SNicolin Chen 
5523117bb31SNicolin Chen /**
5533117bb31SNicolin Chen  * Get DMA channel according to the pair and direction.
5543117bb31SNicolin Chen  */
5553117bb31SNicolin Chen struct dma_chan *fsl_asrc_get_dma_channel(struct fsl_asrc_pair *pair, bool dir)
5563117bb31SNicolin Chen {
5577470704dSShengjiu Wang 	struct fsl_asrc *asrc = pair->asrc;
5583117bb31SNicolin Chen 	enum asrc_pair_index index = pair->index;
5593117bb31SNicolin Chen 	char name[4];
5603117bb31SNicolin Chen 
5613117bb31SNicolin Chen 	sprintf(name, "%cx%c", dir == IN ? 'r' : 't', index + 'a');
5623117bb31SNicolin Chen 
5637470704dSShengjiu Wang 	return dma_request_slave_channel(&asrc->pdev->dev, name);
5643117bb31SNicolin Chen }
5653117bb31SNicolin Chen EXPORT_SYMBOL_GPL(fsl_asrc_get_dma_channel);
5663117bb31SNicolin Chen 
56753f67a78SS.j. Wang static int fsl_asrc_dai_startup(struct snd_pcm_substream *substream,
56853f67a78SS.j. Wang 				struct snd_soc_dai *dai)
56953f67a78SS.j. Wang {
5707470704dSShengjiu Wang 	struct fsl_asrc *asrc = snd_soc_dai_get_drvdata(dai);
57153f67a78SS.j. Wang 
57253f67a78SS.j. Wang 	/* Odd channel number is not valid for older ASRC (channel_bits==3) */
5737470704dSShengjiu Wang 	if (asrc->soc->channel_bits == 3)
57453f67a78SS.j. Wang 		snd_pcm_hw_constraint_step(substream->runtime, 0,
57553f67a78SS.j. Wang 					   SNDRV_PCM_HW_PARAM_CHANNELS, 2);
57653f67a78SS.j. Wang 
577d281bf5dSS.j. Wang 
578d281bf5dSS.j. Wang 	return snd_pcm_hw_constraint_list(substream->runtime, 0,
579d281bf5dSS.j. Wang 			SNDRV_PCM_HW_PARAM_RATE, &fsl_asrc_rate_constraints);
58053f67a78SS.j. Wang }
58153f67a78SS.j. Wang 
5823117bb31SNicolin Chen static int fsl_asrc_dai_hw_params(struct snd_pcm_substream *substream,
5833117bb31SNicolin Chen 				  struct snd_pcm_hw_params *params,
5843117bb31SNicolin Chen 				  struct snd_soc_dai *dai)
5853117bb31SNicolin Chen {
5867470704dSShengjiu Wang 	struct fsl_asrc *asrc = snd_soc_dai_get_drvdata(dai);
5873117bb31SNicolin Chen 	struct snd_pcm_runtime *runtime = substream->runtime;
5883117bb31SNicolin Chen 	struct fsl_asrc_pair *pair = runtime->private_data;
5893117bb31SNicolin Chen 	unsigned int channels = params_channels(params);
5903117bb31SNicolin Chen 	unsigned int rate = params_rate(params);
5913117bb31SNicolin Chen 	struct asrc_config config;
5924bf62571SShengjiu Wang 	int ret;
5933117bb31SNicolin Chen 
5943117bb31SNicolin Chen 	ret = fsl_asrc_request_pair(channels, pair);
5953117bb31SNicolin Chen 	if (ret) {
5963117bb31SNicolin Chen 		dev_err(dai->dev, "fail to request asrc pair\n");
5973117bb31SNicolin Chen 		return ret;
5983117bb31SNicolin Chen 	}
5993117bb31SNicolin Chen 
6003117bb31SNicolin Chen 	pair->config = &config;
6013117bb31SNicolin Chen 
6023117bb31SNicolin Chen 	config.pair = pair->index;
6033117bb31SNicolin Chen 	config.channel_num = channels;
6043117bb31SNicolin Chen 	config.inclk = INCLK_NONE;
6053117bb31SNicolin Chen 	config.outclk = OUTCLK_ASRCK1_CLK;
6063117bb31SNicolin Chen 
6073117bb31SNicolin Chen 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
6084bf62571SShengjiu Wang 		config.input_format   = params_format(params);
609*4520af41SShengjiu Wang 		config.output_format  = asrc->asrc_format;
6103117bb31SNicolin Chen 		config.input_sample_rate  = rate;
6117470704dSShengjiu Wang 		config.output_sample_rate = asrc->asrc_rate;
6123117bb31SNicolin Chen 	} else {
613*4520af41SShengjiu Wang 		config.input_format   = asrc->asrc_format;
6144bf62571SShengjiu Wang 		config.output_format  = params_format(params);
6157470704dSShengjiu Wang 		config.input_sample_rate  = asrc->asrc_rate;
6163117bb31SNicolin Chen 		config.output_sample_rate = rate;
6173117bb31SNicolin Chen 	}
6183117bb31SNicolin Chen 
619b39eb1e2SShengjiu Wang 	ret = fsl_asrc_config_pair(pair, false);
6203117bb31SNicolin Chen 	if (ret) {
6213117bb31SNicolin Chen 		dev_err(dai->dev, "fail to config asrc pair\n");
6223117bb31SNicolin Chen 		return ret;
6233117bb31SNicolin Chen 	}
6243117bb31SNicolin Chen 
6253117bb31SNicolin Chen 	return 0;
6263117bb31SNicolin Chen }
6273117bb31SNicolin Chen 
6283117bb31SNicolin Chen static int fsl_asrc_dai_hw_free(struct snd_pcm_substream *substream,
6293117bb31SNicolin Chen 				struct snd_soc_dai *dai)
6303117bb31SNicolin Chen {
6313117bb31SNicolin Chen 	struct snd_pcm_runtime *runtime = substream->runtime;
6323117bb31SNicolin Chen 	struct fsl_asrc_pair *pair = runtime->private_data;
6333117bb31SNicolin Chen 
6343117bb31SNicolin Chen 	if (pair)
6353117bb31SNicolin Chen 		fsl_asrc_release_pair(pair);
6363117bb31SNicolin Chen 
6373117bb31SNicolin Chen 	return 0;
6383117bb31SNicolin Chen }
6393117bb31SNicolin Chen 
6403117bb31SNicolin Chen static int fsl_asrc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
6413117bb31SNicolin Chen 				struct snd_soc_dai *dai)
6423117bb31SNicolin Chen {
6433117bb31SNicolin Chen 	struct snd_pcm_runtime *runtime = substream->runtime;
6443117bb31SNicolin Chen 	struct fsl_asrc_pair *pair = runtime->private_data;
6453117bb31SNicolin Chen 
6463117bb31SNicolin Chen 	switch (cmd) {
6473117bb31SNicolin Chen 	case SNDRV_PCM_TRIGGER_START:
6483117bb31SNicolin Chen 	case SNDRV_PCM_TRIGGER_RESUME:
6493117bb31SNicolin Chen 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
6503117bb31SNicolin Chen 		fsl_asrc_start_pair(pair);
6513117bb31SNicolin Chen 		break;
6523117bb31SNicolin Chen 	case SNDRV_PCM_TRIGGER_STOP:
6533117bb31SNicolin Chen 	case SNDRV_PCM_TRIGGER_SUSPEND:
6543117bb31SNicolin Chen 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
6553117bb31SNicolin Chen 		fsl_asrc_stop_pair(pair);
6563117bb31SNicolin Chen 		break;
6573117bb31SNicolin Chen 	default:
6583117bb31SNicolin Chen 		return -EINVAL;
6593117bb31SNicolin Chen 	}
6603117bb31SNicolin Chen 
6613117bb31SNicolin Chen 	return 0;
6623117bb31SNicolin Chen }
6633117bb31SNicolin Chen 
66429a22ebfSGustavo A. R. Silva static const struct snd_soc_dai_ops fsl_asrc_dai_ops = {
66553f67a78SS.j. Wang 	.startup      = fsl_asrc_dai_startup,
6663117bb31SNicolin Chen 	.hw_params    = fsl_asrc_dai_hw_params,
6673117bb31SNicolin Chen 	.hw_free      = fsl_asrc_dai_hw_free,
6683117bb31SNicolin Chen 	.trigger      = fsl_asrc_dai_trigger,
6693117bb31SNicolin Chen };
6703117bb31SNicolin Chen 
6713117bb31SNicolin Chen static int fsl_asrc_dai_probe(struct snd_soc_dai *dai)
6723117bb31SNicolin Chen {
6737470704dSShengjiu Wang 	struct fsl_asrc *asrc = snd_soc_dai_get_drvdata(dai);
6743117bb31SNicolin Chen 
6757470704dSShengjiu Wang 	snd_soc_dai_init_dma_data(dai, &asrc->dma_params_tx,
6767470704dSShengjiu Wang 				  &asrc->dma_params_rx);
6773117bb31SNicolin Chen 
6783117bb31SNicolin Chen 	return 0;
6793117bb31SNicolin Chen }
6803117bb31SNicolin Chen 
6813117bb31SNicolin Chen #define FSL_ASRC_FORMATS	(SNDRV_PCM_FMTBIT_S24_LE | \
6823117bb31SNicolin Chen 				 SNDRV_PCM_FMTBIT_S16_LE | \
683109539c9SShengjiu Wang 				 SNDRV_PCM_FMTBIT_S24_3LE)
6843117bb31SNicolin Chen 
6853117bb31SNicolin Chen static struct snd_soc_dai_driver fsl_asrc_dai = {
6863117bb31SNicolin Chen 	.probe = fsl_asrc_dai_probe,
6873117bb31SNicolin Chen 	.playback = {
6883117bb31SNicolin Chen 		.stream_name = "ASRC-Playback",
6893117bb31SNicolin Chen 		.channels_min = 1,
6903117bb31SNicolin Chen 		.channels_max = 10,
691d281bf5dSS.j. Wang 		.rate_min = 5512,
692d281bf5dSS.j. Wang 		.rate_max = 192000,
693d281bf5dSS.j. Wang 		.rates = SNDRV_PCM_RATE_KNOT,
694109539c9SShengjiu Wang 		.formats = FSL_ASRC_FORMATS |
695109539c9SShengjiu Wang 			   SNDRV_PCM_FMTBIT_S8,
6963117bb31SNicolin Chen 	},
6973117bb31SNicolin Chen 	.capture = {
6983117bb31SNicolin Chen 		.stream_name = "ASRC-Capture",
6993117bb31SNicolin Chen 		.channels_min = 1,
7003117bb31SNicolin Chen 		.channels_max = 10,
701d281bf5dSS.j. Wang 		.rate_min = 5512,
702d281bf5dSS.j. Wang 		.rate_max = 192000,
703d281bf5dSS.j. Wang 		.rates = SNDRV_PCM_RATE_KNOT,
7043117bb31SNicolin Chen 		.formats = FSL_ASRC_FORMATS,
7053117bb31SNicolin Chen 	},
7063117bb31SNicolin Chen 	.ops = &fsl_asrc_dai_ops,
7073117bb31SNicolin Chen };
7083117bb31SNicolin Chen 
7093117bb31SNicolin Chen static bool fsl_asrc_readable_reg(struct device *dev, unsigned int reg)
7103117bb31SNicolin Chen {
7113117bb31SNicolin Chen 	switch (reg) {
7123117bb31SNicolin Chen 	case REG_ASRCTR:
7133117bb31SNicolin Chen 	case REG_ASRIER:
7143117bb31SNicolin Chen 	case REG_ASRCNCR:
7153117bb31SNicolin Chen 	case REG_ASRCFG:
7163117bb31SNicolin Chen 	case REG_ASRCSR:
7173117bb31SNicolin Chen 	case REG_ASRCDR1:
7183117bb31SNicolin Chen 	case REG_ASRCDR2:
7193117bb31SNicolin Chen 	case REG_ASRSTR:
7203117bb31SNicolin Chen 	case REG_ASRPM1:
7213117bb31SNicolin Chen 	case REG_ASRPM2:
7223117bb31SNicolin Chen 	case REG_ASRPM3:
7233117bb31SNicolin Chen 	case REG_ASRPM4:
7243117bb31SNicolin Chen 	case REG_ASRPM5:
7253117bb31SNicolin Chen 	case REG_ASRTFR1:
7263117bb31SNicolin Chen 	case REG_ASRCCR:
7273117bb31SNicolin Chen 	case REG_ASRDOA:
7283117bb31SNicolin Chen 	case REG_ASRDOB:
7293117bb31SNicolin Chen 	case REG_ASRDOC:
7303117bb31SNicolin Chen 	case REG_ASRIDRHA:
7313117bb31SNicolin Chen 	case REG_ASRIDRLA:
7323117bb31SNicolin Chen 	case REG_ASRIDRHB:
7333117bb31SNicolin Chen 	case REG_ASRIDRLB:
7343117bb31SNicolin Chen 	case REG_ASRIDRHC:
7353117bb31SNicolin Chen 	case REG_ASRIDRLC:
7363117bb31SNicolin Chen 	case REG_ASR76K:
7373117bb31SNicolin Chen 	case REG_ASR56K:
7383117bb31SNicolin Chen 	case REG_ASRMCRA:
7393117bb31SNicolin Chen 	case REG_ASRFSTA:
7403117bb31SNicolin Chen 	case REG_ASRMCRB:
7413117bb31SNicolin Chen 	case REG_ASRFSTB:
7423117bb31SNicolin Chen 	case REG_ASRMCRC:
7433117bb31SNicolin Chen 	case REG_ASRFSTC:
7443117bb31SNicolin Chen 	case REG_ASRMCR1A:
7453117bb31SNicolin Chen 	case REG_ASRMCR1B:
7463117bb31SNicolin Chen 	case REG_ASRMCR1C:
7473117bb31SNicolin Chen 		return true;
7483117bb31SNicolin Chen 	default:
7493117bb31SNicolin Chen 		return false;
7503117bb31SNicolin Chen 	}
7513117bb31SNicolin Chen }
7523117bb31SNicolin Chen 
7533117bb31SNicolin Chen static bool fsl_asrc_volatile_reg(struct device *dev, unsigned int reg)
7543117bb31SNicolin Chen {
7553117bb31SNicolin Chen 	switch (reg) {
7563117bb31SNicolin Chen 	case REG_ASRSTR:
7573117bb31SNicolin Chen 	case REG_ASRDIA:
7583117bb31SNicolin Chen 	case REG_ASRDIB:
7593117bb31SNicolin Chen 	case REG_ASRDIC:
7603117bb31SNicolin Chen 	case REG_ASRDOA:
7613117bb31SNicolin Chen 	case REG_ASRDOB:
7623117bb31SNicolin Chen 	case REG_ASRDOC:
7633117bb31SNicolin Chen 	case REG_ASRFSTA:
7643117bb31SNicolin Chen 	case REG_ASRFSTB:
7653117bb31SNicolin Chen 	case REG_ASRFSTC:
7663117bb31SNicolin Chen 	case REG_ASRCFG:
7673117bb31SNicolin Chen 		return true;
7683117bb31SNicolin Chen 	default:
7693117bb31SNicolin Chen 		return false;
7703117bb31SNicolin Chen 	}
7713117bb31SNicolin Chen }
7723117bb31SNicolin Chen 
7733117bb31SNicolin Chen static bool fsl_asrc_writeable_reg(struct device *dev, unsigned int reg)
7743117bb31SNicolin Chen {
7753117bb31SNicolin Chen 	switch (reg) {
7763117bb31SNicolin Chen 	case REG_ASRCTR:
7773117bb31SNicolin Chen 	case REG_ASRIER:
7783117bb31SNicolin Chen 	case REG_ASRCNCR:
7793117bb31SNicolin Chen 	case REG_ASRCFG:
7803117bb31SNicolin Chen 	case REG_ASRCSR:
7813117bb31SNicolin Chen 	case REG_ASRCDR1:
7823117bb31SNicolin Chen 	case REG_ASRCDR2:
7833117bb31SNicolin Chen 	case REG_ASRSTR:
7843117bb31SNicolin Chen 	case REG_ASRPM1:
7853117bb31SNicolin Chen 	case REG_ASRPM2:
7863117bb31SNicolin Chen 	case REG_ASRPM3:
7873117bb31SNicolin Chen 	case REG_ASRPM4:
7883117bb31SNicolin Chen 	case REG_ASRPM5:
7893117bb31SNicolin Chen 	case REG_ASRTFR1:
7903117bb31SNicolin Chen 	case REG_ASRCCR:
7913117bb31SNicolin Chen 	case REG_ASRDIA:
7923117bb31SNicolin Chen 	case REG_ASRDIB:
7933117bb31SNicolin Chen 	case REG_ASRDIC:
7943117bb31SNicolin Chen 	case REG_ASRIDRHA:
7953117bb31SNicolin Chen 	case REG_ASRIDRLA:
7963117bb31SNicolin Chen 	case REG_ASRIDRHB:
7973117bb31SNicolin Chen 	case REG_ASRIDRLB:
7983117bb31SNicolin Chen 	case REG_ASRIDRHC:
7993117bb31SNicolin Chen 	case REG_ASRIDRLC:
8003117bb31SNicolin Chen 	case REG_ASR76K:
8013117bb31SNicolin Chen 	case REG_ASR56K:
8023117bb31SNicolin Chen 	case REG_ASRMCRA:
8033117bb31SNicolin Chen 	case REG_ASRMCRB:
8043117bb31SNicolin Chen 	case REG_ASRMCRC:
8053117bb31SNicolin Chen 	case REG_ASRMCR1A:
8063117bb31SNicolin Chen 	case REG_ASRMCR1B:
8073117bb31SNicolin Chen 	case REG_ASRMCR1C:
8083117bb31SNicolin Chen 		return true;
8093117bb31SNicolin Chen 	default:
8103117bb31SNicolin Chen 		return false;
8113117bb31SNicolin Chen 	}
8123117bb31SNicolin Chen }
8133117bb31SNicolin Chen 
81486a570c5SNicolin Chen static struct reg_default fsl_asrc_reg[] = {
81586a570c5SNicolin Chen 	{ REG_ASRCTR, 0x0000 }, { REG_ASRIER, 0x0000 },
81686a570c5SNicolin Chen 	{ REG_ASRCNCR, 0x0000 }, { REG_ASRCFG, 0x0000 },
81786a570c5SNicolin Chen 	{ REG_ASRCSR, 0x0000 }, { REG_ASRCDR1, 0x0000 },
81886a570c5SNicolin Chen 	{ REG_ASRCDR2, 0x0000 }, { REG_ASRSTR, 0x0000 },
81986a570c5SNicolin Chen 	{ REG_ASRRA, 0x0000 }, { REG_ASRRB, 0x0000 },
82086a570c5SNicolin Chen 	{ REG_ASRRC, 0x0000 }, { REG_ASRPM1, 0x0000 },
82186a570c5SNicolin Chen 	{ REG_ASRPM2, 0x0000 }, { REG_ASRPM3, 0x0000 },
82286a570c5SNicolin Chen 	{ REG_ASRPM4, 0x0000 }, { REG_ASRPM5, 0x0000 },
82386a570c5SNicolin Chen 	{ REG_ASRTFR1, 0x0000 }, { REG_ASRCCR, 0x0000 },
82486a570c5SNicolin Chen 	{ REG_ASRDIA, 0x0000 }, { REG_ASRDOA, 0x0000 },
82586a570c5SNicolin Chen 	{ REG_ASRDIB, 0x0000 }, { REG_ASRDOB, 0x0000 },
82686a570c5SNicolin Chen 	{ REG_ASRDIC, 0x0000 }, { REG_ASRDOC, 0x0000 },
82786a570c5SNicolin Chen 	{ REG_ASRIDRHA, 0x0000 }, { REG_ASRIDRLA, 0x0000 },
82886a570c5SNicolin Chen 	{ REG_ASRIDRHB, 0x0000 }, { REG_ASRIDRLB, 0x0000 },
82986a570c5SNicolin Chen 	{ REG_ASRIDRHC, 0x0000 }, { REG_ASRIDRLC, 0x0000 },
83086a570c5SNicolin Chen 	{ REG_ASR76K, 0x0A47 }, { REG_ASR56K, 0x0DF3 },
83186a570c5SNicolin Chen 	{ REG_ASRMCRA, 0x0000 }, { REG_ASRFSTA, 0x0000 },
83286a570c5SNicolin Chen 	{ REG_ASRMCRB, 0x0000 }, { REG_ASRFSTB, 0x0000 },
83386a570c5SNicolin Chen 	{ REG_ASRMCRC, 0x0000 }, { REG_ASRFSTC, 0x0000 },
83486a570c5SNicolin Chen 	{ REG_ASRMCR1A, 0x0000 }, { REG_ASRMCR1B, 0x0000 },
83586a570c5SNicolin Chen 	{ REG_ASRMCR1C, 0x0000 },
83686a570c5SNicolin Chen };
83786a570c5SNicolin Chen 
838bf16d883SXiubo Li static const struct regmap_config fsl_asrc_regmap_config = {
8393117bb31SNicolin Chen 	.reg_bits = 32,
8403117bb31SNicolin Chen 	.reg_stride = 4,
8413117bb31SNicolin Chen 	.val_bits = 32,
8423117bb31SNicolin Chen 
8433117bb31SNicolin Chen 	.max_register = REG_ASRMCR1C,
84486a570c5SNicolin Chen 	.reg_defaults = fsl_asrc_reg,
84586a570c5SNicolin Chen 	.num_reg_defaults = ARRAY_SIZE(fsl_asrc_reg),
8463117bb31SNicolin Chen 	.readable_reg = fsl_asrc_readable_reg,
8473117bb31SNicolin Chen 	.volatile_reg = fsl_asrc_volatile_reg,
8483117bb31SNicolin Chen 	.writeable_reg = fsl_asrc_writeable_reg,
849b4138868SMarek Vasut 	.cache_type = REGCACHE_FLAT,
8503117bb31SNicolin Chen };
8513117bb31SNicolin Chen 
8523117bb31SNicolin Chen /**
8533117bb31SNicolin Chen  * Initialize ASRC registers with a default configurations
8543117bb31SNicolin Chen  */
8557470704dSShengjiu Wang static int fsl_asrc_init(struct fsl_asrc *asrc)
8563117bb31SNicolin Chen {
8573117bb31SNicolin Chen 	/* Halt ASRC internal FP when input FIFO needs data for pair A, B, C */
8587470704dSShengjiu Wang 	regmap_write(asrc->regmap, REG_ASRCTR, ASRCTR_ASRCEN);
8593117bb31SNicolin Chen 
8603117bb31SNicolin Chen 	/* Disable interrupt by default */
8617470704dSShengjiu Wang 	regmap_write(asrc->regmap, REG_ASRIER, 0x0);
8623117bb31SNicolin Chen 
8633117bb31SNicolin Chen 	/* Apply recommended settings for parameters from Reference Manual */
8647470704dSShengjiu Wang 	regmap_write(asrc->regmap, REG_ASRPM1, 0x7fffff);
8657470704dSShengjiu Wang 	regmap_write(asrc->regmap, REG_ASRPM2, 0x255555);
8667470704dSShengjiu Wang 	regmap_write(asrc->regmap, REG_ASRPM3, 0xff7280);
8677470704dSShengjiu Wang 	regmap_write(asrc->regmap, REG_ASRPM4, 0xff7280);
8687470704dSShengjiu Wang 	regmap_write(asrc->regmap, REG_ASRPM5, 0xff7280);
8693117bb31SNicolin Chen 
8703117bb31SNicolin Chen 	/* Base address for task queue FIFO. Set to 0x7C */
8717470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRTFR1,
8723117bb31SNicolin Chen 			   ASRTFR1_TF_BASE_MASK, ASRTFR1_TF_BASE(0xfc));
8733117bb31SNicolin Chen 
8743117bb31SNicolin Chen 	/* Set the processing clock for 76KHz to 133M */
8757470704dSShengjiu Wang 	regmap_write(asrc->regmap, REG_ASR76K, 0x06D6);
8763117bb31SNicolin Chen 
8773117bb31SNicolin Chen 	/* Set the processing clock for 56KHz to 133M */
8787470704dSShengjiu Wang 	return regmap_write(asrc->regmap, REG_ASR56K, 0x0947);
8793117bb31SNicolin Chen }
8803117bb31SNicolin Chen 
8813117bb31SNicolin Chen /**
8823117bb31SNicolin Chen  * Interrupt handler for ASRC
8833117bb31SNicolin Chen  */
8843117bb31SNicolin Chen static irqreturn_t fsl_asrc_isr(int irq, void *dev_id)
8853117bb31SNicolin Chen {
8867470704dSShengjiu Wang 	struct fsl_asrc *asrc = (struct fsl_asrc *)dev_id;
8877470704dSShengjiu Wang 	struct device *dev = &asrc->pdev->dev;
8883117bb31SNicolin Chen 	enum asrc_pair_index index;
8893117bb31SNicolin Chen 	u32 status;
8903117bb31SNicolin Chen 
8917470704dSShengjiu Wang 	regmap_read(asrc->regmap, REG_ASRSTR, &status);
8923117bb31SNicolin Chen 
8933117bb31SNicolin Chen 	/* Clean overload error */
8947470704dSShengjiu Wang 	regmap_write(asrc->regmap, REG_ASRSTR, ASRSTR_AOLE);
8953117bb31SNicolin Chen 
8963117bb31SNicolin Chen 	/*
8973117bb31SNicolin Chen 	 * We here use dev_dbg() for all exceptions because ASRC itself does
8983117bb31SNicolin Chen 	 * not care if FIFO overflowed or underrun while a warning in the
8993117bb31SNicolin Chen 	 * interrupt would result a ridged conversion.
9003117bb31SNicolin Chen 	 */
9013117bb31SNicolin Chen 	for (index = ASRC_PAIR_A; index < ASRC_PAIR_MAX_NUM; index++) {
9027470704dSShengjiu Wang 		if (!asrc->pair[index])
9033117bb31SNicolin Chen 			continue;
9043117bb31SNicolin Chen 
9053117bb31SNicolin Chen 		if (status & ASRSTR_ATQOL) {
9067470704dSShengjiu Wang 			asrc->pair[index]->error |= ASRC_TASK_Q_OVERLOAD;
9073117bb31SNicolin Chen 			dev_dbg(dev, "ASRC Task Queue FIFO overload\n");
9083117bb31SNicolin Chen 		}
9093117bb31SNicolin Chen 
9103117bb31SNicolin Chen 		if (status & ASRSTR_AOOL(index)) {
9117470704dSShengjiu Wang 			asrc->pair[index]->error |= ASRC_OUTPUT_TASK_OVERLOAD;
9123117bb31SNicolin Chen 			pair_dbg("Output Task Overload\n");
9133117bb31SNicolin Chen 		}
9143117bb31SNicolin Chen 
9153117bb31SNicolin Chen 		if (status & ASRSTR_AIOL(index)) {
9167470704dSShengjiu Wang 			asrc->pair[index]->error |= ASRC_INPUT_TASK_OVERLOAD;
9173117bb31SNicolin Chen 			pair_dbg("Input Task Overload\n");
9183117bb31SNicolin Chen 		}
9193117bb31SNicolin Chen 
9203117bb31SNicolin Chen 		if (status & ASRSTR_AODO(index)) {
9217470704dSShengjiu Wang 			asrc->pair[index]->error |= ASRC_OUTPUT_BUFFER_OVERFLOW;
9223117bb31SNicolin Chen 			pair_dbg("Output Data Buffer has overflowed\n");
9233117bb31SNicolin Chen 		}
9243117bb31SNicolin Chen 
9253117bb31SNicolin Chen 		if (status & ASRSTR_AIDU(index)) {
9267470704dSShengjiu Wang 			asrc->pair[index]->error |= ASRC_INPUT_BUFFER_UNDERRUN;
9273117bb31SNicolin Chen 			pair_dbg("Input Data Buffer has underflowed\n");
9283117bb31SNicolin Chen 		}
9293117bb31SNicolin Chen 	}
9303117bb31SNicolin Chen 
9313117bb31SNicolin Chen 	return IRQ_HANDLED;
9323117bb31SNicolin Chen }
9333117bb31SNicolin Chen 
9343117bb31SNicolin Chen static int fsl_asrc_probe(struct platform_device *pdev)
9353117bb31SNicolin Chen {
9363117bb31SNicolin Chen 	struct device_node *np = pdev->dev.of_node;
9377470704dSShengjiu Wang 	struct fsl_asrc *asrc;
9383117bb31SNicolin Chen 	struct resource *res;
9393117bb31SNicolin Chen 	void __iomem *regs;
9403117bb31SNicolin Chen 	int irq, ret, i;
941c05f10f2SShengjiu Wang 	u32 map_idx;
9423117bb31SNicolin Chen 	char tmp[16];
943*4520af41SShengjiu Wang 	u32 width;
9443117bb31SNicolin Chen 
9457470704dSShengjiu Wang 	asrc = devm_kzalloc(&pdev->dev, sizeof(*asrc), GFP_KERNEL);
9467470704dSShengjiu Wang 	if (!asrc)
9473117bb31SNicolin Chen 		return -ENOMEM;
9483117bb31SNicolin Chen 
9497470704dSShengjiu Wang 	asrc->pdev = pdev;
9503117bb31SNicolin Chen 
9513117bb31SNicolin Chen 	/* Get the addresses and IRQ */
9523117bb31SNicolin Chen 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
9533117bb31SNicolin Chen 	regs = devm_ioremap_resource(&pdev->dev, res);
9543117bb31SNicolin Chen 	if (IS_ERR(regs))
9553117bb31SNicolin Chen 		return PTR_ERR(regs);
9563117bb31SNicolin Chen 
9577470704dSShengjiu Wang 	asrc->paddr = res->start;
9583117bb31SNicolin Chen 
9597470704dSShengjiu Wang 	asrc->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "mem", regs,
9603117bb31SNicolin Chen 						 &fsl_asrc_regmap_config);
9617470704dSShengjiu Wang 	if (IS_ERR(asrc->regmap)) {
9623117bb31SNicolin Chen 		dev_err(&pdev->dev, "failed to init regmap\n");
9637470704dSShengjiu Wang 		return PTR_ERR(asrc->regmap);
9643117bb31SNicolin Chen 	}
9653117bb31SNicolin Chen 
9663117bb31SNicolin Chen 	irq = platform_get_irq(pdev, 0);
967cf9441adSStephen Boyd 	if (irq < 0)
9683117bb31SNicolin Chen 		return irq;
9693117bb31SNicolin Chen 
9703117bb31SNicolin Chen 	ret = devm_request_irq(&pdev->dev, irq, fsl_asrc_isr, 0,
9717470704dSShengjiu Wang 			       dev_name(&pdev->dev), asrc);
9723117bb31SNicolin Chen 	if (ret) {
9733117bb31SNicolin Chen 		dev_err(&pdev->dev, "failed to claim irq %u: %d\n", irq, ret);
9743117bb31SNicolin Chen 		return ret;
9753117bb31SNicolin Chen 	}
9763117bb31SNicolin Chen 
9777470704dSShengjiu Wang 	asrc->mem_clk = devm_clk_get(&pdev->dev, "mem");
9787470704dSShengjiu Wang 	if (IS_ERR(asrc->mem_clk)) {
9793117bb31SNicolin Chen 		dev_err(&pdev->dev, "failed to get mem clock\n");
9807470704dSShengjiu Wang 		return PTR_ERR(asrc->mem_clk);
9813117bb31SNicolin Chen 	}
9823117bb31SNicolin Chen 
9837470704dSShengjiu Wang 	asrc->ipg_clk = devm_clk_get(&pdev->dev, "ipg");
9847470704dSShengjiu Wang 	if (IS_ERR(asrc->ipg_clk)) {
9853117bb31SNicolin Chen 		dev_err(&pdev->dev, "failed to get ipg clock\n");
9867470704dSShengjiu Wang 		return PTR_ERR(asrc->ipg_clk);
9873117bb31SNicolin Chen 	}
9883117bb31SNicolin Chen 
9897470704dSShengjiu Wang 	asrc->spba_clk = devm_clk_get(&pdev->dev, "spba");
9907470704dSShengjiu Wang 	if (IS_ERR(asrc->spba_clk))
99113b8a97aSShengjiu Wang 		dev_warn(&pdev->dev, "failed to get spba clock\n");
99213b8a97aSShengjiu Wang 
9933117bb31SNicolin Chen 	for (i = 0; i < ASRC_CLK_MAX_NUM; i++) {
9943117bb31SNicolin Chen 		sprintf(tmp, "asrck_%x", i);
9957470704dSShengjiu Wang 		asrc->asrck_clk[i] = devm_clk_get(&pdev->dev, tmp);
9967470704dSShengjiu Wang 		if (IS_ERR(asrc->asrck_clk[i])) {
9973117bb31SNicolin Chen 			dev_err(&pdev->dev, "failed to get %s clock\n", tmp);
9987470704dSShengjiu Wang 			return PTR_ERR(asrc->asrck_clk[i]);
9993117bb31SNicolin Chen 		}
10003117bb31SNicolin Chen 	}
10013117bb31SNicolin Chen 
10027470704dSShengjiu Wang 	asrc->soc = of_device_get_match_data(&pdev->dev);
10037470704dSShengjiu Wang 	if (!asrc->soc) {
1004c05f10f2SShengjiu Wang 		dev_err(&pdev->dev, "failed to get soc data\n");
1005c05f10f2SShengjiu Wang 		return -ENODEV;
1006c05f10f2SShengjiu Wang 	}
1007c05f10f2SShengjiu Wang 
1008f3d8ac8cSFabio Estevam 	if (of_device_is_compatible(np, "fsl,imx35-asrc")) {
10097470704dSShengjiu Wang 		asrc->clk_map[IN] = input_clk_map_imx35;
10107470704dSShengjiu Wang 		asrc->clk_map[OUT] = output_clk_map_imx35;
1011c05f10f2SShengjiu Wang 	} else if (of_device_is_compatible(np, "fsl,imx53-asrc")) {
10127470704dSShengjiu Wang 		asrc->clk_map[IN] = input_clk_map_imx53;
10137470704dSShengjiu Wang 		asrc->clk_map[OUT] = output_clk_map_imx53;
1014c05f10f2SShengjiu Wang 	} else if (of_device_is_compatible(np, "fsl,imx8qm-asrc") ||
1015c05f10f2SShengjiu Wang 		   of_device_is_compatible(np, "fsl,imx8qxp-asrc")) {
1016c05f10f2SShengjiu Wang 		ret = of_property_read_u32(np, "fsl,asrc-clk-map", &map_idx);
1017c05f10f2SShengjiu Wang 		if (ret) {
1018c05f10f2SShengjiu Wang 			dev_err(&pdev->dev, "failed to get clk map index\n");
1019c05f10f2SShengjiu Wang 			return ret;
1020c05f10f2SShengjiu Wang 		}
1021c05f10f2SShengjiu Wang 
1022c05f10f2SShengjiu Wang 		if (map_idx > 1) {
1023c05f10f2SShengjiu Wang 			dev_err(&pdev->dev, "unsupported clk map index\n");
1024c05f10f2SShengjiu Wang 			return -EINVAL;
1025c05f10f2SShengjiu Wang 		}
1026c05f10f2SShengjiu Wang 		if (of_device_is_compatible(np, "fsl,imx8qm-asrc")) {
10277470704dSShengjiu Wang 			asrc->clk_map[IN] = clk_map_imx8qm[map_idx];
10287470704dSShengjiu Wang 			asrc->clk_map[OUT] = clk_map_imx8qm[map_idx];
10293117bb31SNicolin Chen 		} else {
10307470704dSShengjiu Wang 			asrc->clk_map[IN] = clk_map_imx8qxp[map_idx];
10317470704dSShengjiu Wang 			asrc->clk_map[OUT] = clk_map_imx8qxp[map_idx];
1032c05f10f2SShengjiu Wang 		}
10333117bb31SNicolin Chen 	}
10343117bb31SNicolin Chen 
10357470704dSShengjiu Wang 	ret = fsl_asrc_init(asrc);
10363117bb31SNicolin Chen 	if (ret) {
10373117bb31SNicolin Chen 		dev_err(&pdev->dev, "failed to init asrc %d\n", ret);
1038c0296950SFabio Estevam 		return ret;
10393117bb31SNicolin Chen 	}
10403117bb31SNicolin Chen 
10417470704dSShengjiu Wang 	asrc->channel_avail = 10;
10423117bb31SNicolin Chen 
10433117bb31SNicolin Chen 	ret = of_property_read_u32(np, "fsl,asrc-rate",
10447470704dSShengjiu Wang 				   &asrc->asrc_rate);
10453117bb31SNicolin Chen 	if (ret) {
10463117bb31SNicolin Chen 		dev_err(&pdev->dev, "failed to get output rate\n");
1047c0296950SFabio Estevam 		return ret;
10483117bb31SNicolin Chen 	}
10493117bb31SNicolin Chen 
1050*4520af41SShengjiu Wang 	ret = of_property_read_u32(np, "fsl,asrc-format", &asrc->asrc_format);
10513117bb31SNicolin Chen 	if (ret) {
1052*4520af41SShengjiu Wang 		ret = of_property_read_u32(np, "fsl,asrc-width", &width);
1053*4520af41SShengjiu Wang 		if (ret) {
1054*4520af41SShengjiu Wang 			dev_err(&pdev->dev, "failed to decide output format\n");
1055c0296950SFabio Estevam 			return ret;
10563117bb31SNicolin Chen 		}
10573117bb31SNicolin Chen 
1058*4520af41SShengjiu Wang 		switch (width) {
1059*4520af41SShengjiu Wang 		case 16:
1060*4520af41SShengjiu Wang 			asrc->asrc_format = SNDRV_PCM_FORMAT_S16_LE;
1061*4520af41SShengjiu Wang 			break;
1062*4520af41SShengjiu Wang 		case 24:
1063*4520af41SShengjiu Wang 			asrc->asrc_format = SNDRV_PCM_FORMAT_S24_LE;
1064*4520af41SShengjiu Wang 			break;
1065*4520af41SShengjiu Wang 		default:
1066*4520af41SShengjiu Wang 			dev_warn(&pdev->dev,
1067*4520af41SShengjiu Wang 				 "unsupported width, use default S24_LE\n");
1068*4520af41SShengjiu Wang 			asrc->asrc_format = SNDRV_PCM_FORMAT_S24_LE;
1069*4520af41SShengjiu Wang 			break;
1070*4520af41SShengjiu Wang 		}
1071*4520af41SShengjiu Wang 	}
1072*4520af41SShengjiu Wang 
1073*4520af41SShengjiu Wang 	if (!(FSL_ASRC_FORMATS & (1ULL << asrc->asrc_format))) {
1074*4520af41SShengjiu Wang 		dev_warn(&pdev->dev, "unsupported width, use default S24_LE\n");
1075*4520af41SShengjiu Wang 		asrc->asrc_format = SNDRV_PCM_FORMAT_S24_LE;
10763117bb31SNicolin Chen 	}
10773117bb31SNicolin Chen 
10787470704dSShengjiu Wang 	platform_set_drvdata(pdev, asrc);
10793117bb31SNicolin Chen 	pm_runtime_enable(&pdev->dev);
10807470704dSShengjiu Wang 	spin_lock_init(&asrc->lock);
10813117bb31SNicolin Chen 
10823117bb31SNicolin Chen 	ret = devm_snd_soc_register_component(&pdev->dev, &fsl_asrc_component,
10833117bb31SNicolin Chen 					      &fsl_asrc_dai, 1);
10843117bb31SNicolin Chen 	if (ret) {
10853117bb31SNicolin Chen 		dev_err(&pdev->dev, "failed to register ASoC DAI\n");
10863117bb31SNicolin Chen 		return ret;
10873117bb31SNicolin Chen 	}
10883117bb31SNicolin Chen 
10893117bb31SNicolin Chen 	return 0;
10903117bb31SNicolin Chen }
10913117bb31SNicolin Chen 
1092641d334bSRafael J. Wysocki #ifdef CONFIG_PM
10933117bb31SNicolin Chen static int fsl_asrc_runtime_resume(struct device *dev)
10943117bb31SNicolin Chen {
10957470704dSShengjiu Wang 	struct fsl_asrc *asrc = dev_get_drvdata(dev);
1096b1ade0f2SFabio Estevam 	int i, ret;
10973117bb31SNicolin Chen 
10987470704dSShengjiu Wang 	ret = clk_prepare_enable(asrc->mem_clk);
1099b1ade0f2SFabio Estevam 	if (ret)
1100b1ade0f2SFabio Estevam 		return ret;
11017470704dSShengjiu Wang 	ret = clk_prepare_enable(asrc->ipg_clk);
1102b1ade0f2SFabio Estevam 	if (ret)
1103b1ade0f2SFabio Estevam 		goto disable_mem_clk;
11047470704dSShengjiu Wang 	if (!IS_ERR(asrc->spba_clk)) {
11057470704dSShengjiu Wang 		ret = clk_prepare_enable(asrc->spba_clk);
110613b8a97aSShengjiu Wang 		if (ret)
110713b8a97aSShengjiu Wang 			goto disable_ipg_clk;
110813b8a97aSShengjiu Wang 	}
1109b1ade0f2SFabio Estevam 	for (i = 0; i < ASRC_CLK_MAX_NUM; i++) {
11107470704dSShengjiu Wang 		ret = clk_prepare_enable(asrc->asrck_clk[i]);
1111b1ade0f2SFabio Estevam 		if (ret)
1112b1ade0f2SFabio Estevam 			goto disable_asrck_clk;
1113b1ade0f2SFabio Estevam 	}
11143117bb31SNicolin Chen 
11153117bb31SNicolin Chen 	return 0;
1116b1ade0f2SFabio Estevam 
1117b1ade0f2SFabio Estevam disable_asrck_clk:
1118b1ade0f2SFabio Estevam 	for (i--; i >= 0; i--)
11197470704dSShengjiu Wang 		clk_disable_unprepare(asrc->asrck_clk[i]);
11207470704dSShengjiu Wang 	if (!IS_ERR(asrc->spba_clk))
11217470704dSShengjiu Wang 		clk_disable_unprepare(asrc->spba_clk);
112213b8a97aSShengjiu Wang disable_ipg_clk:
11237470704dSShengjiu Wang 	clk_disable_unprepare(asrc->ipg_clk);
1124b1ade0f2SFabio Estevam disable_mem_clk:
11257470704dSShengjiu Wang 	clk_disable_unprepare(asrc->mem_clk);
1126b1ade0f2SFabio Estevam 	return ret;
11273117bb31SNicolin Chen }
11283117bb31SNicolin Chen 
11293117bb31SNicolin Chen static int fsl_asrc_runtime_suspend(struct device *dev)
11303117bb31SNicolin Chen {
11317470704dSShengjiu Wang 	struct fsl_asrc *asrc = dev_get_drvdata(dev);
11323117bb31SNicolin Chen 	int i;
11333117bb31SNicolin Chen 
11343117bb31SNicolin Chen 	for (i = 0; i < ASRC_CLK_MAX_NUM; i++)
11357470704dSShengjiu Wang 		clk_disable_unprepare(asrc->asrck_clk[i]);
11367470704dSShengjiu Wang 	if (!IS_ERR(asrc->spba_clk))
11377470704dSShengjiu Wang 		clk_disable_unprepare(asrc->spba_clk);
11387470704dSShengjiu Wang 	clk_disable_unprepare(asrc->ipg_clk);
11397470704dSShengjiu Wang 	clk_disable_unprepare(asrc->mem_clk);
11403117bb31SNicolin Chen 
11413117bb31SNicolin Chen 	return 0;
11423117bb31SNicolin Chen }
1143641d334bSRafael J. Wysocki #endif /* CONFIG_PM */
11443117bb31SNicolin Chen 
1145d3dacda9SFabio Estevam #ifdef CONFIG_PM_SLEEP
11463117bb31SNicolin Chen static int fsl_asrc_suspend(struct device *dev)
11473117bb31SNicolin Chen {
11487470704dSShengjiu Wang 	struct fsl_asrc *asrc = dev_get_drvdata(dev);
11493117bb31SNicolin Chen 
11507470704dSShengjiu Wang 	regmap_read(asrc->regmap, REG_ASRCFG,
11517470704dSShengjiu Wang 		    &asrc->regcache_cfg);
1152d44c6114SZidan Wang 
11537470704dSShengjiu Wang 	regcache_cache_only(asrc->regmap, true);
11547470704dSShengjiu Wang 	regcache_mark_dirty(asrc->regmap);
11553117bb31SNicolin Chen 
11563117bb31SNicolin Chen 	return 0;
11573117bb31SNicolin Chen }
11583117bb31SNicolin Chen 
11593117bb31SNicolin Chen static int fsl_asrc_resume(struct device *dev)
11603117bb31SNicolin Chen {
11617470704dSShengjiu Wang 	struct fsl_asrc *asrc = dev_get_drvdata(dev);
11623117bb31SNicolin Chen 	u32 asrctr;
11633117bb31SNicolin Chen 
11643117bb31SNicolin Chen 	/* Stop all pairs provisionally */
11657470704dSShengjiu Wang 	regmap_read(asrc->regmap, REG_ASRCTR, &asrctr);
11667470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCTR,
11673117bb31SNicolin Chen 			   ASRCTR_ASRCEi_ALL_MASK, 0);
11683117bb31SNicolin Chen 
11693117bb31SNicolin Chen 	/* Restore all registers */
11707470704dSShengjiu Wang 	regcache_cache_only(asrc->regmap, false);
11717470704dSShengjiu Wang 	regcache_sync(asrc->regmap);
11723117bb31SNicolin Chen 
11737470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCFG,
1174d44c6114SZidan Wang 			   ASRCFG_NDPRi_ALL_MASK | ASRCFG_POSTMODi_ALL_MASK |
11757470704dSShengjiu Wang 			   ASRCFG_PREMODi_ALL_MASK, asrc->regcache_cfg);
1176d44c6114SZidan Wang 
11773117bb31SNicolin Chen 	/* Restart enabled pairs */
11787470704dSShengjiu Wang 	regmap_update_bits(asrc->regmap, REG_ASRCTR,
11793117bb31SNicolin Chen 			   ASRCTR_ASRCEi_ALL_MASK, asrctr);
11803117bb31SNicolin Chen 
11813117bb31SNicolin Chen 	return 0;
11823117bb31SNicolin Chen }
11833117bb31SNicolin Chen #endif /* CONFIG_PM_SLEEP */
11843117bb31SNicolin Chen 
11853117bb31SNicolin Chen static const struct dev_pm_ops fsl_asrc_pm = {
11863117bb31SNicolin Chen 	SET_RUNTIME_PM_OPS(fsl_asrc_runtime_suspend, fsl_asrc_runtime_resume, NULL)
11873117bb31SNicolin Chen 	SET_SYSTEM_SLEEP_PM_OPS(fsl_asrc_suspend, fsl_asrc_resume)
11883117bb31SNicolin Chen };
11893117bb31SNicolin Chen 
1190c05f10f2SShengjiu Wang static const struct fsl_asrc_soc_data fsl_asrc_imx35_data = {
1191c05f10f2SShengjiu Wang 	.use_edma = false,
1192c05f10f2SShengjiu Wang 	.channel_bits = 3,
1193c05f10f2SShengjiu Wang };
1194c05f10f2SShengjiu Wang 
1195c05f10f2SShengjiu Wang static const struct fsl_asrc_soc_data fsl_asrc_imx53_data = {
1196c05f10f2SShengjiu Wang 	.use_edma = false,
1197c05f10f2SShengjiu Wang 	.channel_bits = 4,
1198c05f10f2SShengjiu Wang };
1199c05f10f2SShengjiu Wang 
1200c05f10f2SShengjiu Wang static const struct fsl_asrc_soc_data fsl_asrc_imx8qm_data = {
1201c05f10f2SShengjiu Wang 	.use_edma = true,
1202c05f10f2SShengjiu Wang 	.channel_bits = 4,
1203c05f10f2SShengjiu Wang };
1204c05f10f2SShengjiu Wang 
1205c05f10f2SShengjiu Wang static const struct fsl_asrc_soc_data fsl_asrc_imx8qxp_data = {
1206c05f10f2SShengjiu Wang 	.use_edma = true,
1207c05f10f2SShengjiu Wang 	.channel_bits = 4,
1208c05f10f2SShengjiu Wang };
1209c05f10f2SShengjiu Wang 
12103117bb31SNicolin Chen static const struct of_device_id fsl_asrc_ids[] = {
1211c05f10f2SShengjiu Wang 	{ .compatible = "fsl,imx35-asrc", .data = &fsl_asrc_imx35_data },
1212c05f10f2SShengjiu Wang 	{ .compatible = "fsl,imx53-asrc", .data = &fsl_asrc_imx53_data },
1213c05f10f2SShengjiu Wang 	{ .compatible = "fsl,imx8qm-asrc", .data = &fsl_asrc_imx8qm_data },
1214c05f10f2SShengjiu Wang 	{ .compatible = "fsl,imx8qxp-asrc", .data = &fsl_asrc_imx8qxp_data },
12153117bb31SNicolin Chen 	{}
12163117bb31SNicolin Chen };
12173117bb31SNicolin Chen MODULE_DEVICE_TABLE(of, fsl_asrc_ids);
12183117bb31SNicolin Chen 
12193117bb31SNicolin Chen static struct platform_driver fsl_asrc_driver = {
12203117bb31SNicolin Chen 	.probe = fsl_asrc_probe,
12213117bb31SNicolin Chen 	.driver = {
12223117bb31SNicolin Chen 		.name = "fsl-asrc",
12233117bb31SNicolin Chen 		.of_match_table = fsl_asrc_ids,
12243117bb31SNicolin Chen 		.pm = &fsl_asrc_pm,
12253117bb31SNicolin Chen 	},
12263117bb31SNicolin Chen };
12273117bb31SNicolin Chen module_platform_driver(fsl_asrc_driver);
12283117bb31SNicolin Chen 
12293117bb31SNicolin Chen MODULE_DESCRIPTION("Freescale ASRC ASoC driver");
12303117bb31SNicolin Chen MODULE_AUTHOR("Nicolin Chen <nicoleotsuka@gmail.com>");
12313117bb31SNicolin Chen MODULE_ALIAS("platform:fsl-asrc");
12323117bb31SNicolin Chen MODULE_LICENSE("GPL v2");
1233