162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Support for Digigram Lola PCI-e boards 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/init.h> 1062306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1162306a36Sopenharmony_ci#include <linux/pci.h> 1262306a36Sopenharmony_ci#include <linux/delay.h> 1362306a36Sopenharmony_ci#include <sound/core.h> 1462306a36Sopenharmony_ci#include <sound/pcm.h> 1562306a36Sopenharmony_ci#include "lola.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define LOLA_MAX_BDL_ENTRIES 8 1862306a36Sopenharmony_ci#define LOLA_MAX_BUF_SIZE (1024*1024*1024) 1962306a36Sopenharmony_ci#define LOLA_BDL_ENTRY_SIZE (16 * 16) 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic struct lola_pcm *lola_get_pcm(struct snd_pcm_substream *substream) 2262306a36Sopenharmony_ci{ 2362306a36Sopenharmony_ci struct lola *chip = snd_pcm_substream_chip(substream); 2462306a36Sopenharmony_ci return &chip->pcm[substream->stream]; 2562306a36Sopenharmony_ci} 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic struct lola_stream *lola_get_stream(struct snd_pcm_substream *substream) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci struct lola_pcm *pcm = lola_get_pcm(substream); 3062306a36Sopenharmony_ci unsigned int idx = substream->number; 3162306a36Sopenharmony_ci return &pcm->streams[idx]; 3262306a36Sopenharmony_ci} 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic unsigned int lola_get_lrc(struct lola *chip) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci return lola_readl(chip, BAR1, LRC); 3762306a36Sopenharmony_ci} 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic unsigned int lola_get_tstamp(struct lola *chip, bool quick_no_sync) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci unsigned int tstamp = lola_get_lrc(chip) >> 8; 4262306a36Sopenharmony_ci if (chip->granularity) { 4362306a36Sopenharmony_ci unsigned int wait_banks = quick_no_sync ? 0 : 8; 4462306a36Sopenharmony_ci tstamp += (wait_banks + 1) * chip->granularity - 1; 4562306a36Sopenharmony_ci tstamp -= tstamp % chip->granularity; 4662306a36Sopenharmony_ci } 4762306a36Sopenharmony_ci return tstamp << 8; 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/* clear any pending interrupt status */ 5162306a36Sopenharmony_cistatic void lola_stream_clear_pending_irq(struct lola *chip, 5262306a36Sopenharmony_ci struct lola_stream *str) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci unsigned int val = lola_dsd_read(chip, str->dsd, STS); 5562306a36Sopenharmony_ci val &= LOLA_DSD_STS_DESE | LOLA_DSD_STS_BCIS; 5662306a36Sopenharmony_ci if (val) 5762306a36Sopenharmony_ci lola_dsd_write(chip, str->dsd, STS, val); 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic void lola_stream_start(struct lola *chip, struct lola_stream *str, 6162306a36Sopenharmony_ci unsigned int tstamp) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci lola_stream_clear_pending_irq(chip, str); 6462306a36Sopenharmony_ci lola_dsd_write(chip, str->dsd, CTL, 6562306a36Sopenharmony_ci LOLA_DSD_CTL_SRUN | 6662306a36Sopenharmony_ci LOLA_DSD_CTL_IOCE | 6762306a36Sopenharmony_ci LOLA_DSD_CTL_DEIE | 6862306a36Sopenharmony_ci LOLA_DSD_CTL_VLRCV | 6962306a36Sopenharmony_ci tstamp); 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic void lola_stream_stop(struct lola *chip, struct lola_stream *str, 7362306a36Sopenharmony_ci unsigned int tstamp) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci lola_dsd_write(chip, str->dsd, CTL, 7662306a36Sopenharmony_ci LOLA_DSD_CTL_IOCE | 7762306a36Sopenharmony_ci LOLA_DSD_CTL_DEIE | 7862306a36Sopenharmony_ci LOLA_DSD_CTL_VLRCV | 7962306a36Sopenharmony_ci tstamp); 8062306a36Sopenharmony_ci lola_stream_clear_pending_irq(chip, str); 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic void wait_for_srst_clear(struct lola *chip, struct lola_stream *str) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci unsigned long end_time = jiffies + msecs_to_jiffies(200); 8662306a36Sopenharmony_ci while (time_before(jiffies, end_time)) { 8762306a36Sopenharmony_ci unsigned int val; 8862306a36Sopenharmony_ci val = lola_dsd_read(chip, str->dsd, CTL); 8962306a36Sopenharmony_ci if (!(val & LOLA_DSD_CTL_SRST)) 9062306a36Sopenharmony_ci return; 9162306a36Sopenharmony_ci msleep(1); 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci dev_warn(chip->card->dev, "SRST not clear (stream %d)\n", str->dsd); 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic int lola_stream_wait_for_fifo(struct lola *chip, 9762306a36Sopenharmony_ci struct lola_stream *str, 9862306a36Sopenharmony_ci bool ready) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci unsigned int val = ready ? LOLA_DSD_STS_FIFORDY : 0; 10162306a36Sopenharmony_ci unsigned long end_time = jiffies + msecs_to_jiffies(200); 10262306a36Sopenharmony_ci while (time_before(jiffies, end_time)) { 10362306a36Sopenharmony_ci unsigned int reg = lola_dsd_read(chip, str->dsd, STS); 10462306a36Sopenharmony_ci if ((reg & LOLA_DSD_STS_FIFORDY) == val) 10562306a36Sopenharmony_ci return 0; 10662306a36Sopenharmony_ci msleep(1); 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci dev_warn(chip->card->dev, "FIFO not ready (stream %d)\n", str->dsd); 10962306a36Sopenharmony_ci return -EIO; 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci/* sync for FIFO ready/empty for all linked streams; 11362306a36Sopenharmony_ci * clear paused flag when FIFO gets ready again 11462306a36Sopenharmony_ci */ 11562306a36Sopenharmony_cistatic int lola_sync_wait_for_fifo(struct lola *chip, 11662306a36Sopenharmony_ci struct snd_pcm_substream *substream, 11762306a36Sopenharmony_ci bool ready) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci unsigned int val = ready ? LOLA_DSD_STS_FIFORDY : 0; 12062306a36Sopenharmony_ci unsigned long end_time = jiffies + msecs_to_jiffies(200); 12162306a36Sopenharmony_ci struct snd_pcm_substream *s; 12262306a36Sopenharmony_ci int pending = 0; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci while (time_before(jiffies, end_time)) { 12562306a36Sopenharmony_ci pending = 0; 12662306a36Sopenharmony_ci snd_pcm_group_for_each_entry(s, substream) { 12762306a36Sopenharmony_ci struct lola_stream *str; 12862306a36Sopenharmony_ci if (s->pcm->card != substream->pcm->card) 12962306a36Sopenharmony_ci continue; 13062306a36Sopenharmony_ci str = lola_get_stream(s); 13162306a36Sopenharmony_ci if (str->prepared && str->paused) { 13262306a36Sopenharmony_ci unsigned int reg; 13362306a36Sopenharmony_ci reg = lola_dsd_read(chip, str->dsd, STS); 13462306a36Sopenharmony_ci if ((reg & LOLA_DSD_STS_FIFORDY) != val) { 13562306a36Sopenharmony_ci pending = str->dsd + 1; 13662306a36Sopenharmony_ci break; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci if (ready) 13962306a36Sopenharmony_ci str->paused = 0; 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci if (!pending) 14362306a36Sopenharmony_ci return 0; 14462306a36Sopenharmony_ci msleep(1); 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci dev_warn(chip->card->dev, "FIFO not ready (pending %d)\n", pending - 1); 14762306a36Sopenharmony_ci return -EIO; 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci/* finish pause - prepare for a new resume */ 15162306a36Sopenharmony_cistatic void lola_sync_pause(struct lola *chip, 15262306a36Sopenharmony_ci struct snd_pcm_substream *substream) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci struct snd_pcm_substream *s; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci lola_sync_wait_for_fifo(chip, substream, false); 15762306a36Sopenharmony_ci snd_pcm_group_for_each_entry(s, substream) { 15862306a36Sopenharmony_ci struct lola_stream *str; 15962306a36Sopenharmony_ci if (s->pcm->card != substream->pcm->card) 16062306a36Sopenharmony_ci continue; 16162306a36Sopenharmony_ci str = lola_get_stream(s); 16262306a36Sopenharmony_ci if (str->paused && str->prepared) 16362306a36Sopenharmony_ci lola_dsd_write(chip, str->dsd, CTL, LOLA_DSD_CTL_SRUN | 16462306a36Sopenharmony_ci LOLA_DSD_CTL_IOCE | LOLA_DSD_CTL_DEIE); 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci lola_sync_wait_for_fifo(chip, substream, true); 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic void lola_stream_reset(struct lola *chip, struct lola_stream *str) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci if (str->prepared) { 17262306a36Sopenharmony_ci if (str->paused) 17362306a36Sopenharmony_ci lola_sync_pause(chip, str->substream); 17462306a36Sopenharmony_ci str->prepared = 0; 17562306a36Sopenharmony_ci lola_dsd_write(chip, str->dsd, CTL, 17662306a36Sopenharmony_ci LOLA_DSD_CTL_IOCE | LOLA_DSD_CTL_DEIE); 17762306a36Sopenharmony_ci lola_stream_wait_for_fifo(chip, str, false); 17862306a36Sopenharmony_ci lola_stream_clear_pending_irq(chip, str); 17962306a36Sopenharmony_ci lola_dsd_write(chip, str->dsd, CTL, LOLA_DSD_CTL_SRST); 18062306a36Sopenharmony_ci lola_dsd_write(chip, str->dsd, LVI, 0); 18162306a36Sopenharmony_ci lola_dsd_write(chip, str->dsd, BDPU, 0); 18262306a36Sopenharmony_ci lola_dsd_write(chip, str->dsd, BDPL, 0); 18362306a36Sopenharmony_ci wait_for_srst_clear(chip, str); 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic const struct snd_pcm_hardware lola_pcm_hw = { 18862306a36Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | 18962306a36Sopenharmony_ci SNDRV_PCM_INFO_INTERLEAVED | 19062306a36Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 19162306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | 19262306a36Sopenharmony_ci SNDRV_PCM_INFO_PAUSE), 19362306a36Sopenharmony_ci .formats = (SNDRV_PCM_FMTBIT_S16_LE | 19462306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_LE | 19562306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE | 19662306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_FLOAT_LE), 19762306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_192000, 19862306a36Sopenharmony_ci .rate_min = 8000, 19962306a36Sopenharmony_ci .rate_max = 192000, 20062306a36Sopenharmony_ci .channels_min = 1, 20162306a36Sopenharmony_ci .channels_max = 2, 20262306a36Sopenharmony_ci .buffer_bytes_max = LOLA_MAX_BUF_SIZE, 20362306a36Sopenharmony_ci .period_bytes_min = 128, 20462306a36Sopenharmony_ci .period_bytes_max = LOLA_MAX_BUF_SIZE / 2, 20562306a36Sopenharmony_ci .periods_min = 2, 20662306a36Sopenharmony_ci .periods_max = LOLA_MAX_BDL_ENTRIES, 20762306a36Sopenharmony_ci .fifo_size = 0, 20862306a36Sopenharmony_ci}; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic int lola_pcm_open(struct snd_pcm_substream *substream) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci struct lola *chip = snd_pcm_substream_chip(substream); 21362306a36Sopenharmony_ci struct lola_pcm *pcm = lola_get_pcm(substream); 21462306a36Sopenharmony_ci struct lola_stream *str = lola_get_stream(substream); 21562306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci mutex_lock(&chip->open_mutex); 21862306a36Sopenharmony_ci if (str->opened) { 21962306a36Sopenharmony_ci mutex_unlock(&chip->open_mutex); 22062306a36Sopenharmony_ci return -EBUSY; 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci str->substream = substream; 22362306a36Sopenharmony_ci str->master = NULL; 22462306a36Sopenharmony_ci str->opened = 1; 22562306a36Sopenharmony_ci runtime->hw = lola_pcm_hw; 22662306a36Sopenharmony_ci runtime->hw.channels_max = pcm->num_streams - str->index; 22762306a36Sopenharmony_ci if (chip->sample_rate) { 22862306a36Sopenharmony_ci /* sample rate is locked */ 22962306a36Sopenharmony_ci runtime->hw.rate_min = chip->sample_rate; 23062306a36Sopenharmony_ci runtime->hw.rate_max = chip->sample_rate; 23162306a36Sopenharmony_ci } else { 23262306a36Sopenharmony_ci runtime->hw.rate_min = chip->sample_rate_min; 23362306a36Sopenharmony_ci runtime->hw.rate_max = chip->sample_rate_max; 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci chip->ref_count_rate++; 23662306a36Sopenharmony_ci snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); 23762306a36Sopenharmony_ci /* period size = multiple of chip->granularity (8, 16 or 32 frames)*/ 23862306a36Sopenharmony_ci snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 23962306a36Sopenharmony_ci chip->granularity); 24062306a36Sopenharmony_ci snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 24162306a36Sopenharmony_ci chip->granularity); 24262306a36Sopenharmony_ci mutex_unlock(&chip->open_mutex); 24362306a36Sopenharmony_ci return 0; 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic void lola_cleanup_slave_streams(struct lola_pcm *pcm, 24762306a36Sopenharmony_ci struct lola_stream *str) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci int i; 25062306a36Sopenharmony_ci for (i = str->index + 1; i < pcm->num_streams; i++) { 25162306a36Sopenharmony_ci struct lola_stream *s = &pcm->streams[i]; 25262306a36Sopenharmony_ci if (s->master != str) 25362306a36Sopenharmony_ci break; 25462306a36Sopenharmony_ci s->master = NULL; 25562306a36Sopenharmony_ci s->opened = 0; 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic int lola_pcm_close(struct snd_pcm_substream *substream) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci struct lola *chip = snd_pcm_substream_chip(substream); 26262306a36Sopenharmony_ci struct lola_stream *str = lola_get_stream(substream); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci mutex_lock(&chip->open_mutex); 26562306a36Sopenharmony_ci if (str->substream == substream) { 26662306a36Sopenharmony_ci str->substream = NULL; 26762306a36Sopenharmony_ci str->opened = 0; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci if (--chip->ref_count_rate == 0) { 27062306a36Sopenharmony_ci /* release sample rate */ 27162306a36Sopenharmony_ci chip->sample_rate = 0; 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci mutex_unlock(&chip->open_mutex); 27462306a36Sopenharmony_ci return 0; 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_cistatic int lola_pcm_hw_params(struct snd_pcm_substream *substream, 27862306a36Sopenharmony_ci struct snd_pcm_hw_params *hw_params) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci struct lola_stream *str = lola_get_stream(substream); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci str->bufsize = 0; 28362306a36Sopenharmony_ci str->period_bytes = 0; 28462306a36Sopenharmony_ci str->format_verb = 0; 28562306a36Sopenharmony_ci return 0; 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_cistatic int lola_pcm_hw_free(struct snd_pcm_substream *substream) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci struct lola *chip = snd_pcm_substream_chip(substream); 29162306a36Sopenharmony_ci struct lola_pcm *pcm = lola_get_pcm(substream); 29262306a36Sopenharmony_ci struct lola_stream *str = lola_get_stream(substream); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci mutex_lock(&chip->open_mutex); 29562306a36Sopenharmony_ci lola_stream_reset(chip, str); 29662306a36Sopenharmony_ci lola_cleanup_slave_streams(pcm, str); 29762306a36Sopenharmony_ci mutex_unlock(&chip->open_mutex); 29862306a36Sopenharmony_ci return 0; 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci/* 30262306a36Sopenharmony_ci * set up a BDL entry 30362306a36Sopenharmony_ci */ 30462306a36Sopenharmony_cistatic int setup_bdle(struct snd_pcm_substream *substream, 30562306a36Sopenharmony_ci struct lola_stream *str, __le32 **bdlp, 30662306a36Sopenharmony_ci int ofs, int size) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci __le32 *bdl = *bdlp; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci while (size > 0) { 31162306a36Sopenharmony_ci dma_addr_t addr; 31262306a36Sopenharmony_ci int chunk; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci if (str->frags >= LOLA_MAX_BDL_ENTRIES) 31562306a36Sopenharmony_ci return -EINVAL; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci addr = snd_pcm_sgbuf_get_addr(substream, ofs); 31862306a36Sopenharmony_ci /* program the address field of the BDL entry */ 31962306a36Sopenharmony_ci bdl[0] = cpu_to_le32((u32)addr); 32062306a36Sopenharmony_ci bdl[1] = cpu_to_le32(upper_32_bits(addr)); 32162306a36Sopenharmony_ci /* program the size field of the BDL entry */ 32262306a36Sopenharmony_ci chunk = snd_pcm_sgbuf_get_chunk_size(substream, ofs, size); 32362306a36Sopenharmony_ci bdl[2] = cpu_to_le32(chunk); 32462306a36Sopenharmony_ci /* program the IOC to enable interrupt 32562306a36Sopenharmony_ci * only when the whole fragment is processed 32662306a36Sopenharmony_ci */ 32762306a36Sopenharmony_ci size -= chunk; 32862306a36Sopenharmony_ci bdl[3] = size ? 0 : cpu_to_le32(0x01); 32962306a36Sopenharmony_ci bdl += 4; 33062306a36Sopenharmony_ci str->frags++; 33162306a36Sopenharmony_ci ofs += chunk; 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci *bdlp = bdl; 33462306a36Sopenharmony_ci return ofs; 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci/* 33862306a36Sopenharmony_ci * set up BDL entries 33962306a36Sopenharmony_ci */ 34062306a36Sopenharmony_cistatic int lola_setup_periods(struct lola *chip, struct lola_pcm *pcm, 34162306a36Sopenharmony_ci struct snd_pcm_substream *substream, 34262306a36Sopenharmony_ci struct lola_stream *str) 34362306a36Sopenharmony_ci{ 34462306a36Sopenharmony_ci __le32 *bdl; 34562306a36Sopenharmony_ci int i, ofs, periods, period_bytes; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci period_bytes = str->period_bytes; 34862306a36Sopenharmony_ci periods = str->bufsize / period_bytes; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci /* program the initial BDL entries */ 35162306a36Sopenharmony_ci bdl = (__le32 *)(pcm->bdl->area + LOLA_BDL_ENTRY_SIZE * str->index); 35262306a36Sopenharmony_ci ofs = 0; 35362306a36Sopenharmony_ci str->frags = 0; 35462306a36Sopenharmony_ci for (i = 0; i < periods; i++) { 35562306a36Sopenharmony_ci ofs = setup_bdle(substream, str, &bdl, ofs, period_bytes); 35662306a36Sopenharmony_ci if (ofs < 0) 35762306a36Sopenharmony_ci goto error; 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci return 0; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci error: 36262306a36Sopenharmony_ci dev_err(chip->card->dev, "Too many BDL entries: buffer=%d, period=%d\n", 36362306a36Sopenharmony_ci str->bufsize, period_bytes); 36462306a36Sopenharmony_ci return -EINVAL; 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_cistatic unsigned int lola_get_format_verb(struct snd_pcm_substream *substream) 36862306a36Sopenharmony_ci{ 36962306a36Sopenharmony_ci unsigned int verb; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci switch (substream->runtime->format) { 37262306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_LE: 37362306a36Sopenharmony_ci verb = 0x00000000; 37462306a36Sopenharmony_ci break; 37562306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S24_LE: 37662306a36Sopenharmony_ci verb = 0x00000200; 37762306a36Sopenharmony_ci break; 37862306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S32_LE: 37962306a36Sopenharmony_ci verb = 0x00000300; 38062306a36Sopenharmony_ci break; 38162306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_FLOAT_LE: 38262306a36Sopenharmony_ci verb = 0x00001300; 38362306a36Sopenharmony_ci break; 38462306a36Sopenharmony_ci default: 38562306a36Sopenharmony_ci return 0; 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci verb |= substream->runtime->channels; 38862306a36Sopenharmony_ci return verb; 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_cistatic int lola_set_stream_config(struct lola *chip, 39262306a36Sopenharmony_ci struct lola_stream *str, 39362306a36Sopenharmony_ci int channels) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci int i, err; 39662306a36Sopenharmony_ci unsigned int verb, val; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci /* set format info for all channels 39962306a36Sopenharmony_ci * (with only one command for the first channel) 40062306a36Sopenharmony_ci */ 40162306a36Sopenharmony_ci err = lola_codec_read(chip, str->nid, LOLA_VERB_SET_STREAM_FORMAT, 40262306a36Sopenharmony_ci str->format_verb, 0, &val, NULL); 40362306a36Sopenharmony_ci if (err < 0) { 40462306a36Sopenharmony_ci dev_err(chip->card->dev, "Cannot set stream format 0x%x\n", 40562306a36Sopenharmony_ci str->format_verb); 40662306a36Sopenharmony_ci return err; 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci /* update stream - channel config */ 41062306a36Sopenharmony_ci for (i = 0; i < channels; i++) { 41162306a36Sopenharmony_ci verb = (str->index << 6) | i; 41262306a36Sopenharmony_ci err = lola_codec_read(chip, str[i].nid, 41362306a36Sopenharmony_ci LOLA_VERB_SET_CHANNEL_STREAMID, 0, verb, 41462306a36Sopenharmony_ci &val, NULL); 41562306a36Sopenharmony_ci if (err < 0) { 41662306a36Sopenharmony_ci dev_err(chip->card->dev, 41762306a36Sopenharmony_ci "Cannot set stream channel %d\n", i); 41862306a36Sopenharmony_ci return err; 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci } 42162306a36Sopenharmony_ci return 0; 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci/* 42562306a36Sopenharmony_ci * set up the SD for streaming 42662306a36Sopenharmony_ci */ 42762306a36Sopenharmony_cistatic int lola_setup_controller(struct lola *chip, struct lola_pcm *pcm, 42862306a36Sopenharmony_ci struct lola_stream *str) 42962306a36Sopenharmony_ci{ 43062306a36Sopenharmony_ci dma_addr_t bdl; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci if (str->prepared) 43362306a36Sopenharmony_ci return -EINVAL; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci /* set up BDL */ 43662306a36Sopenharmony_ci bdl = pcm->bdl->addr + LOLA_BDL_ENTRY_SIZE * str->index; 43762306a36Sopenharmony_ci lola_dsd_write(chip, str->dsd, BDPL, (u32)bdl); 43862306a36Sopenharmony_ci lola_dsd_write(chip, str->dsd, BDPU, upper_32_bits(bdl)); 43962306a36Sopenharmony_ci /* program the stream LVI (last valid index) of the BDL */ 44062306a36Sopenharmony_ci lola_dsd_write(chip, str->dsd, LVI, str->frags - 1); 44162306a36Sopenharmony_ci lola_stream_clear_pending_irq(chip, str); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci lola_dsd_write(chip, str->dsd, CTL, 44462306a36Sopenharmony_ci LOLA_DSD_CTL_IOCE | LOLA_DSD_CTL_DEIE | LOLA_DSD_CTL_SRUN); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci str->prepared = 1; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci return lola_stream_wait_for_fifo(chip, str, true); 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_cistatic int lola_pcm_prepare(struct snd_pcm_substream *substream) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci struct lola *chip = snd_pcm_substream_chip(substream); 45462306a36Sopenharmony_ci struct lola_pcm *pcm = lola_get_pcm(substream); 45562306a36Sopenharmony_ci struct lola_stream *str = lola_get_stream(substream); 45662306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 45762306a36Sopenharmony_ci unsigned int bufsize, period_bytes, format_verb; 45862306a36Sopenharmony_ci int i, err; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci mutex_lock(&chip->open_mutex); 46162306a36Sopenharmony_ci lola_stream_reset(chip, str); 46262306a36Sopenharmony_ci lola_cleanup_slave_streams(pcm, str); 46362306a36Sopenharmony_ci if (str->index + runtime->channels > pcm->num_streams) { 46462306a36Sopenharmony_ci mutex_unlock(&chip->open_mutex); 46562306a36Sopenharmony_ci return -EINVAL; 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci for (i = 1; i < runtime->channels; i++) { 46862306a36Sopenharmony_ci str[i].master = str; 46962306a36Sopenharmony_ci str[i].opened = 1; 47062306a36Sopenharmony_ci } 47162306a36Sopenharmony_ci mutex_unlock(&chip->open_mutex); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci bufsize = snd_pcm_lib_buffer_bytes(substream); 47462306a36Sopenharmony_ci period_bytes = snd_pcm_lib_period_bytes(substream); 47562306a36Sopenharmony_ci format_verb = lola_get_format_verb(substream); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci str->bufsize = bufsize; 47862306a36Sopenharmony_ci str->period_bytes = period_bytes; 47962306a36Sopenharmony_ci str->format_verb = format_verb; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci err = lola_setup_periods(chip, pcm, substream, str); 48262306a36Sopenharmony_ci if (err < 0) 48362306a36Sopenharmony_ci return err; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci err = lola_set_sample_rate(chip, runtime->rate); 48662306a36Sopenharmony_ci if (err < 0) 48762306a36Sopenharmony_ci return err; 48862306a36Sopenharmony_ci chip->sample_rate = runtime->rate; /* sample rate gets locked */ 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci err = lola_set_stream_config(chip, str, runtime->channels); 49162306a36Sopenharmony_ci if (err < 0) 49262306a36Sopenharmony_ci return err; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci err = lola_setup_controller(chip, pcm, str); 49562306a36Sopenharmony_ci if (err < 0) { 49662306a36Sopenharmony_ci lola_stream_reset(chip, str); 49762306a36Sopenharmony_ci return err; 49862306a36Sopenharmony_ci } 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci return 0; 50162306a36Sopenharmony_ci} 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_cistatic int lola_pcm_trigger(struct snd_pcm_substream *substream, int cmd) 50462306a36Sopenharmony_ci{ 50562306a36Sopenharmony_ci struct lola *chip = snd_pcm_substream_chip(substream); 50662306a36Sopenharmony_ci struct lola_stream *str; 50762306a36Sopenharmony_ci struct snd_pcm_substream *s; 50862306a36Sopenharmony_ci unsigned int start; 50962306a36Sopenharmony_ci unsigned int tstamp; 51062306a36Sopenharmony_ci bool sync_streams; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci switch (cmd) { 51362306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 51462306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 51562306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 51662306a36Sopenharmony_ci start = 1; 51762306a36Sopenharmony_ci break; 51862306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 51962306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 52062306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 52162306a36Sopenharmony_ci start = 0; 52262306a36Sopenharmony_ci break; 52362306a36Sopenharmony_ci default: 52462306a36Sopenharmony_ci return -EINVAL; 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci /* 52862306a36Sopenharmony_ci * sample correct synchronization is only needed starting several 52962306a36Sopenharmony_ci * streams. On stop or if only one stream do as quick as possible 53062306a36Sopenharmony_ci */ 53162306a36Sopenharmony_ci sync_streams = (start && snd_pcm_stream_linked(substream)); 53262306a36Sopenharmony_ci tstamp = lola_get_tstamp(chip, !sync_streams); 53362306a36Sopenharmony_ci spin_lock(&chip->reg_lock); 53462306a36Sopenharmony_ci snd_pcm_group_for_each_entry(s, substream) { 53562306a36Sopenharmony_ci if (s->pcm->card != substream->pcm->card) 53662306a36Sopenharmony_ci continue; 53762306a36Sopenharmony_ci str = lola_get_stream(s); 53862306a36Sopenharmony_ci if (start) 53962306a36Sopenharmony_ci lola_stream_start(chip, str, tstamp); 54062306a36Sopenharmony_ci else 54162306a36Sopenharmony_ci lola_stream_stop(chip, str, tstamp); 54262306a36Sopenharmony_ci str->running = start; 54362306a36Sopenharmony_ci str->paused = !start; 54462306a36Sopenharmony_ci snd_pcm_trigger_done(s, substream); 54562306a36Sopenharmony_ci } 54662306a36Sopenharmony_ci spin_unlock(&chip->reg_lock); 54762306a36Sopenharmony_ci return 0; 54862306a36Sopenharmony_ci} 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_cistatic snd_pcm_uframes_t lola_pcm_pointer(struct snd_pcm_substream *substream) 55162306a36Sopenharmony_ci{ 55262306a36Sopenharmony_ci struct lola *chip = snd_pcm_substream_chip(substream); 55362306a36Sopenharmony_ci struct lola_stream *str = lola_get_stream(substream); 55462306a36Sopenharmony_ci unsigned int pos = lola_dsd_read(chip, str->dsd, LPIB); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci if (pos >= str->bufsize) 55762306a36Sopenharmony_ci pos = 0; 55862306a36Sopenharmony_ci return bytes_to_frames(substream->runtime, pos); 55962306a36Sopenharmony_ci} 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_civoid lola_pcm_update(struct lola *chip, struct lola_pcm *pcm, unsigned int bits) 56262306a36Sopenharmony_ci{ 56362306a36Sopenharmony_ci int i; 56462306a36Sopenharmony_ci u8 num_streams = min_t(u8, pcm->num_streams, ARRAY_SIZE(pcm->streams)); 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci for (i = 0; bits && i < num_streams; i++) { 56762306a36Sopenharmony_ci if (bits & (1 << i)) { 56862306a36Sopenharmony_ci struct lola_stream *str = &pcm->streams[i]; 56962306a36Sopenharmony_ci if (str->substream && str->running) 57062306a36Sopenharmony_ci snd_pcm_period_elapsed(str->substream); 57162306a36Sopenharmony_ci bits &= ~(1 << i); 57262306a36Sopenharmony_ci } 57362306a36Sopenharmony_ci } 57462306a36Sopenharmony_ci} 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_cistatic const struct snd_pcm_ops lola_pcm_ops = { 57762306a36Sopenharmony_ci .open = lola_pcm_open, 57862306a36Sopenharmony_ci .close = lola_pcm_close, 57962306a36Sopenharmony_ci .hw_params = lola_pcm_hw_params, 58062306a36Sopenharmony_ci .hw_free = lola_pcm_hw_free, 58162306a36Sopenharmony_ci .prepare = lola_pcm_prepare, 58262306a36Sopenharmony_ci .trigger = lola_pcm_trigger, 58362306a36Sopenharmony_ci .pointer = lola_pcm_pointer, 58462306a36Sopenharmony_ci}; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ciint lola_create_pcm(struct lola *chip) 58762306a36Sopenharmony_ci{ 58862306a36Sopenharmony_ci struct snd_pcm *pcm; 58962306a36Sopenharmony_ci int i, err; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci for (i = 0; i < 2; i++) { 59262306a36Sopenharmony_ci chip->pcm[i].bdl = 59362306a36Sopenharmony_ci snd_devm_alloc_pages(&chip->pci->dev, SNDRV_DMA_TYPE_DEV, 59462306a36Sopenharmony_ci PAGE_SIZE); 59562306a36Sopenharmony_ci if (!chip->pcm[i].bdl) 59662306a36Sopenharmony_ci return -ENOMEM; 59762306a36Sopenharmony_ci } 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci err = snd_pcm_new(chip->card, "Digigram Lola", 0, 60062306a36Sopenharmony_ci chip->pcm[SNDRV_PCM_STREAM_PLAYBACK].num_streams, 60162306a36Sopenharmony_ci chip->pcm[SNDRV_PCM_STREAM_CAPTURE].num_streams, 60262306a36Sopenharmony_ci &pcm); 60362306a36Sopenharmony_ci if (err < 0) 60462306a36Sopenharmony_ci return err; 60562306a36Sopenharmony_ci strscpy(pcm->name, "Digigram Lola", sizeof(pcm->name)); 60662306a36Sopenharmony_ci pcm->private_data = chip; 60762306a36Sopenharmony_ci for (i = 0; i < 2; i++) { 60862306a36Sopenharmony_ci if (chip->pcm[i].num_streams) 60962306a36Sopenharmony_ci snd_pcm_set_ops(pcm, i, &lola_pcm_ops); 61062306a36Sopenharmony_ci } 61162306a36Sopenharmony_ci /* buffer pre-allocation */ 61262306a36Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_SG, 61362306a36Sopenharmony_ci &chip->pci->dev, 61462306a36Sopenharmony_ci 1024 * 64, 32 * 1024 * 1024); 61562306a36Sopenharmony_ci return 0; 61662306a36Sopenharmony_ci} 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci/* 61962306a36Sopenharmony_ci */ 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_cistatic int lola_init_stream(struct lola *chip, struct lola_stream *str, 62262306a36Sopenharmony_ci int idx, int nid, int dir) 62362306a36Sopenharmony_ci{ 62462306a36Sopenharmony_ci unsigned int val; 62562306a36Sopenharmony_ci int err; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci str->nid = nid; 62862306a36Sopenharmony_ci str->index = idx; 62962306a36Sopenharmony_ci str->dsd = idx; 63062306a36Sopenharmony_ci if (dir == PLAY) 63162306a36Sopenharmony_ci str->dsd += MAX_STREAM_IN_COUNT; 63262306a36Sopenharmony_ci err = lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val); 63362306a36Sopenharmony_ci if (err < 0) { 63462306a36Sopenharmony_ci dev_err(chip->card->dev, "Can't read wcaps for 0x%x\n", nid); 63562306a36Sopenharmony_ci return err; 63662306a36Sopenharmony_ci } 63762306a36Sopenharmony_ci if (dir == PLAY) { 63862306a36Sopenharmony_ci /* test TYPE and bits 0..11 (no test bit9 : Digital = 0/1) */ 63962306a36Sopenharmony_ci if ((val & 0x00f00dff) != 0x00000010) { 64062306a36Sopenharmony_ci dev_err(chip->card->dev, 64162306a36Sopenharmony_ci "Invalid wcaps 0x%x for 0x%x\n", 64262306a36Sopenharmony_ci val, nid); 64362306a36Sopenharmony_ci return -EINVAL; 64462306a36Sopenharmony_ci } 64562306a36Sopenharmony_ci } else { 64662306a36Sopenharmony_ci /* test TYPE and bits 0..11 (no test bit9 : Digital = 0/1) 64762306a36Sopenharmony_ci * (bug : ignore bit8: Conn list = 0/1) 64862306a36Sopenharmony_ci */ 64962306a36Sopenharmony_ci if ((val & 0x00f00cff) != 0x00100010) { 65062306a36Sopenharmony_ci dev_err(chip->card->dev, 65162306a36Sopenharmony_ci "Invalid wcaps 0x%x for 0x%x\n", 65262306a36Sopenharmony_ci val, nid); 65362306a36Sopenharmony_ci return -EINVAL; 65462306a36Sopenharmony_ci } 65562306a36Sopenharmony_ci /* test bit9:DIGITAL and bit12:SRC_PRESENT*/ 65662306a36Sopenharmony_ci if ((val & 0x00001200) == 0x00001200) 65762306a36Sopenharmony_ci chip->input_src_caps_mask |= (1 << idx); 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci err = lola_read_param(chip, nid, LOLA_PAR_STREAM_FORMATS, &val); 66162306a36Sopenharmony_ci if (err < 0) { 66262306a36Sopenharmony_ci dev_err(chip->card->dev, "Can't read FORMATS 0x%x\n", nid); 66362306a36Sopenharmony_ci return err; 66462306a36Sopenharmony_ci } 66562306a36Sopenharmony_ci val &= 3; 66662306a36Sopenharmony_ci if (val == 3) 66762306a36Sopenharmony_ci str->can_float = true; 66862306a36Sopenharmony_ci if (!(val & 1)) { 66962306a36Sopenharmony_ci dev_err(chip->card->dev, 67062306a36Sopenharmony_ci "Invalid formats 0x%x for 0x%x", val, nid); 67162306a36Sopenharmony_ci return -EINVAL; 67262306a36Sopenharmony_ci } 67362306a36Sopenharmony_ci return 0; 67462306a36Sopenharmony_ci} 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ciint lola_init_pcm(struct lola *chip, int dir, int *nidp) 67762306a36Sopenharmony_ci{ 67862306a36Sopenharmony_ci struct lola_pcm *pcm = &chip->pcm[dir]; 67962306a36Sopenharmony_ci int i, nid, err; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci nid = *nidp; 68262306a36Sopenharmony_ci for (i = 0; i < pcm->num_streams; i++, nid++) { 68362306a36Sopenharmony_ci err = lola_init_stream(chip, &pcm->streams[i], i, nid, dir); 68462306a36Sopenharmony_ci if (err < 0) 68562306a36Sopenharmony_ci return err; 68662306a36Sopenharmony_ci } 68762306a36Sopenharmony_ci *nidp = nid; 68862306a36Sopenharmony_ci return 0; 68962306a36Sopenharmony_ci} 690