staticinlinevoid 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;
}
}
staticint 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;
/* 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 &= pcm_format_to_bits(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;
err = snd_pcm_hw_constraint_integer(pi->substream->runtime,
SNDRV_PCM_HW_PARAM_PERIODS); if (err < 0) {
result = err; goto out_unlock;
}
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;
}
}
}
staticint 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; unsignedint 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;
}
/* 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);
/* 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);
staticint i2sbus_pcm_trigger(struct i2sbus_dev *i2sdev, int in, int cmd)
{ struct codec_info_item *cii; struct pcm_info *pi; int result = 0; unsignedlong flags;
spin_lock_irqsave(&i2sdev->low_lock, flags);
get_pcm_info(i2sdev, in, &pi, NULL);
switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: if (pi->dbdma_ring.running) {
result = -EALREADY; goto out_unlock;
}
list_for_each_entry(cii, &i2sdev->sound.codec_list, list) if (cii->codec->start)
cii->codec->start(cii, pi->substream);
pi->dbdma_ring.running = 1;
if (pi->dbdma_ring.stopping) { /* Clear the S0 bit, then see if we stopped yet */
out_le32(&pi->dbdma->control, 1 << 16); if (in_le32(&pi->dbdma->status) & ACTIVE) { /* possible race here? */
udelay(10); if (in_le32(&pi->dbdma->status) & ACTIVE) {
pi->dbdma_ring.stopping = 0; goto out_unlock; /* keep running */
}
}
}
/* make sure RUN, PAUSE and S0 bits are cleared */
out_le32(&pi->dbdma->control, (RUN | PAUSE | 1) << 16);
/* write dma command buffer address to the dbdma chip */
out_le32(&pi->dbdma->cmdptr, pi->dbdma_ring.bus_cmd_start);
/* initialize the frame count and current period */
pi->current_period = 0;
pi->frame_count = in_le32(&i2sdev->intfregs->frame_count);
/* set the DMA controller running */
out_le32(&pi->dbdma->control, (RUN << 16) | RUN);
/* off you go! */ break;
case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: if (!pi->dbdma_ring.running) {
result = -EALREADY; goto out_unlock;
}
pi->dbdma_ring.running = 0;
/* Set the S0 bit to make the DMA branch to the stop cmd */
out_le32(&pi->dbdma->control, (1 << 16) | 1);
pi->dbdma_ring.stopping = 1;
list_for_each_entry(cii, &i2sdev->sound.codec_list, list) if (cii->codec->stop)
cii->codec->stop(cii, pi->substream); break; default:
result = -EINVAL; goto out_unlock;
}
fc = in_le32(&i2sdev->intfregs->frame_count);
fc = fc - pi->frame_count;
if (fc >= pi->substream->runtime->buffer_size)
fc %= pi->substream->runtime->buffer_size; return fc;
}
staticinlinevoid handle_interrupt(struct i2sbus_dev *i2sdev, int in)
{ struct pcm_info *pi;
u32 fc, nframes;
u32 status; int timeout, i; int dma_stopped = 0; struct snd_pcm_runtime *runtime;
spin_lock(&i2sdev->low_lock);
get_pcm_info(i2sdev, in, &pi, NULL); if (!pi->dbdma_ring.running && !pi->dbdma_ring.stopping) goto out_unlock;
i = pi->current_period;
runtime = pi->substream->runtime; while (pi->dbdma_ring.cmds[i].xfer_status) { if (le16_to_cpu(pi->dbdma_ring.cmds[i].xfer_status) & BT) /* * BT is the branch taken bit. If it took a branch * it is because we set the S0 bit to make it * branch to the stop command.
*/
dma_stopped = 1;
pi->dbdma_ring.cmds[i].xfer_status = 0;
if (++i >= runtime->periods) {
i = 0;
pi->frame_count += runtime->buffer_size;
}
pi->current_period = i;
/* * Check the frame count. The DMA tends to get a bit * ahead of the frame counter, which confuses the core.
*/
fc = in_le32(&i2sdev->intfregs->frame_count);
nframes = i * runtime->period_size; if (fc < pi->frame_count + nframes)
pi->frame_count = fc - nframes;
}
if (dma_stopped) {
timeout = 1000; for (;;) {
status = in_le32(&pi->dbdma->status); if (!(status & ACTIVE) && (!in || (status & 0x80))) break; if (--timeout <= 0) {
printk(KERN_ERR "i2sbus: timed out " "waiting for DMA to stop!\n"); break;
}
udelay(1);
}
/* Turn off DMA controller, clear S0 bit */
out_le32(&pi->dbdma->control, (RUN | PAUSE | 1) << 16);
pi->dbdma_ring.stopping = 0; if (pi->stop_completion)
complete(pi->stop_completion);
}
if (!pi->dbdma_ring.running) goto out_unlock;
spin_unlock(&i2sdev->low_lock); /* may call _trigger again, hence needs to be unlocked */
snd_pcm_period_elapsed(pi->substream); return;
if (!ci->transfers || !ci->transfers->formats
|| !ci->transfers->rates || !ci->usable) return -EINVAL;
/* we currently code the i2s transfer on the clock, and support only
* 32 and 64 */ if (ci->bus_factor != 32 && ci->bus_factor != 64) return -EINVAL;
/* If you want to fix this, you need to keep track of what transport infos * are to be used, which codecs they belong to, and then fix all the
* sysclock/busclock stuff above to depend on which is usable */
list_for_each_entry(cii, &dev->codec_list, list) { if (cii->codec->sysclock_factor != ci->sysclock_factor) {
printk(KERN_DEBUG "cannot yet handle multiple different sysclocks!\n"); return -EINVAL;
} if (cii->codec->bus_factor != ci->bus_factor) {
printk(KERN_DEBUG "cannot yet handle multiple different bus clocks!\n"); return -EINVAL;
}
}
tmp = ci->transfers; while (tmp->formats && tmp->rates) { if (tmp->transfer_in)
in = 1; else
out = 1;
tmp++;
}
cii = kzalloc(sizeof(struct codec_info_item), GFP_KERNEL); if (!cii) return -ENOMEM;
/* use the private data to point to the codec info */
cii->sdev = soundbus_dev_get(dev);
cii->codec = ci;
cii->codec_data = data;
if (!cii->sdev) {
printk(KERN_DEBUG "i2sbus: failed to get soundbus dev reference\n");
err = -ENODEV; goto out_free_cii;
}
if (!try_module_get(THIS_MODULE)) {
printk(KERN_DEBUG "i2sbus: failed to get module reference!\n");
err = -EBUSY; goto out_put_sdev;
}
if (!try_module_get(ci->owner)) {
printk(KERN_DEBUG "i2sbus: failed to get module reference to codec owner!\n");
err = -EBUSY; goto out_put_this_module;
}
if (!dev->pcm) {
err = snd_pcm_new(card, dev->pcmname, dev->pcmid, 0, 0,
&dev->pcm); if (err) {
printk(KERN_DEBUG "i2sbus: failed to create pcm\n"); goto out_put_ci_module;
}
}
/* ALSA yet again sucks.
* If it is ever fixed, remove this line. See below. */
out = in = 1;
if (!i2sdev->out.created && out) { if (dev->pcm->card != card) { /* eh? */
printk(KERN_ERR "Can't attach same bus to different cards!\n");
err = -EINVAL; goto out_put_ci_module;
}
err = snd_pcm_new_stream(dev->pcm, SNDRV_PCM_STREAM_PLAYBACK, 1); if (err) goto out_put_ci_module;
snd_pcm_set_ops(dev->pcm, SNDRV_PCM_STREAM_PLAYBACK,
&i2sbus_playback_ops);
dev->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].dev->parent =
&dev->ofdev.dev;
i2sdev->out.created = 1;
}
if (!i2sdev->in.created && in) { if (dev->pcm->card != card) {
printk(KERN_ERR "Can't attach same bus to different cards!\n");
err = -EINVAL; goto out_put_ci_module;
}
err = snd_pcm_new_stream(dev->pcm, SNDRV_PCM_STREAM_CAPTURE, 1); if (err) goto out_put_ci_module;
snd_pcm_set_ops(dev->pcm, SNDRV_PCM_STREAM_CAPTURE,
&i2sbus_record_ops);
dev->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].dev->parent =
&dev->ofdev.dev;
i2sdev->in.created = 1;
}
/* so we have to register the pcm after adding any substream * to it because alsa doesn't create the devices for the * substreams when we add them later. * Therefore, force in and out on both busses (above) and * register the pcm now instead of just after creating it.
*/
err = snd_device_register(card, dev->pcm); if (err) {
printk(KERN_ERR "i2sbus: error registering new pcm\n"); goto out_put_ci_module;
} /* no errors any more, so let's add this to our list */
list_add(&cii->list, &dev->codec_list);
list_for_each_entry(i, &dev->codec_list, list) { if (i->codec_data == data) {
cii = i; break;
}
} if (cii) {
list_del(&cii->list);
module_put(cii->codec->owner);
kfree(cii);
} /* no more codecs, but still a pcm? */ if (list_empty(&dev->codec_list) && dev->pcm) { /* the actual cleanup is done by the callback above! */
snd_device_free(dev->pcm->card, dev->pcm);
}
}
Messung V0.5
¤ Dauer der Verarbeitung: 0.23 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.