Esempio n. 1
0
static long asrc_ioctl_flush(struct asrc_pair_params *params,
				void __user *user)
{
	enum asrc_pair_index index = params->index;
	init_completion(&params->input_complete);
	init_completion(&params->output_complete);
	init_completion(&params->lastperiod_complete);

	/* Release DMA and request again */
	dma_release_channel(params->input_dma_channel);
	dma_release_channel(params->output_dma_channel);

	params->input_dma_channel = imx_asrc_get_dma_channel(index, true);
	if (params->input_dma_channel == NULL) {
		pair_err("failed to request input task dma channel\n");
		return -EBUSY;
	}

	params->output_dma_channel = imx_asrc_get_dma_channel(index, false);
	if (params->output_dma_channel == NULL) {
		pair_err("failed to request output task dma channel\n");
		return -EBUSY;
	}

	return 0;
}
Esempio n. 2
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;
	}

	if ((outrate > 8000 && outrate < 30000) &&
	    (outrate/inrate > 24 || inrate/outrate > 8)) {
		pair_err("exceed supported ratio range [1/24, 8] for \
				inrate/outrate: %d/%d\n", inrate, outrate);
		return -EINVAL;
	}
Esempio n. 3
0
int mxc_asrc_process_io_buffer(struct asrc_pair_params *params,
				struct asrc_convert_buffer *pbuf, bool in)
{
	void *last_vaddr = params->output_last_period.dma_vaddr;
	unsigned int *last_len = &params->output_last_period.length;
	enum asrc_pair_index index = params->index;
	unsigned int dma_len, *buf_len;
	struct completion *complete;
	void __user *buf_vaddr;
	void *dma_vaddr;

	if (in) {
		dma_vaddr = params->input_dma_total.dma_vaddr;
		dma_len = params->input_dma_total.length;
		buf_len = &pbuf->input_buffer_length;
		complete = &params->input_complete;
		buf_vaddr = (void __user *)pbuf->input_buffer_vaddr;
	} else {
		dma_vaddr = params->output_dma_total.dma_vaddr;
		dma_len = params->output_dma_total.length;
		buf_len = &pbuf->output_buffer_length;
		complete = &params->lastperiod_complete;
		buf_vaddr = (void __user *)pbuf->output_buffer_vaddr;
	}

	if (!wait_for_completion_interruptible_timeout(complete, 10 * HZ)) {
		pair_err("%s task timeout\n", in ? "input dma" : "last period");
		return -ETIME;
	} else if (signal_pending(current)) {
		pair_err("%sput task forcibly aborted\n", in ? "in" : "out");
		return -ERESTARTSYS;
	}

	init_completion(complete);

	*buf_len = dma_len;

	/* Only output need return data to user space */
	if (!in) {
		if (copy_to_user(buf_vaddr, dma_vaddr, dma_len))
			return -EFAULT;

		*buf_len += *last_len;

		if (copy_to_user(buf_vaddr + dma_len, last_vaddr, *last_len))
			return -EFAULT;
	}

	return 0;
}
Esempio n. 4
0
static int mxc_asrc_prepare_buffer(struct asrc_pair_params *params,
				struct asrc_convert_buffer *pbuf)
{
	enum asrc_pair_index index = params->index;
	int ret;

	ret = mxc_asrc_prepare_io_buffer(params, pbuf, true);
	if (ret) {
		pair_err("failed to prepare input buffer: %d\n", ret);
		return ret;
	}

	ret = mxc_asrc_prepare_io_buffer(params, pbuf, false);
	if (ret) {
		pair_err("failed to prepare output buffer: %d\n", ret);
		return ret;
	}

