Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ASoC: SPEAr spdif_in: Add spdif IN support

This patch implements the spdif IN driver for ST peripheral

Signed-off-by: Vipin Kumar <vipin.kumar@st.com>
Signed-off-by: Rajeev Kumar <rajeev-dlh.kumar@st.com>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>

authored by

Vipin Kumar and committed by
Mark Brown
ace36d85 241b446f

+386
+29
include/sound/spear_spdif.h
··· 1 + /* 2 + * Copyright (ST) 2012 Vipin Kumar (vipin.kumar@st.com) 3 + * 4 + * This program is free software; you can redistribute it and/or modify 5 + * it under the terms of the GNU General Public License as published by 6 + * the Free Software Foundation; either version 2 of the License, or 7 + * (at your option) any later version. 8 + * 9 + * This program is distributed in the hope that it will be useful, 10 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 + * GNU General Public License for more details. 13 + * 14 + * You should have received a copy of the GNU General Public License 15 + * along with this program; if not, write to the Free Software 16 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 + */ 18 + 19 + #ifndef __SOUND_SPDIF_H 20 + #define __SOUND_SPDIF_H 21 + 22 + struct spear_spdif_platform_data { 23 + /* DMA params */ 24 + void *dma_params; 25 + bool (*filter)(struct dma_chan *chan, void *slave); 26 + void (*reset_perip)(void); 27 + }; 28 + 29 + #endif /* SOUND_SPDIF_H */
+297
sound/soc/spear/spdif_in.c
··· 1 + /* 2 + * ALSA SoC SPDIF In Audio Layer for spear processors 3 + * 4 + * Copyright (C) 2012 ST Microelectronics 5 + * Vipin Kumar <vipin.kumar@st.com> 6 + * 7 + * This file is licensed under the terms of the GNU General Public 8 + * License version 2. This program is licensed "as is" without any 9 + * warranty of any kind, whether express or implied. 10 + */ 11 + 12 + #include <linux/clk.h> 13 + #include <linux/delay.h> 14 + #include <linux/device.h> 15 + #include <linux/kernel.h> 16 + #include <linux/init.h> 17 + #include <linux/io.h> 18 + #include <linux/ioport.h> 19 + #include <linux/module.h> 20 + #include <linux/platform_device.h> 21 + #include <sound/pcm.h> 22 + #include <sound/pcm_params.h> 23 + #include <sound/soc.h> 24 + #include <sound/spear_dma.h> 25 + #include <sound/spear_spdif.h> 26 + #include "spdif_in_regs.h" 27 + 28 + struct spdif_in_params { 29 + u32 format; 30 + }; 31 + 32 + struct spdif_in_dev { 33 + struct clk *clk; 34 + struct spear_dma_data dma_params; 35 + struct spdif_in_params saved_params; 36 + void *io_base; 37 + struct device *dev; 38 + void (*reset_perip)(void); 39 + int irq; 40 + }; 41 + 42 + static void spdif_in_configure(struct spdif_in_dev *host) 43 + { 44 + u32 ctrl = SPDIF_IN_PRTYEN | SPDIF_IN_STATEN | SPDIF_IN_USREN | 45 + SPDIF_IN_VALEN | SPDIF_IN_BLKEN; 46 + ctrl |= SPDIF_MODE_16BIT | SPDIF_FIFO_THRES_16; 47 + 48 + writel(ctrl, host->io_base + SPDIF_IN_CTRL); 49 + writel(0xF, host->io_base + SPDIF_IN_IRQ_MASK); 50 + } 51 + 52 + static int spdif_in_startup(struct snd_pcm_substream *substream, 53 + struct snd_soc_dai *cpu_dai) 54 + { 55 + struct spdif_in_dev *host = snd_soc_dai_get_drvdata(cpu_dai); 56 + 57 + if (substream->stream != SNDRV_PCM_STREAM_CAPTURE) 58 + return -EINVAL; 59 + 60 + snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)&host->dma_params); 61 + return 0; 62 + } 63 + 64 + static void spdif_in_shutdown(struct snd_pcm_substream *substream, 65 + struct snd_soc_dai *dai) 66 + { 67 + struct spdif_in_dev *host = snd_soc_dai_get_drvdata(dai); 68 + 69 + if (substream->stream != SNDRV_PCM_STREAM_CAPTURE) 70 + return; 71 + 72 + writel(0x0, host->io_base + SPDIF_IN_IRQ_MASK); 73 + snd_soc_dai_set_dma_data(dai, substream, NULL); 74 + } 75 + 76 + static void spdif_in_format(struct spdif_in_dev *host, u32 format) 77 + { 78 + u32 ctrl = readl(host->io_base + SPDIF_IN_CTRL); 79 + 80 + switch (format) { 81 + case SNDRV_PCM_FORMAT_S16_LE: 82 + ctrl |= SPDIF_XTRACT_16BIT; 83 + break; 84 + 85 + case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE: 86 + ctrl &= ~SPDIF_XTRACT_16BIT; 87 + break; 88 + } 89 + 90 + writel(ctrl, host->io_base + SPDIF_IN_CTRL); 91 + } 92 + 93 + static int spdif_in_hw_params(struct snd_pcm_substream *substream, 94 + struct snd_pcm_hw_params *params, 95 + struct snd_soc_dai *dai) 96 + { 97 + struct spdif_in_dev *host = snd_soc_dai_get_drvdata(dai); 98 + u32 format; 99 + 100 + if (substream->stream != SNDRV_PCM_STREAM_CAPTURE) 101 + return -EINVAL; 102 + 103 + format = params_format(params); 104 + host->saved_params.format = format; 105 + 106 + return 0; 107 + } 108 + 109 + static int spdif_in_trigger(struct snd_pcm_substream *substream, int cmd, 110 + struct snd_soc_dai *dai) 111 + { 112 + struct spdif_in_dev *host = snd_soc_dai_get_drvdata(dai); 113 + u32 ctrl; 114 + int ret = 0; 115 + 116 + if (substream->stream != SNDRV_PCM_STREAM_CAPTURE) 117 + return -EINVAL; 118 + 119 + switch (cmd) { 120 + case SNDRV_PCM_TRIGGER_START: 121 + case SNDRV_PCM_TRIGGER_RESUME: 122 + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 123 + clk_enable(host->clk); 124 + spdif_in_configure(host); 125 + spdif_in_format(host, host->saved_params.format); 126 + 127 + ctrl = readl(host->io_base + SPDIF_IN_CTRL); 128 + ctrl |= SPDIF_IN_SAMPLE | SPDIF_IN_ENB; 129 + writel(ctrl, host->io_base + SPDIF_IN_CTRL); 130 + writel(0xF, host->io_base + SPDIF_IN_IRQ_MASK); 131 + break; 132 + 133 + case SNDRV_PCM_TRIGGER_STOP: 134 + case SNDRV_PCM_TRIGGER_SUSPEND: 135 + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 136 + ctrl = readl(host->io_base + SPDIF_IN_CTRL); 137 + ctrl &= ~(SPDIF_IN_SAMPLE | SPDIF_IN_ENB); 138 + writel(ctrl, host->io_base + SPDIF_IN_CTRL); 139 + writel(0x0, host->io_base + SPDIF_IN_IRQ_MASK); 140 + 141 + if (host->reset_perip) 142 + host->reset_perip(); 143 + clk_disable(host->clk); 144 + break; 145 + 146 + default: 147 + ret = -EINVAL; 148 + break; 149 + } 150 + return ret; 151 + } 152 + 153 + static struct snd_soc_dai_ops spdif_in_dai_ops = { 154 + .startup = spdif_in_startup, 155 + .shutdown = spdif_in_shutdown, 156 + .trigger = spdif_in_trigger, 157 + .hw_params = spdif_in_hw_params, 158 + }; 159 + 160 + struct snd_soc_dai_driver spdif_in_dai = { 161 + .capture = { 162 + .channels_min = 2, 163 + .channels_max = 2, 164 + .rates = (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ 165 + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | \ 166 + SNDRV_PCM_RATE_192000), 167 + .formats = SNDRV_PCM_FMTBIT_S16_LE | \ 168 + SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE, 169 + }, 170 + .ops = &spdif_in_dai_ops, 171 + }; 172 + 173 + static irqreturn_t spdif_in_irq(int irq, void *arg) 174 + { 175 + struct spdif_in_dev *host = (struct spdif_in_dev *)arg; 176 + 177 + u32 irq_status = readl(host->io_base + SPDIF_IN_IRQ); 178 + 179 + if (!irq_status) 180 + return IRQ_NONE; 181 + 182 + if (irq_status & SPDIF_IRQ_FIFOWRITE) 183 + dev_err(host->dev, "spdif in: fifo write error"); 184 + if (irq_status & SPDIF_IRQ_EMPTYFIFOREAD) 185 + dev_err(host->dev, "spdif in: empty fifo read error"); 186 + if (irq_status & SPDIF_IRQ_FIFOFULL) 187 + dev_err(host->dev, "spdif in: fifo full error"); 188 + if (irq_status & SPDIF_IRQ_OUTOFRANGE) 189 + dev_err(host->dev, "spdif in: out of range error"); 190 + 191 + writel(0, host->io_base + SPDIF_IN_IRQ); 192 + 193 + return IRQ_HANDLED; 194 + } 195 + 196 + static int spdif_in_probe(struct platform_device *pdev) 197 + { 198 + struct spdif_in_dev *host; 199 + struct spear_spdif_platform_data *pdata; 200 + struct resource *res, *res_fifo; 201 + int ret; 202 + 203 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 204 + if (!res) 205 + return -EINVAL; 206 + 207 + res_fifo = platform_get_resource(pdev, IORESOURCE_IO, 0); 208 + if (!res_fifo) 209 + return -EINVAL; 210 + 211 + if (!devm_request_mem_region(&pdev->dev, res->start, 212 + resource_size(res), pdev->name)) { 213 + dev_warn(&pdev->dev, "Failed to get memory resourse\n"); 214 + return -ENOENT; 215 + } 216 + 217 + host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL); 218 + if (!host) { 219 + dev_warn(&pdev->dev, "kzalloc fail\n"); 220 + return -ENOMEM; 221 + } 222 + 223 + host->io_base = devm_ioremap(&pdev->dev, res->start, 224 + resource_size(res)); 225 + if (!host->io_base) { 226 + dev_warn(&pdev->dev, "ioremap failed\n"); 227 + return -ENOMEM; 228 + } 229 + 230 + host->irq = platform_get_irq(pdev, 0); 231 + if (host->irq < 0) 232 + return -EINVAL; 233 + 234 + host->clk = clk_get(&pdev->dev, NULL); 235 + if (IS_ERR(host->clk)) 236 + return PTR_ERR(host->clk); 237 + 238 + pdata = dev_get_platdata(&pdev->dev); 239 + 240 + if (!pdata) 241 + return -EINVAL; 242 + 243 + host->dma_params.data = pdata->dma_params; 244 + host->dma_params.addr = res_fifo->start; 245 + host->dma_params.max_burst = 16; 246 + host->dma_params.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 247 + host->dma_params.filter = pdata->filter; 248 + host->reset_perip = pdata->reset_perip; 249 + 250 + host->dev = &pdev->dev; 251 + dev_set_drvdata(&pdev->dev, host); 252 + 253 + ret = devm_request_irq(&pdev->dev, host->irq, spdif_in_irq, 0, 254 + "spdif-in", host); 255 + if (ret) { 256 + clk_put(host->clk); 257 + dev_warn(&pdev->dev, "request_irq failed\n"); 258 + return ret; 259 + } 260 + 261 + ret = snd_soc_register_dai(&pdev->dev, &spdif_in_dai); 262 + if (ret != 0) { 263 + clk_put(host->clk); 264 + return ret; 265 + } 266 + 267 + return 0; 268 + } 269 + 270 + static int spdif_in_remove(struct platform_device *pdev) 271 + { 272 + struct spdif_in_dev *host = dev_get_drvdata(&pdev->dev); 273 + 274 + snd_soc_unregister_dai(&pdev->dev); 275 + dev_set_drvdata(&pdev->dev, NULL); 276 + 277 + clk_put(host->clk); 278 + 279 + return 0; 280 + } 281 + 282 + 283 + static struct platform_driver spdif_in_driver = { 284 + .probe = spdif_in_probe, 285 + .remove = spdif_in_remove, 286 + .driver = { 287 + .name = "spdif-in", 288 + .owner = THIS_MODULE, 289 + }, 290 + }; 291 + 292 + module_platform_driver(spdif_in_driver); 293 + 294 + MODULE_AUTHOR("Vipin Kumar <vipin.kumar@st.com>"); 295 + MODULE_DESCRIPTION("SPEAr SPDIF IN SoC Interface"); 296 + MODULE_LICENSE("GPL"); 297 + MODULE_ALIAS("platform:spdif_in");
+60
sound/soc/spear/spdif_in_regs.h
··· 1 + /* 2 + * SPEAr SPDIF IN controller header file 3 + * 4 + * Copyright (ST) 2011 Vipin Kumar (vipin.kumar@st.com) 5 + * 6 + * This program is free software; you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License as published by 8 + * the Free Software Foundation; either version 2 of the License, or 9 + * (at your option) any later version. 10 + * 11 + * This program is distributed in the hope that it will be useful, 12 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 + * GNU General Public License for more details. 15 + * 16 + * You should have received a copy of the GNU General Public License 17 + * along with this program; if not, write to the Free Software 18 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 + */ 20 + 21 + #ifndef SPDIF_IN_REGS_H 22 + #define SPDIF_IN_REGS_H 23 + 24 + #define SPDIF_IN_CTRL 0x00 25 + #define SPDIF_IN_PRTYEN (1 << 20) 26 + #define SPDIF_IN_STATEN (1 << 19) 27 + #define SPDIF_IN_USREN (1 << 18) 28 + #define SPDIF_IN_VALEN (1 << 17) 29 + #define SPDIF_IN_BLKEN (1 << 16) 30 + 31 + #define SPDIF_MODE_24BIT (8 << 12) 32 + #define SPDIF_MODE_23BIT (7 << 12) 33 + #define SPDIF_MODE_22BIT (6 << 12) 34 + #define SPDIF_MODE_21BIT (5 << 12) 35 + #define SPDIF_MODE_20BIT (4 << 12) 36 + #define SPDIF_MODE_19BIT (3 << 12) 37 + #define SPDIF_MODE_18BIT (2 << 12) 38 + #define SPDIF_MODE_17BIT (1 << 12) 39 + #define SPDIF_MODE_16BIT (0 << 12) 40 + #define SPDIF_MODE_MASK (0x0F << 12) 41 + 42 + #define SPDIF_IN_VALID (1 << 11) 43 + #define SPDIF_IN_SAMPLE (1 << 10) 44 + #define SPDIF_DATA_SWAP (1 << 9) 45 + #define SPDIF_IN_ENB (1 << 8) 46 + #define SPDIF_DATA_REVERT (1 << 7) 47 + #define SPDIF_XTRACT_16BIT (1 << 6) 48 + #define SPDIF_FIFO_THRES_16 (16 << 0) 49 + 50 + #define SPDIF_IN_IRQ_MASK 0x04 51 + #define SPDIF_IN_IRQ 0x08 52 + #define SPDIF_IRQ_FIFOWRITE (1 << 0) 53 + #define SPDIF_IRQ_EMPTYFIFOREAD (1 << 1) 54 + #define SPDIF_IRQ_FIFOFULL (1 << 2) 55 + #define SPDIF_IRQ_OUTOFRANGE (1 << 3) 56 + 57 + #define SPDIF_IN_STA 0x0C 58 + #define SPDIF_IN_LOCK (0x1 << 0) 59 + 60 + #endif /* SPDIF_IN_REGS_H */