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