	return 0;
}
Esempio n. 5
0
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));
}
Esempio n. 6
0
static long asrc_ioctl_convert(struct asrc_pair_params *params,
				void __user *user)
{
	enum asrc_pair_index index = params->index;
	struct asrc_convert_buffer buf;
	long ret;

	ret = copy_from_user(&buf, user, sizeof(buf));
	if (ret) {
		pair_err("failed to get buf from user space: %ld\n", ret);
		return ret;
	}

	ret = mxc_asrc_prepare_buffer(params, &buf);
	if (ret) {
		pair_err("failed to prepare buffer: %ld\n", ret);
		return ret;
	}

#ifdef ASRC_POLLING_WITHOUT_DMA
	asrc_polling_debug(params);
#else
	mxc_asrc_submit_dma(params);
#endif

	ret = mxc_asrc_process_buffer(params, &buf);
	if (ret) {
		pair_err("failed to process buffer: %ld\n", ret);
		return ret;
	}

	ret = copy_to_user(user, &buf, sizeof(buf));
	if (ret) {
		pair_err("failed to send buf to user space: %ld\n", ret);
		return ret;
	}

	return 0;
}
Esempio n. 7
0
static int mxc_allocate_dma_buf(struct asrc_pair_params *params)
{
	struct dma_block *input_a, *output_a, *last_period;
	enum asrc_pair_index index = params->index;

	input_a = &params->input_dma_total;
	output_a = &params->output_dma_total;
	last_period = &params->output_last_period;

	input_a->dma_vaddr = kzalloc(input_a->length, GFP_KERNEL);
	if (!input_a->dma_vaddr) {
		pair_err("failed to allocate input dma buffer\n");
		goto exit;
	}
	input_a->dma_paddr = virt_to_dma(NULL, input_a->dma_vaddr);

	output_a->dma_vaddr = kzalloc(output_a->length, GFP_KERNEL);
	if (!output_a->dma_vaddr) {
		pair_err("failed to allocate output dma buffer\n");
		goto exit;
	}
	output_a->dma_paddr = virt_to_dma(NULL, output_a->dma_vaddr);

	last_period->dma_vaddr = dma_alloc_coherent(asrc->dev,
			1024 * params->last_period_sample,
			&last_period->dma_paddr, GFP_KERNEL);
	if (!last_period->dma_vaddr) {
		pair_err("failed to allocate last period buffer\n");
		goto exit;
	}

	return 0;

exit:
	mxc_free_dma_buf(params);

	return -ENOBUFS;
}
Esempio n. 8
0
static long asrc_ioctl_status(struct asrc_pair_params *params,
				void __user *user)
{
	enum asrc_pair_index index = params->index;
	struct asrc_status_flags flags;
	long ret;

	ret = copy_from_user(&flags, user, sizeof(flags));
	if (ret) {
		pair_err("failed to get flags from user space: %ld\n", ret);
		return ret;
	}

	asrc_get_status(&flags);

	ret = copy_to_user(user, &flags, sizeof(flags));
	if (ret) {
		pair_err("failed to send flags to user space: %ld\n", ret);
		return ret;
	}

	return 0;
}
Esempio n. 9
0
/**
 * Calculate and set the ratio for Ideal Ratio mode only
 *
 * The ratio is a 32-bit fixed point value with 26 fractional bits.
 */
static int fsl_asrc_set_ideal_ratio(struct fsl_asrc_pair *pair,
				    int inrate, int outrate)
{
	struct fsl_asrc *asrc_priv = pair->asrc_priv;
	enum asrc_pair_index index = pair->index;
	unsigned long ratio;
	int i;

	if (!outrate) {
		pair_err("output rate should not be zero\n");
		return -EINVAL;
	}

	/* Calculate the intergal part of the ratio */
	ratio = (inrate / outrate) << IDEAL_RATIO_DECIMAL_DEPTH;

	/* ... and then the 26 depth decimal part */
	inrate %= outrate;

	for (i = 1; i <= IDEAL_RATIO_DECIMAL_DEPTH; i++) {
		inrate <<= 1;

		if (inrate < outrate)
			continue;

		ratio |= 1 << (IDEAL_RATIO_DECIMAL_DEPTH - i);
		inrate -= outrate;

		if (!inrate)
			break;
	}

	regmap_write(asrc_priv->regmap, REG_ASRIDRL(index), ratio);
	regmap_write(asrc_priv->regmap, REG_ASRIDRH(index), ratio >> 24);

	return 0;
}
Esempio n. 10
0
static void asrc_output_task_worker(struct work_struct *w)
{
	struct asrc_pair_params *params =
		container_of(w, struct asrc_pair_params, task_output_work);
	enum asrc_pair_index index = params->index;
	unsigned long lock_flags;

	if (!wait_for_completion_interruptible_timeout(&params->output_complete, HZ / 10)) {
		pair_err("output dma task timeout\n");
		return;
	}

	init_completion(&params->output_complete);

	spin_lock_irqsave(&pair_lock, lock_flags);
	if (!params->pair_hold) {
		spin_unlock_irqrestore(&pair_lock, lock_flags);
		return;
	}
	asrc_read_output_FIFO(params);
	spin_unlock_irqrestore(&pair_lock, lock_flags);

	complete(&params->lastperiod_complete);
}
Esempio n. 11
0
static int mxc_asrc_prepare_io_buffer(struct asrc_pair_params *params,
				struct asrc_convert_buffer *pbuf, bool in)
{
	enum asrc_pair_index index = params->index;
	struct dma_chan *dma_channel;
	enum asrc_word_width width;
	unsigned int *dma_len, *sg_nodes, buf_len, wm;
	void __user *buf_vaddr;
	void *dma_vaddr;
	u32 word_size, fifo_addr;

