📄 i2sbus-pcm.c
字号:
/* * 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 + -