diff options
Diffstat (limited to 'sound/soc/dwc/designware_i2s.c')
| -rw-r--r-- | sound/soc/dwc/designware_i2s.c | 464 |
1 files changed, 0 insertions, 464 deletions
diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c deleted file mode 100644 index 70eb37a5dd16..000000000000 --- a/sound/soc/dwc/designware_i2s.c +++ /dev/null @@ -1,464 +0,0 @@ -/* - * ALSA SoC Synopsys I2S Audio Layer - * - * sound/soc/dwc/designware_i2s.c - * - * Copyright (C) 2010 ST Microelectronics - * Rajeev Kumar <rajeev-dlh.kumar@st.com> - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ - -#include <linux/clk.h> -#include <linux/device.h> -#include <linux/init.h> -#include <linux/io.h> -#include <linux/interrupt.h> -#include <linux/module.h> -#include <linux/slab.h> -#include <sound/designware_i2s.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> -#include <sound/soc.h> - -/* common register for all channel */ -#define IER 0x000 -#define IRER 0x004 -#define ITER 0x008 -#define CER 0x00C -#define CCR 0x010 -#define RXFFR 0x014 -#define TXFFR 0x018 - -/* I2STxRxRegisters for all channels */ -#define LRBR_LTHR(x) (0x40 * x + 0x020) -#define RRBR_RTHR(x) (0x40 * x + 0x024) -#define RER(x) (0x40 * x + 0x028) -#define TER(x) (0x40 * x + 0x02C) -#define RCR(x) (0x40 * x + 0x030) -#define TCR(x) (0x40 * x + 0x034) -#define ISR(x) (0x40 * x + 0x038) -#define IMR(x) (0x40 * x + 0x03C) -#define ROR(x) (0x40 * x + 0x040) -#define TOR(x) (0x40 * x + 0x044) -#define RFCR(x) (0x40 * x + 0x048) -#define TFCR(x) (0x40 * x + 0x04C) -#define RFF(x) (0x40 * x + 0x050) -#define TFF(x) (0x40 * x + 0x054) - -/* I2SCOMPRegisters */ -#define I2S_COMP_PARAM_2 0x01F0 -#define I2S_COMP_PARAM_1 0x01F4 -#define I2S_COMP_VERSION 0x01F8 -#define I2S_COMP_TYPE 0x01FC - -#define MAX_CHANNEL_NUM 8 -#define MIN_CHANNEL_NUM 2 - -struct dw_i2s_dev { - void __iomem *i2s_base; - struct clk *clk; - int active; - unsigned int capability; - struct device *dev; - - /* data related to DMA transfers b/w i2s and DMAC */ - struct i2s_dma_data play_dma_data; - struct i2s_dma_data capture_dma_data; - struct i2s_clk_config_data config; - int (*i2s_clk_cfg)(struct i2s_clk_config_data *config); -}; - -static inline void i2s_write_reg(void __iomem *io_base, int reg, u32 val) -{ - writel(val, io_base + reg); -} - -static inline u32 i2s_read_reg(void __iomem *io_base, int reg) -{ - return readl(io_base + reg); -} - -static inline void i2s_disable_channels(struct dw_i2s_dev *dev, u32 stream) -{ - u32 i = 0; - - if (stream == SNDRV_PCM_STREAM_PLAYBACK) { - for (i = 0; i < 4; i++) - i2s_write_reg(dev->i2s_base, TER(i), 0); - } else { - for (i = 0; i < 4; i++) - i2s_write_reg(dev->i2s_base, RER(i), 0); - } -} - -static inline void i2s_clear_irqs(struct dw_i2s_dev *dev, u32 stream) -{ - u32 i = 0; - - if (stream == SNDRV_PCM_STREAM_PLAYBACK) { - for (i = 0; i < 4; i++) - i2s_write_reg(dev->i2s_base, TOR(i), 0); - } else { - for (i = 0; i < 4; i++) - i2s_write_reg(dev->i2s_base, ROR(i), 0); - } -} - -static void i2s_start(struct dw_i2s_dev *dev, - struct snd_pcm_substream *substream) -{ - - i2s_write_reg(dev->i2s_base, IER, 1); - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - i2s_write_reg(dev->i2s_base, ITER, 1); - else - i2s_write_reg(dev->i2s_base, IRER, 1); - - i2s_write_reg(dev->i2s_base, CER, 1); -} - -static void i2s_stop(struct dw_i2s_dev *dev, - struct snd_pcm_substream *substream) -{ - u32 i = 0, irq; - - i2s_clear_irqs(dev, substream->stream); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - i2s_write_reg(dev->i2s_base, ITER, 0); - - for (i = 0; i < 4; i++) { - irq = i2s_read_reg(dev->i2s_base, IMR(i)); - i2s_write_reg(dev->i2s_base, IMR(i), irq | 0x30); - } - } else { - i2s_write_reg(dev->i2s_base, IRER, 0); - - for (i = 0; i < 4; i++) { - irq = i2s_read_reg(dev->i2s_base, IMR(i)); - i2s_write_reg(dev->i2s_base, IMR(i), irq | 0x03); - } - } - - if (!dev->active) { - i2s_write_reg(dev->i2s_base, CER, 0); - i2s_write_reg(dev->i2s_base, IER, 0); - } -} - -static int dw_i2s_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *cpu_dai) -{ - struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai); - struct i2s_dma_data *dma_data = NULL; - - if (!(dev->capability & DWC_I2S_RECORD) && - (substream->stream == SNDRV_PCM_STREAM_CAPTURE)) - return -EINVAL; - - if (!(dev->capability & DWC_I2S_PLAY) && - (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)) - return -EINVAL; - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - dma_data = &dev->play_dma_data; - else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) - dma_data = &dev->capture_dma_data; - - snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)dma_data); - - return 0; -} - -static int dw_i2s_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) -{ - struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); - struct i2s_clk_config_data *config = &dev->config; - u32 ccr, xfer_resolution, ch_reg, irq; - int ret; - - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: - config->data_width = 16; - ccr = 0x00; - xfer_resolution = 0x02; - break; - - case SNDRV_PCM_FORMAT_S24_LE: - config->data_width = 24; - ccr = 0x08; - xfer_resolution = 0x04; - break; - - case SNDRV_PCM_FORMAT_S32_LE: - config->data_width = 32; - ccr = 0x10; - xfer_resolution = 0x05; - break; - - default: - dev_err(dev->dev, "designware-i2s: unsuppted PCM fmt"); - return -EINVAL; - } - - config->chan_nr = params_channels(params); - - switch (config->chan_nr) { - case EIGHT_CHANNEL_SUPPORT: - ch_reg = 3; - break; - case SIX_CHANNEL_SUPPORT: - ch_reg = 2; - break; - case FOUR_CHANNEL_SUPPORT: - ch_reg = 1; - break; - case TWO_CHANNEL_SUPPORT: - ch_reg = 0; - break; - default: - dev_err(dev->dev, "channel not supported\n"); - return -EINVAL; - } - - i2s_disable_channels(dev, substream->stream); - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - i2s_write_reg(dev->i2s_base, TCR(ch_reg), xfer_resolution); - i2s_write_reg(dev->i2s_base, TFCR(ch_reg), 0x02); - irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg)); - i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x30); - i2s_write_reg(dev->i2s_base, TER(ch_reg), 1); - } else { - i2s_write_reg(dev->i2s_base, RCR(ch_reg), xfer_resolution); - i2s_write_reg(dev->i2s_base, RFCR(ch_reg), 0x07); - irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg)); - i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x03); - i2s_write_reg(dev->i2s_base, RER(ch_reg), 1); - } - - i2s_write_reg(dev->i2s_base, CCR, ccr); - - config->sample_rate = params_rate(params); - - if (!dev->i2s_clk_cfg) - return -EINVAL; - - ret = dev->i2s_clk_cfg(config); - if (ret < 0) { - dev_err(dev->dev, "runtime audio clk config fail\n"); - return ret; - } - - return 0; -} - -static void dw_i2s_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - snd_soc_dai_set_dma_data(dai, substream, NULL); -} - -static int dw_i2s_trigger(struct snd_pcm_substream *substream, - int cmd, struct snd_soc_dai *dai) -{ - struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); - int ret = 0; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - dev->active++; - i2s_start(dev, substream); - break; - - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - dev->active--; - i2s_stop(dev, substream); - break; - default: - ret = -EINVAL; - break; - } - return ret; -} - -static struct snd_soc_dai_ops dw_i2s_dai_ops = { - .startup = dw_i2s_startup, - .shutdown = dw_i2s_shutdown, - .hw_params = dw_i2s_hw_params, - .trigger = dw_i2s_trigger, -}; - -static const struct snd_soc_component_driver dw_i2s_component = { - .name = "dw-i2s", -}; - -#ifdef CONFIG_PM - -static int dw_i2s_suspend(struct snd_soc_dai *dai) -{ - struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); - - clk_disable(dev->clk); - return 0; -} - -static int dw_i2s_resume(struct snd_soc_dai *dai) -{ - struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); - - clk_enable(dev->clk); - return 0; -} - -#else -#define dw_i2s_suspend NULL -#define dw_i2s_resume NULL -#endif - -static int dw_i2s_probe(struct platform_device *pdev) -{ - const struct i2s_platform_data *pdata = pdev->dev.platform_data; - struct dw_i2s_dev *dev; - struct resource *res; - int ret; - unsigned int cap; - struct snd_soc_dai_driver *dw_i2s_dai; - - if (!pdata) { - dev_err(&pdev->dev, "Invalid platform data\n"); - return -EINVAL; - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "no i2s resource defined\n"); - return -ENODEV; - } - - if (!devm_request_mem_region(&pdev->dev, res->start, - resource_size(res), pdev->name)) { - dev_err(&pdev->dev, "i2s region already claimed\n"); - return -EBUSY; - } - - dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); - if (!dev) { - dev_warn(&pdev->dev, "kzalloc fail\n"); - return -ENOMEM; - } - - dev->i2s_base = devm_ioremap(&pdev->dev, res->start, - resource_size(res)); - if (!dev->i2s_base) { - dev_err(&pdev->dev, "ioremap fail for i2s_region\n"); - return -ENOMEM; - } - - cap = pdata->cap; - dev->capability = cap; - dev->i2s_clk_cfg = pdata->i2s_clk_cfg; - - /* Set DMA slaves info */ - - dev->play_dma_data.data = pdata->play_dma_data; - dev->capture_dma_data.data = pdata->capture_dma_data; - dev->play_dma_data.addr = res->start + I2S_TXDMA; - dev->capture_dma_data.addr = res->start + I2S_RXDMA; - dev->play_dma_data.max_burst = 16; - dev->capture_dma_data.max_burst = 16; - dev->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; - dev->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; - dev->play_dma_data.filter = pdata->filter; - dev->capture_dma_data.filter = pdata->filter; - - dev->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(dev->clk)) - return PTR_ERR(dev->clk); - - ret = clk_enable(dev->clk); - if (ret < 0) - goto err_clk_put; - - dw_i2s_dai = devm_kzalloc(&pdev->dev, sizeof(*dw_i2s_dai), GFP_KERNEL); - if (!dw_i2s_dai) { - dev_err(&pdev->dev, "mem allocation failed for dai driver\n"); - ret = -ENOMEM; - goto err_clk_disable; - } - - if (cap & DWC_I2S_PLAY) { - dev_dbg(&pdev->dev, " designware: play supported\n"); - dw_i2s_dai->playback.channels_min = MIN_CHANNEL_NUM; - dw_i2s_dai->playback.channels_max = pdata->channel; - dw_i2s_dai->playback.formats = pdata->snd_fmts; - dw_i2s_dai->playback.rates = pdata->snd_rates; - } - - if (cap & DWC_I2S_RECORD) { - dev_dbg(&pdev->dev, "designware: record supported\n"); - dw_i2s_dai->capture.channels_min = MIN_CHANNEL_NUM; - dw_i2s_dai->capture.channels_max = pdata->channel; - dw_i2s_dai->capture.formats = pdata->snd_fmts; - dw_i2s_dai->capture.rates = pdata->snd_rates; - } - - dw_i2s_dai->ops = &dw_i2s_dai_ops; - dw_i2s_dai->suspend = dw_i2s_suspend; - dw_i2s_dai->resume = dw_i2s_resume; - - dev->dev = &pdev->dev; - dev_set_drvdata(&pdev->dev, dev); - ret = snd_soc_register_component(&pdev->dev, &dw_i2s_component, - dw_i2s_dai, 1); - if (ret != 0) { - dev_err(&pdev->dev, "not able to register dai\n"); - goto err_set_drvdata; - } - - return 0; - -err_set_drvdata: - dev_set_drvdata(&pdev->dev, NULL); -err_clk_disable: - clk_disable(dev->clk); -err_clk_put: - clk_put(dev->clk); - return ret; -} - -static int dw_i2s_remove(struct platform_device *pdev) -{ - struct dw_i2s_dev *dev = dev_get_drvdata(&pdev->dev); - - snd_soc_unregister_component(&pdev->dev); - dev_set_drvdata(&pdev->dev, NULL); - - clk_put(dev->clk); - - return 0; -} - -static struct platform_driver dw_i2s_driver = { - .probe = dw_i2s_probe, - .remove = dw_i2s_remove, - .driver = { - .name = "designware-i2s", - .owner = THIS_MODULE, - }, -}; - -module_platform_driver(dw_i2s_driver); - -MODULE_AUTHOR("Rajeev Kumar <rajeev-dlh.kumar@st.com>"); -MODULE_DESCRIPTION("DESIGNWARE I2S SoC Interface"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:designware_i2s"); |