	if (in) {
		dma_channel = params->input_dma_channel;
		dma_vaddr = params->input_dma_total.dma_vaddr;
		dma_len = &params->input_dma_total.length;
		width = params->input_word_width;
		sg_nodes = &params->input_sg_nodes;
		wm = params->input_wm;
		buf_vaddr = (void __user *)pbuf->input_buffer_vaddr;
		buf_len = pbuf->input_buffer_length;
	} else {
		dma_channel = params->output_dma_channel;
		dma_vaddr = params->output_dma_total.dma_vaddr;
		dma_len = &params->output_dma_total.length;
		width = params->output_word_width;
		sg_nodes = &params->output_sg_nodes;
		wm = params->last_period_sample;
		buf_vaddr = (void __user *)pbuf->output_buffer_vaddr;
		buf_len = pbuf->output_buffer_length;
	}

	switch (width) {
	case ASRC_WIDTH_24_BIT:
		word_size = 4;
		break;
	case ASRC_WIDTH_16_BIT:
	case ASRC_WIDTH_8_BIT:
		word_size = 2;
		break;
	default:
		pair_err("invalid %sput word size!\n", in ? "in" : "out");
		return -EINVAL;
	}

	if (buf_len < word_size * params->channel_nums * wm) {
		pair_err("%sput buffer size[%d] is too small!\n",
				in ? "in" : "out", buf_len);
		return -EINVAL;
	}

	/* Copy origin data into input buffer */
	if (in && copy_from_user(dma_vaddr, buf_vaddr, buf_len))
		return -EFAULT;

	*dma_len = buf_len;
	if (!in)
		*dma_len -= wm * word_size * params->channel_nums;

	*sg_nodes = *dma_len / ASRC_MAX_BUFFER_SIZE + 1;

	fifo_addr = asrc_get_per_addr(params->index, in);

	return imx_asrc_dma_config(params, dma_channel, fifo_addr, dma_vaddr,
			*dma_len, in, width);
}
Esempio n. 12
0
static int imx_asrc_dma_config(struct asrc_pair_params *params,
				struct dma_chan *chan, u32 dma_addr,
				void *buf_addr, u32 buf_len, bool in,
				enum asrc_word_width word_width)
{
	enum asrc_pair_index index = params->index;
	struct dma_async_tx_descriptor *desc;
	struct dma_slave_config slave_config;
	enum dma_slave_buswidth buswidth;
	struct scatterlist *sg;
	unsigned int sg_nent, i;
	int ret;

	if (in) {
		sg = params->input_sg;
		sg_nent = params->input_sg_nodes;
		desc = params->desc_in;
	} else {
		sg = params->output_sg;
		sg_nent = params->output_sg_nodes;
		desc = params->desc_out;
	}

	switch (word_width) {
	case ASRC_WIDTH_16_BIT:
		buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
		break;
	case ASRC_WIDTH_24_BIT:
		buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES;
		break;
	default:
		pair_err("invalid word width\n");
		return -EINVAL;
	}

	slave_config.dma_request0 = 0;
	slave_config.dma_request1 = 0;

	if (in) {
		slave_config.direction = DMA_MEM_TO_DEV;
		slave_config.dst_addr = dma_addr;
		slave_config.dst_addr_width = buswidth;
		slave_config.dst_maxburst =
			params->input_wm * params->channel_nums / buswidth;
	} else {
		slave_config.direction = DMA_DEV_TO_MEM;
		slave_config.src_addr = dma_addr;
		slave_config.src_addr_width = buswidth;
		slave_config.src_maxburst =
			params->output_wm * params->channel_nums / buswidth;
	}
	ret = dmaengine_slave_config(chan, &slave_config);
	if (ret) {
		pair_err("failed to config dmaengine for %sput task: %d\n",
				in ? "in" : "out", ret);
		return -EINVAL;
	}

