1a59d1b8e0
Based on 1 normalized pattern(s): this program is free software you can redistribute it and or modify it under the terms of the gnu general public license as published by the free software foundation either version 2 of the license or at your option any later version this program is distributed in the hope that it will be useful but without any warranty without even the implied warranty of merchantability or fitness for a particular purpose see the gnu general public license for more details you should have received a copy of the gnu general public license along with this program if not write to the free software foundation inc 59 temple place suite 330 boston ma 02111 1307 usa extracted by the scancode license scanner the SPDX license identifier GPL-2.0-or-later has been chosen to replace the boilerplate/reference in 1334 file(s). Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Reviewed-by: Allison Randal <allison@lohutok.net> Reviewed-by: Richard Fontana <rfontana@redhat.com> Cc: linux-spdx@vger.kernel.org Link: https://lkml.kernel.org/r/20190527070033.113240726@linutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
247 lines
6.4 KiB
C
247 lines
6.4 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* ALSA SoC Voice Codec Interface for TI DAVINCI processor
|
|
*
|
|
* Copyright (C) 2010 Texas Instruments.
|
|
*
|
|
* Author: Miguel Aguilar <miguel.aguilar@ridgerun.com>
|
|
*/
|
|
|
|
#include <linux/init.h>
|
|
#include <linux/module.h>
|
|
#include <linux/device.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/io.h>
|
|
#include <linux/mfd/davinci_voicecodec.h>
|
|
|
|
#include <sound/core.h>
|
|
#include <sound/pcm.h>
|
|
#include <sound/pcm_params.h>
|
|
#include <sound/initval.h>
|
|
#include <sound/soc.h>
|
|
#include <sound/dmaengine_pcm.h>
|
|
|
|
#include "edma-pcm.h"
|
|
#include "davinci-i2s.h"
|
|
|
|
#define MOD_REG_BIT(val, mask, set) do { \
|
|
if (set) { \
|
|
val |= mask; \
|
|
} else { \
|
|
val &= ~mask; \
|
|
} \
|
|
} while (0)
|
|
|
|
struct davinci_vcif_dev {
|
|
struct davinci_vc *davinci_vc;
|
|
struct snd_dmaengine_dai_dma_data dma_data[2];
|
|
int dma_request[2];
|
|
};
|
|
|
|
static void davinci_vcif_start(struct snd_pcm_substream *substream)
|
|
{
|
|
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
struct davinci_vcif_dev *davinci_vcif_dev =
|
|
snd_soc_dai_get_drvdata(rtd->cpu_dai);
|
|
struct davinci_vc *davinci_vc = davinci_vcif_dev->davinci_vc;
|
|
u32 w;
|
|
|
|
/* Start the sample generator and enable transmitter/receiver */
|
|
w = readl(davinci_vc->base + DAVINCI_VC_CTRL);
|
|
|
|
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
|
MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTDAC, 0);
|
|
else
|
|
MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTADC, 0);
|
|
|
|
writel(w, davinci_vc->base + DAVINCI_VC_CTRL);
|
|
}
|
|
|
|
static void davinci_vcif_stop(struct snd_pcm_substream *substream)
|
|
{
|
|
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
struct davinci_vcif_dev *davinci_vcif_dev =
|
|
snd_soc_dai_get_drvdata(rtd->cpu_dai);
|
|
struct davinci_vc *davinci_vc = davinci_vcif_dev->davinci_vc;
|
|
u32 w;
|
|
|
|
/* Reset transmitter/receiver and sample rate/frame sync generators */
|
|
w = readl(davinci_vc->base + DAVINCI_VC_CTRL);
|
|
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
|
MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTDAC, 1);
|
|
else
|
|
MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTADC, 1);
|
|
|
|
writel(w, davinci_vc->base + DAVINCI_VC_CTRL);
|
|
}
|
|
|
|
static int davinci_vcif_hw_params(struct snd_pcm_substream *substream,
|
|
struct snd_pcm_hw_params *params,
|
|
struct snd_soc_dai *dai)
|
|
{
|
|
struct davinci_vcif_dev *davinci_vcif_dev = snd_soc_dai_get_drvdata(dai);
|
|
struct davinci_vc *davinci_vc = davinci_vcif_dev->davinci_vc;
|
|
u32 w;
|
|
|
|
/* Restart the codec before setup */
|
|
davinci_vcif_stop(substream);
|
|
davinci_vcif_start(substream);
|
|
|
|
/* General line settings */
|
|
writel(DAVINCI_VC_CTRL_MASK, davinci_vc->base + DAVINCI_VC_CTRL);
|
|
|
|
writel(DAVINCI_VC_INT_MASK, davinci_vc->base + DAVINCI_VC_INTCLR);
|
|
|
|
writel(DAVINCI_VC_INT_MASK, davinci_vc->base + DAVINCI_VC_INTEN);
|
|
|
|
w = readl(davinci_vc->base + DAVINCI_VC_CTRL);
|
|
|
|
/* Determine xfer data type */
|
|
switch (params_format(params)) {
|
|
case SNDRV_PCM_FORMAT_U8:
|
|
MOD_REG_BIT(w, DAVINCI_VC_CTRL_RD_BITS_8 |
|
|
DAVINCI_VC_CTRL_RD_UNSIGNED |
|
|
DAVINCI_VC_CTRL_WD_BITS_8 |
|
|
DAVINCI_VC_CTRL_WD_UNSIGNED, 1);
|
|
break;
|
|
case SNDRV_PCM_FORMAT_S8:
|
|
MOD_REG_BIT(w, DAVINCI_VC_CTRL_RD_BITS_8 |
|
|
DAVINCI_VC_CTRL_WD_BITS_8, 1);
|
|
|
|
MOD_REG_BIT(w, DAVINCI_VC_CTRL_RD_UNSIGNED |
|
|
DAVINCI_VC_CTRL_WD_UNSIGNED, 0);
|
|
break;
|
|
case SNDRV_PCM_FORMAT_S16_LE:
|
|
MOD_REG_BIT(w, DAVINCI_VC_CTRL_RD_BITS_8 |
|
|
DAVINCI_VC_CTRL_RD_UNSIGNED |
|
|
DAVINCI_VC_CTRL_WD_BITS_8 |
|
|
DAVINCI_VC_CTRL_WD_UNSIGNED, 0);
|
|
break;
|
|
default:
|
|
printk(KERN_WARNING "davinci-vcif: unsupported PCM format");
|
|
return -EINVAL;
|
|
}
|
|
|
|
writel(w, davinci_vc->base + DAVINCI_VC_CTRL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int davinci_vcif_trigger(struct snd_pcm_substream *substream, int cmd,
|
|
struct snd_soc_dai *dai)
|
|
{
|
|
int ret = 0;
|
|
|
|
switch (cmd) {
|
|
case SNDRV_PCM_TRIGGER_START:
|
|
case SNDRV_PCM_TRIGGER_RESUME:
|
|
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
|
davinci_vcif_start(substream);
|
|
break;
|
|
case SNDRV_PCM_TRIGGER_STOP:
|
|
case SNDRV_PCM_TRIGGER_SUSPEND:
|
|
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
|
davinci_vcif_stop(substream);
|
|
break;
|
|
default:
|
|
ret = -EINVAL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
#define DAVINCI_VCIF_RATES SNDRV_PCM_RATE_8000_48000
|
|
|
|
static const struct snd_soc_dai_ops davinci_vcif_dai_ops = {
|
|
.trigger = davinci_vcif_trigger,
|
|
.hw_params = davinci_vcif_hw_params,
|
|
};
|
|
|
|
static int davinci_vcif_dai_probe(struct snd_soc_dai *dai)
|
|
{
|
|
struct davinci_vcif_dev *dev = snd_soc_dai_get_drvdata(dai);
|
|
|
|
dai->playback_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK];
|
|
dai->capture_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_CAPTURE];
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct snd_soc_dai_driver davinci_vcif_dai = {
|
|
.probe = davinci_vcif_dai_probe,
|
|
.playback = {
|
|
.channels_min = 1,
|
|
.channels_max = 2,
|
|
.rates = DAVINCI_VCIF_RATES,
|
|
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
|
|
.capture = {
|
|
.channels_min = 1,
|
|
.channels_max = 2,
|
|
.rates = DAVINCI_VCIF_RATES,
|
|
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
|
|
.ops = &davinci_vcif_dai_ops,
|
|
|
|
};
|
|
|
|
static const struct snd_soc_component_driver davinci_vcif_component = {
|
|
.name = "davinci-vcif",
|
|
};
|
|
|
|
static int davinci_vcif_probe(struct platform_device *pdev)
|
|
{
|
|
struct davinci_vc *davinci_vc = pdev->dev.platform_data;
|
|
struct davinci_vcif_dev *davinci_vcif_dev;
|
|
int ret;
|
|
|
|
davinci_vcif_dev = devm_kzalloc(&pdev->dev,
|
|
sizeof(struct davinci_vcif_dev),
|
|
GFP_KERNEL);
|
|
if (!davinci_vcif_dev)
|
|
return -ENOMEM;
|
|
|
|
/* DMA tx params */
|
|
davinci_vcif_dev->davinci_vc = davinci_vc;
|
|
davinci_vcif_dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].filter_data =
|
|
&davinci_vc->davinci_vcif.dma_tx_channel;
|
|
davinci_vcif_dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr =
|
|
davinci_vc->davinci_vcif.dma_tx_addr;
|
|
|
|
/* DMA rx params */
|
|
davinci_vcif_dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].filter_data =
|
|
&davinci_vc->davinci_vcif.dma_rx_channel;
|
|
davinci_vcif_dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr =
|
|
davinci_vc->davinci_vcif.dma_rx_addr;
|
|
|
|
dev_set_drvdata(&pdev->dev, davinci_vcif_dev);
|
|
|
|
ret = devm_snd_soc_register_component(&pdev->dev,
|
|
&davinci_vcif_component,
|
|
&davinci_vcif_dai, 1);
|
|
if (ret != 0) {
|
|
dev_err(&pdev->dev, "could not register dai\n");
|
|
return ret;
|
|
}
|
|
|
|
ret = edma_pcm_platform_register(&pdev->dev);
|
|
if (ret) {
|
|
dev_err(&pdev->dev, "register PCM failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct platform_driver davinci_vcif_driver = {
|
|
.probe = davinci_vcif_probe,
|
|
.driver = {
|
|
.name = "davinci-vcif",
|
|
},
|
|
};
|
|
|
|
module_platform_driver(davinci_vcif_driver);
|
|
|
|
MODULE_AUTHOR("Miguel Aguilar");
|
|
MODULE_DESCRIPTION("Texas Instruments DaVinci ASoC Voice Codec Interface");
|
|
MODULE_LICENSE("GPL");
|