276 lines · c
1/*2 * ALSA SoC SPDIF In Audio Layer for spear processors3 *4 * Copyright (C) 2012 ST Microelectronics5 * Vipin Kumar <vipin.kumar@st.com>6 *7 * This file is licensed under the terms of the GNU General Public8 * License version 2. This program is licensed "as is" without any9 * 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 30struct spdif_in_params {31 u32 format;32};33 34struct 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 46static 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 56static 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 66static 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 77static 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 94static 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 110static 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 154static 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 160static 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 174static const struct snd_soc_component_driver spdif_in_component = {175 .name = "spdif-in",176 .legacy_dai_naming = 1,177};178 179static 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 202static 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 263static struct platform_driver spdif_in_driver = {264 .probe = spdif_in_probe,265 .driver = {266 .name = "spdif-in",267 },268};269 270module_platform_driver(spdif_in_driver);271 272MODULE_AUTHOR("Vipin Kumar <vipin.kumar@st.com>");273MODULE_DESCRIPTION("SPEAr SPDIF IN SoC Interface");274MODULE_LICENSE("GPL");275MODULE_ALIAS("platform:spdif_in");276