	sg_init_table(sg, sg_nent);
	switch (sg_nent) {
	case 1:
		sg_init_one(sg, buf_addr, buf_len);
		break;
	case 2:
	case 3:
	case 4:
		for (i = 0; i < (sg_nent - 1); i++)
			sg_set_buf(&sg[i], buf_addr + i * ASRC_MAX_BUFFER_SIZE,
					ASRC_MAX_BUFFER_SIZE);

		sg_set_buf(&sg[i], buf_addr + i * ASRC_MAX_BUFFER_SIZE,
				buf_len - ASRC_MAX_BUFFER_SIZE * i);
		break;
	default:
		pair_err("invalid input DMA nodes number: %d\n", sg_nent);
		return -EINVAL;
	}

	ret = dma_map_sg(NULL, sg, sg_nent, slave_config.direction);
	if (ret != sg_nent) {
		pair_err("failed to map dma sg for %sput task\n",
				in ? "in" : "out");
		return -EINVAL;
	}

	desc = dmaengine_prep_slave_sg(chan, sg, sg_nent,
			slave_config.direction, DMA_PREP_INTERRUPT);
	if (!desc) {
		pair_err("failed to prepare slave sg for %sput task\n",
				in ? "in" : "out");
		return -EINVAL;
	}

	if (in) {
		params->desc_in = desc;
		params->desc_in->callback = asrc_input_dma_callback;
	} else {
		params->desc_out = desc;
		params->desc_out->callback = asrc_output_dma_callback;
	}

	desc->callback = ASRC_xPUT_DMA_CALLBACK(in);
	desc->callback_param = params;

	return 0;
}
Esempio n. 13
0
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);
}
Esempio n. 14
0
static long asrc_ioctl_config_pair(struct asrc_pair_params *params,
				void __user *user)
{
	struct asrc_config config;
	enum asrc_pair_index index;
	long ret;

	ret = copy_from_user(&config, user, sizeof(config));
	if (ret) {
		dev_err(asrc->dev, "failed to get config from user space: %ld\n", ret);
		return ret;
	}

	index = config.pair;

	ret = asrc_config_pair(&config);
	if (ret) {
		pair_err("failed to config pair: %ld\n", ret);
		return ret;
	}

	params->input_wm = 4;
	params->output_wm = 2;

	ret = asrc_set_watermark(index, params->input_wm, params->output_wm);
	if (ret)
		return ret;

	params->output_buffer_size = config.dma_buffer_size;
	params->input_buffer_size = config.dma_buffer_size;
	if (config.buffer_num > ASRC_DMA_BUFFER_NUM)
		params->buffer_num = ASRC_DMA_BUFFER_NUM;
	else
		params->buffer_num = config.buffer_num;

	params->input_dma_total.length = ASRC_DMA_BUFFER_SIZE;
	params->output_dma_total.length = ASRC_DMA_BUFFER_SIZE;

	params->input_word_width = config.input_word_width;
	params->output_word_width = config.output_word_width;

	params->input_sample_rate = config.input_sample_rate;
	params->output_sample_rate = config.output_sample_rate;

	params->last_period_sample = ASRC_OUTPUT_LAST_SAMPLE_DEFAULT;

	ret = mxc_allocate_dma_buf(params);
	if (ret) {
		pair_err("failed to allocate dma buffer: %ld\n", ret);
		return ret;
	}

	/* Request DMA channel for both input and output */
	params->input_dma_channel = imx_asrc_get_dma_channel(index, true);
	if (params->input_dma_channel == NULL) {
		pair_err("failed to request input task dma channel\n");
		return  -EBUSY;
	}

	params->output_dma_channel = imx_asrc_get_dma_channel(index, false);
	if (params->output_dma_channel == NULL) {
		pair_err("failed to request output task dma channel\n");
		return  -EBUSY;
	}

	init_completion(&params->input_complete);
	init_completion(&params->output_complete);
	init_completion(&params->lastperiod_complete);

	/* Add work struct to receive last period of output data */
	INIT_WORK(&params->task_output_work, asrc_output_task_worker);

	ret = copy_to_user(user, &config, sizeof(config));
	if (ret) {
		pair_err("failed to send config to user space: %ld\n", ret);
		return ret;
	}

	return 0;
}
Esempio n. 15
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);
}