brintos

brintos / linux-shallow public Read only

0
0
Text · 6.7 KiB · 4ad8b1f Raw
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