static int oxygen_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct oxygen *chip = snd_pcm_substream_chip(substream); unsigned int channel = oxygen_substream_channel(substream); int err; err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); if (err < 0) return err; oxygen_write32(chip, channel_base_registers[channel], (u32)substream->runtime->dma_addr); if (channel == PCM_MULTICH) { oxygen_write32(chip, OXYGEN_DMA_MULTICH_COUNT, params_buffer_bytes(hw_params) / 4 - 1); oxygen_write32(chip, OXYGEN_DMA_MULTICH_TCOUNT, params_period_bytes(hw_params) / 4 - 1); } else { oxygen_write16(chip, channel_base_registers[channel] + 4, params_buffer_bytes(hw_params) / 4 - 1); oxygen_write16(chip, channel_base_registers[channel] + 6, params_period_bytes(hw_params) / 4 - 1); } return 0; }
static snd_pcm_uframes_t oxygen_pointer(struct snd_pcm_substream *substream) { struct oxygen *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; unsigned int channel = oxygen_substream_channel(substream); u32 curr_addr; /* no spinlock, this read should be atomic */ curr_addr = oxygen_read32(chip, channel_base_registers[channel]); return bytes_to_frames(runtime, curr_addr - (u32)runtime->dma_addr); }
static int oxygen_prepare(struct snd_pcm_substream *substream) { struct oxygen *chip = snd_pcm_substream_chip(substream); unsigned int channel = oxygen_substream_channel(substream); unsigned int channel_mask = 1 << channel; spin_lock_irq(&chip->reg_lock); oxygen_set_bits8(chip, OXYGEN_DMA_FLUSH, channel_mask); oxygen_clear_bits8(chip, OXYGEN_DMA_FLUSH, channel_mask); chip->interrupt_mask |= channel_mask; oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask); spin_unlock_irq(&chip->reg_lock); return 0; }
static int oxygen_trigger(struct snd_pcm_substream *substream, int cmd) { struct oxygen *chip = snd_pcm_substream_chip(substream); struct snd_pcm_substream *s; unsigned int mask = 0; int pausing; switch (cmd) { case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_SUSPEND: pausing = 0; break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: pausing = 1; break; default: return -EINVAL; } snd_pcm_group_for_each_entry(s, substream) { if (snd_pcm_substream_chip(s) == chip) { mask |= 1 << oxygen_substream_channel(s); snd_pcm_trigger_done(s, substream); } } spin_lock(&chip->reg_lock); if (!pausing) { if (cmd == SNDRV_PCM_TRIGGER_START) chip->pcm_running |= mask; else chip->pcm_running &= ~mask; oxygen_write8(chip, OXYGEN_DMA_STATUS, chip->pcm_running); } else { if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH) oxygen_set_bits8(chip, OXYGEN_DMA_PAUSE, mask); else oxygen_clear_bits8(chip, OXYGEN_DMA_PAUSE, mask); } spin_unlock(&chip->reg_lock); return 0; }
static int oxygen_close(struct snd_pcm_substream *substream) { struct oxygen *chip = snd_pcm_substream_chip(substream); unsigned int channel = oxygen_substream_channel(substream); mutex_lock(&chip->mutex); chip->pcm_active &= ~(1 << channel); if (channel == PCM_SPDIF) { chip->controls[CONTROL_SPDIF_PCM]->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO, &chip->controls[CONTROL_SPDIF_PCM]->id); } if (channel == PCM_SPDIF || channel == PCM_MULTICH) oxygen_update_spdif_source(chip); mutex_unlock(&chip->mutex); chip->streams[channel] = NULL; return 0; }