⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 i2sbus-pcm.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * i2sbus driver -- pcm routines * * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> * * GPL v2, can be found in COPYING. */#include <asm/io.h>#include <linux/delay.h>/* So apparently there's a reason for requiring driver.h * to be included first, even if I don't know it... */#include <sound/driver.h>#include <sound/core.h>#include <asm/macio.h>#include <linux/pci.h>#include "../soundbus.h"#include "i2sbus.h"static inline void get_pcm_info(struct i2sbus_dev *i2sdev, int in,				struct pcm_info **pi, struct pcm_info **other){	if (in) {		if (pi)			*pi = &i2sdev->in;		if (other)			*other = &i2sdev->out;	} else {		if (pi)			*pi = &i2sdev->out;		if (other)			*other = &i2sdev->in;	}}static int clock_and_divisors(int mclk, int sclk, int rate, int *out){	/* sclk must be derived from mclk! */	if (mclk % sclk)		return -1;	/* derive sclk register value */	if (i2s_sf_sclkdiv(mclk / sclk, out))		return -1;	if (I2S_CLOCK_SPEED_18MHz % (rate * mclk) == 0) {		if (!i2s_sf_mclkdiv(I2S_CLOCK_SPEED_18MHz / (rate * mclk), out)) {			*out |= I2S_SF_CLOCK_SOURCE_18MHz;			return 0;		}	}	if (I2S_CLOCK_SPEED_45MHz % (rate * mclk) == 0) {		if (!i2s_sf_mclkdiv(I2S_CLOCK_SPEED_45MHz / (rate * mclk), out)) {			*out |= I2S_SF_CLOCK_SOURCE_45MHz;			return 0;		}	}	if (I2S_CLOCK_SPEED_49MHz % (rate * mclk) == 0) {		if (!i2s_sf_mclkdiv(I2S_CLOCK_SPEED_49MHz / (rate * mclk), out)) {			*out |= I2S_SF_CLOCK_SOURCE_49MHz;			return 0;		}	}	return -1;}#define CHECK_RATE(rate)						\	do { if (rates & SNDRV_PCM_RATE_ ##rate) {			\		int dummy;						\		if (clock_and_divisors(sysclock_factor,			\				       bus_factor, rate, &dummy))	\			rates &= ~SNDRV_PCM_RATE_ ##rate;		\	} } while (0)static int i2sbus_pcm_open(struct i2sbus_dev *i2sdev, int in){	struct pcm_info *pi, *other;	struct soundbus_dev *sdev;	int masks_inited = 0, err;	struct codec_info_item *cii, *rev;	struct snd_pcm_hardware *hw;	u64 formats = 0;	unsigned int rates = 0;	struct transfer_info v;	int result = 0;	int bus_factor = 0, sysclock_factor = 0;	int found_this;	mutex_lock(&i2sdev->lock);	get_pcm_info(i2sdev, in, &pi, &other);	hw = &pi->substream->runtime->hw;	sdev = &i2sdev->sound;	if (pi->active) {		/* alsa messed up */		result = -EBUSY;		goto out_unlock;	}	/* we now need to assign the hw */	list_for_each_entry(cii, &sdev->codec_list, list) {		struct transfer_info *ti = cii->codec->transfers;		bus_factor = cii->codec->bus_factor;		sysclock_factor = cii->codec->sysclock_factor;		while (ti->formats && ti->rates) {			v = *ti;			if (ti->transfer_in == in			    && cii->codec->usable(cii, ti, &v)) {				if (masks_inited) {					formats &= v.formats;					rates &= v.rates;				} else {					formats = v.formats;					rates = v.rates;					masks_inited = 1;				}			}			ti++;		}	}	if (!masks_inited || !bus_factor || !sysclock_factor) {		result = -ENODEV;		goto out_unlock;	}	/* bus dependent stuff */	hw->info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |		   SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_RESUME |		   SNDRV_PCM_INFO_JOINT_DUPLEX;	CHECK_RATE(5512);	CHECK_RATE(8000);	CHECK_RATE(11025);	CHECK_RATE(16000);	CHECK_RATE(22050);	CHECK_RATE(32000);	CHECK_RATE(44100);	CHECK_RATE(48000);	CHECK_RATE(64000);	CHECK_RATE(88200);	CHECK_RATE(96000);	CHECK_RATE(176400);	CHECK_RATE(192000);	hw->rates = rates;	/* well. the codec might want 24 bits only, and we'll	 * ever only transfer 24 bits, but they are top-aligned!	 * So for alsa, we claim that we're doing full 32 bit	 * while in reality we'll ignore the lower 8 bits of	 * that when doing playback (they're transferred as 0	 * as far as I know, no codecs we have are 32-bit capable	 * so I can't really test) and when doing recording we'll	 * always have those lower 8 bits recorded as 0 */	if (formats & SNDRV_PCM_FMTBIT_S24_BE)		formats |= SNDRV_PCM_FMTBIT_S32_BE;	if (formats & SNDRV_PCM_FMTBIT_U24_BE)		formats |= SNDRV_PCM_FMTBIT_U32_BE;	/* now mask off what we can support. I suppose we could	 * also support S24_3LE and some similar formats, but I	 * doubt there's a codec that would be able to use that,	 * so we don't support it here. */	hw->formats = formats & (SNDRV_PCM_FMTBIT_S16_BE |				 SNDRV_PCM_FMTBIT_U16_BE |				 SNDRV_PCM_FMTBIT_S32_BE |				 SNDRV_PCM_FMTBIT_U32_BE);	/* we need to set the highest and lowest rate possible.	 * These are the highest and lowest rates alsa can	 * support properly in its bitfield.	 * Below, we'll use that to restrict to the rate	 * currently in use (if any). */	hw->rate_min = 5512;	hw->rate_max = 192000;	/* if the other stream is active, then we can only	 * support what it is currently using.	 * FIXME: I lied. This comment is wrong. We can support	 * anything that works with the same serial format, ie.	 * when recording 24 bit sound we can well play 16 bit	 * sound at the same time iff using the same transfer mode.	 */	if (other->active) {		/* FIXME: is this guaranteed by the alsa api? */		hw->formats &= (1ULL << i2sdev->format);		/* see above, restrict rates to the one we already have */		hw->rate_min = i2sdev->rate;		hw->rate_max = i2sdev->rate;	}	hw->channels_min = 2;	hw->channels_max = 2;	/* these are somewhat arbitrary */	hw->buffer_bytes_max = 131072;	hw->period_bytes_min = 256;	hw->period_bytes_max = 16384;	hw->periods_min = 3;	hw->periods_max = MAX_DBDMA_COMMANDS;	list_for_each_entry(cii, &sdev->codec_list, list) {		if (cii->codec->open) {			err = cii->codec->open(cii, pi->substream);			if (err) {				result = err;				/* unwind */				found_this = 0;				list_for_each_entry_reverse(rev,				    &sdev->codec_list, list) {					if (found_this && rev->codec->close) {						rev->codec->close(rev,								pi->substream);					}					if (rev == cii)						found_this = 1;				}				goto out_unlock;			}		}	} out_unlock:	mutex_unlock(&i2sdev->lock);	return result;}#undef CHECK_RATEstatic int i2sbus_pcm_close(struct i2sbus_dev *i2sdev, int in){	struct codec_info_item *cii;	struct pcm_info *pi;	int err = 0, tmp;	mutex_lock(&i2sdev->lock);	get_pcm_info(i2sdev, in, &pi, NULL);	list_for_each_entry(cii, &i2sdev->sound.codec_list, list) {		if (cii->codec->close) {			tmp = cii->codec->close(cii, pi->substream);			if (tmp)				err = tmp;		}	}	pi->substream = NULL;	pi->active = 0;	mutex_unlock(&i2sdev->lock);	return err;}static void i2sbus_wait_for_stop(struct i2sbus_dev *i2sdev,				 struct pcm_info *pi){	unsigned long flags;	struct completion done;	long timeout;	spin_lock_irqsave(&i2sdev->low_lock, flags);	if (pi->dbdma_ring.stopping) {		init_completion(&done);		pi->stop_completion = &done;		spin_unlock_irqrestore(&i2sdev->low_lock, flags);		timeout = wait_for_completion_timeout(&done, HZ);		spin_lock_irqsave(&i2sdev->low_lock, flags);		pi->stop_completion = NULL;		if (timeout == 0) {			/* timeout expired, stop dbdma forcefully */			printk(KERN_ERR "i2sbus_wait_for_stop: timed out\n");			/* make sure RUN, PAUSE and S0 bits are cleared */			out_le32(&pi->dbdma->control, (RUN | PAUSE | 1) << 16);			pi->dbdma_ring.stopping = 0;			timeout = 10;			while (in_le32(&pi->dbdma->status) & ACTIVE) {				if (--timeout <= 0)					break;				udelay(1);			}		}	}	spin_unlock_irqrestore(&i2sdev->low_lock, flags);}#ifdef CONFIG_PMvoid i2sbus_wait_for_stop_both(struct i2sbus_dev *i2sdev){	struct pcm_info *pi;	get_pcm_info(i2sdev, 0, &pi, NULL);	i2sbus_wait_for_stop(i2sdev, pi);	get_pcm_info(i2sdev, 1, &pi, NULL);	i2sbus_wait_for_stop(i2sdev, pi);}#endifstatic int i2sbus_hw_params(struct snd_pcm_substream *substream,			    struct snd_pcm_hw_params *params){	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));}static inline int i2sbus_hw_free(struct snd_pcm_substream *substream, int in){	struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream);	struct pcm_info *pi;	get_pcm_info(i2sdev, in, &pi, NULL);	if (pi->dbdma_ring.stopping)		i2sbus_wait_for_stop(i2sdev, pi);	snd_pcm_lib_free_pages(substream);	return 0;}static int i2sbus_playback_hw_free(struct snd_pcm_substream *substream){	return i2sbus_hw_free(substream, 0);}static int i2sbus_record_hw_free(struct snd_pcm_substream *substream){	return i2sbus_hw_free(substream, 1);}static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in){	/* whee. Hard work now. The user has selected a bitrate	 * and bit format, so now we have to program our	 * I2S controller appropriately. */	struct snd_pcm_runtime *runtime;	struct dbdma_cmd *command;	int i, periodsize, nperiods;	dma_addr_t offset;	struct bus_info bi;	struct codec_info_item *cii;	int sfr = 0;		/* serial format register */	int dws = 0;		/* data word sizes reg */	int input_16bit;	struct pcm_info *pi, *other;	int cnt;	int result = 0;	unsigned int cmd, stopaddr;	mutex_lock(&i2sdev->lock);	get_pcm_info(i2sdev, in, &pi, &other);	if (pi->dbdma_ring.running) {		result = -EBUSY;		goto out_unlock;	}	if (pi->dbdma_ring.stopping)		i2sbus_wait_for_stop(i2sdev, pi);	if (!pi->substream || !pi->substream->runtime) {		result = -EINVAL;		goto out_unlock;	}	runtime = pi->substream->runtime;	pi->active = 1;	if (other->active &&	    ((i2sdev->format != runtime->format)	     || (i2sdev->rate != runtime->rate))) {		result = -EINVAL;		goto out_unlock;	}	i2sdev->format = runtime->format;	i2sdev->rate = runtime->rate;	periodsize = snd_pcm_lib_period_bytes(pi->substream);	nperiods = pi->substream->runtime->periods;	pi->current_period = 0;	/* generate dbdma command ring first */	command = pi->dbdma_ring.cmds;	memset(command, 0, (nperiods + 2) * sizeof(struct dbdma_cmd));	/* commands to DMA to/from the ring */	/*	 * For input, we need to do a graceful stop; if we abort	 * the DMA, we end up with leftover bytes that corrupt	 * the next recording.  To do this we set the S0 status	 * bit and wait for the DMA controller to stop.  Each	 * command has a branch condition to	 * make it branch to a stop command if S0 is set.	 * On input we also need to wait for the S7 bit to be	 * set before turning off the DMA controller.	 * In fact we do the graceful stop for output as well.	 */	offset = runtime->dma_addr;	cmd = (in? INPUT_MORE: OUTPUT_MORE) | BR_IFSET | INTR_ALWAYS;	stopaddr = pi->dbdma_ring.bus_cmd_start +		(nperiods + 1) * sizeof(struct dbdma_cmd);	for (i = 0; i < nperiods; i++, command++, offset += periodsize) {		command->command = cpu_to_le16(cmd);		command->cmd_dep = cpu_to_le32(stopaddr);		command->phy_addr = cpu_to_le32(offset);		command->req_count = cpu_to_le16(periodsize);	}	/* branch back to beginning of ring */	command->command = cpu_to_le16(DBDMA_NOP | BR_ALWAYS);	command->cmd_dep = cpu_to_le32(pi->dbdma_ring.bus_cmd_start);	command++;	/* set stop command */	command->command = cpu_to_le16(DBDMA_STOP);	/* ok, let's set the serial format and stuff */	switch (runtime->format) {	/* 16 bit formats */	case SNDRV_PCM_FORMAT_S16_BE:	case SNDRV_PCM_FORMAT_U16_BE:		/* FIXME: if we add different bus factors we need to		 * do more here!! */		bi.bus_factor = 0;		list_for_each_entry(cii, &i2sdev->sound.codec_list, list) {			bi.bus_factor = cii->codec->bus_factor;			break;		}		if (!bi.bus_factor) {			result = -ENODEV;			goto out_unlock;		}		input_16bit = 1;		break;	case SNDRV_PCM_FORMAT_S32_BE:	case SNDRV_PCM_FORMAT_U32_BE:		/* force 64x bus speed, otherwise the data cannot be		 * transferred quickly enough! */		bi.bus_factor = 64;		input_16bit = 0;		break;	default:		result = -EINVAL;		goto out_unlock;	}	/* we assume all sysclocks are the same! */	list_for_each_entry(cii, &i2sdev->sound.codec_list, list) {		bi.sysclock_factor = cii->codec->sysclock_factor;		break;	}	if (clock_and_divisors(bi.sysclock_factor,			       bi.bus_factor,			       runtime->rate,			       &sfr) < 0) {		result = -EINVAL;		goto out_unlock;	}	switch (bi.bus_factor) {	case 32:		sfr |= I2S_SF_SERIAL_FORMAT_I2S_32X;		break;	case 64:		sfr |= I2S_SF_SERIAL_FORMAT_I2S_64X;		break;	}	/* FIXME: THIS ASSUMES MASTER ALL THE TIME */	sfr |= I2S_SF_SCLK_MASTER;	list_for_each_entry(cii, &i2sdev->sound.codec_list, list) {		int err = 0;		if (cii->codec->prepare)			err = cii->codec->prepare(cii, &bi, pi->substream);		if (err) {			result = err;			goto out_unlock;		}	}	/* codecs are fine with it, so set our clocks */	if (input_16bit)		dws =	(2 << I2S_DWS_NUM_CHANNELS_IN_SHIFT) |			(2 << I2S_DWS_NUM_CHANNELS_OUT_SHIFT) |			I2S_DWS_DATA_IN_16BIT | I2S_DWS_DATA_OUT_16BIT;	else		dws =	(2 << I2S_DWS_NUM_CHANNELS_IN_SHIFT) |			(2 << I2S_DWS_NUM_CHANNELS_OUT_SHIFT) |			I2S_DWS_DATA_IN_24BIT | I2S_DWS_DATA_OUT_24BIT;	/* early exit if already programmed correctly */	/* not locking these is fine since we touch them only in this function */	if (in_le32(&i2sdev->intfregs->serial_format) == sfr	 && in_le32(&i2sdev->intfregs->data_word_sizes) == dws)		goto out_unlock;	/* let's notify the codecs about clocks going away.	 * For now we only do mastering on the i2s cell... */	list_for_each_entry(cii, &i2sdev->sound.codec_list, list)		if (cii->codec->switch_clock)			cii->codec->switch_clock(cii, CLOCK_SWITCH_PREPARE_SLAVE);	i2sbus_control_enable(i2sdev->control, i2sdev);	i2sbus_control_cell(i2sdev->control, i2sdev, 1);	out_le32(&i2sdev->intfregs->intr_ctl, I2S_PENDING_CLOCKS_STOPPED);	i2sbus_control_clock(i2sdev->control, i2sdev, 0);	msleep(1);	/* wait for clock stopped. This can apparently take a while... */	cnt = 100;	while (cnt-- &&	    !(in_le32(&i2sdev->intfregs->intr_ctl) & I2S_PENDING_CLOCKS_STOPPED)) {		msleep(5);	}	out_le32(&i2sdev->intfregs->intr_ctl, I2S_PENDING_CLOCKS_STOPPED);	/* not locking these is fine since we touch them only in this function */	out_le32(&i2sdev->intfregs->serial_format, sfr);	out_le32(&i2sdev->intfregs->data_word_sizes, dws);        i2sbus_control_enable(i2sdev->control, i2sdev);        i2sbus_control_cell(i2sdev->control, i2sdev, 1);        i2sbus_control_clock(i2sdev->control, i2sdev, 1);	msleep(1);	list_for_each_entry(cii, &i2sdev->sound.codec_list, list)		if (cii->codec->switch_clock)			cii->codec->switch_clock(cii, CLOCK_SWITCH_SLAVE); out_unlock:	mutex_unlock(&i2sdev->lock);	return result;}#ifdef CONFIG_PMvoid i2sbus_pcm_prepare_both(struct i2sbus_dev *i2sdev){	i2sbus_pcm_prepare(i2sdev, 0);	i2sbus_pcm_prepare(i2sdev, 1);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -