static void s6000_i2s_start_channel(struct s6000_i2s_dev *dev, int channel) { int i, j, cur, prev; /* * Wait for WCLK to toggle 5 times before enabling the channel * s6000 Family Datasheet 3.6.4: * "At least two cycles of WS must occur between commands * to disable or enable the interface" */ j = 0; prev = ~S6_I2S_CUR_WS; for (i = 1000000; --i && j < 6; ) { cur = s6_i2s_read_reg(dev, S6_I2S_INTERFACE_CFG(channel)) & S6_I2S_CUR_WS; if (prev != cur) { prev = cur; j++; } } if (j < 6) printk(KERN_WARNING "s6000-i2s: timeout waiting for WCLK\n"); s6_i2s_write_reg(dev, S6_I2S_ENABLE(channel), S6_I2S_ENABLE_IF); }
static void s6000_i2s_start_channel(struct s6000_i2s_dev *dev, int channel) { int i, j, cur, prev; /* */ j = 0; prev = ~S6_I2S_CUR_WS; for (i = 1000000; --i && j < 6; ) { cur = s6_i2s_read_reg(dev, S6_I2S_INTERFACE_CFG(channel)) & S6_I2S_CUR_WS; if (prev != cur) { prev = cur; j++; } } if (j < 6) printk(KERN_WARNING "s6000-i2s: timeout waiting for WCLK\n"); s6_i2s_write_reg(dev, S6_I2S_ENABLE(channel), S6_I2S_ENABLE_IF); }
static int s6000_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct s6000_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); int interf; u32 w = 0; if (dev->wide) interf = 0; else { w |= (((params_channels(params) - 2) / 2) << S6_I2S_CHANNELS_SHIFT) & S6_I2S_CHANNELS_MASK; interf = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? dev->channel_out : dev->channel_in; } switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: w |= S6_I2S_16BIT | S6_I2S_MEM_16BIT; break; case SNDRV_PCM_FORMAT_S32_LE: w |= S6_I2S_32BIT | S6_I2S_MEM_32BIT; break; default: #ifdef CONFIG_DEBUG_PRINTK printk(KERN_WARNING "s6000-i2s: unsupported PCM format %x\n", params_format(params)); #else ; #endif return -EINVAL; } if (s6_i2s_read_reg(dev, S6_I2S_INTERFACE_CFG(interf)) & S6_I2S_IS_ENABLED) { printk(KERN_ERR "s6000-i2s: interface already enabled\n"); return -EBUSY; } s6_i2s_mod_reg(dev, S6_I2S_INTERFACE_CFG(interf), S6_I2S_CHANNELS_MASK|S6_I2S_MEM_MASK|S6_I2S_BITS_MASK, w); return 0; }
static int s6000_i2s_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div) { struct s6000_i2s_dev *dev = dai->private_data; if (!div || (div & 1) || div > (S6_I2S_DIV_MASK + 1) * 2) return -EINVAL; s6_i2s_mod_reg(dev, S6_I2S_INTERFACE_CFG(div_id), S6_I2S_DIV_MASK, div / 2 - 1); return 0; }
static int s6000_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) { struct s6000_i2s_dev *dev = cpu_dai->private_data; u32 w; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBM_CFM: w = S6_I2S_SCK_IN | S6_I2S_WS_IN; break; case SND_SOC_DAIFMT_CBS_CFM: w = S6_I2S_SCK_OUT | S6_I2S_WS_IN; break; case SND_SOC_DAIFMT_CBM_CFS: w = S6_I2S_SCK_IN | S6_I2S_WS_OUT; break; case SND_SOC_DAIFMT_CBS_CFS: w = S6_I2S_SCK_OUT | S6_I2S_WS_OUT; break; default: return -EINVAL; } switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_NB_NF: w |= S6_I2S_LEFT_FIRST; break; case SND_SOC_DAIFMT_NB_IF: w |= S6_I2S_RIGHT_FIRST; break; default: return -EINVAL; } s6_i2s_mod_reg(dev, S6_I2S_INTERFACE_CFG(0), S6_I2S_FIRST | S6_I2S_WS_DIR | S6_I2S_SCK_DIR, w); s6_i2s_mod_reg(dev, S6_I2S_INTERFACE_CFG(1), S6_I2S_FIRST | S6_I2S_WS_DIR | S6_I2S_SCK_DIR, w); return 0; }