diff options
| author | Daniel Glöckner <dg@emlix.com> | 2014-10-18 18:50:50 -0400 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2014-10-20 06:03:52 -0400 |
| commit | 19c768db42fcf1884cb1f9550969d1e9e69aa928 (patch) | |
| tree | 52aacde220194728e9b8cbea0b6a40d8f1fbbe9b | |
| parent | a5448c88b812390a3622e76d774e10c0da1fb970 (diff) | |
ASoC: s6000: remove driver
The s6000 Xtensa support is removed from the kernel. There are no
other chips known to use this I2S controller.
Signed-off-by: Daniel Glöckner <dg@emlix.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
| -rw-r--r-- | sound/soc/Kconfig | 1 | ||||
| -rw-r--r-- | sound/soc/Makefile | 1 | ||||
| -rw-r--r-- | sound/soc/s6000/Kconfig | 26 | ||||
| -rw-r--r-- | sound/soc/s6000/Makefile | 11 | ||||
| -rw-r--r-- | sound/soc/s6000/s6000-i2s.c | 617 | ||||
| -rw-r--r-- | sound/soc/s6000/s6000-i2s.h | 23 | ||||
| -rw-r--r-- | sound/soc/s6000/s6000-pcm.c | 521 | ||||
| -rw-r--r-- | sound/soc/s6000/s6000-pcm.h | 33 | ||||
| -rw-r--r-- | sound/soc/s6000/s6105-ipcam.c | 221 |
9 files changed, 0 insertions, 1454 deletions
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 0e9623368ab0..7d5d6444a837 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig | |||
| @@ -49,7 +49,6 @@ source "sound/soc/mxs/Kconfig" | |||
| 49 | source "sound/soc/pxa/Kconfig" | 49 | source "sound/soc/pxa/Kconfig" |
| 50 | source "sound/soc/rockchip/Kconfig" | 50 | source "sound/soc/rockchip/Kconfig" |
| 51 | source "sound/soc/samsung/Kconfig" | 51 | source "sound/soc/samsung/Kconfig" |
| 52 | source "sound/soc/s6000/Kconfig" | ||
| 53 | source "sound/soc/sh/Kconfig" | 52 | source "sound/soc/sh/Kconfig" |
| 54 | source "sound/soc/sirf/Kconfig" | 53 | source "sound/soc/sirf/Kconfig" |
| 55 | source "sound/soc/spear/Kconfig" | 54 | source "sound/soc/spear/Kconfig" |
diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 534714a1ca44..d88edfced8c4 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile | |||
| @@ -26,7 +26,6 @@ obj-$(CONFIG_SND_SOC) += kirkwood/ | |||
| 26 | obj-$(CONFIG_SND_SOC) += pxa/ | 26 | obj-$(CONFIG_SND_SOC) += pxa/ |
| 27 | obj-$(CONFIG_SND_SOC) += rockchip/ | 27 | obj-$(CONFIG_SND_SOC) += rockchip/ |
| 28 | obj-$(CONFIG_SND_SOC) += samsung/ | 28 | obj-$(CONFIG_SND_SOC) += samsung/ |
| 29 | obj-$(CONFIG_SND_SOC) += s6000/ | ||
| 30 | obj-$(CONFIG_SND_SOC) += sh/ | 29 | obj-$(CONFIG_SND_SOC) += sh/ |
| 31 | obj-$(CONFIG_SND_SOC) += sirf/ | 30 | obj-$(CONFIG_SND_SOC) += sirf/ |
| 32 | obj-$(CONFIG_SND_SOC) += spear/ | 31 | obj-$(CONFIG_SND_SOC) += spear/ |
diff --git a/sound/soc/s6000/Kconfig b/sound/soc/s6000/Kconfig deleted file mode 100644 index f244a2566f20..000000000000 --- a/sound/soc/s6000/Kconfig +++ /dev/null | |||
| @@ -1,26 +0,0 @@ | |||
| 1 | config SND_S6000_SOC | ||
| 2 | tristate "SoC Audio for the Stretch s6000 family" | ||
| 3 | depends on XTENSA_VARIANT_S6000 || COMPILE_TEST | ||
| 4 | depends on HAS_IOMEM | ||
| 5 | select SND_S6000_SOC_PCM if XTENSA_VARIANT_S6000 | ||
| 6 | help | ||
| 7 | Say Y or M if you want to add support for codecs attached to | ||
| 8 | s6000 family chips. You will also need to select the platform | ||
| 9 | to support below. | ||
| 10 | |||
| 11 | config SND_S6000_SOC_PCM | ||
| 12 | tristate | ||
| 13 | |||
| 14 | config SND_S6000_SOC_I2S | ||
| 15 | tristate | ||
| 16 | |||
| 17 | config SND_S6000_SOC_S6IPCAM | ||
| 18 | bool "SoC Audio support for Stretch 6105 IP Camera" | ||
| 19 | depends on SND_S6000_SOC=y | ||
| 20 | depends on I2C=y | ||
| 21 | depends on XTENSA_PLATFORM_S6105 || COMPILE_TEST | ||
| 22 | select SND_S6000_SOC_I2S | ||
| 23 | select SND_SOC_TLV320AIC3X | ||
| 24 | help | ||
| 25 | Say Y if you want to add support for SoC audio on the | ||
| 26 | Stretch s6105 IP Camera Reference Design. | ||
diff --git a/sound/soc/s6000/Makefile b/sound/soc/s6000/Makefile deleted file mode 100644 index 0f0ae2a012aa..000000000000 --- a/sound/soc/s6000/Makefile +++ /dev/null | |||
| @@ -1,11 +0,0 @@ | |||
| 1 | # s6000 Platform Support | ||
| 2 | snd-soc-s6000-objs := s6000-pcm.o | ||
| 3 | snd-soc-s6000-i2s-objs := s6000-i2s.o | ||
| 4 | |||
| 5 | obj-$(CONFIG_SND_S6000_SOC_PCM) += snd-soc-s6000.o | ||
| 6 | obj-$(CONFIG_SND_S6000_SOC_I2S) += snd-soc-s6000-i2s.o | ||
| 7 | |||
| 8 | # s6105 Machine Support | ||
| 9 | snd-soc-s6ipcam-objs := s6105-ipcam.o | ||
| 10 | |||
| 11 | obj-$(CONFIG_SND_S6000_SOC_S6IPCAM) += snd-soc-s6ipcam.o | ||
diff --git a/sound/soc/s6000/s6000-i2s.c b/sound/soc/s6000/s6000-i2s.c deleted file mode 100644 index 1c8d01166e5b..000000000000 --- a/sound/soc/s6000/s6000-i2s.c +++ /dev/null | |||
| @@ -1,617 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * ALSA SoC I2S Audio Layer for the Stretch S6000 family | ||
| 3 | * | ||
| 4 | * Author: Daniel Gloeckner, <dg@emlix.com> | ||
| 5 | * Copyright: (C) 2009 emlix GmbH <info@emlix.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License version 2 as | ||
| 9 | * published by the Free Software Foundation. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/init.h> | ||
| 13 | #include <linux/module.h> | ||
| 14 | #include <linux/device.h> | ||
| 15 | #include <linux/delay.h> | ||
| 16 | #include <linux/clk.h> | ||
| 17 | #include <linux/interrupt.h> | ||
| 18 | #include <linux/io.h> | ||
| 19 | #include <linux/slab.h> | ||
| 20 | |||
| 21 | #include <sound/core.h> | ||
| 22 | #include <sound/pcm.h> | ||
| 23 | #include <sound/pcm_params.h> | ||
| 24 | #include <sound/initval.h> | ||
| 25 | #include <sound/soc.h> | ||
| 26 | |||
| 27 | #include "s6000-i2s.h" | ||
| 28 | #include "s6000-pcm.h" | ||
| 29 | |||
| 30 | struct s6000_i2s_dev { | ||
| 31 | dma_addr_t sifbase; | ||
| 32 | u8 __iomem *scbbase; | ||
| 33 | unsigned int wide; | ||
| 34 | unsigned int channel_in; | ||
| 35 | unsigned int channel_out; | ||
| 36 | unsigned int lines_in; | ||
| 37 | unsigned int lines_out; | ||
| 38 | struct s6000_pcm_dma_params dma_params; | ||
| 39 | }; | ||
| 40 | |||
| 41 | #define S6_I2S_INTERRUPT_STATUS 0x00 | ||
| 42 | #define S6_I2S_INT_OVERRUN 1 | ||
| 43 | #define S6_I2S_INT_UNDERRUN 2 | ||
| 44 | #define S6_I2S_INT_ALIGNMENT 4 | ||
| 45 | #define S6_I2S_INTERRUPT_ENABLE 0x04 | ||
| 46 | #define S6_I2S_INTERRUPT_RAW 0x08 | ||
| 47 | #define S6_I2S_INTERRUPT_CLEAR 0x0C | ||
| 48 | #define S6_I2S_INTERRUPT_SET 0x10 | ||
| 49 | #define S6_I2S_MODE 0x20 | ||
| 50 | #define S6_I2S_DUAL 0 | ||
| 51 | #define S6_I2S_WIDE 1 | ||
| 52 | #define S6_I2S_TX_DEFAULT 0x24 | ||
| 53 | #define S6_I2S_DATA_CFG(c) (0x40 + 0x10 * (c)) | ||
| 54 | #define S6_I2S_IN 0 | ||
| 55 | #define S6_I2S_OUT 1 | ||
| 56 | #define S6_I2S_UNUSED 2 | ||
| 57 | #define S6_I2S_INTERFACE_CFG(c) (0x44 + 0x10 * (c)) | ||
| 58 | #define S6_I2S_DIV_MASK 0x001fff | ||
| 59 | #define S6_I2S_16BIT 0x000000 | ||
| 60 | #define S6_I2S_20BIT 0x002000 | ||
| 61 | #define S6_I2S_24BIT 0x004000 | ||
| 62 | #define S6_I2S_32BIT 0x006000 | ||
| 63 | #define S6_I2S_BITS_MASK 0x006000 | ||
| 64 | #define S6_I2S_MEM_16BIT 0x000000 | ||
| 65 | #define S6_I2S_MEM_32BIT 0x008000 | ||
| 66 | #define S6_I2S_MEM_MASK 0x008000 | ||
| 67 | #define S6_I2S_CHANNELS_SHIFT 16 | ||
| 68 | #define S6_I2S_CHANNELS_MASK 0x030000 | ||
| 69 | #define S6_I2S_SCK_IN 0x000000 | ||
| 70 | #define S6_I2S_SCK_OUT 0x040000 | ||
| 71 | #define S6_I2S_SCK_DIR 0x040000 | ||
| 72 | #define S6_I2S_WS_IN 0x000000 | ||
| 73 | #define S6_I2S_WS_OUT 0x080000 | ||
| 74 | #define S6_I2S_WS_DIR 0x080000 | ||
| 75 | #define S6_I2S_LEFT_FIRST 0x000000 | ||
| 76 | #define S6_I2S_RIGHT_FIRST 0x100000 | ||
| 77 | #define S6_I2S_FIRST 0x100000 | ||
| 78 | #define S6_I2S_CUR_SCK 0x200000 | ||
| 79 | #define S6_I2S_CUR_WS 0x400000 | ||
| 80 | #define S6_I2S_ENABLE(c) (0x48 + 0x10 * (c)) | ||
| 81 | #define S6_I2S_DISABLE_IF 0x02 | ||
| 82 | #define S6_I2S_ENABLE_IF 0x03 | ||
| 83 | #define S6_I2S_IS_BUSY 0x04 | ||
| 84 | #define S6_I2S_DMA_ACTIVE 0x08 | ||
| 85 | #define S6_I2S_IS_ENABLED 0x10 | ||
| 86 | |||
| 87 | #define S6_I2S_NUM_LINES 4 | ||
| 88 | |||
| 89 | #define S6_I2S_SIF_PORT0 0x0000000 | ||
| 90 | #define S6_I2S_SIF_PORT1 0x0000080 /* docs say 0x0000010 */ | ||
| 91 | |||
| 92 | static inline void s6_i2s_write_reg(struct s6000_i2s_dev *dev, int reg, u32 val) | ||
| 93 | { | ||
| 94 | writel(val, dev->scbbase + reg); | ||
| 95 | } | ||
| 96 | |||
| 97 | static inline u32 s6_i2s_read_reg(struct s6000_i2s_dev *dev, int reg) | ||
| 98 | { | ||
| 99 | return readl(dev->scbbase + reg); | ||
| 100 | } | ||
| 101 | |||
| 102 | static inline void s6_i2s_mod_reg(struct s6000_i2s_dev *dev, int reg, | ||
| 103 | u32 mask, u32 val) | ||
| 104 | { | ||
| 105 | val ^= s6_i2s_read_reg(dev, reg) & ~mask; | ||
| 106 | s6_i2s_write_reg(dev, reg, val); | ||
| 107 | } | ||
| 108 | |||
| 109 | static void s6000_i2s_start_channel(struct s6000_i2s_dev *dev, int channel) | ||
| 110 | { | ||
| 111 | int i, j, cur, prev; | ||
| 112 | |||
| 113 | /* | ||
| 114 | * Wait for WCLK to toggle 5 times before enabling the channel | ||
| 115 | * s6000 Family Datasheet 3.6.4: | ||
| 116 | * "At least two cycles of WS must occur between commands | ||
| 117 | * to disable or enable the interface" | ||
| 118 | */ | ||
| 119 | j = 0; | ||
| 120 | prev = ~S6_I2S_CUR_WS; | ||
| 121 | for (i = 1000000; --i && j < 6; ) { | ||
| 122 | cur = s6_i2s_read_reg(dev, S6_I2S_INTERFACE_CFG(channel)) | ||
| 123 | & S6_I2S_CUR_WS; | ||
| 124 | if (prev != cur) { | ||
| 125 | prev = cur; | ||
| 126 | j++; | ||
| 127 | } | ||
| 128 | } | ||
| 129 | if (j < 6) | ||
| 130 | printk(KERN_WARNING "s6000-i2s: timeout waiting for WCLK\n"); | ||
| 131 | |||
| 132 | s6_i2s_write_reg(dev, S6_I2S_ENABLE(channel), S6_I2S_ENABLE_IF); | ||
| 133 | } | ||
| 134 | |||
| 135 | static void s6000_i2s_stop_channel(struct s6000_i2s_dev *dev, int channel) | ||
| 136 | { | ||
| 137 | s6_i2s_write_reg(dev, S6_I2S_ENABLE(channel), S6_I2S_DISABLE_IF); | ||
| 138 | } | ||
| 139 | |||
| 140 | static void s6000_i2s_start(struct snd_pcm_substream *substream) | ||
| 141 | { | ||
| 142 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
| 143 | struct s6000_i2s_dev *dev = snd_soc_dai_get_drvdata(rtd->cpu_dai); | ||
| 144 | int channel; | ||
| 145 | |||
| 146 | channel = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? | ||
| 147 | dev->channel_out : dev->channel_in; | ||
| 148 | |||
| 149 | s6000_i2s_start_channel(dev, channel); | ||
| 150 | } | ||
| 151 | |||
| 152 | static void s6000_i2s_stop(struct snd_pcm_substream *substream) | ||
| 153 | { | ||
| 154 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
| 155 | struct s6000_i2s_dev *dev = snd_soc_dai_get_drvdata(rtd->cpu_dai); | ||
| 156 | int channel; | ||
| 157 | |||
| 158 | channel = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? | ||
| 159 | dev->channel_out : dev->channel_in; | ||
| 160 | |||
| 161 | s6000_i2s_stop_channel(dev, channel); | ||
| 162 | } | ||
| 163 | |||
| 164 | static int s6000_i2s_trigger(struct snd_pcm_substream *substream, int cmd, | ||
| 165 | int after) | ||
| 166 | { | ||
| 167 | switch (cmd) { | ||
| 168 | case SNDRV_PCM_TRIGGER_START: | ||
| 169 | case SNDRV_PCM_TRIGGER_RESUME: | ||
| 170 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
| 171 | if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE) ^ !after) | ||
| 172 | s6000_i2s_start(substream); | ||
| 173 | break; | ||
| 174 | case SNDRV_PCM_TRIGGER_STOP: | ||
| 175 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
| 176 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
| 177 | if (!after) | ||
| 178 | s6000_i2s_stop(substream); | ||
| 179 | } | ||
| 180 | return 0; | ||
| 181 | } | ||
| 182 | |||
| 183 | static unsigned int s6000_i2s_int_sources(struct s6000_i2s_dev *dev) | ||
| 184 | { | ||
| 185 | unsigned int pending; | ||
| 186 | pending = s6_i2s_read_reg(dev, S6_I2S_INTERRUPT_RAW); | ||
| 187 | pending &= S6_I2S_INT_ALIGNMENT | | ||
| 188 | S6_I2S_INT_UNDERRUN | | ||
| 189 | S6_I2S_INT_OVERRUN; | ||
| 190 | s6_i2s_write_reg(dev, S6_I2S_INTERRUPT_CLEAR, pending); | ||
| 191 | |||
| 192 | return pending; | ||
| 193 | } | ||
| 194 | |||
| 195 | static unsigned int s6000_i2s_check_xrun(struct snd_soc_dai *cpu_dai) | ||
| 196 | { | ||
| 197 | struct s6000_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai); | ||
| 198 | unsigned int errors; | ||
| 199 | unsigned int ret; | ||
| 200 | |||
| 201 | errors = s6000_i2s_int_sources(dev); | ||
| 202 | if (likely(!errors)) | ||
| 203 | return 0; | ||
| 204 | |||
| 205 | ret = 0; | ||
| 206 | if (errors & S6_I2S_INT_ALIGNMENT) | ||
| 207 | printk(KERN_ERR "s6000-i2s: WCLK misaligned\n"); | ||
| 208 | if (errors & S6_I2S_INT_UNDERRUN) | ||
| 209 | ret |= 1 << SNDRV_PCM_STREAM_PLAYBACK; | ||
| 210 | if (errors & S6_I2S_INT_OVERRUN) | ||
| 211 | ret |= 1 << SNDRV_PCM_STREAM_CAPTURE; | ||
| 212 | return ret; | ||
| 213 | } | ||
| 214 | |||
| 215 | static void s6000_i2s_wait_disabled(struct s6000_i2s_dev *dev) | ||
| 216 | { | ||
| 217 | int channel; | ||
| 218 | int n = 50; | ||
| 219 | for (channel = 0; channel < 2; channel++) { | ||
| 220 | while (--n >= 0) { | ||
| 221 | int v = s6_i2s_read_reg(dev, S6_I2S_ENABLE(channel)); | ||
| 222 | if ((v & S6_I2S_IS_ENABLED) | ||
| 223 | || !(v & (S6_I2S_DMA_ACTIVE | S6_I2S_IS_BUSY))) | ||
| 224 | break; | ||
| 225 | udelay(20); | ||
| 226 | } | ||
| 227 | } | ||
| 228 | if (n < 0) | ||
| 229 | printk(KERN_WARNING "s6000-i2s: timeout disabling interfaces"); | ||
| 230 | } | ||
| 231 | |||
| 232 | static int s6000_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, | ||
| 233 | unsigned int fmt) | ||
| 234 | { | ||
| 235 | struct s6000_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai); | ||
| 236 | u32 w; | ||
| 237 | |||
| 238 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | ||
| 239 | case SND_SOC_DAIFMT_CBM_CFM: | ||
| 240 | w = S6_I2S_SCK_IN | S6_I2S_WS_IN; | ||
| 241 | break; | ||
| 242 | case SND_SOC_DAIFMT_CBS_CFM: | ||
| 243 | w = S6_I2S_SCK_OUT | S6_I2S_WS_IN; | ||
| 244 | break; | ||
| 245 | case SND_SOC_DAIFMT_CBM_CFS: | ||
| 246 | w = S6_I2S_SCK_IN | S6_I2S_WS_OUT; | ||
| 247 | break; | ||
| 248 | case SND_SOC_DAIFMT_CBS_CFS: | ||
| 249 | w = S6_I2S_SCK_OUT | S6_I2S_WS_OUT; | ||
| 250 | break; | ||
| 251 | default: | ||
| 252 | return -EINVAL; | ||
| 253 | } | ||
| 254 | |||
| 255 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { | ||
| 256 | case SND_SOC_DAIFMT_NB_NF: | ||
| 257 | w |= S6_I2S_LEFT_FIRST; | ||
| 258 | break; | ||
| 259 | case SND_SOC_DAIFMT_NB_IF: | ||
| 260 | w |= S6_I2S_RIGHT_FIRST; | ||
| 261 | break; | ||
| 262 | default: | ||
| 263 | return -EINVAL; | ||
| 264 | } | ||
| 265 | |||
| 266 | s6_i2s_mod_reg(dev, S6_I2S_INTERFACE_CFG(0), | ||
| 267 | S6_I2S_FIRST | S6_I2S_WS_DIR | S6_I2S_SCK_DIR, w); | ||
| 268 | s6_i2s_mod_reg(dev, S6_I2S_INTERFACE_CFG(1), | ||
| 269 | S6_I2S_FIRST | S6_I2S_WS_DIR | S6_I2S_SCK_DIR, w); | ||
| 270 | |||
| 271 | return 0; | ||
| 272 | } | ||
| 273 | |||
| 274 | static int s6000_i2s_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div) | ||
| 275 | { | ||
| 276 | struct s6000_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); | ||
| 277 | |||
| 278 | if (!div || (div & 1) || div > (S6_I2S_DIV_MASK + 1) * 2) | ||
| 279 | return -EINVAL; | ||
| 280 | |||
| 281 | s6_i2s_mod_reg(dev, S6_I2S_INTERFACE_CFG(div_id), | ||
| 282 | S6_I2S_DIV_MASK, div / 2 - 1); | ||
| 283 | return 0; | ||
| 284 | } | ||
| 285 | |||
| 286 | static int s6000_i2s_hw_params(struct snd_pcm_substream *substream, | ||
| 287 | struct snd_pcm_hw_params *params, | ||
| 288 | struct snd_soc_dai *dai) | ||
| 289 | { | ||
| 290 | struct s6000_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); | ||
| 291 | int interf; | ||
| 292 | u32 w = 0; | ||
| 293 | |||
| 294 | if (dev->wide) | ||
| 295 | interf = 0; | ||
| 296 | else { | ||
| 297 | w |= (((params_channels(params) - 2) / 2) | ||
| 298 | << S6_I2S_CHANNELS_SHIFT) & S6_I2S_CHANNELS_MASK; | ||
| 299 | interf = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
| 300 | ? dev->channel_out : dev->channel_in; | ||
| 301 | } | ||
| 302 | |||
| 303 | switch (params_format(params)) { | ||
| 304 | case SNDRV_PCM_FORMAT_S16_LE: | ||
| 305 | w |= S6_I2S_16BIT | S6_I2S_MEM_16BIT; | ||
| 306 | break; | ||
| 307 | case SNDRV_PCM_FORMAT_S32_LE: | ||
| 308 | w |= S6_I2S_32BIT | S6_I2S_MEM_32BIT; | ||
| 309 | break; | ||
| 310 | default: | ||
| 311 | printk(KERN_WARNING "s6000-i2s: unsupported PCM format %x\n", | ||
| 312 | params_format(params)); | ||
| 313 | return -EINVAL; | ||
| 314 | } | ||
| 315 | |||
| 316 | if (s6_i2s_read_reg(dev, S6_I2S_INTERFACE_CFG(interf)) | ||
| 317 | & S6_I2S_IS_ENABLED) { | ||
| 318 | printk(KERN_ERR "s6000-i2s: interface already enabled\n"); | ||
| 319 | return -EBUSY; | ||
| 320 | } | ||
| 321 | |||
| 322 | s6_i2s_mod_reg(dev, S6_I2S_INTERFACE_CFG(interf), | ||
| 323 | S6_I2S_CHANNELS_MASK|S6_I2S_MEM_MASK|S6_I2S_BITS_MASK, | ||
| 324 | w); | ||
| 325 | |||
| 326 | return 0; | ||
| 327 | } | ||
| 328 | |||
| 329 | static int s6000_i2s_dai_probe(struct snd_soc_dai *dai) | ||
| 330 | { | ||
| 331 | struct s6000_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); | ||
| 332 | struct s6000_snd_platform_data *pdata = dai->dev->platform_data; | ||
| 333 | |||
| 334 | if (!pdata) | ||
| 335 | return -EINVAL; | ||
| 336 | |||
| 337 | dai->capture_dma_data = &dev->dma_params; | ||
| 338 | dai->playback_dma_data = &dev->dma_params; | ||
| 339 | |||
| 340 | dev->wide = pdata->wide; | ||
| 341 | dev->channel_in = pdata->channel_in; | ||
| 342 | dev->channel_out = pdata->channel_out; | ||
| 343 | dev->lines_in = pdata->lines_in; | ||
| 344 | dev->lines_out = pdata->lines_out; | ||
| 345 | |||
| 346 | s6_i2s_write_reg(dev, S6_I2S_MODE, | ||
| 347 | dev->wide ? S6_I2S_WIDE : S6_I2S_DUAL); | ||
| 348 | |||
| 349 | if (dev->wide) { | ||
| 350 | int i; | ||
| 351 | |||
| 352 | if (dev->lines_in + dev->lines_out > S6_I2S_NUM_LINES) | ||
| 353 | return -EINVAL; | ||
| 354 | |||
| 355 | dev->channel_in = 0; | ||
| 356 | dev->channel_out = 1; | ||
| 357 | dai->driver->capture.channels_min = 2 * dev->lines_in; | ||
| 358 | dai->driver->capture.channels_max = dai->driver->capture.channels_min; | ||
| 359 | dai->driver->playback.channels_min = 2 * dev->lines_out; | ||
| 360 | dai->driver->playback.channels_max = dai->driver->playback.channels_min; | ||
| 361 | |||
| 362 | for (i = 0; i < dev->lines_out; i++) | ||
| 363 | s6_i2s_write_reg(dev, S6_I2S_DATA_CFG(i), S6_I2S_OUT); | ||
| 364 | |||
| 365 | for (; i < S6_I2S_NUM_LINES - dev->lines_in; i++) | ||
| 366 | s6_i2s_write_reg(dev, S6_I2S_DATA_CFG(i), | ||
| 367 | S6_I2S_UNUSED); | ||
| 368 | |||
| 369 | for (; i < S6_I2S_NUM_LINES; i++) | ||
| 370 | s6_i2s_write_reg(dev, S6_I2S_DATA_CFG(i), S6_I2S_IN); | ||
| 371 | } else { | ||
| 372 | unsigned int cfg[2] = {S6_I2S_UNUSED, S6_I2S_UNUSED}; | ||
| 373 | |||
| 374 | if (dev->lines_in > 1 || dev->lines_out > 1) | ||
| 375 | return -EINVAL; | ||
| 376 | |||
| 377 | dai->driver->capture.channels_min = 2 * dev->lines_in; | ||
| 378 | dai->driver->capture.channels_max = 8 * dev->lines_in; | ||
| 379 | dai->driver->playback.channels_min = 2 * dev->lines_out; | ||
| 380 | dai->driver->playback.channels_max = 8 * dev->lines_out; | ||
| 381 | |||
| 382 | if (dev->lines_in) | ||
| 383 | cfg[dev->channel_in] = S6_I2S_IN; | ||
| 384 | if (dev->lines_out) | ||
| 385 | cfg[dev->channel_out] = S6_I2S_OUT; | ||
| 386 | |||
| 387 | s6_i2s_write_reg(dev, S6_I2S_DATA_CFG(0), cfg[0]); | ||
| 388 | s6_i2s_write_reg(dev, S6_I2S_DATA_CFG(1), cfg[1]); | ||
| 389 | } | ||
| 390 | |||
| 391 | if (dev->lines_out) { | ||
| 392 | if (dev->lines_in) { | ||
| 393 | if (!dev->dma_params.dma_out) | ||
| 394 | return -ENODEV; | ||
| 395 | } else { | ||
| 396 | dev->dma_params.dma_out = dev->dma_params.dma_in; | ||
| 397 | dev->dma_params.dma_in = 0; | ||
| 398 | } | ||
| 399 | } | ||
| 400 | dev->dma_params.sif_in = dev->sifbase + (dev->channel_in ? | ||
| 401 | S6_I2S_SIF_PORT1 : S6_I2S_SIF_PORT0); | ||
| 402 | dev->dma_params.sif_out = dev->sifbase + (dev->channel_out ? | ||
| 403 | S6_I2S_SIF_PORT1 : S6_I2S_SIF_PORT0); | ||
| 404 | dev->dma_params.same_rate = pdata->same_rate | pdata->wide; | ||
| 405 | return 0; | ||
| 406 | } | ||
| 407 | |||
| 408 | #define S6000_I2S_RATES SNDRV_PCM_RATE_CONTINUOUS | ||
| 409 | #define S6000_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE) | ||
| 410 | |||
| 411 | static const struct snd_soc_dai_ops s6000_i2s_dai_ops = { | ||
| 412 | .set_fmt = s6000_i2s_set_dai_fmt, | ||
| 413 | .set_clkdiv = s6000_i2s_set_clkdiv, | ||
| 414 | .hw_params = s6000_i2s_hw_params, | ||
| 415 | }; | ||
| 416 | |||
| 417 | static struct snd_soc_dai_driver s6000_i2s_dai = { | ||
| 418 | .probe = s6000_i2s_dai_probe, | ||
| 419 | .playback = { | ||
| 420 | .channels_min = 2, | ||
| 421 | .channels_max = 8, | ||
| 422 | .formats = S6000_I2S_FORMATS, | ||
| 423 | .rates = S6000_I2S_RATES, | ||
| 424 | .rate_min = 0, | ||
| 425 | .rate_max = 1562500, | ||
| 426 | }, | ||
| 427 | .capture = { | ||
| 428 | .channels_min = 2, | ||
| 429 | .channels_max = 8, | ||
| 430 | .formats = S6000_I2S_FORMATS, | ||
| 431 | .rates = S6000_I2S_RATES, | ||
| 432 | .rate_min = 0, | ||
| 433 | .rate_max = 1562500, | ||
| 434 | }, | ||
| 435 | .ops = &s6000_i2s_dai_ops, | ||
| 436 | }; | ||
| 437 | |||
| 438 | static const struct snd_soc_component_driver s6000_i2s_component = { | ||
| 439 | .name = "s6000-i2s", | ||
| 440 | }; | ||
| 441 | |||
| 442 | static int s6000_i2s_probe(struct platform_device *pdev) | ||
| 443 | { | ||
| 444 | struct s6000_i2s_dev *dev; | ||
| 445 | struct resource *scbmem, *sifmem, *region, *dma1, *dma2; | ||
| 446 | u8 __iomem *mmio; | ||
| 447 | int ret; | ||
| 448 | |||
| 449 | scbmem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 450 | if (!scbmem) { | ||
| 451 | dev_err(&pdev->dev, "no mem resource?\n"); | ||
| 452 | ret = -ENODEV; | ||
| 453 | goto err_release_none; | ||
| 454 | } | ||
| 455 | |||
| 456 | region = request_mem_region(scbmem->start, resource_size(scbmem), | ||
| 457 | pdev->name); | ||
| 458 | if (!region) { | ||
| 459 | dev_err(&pdev->dev, "I2S SCB region already claimed\n"); | ||
| 460 | ret = -EBUSY; | ||
| 461 | goto err_release_none; | ||
| 462 | } | ||
| 463 | |||
| 464 | mmio = ioremap(scbmem->start, resource_size(scbmem)); | ||
| 465 | if (!mmio) { | ||
| 466 | dev_err(&pdev->dev, "can't ioremap SCB region\n"); | ||
| 467 | ret = -ENOMEM; | ||
| 468 | goto err_release_scb; | ||
| 469 | } | ||
| 470 | |||
| 471 | sifmem = platform_get_resource(pdev, IORESOURCE_MEM, 1); | ||
| 472 | if (!sifmem) { | ||
| 473 | dev_err(&pdev->dev, "no second mem resource?\n"); | ||
| 474 | ret = -ENODEV; | ||
| 475 | goto err_release_map; | ||
| 476 | } | ||
| 477 | |||
| 478 | region = request_mem_region(sifmem->start, resource_size(sifmem), | ||
| 479 | pdev->name); | ||
| 480 | if (!region) { | ||
| 481 | dev_err(&pdev->dev, "I2S SIF region already claimed\n"); | ||
| 482 | ret = -EBUSY; | ||
| 483 | goto err_release_map; | ||
| 484 | } | ||
| 485 | |||
| 486 | dma1 = platform_get_resource(pdev, IORESOURCE_DMA, 0); | ||
| 487 | if (!dma1) { | ||
| 488 | dev_err(&pdev->dev, "no dma resource?\n"); | ||
| 489 | ret = -ENODEV; | ||
| 490 | goto err_release_sif; | ||
| 491 | } | ||
| 492 | |||
| 493 | region = request_mem_region(dma1->start, resource_size(dma1), | ||
| 494 | pdev->name); | ||
| 495 | if (!region) { | ||
| 496 | dev_err(&pdev->dev, "I2S DMA region already claimed\n"); | ||
| 497 | ret = -EBUSY; | ||
| 498 | goto err_release_sif; | ||
| 499 | } | ||
| 500 | |||
| 501 | dma2 = platform_get_resource(pdev, IORESOURCE_DMA, 1); | ||
| 502 | if (dma2) { | ||
| 503 | region = request_mem_region(dma2->start, resource_size(dma2), | ||
| 504 | pdev->name); | ||
| 505 | if (!region) { | ||
| 506 | dev_err(&pdev->dev, | ||
| 507 | "I2S DMA region already claimed\n"); | ||
| 508 | ret = -EBUSY; | ||
| 509 | goto err_release_dma1; | ||
| 510 | } | ||
| 511 | } | ||
| 512 | |||
| 513 | dev = kzalloc(sizeof(struct s6000_i2s_dev), GFP_KERNEL); | ||
| 514 | if (!dev) { | ||
| 515 | ret = -ENOMEM; | ||
| 516 | goto err_release_dma2; | ||
| 517 | } | ||
| 518 | dev_set_drvdata(&pdev->dev, dev); | ||
| 519 | |||
| 520 | dev->sifbase = sifmem->start; | ||
| 521 | dev->scbbase = mmio; | ||
| 522 | |||
| 523 | s6_i2s_write_reg(dev, S6_I2S_INTERRUPT_ENABLE, 0); | ||
| 524 | s6_i2s_write_reg(dev, S6_I2S_INTERRUPT_CLEAR, | ||
| 525 | S6_I2S_INT_ALIGNMENT | | ||
| 526 | S6_I2S_INT_UNDERRUN | | ||
| 527 | S6_I2S_INT_OVERRUN); | ||
| 528 | |||
| 529 | s6000_i2s_stop_channel(dev, 0); | ||
| 530 | s6000_i2s_stop_channel(dev, 1); | ||
| 531 | s6000_i2s_wait_disabled(dev); | ||
| 532 | |||
| 533 | dev->dma_params.check_xrun = s6000_i2s_check_xrun; | ||
| 534 | dev->dma_params.trigger = s6000_i2s_trigger; | ||
| 535 | dev->dma_params.dma_in = dma1->start; | ||
| 536 | dev->dma_params.dma_out = dma2 ? dma2->start : 0; | ||
| 537 | dev->dma_params.irq = platform_get_irq(pdev, 0); | ||
| 538 | if (dev->dma_params.irq < 0) { | ||
| 539 | dev_err(&pdev->dev, "no irq resource?\n"); | ||
| 540 | ret = -ENODEV; | ||
| 541 | goto err_release_dev; | ||
| 542 | } | ||
| 543 | |||
| 544 | s6_i2s_write_reg(dev, S6_I2S_INTERRUPT_ENABLE, | ||
| 545 | S6_I2S_INT_ALIGNMENT | | ||
| 546 | S6_I2S_INT_UNDERRUN | | ||
| 547 | S6_I2S_INT_OVERRUN); | ||
| 548 | |||
| 549 | ret = snd_soc_register_component(&pdev->dev, &s6000_i2s_component, | ||
| 550 | &s6000_i2s_dai, 1); | ||
| 551 | if (ret) | ||
| 552 | goto err_release_dev; | ||
| 553 | |||
| 554 | return 0; | ||
| 555 | |||
| 556 | err_release_dev: | ||
| 557 | kfree(dev); | ||
| 558 | err_release_dma2: | ||
| 559 | if (dma2) | ||
| 560 | release_mem_region(dma2->start, resource_size(dma2)); | ||
| 561 | err_release_dma1: | ||
| 562 | release_mem_region(dma1->start, resource_size(dma1)); | ||
| 563 | err_release_sif: | ||
| 564 | release_mem_region(sifmem->start, resource_size(sifmem)); | ||
| 565 | err_release_map: | ||
| 566 | iounmap(mmio); | ||
| 567 | err_release_scb: | ||
| 568 | release_mem_region(scbmem->start, resource_size(scbmem)); | ||
| 569 | err_release_none: | ||
| 570 | return ret; | ||
| 571 | } | ||
| 572 | |||
| 573 | static int s6000_i2s_remove(struct platform_device *pdev) | ||
| 574 | { | ||
| 575 | struct s6000_i2s_dev *dev = dev_get_drvdata(&pdev->dev); | ||
| 576 | struct resource *region; | ||
| 577 | void __iomem *mmio = dev->scbbase; | ||
| 578 | |||
| 579 | snd_soc_unregister_component(&pdev->dev); | ||
| 580 | |||
| 581 | s6000_i2s_stop_channel(dev, 0); | ||
| 582 | s6000_i2s_stop_channel(dev, 1); | ||
| 583 | |||
| 584 | s6_i2s_write_reg(dev, S6_I2S_INTERRUPT_ENABLE, 0); | ||
| 585 | kfree(dev); | ||
| 586 | |||
| 587 | region = platform_get_resource(pdev, IORESOURCE_DMA, 0); | ||
| 588 | release_mem_region(region->start, resource_size(region)); | ||
| 589 | |||
| 590 | region = platform_get_resource(pdev, IORESOURCE_DMA, 1); | ||
| 591 | if (region) | ||
| 592 | release_mem_region(region->start, resource_size(region)); | ||
| 593 | |||
| 594 | region = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 595 | release_mem_region(region->start, resource_size(region)); | ||
| 596 | |||
| 597 | iounmap(mmio); | ||
| 598 | region = platform_get_resource(pdev, IORESOURCE_IO, 0); | ||
| 599 | release_mem_region(region->start, resource_size(region)); | ||
| 600 | |||
| 601 | return 0; | ||
| 602 | } | ||
| 603 | |||
| 604 | static struct platform_driver s6000_i2s_driver = { | ||
| 605 | .probe = s6000_i2s_probe, | ||
| 606 | .remove = s6000_i2s_remove, | ||
| 607 | .driver = { | ||
| 608 | .name = "s6000-i2s", | ||
| 609 | .owner = THIS_MODULE, | ||
| 610 | }, | ||
| 611 | }; | ||
| 612 | |||
| 613 | module_platform_driver(s6000_i2s_driver); | ||
| 614 | |||
| 615 | MODULE_AUTHOR("Daniel Gloeckner"); | ||
| 616 | MODULE_DESCRIPTION("Stretch s6000 family I2S SoC Interface"); | ||
| 617 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/s6000/s6000-i2s.h b/sound/soc/s6000/s6000-i2s.h deleted file mode 100644 index 86aa1921c89e..000000000000 --- a/sound/soc/s6000/s6000-i2s.h +++ /dev/null | |||
| @@ -1,23 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * ALSA SoC I2S Audio Layer for the Stretch s6000 family | ||
| 3 | * | ||
| 4 | * Author: Daniel Gloeckner, <dg@emlix.com> | ||
| 5 | * Copyright: (C) 2009 emlix GmbH <info@emlix.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License version 2 as | ||
| 9 | * published by the Free Software Foundation. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #ifndef _S6000_I2S_H | ||
| 13 | #define _S6000_I2S_H | ||
| 14 | |||
| 15 | struct s6000_snd_platform_data { | ||
| 16 | int lines_in; | ||
| 17 | int lines_out; | ||
| 18 | int channel_in; | ||
| 19 | int channel_out; | ||
| 20 | int wide; | ||
| 21 | int same_rate; | ||
| 22 | }; | ||
| 23 | #endif | ||
diff --git a/sound/soc/s6000/s6000-pcm.c b/sound/soc/s6000/s6000-pcm.c deleted file mode 100644 index fb8461e1b1f6..000000000000 --- a/sound/soc/s6000/s6000-pcm.c +++ /dev/null | |||
| @@ -1,521 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * ALSA PCM interface for the Stetch s6000 family | ||
| 3 | * | ||
| 4 | * Author: Daniel Gloeckner, <dg@emlix.com> | ||
| 5 | * Copyright: (C) 2009 emlix GmbH <info@emlix.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License version 2 as | ||
| 9 | * published by the Free Software Foundation. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/module.h> | ||
| 13 | #include <linux/init.h> | ||
| 14 | #include <linux/platform_device.h> | ||
| 15 | #include <linux/slab.h> | ||
| 16 | #include <linux/dma-mapping.h> | ||
| 17 | #include <linux/interrupt.h> | ||
| 18 | |||
| 19 | #include <sound/core.h> | ||
| 20 | #include <sound/pcm.h> | ||
| 21 | #include <sound/pcm_params.h> | ||
| 22 | #include <sound/soc.h> | ||
| 23 | |||
| 24 | #include <asm/dma.h> | ||
| 25 | #include <variant/dmac.h> | ||
| 26 | |||
| 27 | #include "s6000-pcm.h" | ||
| 28 | |||
| 29 | #define S6_PCM_PREALLOCATE_SIZE (96 * 1024) | ||
| 30 | #define S6_PCM_PREALLOCATE_MAX (2048 * 1024) | ||
| 31 | |||
| 32 | static struct snd_pcm_hardware s6000_pcm_hardware = { | ||
| 33 | .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | | ||
| 34 | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | | ||
| 35 | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_JOINT_DUPLEX), | ||
| 36 | .buffer_bytes_max = 0x7ffffff0, | ||
| 37 | .period_bytes_min = 16, | ||
| 38 | .period_bytes_max = 0xfffff0, | ||
| 39 | .periods_min = 2, | ||
| 40 | .periods_max = 1024, /* no limit */ | ||
| 41 | .fifo_size = 0, | ||
| 42 | }; | ||
| 43 | |||
| 44 | struct s6000_runtime_data { | ||
| 45 | spinlock_t lock; | ||
| 46 | int period; /* current DMA period */ | ||
| 47 | }; | ||
| 48 | |||
| 49 | static void s6000_pcm_enqueue_dma(struct snd_pcm_substream *substream) | ||
| 50 | { | ||
| 51 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
| 52 | struct s6000_runtime_data *prtd = runtime->private_data; | ||
| 53 | struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; | ||
| 54 | struct s6000_pcm_dma_params *par; | ||
| 55 | int channel; | ||
| 56 | unsigned int period_size; | ||
| 57 | unsigned int dma_offset; | ||
| 58 | dma_addr_t dma_pos; | ||
| 59 | dma_addr_t src, dst; | ||
| 60 | |||
| 61 | par = snd_soc_dai_get_dma_data(soc_runtime->cpu_dai, substream); | ||
| 62 | |||
| 63 | period_size = snd_pcm_lib_period_bytes(substream); | ||
| 64 | dma_offset = prtd->period * period_size; | ||
| 65 | dma_pos = runtime->dma_addr + dma_offset; | ||
| 66 | |||
| 67 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
| 68 | src = dma_pos; | ||
| 69 | dst = par->sif_out; | ||
| 70 | channel = par->dma_out; | ||
| 71 | } else { | ||
| 72 | src = par->sif_in; | ||
| 73 | dst = dma_pos; | ||
| 74 | channel = par->dma_in; | ||
| 75 | } | ||
| 76 | |||
| 77 | if (!s6dmac_channel_enabled(DMA_MASK_DMAC(channel), | ||
| 78 | DMA_INDEX_CHNL(channel))) | ||
| 79 | return; | ||
| 80 | |||
| 81 | if (s6dmac_fifo_full(DMA_MASK_DMAC(channel), DMA_INDEX_CHNL(channel))) { | ||
| 82 | printk(KERN_ERR "s6000-pcm: fifo full\n"); | ||
| 83 | return; | ||
| 84 | } | ||
| 85 | |||
| 86 | if (WARN_ON(period_size & 15)) | ||
| 87 | return; | ||
| 88 | s6dmac_put_fifo(DMA_MASK_DMAC(channel), DMA_INDEX_CHNL(channel), | ||
| 89 | src, dst, period_size); | ||
| 90 | |||
| 91 | prtd->period++; | ||
| 92 | if (unlikely(prtd->period >= runtime->periods)) | ||
| 93 | prtd->period = 0; | ||
| 94 | } | ||
| 95 | |||
| 96 | static irqreturn_t s6000_pcm_irq(int irq, void *data) | ||
| 97 | { | ||
| 98 | struct snd_pcm *pcm = data; | ||
| 99 | struct snd_soc_pcm_runtime *runtime = pcm->private_data; | ||
| 100 | struct s6000_runtime_data *prtd; | ||
| 101 | unsigned int has_xrun; | ||
| 102 | int i, ret = IRQ_NONE; | ||
| 103 | |||
| 104 | for (i = 0; i < 2; ++i) { | ||
| 105 | struct snd_pcm_substream *substream = pcm->streams[i].substream; | ||
| 106 | struct s6000_pcm_dma_params *params = | ||
| 107 | snd_soc_dai_get_dma_data(runtime->cpu_dai, substream); | ||
| 108 | u32 channel; | ||
| 109 | unsigned int pending; | ||
| 110 | |||
| 111 | if (substream == SNDRV_PCM_STREAM_PLAYBACK) | ||
| 112 | channel = params->dma_out; | ||
| 113 | else | ||
| 114 | channel = params->dma_in; | ||
| 115 | |||
| 116 | has_xrun = params->check_xrun(runtime->cpu_dai); | ||
| 117 | |||
| 118 | if (!channel) | ||
| 119 | continue; | ||
| 120 | |||
| 121 | if (unlikely(has_xrun & (1 << i)) && | ||
| 122 | substream->runtime && | ||
| 123 | snd_pcm_running(substream)) { | ||
| 124 | dev_dbg(pcm->dev, "xrun\n"); | ||
| 125 | snd_pcm_stream_lock(substream); | ||
| 126 | snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); | ||
| 127 | snd_pcm_stream_unlock(substream); | ||
| 128 | ret = IRQ_HANDLED; | ||
| 129 | } | ||
| 130 | |||
| 131 | pending = s6dmac_int_sources(DMA_MASK_DMAC(channel), | ||
| 132 | DMA_INDEX_CHNL(channel)); | ||
| 133 | |||
| 134 | if (pending & 1) { | ||
| 135 | ret = IRQ_HANDLED; | ||
| 136 | if (likely(substream->runtime && | ||
| 137 | snd_pcm_running(substream))) { | ||
| 138 | snd_pcm_period_elapsed(substream); | ||
| 139 | dev_dbg(pcm->dev, "period elapsed %x %x\n", | ||
| 140 | s6dmac_cur_src(DMA_MASK_DMAC(channel), | ||
| 141 | DMA_INDEX_CHNL(channel)), | ||
| 142 | s6dmac_cur_dst(DMA_MASK_DMAC(channel), | ||
| 143 | DMA_INDEX_CHNL(channel))); | ||
| 144 | prtd = substream->runtime->private_data; | ||
| 145 | spin_lock(&prtd->lock); | ||
| 146 | s6000_pcm_enqueue_dma(substream); | ||
| 147 | spin_unlock(&prtd->lock); | ||
| 148 | } | ||
| 149 | } | ||
| 150 | |||
| 151 | if (unlikely(pending & ~7)) { | ||
| 152 | if (pending & (1 << 3)) | ||
| 153 | printk(KERN_WARNING | ||
| 154 | "s6000-pcm: DMA %x Underflow\n", | ||
| 155 | channel); | ||
| 156 | if (pending & (1 << 4)) | ||
| 157 | printk(KERN_WARNING | ||
| 158 | "s6000-pcm: DMA %x Overflow\n", | ||
| 159 | channel); | ||
| 160 | if (pending & 0x1e0) | ||
| 161 | printk(KERN_WARNING | ||
| 162 | "s6000-pcm: DMA %x Master Error " | ||
| 163 | "(mask %x)\n", | ||
| 164 | channel, pending >> 5); | ||
| 165 | |||
| 166 | } | ||
| 167 | } | ||
| 168 | |||
| 169 | return ret; | ||
| 170 | } | ||
| 171 | |||
| 172 | static int s6000_pcm_start(struct snd_pcm_substream *substream) | ||
| 173 | { | ||
| 174 | struct s6000_runtime_data *prtd = substream->runtime->private_data; | ||
| 175 | struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; | ||
| 176 | struct s6000_pcm_dma_params *par; | ||
| 177 | unsigned long flags; | ||
| 178 | int srcinc; | ||
| 179 | u32 dma; | ||
| 180 | |||
| 181 | par = snd_soc_dai_get_dma_data(soc_runtime->cpu_dai, substream); | ||
| 182 | |||
| 183 | spin_lock_irqsave(&prtd->lock, flags); | ||
| 184 | |||
| 185 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
| 186 | srcinc = 1; | ||
| 187 | dma = par->dma_out; | ||
| 188 | } else { | ||
| 189 | srcinc = 0; | ||
| 190 | dma = par->dma_in; | ||
| 191 | } | ||
| 192 | s6dmac_enable_chan(DMA_MASK_DMAC(dma), DMA_INDEX_CHNL(dma), | ||
| 193 | 1 /* priority 1 (0 is max) */, | ||
| 194 | 0 /* peripheral requests w/o xfer length mode */, | ||
| 195 | srcinc /* source address increment */, | ||
| 196 | srcinc^1 /* destination address increment */, | ||
| 197 | 0 /* chunksize 0 (skip impossible on this dma) */, | ||
| 198 | 0 /* source skip after chunk (impossible) */, | ||
| 199 | 0 /* destination skip after chunk (impossible) */, | ||
| 200 | 4 /* 16 byte burst size */, | ||
| 201 | -1 /* don't conserve bandwidth */, | ||
| 202 | 0 /* low watermark irq descriptor threshold */, | ||
| 203 | 0 /* disable hardware timestamps */, | ||
| 204 | 1 /* enable channel */); | ||
| 205 | |||
| 206 | s6000_pcm_enqueue_dma(substream); | ||
| 207 | s6000_pcm_enqueue_dma(substream); | ||
| 208 | |||
| 209 | spin_unlock_irqrestore(&prtd->lock, flags); | ||
| 210 | |||
| 211 | return 0; | ||
| 212 | } | ||
| 213 | |||
| 214 | static int s6000_pcm_stop(struct snd_pcm_substream *substream) | ||
| 215 | { | ||
| 216 | struct s6000_runtime_data *prtd = substream->runtime->private_data; | ||
| 217 | struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; | ||
| 218 | struct s6000_pcm_dma_params *par; | ||
| 219 | unsigned long flags; | ||
| 220 | u32 channel; | ||
| 221 | |||
| 222 | par = snd_soc_dai_get_dma_data(soc_runtime->cpu_dai, substream); | ||
| 223 | |||
| 224 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
| 225 | channel = par->dma_out; | ||
| 226 | else | ||
| 227 | channel = par->dma_in; | ||
| 228 | |||
| 229 | s6dmac_set_terminal_count(DMA_MASK_DMAC(channel), | ||
| 230 | DMA_INDEX_CHNL(channel), 0); | ||
| 231 | |||
| 232 | spin_lock_irqsave(&prtd->lock, flags); | ||
| 233 | |||
| 234 | s6dmac_disable_chan(DMA_MASK_DMAC(channel), DMA_INDEX_CHNL(channel)); | ||
| 235 | |||
| 236 | spin_unlock_irqrestore(&prtd->lock, flags); | ||
| 237 | |||
| 238 | return 0; | ||
| 239 | } | ||
| 240 | |||
| 241 | static int s6000_pcm_trigger(struct snd_pcm_substream *substream, int cmd) | ||
| 242 | { | ||
| 243 | struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; | ||
| 244 | struct s6000_pcm_dma_params *par; | ||
| 245 | int ret; | ||
| 246 | |||
| 247 | par = snd_soc_dai_get_dma_data(soc_runtime->cpu_dai, substream); | ||
| 248 | |||
| 249 | ret = par->trigger(substream, cmd, 0); | ||
| 250 | if (ret < 0) | ||
| 251 | return ret; | ||
| 252 | |||
| 253 | switch (cmd) { | ||
| 254 | case SNDRV_PCM_TRIGGER_START: | ||
| 255 | case SNDRV_PCM_TRIGGER_RESUME: | ||
| 256 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
| 257 | ret = s6000_pcm_start(substream); | ||
| 258 | break; | ||
| 259 | case SNDRV_PCM_TRIGGER_STOP: | ||
| 260 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
| 261 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
| 262 | ret = s6000_pcm_stop(substream); | ||
| 263 | break; | ||
| 264 | default: | ||
| 265 | ret = -EINVAL; | ||
| 266 | } | ||
| 267 | if (ret < 0) | ||
| 268 | return ret; | ||
| 269 | |||
| 270 | return par->trigger(substream, cmd, 1); | ||
| 271 | } | ||
| 272 | |||
| 273 | static int s6000_pcm_prepare(struct snd_pcm_substream *substream) | ||
| 274 | { | ||
| 275 | struct s6000_runtime_data *prtd = substream->runtime->private_data; | ||
| 276 | |||
| 277 | prtd->period = 0; | ||
| 278 | |||
| 279 | return 0; | ||
| 280 | } | ||
| 281 | |||
| 282 | static snd_pcm_uframes_t s6000_pcm_pointer(struct snd_pcm_substream *substream) | ||
| 283 | { | ||
| 284 | struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; | ||
| 285 | struct s6000_pcm_dma_params *par; | ||
| 286 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
| 287 | struct s6000_runtime_data *prtd = runtime->private_data; | ||
| 288 | unsigned long flags; | ||
| 289 | unsigned int offset; | ||
| 290 | dma_addr_t count; | ||
| 291 | |||
| 292 | par = snd_soc_dai_get_dma_data(soc_runtime->cpu_dai, substream); | ||
| 293 | |||
| 294 | spin_lock_irqsave(&prtd->lock, flags); | ||
| 295 | |||
| 296 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
| 297 | count = s6dmac_cur_src(DMA_MASK_DMAC(par->dma_out), | ||
| 298 | DMA_INDEX_CHNL(par->dma_out)); | ||
| 299 | else | ||
| 300 | count = s6dmac_cur_dst(DMA_MASK_DMAC(par->dma_in), | ||
| 301 | DMA_INDEX_CHNL(par->dma_in)); | ||
| 302 | |||
| 303 | count -= runtime->dma_addr; | ||
| 304 | |||
| 305 | spin_unlock_irqrestore(&prtd->lock, flags); | ||
| 306 | |||
| 307 | offset = bytes_to_frames(runtime, count); | ||
| 308 | if (unlikely(offset >= runtime->buffer_size)) | ||
| 309 | offset = 0; | ||
| 310 | |||
| 311 | return offset; | ||
| 312 | } | ||
| 313 | |||
| 314 | static int s6000_pcm_open(struct snd_pcm_substream *substream) | ||
| 315 | { | ||
| 316 | struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; | ||
| 317 | struct s6000_pcm_dma_params *par; | ||
| 318 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
| 319 | struct s6000_runtime_data *prtd; | ||
| 320 | int ret; | ||
| 321 | |||
| 322 | par = snd_soc_dai_get_dma_data(soc_runtime->cpu_dai, substream); | ||
| 323 | snd_soc_set_runtime_hwparams(substream, &s6000_pcm_hardware); | ||
| 324 | |||
| 325 | ret = snd_pcm_hw_constraint_step(runtime, 0, | ||
| 326 | SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 16); | ||
| 327 | if (ret < 0) | ||
| 328 | return ret; | ||
| 329 | ret = snd_pcm_hw_constraint_step(runtime, 0, | ||
| 330 | SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 16); | ||
| 331 | if (ret < 0) | ||
| 332 | return ret; | ||
| 333 | ret = snd_pcm_hw_constraint_integer(runtime, | ||
| 334 | SNDRV_PCM_HW_PARAM_PERIODS); | ||
| 335 | if (ret < 0) | ||
| 336 | return ret; | ||
| 337 | |||
| 338 | if (par->same_rate) { | ||
| 339 | int rate; | ||
| 340 | spin_lock(&par->lock); /* needed? */ | ||
| 341 | rate = par->rate; | ||
| 342 | spin_unlock(&par->lock); | ||
| 343 | if (rate != -1) { | ||
| 344 | ret = snd_pcm_hw_constraint_minmax(runtime, | ||
| 345 | SNDRV_PCM_HW_PARAM_RATE, | ||
| 346 | rate, rate); | ||
| 347 | if (ret < 0) | ||
| 348 | return ret; | ||
| 349 | } | ||
| 350 | } | ||
| 351 | |||
| 352 | prtd = kzalloc(sizeof(struct s6000_runtime_data), GFP_KERNEL); | ||
| 353 | if (prtd == NULL) | ||
| 354 | return -ENOMEM; | ||
| 355 | |||
| 356 | spin_lock_init(&prtd->lock); | ||
| 357 | |||
| 358 | runtime->private_data = prtd; | ||
| 359 | |||
| 360 | return 0; | ||
| 361 | } | ||
| 362 | |||
| 363 | static int s6000_pcm_close(struct snd_pcm_substream *substream) | ||
| 364 | { | ||
| 365 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
| 366 | struct s6000_runtime_data *prtd = runtime->private_data; | ||
| 367 | |||
| 368 | kfree(prtd); | ||
| 369 | |||
| 370 | return 0; | ||
| 371 | } | ||
| 372 | |||
| 373 | static int s6000_pcm_hw_params(struct snd_pcm_substream *substream, | ||
| 374 | struct snd_pcm_hw_params *hw_params) | ||
| 375 | { | ||
| 376 | struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; | ||
| 377 | struct s6000_pcm_dma_params *par; | ||
| 378 | int ret; | ||
| 379 | ret = snd_pcm_lib_malloc_pages(substream, | ||
| 380 | params_buffer_bytes(hw_params)); | ||
| 381 | if (ret < 0) { | ||
| 382 | printk(KERN_WARNING "s6000-pcm: allocation of memory failed\n"); | ||
| 383 | return ret; | ||
| 384 | } | ||
| 385 | |||
| 386 | par = snd_soc_dai_get_dma_data(soc_runtime->cpu_dai, substream); | ||
| 387 | |||
| 388 | if (par->same_rate) { | ||
| 389 | spin_lock(&par->lock); | ||
| 390 | if (par->rate == -1 || | ||
| 391 | !(par->in_use & ~(1 << substream->stream))) { | ||
| 392 | par->rate = params_rate(hw_params); | ||
| 393 | par->in_use |= 1 << substream->stream; | ||
| 394 | } else if (params_rate(hw_params) != par->rate) { | ||
| 395 | snd_pcm_lib_free_pages(substream); | ||
| 396 | par->in_use &= ~(1 << substream->stream); | ||
| 397 | ret = -EBUSY; | ||
| 398 | } | ||
| 399 | spin_unlock(&par->lock); | ||
| 400 | } | ||
| 401 | return ret; | ||
| 402 | } | ||
| 403 | |||
| 404 | static int s6000_pcm_hw_free(struct snd_pcm_substream *substream) | ||
| 405 | { | ||
| 406 | struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; | ||
| 407 | struct s6000_pcm_dma_params *par = | ||
| 408 | snd_soc_dai_get_dma_data(soc_runtime->cpu_dai, substream); | ||
| 409 | |||
| 410 | spin_lock(&par->lock); | ||
| 411 | par->in_use &= ~(1 << substream->stream); | ||
| 412 | if (!par->in_use) | ||
| 413 | par->rate = -1; | ||
| 414 | spin_unlock(&par->lock); | ||
| 415 | |||
| 416 | return snd_pcm_lib_free_pages(substream); | ||
| 417 | } | ||
| 418 | |||
| 419 | static struct snd_pcm_ops s6000_pcm_ops = { | ||
| 420 | .open = s6000_pcm_open, | ||
| 421 | .close = s6000_pcm_close, | ||
| 422 | .ioctl = snd_pcm_lib_ioctl, | ||
| 423 | .hw_params = s6000_pcm_hw_params, | ||
| 424 | .hw_free = s6000_pcm_hw_free, | ||
| 425 | .trigger = s6000_pcm_trigger, | ||
| 426 | .prepare = s6000_pcm_prepare, | ||
| 427 | .pointer = s6000_pcm_pointer, | ||
| 428 | }; | ||
| 429 | |||
| 430 | static void s6000_pcm_free(struct snd_pcm *pcm) | ||
| 431 | { | ||
| 432 | struct snd_soc_pcm_runtime *runtime = pcm->private_data; | ||
| 433 | struct s6000_pcm_dma_params *params = | ||
| 434 | snd_soc_dai_get_dma_data(runtime->cpu_dai, | ||
| 435 | pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream); | ||
| 436 | |||
| 437 | free_irq(params->irq, pcm); | ||
| 438 | snd_pcm_lib_preallocate_free_for_all(pcm); | ||
| 439 | } | ||
| 440 | |||
| 441 | static int s6000_pcm_new(struct snd_soc_pcm_runtime *runtime) | ||
| 442 | { | ||
| 443 | struct snd_card *card = runtime->card->snd_card; | ||
| 444 | struct snd_pcm *pcm = runtime->pcm; | ||
| 445 | struct s6000_pcm_dma_params *params; | ||
| 446 | int res; | ||
| 447 | |||
| 448 | params = snd_soc_dai_get_dma_data(runtime->cpu_dai, | ||
| 449 | pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream); | ||
| 450 | |||
| 451 | res = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32)); | ||
| 452 | if (res) | ||
| 453 | return res; | ||
| 454 | |||
| 455 | if (params->dma_in) { | ||
| 456 | s6dmac_disable_chan(DMA_MASK_DMAC(params->dma_in), | ||
| 457 | DMA_INDEX_CHNL(params->dma_in)); | ||
| 458 | s6dmac_int_sources(DMA_MASK_DMAC(params->dma_in), | ||
| 459 | DMA_INDEX_CHNL(params->dma_in)); | ||
| 460 | } | ||
| 461 | |||
| 462 | if (params->dma_out) { | ||
| 463 | s6dmac_disable_chan(DMA_MASK_DMAC(params->dma_out), | ||
| 464 | DMA_INDEX_CHNL(params->dma_out)); | ||
| 465 | s6dmac_int_sources(DMA_MASK_DMAC(params->dma_out), | ||
| 466 | DMA_INDEX_CHNL(params->dma_out)); | ||
| 467 | } | ||
| 468 | |||
| 469 | res = request_irq(params->irq, s6000_pcm_irq, IRQF_SHARED, | ||
| 470 | "s6000-audio", pcm); | ||
| 471 | if (res) { | ||
| 472 | printk(KERN_ERR "s6000-pcm couldn't get IRQ\n"); | ||
| 473 | return res; | ||
| 474 | } | ||
| 475 | |||
| 476 | res = snd_pcm_lib_preallocate_pages_for_all(pcm, | ||
| 477 | SNDRV_DMA_TYPE_DEV, | ||
| 478 | card->dev, | ||
| 479 | S6_PCM_PREALLOCATE_SIZE, | ||
| 480 | S6_PCM_PREALLOCATE_MAX); | ||
| 481 | if (res) | ||
| 482 | printk(KERN_WARNING "s6000-pcm: preallocation failed\n"); | ||
| 483 | |||
| 484 | spin_lock_init(¶ms->lock); | ||
| 485 | params->in_use = 0; | ||
| 486 | params->rate = -1; | ||
| 487 | return 0; | ||
| 488 | } | ||
| 489 | |||
| 490 | static struct snd_soc_platform_driver s6000_soc_platform = { | ||
| 491 | .ops = &s6000_pcm_ops, | ||
| 492 | .pcm_new = s6000_pcm_new, | ||
| 493 | .pcm_free = s6000_pcm_free, | ||
| 494 | }; | ||
| 495 | |||
| 496 | static int s6000_soc_platform_probe(struct platform_device *pdev) | ||
| 497 | { | ||
| 498 | return snd_soc_register_platform(&pdev->dev, &s6000_soc_platform); | ||
| 499 | } | ||
| 500 | |||
| 501 | static int s6000_soc_platform_remove(struct platform_device *pdev) | ||
| 502 | { | ||
| 503 | snd_soc_unregister_platform(&pdev->dev); | ||
| 504 | return 0; | ||
| 505 | } | ||
| 506 | |||
| 507 | static struct platform_driver s6000_pcm_driver = { | ||
| 508 | .driver = { | ||
| 509 | .name = "s6000-pcm-audio", | ||
| 510 | .owner = THIS_MODULE, | ||
| 511 | }, | ||
| 512 | |||
| 513 | .probe = s6000_soc_platform_probe, | ||
| 514 | .remove = s6000_soc_platform_remove, | ||
| 515 | }; | ||
| 516 | |||
| 517 | module_platform_driver(s6000_pcm_driver); | ||
| 518 | |||
| 519 | MODULE_AUTHOR("Daniel Gloeckner"); | ||
| 520 | MODULE_DESCRIPTION("Stretch s6000 family PCM DMA module"); | ||
| 521 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/s6000/s6000-pcm.h b/sound/soc/s6000/s6000-pcm.h deleted file mode 100644 index 09d9b883e58b..000000000000 --- a/sound/soc/s6000/s6000-pcm.h +++ /dev/null | |||
| @@ -1,33 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * ALSA PCM interface for the Stretch s6000 family | ||
| 3 | * | ||
| 4 | * Author: Daniel Gloeckner, <dg@emlix.com> | ||
| 5 | * Copyright: (C) 2009 emlix GmbH <info@emlix.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License version 2 as | ||
| 9 | * published by the Free Software Foundation. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #ifndef _S6000_PCM_H | ||
| 13 | #define _S6000_PCM_H | ||
| 14 | |||
| 15 | struct snd_soc_dai; | ||
| 16 | struct snd_pcm_substream; | ||
| 17 | |||
| 18 | struct s6000_pcm_dma_params { | ||
| 19 | unsigned int (*check_xrun)(struct snd_soc_dai *cpu_dai); | ||
| 20 | int (*trigger)(struct snd_pcm_substream *substream, int cmd, int after); | ||
| 21 | dma_addr_t sif_in; | ||
| 22 | dma_addr_t sif_out; | ||
| 23 | u32 dma_in; | ||
| 24 | u32 dma_out; | ||
| 25 | int irq; | ||
| 26 | int same_rate; | ||
| 27 | |||
| 28 | spinlock_t lock; | ||
| 29 | int in_use; | ||
| 30 | int rate; | ||
| 31 | }; | ||
| 32 | |||
| 33 | #endif | ||
diff --git a/sound/soc/s6000/s6105-ipcam.c b/sound/soc/s6000/s6105-ipcam.c deleted file mode 100644 index 3510c01f8a6a..000000000000 --- a/sound/soc/s6000/s6105-ipcam.c +++ /dev/null | |||
| @@ -1,221 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * ASoC driver for Stretch s6105 IP camera platform | ||
| 3 | * | ||
| 4 | * Author: Daniel Gloeckner, <dg@emlix.com> | ||
| 5 | * Copyright: (C) 2009 emlix GmbH <info@emlix.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License version 2 as | ||
| 9 | * published by the Free Software Foundation. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/module.h> | ||
| 13 | #include <linux/moduleparam.h> | ||
| 14 | #include <linux/timer.h> | ||
| 15 | #include <linux/interrupt.h> | ||
| 16 | #include <linux/platform_device.h> | ||
| 17 | #include <linux/i2c.h> | ||
| 18 | #include <sound/core.h> | ||
| 19 | #include <sound/pcm.h> | ||
| 20 | #include <sound/soc.h> | ||
| 21 | |||
| 22 | #include "s6000-pcm.h" | ||
| 23 | #include "s6000-i2s.h" | ||
| 24 | |||
| 25 | #define S6105_CAM_CODEC_CLOCK 12288000 | ||
| 26 | |||
| 27 | static int s6105_hw_params(struct snd_pcm_substream *substream, | ||
| 28 | struct snd_pcm_hw_params *params) | ||
| 29 | { | ||
| 30 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
| 31 | struct snd_soc_dai *codec_dai = rtd->codec_dai; | ||
| 32 | struct snd_soc_dai *cpu_dai = rtd->cpu_dai; | ||
| 33 | int ret = 0; | ||
| 34 | |||
| 35 | /* set codec DAI configuration */ | ||
| 36 | ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | | ||
| 37 | SND_SOC_DAIFMT_CBM_CFM); | ||
| 38 | if (ret < 0) | ||
| 39 | return ret; | ||
| 40 | |||
| 41 | /* set cpu DAI configuration */ | ||
| 42 | ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBM_CFM | | ||
| 43 | SND_SOC_DAIFMT_NB_NF); | ||
| 44 | if (ret < 0) | ||
| 45 | return ret; | ||
| 46 | |||
| 47 | /* set the codec system clock */ | ||
| 48 | ret = snd_soc_dai_set_sysclk(codec_dai, 0, S6105_CAM_CODEC_CLOCK, | ||
| 49 | SND_SOC_CLOCK_OUT); | ||
| 50 | if (ret < 0) | ||
| 51 | return ret; | ||
| 52 | |||
| 53 | return 0; | ||
| 54 | } | ||
| 55 | |||
| 56 | static struct snd_soc_ops s6105_ops = { | ||
| 57 | .hw_params = s6105_hw_params, | ||
| 58 | }; | ||
| 59 | |||
| 60 | /* s6105 machine dapm widgets */ | ||
| 61 | static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = { | ||
| 62 | SND_SOC_DAPM_LINE("Audio Out Differential", NULL), | ||
| 63 | SND_SOC_DAPM_LINE("Audio Out Stereo", NULL), | ||
| 64 | SND_SOC_DAPM_LINE("Audio In", NULL), | ||
| 65 | }; | ||
| 66 | |||
| 67 | /* s6105 machine audio_mapnections to the codec pins */ | ||
| 68 | static const struct snd_soc_dapm_route audio_map[] = { | ||
| 69 | /* Audio Out connected to HPLOUT, HPLCOM, HPROUT */ | ||
| 70 | {"Audio Out Differential", NULL, "HPLOUT"}, | ||
| 71 | {"Audio Out Differential", NULL, "HPLCOM"}, | ||
| 72 | {"Audio Out Stereo", NULL, "HPLOUT"}, | ||
| 73 | {"Audio Out Stereo", NULL, "HPROUT"}, | ||
| 74 | |||
| 75 | /* Audio In connected to LINE1L, LINE1R */ | ||
| 76 | {"LINE1L", NULL, "Audio In"}, | ||
| 77 | {"LINE1R", NULL, "Audio In"}, | ||
| 78 | }; | ||
| 79 | |||
| 80 | static int output_type_info(struct snd_kcontrol *kcontrol, | ||
| 81 | struct snd_ctl_elem_info *uinfo) | ||
| 82 | { | ||
| 83 | uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | ||
| 84 | uinfo->count = 1; | ||
| 85 | uinfo->value.enumerated.items = 2; | ||
| 86 | if (uinfo->value.enumerated.item) { | ||
| 87 | uinfo->value.enumerated.item = 1; | ||
| 88 | strcpy(uinfo->value.enumerated.name, "HPLOUT/HPROUT"); | ||
| 89 | } else { | ||
| 90 | strcpy(uinfo->value.enumerated.name, "HPLOUT/HPLCOM"); | ||
| 91 | } | ||
| 92 | return 0; | ||
| 93 | } | ||
| 94 | |||
| 95 | static int output_type_get(struct snd_kcontrol *kcontrol, | ||
| 96 | struct snd_ctl_elem_value *ucontrol) | ||
| 97 | { | ||
| 98 | ucontrol->value.enumerated.item[0] = kcontrol->private_value; | ||
| 99 | return 0; | ||
| 100 | } | ||
| 101 | |||
| 102 | static int output_type_put(struct snd_kcontrol *kcontrol, | ||
| 103 | struct snd_ctl_elem_value *ucontrol) | ||
| 104 | { | ||
| 105 | struct snd_soc_card *card = kcontrol->private_data; | ||
| 106 | struct snd_soc_dapm_context *dapm = &card->dapm; | ||
| 107 | unsigned int val = (ucontrol->value.enumerated.item[0] != 0); | ||
| 108 | char *differential = "Audio Out Differential"; | ||
| 109 | char *stereo = "Audio Out Stereo"; | ||
| 110 | |||
| 111 | if (kcontrol->private_value == val) | ||
| 112 | return 0; | ||
| 113 | kcontrol->private_value = val; | ||
| 114 | snd_soc_dapm_disable_pin(dapm, val ? differential : stereo); | ||
| 115 | snd_soc_dapm_sync(dapm); | ||
| 116 | snd_soc_dapm_enable_pin(dapm, val ? stereo : differential); | ||
| 117 | snd_soc_dapm_sync(dapm); | ||
| 118 | |||
| 119 | return 1; | ||
| 120 | } | ||
| 121 | |||
| 122 | static const struct snd_kcontrol_new audio_out_mux = { | ||
| 123 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
| 124 | .name = "Master Output Mux", | ||
| 125 | .index = 0, | ||
| 126 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | ||
| 127 | .info = output_type_info, | ||
| 128 | .get = output_type_get, | ||
| 129 | .put = output_type_put, | ||
| 130 | .private_value = 1 /* default to stereo */ | ||
| 131 | }; | ||
| 132 | |||
| 133 | /* Logic for a aic3x as connected on the s6105 ip camera ref design */ | ||
| 134 | static int s6105_aic3x_init(struct snd_soc_pcm_runtime *rtd) | ||
| 135 | { | ||
| 136 | struct snd_soc_card *card = rtd->card; | ||
| 137 | |||
| 138 | /* must correspond to audio_out_mux.private_value initializer */ | ||
| 139 | snd_soc_dapm_disable_pin(&card->dapm, "Audio Out Differential"); | ||
| 140 | |||
| 141 | snd_ctl_add(card->snd_card, snd_ctl_new1(&audio_out_mux, card)); | ||
| 142 | |||
| 143 | return 0; | ||
| 144 | } | ||
| 145 | |||
| 146 | /* s6105 digital audio interface glue - connects codec <--> CPU */ | ||
| 147 | static struct snd_soc_dai_link s6105_dai = { | ||
| 148 | .name = "TLV320AIC31", | ||
| 149 | .stream_name = "AIC31", | ||
| 150 | .cpu_dai_name = "s6000-i2s", | ||
| 151 | .codec_dai_name = "tlv320aic3x-hifi", | ||
| 152 | .platform_name = "s6000-pcm-audio", | ||
| 153 | .codec_name = "tlv320aic3x-codec.0-001a", | ||
| 154 | .init = s6105_aic3x_init, | ||
| 155 | .ops = &s6105_ops, | ||
| 156 | }; | ||
| 157 | |||
| 158 | /* s6105 audio machine driver */ | ||
| 159 | static struct snd_soc_card snd_soc_card_s6105 = { | ||
| 160 | .name = "Stretch IP Camera", | ||
| 161 | .owner = THIS_MODULE, | ||
| 162 | .dai_link = &s6105_dai, | ||
| 163 | .num_links = 1, | ||
| 164 | |||
| 165 | .dapm_widgets = aic3x_dapm_widgets, | ||
| 166 | .num_dapm_widgets = ARRAY_SIZE(aic3x_dapm_widgets), | ||
| 167 | .dapm_routes = audio_map, | ||
| 168 | .num_dapm_routes = ARRAY_SIZE(audio_map), | ||
| 169 | .fully_routed = true, | ||
| 170 | }; | ||
| 171 | |||
| 172 | static struct s6000_snd_platform_data s6105_snd_data __initdata = { | ||
| 173 | .wide = 0, | ||
| 174 | .channel_in = 0, | ||
| 175 | .channel_out = 1, | ||
| 176 | .lines_in = 1, | ||
| 177 | .lines_out = 1, | ||
| 178 | .same_rate = 1, | ||
| 179 | }; | ||
| 180 | |||
| 181 | static struct platform_device *s6105_snd_device; | ||
| 182 | |||
| 183 | /* temporary i2c device creation until this can be moved into the machine | ||
| 184 | * support file. | ||
| 185 | */ | ||
| 186 | static struct i2c_board_info i2c_device[] = { | ||
| 187 | { I2C_BOARD_INFO("tlv320aic33", 0x18), } | ||
| 188 | }; | ||
| 189 | |||
| 190 | static int __init s6105_init(void) | ||
| 191 | { | ||
| 192 | int ret; | ||
| 193 | |||
| 194 | i2c_register_board_info(0, i2c_device, ARRAY_SIZE(i2c_device)); | ||
| 195 | |||
| 196 | s6105_snd_device = platform_device_alloc("soc-audio", -1); | ||
| 197 | if (!s6105_snd_device) | ||
| 198 | return -ENOMEM; | ||
| 199 | |||
| 200 | platform_set_drvdata(s6105_snd_device, &snd_soc_card_s6105); | ||
| 201 | platform_device_add_data(s6105_snd_device, &s6105_snd_data, | ||
| 202 | sizeof(s6105_snd_data)); | ||
| 203 | |||
| 204 | ret = platform_device_add(s6105_snd_device); | ||
| 205 | if (ret) | ||
| 206 | platform_device_put(s6105_snd_device); | ||
| 207 | |||
| 208 | return ret; | ||
| 209 | } | ||
| 210 | |||
| 211 | static void __exit s6105_exit(void) | ||
| 212 | { | ||
| 213 | platform_device_unregister(s6105_snd_device); | ||
| 214 | } | ||
| 215 | |||
| 216 | module_init(s6105_init); | ||
| 217 | module_exit(s6105_exit); | ||
| 218 | |||
| 219 | MODULE_AUTHOR("Daniel Gloeckner"); | ||
| 220 | MODULE_DESCRIPTION("Stretch s6105 IP camera ASoC driver"); | ||
| 221 | MODULE_LICENSE("GPL"); | ||
