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/dmaengine_pcm.h> 22 #include <sound/pcm.h> 23 #include <sound/pcm_params.h> 24 #include <sound/soc.h> 25 #include <sound/spear_dma.h> 26 #include <sound/spear_spdif.h> 27 #include "spdif_in_regs.h" 28 #include "spear_pcm.h" 29 30 struct spdif_in_params { 31 u32 format; 32 }; 33 34 struct spdif_in_dev { 35 struct clk *clk; 36 struct spear_dma_data dma_params; 37 struct spdif_in_params saved_params; 38 void *io_base; 39 struct device *dev; 40 void (*reset_perip)(void); 41 int irq; 42 struct snd_dmaengine_dai_dma_data dma_params_rx; 43 struct snd_dmaengine_pcm_config config; 44 }; 45 46 static void spdif_in_configure(struct spdif_in_dev *host) 47 { 48 u32 ctrl = SPDIF_IN_PRTYEN | SPDIF_IN_STATEN | SPDIF_IN_USREN | 49 SPDIF_IN_VALEN | SPDIF_IN_BLKEN; 50 ctrl |= SPDIF_MODE_16BIT | SPDIF_FIFO_THRES_16; 51 52 writel(ctrl, host->io_base + SPDIF_IN_CTRL); 53 writel(0xF, host->io_base + SPDIF_IN_IRQ_MASK); 54 } 55 56 static int spdif_in_dai_probe(struct snd_soc_dai *dai) 57 { 58 struct spdif_in_dev *host = snd_soc_dai_get_drvdata(dai); 59 60 host->dma_params_rx.filter_data = &host->dma_params; 61 dai->capture_dma_data = &host->dma_params_rx; 62 63 return 0; 64 } 65 66 static void spdif_in_shutdown(struct snd_pcm_substream *substream, 67 struct snd_soc_dai *dai) 68 { 69 struct spdif_in_dev *host = snd_soc_dai_get_drvdata(dai); 70 71 if (substream->stream != SNDRV_PCM_STREAM_CAPTURE) 72 return; 73 74 writel(0x0, host->io_base + SPDIF_IN_IRQ_MASK); 75 } 76 77 static void spdif_in_format(struct spdif_in_dev *host, u32 format) 78 { 79 u32 ctrl = readl(host->io_base + SPDIF_IN_CTRL); 80 81 switch (format) { 82 case SNDRV_PCM_FORMAT_S16_LE: 83 ctrl |= SPDIF_XTRACT_16BIT; 84 break; 85 86 case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE: 87 ctrl &= ~SPDIF_XTRACT_16BIT; 88 break; 89 } 90 91 writel(ctrl, host->io_base + SPDIF_IN_CTRL); 92 } 93 94 static int spdif_in_hw_params(struct snd_pcm_substream *substream, 95 struct snd_pcm_hw_params *params, 96 struct snd_soc_dai *dai) 97 { 98 struct spdif_in_dev *host = snd_soc_dai_get_drvdata(dai); 99 u32 format; 100 101 if (substream->stream != SNDRV_PCM_STREAM_CAPTURE) 102 return -EINVAL; 103 104 format = params_format(params); 105 host->saved_params.format = format; 106 107 return 0; 108 } 109 110 static int spdif_in_trigger(struct snd_pcm_substream *substream, int cmd, 111 struct snd_soc_dai *dai) 112 { 113 struct spdif_in_dev *host = snd_soc_dai_get_drvdata(dai); 114 u32 ctrl; 115 int ret = 0; 116 117 if (substream->stream != SNDRV_PCM_STREAM_CAPTURE) 118 return -EINVAL; 119 120 switch (cmd) { 121 case SNDRV_PCM_TRIGGER_START: 122 case SNDRV_PCM_TRIGGER_RESUME: 123 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 124 clk_enable(host->clk); 125 spdif_in_configure(host); 126 spdif_in_format(host, host->saved_params.format); 127 128 ctrl = readl(host->io_base + SPDIF_IN_CTRL); 129 ctrl |= SPDIF_IN_SAMPLE | SPDIF_IN_ENB; 130 writel(ctrl, host->io_base + SPDIF_IN_CTRL); 131 writel(0xF, host->io_base + SPDIF_IN_IRQ_MASK); 132 break; 133 134 case SNDRV_PCM_TRIGGER_STOP: 135 case SNDRV_PCM_TRIGGER_SUSPEND: 136 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 137 ctrl = readl(host->io_base + SPDIF_IN_CTRL); 138 ctrl &= ~(SPDIF_IN_SAMPLE | SPDIF_IN_ENB); 139 writel(ctrl, host->io_base + SPDIF_IN_CTRL); 140 writel(0x0, host->io_base + SPDIF_IN_IRQ_MASK); 141 142 if (host->reset_perip) 143 host->reset_perip(); 144 clk_disable(host->clk); 145 break; 146 147 default: 148 ret = -EINVAL; 149 break; 150 } 151 return ret; 152 } 153 154 static const struct snd_soc_dai_ops spdif_in_dai_ops = { 155 .shutdown = spdif_in_shutdown, 156 .trigger = spdif_in_trigger, 157 .hw_params = spdif_in_hw_params, 158 }; 159 160 static struct snd_soc_dai_driver spdif_in_dai = { 161 .probe = spdif_in_dai_probe, 162 .capture = { 163 .channels_min = 2, 164 .channels_max = 2, 165 .rates = (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ 166 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | \ 167 SNDRV_PCM_RATE_192000), 168 .formats = SNDRV_PCM_FMTBIT_S16_LE | \ 169 SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE, 170 }, 171 .ops = &spdif_in_dai_ops, 172 }; 173 174 static const struct snd_soc_component_driver spdif_in_component = { 175 .name = "spdif-in", 176 .legacy_dai_naming = 1, 177 }; 178 179 static irqreturn_t spdif_in_irq(int irq, void *arg) 180 { 181 struct spdif_in_dev *host = (struct spdif_in_dev *)arg; 182 183 u32 irq_status = readl(host->io_base + SPDIF_IN_IRQ); 184 185 if (!irq_status) 186 return IRQ_NONE; 187 188 if (irq_status & SPDIF_IRQ_FIFOWRITE) 189 dev_err(host->dev, "spdif in: fifo write error"); 190 if (irq_status & SPDIF_IRQ_EMPTYFIFOREAD) 191 dev_err(host->dev, "spdif in: empty fifo read error"); 192 if (irq_status & SPDIF_IRQ_FIFOFULL) 193 dev_err(host->dev, "spdif in: fifo full error"); 194 if (irq_status & SPDIF_IRQ_OUTOFRANGE) 195 dev_err(host->dev, "spdif in: out of range error"); 196 197 writel(0, host->io_base + SPDIF_IN_IRQ); 198 199 return IRQ_HANDLED; 200 } 201 202 static int spdif_in_probe(struct platform_device *pdev) 203 { 204 struct spdif_in_dev *host; 205 struct spear_spdif_platform_data *pdata; 206 struct resource *res_fifo; 207 void __iomem *io_base; 208 int ret; 209 210 io_base = devm_platform_ioremap_resource(pdev, 0); 211 if (IS_ERR(io_base)) 212 return PTR_ERR(io_base); 213 214 res_fifo = platform_get_resource(pdev, IORESOURCE_IO, 0); 215 if (!res_fifo) 216 return -EINVAL; 217 218 host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL); 219 if (!host) 220 return -ENOMEM; 221 222 host->io_base = io_base; 223 host->irq = platform_get_irq(pdev, 0); 224 if (host->irq < 0) { 225 dev_warn(&pdev->dev, "failed to get IRQ: %d\n", host->irq); 226 return host->irq; 227 } 228 229 host->clk = devm_clk_get(&pdev->dev, NULL); 230 if (IS_ERR(host->clk)) 231 return PTR_ERR(host->clk); 232 233 pdata = dev_get_platdata(&pdev->dev); 234 235 if (!pdata) 236 return -EINVAL; 237 238 host->dma_params.data = pdata->dma_params; 239 host->dma_params.addr = res_fifo->start; 240 host->dma_params.max_burst = 16; 241 host->dma_params.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 242 host->reset_perip = pdata->reset_perip; 243 244 host->dev = &pdev->dev; 245 dev_set_drvdata(&pdev->dev, host); 246 247 ret = devm_request_irq(&pdev->dev, host->irq, spdif_in_irq, 0, 248 "spdif-in", host); 249 if (ret) { 250 dev_warn(&pdev->dev, "request_irq failed\n"); 251 return ret; 252 } 253 254 ret = devm_snd_soc_register_component(&pdev->dev, &spdif_in_component, 255 &spdif_in_dai, 1); 256 if (ret) 257 return ret; 258 259 return devm_spear_pcm_platform_register(&pdev->dev, &host->config, 260 pdata->filter); 261 } 262 263 static struct platform_driver spdif_in_driver = { 264 .probe = spdif_in_probe, 265 .driver = { 266 .name = "spdif-in", 267 }, 268 }; 269 270 module_platform_driver(spdif_in_driver); 271 272 MODULE_AUTHOR("Vipin Kumar <vipin.kumar@st.com>"); 273 MODULE_DESCRIPTION("SPEAr SPDIF IN SoC Interface"); 274 MODULE_LICENSE("GPL"); 275 MODULE_ALIAS("platform:spdif_in"); 276
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.