162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * i2sbus driver -- pcm routines 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/io.h> 962306a36Sopenharmony_ci#include <linux/delay.h> 1062306a36Sopenharmony_ci#include <linux/slab.h> 1162306a36Sopenharmony_ci#include <sound/core.h> 1262306a36Sopenharmony_ci#include <asm/macio.h> 1362306a36Sopenharmony_ci#include <linux/pci.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include "../soundbus.h" 1662306a36Sopenharmony_ci#include "i2sbus.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistatic inline void get_pcm_info(struct i2sbus_dev *i2sdev, int in, 1962306a36Sopenharmony_ci struct pcm_info **pi, struct pcm_info **other) 2062306a36Sopenharmony_ci{ 2162306a36Sopenharmony_ci if (in) { 2262306a36Sopenharmony_ci if (pi) 2362306a36Sopenharmony_ci *pi = &i2sdev->in; 2462306a36Sopenharmony_ci if (other) 2562306a36Sopenharmony_ci *other = &i2sdev->out; 2662306a36Sopenharmony_ci } else { 2762306a36Sopenharmony_ci if (pi) 2862306a36Sopenharmony_ci *pi = &i2sdev->out; 2962306a36Sopenharmony_ci if (other) 3062306a36Sopenharmony_ci *other = &i2sdev->in; 3162306a36Sopenharmony_ci } 3262306a36Sopenharmony_ci} 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic int clock_and_divisors(int mclk, int sclk, int rate, int *out) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci /* sclk must be derived from mclk! */ 3762306a36Sopenharmony_ci if (mclk % sclk) 3862306a36Sopenharmony_ci return -1; 3962306a36Sopenharmony_ci /* derive sclk register value */ 4062306a36Sopenharmony_ci if (i2s_sf_sclkdiv(mclk / sclk, out)) 4162306a36Sopenharmony_ci return -1; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci if (I2S_CLOCK_SPEED_18MHz % (rate * mclk) == 0) { 4462306a36Sopenharmony_ci if (!i2s_sf_mclkdiv(I2S_CLOCK_SPEED_18MHz / (rate * mclk), out)) { 4562306a36Sopenharmony_ci *out |= I2S_SF_CLOCK_SOURCE_18MHz; 4662306a36Sopenharmony_ci return 0; 4762306a36Sopenharmony_ci } 4862306a36Sopenharmony_ci } 4962306a36Sopenharmony_ci if (I2S_CLOCK_SPEED_45MHz % (rate * mclk) == 0) { 5062306a36Sopenharmony_ci if (!i2s_sf_mclkdiv(I2S_CLOCK_SPEED_45MHz / (rate * mclk), out)) { 5162306a36Sopenharmony_ci *out |= I2S_SF_CLOCK_SOURCE_45MHz; 5262306a36Sopenharmony_ci return 0; 5362306a36Sopenharmony_ci } 5462306a36Sopenharmony_ci } 5562306a36Sopenharmony_ci if (I2S_CLOCK_SPEED_49MHz % (rate * mclk) == 0) { 5662306a36Sopenharmony_ci if (!i2s_sf_mclkdiv(I2S_CLOCK_SPEED_49MHz / (rate * mclk), out)) { 5762306a36Sopenharmony_ci *out |= I2S_SF_CLOCK_SOURCE_49MHz; 5862306a36Sopenharmony_ci return 0; 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci } 6162306a36Sopenharmony_ci return -1; 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci#define CHECK_RATE(rate) \ 6562306a36Sopenharmony_ci do { if (rates & SNDRV_PCM_RATE_ ##rate) { \ 6662306a36Sopenharmony_ci int dummy; \ 6762306a36Sopenharmony_ci if (clock_and_divisors(sysclock_factor, \ 6862306a36Sopenharmony_ci bus_factor, rate, &dummy)) \ 6962306a36Sopenharmony_ci rates &= ~SNDRV_PCM_RATE_ ##rate; \ 7062306a36Sopenharmony_ci } } while (0) 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic int i2sbus_pcm_open(struct i2sbus_dev *i2sdev, int in) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci struct pcm_info *pi, *other; 7562306a36Sopenharmony_ci struct soundbus_dev *sdev; 7662306a36Sopenharmony_ci int masks_inited = 0, err; 7762306a36Sopenharmony_ci struct codec_info_item *cii, *rev; 7862306a36Sopenharmony_ci struct snd_pcm_hardware *hw; 7962306a36Sopenharmony_ci u64 formats = 0; 8062306a36Sopenharmony_ci unsigned int rates = 0; 8162306a36Sopenharmony_ci struct transfer_info v; 8262306a36Sopenharmony_ci int result = 0; 8362306a36Sopenharmony_ci int bus_factor = 0, sysclock_factor = 0; 8462306a36Sopenharmony_ci int found_this; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci mutex_lock(&i2sdev->lock); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci get_pcm_info(i2sdev, in, &pi, &other); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci hw = &pi->substream->runtime->hw; 9162306a36Sopenharmony_ci sdev = &i2sdev->sound; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (pi->active) { 9462306a36Sopenharmony_ci /* alsa messed up */ 9562306a36Sopenharmony_ci result = -EBUSY; 9662306a36Sopenharmony_ci goto out_unlock; 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci /* we now need to assign the hw */ 10062306a36Sopenharmony_ci list_for_each_entry(cii, &sdev->codec_list, list) { 10162306a36Sopenharmony_ci struct transfer_info *ti = cii->codec->transfers; 10262306a36Sopenharmony_ci bus_factor = cii->codec->bus_factor; 10362306a36Sopenharmony_ci sysclock_factor = cii->codec->sysclock_factor; 10462306a36Sopenharmony_ci while (ti->formats && ti->rates) { 10562306a36Sopenharmony_ci v = *ti; 10662306a36Sopenharmony_ci if (ti->transfer_in == in 10762306a36Sopenharmony_ci && cii->codec->usable(cii, ti, &v)) { 10862306a36Sopenharmony_ci if (masks_inited) { 10962306a36Sopenharmony_ci formats &= v.formats; 11062306a36Sopenharmony_ci rates &= v.rates; 11162306a36Sopenharmony_ci } else { 11262306a36Sopenharmony_ci formats = v.formats; 11362306a36Sopenharmony_ci rates = v.rates; 11462306a36Sopenharmony_ci masks_inited = 1; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci ti++; 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci if (!masks_inited || !bus_factor || !sysclock_factor) { 12162306a36Sopenharmony_ci result = -ENODEV; 12262306a36Sopenharmony_ci goto out_unlock; 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci /* bus dependent stuff */ 12562306a36Sopenharmony_ci hw->info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | 12662306a36Sopenharmony_ci SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_RESUME | 12762306a36Sopenharmony_ci SNDRV_PCM_INFO_JOINT_DUPLEX; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci CHECK_RATE(5512); 13062306a36Sopenharmony_ci CHECK_RATE(8000); 13162306a36Sopenharmony_ci CHECK_RATE(11025); 13262306a36Sopenharmony_ci CHECK_RATE(16000); 13362306a36Sopenharmony_ci CHECK_RATE(22050); 13462306a36Sopenharmony_ci CHECK_RATE(32000); 13562306a36Sopenharmony_ci CHECK_RATE(44100); 13662306a36Sopenharmony_ci CHECK_RATE(48000); 13762306a36Sopenharmony_ci CHECK_RATE(64000); 13862306a36Sopenharmony_ci CHECK_RATE(88200); 13962306a36Sopenharmony_ci CHECK_RATE(96000); 14062306a36Sopenharmony_ci CHECK_RATE(176400); 14162306a36Sopenharmony_ci CHECK_RATE(192000); 14262306a36Sopenharmony_ci hw->rates = rates; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci /* well. the codec might want 24 bits only, and we'll 14562306a36Sopenharmony_ci * ever only transfer 24 bits, but they are top-aligned! 14662306a36Sopenharmony_ci * So for alsa, we claim that we're doing full 32 bit 14762306a36Sopenharmony_ci * while in reality we'll ignore the lower 8 bits of 14862306a36Sopenharmony_ci * that when doing playback (they're transferred as 0 14962306a36Sopenharmony_ci * as far as I know, no codecs we have are 32-bit capable 15062306a36Sopenharmony_ci * so I can't really test) and when doing recording we'll 15162306a36Sopenharmony_ci * always have those lower 8 bits recorded as 0 */ 15262306a36Sopenharmony_ci if (formats & SNDRV_PCM_FMTBIT_S24_BE) 15362306a36Sopenharmony_ci formats |= SNDRV_PCM_FMTBIT_S32_BE; 15462306a36Sopenharmony_ci if (formats & SNDRV_PCM_FMTBIT_U24_BE) 15562306a36Sopenharmony_ci formats |= SNDRV_PCM_FMTBIT_U32_BE; 15662306a36Sopenharmony_ci /* now mask off what we can support. I suppose we could 15762306a36Sopenharmony_ci * also support S24_3LE and some similar formats, but I 15862306a36Sopenharmony_ci * doubt there's a codec that would be able to use that, 15962306a36Sopenharmony_ci * so we don't support it here. */ 16062306a36Sopenharmony_ci hw->formats = formats & (SNDRV_PCM_FMTBIT_S16_BE | 16162306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_U16_BE | 16262306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_BE | 16362306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_U32_BE); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci /* we need to set the highest and lowest rate possible. 16662306a36Sopenharmony_ci * These are the highest and lowest rates alsa can 16762306a36Sopenharmony_ci * support properly in its bitfield. 16862306a36Sopenharmony_ci * Below, we'll use that to restrict to the rate 16962306a36Sopenharmony_ci * currently in use (if any). */ 17062306a36Sopenharmony_ci hw->rate_min = 5512; 17162306a36Sopenharmony_ci hw->rate_max = 192000; 17262306a36Sopenharmony_ci /* if the other stream is active, then we can only 17362306a36Sopenharmony_ci * support what it is currently using. 17462306a36Sopenharmony_ci * FIXME: I lied. This comment is wrong. We can support 17562306a36Sopenharmony_ci * anything that works with the same serial format, ie. 17662306a36Sopenharmony_ci * when recording 24 bit sound we can well play 16 bit 17762306a36Sopenharmony_ci * sound at the same time iff using the same transfer mode. 17862306a36Sopenharmony_ci */ 17962306a36Sopenharmony_ci if (other->active) { 18062306a36Sopenharmony_ci /* FIXME: is this guaranteed by the alsa api? */ 18162306a36Sopenharmony_ci hw->formats &= pcm_format_to_bits(i2sdev->format); 18262306a36Sopenharmony_ci /* see above, restrict rates to the one we already have */ 18362306a36Sopenharmony_ci hw->rate_min = i2sdev->rate; 18462306a36Sopenharmony_ci hw->rate_max = i2sdev->rate; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci hw->channels_min = 2; 18862306a36Sopenharmony_ci hw->channels_max = 2; 18962306a36Sopenharmony_ci /* these are somewhat arbitrary */ 19062306a36Sopenharmony_ci hw->buffer_bytes_max = 131072; 19162306a36Sopenharmony_ci hw->period_bytes_min = 256; 19262306a36Sopenharmony_ci hw->period_bytes_max = 16384; 19362306a36Sopenharmony_ci hw->periods_min = 3; 19462306a36Sopenharmony_ci hw->periods_max = MAX_DBDMA_COMMANDS; 19562306a36Sopenharmony_ci err = snd_pcm_hw_constraint_integer(pi->substream->runtime, 19662306a36Sopenharmony_ci SNDRV_PCM_HW_PARAM_PERIODS); 19762306a36Sopenharmony_ci if (err < 0) { 19862306a36Sopenharmony_ci result = err; 19962306a36Sopenharmony_ci goto out_unlock; 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci list_for_each_entry(cii, &sdev->codec_list, list) { 20262306a36Sopenharmony_ci if (cii->codec->open) { 20362306a36Sopenharmony_ci err = cii->codec->open(cii, pi->substream); 20462306a36Sopenharmony_ci if (err) { 20562306a36Sopenharmony_ci result = err; 20662306a36Sopenharmony_ci /* unwind */ 20762306a36Sopenharmony_ci found_this = 0; 20862306a36Sopenharmony_ci list_for_each_entry_reverse(rev, 20962306a36Sopenharmony_ci &sdev->codec_list, list) { 21062306a36Sopenharmony_ci if (found_this && rev->codec->close) { 21162306a36Sopenharmony_ci rev->codec->close(rev, 21262306a36Sopenharmony_ci pi->substream); 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci if (rev == cii) 21562306a36Sopenharmony_ci found_this = 1; 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci goto out_unlock; 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci out_unlock: 22362306a36Sopenharmony_ci mutex_unlock(&i2sdev->lock); 22462306a36Sopenharmony_ci return result; 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci#undef CHECK_RATE 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic int i2sbus_pcm_close(struct i2sbus_dev *i2sdev, int in) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci struct codec_info_item *cii; 23262306a36Sopenharmony_ci struct pcm_info *pi; 23362306a36Sopenharmony_ci int err = 0, tmp; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci mutex_lock(&i2sdev->lock); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci get_pcm_info(i2sdev, in, &pi, NULL); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci list_for_each_entry(cii, &i2sdev->sound.codec_list, list) { 24062306a36Sopenharmony_ci if (cii->codec->close) { 24162306a36Sopenharmony_ci tmp = cii->codec->close(cii, pi->substream); 24262306a36Sopenharmony_ci if (tmp) 24362306a36Sopenharmony_ci err = tmp; 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci pi->substream = NULL; 24862306a36Sopenharmony_ci pi->active = 0; 24962306a36Sopenharmony_ci mutex_unlock(&i2sdev->lock); 25062306a36Sopenharmony_ci return err; 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_cistatic void i2sbus_wait_for_stop(struct i2sbus_dev *i2sdev, 25462306a36Sopenharmony_ci struct pcm_info *pi) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci unsigned long flags; 25762306a36Sopenharmony_ci DECLARE_COMPLETION_ONSTACK(done); 25862306a36Sopenharmony_ci long timeout; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci spin_lock_irqsave(&i2sdev->low_lock, flags); 26162306a36Sopenharmony_ci if (pi->dbdma_ring.stopping) { 26262306a36Sopenharmony_ci pi->stop_completion = &done; 26362306a36Sopenharmony_ci spin_unlock_irqrestore(&i2sdev->low_lock, flags); 26462306a36Sopenharmony_ci timeout = wait_for_completion_timeout(&done, HZ); 26562306a36Sopenharmony_ci spin_lock_irqsave(&i2sdev->low_lock, flags); 26662306a36Sopenharmony_ci pi->stop_completion = NULL; 26762306a36Sopenharmony_ci if (timeout == 0) { 26862306a36Sopenharmony_ci /* timeout expired, stop dbdma forcefully */ 26962306a36Sopenharmony_ci printk(KERN_ERR "i2sbus_wait_for_stop: timed out\n"); 27062306a36Sopenharmony_ci /* make sure RUN, PAUSE and S0 bits are cleared */ 27162306a36Sopenharmony_ci out_le32(&pi->dbdma->control, (RUN | PAUSE | 1) << 16); 27262306a36Sopenharmony_ci pi->dbdma_ring.stopping = 0; 27362306a36Sopenharmony_ci timeout = 10; 27462306a36Sopenharmony_ci while (in_le32(&pi->dbdma->status) & ACTIVE) { 27562306a36Sopenharmony_ci if (--timeout <= 0) 27662306a36Sopenharmony_ci break; 27762306a36Sopenharmony_ci udelay(1); 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci spin_unlock_irqrestore(&i2sdev->low_lock, flags); 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci#ifdef CONFIG_PM 28562306a36Sopenharmony_civoid i2sbus_wait_for_stop_both(struct i2sbus_dev *i2sdev) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci struct pcm_info *pi; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci get_pcm_info(i2sdev, 0, &pi, NULL); 29062306a36Sopenharmony_ci i2sbus_wait_for_stop(i2sdev, pi); 29162306a36Sopenharmony_ci get_pcm_info(i2sdev, 1, &pi, NULL); 29262306a36Sopenharmony_ci i2sbus_wait_for_stop(i2sdev, pi); 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci#endif 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_cistatic inline int i2sbus_hw_free(struct snd_pcm_substream *substream, int in) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream); 29962306a36Sopenharmony_ci struct pcm_info *pi; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci get_pcm_info(i2sdev, in, &pi, NULL); 30262306a36Sopenharmony_ci if (pi->dbdma_ring.stopping) 30362306a36Sopenharmony_ci i2sbus_wait_for_stop(i2sdev, pi); 30462306a36Sopenharmony_ci return 0; 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cistatic int i2sbus_playback_hw_free(struct snd_pcm_substream *substream) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci return i2sbus_hw_free(substream, 0); 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cistatic int i2sbus_record_hw_free(struct snd_pcm_substream *substream) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci return i2sbus_hw_free(substream, 1); 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_cistatic int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci /* whee. Hard work now. The user has selected a bitrate 32062306a36Sopenharmony_ci * and bit format, so now we have to program our 32162306a36Sopenharmony_ci * I2S controller appropriately. */ 32262306a36Sopenharmony_ci struct snd_pcm_runtime *runtime; 32362306a36Sopenharmony_ci struct dbdma_cmd *command; 32462306a36Sopenharmony_ci int i, periodsize, nperiods; 32562306a36Sopenharmony_ci dma_addr_t offset; 32662306a36Sopenharmony_ci struct bus_info bi; 32762306a36Sopenharmony_ci struct codec_info_item *cii; 32862306a36Sopenharmony_ci int sfr = 0; /* serial format register */ 32962306a36Sopenharmony_ci int dws = 0; /* data word sizes reg */ 33062306a36Sopenharmony_ci int input_16bit; 33162306a36Sopenharmony_ci struct pcm_info *pi, *other; 33262306a36Sopenharmony_ci int cnt; 33362306a36Sopenharmony_ci int result = 0; 33462306a36Sopenharmony_ci unsigned int cmd, stopaddr; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci mutex_lock(&i2sdev->lock); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci get_pcm_info(i2sdev, in, &pi, &other); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci if (pi->dbdma_ring.running) { 34162306a36Sopenharmony_ci result = -EBUSY; 34262306a36Sopenharmony_ci goto out_unlock; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci if (pi->dbdma_ring.stopping) 34562306a36Sopenharmony_ci i2sbus_wait_for_stop(i2sdev, pi); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci if (!pi->substream || !pi->substream->runtime) { 34862306a36Sopenharmony_ci result = -EINVAL; 34962306a36Sopenharmony_ci goto out_unlock; 35062306a36Sopenharmony_ci } 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci runtime = pi->substream->runtime; 35362306a36Sopenharmony_ci pi->active = 1; 35462306a36Sopenharmony_ci if (other->active && 35562306a36Sopenharmony_ci ((i2sdev->format != runtime->format) 35662306a36Sopenharmony_ci || (i2sdev->rate != runtime->rate))) { 35762306a36Sopenharmony_ci result = -EINVAL; 35862306a36Sopenharmony_ci goto out_unlock; 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci i2sdev->format = runtime->format; 36262306a36Sopenharmony_ci i2sdev->rate = runtime->rate; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci periodsize = snd_pcm_lib_period_bytes(pi->substream); 36562306a36Sopenharmony_ci nperiods = pi->substream->runtime->periods; 36662306a36Sopenharmony_ci pi->current_period = 0; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci /* generate dbdma command ring first */ 36962306a36Sopenharmony_ci command = pi->dbdma_ring.cmds; 37062306a36Sopenharmony_ci memset(command, 0, (nperiods + 2) * sizeof(struct dbdma_cmd)); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci /* commands to DMA to/from the ring */ 37362306a36Sopenharmony_ci /* 37462306a36Sopenharmony_ci * For input, we need to do a graceful stop; if we abort 37562306a36Sopenharmony_ci * the DMA, we end up with leftover bytes that corrupt 37662306a36Sopenharmony_ci * the next recording. To do this we set the S0 status 37762306a36Sopenharmony_ci * bit and wait for the DMA controller to stop. Each 37862306a36Sopenharmony_ci * command has a branch condition to 37962306a36Sopenharmony_ci * make it branch to a stop command if S0 is set. 38062306a36Sopenharmony_ci * On input we also need to wait for the S7 bit to be 38162306a36Sopenharmony_ci * set before turning off the DMA controller. 38262306a36Sopenharmony_ci * In fact we do the graceful stop for output as well. 38362306a36Sopenharmony_ci */ 38462306a36Sopenharmony_ci offset = runtime->dma_addr; 38562306a36Sopenharmony_ci cmd = (in? INPUT_MORE: OUTPUT_MORE) | BR_IFSET | INTR_ALWAYS; 38662306a36Sopenharmony_ci stopaddr = pi->dbdma_ring.bus_cmd_start + 38762306a36Sopenharmony_ci (nperiods + 1) * sizeof(struct dbdma_cmd); 38862306a36Sopenharmony_ci for (i = 0; i < nperiods; i++, command++, offset += periodsize) { 38962306a36Sopenharmony_ci command->command = cpu_to_le16(cmd); 39062306a36Sopenharmony_ci command->cmd_dep = cpu_to_le32(stopaddr); 39162306a36Sopenharmony_ci command->phy_addr = cpu_to_le32(offset); 39262306a36Sopenharmony_ci command->req_count = cpu_to_le16(periodsize); 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci /* branch back to beginning of ring */ 39662306a36Sopenharmony_ci command->command = cpu_to_le16(DBDMA_NOP | BR_ALWAYS); 39762306a36Sopenharmony_ci command->cmd_dep = cpu_to_le32(pi->dbdma_ring.bus_cmd_start); 39862306a36Sopenharmony_ci command++; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci /* set stop command */ 40162306a36Sopenharmony_ci command->command = cpu_to_le16(DBDMA_STOP); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci /* ok, let's set the serial format and stuff */ 40462306a36Sopenharmony_ci switch (runtime->format) { 40562306a36Sopenharmony_ci /* 16 bit formats */ 40662306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_BE: 40762306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_U16_BE: 40862306a36Sopenharmony_ci /* FIXME: if we add different bus factors we need to 40962306a36Sopenharmony_ci * do more here!! */ 41062306a36Sopenharmony_ci bi.bus_factor = 0; 41162306a36Sopenharmony_ci list_for_each_entry(cii, &i2sdev->sound.codec_list, list) { 41262306a36Sopenharmony_ci bi.bus_factor = cii->codec->bus_factor; 41362306a36Sopenharmony_ci break; 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci if (!bi.bus_factor) { 41662306a36Sopenharmony_ci result = -ENODEV; 41762306a36Sopenharmony_ci goto out_unlock; 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci input_16bit = 1; 42062306a36Sopenharmony_ci break; 42162306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S32_BE: 42262306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_U32_BE: 42362306a36Sopenharmony_ci /* force 64x bus speed, otherwise the data cannot be 42462306a36Sopenharmony_ci * transferred quickly enough! */ 42562306a36Sopenharmony_ci bi.bus_factor = 64; 42662306a36Sopenharmony_ci input_16bit = 0; 42762306a36Sopenharmony_ci break; 42862306a36Sopenharmony_ci default: 42962306a36Sopenharmony_ci result = -EINVAL; 43062306a36Sopenharmony_ci goto out_unlock; 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci /* we assume all sysclocks are the same! */ 43362306a36Sopenharmony_ci list_for_each_entry(cii, &i2sdev->sound.codec_list, list) { 43462306a36Sopenharmony_ci bi.sysclock_factor = cii->codec->sysclock_factor; 43562306a36Sopenharmony_ci break; 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci if (clock_and_divisors(bi.sysclock_factor, 43962306a36Sopenharmony_ci bi.bus_factor, 44062306a36Sopenharmony_ci runtime->rate, 44162306a36Sopenharmony_ci &sfr) < 0) { 44262306a36Sopenharmony_ci result = -EINVAL; 44362306a36Sopenharmony_ci goto out_unlock; 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci switch (bi.bus_factor) { 44662306a36Sopenharmony_ci case 32: 44762306a36Sopenharmony_ci sfr |= I2S_SF_SERIAL_FORMAT_I2S_32X; 44862306a36Sopenharmony_ci break; 44962306a36Sopenharmony_ci case 64: 45062306a36Sopenharmony_ci sfr |= I2S_SF_SERIAL_FORMAT_I2S_64X; 45162306a36Sopenharmony_ci break; 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci /* FIXME: THIS ASSUMES MASTER ALL THE TIME */ 45462306a36Sopenharmony_ci sfr |= I2S_SF_SCLK_MASTER; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci list_for_each_entry(cii, &i2sdev->sound.codec_list, list) { 45762306a36Sopenharmony_ci int err = 0; 45862306a36Sopenharmony_ci if (cii->codec->prepare) 45962306a36Sopenharmony_ci err = cii->codec->prepare(cii, &bi, pi->substream); 46062306a36Sopenharmony_ci if (err) { 46162306a36Sopenharmony_ci result = err; 46262306a36Sopenharmony_ci goto out_unlock; 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci /* codecs are fine with it, so set our clocks */ 46662306a36Sopenharmony_ci if (input_16bit) 46762306a36Sopenharmony_ci dws = (2 << I2S_DWS_NUM_CHANNELS_IN_SHIFT) | 46862306a36Sopenharmony_ci (2 << I2S_DWS_NUM_CHANNELS_OUT_SHIFT) | 46962306a36Sopenharmony_ci I2S_DWS_DATA_IN_16BIT | I2S_DWS_DATA_OUT_16BIT; 47062306a36Sopenharmony_ci else 47162306a36Sopenharmony_ci dws = (2 << I2S_DWS_NUM_CHANNELS_IN_SHIFT) | 47262306a36Sopenharmony_ci (2 << I2S_DWS_NUM_CHANNELS_OUT_SHIFT) | 47362306a36Sopenharmony_ci I2S_DWS_DATA_IN_24BIT | I2S_DWS_DATA_OUT_24BIT; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci /* early exit if already programmed correctly */ 47662306a36Sopenharmony_ci /* not locking these is fine since we touch them only in this function */ 47762306a36Sopenharmony_ci if (in_le32(&i2sdev->intfregs->serial_format) == sfr 47862306a36Sopenharmony_ci && in_le32(&i2sdev->intfregs->data_word_sizes) == dws) 47962306a36Sopenharmony_ci goto out_unlock; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci /* let's notify the codecs about clocks going away. 48262306a36Sopenharmony_ci * For now we only do mastering on the i2s cell... */ 48362306a36Sopenharmony_ci list_for_each_entry(cii, &i2sdev->sound.codec_list, list) 48462306a36Sopenharmony_ci if (cii->codec->switch_clock) 48562306a36Sopenharmony_ci cii->codec->switch_clock(cii, CLOCK_SWITCH_PREPARE_SLAVE); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci i2sbus_control_enable(i2sdev->control, i2sdev); 48862306a36Sopenharmony_ci i2sbus_control_cell(i2sdev->control, i2sdev, 1); 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci out_le32(&i2sdev->intfregs->intr_ctl, I2S_PENDING_CLOCKS_STOPPED); 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci i2sbus_control_clock(i2sdev->control, i2sdev, 0); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci msleep(1); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci /* wait for clock stopped. This can apparently take a while... */ 49762306a36Sopenharmony_ci cnt = 100; 49862306a36Sopenharmony_ci while (cnt-- && 49962306a36Sopenharmony_ci !(in_le32(&i2sdev->intfregs->intr_ctl) & I2S_PENDING_CLOCKS_STOPPED)) { 50062306a36Sopenharmony_ci msleep(5); 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci out_le32(&i2sdev->intfregs->intr_ctl, I2S_PENDING_CLOCKS_STOPPED); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci /* not locking these is fine since we touch them only in this function */ 50562306a36Sopenharmony_ci out_le32(&i2sdev->intfregs->serial_format, sfr); 50662306a36Sopenharmony_ci out_le32(&i2sdev->intfregs->data_word_sizes, dws); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci i2sbus_control_enable(i2sdev->control, i2sdev); 50962306a36Sopenharmony_ci i2sbus_control_cell(i2sdev->control, i2sdev, 1); 51062306a36Sopenharmony_ci i2sbus_control_clock(i2sdev->control, i2sdev, 1); 51162306a36Sopenharmony_ci msleep(1); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci list_for_each_entry(cii, &i2sdev->sound.codec_list, list) 51462306a36Sopenharmony_ci if (cii->codec->switch_clock) 51562306a36Sopenharmony_ci cii->codec->switch_clock(cii, CLOCK_SWITCH_SLAVE); 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci out_unlock: 51862306a36Sopenharmony_ci mutex_unlock(&i2sdev->lock); 51962306a36Sopenharmony_ci return result; 52062306a36Sopenharmony_ci} 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci#ifdef CONFIG_PM 52362306a36Sopenharmony_civoid i2sbus_pcm_prepare_both(struct i2sbus_dev *i2sdev) 52462306a36Sopenharmony_ci{ 52562306a36Sopenharmony_ci i2sbus_pcm_prepare(i2sdev, 0); 52662306a36Sopenharmony_ci i2sbus_pcm_prepare(i2sdev, 1); 52762306a36Sopenharmony_ci} 52862306a36Sopenharmony_ci#endif 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_cistatic int i2sbus_pcm_trigger(struct i2sbus_dev *i2sdev, int in, int cmd) 53162306a36Sopenharmony_ci{ 53262306a36Sopenharmony_ci struct codec_info_item *cii; 53362306a36Sopenharmony_ci struct pcm_info *pi; 53462306a36Sopenharmony_ci int result = 0; 53562306a36Sopenharmony_ci unsigned long flags; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci spin_lock_irqsave(&i2sdev->low_lock, flags); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci get_pcm_info(i2sdev, in, &pi, NULL); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci switch (cmd) { 54262306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 54362306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 54462306a36Sopenharmony_ci if (pi->dbdma_ring.running) { 54562306a36Sopenharmony_ci result = -EALREADY; 54662306a36Sopenharmony_ci goto out_unlock; 54762306a36Sopenharmony_ci } 54862306a36Sopenharmony_ci list_for_each_entry(cii, &i2sdev->sound.codec_list, list) 54962306a36Sopenharmony_ci if (cii->codec->start) 55062306a36Sopenharmony_ci cii->codec->start(cii, pi->substream); 55162306a36Sopenharmony_ci pi->dbdma_ring.running = 1; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci if (pi->dbdma_ring.stopping) { 55462306a36Sopenharmony_ci /* Clear the S0 bit, then see if we stopped yet */ 55562306a36Sopenharmony_ci out_le32(&pi->dbdma->control, 1 << 16); 55662306a36Sopenharmony_ci if (in_le32(&pi->dbdma->status) & ACTIVE) { 55762306a36Sopenharmony_ci /* possible race here? */ 55862306a36Sopenharmony_ci udelay(10); 55962306a36Sopenharmony_ci if (in_le32(&pi->dbdma->status) & ACTIVE) { 56062306a36Sopenharmony_ci pi->dbdma_ring.stopping = 0; 56162306a36Sopenharmony_ci goto out_unlock; /* keep running */ 56262306a36Sopenharmony_ci } 56362306a36Sopenharmony_ci } 56462306a36Sopenharmony_ci } 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci /* make sure RUN, PAUSE and S0 bits are cleared */ 56762306a36Sopenharmony_ci out_le32(&pi->dbdma->control, (RUN | PAUSE | 1) << 16); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci /* set branch condition select register */ 57062306a36Sopenharmony_ci out_le32(&pi->dbdma->br_sel, (1 << 16) | 1); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci /* write dma command buffer address to the dbdma chip */ 57362306a36Sopenharmony_ci out_le32(&pi->dbdma->cmdptr, pi->dbdma_ring.bus_cmd_start); 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci /* initialize the frame count and current period */ 57662306a36Sopenharmony_ci pi->current_period = 0; 57762306a36Sopenharmony_ci pi->frame_count = in_le32(&i2sdev->intfregs->frame_count); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci /* set the DMA controller running */ 58062306a36Sopenharmony_ci out_le32(&pi->dbdma->control, (RUN << 16) | RUN); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci /* off you go! */ 58362306a36Sopenharmony_ci break; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 58662306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 58762306a36Sopenharmony_ci if (!pi->dbdma_ring.running) { 58862306a36Sopenharmony_ci result = -EALREADY; 58962306a36Sopenharmony_ci goto out_unlock; 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci pi->dbdma_ring.running = 0; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci /* Set the S0 bit to make the DMA branch to the stop cmd */ 59462306a36Sopenharmony_ci out_le32(&pi->dbdma->control, (1 << 16) | 1); 59562306a36Sopenharmony_ci pi->dbdma_ring.stopping = 1; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci list_for_each_entry(cii, &i2sdev->sound.codec_list, list) 59862306a36Sopenharmony_ci if (cii->codec->stop) 59962306a36Sopenharmony_ci cii->codec->stop(cii, pi->substream); 60062306a36Sopenharmony_ci break; 60162306a36Sopenharmony_ci default: 60262306a36Sopenharmony_ci result = -EINVAL; 60362306a36Sopenharmony_ci goto out_unlock; 60462306a36Sopenharmony_ci } 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci out_unlock: 60762306a36Sopenharmony_ci spin_unlock_irqrestore(&i2sdev->low_lock, flags); 60862306a36Sopenharmony_ci return result; 60962306a36Sopenharmony_ci} 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_cistatic snd_pcm_uframes_t i2sbus_pcm_pointer(struct i2sbus_dev *i2sdev, int in) 61262306a36Sopenharmony_ci{ 61362306a36Sopenharmony_ci struct pcm_info *pi; 61462306a36Sopenharmony_ci u32 fc; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci get_pcm_info(i2sdev, in, &pi, NULL); 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci fc = in_le32(&i2sdev->intfregs->frame_count); 61962306a36Sopenharmony_ci fc = fc - pi->frame_count; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci if (fc >= pi->substream->runtime->buffer_size) 62262306a36Sopenharmony_ci fc %= pi->substream->runtime->buffer_size; 62362306a36Sopenharmony_ci return fc; 62462306a36Sopenharmony_ci} 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_cistatic inline void handle_interrupt(struct i2sbus_dev *i2sdev, int in) 62762306a36Sopenharmony_ci{ 62862306a36Sopenharmony_ci struct pcm_info *pi; 62962306a36Sopenharmony_ci u32 fc, nframes; 63062306a36Sopenharmony_ci u32 status; 63162306a36Sopenharmony_ci int timeout, i; 63262306a36Sopenharmony_ci int dma_stopped = 0; 63362306a36Sopenharmony_ci struct snd_pcm_runtime *runtime; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci spin_lock(&i2sdev->low_lock); 63662306a36Sopenharmony_ci get_pcm_info(i2sdev, in, &pi, NULL); 63762306a36Sopenharmony_ci if (!pi->dbdma_ring.running && !pi->dbdma_ring.stopping) 63862306a36Sopenharmony_ci goto out_unlock; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci i = pi->current_period; 64162306a36Sopenharmony_ci runtime = pi->substream->runtime; 64262306a36Sopenharmony_ci while (pi->dbdma_ring.cmds[i].xfer_status) { 64362306a36Sopenharmony_ci if (le16_to_cpu(pi->dbdma_ring.cmds[i].xfer_status) & BT) 64462306a36Sopenharmony_ci /* 64562306a36Sopenharmony_ci * BT is the branch taken bit. If it took a branch 64662306a36Sopenharmony_ci * it is because we set the S0 bit to make it 64762306a36Sopenharmony_ci * branch to the stop command. 64862306a36Sopenharmony_ci */ 64962306a36Sopenharmony_ci dma_stopped = 1; 65062306a36Sopenharmony_ci pi->dbdma_ring.cmds[i].xfer_status = 0; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci if (++i >= runtime->periods) { 65362306a36Sopenharmony_ci i = 0; 65462306a36Sopenharmony_ci pi->frame_count += runtime->buffer_size; 65562306a36Sopenharmony_ci } 65662306a36Sopenharmony_ci pi->current_period = i; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci /* 65962306a36Sopenharmony_ci * Check the frame count. The DMA tends to get a bit 66062306a36Sopenharmony_ci * ahead of the frame counter, which confuses the core. 66162306a36Sopenharmony_ci */ 66262306a36Sopenharmony_ci fc = in_le32(&i2sdev->intfregs->frame_count); 66362306a36Sopenharmony_ci nframes = i * runtime->period_size; 66462306a36Sopenharmony_ci if (fc < pi->frame_count + nframes) 66562306a36Sopenharmony_ci pi->frame_count = fc - nframes; 66662306a36Sopenharmony_ci } 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci if (dma_stopped) { 66962306a36Sopenharmony_ci timeout = 1000; 67062306a36Sopenharmony_ci for (;;) { 67162306a36Sopenharmony_ci status = in_le32(&pi->dbdma->status); 67262306a36Sopenharmony_ci if (!(status & ACTIVE) && (!in || (status & 0x80))) 67362306a36Sopenharmony_ci break; 67462306a36Sopenharmony_ci if (--timeout <= 0) { 67562306a36Sopenharmony_ci printk(KERN_ERR "i2sbus: timed out " 67662306a36Sopenharmony_ci "waiting for DMA to stop!\n"); 67762306a36Sopenharmony_ci break; 67862306a36Sopenharmony_ci } 67962306a36Sopenharmony_ci udelay(1); 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci /* Turn off DMA controller, clear S0 bit */ 68362306a36Sopenharmony_ci out_le32(&pi->dbdma->control, (RUN | PAUSE | 1) << 16); 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci pi->dbdma_ring.stopping = 0; 68662306a36Sopenharmony_ci if (pi->stop_completion) 68762306a36Sopenharmony_ci complete(pi->stop_completion); 68862306a36Sopenharmony_ci } 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci if (!pi->dbdma_ring.running) 69162306a36Sopenharmony_ci goto out_unlock; 69262306a36Sopenharmony_ci spin_unlock(&i2sdev->low_lock); 69362306a36Sopenharmony_ci /* may call _trigger again, hence needs to be unlocked */ 69462306a36Sopenharmony_ci snd_pcm_period_elapsed(pi->substream); 69562306a36Sopenharmony_ci return; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci out_unlock: 69862306a36Sopenharmony_ci spin_unlock(&i2sdev->low_lock); 69962306a36Sopenharmony_ci} 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ciirqreturn_t i2sbus_tx_intr(int irq, void *devid) 70262306a36Sopenharmony_ci{ 70362306a36Sopenharmony_ci handle_interrupt((struct i2sbus_dev *)devid, 0); 70462306a36Sopenharmony_ci return IRQ_HANDLED; 70562306a36Sopenharmony_ci} 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ciirqreturn_t i2sbus_rx_intr(int irq, void *devid) 70862306a36Sopenharmony_ci{ 70962306a36Sopenharmony_ci handle_interrupt((struct i2sbus_dev *)devid, 1); 71062306a36Sopenharmony_ci return IRQ_HANDLED; 71162306a36Sopenharmony_ci} 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_cistatic int i2sbus_playback_open(struct snd_pcm_substream *substream) 71462306a36Sopenharmony_ci{ 71562306a36Sopenharmony_ci struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream); 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci if (!i2sdev) 71862306a36Sopenharmony_ci return -EINVAL; 71962306a36Sopenharmony_ci i2sdev->out.substream = substream; 72062306a36Sopenharmony_ci return i2sbus_pcm_open(i2sdev, 0); 72162306a36Sopenharmony_ci} 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_cistatic int i2sbus_playback_close(struct snd_pcm_substream *substream) 72462306a36Sopenharmony_ci{ 72562306a36Sopenharmony_ci struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream); 72662306a36Sopenharmony_ci int err; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci if (!i2sdev) 72962306a36Sopenharmony_ci return -EINVAL; 73062306a36Sopenharmony_ci if (i2sdev->out.substream != substream) 73162306a36Sopenharmony_ci return -EINVAL; 73262306a36Sopenharmony_ci err = i2sbus_pcm_close(i2sdev, 0); 73362306a36Sopenharmony_ci if (!err) 73462306a36Sopenharmony_ci i2sdev->out.substream = NULL; 73562306a36Sopenharmony_ci return err; 73662306a36Sopenharmony_ci} 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_cistatic int i2sbus_playback_prepare(struct snd_pcm_substream *substream) 73962306a36Sopenharmony_ci{ 74062306a36Sopenharmony_ci struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream); 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci if (!i2sdev) 74362306a36Sopenharmony_ci return -EINVAL; 74462306a36Sopenharmony_ci if (i2sdev->out.substream != substream) 74562306a36Sopenharmony_ci return -EINVAL; 74662306a36Sopenharmony_ci return i2sbus_pcm_prepare(i2sdev, 0); 74762306a36Sopenharmony_ci} 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_cistatic int i2sbus_playback_trigger(struct snd_pcm_substream *substream, int cmd) 75062306a36Sopenharmony_ci{ 75162306a36Sopenharmony_ci struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream); 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci if (!i2sdev) 75462306a36Sopenharmony_ci return -EINVAL; 75562306a36Sopenharmony_ci if (i2sdev->out.substream != substream) 75662306a36Sopenharmony_ci return -EINVAL; 75762306a36Sopenharmony_ci return i2sbus_pcm_trigger(i2sdev, 0, cmd); 75862306a36Sopenharmony_ci} 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_cistatic snd_pcm_uframes_t i2sbus_playback_pointer(struct snd_pcm_substream 76162306a36Sopenharmony_ci *substream) 76262306a36Sopenharmony_ci{ 76362306a36Sopenharmony_ci struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream); 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci if (!i2sdev) 76662306a36Sopenharmony_ci return -EINVAL; 76762306a36Sopenharmony_ci if (i2sdev->out.substream != substream) 76862306a36Sopenharmony_ci return 0; 76962306a36Sopenharmony_ci return i2sbus_pcm_pointer(i2sdev, 0); 77062306a36Sopenharmony_ci} 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_cistatic const struct snd_pcm_ops i2sbus_playback_ops = { 77362306a36Sopenharmony_ci .open = i2sbus_playback_open, 77462306a36Sopenharmony_ci .close = i2sbus_playback_close, 77562306a36Sopenharmony_ci .hw_free = i2sbus_playback_hw_free, 77662306a36Sopenharmony_ci .prepare = i2sbus_playback_prepare, 77762306a36Sopenharmony_ci .trigger = i2sbus_playback_trigger, 77862306a36Sopenharmony_ci .pointer = i2sbus_playback_pointer, 77962306a36Sopenharmony_ci}; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_cistatic int i2sbus_record_open(struct snd_pcm_substream *substream) 78262306a36Sopenharmony_ci{ 78362306a36Sopenharmony_ci struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream); 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci if (!i2sdev) 78662306a36Sopenharmony_ci return -EINVAL; 78762306a36Sopenharmony_ci i2sdev->in.substream = substream; 78862306a36Sopenharmony_ci return i2sbus_pcm_open(i2sdev, 1); 78962306a36Sopenharmony_ci} 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_cistatic int i2sbus_record_close(struct snd_pcm_substream *substream) 79262306a36Sopenharmony_ci{ 79362306a36Sopenharmony_ci struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream); 79462306a36Sopenharmony_ci int err; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci if (!i2sdev) 79762306a36Sopenharmony_ci return -EINVAL; 79862306a36Sopenharmony_ci if (i2sdev->in.substream != substream) 79962306a36Sopenharmony_ci return -EINVAL; 80062306a36Sopenharmony_ci err = i2sbus_pcm_close(i2sdev, 1); 80162306a36Sopenharmony_ci if (!err) 80262306a36Sopenharmony_ci i2sdev->in.substream = NULL; 80362306a36Sopenharmony_ci return err; 80462306a36Sopenharmony_ci} 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_cistatic int i2sbus_record_prepare(struct snd_pcm_substream *substream) 80762306a36Sopenharmony_ci{ 80862306a36Sopenharmony_ci struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream); 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci if (!i2sdev) 81162306a36Sopenharmony_ci return -EINVAL; 81262306a36Sopenharmony_ci if (i2sdev->in.substream != substream) 81362306a36Sopenharmony_ci return -EINVAL; 81462306a36Sopenharmony_ci return i2sbus_pcm_prepare(i2sdev, 1); 81562306a36Sopenharmony_ci} 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_cistatic int i2sbus_record_trigger(struct snd_pcm_substream *substream, int cmd) 81862306a36Sopenharmony_ci{ 81962306a36Sopenharmony_ci struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream); 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci if (!i2sdev) 82262306a36Sopenharmony_ci return -EINVAL; 82362306a36Sopenharmony_ci if (i2sdev->in.substream != substream) 82462306a36Sopenharmony_ci return -EINVAL; 82562306a36Sopenharmony_ci return i2sbus_pcm_trigger(i2sdev, 1, cmd); 82662306a36Sopenharmony_ci} 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_cistatic snd_pcm_uframes_t i2sbus_record_pointer(struct snd_pcm_substream 82962306a36Sopenharmony_ci *substream) 83062306a36Sopenharmony_ci{ 83162306a36Sopenharmony_ci struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream); 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci if (!i2sdev) 83462306a36Sopenharmony_ci return -EINVAL; 83562306a36Sopenharmony_ci if (i2sdev->in.substream != substream) 83662306a36Sopenharmony_ci return 0; 83762306a36Sopenharmony_ci return i2sbus_pcm_pointer(i2sdev, 1); 83862306a36Sopenharmony_ci} 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_cistatic const struct snd_pcm_ops i2sbus_record_ops = { 84162306a36Sopenharmony_ci .open = i2sbus_record_open, 84262306a36Sopenharmony_ci .close = i2sbus_record_close, 84362306a36Sopenharmony_ci .hw_free = i2sbus_record_hw_free, 84462306a36Sopenharmony_ci .prepare = i2sbus_record_prepare, 84562306a36Sopenharmony_ci .trigger = i2sbus_record_trigger, 84662306a36Sopenharmony_ci .pointer = i2sbus_record_pointer, 84762306a36Sopenharmony_ci}; 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_cistatic void i2sbus_private_free(struct snd_pcm *pcm) 85062306a36Sopenharmony_ci{ 85162306a36Sopenharmony_ci struct i2sbus_dev *i2sdev = snd_pcm_chip(pcm); 85262306a36Sopenharmony_ci struct codec_info_item *p, *tmp; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci i2sdev->sound.pcm = NULL; 85562306a36Sopenharmony_ci i2sdev->out.created = 0; 85662306a36Sopenharmony_ci i2sdev->in.created = 0; 85762306a36Sopenharmony_ci list_for_each_entry_safe(p, tmp, &i2sdev->sound.codec_list, list) { 85862306a36Sopenharmony_ci printk(KERN_ERR "i2sbus: a codec didn't unregister!\n"); 85962306a36Sopenharmony_ci list_del(&p->list); 86062306a36Sopenharmony_ci module_put(p->codec->owner); 86162306a36Sopenharmony_ci kfree(p); 86262306a36Sopenharmony_ci } 86362306a36Sopenharmony_ci soundbus_dev_put(&i2sdev->sound); 86462306a36Sopenharmony_ci module_put(THIS_MODULE); 86562306a36Sopenharmony_ci} 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ciint 86862306a36Sopenharmony_cii2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card, 86962306a36Sopenharmony_ci struct codec_info *ci, void *data) 87062306a36Sopenharmony_ci{ 87162306a36Sopenharmony_ci int err, in = 0, out = 0; 87262306a36Sopenharmony_ci struct transfer_info *tmp; 87362306a36Sopenharmony_ci struct i2sbus_dev *i2sdev = soundbus_dev_to_i2sbus_dev(dev); 87462306a36Sopenharmony_ci struct codec_info_item *cii; 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci if (!dev->pcmname || dev->pcmid == -1) { 87762306a36Sopenharmony_ci printk(KERN_ERR "i2sbus: pcm name and id must be set!\n"); 87862306a36Sopenharmony_ci return -EINVAL; 87962306a36Sopenharmony_ci } 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci list_for_each_entry(cii, &dev->codec_list, list) { 88262306a36Sopenharmony_ci if (cii->codec_data == data) 88362306a36Sopenharmony_ci return -EALREADY; 88462306a36Sopenharmony_ci } 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci if (!ci->transfers || !ci->transfers->formats 88762306a36Sopenharmony_ci || !ci->transfers->rates || !ci->usable) 88862306a36Sopenharmony_ci return -EINVAL; 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci /* we currently code the i2s transfer on the clock, and support only 89162306a36Sopenharmony_ci * 32 and 64 */ 89262306a36Sopenharmony_ci if (ci->bus_factor != 32 && ci->bus_factor != 64) 89362306a36Sopenharmony_ci return -EINVAL; 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci /* If you want to fix this, you need to keep track of what transport infos 89662306a36Sopenharmony_ci * are to be used, which codecs they belong to, and then fix all the 89762306a36Sopenharmony_ci * sysclock/busclock stuff above to depend on which is usable */ 89862306a36Sopenharmony_ci list_for_each_entry(cii, &dev->codec_list, list) { 89962306a36Sopenharmony_ci if (cii->codec->sysclock_factor != ci->sysclock_factor) { 90062306a36Sopenharmony_ci printk(KERN_DEBUG 90162306a36Sopenharmony_ci "cannot yet handle multiple different sysclocks!\n"); 90262306a36Sopenharmony_ci return -EINVAL; 90362306a36Sopenharmony_ci } 90462306a36Sopenharmony_ci if (cii->codec->bus_factor != ci->bus_factor) { 90562306a36Sopenharmony_ci printk(KERN_DEBUG 90662306a36Sopenharmony_ci "cannot yet handle multiple different bus clocks!\n"); 90762306a36Sopenharmony_ci return -EINVAL; 90862306a36Sopenharmony_ci } 90962306a36Sopenharmony_ci } 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci tmp = ci->transfers; 91262306a36Sopenharmony_ci while (tmp->formats && tmp->rates) { 91362306a36Sopenharmony_ci if (tmp->transfer_in) 91462306a36Sopenharmony_ci in = 1; 91562306a36Sopenharmony_ci else 91662306a36Sopenharmony_ci out = 1; 91762306a36Sopenharmony_ci tmp++; 91862306a36Sopenharmony_ci } 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci cii = kzalloc(sizeof(struct codec_info_item), GFP_KERNEL); 92162306a36Sopenharmony_ci if (!cii) 92262306a36Sopenharmony_ci return -ENOMEM; 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci /* use the private data to point to the codec info */ 92562306a36Sopenharmony_ci cii->sdev = soundbus_dev_get(dev); 92662306a36Sopenharmony_ci cii->codec = ci; 92762306a36Sopenharmony_ci cii->codec_data = data; 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci if (!cii->sdev) { 93062306a36Sopenharmony_ci printk(KERN_DEBUG 93162306a36Sopenharmony_ci "i2sbus: failed to get soundbus dev reference\n"); 93262306a36Sopenharmony_ci err = -ENODEV; 93362306a36Sopenharmony_ci goto out_free_cii; 93462306a36Sopenharmony_ci } 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci if (!try_module_get(THIS_MODULE)) { 93762306a36Sopenharmony_ci printk(KERN_DEBUG "i2sbus: failed to get module reference!\n"); 93862306a36Sopenharmony_ci err = -EBUSY; 93962306a36Sopenharmony_ci goto out_put_sdev; 94062306a36Sopenharmony_ci } 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci if (!try_module_get(ci->owner)) { 94362306a36Sopenharmony_ci printk(KERN_DEBUG 94462306a36Sopenharmony_ci "i2sbus: failed to get module reference to codec owner!\n"); 94562306a36Sopenharmony_ci err = -EBUSY; 94662306a36Sopenharmony_ci goto out_put_this_module; 94762306a36Sopenharmony_ci } 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci if (!dev->pcm) { 95062306a36Sopenharmony_ci err = snd_pcm_new(card, dev->pcmname, dev->pcmid, 0, 0, 95162306a36Sopenharmony_ci &dev->pcm); 95262306a36Sopenharmony_ci if (err) { 95362306a36Sopenharmony_ci printk(KERN_DEBUG "i2sbus: failed to create pcm\n"); 95462306a36Sopenharmony_ci goto out_put_ci_module; 95562306a36Sopenharmony_ci } 95662306a36Sopenharmony_ci } 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci /* ALSA yet again sucks. 95962306a36Sopenharmony_ci * If it is ever fixed, remove this line. See below. */ 96062306a36Sopenharmony_ci out = in = 1; 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci if (!i2sdev->out.created && out) { 96362306a36Sopenharmony_ci if (dev->pcm->card != card) { 96462306a36Sopenharmony_ci /* eh? */ 96562306a36Sopenharmony_ci printk(KERN_ERR 96662306a36Sopenharmony_ci "Can't attach same bus to different cards!\n"); 96762306a36Sopenharmony_ci err = -EINVAL; 96862306a36Sopenharmony_ci goto out_put_ci_module; 96962306a36Sopenharmony_ci } 97062306a36Sopenharmony_ci err = snd_pcm_new_stream(dev->pcm, SNDRV_PCM_STREAM_PLAYBACK, 1); 97162306a36Sopenharmony_ci if (err) 97262306a36Sopenharmony_ci goto out_put_ci_module; 97362306a36Sopenharmony_ci snd_pcm_set_ops(dev->pcm, SNDRV_PCM_STREAM_PLAYBACK, 97462306a36Sopenharmony_ci &i2sbus_playback_ops); 97562306a36Sopenharmony_ci dev->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].dev->parent = 97662306a36Sopenharmony_ci &dev->ofdev.dev; 97762306a36Sopenharmony_ci i2sdev->out.created = 1; 97862306a36Sopenharmony_ci } 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci if (!i2sdev->in.created && in) { 98162306a36Sopenharmony_ci if (dev->pcm->card != card) { 98262306a36Sopenharmony_ci printk(KERN_ERR 98362306a36Sopenharmony_ci "Can't attach same bus to different cards!\n"); 98462306a36Sopenharmony_ci err = -EINVAL; 98562306a36Sopenharmony_ci goto out_put_ci_module; 98662306a36Sopenharmony_ci } 98762306a36Sopenharmony_ci err = snd_pcm_new_stream(dev->pcm, SNDRV_PCM_STREAM_CAPTURE, 1); 98862306a36Sopenharmony_ci if (err) 98962306a36Sopenharmony_ci goto out_put_ci_module; 99062306a36Sopenharmony_ci snd_pcm_set_ops(dev->pcm, SNDRV_PCM_STREAM_CAPTURE, 99162306a36Sopenharmony_ci &i2sbus_record_ops); 99262306a36Sopenharmony_ci dev->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].dev->parent = 99362306a36Sopenharmony_ci &dev->ofdev.dev; 99462306a36Sopenharmony_ci i2sdev->in.created = 1; 99562306a36Sopenharmony_ci } 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci /* so we have to register the pcm after adding any substream 99862306a36Sopenharmony_ci * to it because alsa doesn't create the devices for the 99962306a36Sopenharmony_ci * substreams when we add them later. 100062306a36Sopenharmony_ci * Therefore, force in and out on both busses (above) and 100162306a36Sopenharmony_ci * register the pcm now instead of just after creating it. 100262306a36Sopenharmony_ci */ 100362306a36Sopenharmony_ci err = snd_device_register(card, dev->pcm); 100462306a36Sopenharmony_ci if (err) { 100562306a36Sopenharmony_ci printk(KERN_ERR "i2sbus: error registering new pcm\n"); 100662306a36Sopenharmony_ci goto out_put_ci_module; 100762306a36Sopenharmony_ci } 100862306a36Sopenharmony_ci /* no errors any more, so let's add this to our list */ 100962306a36Sopenharmony_ci list_add(&cii->list, &dev->codec_list); 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci dev->pcm->private_data = i2sdev; 101262306a36Sopenharmony_ci dev->pcm->private_free = i2sbus_private_free; 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci /* well, we really should support scatter/gather DMA */ 101562306a36Sopenharmony_ci snd_pcm_set_managed_buffer_all( 101662306a36Sopenharmony_ci dev->pcm, SNDRV_DMA_TYPE_DEV, 101762306a36Sopenharmony_ci &macio_get_pci_dev(i2sdev->macio)->dev, 101862306a36Sopenharmony_ci 64 * 1024, 64 * 1024); 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci return 0; 102162306a36Sopenharmony_ci out_put_ci_module: 102262306a36Sopenharmony_ci module_put(ci->owner); 102362306a36Sopenharmony_ci out_put_this_module: 102462306a36Sopenharmony_ci module_put(THIS_MODULE); 102562306a36Sopenharmony_ci out_put_sdev: 102662306a36Sopenharmony_ci soundbus_dev_put(dev); 102762306a36Sopenharmony_ci out_free_cii: 102862306a36Sopenharmony_ci kfree(cii); 102962306a36Sopenharmony_ci return err; 103062306a36Sopenharmony_ci} 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_civoid i2sbus_detach_codec(struct soundbus_dev *dev, void *data) 103362306a36Sopenharmony_ci{ 103462306a36Sopenharmony_ci struct codec_info_item *cii = NULL, *i; 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci list_for_each_entry(i, &dev->codec_list, list) { 103762306a36Sopenharmony_ci if (i->codec_data == data) { 103862306a36Sopenharmony_ci cii = i; 103962306a36Sopenharmony_ci break; 104062306a36Sopenharmony_ci } 104162306a36Sopenharmony_ci } 104262306a36Sopenharmony_ci if (cii) { 104362306a36Sopenharmony_ci list_del(&cii->list); 104462306a36Sopenharmony_ci module_put(cii->codec->owner); 104562306a36Sopenharmony_ci kfree(cii); 104662306a36Sopenharmony_ci } 104762306a36Sopenharmony_ci /* no more codecs, but still a pcm? */ 104862306a36Sopenharmony_ci if (list_empty(&dev->codec_list) && dev->pcm) { 104962306a36Sopenharmony_ci /* the actual cleanup is done by the callback above! */ 105062306a36Sopenharmony_ci snd_device_free(dev->pcm->card, dev->pcm); 105162306a36Sopenharmony_ci } 105262306a36Sopenharmony_ci} 1053