mirror of https://gitee.com/openkylin/linux.git
ASoC: Blackfin: push down SPORT settings from global variables
Now that we have multi-component support, take the time to unify the SPORT implementations a bit and make the setup dynamic. This kills off the global sport_handle which was shared across all the Blackfin machine drivers. The pin management aspect is off loaded to platform resources, and now multiple SPORTs can be instantiated simultaneously. Signed-off-by: Barry Song <barry.song@analog.com> Signed-off-by: Scott Jiang <scott.jiang@analog.com> Signed-off-by: Mike Frysinger <vapier@gentoo.org> Acked-by: Liam Girdwood <lrg@ti.com> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
This commit is contained in:
parent
bfe4ee0a93
commit
2c66cb99d1
|
@ -243,6 +243,9 @@ static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream)
|
|||
|
||||
static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
int ret;
|
||||
|
||||
|
@ -314,6 +317,9 @@ static struct snd_pcm_ops bf5xx_pcm_ac97_ops = {
|
|||
|
||||
static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = pcm->private_data;
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
struct snd_pcm_substream *substream = pcm->streams[stream].substream;
|
||||
struct snd_dma_buffer *buf = &substream->dma_buffer;
|
||||
size_t size = bf5xx_pcm_hardware.buffer_bytes_max
|
||||
|
@ -377,6 +383,9 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
|
|||
struct snd_dma_buffer *buf;
|
||||
int stream;
|
||||
#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
|
||||
struct snd_soc_pcm_runtime *rtd = pcm->private_data;
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
size_t size = bf5xx_pcm_hardware.buffer_bytes_max *
|
||||
sizeof(struct ac97_frame) / 4;
|
||||
#endif
|
||||
|
@ -405,8 +414,6 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
|
|||
}
|
||||
#endif
|
||||
}
|
||||
if (sport_handle)
|
||||
sport_done(sport_handle);
|
||||
}
|
||||
|
||||
static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32);
|
||||
|
|
|
@ -41,48 +41,7 @@
|
|||
* anomaly does not affect blackfin sound drivers.
|
||||
*/
|
||||
|
||||
static int *cmd_count;
|
||||
static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM;
|
||||
|
||||
#define SPORT_REQ(x) \
|
||||
[x] = {P_SPORT##x##_TFS, P_SPORT##x##_DTPRI, P_SPORT##x##_TSCLK, \
|
||||
P_SPORT##x##_RFS, P_SPORT##x##_DRPRI, P_SPORT##x##_RSCLK, 0}
|
||||
static u16 sport_req[][7] = {
|
||||
#ifdef SPORT0_TCR1
|
||||
SPORT_REQ(0),
|
||||
#endif
|
||||
#ifdef SPORT1_TCR1
|
||||
SPORT_REQ(1),
|
||||
#endif
|
||||
#ifdef SPORT2_TCR1
|
||||
SPORT_REQ(2),
|
||||
#endif
|
||||
#ifdef SPORT3_TCR1
|
||||
SPORT_REQ(3),
|
||||
#endif
|
||||
};
|
||||
|
||||
#define SPORT_PARAMS(x) \
|
||||
[x] = { \
|
||||
.dma_rx_chan = CH_SPORT##x##_RX, \
|
||||
.dma_tx_chan = CH_SPORT##x##_TX, \
|
||||
.err_irq = IRQ_SPORT##x##_ERROR, \
|
||||
.regs = (struct sport_register *)SPORT##x##_TCR1, \
|
||||
}
|
||||
static struct sport_param sport_params[4] = {
|
||||
#ifdef SPORT0_TCR1
|
||||
SPORT_PARAMS(0),
|
||||
#endif
|
||||
#ifdef SPORT1_TCR1
|
||||
SPORT_PARAMS(1),
|
||||
#endif
|
||||
#ifdef SPORT2_TCR1
|
||||
SPORT_PARAMS(2),
|
||||
#endif
|
||||
#ifdef SPORT3_TCR1
|
||||
SPORT_PARAMS(3),
|
||||
#endif
|
||||
};
|
||||
static struct sport_device *ac97_sport_handle;
|
||||
|
||||
void bf5xx_pcm_to_ac97(struct ac97_frame *dst, const __u16 *src,
|
||||
size_t count, unsigned int chan_mask)
|
||||
|
@ -140,7 +99,8 @@ static unsigned int sport_tx_curr_frag(struct sport_device *sport)
|
|||
|
||||
static void enqueue_cmd(struct snd_ac97 *ac97, __u16 addr, __u16 data)
|
||||
{
|
||||
struct sport_device *sport = sport_handle;
|
||||
struct sport_device *sport = ac97_sport_handle;
|
||||
int *cmd_count = sport->private_data;
|
||||
int nextfrag = sport_tx_curr_frag(sport);
|
||||
struct ac97_frame *nextwrite;
|
||||
|
||||
|
@ -161,6 +121,7 @@ static void enqueue_cmd(struct snd_ac97 *ac97, __u16 addr, __u16 data)
|
|||
static unsigned short bf5xx_ac97_read(struct snd_ac97 *ac97,
|
||||
unsigned short reg)
|
||||
{
|
||||
struct sport_device *sport_handle = ac97_sport_handle;
|
||||
struct ac97_frame out_frame[2], in_frame[2];
|
||||
|
||||
pr_debug("%s enter 0x%x\n", __func__, reg);
|
||||
|
@ -185,6 +146,8 @@ static unsigned short bf5xx_ac97_read(struct snd_ac97 *ac97,
|
|||
void bf5xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
|
||||
unsigned short val)
|
||||
{
|
||||
struct sport_device *sport_handle = ac97_sport_handle;
|
||||
|
||||
pr_debug("%s enter 0x%x:0x%04x\n", __func__, reg, val);
|
||||
|
||||
if (sport_handle->tx_run) {
|
||||
|
@ -203,28 +166,19 @@ void bf5xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
|
|||
|
||||
static void bf5xx_ac97_warm_reset(struct snd_ac97 *ac97)
|
||||
{
|
||||
#if defined(CONFIG_BF54x) || defined(CONFIG_BF561) || \
|
||||
(defined(BF537_FAMILY) && (CONFIG_SND_BF5XX_SPORT_NUM == 1))
|
||||
|
||||
#define CONCAT(a, b, c) a ## b ## c
|
||||
#define BFIN_SPORT_RFS(x) CONCAT(P_SPORT, x, _RFS)
|
||||
|
||||
u16 per = BFIN_SPORT_RFS(CONFIG_SND_BF5XX_SPORT_NUM);
|
||||
u16 gpio = P_IDENT(BFIN_SPORT_RFS(CONFIG_SND_BF5XX_SPORT_NUM));
|
||||
struct sport_device *sport_handle = ac97_sport_handle;
|
||||
u16 gpio = P_IDENT(sport_handle->pin_req[3]);
|
||||
|
||||
pr_debug("%s enter\n", __func__);
|
||||
|
||||
peripheral_free(per);
|
||||
peripheral_free_list(sport_handle->pin_req);
|
||||
gpio_request(gpio, "bf5xx-ac97");
|
||||
gpio_direction_output(gpio, 1);
|
||||
udelay(2);
|
||||
gpio_set_value(gpio, 0);
|
||||
udelay(1);
|
||||
gpio_free(gpio);
|
||||
peripheral_request(per, "soc-audio");
|
||||
#else
|
||||
pr_info("%s: Not implemented\n", __func__);
|
||||
#endif
|
||||
peripheral_request_list(sport_handle->pin_req, "soc-audio");
|
||||
}
|
||||
|
||||
static void bf5xx_ac97_cold_reset(struct snd_ac97 *ac97)
|
||||
|
@ -306,18 +260,32 @@ static int bf5xx_ac97_resume(struct snd_soc_dai *dai)
|
|||
#define bf5xx_ac97_resume NULL
|
||||
#endif
|
||||
|
||||
static int bf5xx_ac97_probe(struct snd_soc_dai *dai)
|
||||
{
|
||||
int ret = 0;
|
||||
cmd_count = (int *)get_zeroed_page(GFP_KERNEL);
|
||||
if (cmd_count == NULL)
|
||||
return -ENOMEM;
|
||||
static struct snd_soc_dai_driver bfin_ac97_dai = {
|
||||
.ac97_control = 1,
|
||||
.suspend = bf5xx_ac97_suspend,
|
||||
.resume = bf5xx_ac97_resume,
|
||||
.playback = {
|
||||
.stream_name = "AC97 Playback",
|
||||
.channels_min = 2,
|
||||
#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT)
|
||||
.channels_max = 6,
|
||||
#else
|
||||
.channels_max = 2,
|
||||
#endif
|
||||
.rates = SNDRV_PCM_RATE_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE, },
|
||||
.capture = {
|
||||
.stream_name = "AC97 Capture",
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = SNDRV_PCM_RATE_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE, },
|
||||
};
|
||||
|
||||
if (peripheral_request_list(sport_req[sport_num], "soc-audio")) {
|
||||
pr_err("Requesting Peripherals failed\n");
|
||||
ret = -EFAULT;
|
||||
goto peripheral_err;
|
||||
}
|
||||
static int __devinit asoc_bfin_ac97_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct sport_device *sport_handle;
|
||||
int ret;
|
||||
|
||||
#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
|
||||
/* Request PB3 as reset pin */
|
||||
|
@ -329,12 +297,14 @@ static int bf5xx_ac97_probe(struct snd_soc_dai *dai)
|
|||
}
|
||||
gpio_direction_output(CONFIG_SND_BF5XX_RESET_GPIO_NUM, 1);
|
||||
#endif
|
||||
sport_handle = sport_init(&sport_params[sport_num], 2, \
|
||||
sizeof(struct ac97_frame), NULL);
|
||||
|
||||
sport_handle = sport_init(pdev, 2, sizeof(struct ac97_frame),
|
||||
PAGE_SIZE);
|
||||
if (!sport_handle) {
|
||||
ret = -ENODEV;
|
||||
goto sport_err;
|
||||
}
|
||||
|
||||
/*SPORT works in TDM mode to simulate AC97 transfers*/
|
||||
#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT)
|
||||
ret = sport_set_multichannel(sport_handle, 16, 0x3FF, 1);
|
||||
|
@ -361,67 +331,37 @@ static int bf5xx_ac97_probe(struct snd_soc_dai *dai)
|
|||
goto sport_config_err;
|
||||
}
|
||||
|
||||
ret = snd_soc_register_dai(&pdev->dev, &bfin_ac97_dai);
|
||||
if (ret) {
|
||||
pr_err("Failed to register DAI: %d\n", ret);
|
||||
goto sport_config_err;
|
||||
}
|
||||
|
||||
ac97_sport_handle = sport_handle;
|
||||
|
||||
return 0;
|
||||
|
||||
sport_config_err:
|
||||
kfree(sport_handle);
|
||||
sport_done(sport_handle);
|
||||
sport_err:
|
||||
#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
|
||||
gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
|
||||
gpio_err:
|
||||
#endif
|
||||
peripheral_free_list(sport_req[sport_num]);
|
||||
peripheral_err:
|
||||
free_page((unsigned long)cmd_count);
|
||||
cmd_count = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bf5xx_ac97_remove(struct snd_soc_dai *dai)
|
||||
static int __devexit asoc_bfin_ac97_remove(struct platform_device *pdev)
|
||||
{
|
||||
free_page((unsigned long)cmd_count);
|
||||
cmd_count = NULL;
|
||||
peripheral_free_list(sport_req[sport_num]);
|
||||
struct sport_device *sport_handle = platform_get_drvdata(pdev);
|
||||
|
||||
snd_soc_unregister_dai(&pdev->dev);
|
||||
sport_done(sport_handle);
|
||||
#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
|
||||
gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct snd_soc_dai_driver bfin_ac97_dai = {
|
||||
.ac97_control = 1,
|
||||
.probe = bf5xx_ac97_probe,
|
||||
.remove = bf5xx_ac97_remove,
|
||||
.suspend = bf5xx_ac97_suspend,
|
||||
.resume = bf5xx_ac97_resume,
|
||||
.playback = {
|
||||
.stream_name = "AC97 Playback",
|
||||
.channels_min = 2,
|
||||
#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT)
|
||||
.channels_max = 6,
|
||||
#else
|
||||
.channels_max = 2,
|
||||
#endif
|
||||
.rates = SNDRV_PCM_RATE_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE, },
|
||||
.capture = {
|
||||
.stream_name = "AC97 Capture",
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.rates = SNDRV_PCM_RATE_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE, },
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(bfin_ac97_dai);
|
||||
|
||||
static __devinit int asoc_bfin_ac97_probe(struct platform_device *pdev)
|
||||
{
|
||||
return snd_soc_register_dai(&pdev->dev, &bfin_ac97_dai);
|
||||
}
|
||||
|
||||
static int __devexit asoc_bfin_ac97_remove(struct platform_device *pdev)
|
||||
{
|
||||
snd_soc_unregister_dai(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -29,22 +29,12 @@
|
|||
#include <asm/portmux.h>
|
||||
|
||||
#include "../codecs/ad1836.h"
|
||||
#include "bf5xx-sport.h"
|
||||
|
||||
#include "bf5xx-tdm-pcm.h"
|
||||
#include "bf5xx-tdm.h"
|
||||
|
||||
static struct snd_soc_card bf5xx_ad1836;
|
||||
|
||||
static int bf5xx_ad1836_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
|
||||
snd_soc_dai_set_drvdata(cpu_dai, sport_handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bf5xx_ad1836_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
|
@ -75,23 +65,33 @@ static int bf5xx_ad1836_hw_params(struct snd_pcm_substream *substream,
|
|||
}
|
||||
|
||||
static struct snd_soc_ops bf5xx_ad1836_ops = {
|
||||
.startup = bf5xx_ad1836_startup,
|
||||
.hw_params = bf5xx_ad1836_hw_params,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_link bf5xx_ad1836_dai = {
|
||||
.name = "ad1836",
|
||||
.stream_name = "AD1836",
|
||||
.cpu_dai_name = "bfin-tdm",
|
||||
.codec_dai_name = "ad1836-hifi",
|
||||
.platform_name = "bfin-tdm-pcm-audio",
|
||||
.codec_name = "ad1836.0",
|
||||
.ops = &bf5xx_ad1836_ops,
|
||||
static struct snd_soc_dai_link bf5xx_ad1836_dai[] = {
|
||||
{
|
||||
.name = "ad1836",
|
||||
.stream_name = "AD1836",
|
||||
.cpu_dai_name = "bfin-tdm.0",
|
||||
.codec_dai_name = "ad1836-hifi",
|
||||
.platform_name = "bfin-tdm-pcm-audio",
|
||||
.codec_name = "ad1836.0",
|
||||
.ops = &bf5xx_ad1836_ops,
|
||||
},
|
||||
{
|
||||
.name = "ad1836",
|
||||
.stream_name = "AD1836",
|
||||
.cpu_dai_name = "bfin-tdm.1",
|
||||
.codec_dai_name = "ad1836-hifi",
|
||||
.platform_name = "bfin-tdm-pcm-audio",
|
||||
.codec_name = "ad1836.0",
|
||||
.ops = &bf5xx_ad1836_ops,
|
||||
},
|
||||
};
|
||||
|
||||
static struct snd_soc_card bf5xx_ad1836 = {
|
||||
.name = "bfin-ad1836",
|
||||
.dai_link = &bf5xx_ad1836_dai,
|
||||
.dai_link = &bf5xx_ad1836_dai[CONFIG_SND_BF5XX_SPORT_NUM],
|
||||
.num_links = 1,
|
||||
};
|
||||
|
||||
|
|
|
@ -38,22 +38,12 @@
|
|||
#include <asm/portmux.h>
|
||||
|
||||
#include "../codecs/ad193x.h"
|
||||
#include "bf5xx-sport.h"
|
||||
|
||||
#include "bf5xx-tdm-pcm.h"
|
||||
#include "bf5xx-tdm.h"
|
||||
|
||||
static struct snd_soc_card bf5xx_ad193x;
|
||||
|
||||
static int bf5xx_ad193x_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
|
||||
snd_soc_dai_set_drvdata(cpu_dai, sport_handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bf5xx_ad193x_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
|
@ -103,23 +93,33 @@ static int bf5xx_ad193x_hw_params(struct snd_pcm_substream *substream,
|
|||
}
|
||||
|
||||
static struct snd_soc_ops bf5xx_ad193x_ops = {
|
||||
.startup = bf5xx_ad193x_startup,
|
||||
.hw_params = bf5xx_ad193x_hw_params,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_link bf5xx_ad193x_dai = {
|
||||
.name = "ad193x",
|
||||
.stream_name = "AD193X",
|
||||
.cpu_dai_name = "bfin-tdm",
|
||||
.codec_dai_name ="ad193x-hifi",
|
||||
.platform_name = "bfin-tdm-pcm-audio",
|
||||
.codec_name = "ad193x.5",
|
||||
.ops = &bf5xx_ad193x_ops,
|
||||
static struct snd_soc_dai_link bf5xx_ad193x_dai[] = {
|
||||
{
|
||||
.name = "ad193x",
|
||||
.stream_name = "AD193X",
|
||||
.cpu_dai_name = "bfin-tdm.0",
|
||||
.codec_dai_name ="ad193x-hifi",
|
||||
.platform_name = "bfin-tdm-pcm-audio",
|
||||
.codec_name = "ad193x.5",
|
||||
.ops = &bf5xx_ad193x_ops,
|
||||
},
|
||||
{
|
||||
.name = "ad193x",
|
||||
.stream_name = "AD193X",
|
||||
.cpu_dai_name = "bfin-tdm.1",
|
||||
.codec_dai_name ="ad193x-hifi",
|
||||
.platform_name = "bfin-tdm-pcm-audio",
|
||||
.codec_name = "ad193x.5",
|
||||
.ops = &bf5xx_ad193x_ops,
|
||||
},
|
||||
};
|
||||
|
||||
static struct snd_soc_card bf5xx_ad193x = {
|
||||
.name = "bfin-ad193x",
|
||||
.dai_link = &bf5xx_ad193x_dai,
|
||||
.dai_link = &bf5xx_ad193x_dai[CONFIG_SND_BF5XX_SPORT_NUM],
|
||||
.num_links = 1,
|
||||
};
|
||||
|
||||
|
|
|
@ -47,39 +47,34 @@
|
|||
#include <asm/portmux.h>
|
||||
|
||||
#include "../codecs/ad1980.h"
|
||||
#include "bf5xx-sport.h"
|
||||
|
||||
#include "bf5xx-ac97-pcm.h"
|
||||
#include "bf5xx-ac97.h"
|
||||
|
||||
static struct snd_soc_card bf5xx_board;
|
||||
|
||||
static int bf5xx_board_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
|
||||
pr_debug("%s enter\n", __func__);
|
||||
snd_soc_dai_set_drvdata(cpu_dai, sport_handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_ops bf5xx_board_ops = {
|
||||
.startup = bf5xx_board_startup,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_link bf5xx_board_dai = {
|
||||
.name = "AC97",
|
||||
.stream_name = "AC97 HiFi",
|
||||
.cpu_dai_name = "bfin-ac97",
|
||||
.codec_dai_name = "ad1980-hifi",
|
||||
.platform_name = "bfin-ac97-pcm-audio",
|
||||
.codec_name = "ad1980",
|
||||
.ops = &bf5xx_board_ops,
|
||||
static struct snd_soc_dai_link bf5xx_board_dai[] = {
|
||||
{
|
||||
.name = "AC97",
|
||||
.stream_name = "AC97 HiFi",
|
||||
.cpu_dai_name = "bfin-ac97.0",
|
||||
.codec_dai_name = "ad1980-hifi",
|
||||
.platform_name = "bfin-ac97-pcm-audio",
|
||||
.codec_name = "ad1980",
|
||||
},
|
||||
{
|
||||
.name = "AC97",
|
||||
.stream_name = "AC97 HiFi",
|
||||
.cpu_dai_name = "bfin-ac97.1",
|
||||
.codec_dai_name = "ad1980-hifi",
|
||||
.platform_name = "bfin-ac97-pcm-audio",
|
||||
.codec_name = "ad1980",
|
||||
},
|
||||
};
|
||||
|
||||
static struct snd_soc_card bf5xx_board = {
|
||||
.name = "bfin-ad1980",
|
||||
.dai_link = &bf5xx_board_dai,
|
||||
.dai_link = &bf5xx_board_dai[CONFIG_SND_BF5XX_SPORT_NUM],
|
||||
.num_links = 1,
|
||||
};
|
||||
|
||||
|
|
|
@ -145,16 +145,6 @@ static int bf5xx_probe(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int bf5xx_ad73311_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
|
||||
pr_debug("%s enter\n", __func__);
|
||||
snd_soc_dai_set_drvdata(cpu_dai, sport_handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bf5xx_ad73311_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
|
@ -176,24 +166,34 @@ static int bf5xx_ad73311_hw_params(struct snd_pcm_substream *substream,
|
|||
|
||||
|
||||
static struct snd_soc_ops bf5xx_ad73311_ops = {
|
||||
.startup = bf5xx_ad73311_startup,
|
||||
.hw_params = bf5xx_ad73311_hw_params,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_link bf5xx_ad73311_dai = {
|
||||
.name = "ad73311",
|
||||
.stream_name = "AD73311",
|
||||
.cpu_dai_name = "bfin-i2s",
|
||||
.codec_dai_name = "ad73311-hifi",
|
||||
.platform_name = "bfin-i2s-pcm-audio",
|
||||
.codec_name = "ad73311",
|
||||
.ops = &bf5xx_ad73311_ops,
|
||||
static struct snd_soc_dai_link bf5xx_ad73311_dai[] = {
|
||||
{
|
||||
.name = "ad73311",
|
||||
.stream_name = "AD73311",
|
||||
.cpu_dai_name = "bfin-i2s.0",
|
||||
.codec_dai_name = "ad73311-hifi",
|
||||
.platform_name = "bfin-i2s-pcm-audio",
|
||||
.codec_name = "ad73311",
|
||||
.ops = &bf5xx_ad73311_ops,
|
||||
},
|
||||
{
|
||||
.name = "ad73311",
|
||||
.stream_name = "AD73311",
|
||||
.cpu_dai_name = "bfin-i2s.1",
|
||||
.codec_dai_name = "ad73311-hifi",
|
||||
.platform_name = "bfin-i2s-pcm-audio",
|
||||
.codec_name = "ad73311",
|
||||
.ops = &bf5xx_ad73311_ops,
|
||||
},
|
||||
};
|
||||
|
||||
static struct snd_soc_card bf5xx_ad73311 = {
|
||||
.name = "bfin-ad73311",
|
||||
.probe = bf5xx_probe,
|
||||
.dai_link = &bf5xx_ad73311_dai,
|
||||
.dai_link = &bf5xx_ad73311_dai[CONFIG_SND_BF5XX_SPORT_NUM],
|
||||
.num_links = 1,
|
||||
};
|
||||
|
||||
|
|
|
@ -148,10 +148,15 @@ static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream)
|
|||
|
||||
static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_dma_buffer *buf = &substream->dma_buffer;
|
||||
int ret;
|
||||
|
||||
pr_debug("%s enter\n", __func__);
|
||||
|
||||
snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware);
|
||||
|
||||
ret = snd_pcm_hw_constraint_integer(runtime, \
|
||||
|
@ -159,9 +164,14 @@ static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
|
|||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
if (sport_handle != NULL)
|
||||
if (sport_handle != NULL) {
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
sport_handle->tx_buf = buf->area;
|
||||
else
|
||||
sport_handle->rx_buf = buf->area;
|
||||
|
||||
runtime->private_data = sport_handle;
|
||||
else {
|
||||
} else {
|
||||
pr_err("sport_handle is NULL\n");
|
||||
return -1;
|
||||
}
|
||||
|
@ -214,11 +224,6 @@ static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
|
|||
pr_debug("%s, area:%p, size:0x%08lx\n", __func__,
|
||||
buf->area, buf->bytes);
|
||||
|
||||
if (stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
sport_handle->tx_buf = buf->area;
|
||||
else
|
||||
sport_handle->rx_buf = buf->area;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -239,8 +244,6 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
|
|||
dma_free_coherent(NULL, buf->bytes, buf->area, 0);
|
||||
buf->area = NULL;
|
||||
}
|
||||
if (sport_handle)
|
||||
sport_done(sport_handle);
|
||||
}
|
||||
|
||||
static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32);
|
||||
|
|
|
@ -51,59 +51,24 @@ struct bf5xx_i2s_port {
|
|||
int configured;
|
||||
};
|
||||
|
||||
static struct bf5xx_i2s_port bf5xx_i2s;
|
||||
static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM;
|
||||
|
||||
static struct sport_param sport_params[2] = {
|
||||
{
|
||||
.dma_rx_chan = CH_SPORT0_RX,
|
||||
.dma_tx_chan = CH_SPORT0_TX,
|
||||
.err_irq = IRQ_SPORT0_ERROR,
|
||||
.regs = (struct sport_register *)SPORT0_TCR1,
|
||||
},
|
||||
{
|
||||
.dma_rx_chan = CH_SPORT1_RX,
|
||||
.dma_tx_chan = CH_SPORT1_TX,
|
||||
.err_irq = IRQ_SPORT1_ERROR,
|
||||
.regs = (struct sport_register *)SPORT1_TCR1,
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Setting the TFS pin selector for SPORT 0 based on whether the selected
|
||||
* port id F or G. If the port is F then no conflict should exist for the
|
||||
* TFS. When Port G is selected and EMAC then there is a conflict between
|
||||
* the PHY interrupt line and TFS. Current settings prevent the conflict
|
||||
* by ignoring the TFS pin when Port G is selected. This allows both
|
||||
* codecs and EMAC using Port G concurrently.
|
||||
*/
|
||||
#ifdef CONFIG_BF527_SPORT0_PORTG
|
||||
#define LOCAL_SPORT0_TFS (0)
|
||||
#else
|
||||
#define LOCAL_SPORT0_TFS (P_SPORT0_TFS)
|
||||
#endif
|
||||
|
||||
static u16 sport_req[][7] = { {P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS,
|
||||
P_SPORT0_DRPRI, P_SPORT0_RSCLK, LOCAL_SPORT0_TFS, 0},
|
||||
{P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS, P_SPORT1_DRPRI,
|
||||
P_SPORT1_RSCLK, P_SPORT1_TFS, 0} };
|
||||
|
||||
static int bf5xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
|
||||
unsigned int fmt)
|
||||
{
|
||||
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
|
||||
int ret = 0;
|
||||
|
||||
/* interface format:support I2S,slave mode */
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
bf5xx_i2s.tcr1 |= TFSR | TCKFE;
|
||||
bf5xx_i2s.rcr1 |= RFSR | RCKFE;
|
||||
bf5xx_i2s.tcr2 |= TSFSE;
|
||||
bf5xx_i2s.rcr2 |= RSFSE;
|
||||
bf5xx_i2s->tcr1 |= TFSR | TCKFE;
|
||||
bf5xx_i2s->rcr1 |= RFSR | RCKFE;
|
||||
bf5xx_i2s->tcr2 |= TSFSE;
|
||||
bf5xx_i2s->rcr2 |= RSFSE;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_DSP_A:
|
||||
bf5xx_i2s.tcr1 |= TFSR;
|
||||
bf5xx_i2s.rcr1 |= RFSR;
|
||||
bf5xx_i2s->tcr1 |= TFSR;
|
||||
bf5xx_i2s->rcr1 |= RFSR;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_LEFT_J:
|
||||
ret = -EINVAL;
|
||||
|
@ -135,33 +100,35 @@ static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream,
|
|||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
|
||||
struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
|
||||
int ret = 0;
|
||||
|
||||
bf5xx_i2s.tcr2 &= ~0x1f;
|
||||
bf5xx_i2s.rcr2 &= ~0x1f;
|
||||
bf5xx_i2s->tcr2 &= ~0x1f;
|
||||
bf5xx_i2s->rcr2 &= ~0x1f;
|
||||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_S8:
|
||||
bf5xx_i2s->tcr2 |= 7;
|
||||
bf5xx_i2s->rcr2 |= 7;
|
||||
sport_handle->wdsize = 1;
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
bf5xx_i2s.tcr2 |= 15;
|
||||
bf5xx_i2s.rcr2 |= 15;
|
||||
bf5xx_i2s->tcr2 |= 15;
|
||||
bf5xx_i2s->rcr2 |= 15;
|
||||
sport_handle->wdsize = 2;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S24_LE:
|
||||
bf5xx_i2s.tcr2 |= 23;
|
||||
bf5xx_i2s.rcr2 |= 23;
|
||||
bf5xx_i2s->tcr2 |= 23;
|
||||
bf5xx_i2s->rcr2 |= 23;
|
||||
sport_handle->wdsize = 3;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S32_LE:
|
||||
bf5xx_i2s.tcr2 |= 31;
|
||||
bf5xx_i2s.rcr2 |= 31;
|
||||
bf5xx_i2s->tcr2 |= 31;
|
||||
bf5xx_i2s->rcr2 |= 31;
|
||||
sport_handle->wdsize = 4;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!bf5xx_i2s.configured) {
|
||||
if (!bf5xx_i2s->configured) {
|
||||
/*
|
||||
* TX and RX are not independent,they are enabled at the
|
||||
* same time, even if only one side is running. So, we
|
||||
|
@ -170,16 +137,16 @@ static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream,
|
|||
*
|
||||
* CPU DAI:slave mode.
|
||||
*/
|
||||
bf5xx_i2s.configured = 1;
|
||||
ret = sport_config_rx(sport_handle, bf5xx_i2s.rcr1,
|
||||
bf5xx_i2s.rcr2, 0, 0);
|
||||
bf5xx_i2s->configured = 1;
|
||||
ret = sport_config_rx(sport_handle, bf5xx_i2s->rcr1,
|
||||
bf5xx_i2s->rcr2, 0, 0);
|
||||
if (ret) {
|
||||
pr_err("SPORT is busy!\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
ret = sport_config_tx(sport_handle, bf5xx_i2s.tcr1,
|
||||
bf5xx_i2s.tcr2, 0, 0);
|
||||
ret = sport_config_tx(sport_handle, bf5xx_i2s->tcr1,
|
||||
bf5xx_i2s->tcr2, 0, 0);
|
||||
if (ret) {
|
||||
pr_err("SPORT is busy!\n");
|
||||
return -EBUSY;
|
||||
|
@ -192,41 +159,19 @@ static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream,
|
|||
static void bf5xx_i2s_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
|
||||
struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
|
||||
|
||||
pr_debug("%s enter\n", __func__);
|
||||
/* No active stream, SPORT is allowed to be configured again. */
|
||||
if (!dai->active)
|
||||
bf5xx_i2s.configured = 0;
|
||||
}
|
||||
|
||||
static int bf5xx_i2s_probe(struct snd_soc_dai *dai)
|
||||
{
|
||||
pr_debug("%s enter\n", __func__);
|
||||
if (peripheral_request_list(&sport_req[sport_num][0], "soc-audio")) {
|
||||
pr_err("Requesting Peripherals failed\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
/* request DMA for SPORT */
|
||||
sport_handle = sport_init(&sport_params[sport_num], 4, \
|
||||
2 * sizeof(u32), NULL);
|
||||
if (!sport_handle) {
|
||||
peripheral_free_list(&sport_req[sport_num][0]);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bf5xx_i2s_remove(struct snd_soc_dai *dai)
|
||||
{
|
||||
pr_debug("%s enter\n", __func__);
|
||||
peripheral_free_list(&sport_req[sport_num][0]);
|
||||
return 0;
|
||||
bf5xx_i2s->configured = 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int bf5xx_i2s_suspend(struct snd_soc_dai *dai)
|
||||
{
|
||||
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
pr_debug("%s : sport %d\n", __func__, dai->id);
|
||||
|
||||
|
@ -239,19 +184,21 @@ static int bf5xx_i2s_suspend(struct snd_soc_dai *dai)
|
|||
|
||||
static int bf5xx_i2s_resume(struct snd_soc_dai *dai)
|
||||
{
|
||||
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
|
||||
struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data;
|
||||
int ret;
|
||||
|
||||
pr_debug("%s : sport %d\n", __func__, dai->id);
|
||||
|
||||
ret = sport_config_rx(sport_handle, bf5xx_i2s.rcr1,
|
||||
bf5xx_i2s.rcr2, 0, 0);
|
||||
ret = sport_config_rx(sport_handle, bf5xx_i2s->rcr1,
|
||||
bf5xx_i2s->rcr2, 0, 0);
|
||||
if (ret) {
|
||||
pr_err("SPORT is busy!\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
ret = sport_config_tx(sport_handle, bf5xx_i2s.tcr1,
|
||||
bf5xx_i2s.tcr2, 0, 0);
|
||||
ret = sport_config_tx(sport_handle, bf5xx_i2s->tcr1,
|
||||
bf5xx_i2s->tcr2, 0, 0);
|
||||
if (ret) {
|
||||
pr_err("SPORT is busy!\n");
|
||||
return -EBUSY;
|
||||
|
@ -283,8 +230,6 @@ static struct snd_soc_dai_ops bf5xx_i2s_dai_ops = {
|
|||
};
|
||||
|
||||
static struct snd_soc_dai_driver bf5xx_i2s_dai = {
|
||||
.probe = bf5xx_i2s_probe,
|
||||
.remove = bf5xx_i2s_remove,
|
||||
.suspend = bf5xx_i2s_suspend,
|
||||
.resume = bf5xx_i2s_resume,
|
||||
.playback = {
|
||||
|
@ -300,21 +245,43 @@ static struct snd_soc_dai_driver bf5xx_i2s_dai = {
|
|||
.ops = &bf5xx_i2s_dai_ops,
|
||||
};
|
||||
|
||||
static int bfin_i2s_drv_probe(struct platform_device *pdev)
|
||||
static int __devinit bf5xx_i2s_probe(struct platform_device *pdev)
|
||||
{
|
||||
return snd_soc_register_dai(&pdev->dev, &bf5xx_i2s_dai);
|
||||
struct sport_device *sport_handle;
|
||||
int ret;
|
||||
|
||||
/* configure SPORT for I2S */
|
||||
sport_handle = sport_init(pdev, 4, 2 * sizeof(u32),
|
||||
sizeof(struct bf5xx_i2s_port));
|
||||
if (!sport_handle)
|
||||
return -ENODEV;
|
||||
|
||||
/* register with the ASoC layers */
|
||||
ret = snd_soc_register_dai(&pdev->dev, &bf5xx_i2s_dai);
|
||||
if (ret) {
|
||||
pr_err("Failed to register DAI: %d\n", ret);
|
||||
sport_done(sport_handle);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devexit bfin_i2s_drv_remove(struct platform_device *pdev)
|
||||
static int __devexit bf5xx_i2s_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct sport_device *sport_handle = platform_get_drvdata(pdev);
|
||||
|
||||
pr_debug("%s enter\n", __func__);
|
||||
|
||||
snd_soc_unregister_dai(&pdev->dev);
|
||||
sport_done(sport_handle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver bfin_i2s_driver = {
|
||||
.probe = bfin_i2s_drv_probe,
|
||||
.remove = __devexit_p(bfin_i2s_drv_remove),
|
||||
|
||||
.probe = bf5xx_i2s_probe,
|
||||
.remove = __devexit_p(bf5xx_i2s_remove),
|
||||
.driver = {
|
||||
.name = "bfin-i2s",
|
||||
.owner = THIS_MODULE,
|
||||
|
|
|
@ -42,8 +42,6 @@
|
|||
/* delay between frame sync pulse and first data bit in multichannel mode */
|
||||
#define FRAME_DELAY (1<<12)
|
||||
|
||||
struct sport_device *sport_handle;
|
||||
EXPORT_SYMBOL(sport_handle);
|
||||
/* note: multichannel is in units of 8 channels,
|
||||
* tdm_count is # channels NOT / 8 ! */
|
||||
int sport_set_multichannel(struct sport_device *sport,
|
||||
|
@ -798,86 +796,164 @@ int sport_set_err_callback(struct sport_device *sport,
|
|||
}
|
||||
EXPORT_SYMBOL(sport_set_err_callback);
|
||||
|
||||
struct sport_device *sport_init(struct sport_param *param, unsigned wdsize,
|
||||
unsigned dummy_count, void *private_data)
|
||||
static int sport_config_pdev(struct platform_device *pdev, struct sport_param *param)
|
||||
{
|
||||
int ret;
|
||||
/* Extract settings from platform data */
|
||||
struct device *dev = &pdev->dev;
|
||||
struct bfin_snd_platform_data *pdata = dev->platform_data;
|
||||
struct resource *res;
|
||||
|
||||
param->num = pdev->id;
|
||||
|
||||
if (!pdata) {
|
||||
dev_err(dev, "no platform_data\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
param->pin_req = pdata->pin_req;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(dev, "no MEM resource\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
param->regs = (struct sport_register *)res->start;
|
||||
|
||||
/* first RX, then TX */
|
||||
res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
|
||||
if (!res) {
|
||||
dev_err(dev, "no rx DMA resource\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
param->dma_rx_chan = res->start;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
|
||||
if (!res) {
|
||||
dev_err(dev, "no tx DMA resource\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
param->dma_tx_chan = res->start;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
if (!res) {
|
||||
dev_err(dev, "no irq resource\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
param->err_irq = res->start;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct sport_device *sport_init(struct platform_device *pdev,
|
||||
unsigned int wdsize, unsigned int dummy_count, size_t priv_size)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct sport_param param;
|
||||
struct sport_device *sport;
|
||||
pr_debug("%s enter\n", __func__);
|
||||
BUG_ON(param == NULL);
|
||||
BUG_ON(wdsize == 0 || dummy_count == 0);
|
||||
sport = kmalloc(sizeof(struct sport_device), GFP_KERNEL);
|
||||
if (!sport) {
|
||||
pr_err("Failed to allocate for sport device\n");
|
||||
int ret;
|
||||
|
||||
dev_dbg(dev, "%s enter\n", __func__);
|
||||
|
||||
param.wdsize = wdsize;
|
||||
param.dummy_count = dummy_count;
|
||||
BUG_ON(param.wdsize == 0 || param.dummy_count == 0);
|
||||
|
||||
ret = sport_config_pdev(pdev, ¶m);
|
||||
if (ret)
|
||||
return NULL;
|
||||
|
||||
if (peripheral_request_list(param.pin_req, "soc-audio")) {
|
||||
dev_err(dev, "requesting Peripherals failed\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(sport, 0, sizeof(struct sport_device));
|
||||
sport->dma_rx_chan = param->dma_rx_chan;
|
||||
sport->dma_tx_chan = param->dma_tx_chan;
|
||||
sport->err_irq = param->err_irq;
|
||||
sport->regs = param->regs;
|
||||
sport->private_data = private_data;
|
||||
sport = kzalloc(sizeof(*sport), GFP_KERNEL);
|
||||
if (!sport) {
|
||||
dev_err(dev, "failed to allocate for sport device\n");
|
||||
goto __init_err0;
|
||||
}
|
||||
|
||||
sport->num = param.num;
|
||||
sport->dma_rx_chan = param.dma_rx_chan;
|
||||
sport->dma_tx_chan = param.dma_tx_chan;
|
||||
sport->err_irq = param.err_irq;
|
||||
sport->regs = param.regs;
|
||||
sport->pin_req = param.pin_req;
|
||||
|
||||
if (request_dma(sport->dma_rx_chan, "SPORT RX Data") == -EBUSY) {
|
||||
pr_err("Failed to request RX dma %d\n", \
|
||||
sport->dma_rx_chan);
|
||||
dev_err(dev, "failed to request RX dma %d\n", sport->dma_rx_chan);
|
||||
goto __init_err1;
|
||||
}
|
||||
if (set_dma_callback(sport->dma_rx_chan, rx_handler, sport) != 0) {
|
||||
pr_err("Failed to request RX irq %d\n", \
|
||||
sport->dma_rx_chan);
|
||||
dev_err(dev, "failed to request RX irq %d\n", sport->dma_rx_chan);
|
||||
goto __init_err2;
|
||||
}
|
||||
|
||||
if (request_dma(sport->dma_tx_chan, "SPORT TX Data") == -EBUSY) {
|
||||
pr_err("Failed to request TX dma %d\n", \
|
||||
sport->dma_tx_chan);
|
||||
dev_err(dev, "failed to request TX dma %d\n", sport->dma_tx_chan);
|
||||
goto __init_err2;
|
||||
}
|
||||
|
||||
if (set_dma_callback(sport->dma_tx_chan, tx_handler, sport) != 0) {
|
||||
pr_err("Failed to request TX irq %d\n", \
|
||||
sport->dma_tx_chan);
|
||||
dev_err(dev, "failed to request TX irq %d\n", sport->dma_tx_chan);
|
||||
goto __init_err3;
|
||||
}
|
||||
|
||||
if (request_irq(sport->err_irq, err_handler, IRQF_SHARED, "SPORT err",
|
||||
sport) < 0) {
|
||||
pr_err("Failed to request err irq:%d\n", \
|
||||
sport->err_irq);
|
||||
dev_err(dev, "failed to request err irq %d\n", sport->err_irq);
|
||||
goto __init_err3;
|
||||
}
|
||||
|
||||
pr_err("dma rx:%d tx:%d, err irq:%d, regs:%p\n",
|
||||
dev_info(dev, "dma rx:%d tx:%d, err irq:%d, regs:%p\n",
|
||||
sport->dma_rx_chan, sport->dma_tx_chan,
|
||||
sport->err_irq, sport->regs);
|
||||
|
||||
sport->wdsize = wdsize;
|
||||
sport->dummy_count = dummy_count;
|
||||
sport->wdsize = param.wdsize;
|
||||
sport->dummy_count = param.dummy_count;
|
||||
|
||||
sport->private_data = kzalloc(priv_size, GFP_KERNEL);
|
||||
if (!sport->private_data) {
|
||||
dev_err(dev, "could not alloc priv data %zu bytes\n", priv_size);
|
||||
goto __init_err4;
|
||||
}
|
||||
|
||||
if (L1_DATA_A_LENGTH)
|
||||
sport->dummy_buf = l1_data_sram_zalloc(dummy_count * 2);
|
||||
sport->dummy_buf = l1_data_sram_zalloc(param.dummy_count * 2);
|
||||
else
|
||||
sport->dummy_buf = kzalloc(dummy_count * 2, GFP_KERNEL);
|
||||
sport->dummy_buf = kzalloc(param.dummy_count * 2, GFP_KERNEL);
|
||||
if (sport->dummy_buf == NULL) {
|
||||
pr_err("Failed to allocate dummy buffer\n");
|
||||
goto __error;
|
||||
dev_err(dev, "failed to allocate dummy buffer\n");
|
||||
goto __error1;
|
||||
}
|
||||
|
||||
ret = sport_config_rx_dummy(sport);
|
||||
if (ret) {
|
||||
pr_err("Failed to config rx dummy ring\n");
|
||||
goto __error;
|
||||
dev_err(dev, "failed to config rx dummy ring\n");
|
||||
goto __error2;
|
||||
}
|
||||
ret = sport_config_tx_dummy(sport);
|
||||
if (ret) {
|
||||
pr_err("Failed to config tx dummy ring\n");
|
||||
goto __error;
|
||||
dev_err(dev, "failed to config tx dummy ring\n");
|
||||
goto __error3;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, sport);
|
||||
|
||||
return sport;
|
||||
__error:
|
||||
__error3:
|
||||
if (L1_DATA_A_LENGTH)
|
||||
l1_data_sram_free(sport->dummy_rx_desc);
|
||||
else
|
||||
dma_free_coherent(NULL, 2*sizeof(struct dmasg),
|
||||
sport->dummy_rx_desc, 0);
|
||||
__error2:
|
||||
if (L1_DATA_A_LENGTH)
|
||||
l1_data_sram_free(sport->dummy_buf);
|
||||
else
|
||||
kfree(sport->dummy_buf);
|
||||
__error1:
|
||||
kfree(sport->private_data);
|
||||
__init_err4:
|
||||
free_irq(sport->err_irq, sport);
|
||||
__init_err3:
|
||||
free_dma(sport->dma_tx_chan);
|
||||
|
@ -885,6 +961,8 @@ struct sport_device *sport_init(struct sport_param *param, unsigned wdsize,
|
|||
free_dma(sport->dma_rx_chan);
|
||||
__init_err1:
|
||||
kfree(sport);
|
||||
__init_err0:
|
||||
peripheral_free_list(param.pin_req);
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(sport_init);
|
||||
|
@ -917,8 +995,9 @@ void sport_done(struct sport_device *sport)
|
|||
free_dma(sport->dma_tx_chan);
|
||||
free_irq(sport->err_irq, sport);
|
||||
|
||||
kfree(sport->private_data);
|
||||
peripheral_free_list(sport->pin_req);
|
||||
kfree(sport);
|
||||
sport = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(sport_done);
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* File: bf5xx_ac97_sport.h
|
||||
* File: bf5xx_sport.h
|
||||
* Based on:
|
||||
* Author: Roy Huang <roy.huang@analog.com>
|
||||
*
|
||||
|
@ -33,15 +33,18 @@
|
|||
#include <linux/types.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <asm/dma.h>
|
||||
#include <asm/bfin_sport.h>
|
||||
|
||||
#define DESC_ELEMENT_COUNT 9
|
||||
|
||||
struct sport_device {
|
||||
int num;
|
||||
int dma_rx_chan;
|
||||
int dma_tx_chan;
|
||||
int err_irq;
|
||||
const unsigned short *pin_req;
|
||||
struct sport_register *regs;
|
||||
|
||||
unsigned char *rx_buf;
|
||||
|
@ -103,17 +106,20 @@ struct sport_device {
|
|||
void *private_data;
|
||||
};
|
||||
|
||||
extern struct sport_device *sport_handle;
|
||||
|
||||
struct sport_param {
|
||||
int num;
|
||||
int dma_rx_chan;
|
||||
int dma_tx_chan;
|
||||
int err_irq;
|
||||
const unsigned short *pin_req;
|
||||
struct sport_register *regs;
|
||||
unsigned int wdsize;
|
||||
unsigned int dummy_count;
|
||||
void *private_data;
|
||||
};
|
||||
|
||||
struct sport_device *sport_init(struct sport_param *param, unsigned wdsize,
|
||||
unsigned dummy_count, void *private_data);
|
||||
struct sport_device *sport_init(struct platform_device *pdev,
|
||||
unsigned int wdsize, unsigned int dummy_count, size_t priv_size);
|
||||
|
||||
void sport_done(struct sport_device *sport);
|
||||
|
||||
|
|
|
@ -44,16 +44,6 @@
|
|||
|
||||
static struct snd_soc_card bf5xx_ssm2602;
|
||||
|
||||
static int bf5xx_ssm2602_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
|
||||
pr_debug("%s enter\n", __func__);
|
||||
snd_soc_dai_set_drvdata(cpu_dai, sport_handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bf5xx_ssm2602_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
|
@ -109,23 +99,33 @@ static int bf5xx_ssm2602_hw_params(struct snd_pcm_substream *substream,
|
|||
}
|
||||
|
||||
static struct snd_soc_ops bf5xx_ssm2602_ops = {
|
||||
.startup = bf5xx_ssm2602_startup,
|
||||
.hw_params = bf5xx_ssm2602_hw_params,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_link bf5xx_ssm2602_dai = {
|
||||
.name = "ssm2602",
|
||||
.stream_name = "SSM2602",
|
||||
.cpu_dai_name = "bfin-i2s",
|
||||
.codec_dai_name = "ssm2602-hifi",
|
||||
.platform_name = "bfin-i2s-pcm-audio",
|
||||
.codec_name = "ssm2602.0-001b",
|
||||
.ops = &bf5xx_ssm2602_ops,
|
||||
static struct snd_soc_dai_link bf5xx_ssm2602_dai[] = {
|
||||
{
|
||||
.name = "ssm2602",
|
||||
.stream_name = "SSM2602",
|
||||
.cpu_dai_name = "bfin-i2s.0",
|
||||
.codec_dai_name = "ssm2602-hifi",
|
||||
.platform_name = "bfin-i2s-pcm-audio",
|
||||
.codec_name = "ssm2602.0-001b",
|
||||
.ops = &bf5xx_ssm2602_ops,
|
||||
},
|
||||
{
|
||||
.name = "ssm2602",
|
||||
.stream_name = "SSM2602",
|
||||
.cpu_dai_name = "bfin-i2s.1",
|
||||
.codec_dai_name = "ssm2602-hifi",
|
||||
.platform_name = "bfin-i2s-pcm-audio",
|
||||
.codec_name = "ssm2602.0-001b",
|
||||
.ops = &bf5xx_ssm2602_ops,
|
||||
},
|
||||
};
|
||||
|
||||
static struct snd_soc_card bf5xx_ssm2602 = {
|
||||
.name = "bfin-ssm2602",
|
||||
.dai_link = &bf5xx_ssm2602_dai,
|
||||
.dai_link = &bf5xx_ssm2602_dai[CONFIG_SND_BF5XX_SPORT_NUM],
|
||||
.num_links = 1,
|
||||
};
|
||||
|
||||
|
|
|
@ -154,7 +154,12 @@ static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream)
|
|||
|
||||
static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai);
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_dma_buffer *buf = &substream->dma_buffer;
|
||||
|
||||
int ret = 0;
|
||||
|
||||
snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware);
|
||||
|
@ -164,9 +169,14 @@ static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
|
|||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
if (sport_handle != NULL)
|
||||
if (sport_handle != NULL) {
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
sport_handle->tx_buf = buf->area;
|
||||
else
|
||||
sport_handle->rx_buf = buf->area;
|
||||
|
||||
runtime->private_data = sport_handle;
|
||||
else {
|
||||
} else {
|
||||
pr_err("sport_handle is NULL\n");
|
||||
ret = -ENODEV;
|
||||
}
|
||||
|
@ -249,11 +259,6 @@ static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
|
|||
}
|
||||
buf->bytes = size;
|
||||
|
||||
if (stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
sport_handle->tx_buf = buf->area;
|
||||
else
|
||||
sport_handle->rx_buf = buf->area;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -274,8 +279,6 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
|
|||
dma_free_coherent(NULL, buf->bytes, buf->area, 0);
|
||||
buf->area = NULL;
|
||||
}
|
||||
if (sport_handle)
|
||||
sport_done(sport_handle);
|
||||
}
|
||||
|
||||
static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32);
|
||||
|
|
|
@ -46,43 +46,6 @@
|
|||
#include "bf5xx-sport.h"
|
||||
#include "bf5xx-tdm.h"
|
||||
|
||||
static struct bf5xx_tdm_port bf5xx_tdm;
|
||||
static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM;
|
||||
|
||||
static struct sport_param sport_params[2] = {
|
||||
{
|
||||
.dma_rx_chan = CH_SPORT0_RX,
|
||||
.dma_tx_chan = CH_SPORT0_TX,
|
||||
.err_irq = IRQ_SPORT0_ERROR,
|
||||
.regs = (struct sport_register *)SPORT0_TCR1,
|
||||
},
|
||||
{
|
||||
.dma_rx_chan = CH_SPORT1_RX,
|
||||
.dma_tx_chan = CH_SPORT1_TX,
|
||||
.err_irq = IRQ_SPORT1_ERROR,
|
||||
.regs = (struct sport_register *)SPORT1_TCR1,
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Setting the TFS pin selector for SPORT 0 based on whether the selected
|
||||
* port id F or G. If the port is F then no conflict should exist for the
|
||||
* TFS. When Port G is selected and EMAC then there is a conflict between
|
||||
* the PHY interrupt line and TFS. Current settings prevent the conflict
|
||||
* by ignoring the TFS pin when Port G is selected. This allows both
|
||||
* codecs and EMAC using Port G concurrently.
|
||||
*/
|
||||
#ifdef CONFIG_BF527_SPORT0_PORTG
|
||||
#define LOCAL_SPORT0_TFS (0)
|
||||
#else
|
||||
#define LOCAL_SPORT0_TFS (P_SPORT0_TFS)
|
||||
#endif
|
||||
|
||||
static u16 sport_req[][7] = { {P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS,
|
||||
P_SPORT0_DRPRI, P_SPORT0_RSCLK, LOCAL_SPORT0_TFS, 0},
|
||||
{P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS, P_SPORT1_DRPRI,
|
||||
P_SPORT1_RSCLK, P_SPORT1_TFS, 0} };
|
||||
|
||||
static int bf5xx_tdm_set_dai_fmt(struct snd_soc_dai *cpu_dai,
|
||||
unsigned int fmt)
|
||||
{
|
||||
|
@ -119,14 +82,16 @@ static int bf5xx_tdm_hw_params(struct snd_pcm_substream *substream,
|
|||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
|
||||
struct bf5xx_tdm_port *bf5xx_tdm = sport_handle->private_data;
|
||||
int ret = 0;
|
||||
|
||||
bf5xx_tdm.tcr2 &= ~0x1f;
|
||||
bf5xx_tdm.rcr2 &= ~0x1f;
|
||||
bf5xx_tdm->tcr2 &= ~0x1f;
|
||||
bf5xx_tdm->rcr2 &= ~0x1f;
|
||||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_S32_LE:
|
||||
bf5xx_tdm.tcr2 |= 31;
|
||||
bf5xx_tdm.rcr2 |= 31;
|
||||
bf5xx_tdm->tcr2 |= 31;
|
||||
bf5xx_tdm->rcr2 |= 31;
|
||||
sport_handle->wdsize = 4;
|
||||
break;
|
||||
/* at present, we only support 32bit transfer */
|
||||
|
@ -136,7 +101,7 @@ static int bf5xx_tdm_hw_params(struct snd_pcm_substream *substream,
|
|||
break;
|
||||
}
|
||||
|
||||
if (!bf5xx_tdm.configured) {
|
||||
if (!bf5xx_tdm->configured) {
|
||||
/*
|
||||
* TX and RX are not independent,they are enabled at the
|
||||
* same time, even if only one side is running. So, we
|
||||
|
@ -145,21 +110,21 @@ static int bf5xx_tdm_hw_params(struct snd_pcm_substream *substream,
|
|||
*
|
||||
* CPU DAI:slave mode.
|
||||
*/
|
||||
ret = sport_config_rx(sport_handle, bf5xx_tdm.rcr1,
|
||||
bf5xx_tdm.rcr2, 0, 0);
|
||||
ret = sport_config_rx(sport_handle, bf5xx_tdm->rcr1,
|
||||
bf5xx_tdm->rcr2, 0, 0);
|
||||
if (ret) {
|
||||
pr_err("SPORT is busy!\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
ret = sport_config_tx(sport_handle, bf5xx_tdm.tcr1,
|
||||
bf5xx_tdm.tcr2, 0, 0);
|
||||
ret = sport_config_tx(sport_handle, bf5xx_tdm->tcr1,
|
||||
bf5xx_tdm->tcr2, 0, 0);
|
||||
if (ret) {
|
||||
pr_err("SPORT is busy!\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
bf5xx_tdm.configured = 1;
|
||||
bf5xx_tdm->configured = 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -168,15 +133,20 @@ static int bf5xx_tdm_hw_params(struct snd_pcm_substream *substream,
|
|||
static void bf5xx_tdm_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
|
||||
struct bf5xx_tdm_port *bf5xx_tdm = sport_handle->private_data;
|
||||
|
||||
/* No active stream, SPORT is allowed to be configured again. */
|
||||
if (!dai->active)
|
||||
bf5xx_tdm.configured = 0;
|
||||
bf5xx_tdm->configured = 0;
|
||||
}
|
||||
|
||||
static int bf5xx_tdm_set_channel_map(struct snd_soc_dai *dai,
|
||||
unsigned int tx_num, unsigned int *tx_slot,
|
||||
unsigned int rx_num, unsigned int *rx_slot)
|
||||
{
|
||||
struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai);
|
||||
struct bf5xx_tdm_port *bf5xx_tdm = sport_handle->private_data;
|
||||
int i;
|
||||
unsigned int slot;
|
||||
unsigned int tx_mapped = 0, rx_mapped = 0;
|
||||
|
@ -189,7 +159,7 @@ static int bf5xx_tdm_set_channel_map(struct snd_soc_dai *dai,
|
|||
slot = tx_slot[i];
|
||||
if ((slot < BFIN_TDM_DAI_MAX_SLOTS) &&
|
||||
(!(tx_mapped & (1 << slot)))) {
|
||||
bf5xx_tdm.tx_map[i] = slot;
|
||||
bf5xx_tdm->tx_map[i] = slot;
|
||||
tx_mapped |= 1 << slot;
|
||||
} else
|
||||
return -EINVAL;
|
||||
|
@ -198,7 +168,7 @@ static int bf5xx_tdm_set_channel_map(struct snd_soc_dai *dai,
|
|||
slot = rx_slot[i];
|
||||
if ((slot < BFIN_TDM_DAI_MAX_SLOTS) &&
|
||||
(!(rx_mapped & (1 << slot)))) {
|
||||
bf5xx_tdm.rx_map[i] = slot;
|
||||
bf5xx_tdm->rx_map[i] = slot;
|
||||
rx_mapped |= 1 << slot;
|
||||
} else
|
||||
return -EINVAL;
|
||||
|
@ -212,12 +182,14 @@ static int bf5xx_tdm_suspend(struct snd_soc_dai *dai)
|
|||
{
|
||||
struct sport_device *sport = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
if (!dai->active)
|
||||
return 0;
|
||||
if (dai->capture_active)
|
||||
sport_rx_stop(sport);
|
||||
if (dai->playback_active)
|
||||
sport_tx_stop(sport);
|
||||
if (dai->capture_active)
|
||||
sport_rx_stop(sport);
|
||||
|
||||
/* isolate sync/clock pins from codec while sports resume */
|
||||
peripheral_free_list(sport->pin_req);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -226,9 +198,6 @@ static int bf5xx_tdm_resume(struct snd_soc_dai *dai)
|
|||
int ret;
|
||||
struct sport_device *sport = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
if (!dai->active)
|
||||
return 0;
|
||||
|
||||
ret = sport_set_multichannel(sport, 8, 0xFF, 1);
|
||||
if (ret) {
|
||||
pr_err("SPORT is busy!\n");
|
||||
|
@ -247,6 +216,8 @@ static int bf5xx_tdm_resume(struct snd_soc_dai *dai)
|
|||
ret = -EBUSY;
|
||||
}
|
||||
|
||||
peripheral_request_list(sport->pin_req, "soc-audio");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -280,20 +251,14 @@ static struct snd_soc_dai_driver bf5xx_tdm_dai = {
|
|||
|
||||
static int __devinit bfin_tdm_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
struct sport_device *sport_handle;
|
||||
int ret;
|
||||
|
||||
if (peripheral_request_list(&sport_req[sport_num][0], "soc-audio")) {
|
||||
pr_err("Requesting Peripherals failed\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
/* request DMA for SPORT */
|
||||
sport_handle = sport_init(&sport_params[sport_num], 4, \
|
||||
8 * sizeof(u32), NULL);
|
||||
if (!sport_handle) {
|
||||
peripheral_free_list(&sport_req[sport_num][0]);
|
||||
/* configure SPORT for TDM */
|
||||
sport_handle = sport_init(pdev, 4, 8 * sizeof(u32),
|
||||
sizeof(struct bf5xx_tdm_port));
|
||||
if (!sport_handle)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* SPORT works in TDM mode */
|
||||
ret = sport_set_multichannel(sport_handle, 8, 0xFF, 1);
|
||||
|
@ -323,18 +288,19 @@ static int __devinit bfin_tdm_probe(struct platform_device *pdev)
|
|||
goto sport_config_err;
|
||||
}
|
||||
|
||||
sport_handle->private_data = &bf5xx_tdm;
|
||||
return 0;
|
||||
|
||||
sport_config_err:
|
||||
peripheral_free_list(&sport_req[sport_num][0]);
|
||||
sport_done(sport_handle);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit bfin_tdm_remove(struct platform_device *pdev)
|
||||
{
|
||||
peripheral_free_list(&sport_req[sport_num][0]);
|
||||
struct sport_device *sport_handle = platform_get_drvdata(pdev);
|
||||
|
||||
snd_soc_unregister_dai(&pdev->dev);
|
||||
sport_done(sport_handle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue