int asrc_set_watermark(enum asrc_pair_index index, u32 in_wm, u32 out_wm)
{
	if (in_wm > ASRC_FIFO_THRESHOLD_MAX || out_wm > ASRC_FIFO_THRESHOLD_MAX) {
		pair_err("invalid watermark!\n");
		return -EINVAL;
	}

	return regmap_update_bits(asrc->regmap, REG_ASRMCR(index),
			ASRMCRx_EXTTHRSHx_MASK | ASRMCRx_INFIFO_THRESHOLD_MASK |
			ASRMCRx_OUTFIFO_THRESHOLD_MASK,
			ASRMCRx_EXTTHRSHx | ASRMCRx_INFIFO_THRESHOLD(in_wm) |
			ASRMCRx_OUTFIFO_THRESHOLD(out_wm));
}
Exemple #2
0
/**
 * Configure input and output thresholds
 */
static void fsl_asrc_set_watermarks(struct fsl_asrc_pair *pair, u32 in, u32 out)
{
	struct fsl_asrc *asrc_priv = pair->asrc_priv;
	enum asrc_pair_index index = pair->index;

	regmap_update_bits(asrc_priv->regmap, REG_ASRMCR(index),
			   ASRMCRi_EXTTHRSHi_MASK |
			   ASRMCRi_INFIFO_THRESHOLD_MASK |
			   ASRMCRi_OUTFIFO_THRESHOLD_MASK,
			   ASRMCRi_EXTTHRSHi |
			   ASRMCRi_INFIFO_THRESHOLD(in) |
			   ASRMCRi_OUTFIFO_THRESHOLD(out));
}
int asrc_config_pair(struct asrc_config *config)
{
	u32 inrate = config->input_sample_rate, indiv;
	u32 outrate = config->output_sample_rate, outdiv;
	int ret, channels, index = config->pair;
	unsigned long lock_flags;

	/* Set the channel number */
	spin_lock_irqsave(&data_lock, lock_flags);
	asrc->asrc_pair[index].chn_num = config->channel_num;
	spin_unlock_irqrestore(&data_lock, lock_flags);

	if (asrc->channel_bits > 3)
		channels = config->channel_num;
	else
		channels = (config->channel_num + 1) / 2;

	/* Update channel number of current pair */
	regmap_update_bits(asrc->regmap, REG_ASRCNCR,
			ASRCNCR_ANCx_MASK(index, asrc->channel_bits),
			ASRCNCR_ANCx_set(index, channels, asrc->channel_bits));

	/* Set the clock source */
	regmap_update_bits(asrc->regmap, REG_ASRCSR,
			ASRCSR_AICSx_MASK(index) | ASRCSR_AOCSx_MASK(index),
			ASRCSR_AICS(index, input_clk_map[config->inclk]) |
			ASRCSR_AOCS(index, output_clk_map[config->outclk]));

	/* Default setting: Automatic selection for processing mode */
	regmap_update_bits(asrc->regmap, REG_ASRCTR,
			ASRCTR_ATSx_MASK(index), ASRCTR_ATS(index));
	regmap_update_bits(asrc->regmap, REG_ASRCTR, ASRCTR_USRx_MASK(index), 0);

	/* Default Input Clock Divider Setting */
	switch (config->inclk & ASRCSR_AxCSx_MASK) {
	case INCLK_SPDIF_RX:
		indiv = ASRC_PRESCALER_SPDIF_RX;
		break;
	case INCLK_SPDIF_TX:
		indiv = ASRC_PRESCALER_SPDIF_TX;
		break;
	case INCLK_ASRCK1_CLK:
		indiv = asrc_get_asrck_clock_divider(inrate);
		break;
	default:
		switch (config->input_word_width) {
		case ASRC_WIDTH_16_BIT:
			indiv = ASRC_PRESCALER_I2S_16BIT;
			break;
		case ASRC_WIDTH_24_BIT:
			indiv = ASRC_PRESCALER_I2S_24BIT;
			break;
		default:
			pair_err("unsupported input word width %d\n",
					config->input_word_width);
			return -EINVAL;
		}
		break;
	}

	/* Default Output Clock Divider Setting */
	switch (config->outclk & ASRCSR_AxCSx_MASK) {
	case OUTCLK_SPDIF_RX:
		outdiv = ASRC_PRESCALER_SPDIF_RX;
		break;
	case OUTCLK_SPDIF_TX:
		outdiv = ASRC_PRESCALER_SPDIF_TX;
		break;
	case OUTCLK_ASRCK1_CLK:
		if ((config->inclk & ASRCSR_AxCSx_MASK) == INCLK_NONE)
			outdiv = ASRC_PRESCALER_IDEAL_RATIO;
		else
			outdiv = asrc_get_asrck_clock_divider(outrate);
		break;
	default:
		switch (config->output_word_width) {
		case ASRC_WIDTH_16_BIT:
			outdiv = ASRC_PRESCALER_I2S_16BIT;
			break;
		case ASRC_WIDTH_24_BIT:
			outdiv = ASRC_PRESCALER_I2S_24BIT;
			break;
		default:
			pair_err("unsupported output word width %d\n",
					config->input_word_width);
			return -EINVAL;
		}
		break;
	}

	/* indiv and outdiv'd include prescaler's value, so add its MASK too */
	regmap_update_bits(asrc->regmap, REG_ASRCDR(index),
			ASRCDRx_AOCPx_MASK(index) | ASRCDRx_AICPx_MASK(index) |
			ASRCDRx_AOCDx_MASK(index) | ASRCDRx_AICDx_MASK(index),
			ASRCDRx_AOCP(index, outdiv) | ASRCDRx_AICP(index, indiv));

	/* Check whether ideal ratio is a must */
	switch (config->inclk & ASRCSR_AxCSx_MASK) {
	case INCLK_NONE:
		/* Clear ASTSx bit to use ideal ratio */
		regmap_update_bits(asrc->regmap, REG_ASRCTR,
				ASRCTR_ATSx_MASK(index), 0);

		regmap_update_bits(asrc->regmap, REG_ASRCTR,
				ASRCTR_IDRx_MASK(index) | ASRCTR_USRx_MASK(index),
				ASRCTR_IDR(index) | ASRCTR_USR(index));

		ret = asrc_set_clock_ratio(index, inrate, outrate);
		if (ret)
			return ret;

		ret = asrc_set_process_configuration(index, inrate, outrate);
		if (ret)
			return ret;

		break;
	case INCLK_ASRCK1_CLK:
		/* This case and default are both remained for v1 */
		if (inrate == 44100 || inrate == 88200) {
			pair_err("unsupported sample rate %d by selected clock\n",
					inrate);
			return -EINVAL;
		}
		break;
	default:
		if ((config->outclk & ASRCSR_AxCSx_MASK) != OUTCLK_ASRCK1_CLK)
			break;

		if (outrate == 44100 || outrate == 88200) {
			pair_err("unsupported sample rate %d by selected clock\n",
					outrate);
			return -EINVAL;
		}
		break;
	}

	/* Config input and output wordwidth */
	if (config->output_word_width == ASRC_WIDTH_8_BIT) {
		pair_err("unsupported wordwidth for output: 8bit\n");
		pair_err("output only support: 16bit or 24bit\n");
		return -EINVAL;
	}

	regmap_update_bits(asrc->regmap, REG_ASRMCR1(index),
			ASRMCR1x_OW16_MASK | ASRMCR1x_IWD_MASK,
			ASRMCR1x_OW16(config->output_word_width) |
			ASRMCR1x_IWD(config->input_word_width));

	/* Enable BUFFER STALL */
	regmap_update_bits(asrc->regmap, REG_ASRMCR(index),
			ASRMCRx_BUFSTALLx_MASK, ASRMCRx_BUFSTALLx);

	/* Set Threshold for input and output FIFO */
	return asrc_set_watermark(index, ASRC_INPUTFIFO_THRESHOLD,
			ASRC_INPUTFIFO_THRESHOLD);
}
Exemple #4
0
/**
 * Configure the assigned ASRC pair
 *
 * It configures those ASRC registers according to a configuration instance
 * of struct asrc_config which includes in/output sample rate, width, channel
 * and clock settings.
 */
static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair)
{
	struct asrc_config *config = pair->config;
	struct fsl_asrc *asrc_priv = pair->asrc_priv;
	enum asrc_pair_index index = pair->index;
	u32 inrate, outrate, indiv, outdiv;
	u32 clk_index[2], div[2];
	int in, out, channels;
	struct clk *clk;
	bool ideal;

	if (!config) {
		pair_err("invalid pair config\n");
		return -EINVAL;
	}

	/* Validate channels */
	if (config->channel_num < 1 || config->channel_num > 10) {
		pair_err("does not support %d channels\n", config->channel_num);
		return -EINVAL;
	}

	/* Validate output width */
	if (config->output_word_width == ASRC_WIDTH_8_BIT) {
		pair_err("does not support 8bit width output\n");
		return -EINVAL;
	}

	inrate = config->input_sample_rate;
	outrate = config->output_sample_rate;
	ideal = config->inclk == INCLK_NONE;

	/* Validate input and output sample rates */
	for (in = 0; in < ARRAY_SIZE(supported_input_rate); in++)
		if (inrate == supported_input_rate[in])
			break;

	if (in == ARRAY_SIZE(supported_input_rate)) {
		pair_err("unsupported input sample rate: %dHz\n", inrate);
		return -EINVAL;
	}

	for (out = 0; out < ARRAY_SIZE(supported_asrc_rate); out++)
		if (outrate == supported_asrc_rate[out])
			break;

	if (out == ARRAY_SIZE(supported_asrc_rate)) {
		pair_err("unsupported output sample rate: %dHz\n", outrate);
		return -EINVAL;
	}

	/* Validate input and output clock sources */
	clk_index[IN] = clk_map[IN][config->inclk];
	clk_index[OUT] = clk_map[OUT][config->outclk];

	/* We only have output clock for ideal ratio mode */
	clk = asrc_priv->asrck_clk[clk_index[ideal ? OUT : IN]];

	div[IN] = clk_get_rate(clk) / inrate;
	if (div[IN] == 0) {
		pair_err("failed to support input sample rate %dHz by asrck_%x\n",
				inrate, clk_index[ideal ? OUT : IN]);
		return -EINVAL;
	}

	clk = asrc_priv->asrck_clk[clk_index[OUT]];

	/* Use fixed output rate for Ideal Ratio mode (INCLK_NONE) */
	if (ideal)
		div[OUT] = clk_get_rate(clk) / IDEAL_RATIO_RATE;
	else
		div[OUT] = clk_get_rate(clk) / outrate;

	if (div[OUT] == 0) {
		pair_err("failed to support output sample rate %dHz by asrck_%x\n",
				outrate, clk_index[OUT]);
		return -EINVAL;
	}

	/* Set the channel number */
	channels = config->channel_num;

	if (asrc_priv->channel_bits < 4)
		channels /= 2;

	/* Update channels for current pair */
	regmap_update_bits(asrc_priv->regmap, REG_ASRCNCR,
			   ASRCNCR_ANCi_MASK(index, asrc_priv->channel_bits),
			   ASRCNCR_ANCi(index, channels, asrc_priv->channel_bits));

	/* Default setting: Automatic selection for processing mode */
	regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
			   ASRCTR_ATSi_MASK(index), ASRCTR_ATS(index));
	regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
			   ASRCTR_USRi_MASK(index), 0);

	/* Set the input and output clock sources */
	regmap_update_bits(asrc_priv->regmap, REG_ASRCSR,
			   ASRCSR_AICSi_MASK(index) | ASRCSR_AOCSi_MASK(index),
			   ASRCSR_AICS(index, clk_index[IN]) |
			   ASRCSR_AOCS(index, clk_index[OUT]));

	/* Calculate the input clock divisors */
	indiv = fsl_asrc_cal_asrck_divisor(pair, div[IN]);
	outdiv = fsl_asrc_cal_asrck_divisor(pair, div[OUT]);

	/* Suppose indiv and outdiv includes prescaler, so add its MASK too */
	regmap_update_bits(asrc_priv->regmap, REG_ASRCDR(index),
			   ASRCDRi_AOCPi_MASK(index) | ASRCDRi_AICPi_MASK(index) |
			   ASRCDRi_AOCDi_MASK(index) | ASRCDRi_AICDi_MASK(index),
			   ASRCDRi_AOCP(index, outdiv) | ASRCDRi_AICP(index, indiv));

	/* Implement word_width configurations */
	regmap_update_bits(asrc_priv->regmap, REG_ASRMCR1(index),
			   ASRMCR1i_OW16_MASK | ASRMCR1i_IWD_MASK,
			   ASRMCR1i_OW16(config->output_word_width) |
			   ASRMCR1i_IWD(config->input_word_width));

	/* Enable BUFFER STALL */
	regmap_update_bits(asrc_priv->regmap, REG_ASRMCR(index),
			   ASRMCRi_BUFSTALLi_MASK, ASRMCRi_BUFSTALLi);

	/* Set default thresholds for input and output FIFO */
	fsl_asrc_set_watermarks(pair, ASRC_INPUTFIFO_THRESHOLD,
				ASRC_INPUTFIFO_THRESHOLD);

	/* Configure the followings only for Ideal Ratio mode */
	if (!ideal)
		return 0;

	/* Clear ASTSx bit to use Ideal Ratio mode */
	regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
			   ASRCTR_ATSi_MASK(index), 0);

	/* Enable Ideal Ratio mode */
	regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
			   ASRCTR_IDRi_MASK(index) | ASRCTR_USRi_MASK(index),
			   ASRCTR_IDR(index) | ASRCTR_USR(index));

	/* Apply configurations for pre- and post-processing */
	regmap_update_bits(asrc_priv->regmap, REG_ASRCFG,
			   ASRCFG_PREMODi_MASK(index) |	ASRCFG_POSTMODi_MASK(index),
			   ASRCFG_PREMOD(index, process_option[in][out][0]) |
			   ASRCFG_POSTMOD(index, process_option[in][out][1]));

	return fsl_asrc_set_ideal_ratio(pair, inrate, outrate);
}