diff options
Diffstat (limited to 'sound/soc/blackfin')
25 files changed, 2715 insertions, 886 deletions
diff --git a/sound/soc/blackfin/Kconfig b/sound/soc/blackfin/Kconfig index 0a2f8f9eff5..6410aa2cc2c 100644 --- a/sound/soc/blackfin/Kconfig +++ b/sound/soc/blackfin/Kconfig @@ -1,6 +1,8 @@ config SND_BF5XX_I2S - tristate "SoC I2S Audio for the ADI BF5xx chip" + tristate "SoC I2S Audio for the ADI Blackfin chip" depends on BLACKFIN + select SND_BF5XX_SOC_SPORT if !BF60x + select SND_BF6XX_SOC_SPORT if BF60x help Say Y or M if you want to add support for codecs attached to the Blackfin SPORT (synchronous serial ports) interface in I2S @@ -8,13 +10,96 @@ config SND_BF5XX_I2S You will also need to select the audio interfaces to support below. config SND_BF5XX_SOC_SSM2602 - tristate "SoC SSM2602 Audio support for BF52x ezkit" - depends on SND_BF5XX_I2S + tristate "SoC SSM2602 Audio Codec Add-On Card support" + depends on SND_BF5XX_I2S && SND_SOC_I2C_AND_SPI + select SND_BF5XX_SOC_I2S if !BF60x + select SND_BF6XX_SOC_I2S if BF60x + select SND_SOC_SSM2602_SPI if SPI_MASTER + select SND_SOC_SSM2602_I2C if I2C + help + Say Y if you want to add support for the Analog Devices + SSM2602 Audio Codec Add-On Card. + +config SND_SOC_BFIN_EVAL_ADAU1701 + tristate "Support for the EVAL-ADAU1701MINIZ board on Blackfin eval boards" + depends on SND_BF5XX_I2S && I2C + select SND_BF5XX_SOC_I2S + select SND_SOC_ADAU1701 + help + Say Y if you want to add support for the Analog Devices EVAL-ADAU1701MINIZ + board connected to one of the Blackfin evaluation boards like the + BF5XX-STAMP or BF5XX-EZKIT. + +config SND_SOC_BFIN_EVAL_ADAU1373 + tristate "Support for the EVAL-ADAU1373 board on Blackfin eval boards" + depends on SND_BF5XX_I2S && I2C + select SND_BF5XX_SOC_I2S + select SND_SOC_ADAU1373 + help + Say Y if you want to add support for the Analog Devices EVAL-ADAU1373 + board connected to one of the Blackfin evaluation boards like the + BF5XX-STAMP or BF5XX-EZKIT. + + Note: This driver assumes that first ADAU1373 DAI is connected to the + first SPORT port on the BF5XX board. + +config SND_SOC_BFIN_EVAL_ADAU1X61 + tristate "Support for the EVAL-ADAU1X61 board on Blackfin eval boards" + depends on SND_BF5XX_I2S && I2C + select SND_BF5XX_SOC_I2S + select SND_SOC_ADAU1761_I2C + help + Say Y if you want to add support for the Analog Devices EVAL-ADAU1X61 + board connected to one of the Blackfin evaluation boards like the + BF5XX-STAMP or BF5XX-EZKIT. + + Note: This driver assumes that the ADAU1X61 is connected to the + first SPORT port on the BF5XX board. + +config SND_SOC_BFIN_EVAL_ADAU1X81 + tristate "Support for the EVAL-ADAU1X81 boards on Blackfin eval boards" + depends on SND_BF5XX_I2S && I2C select SND_BF5XX_SOC_I2S - select SND_SOC_SSM2602 - select I2C + select SND_SOC_ADAU1781_I2C help - Say Y if you want to add support for SoC audio on BF527-EZKIT. + Say Y if you want to add support for the Analog Devices EVAL-ADAU1X81 + board connected to one of the Blackfin evaluation boards like the + BF5XX-STAMP or BF5XX-EZKIT. + + Note: This driver assumes that the ADAU1X81 is connected to the + first SPORT port on the BF5XX board. + +config SND_SOC_BFIN_EVAL_ADAV80X + tristate "Support for the EVAL-ADAV80X boards on Blackfin eval boards" + depends on SND_BF5XX_I2S && SND_SOC_I2C_AND_SPI + select SND_BF5XX_SOC_I2S + select SND_SOC_ADAV801 if SPI_MASTER + select SND_SOC_ADAV803 if I2C + help + Say Y if you want to add support for the Analog Devices EVAL-ADAV801 or + EVAL-ADAV803 board connected to one of the Blackfin evaluation boards + like the BF5XX-STAMP or BF5XX-EZKIT. + + Note: This driver assumes that the ADAV80X digital record and playback + interfaces are connected to the first SPORT port on the BF5XX board. + +config SND_BF5XX_SOC_AD1836 + tristate "SoC AD1836 Audio support for BF5xx" + depends on SND_BF5XX_I2S && SPI_MASTER + select SND_BF5XX_SOC_I2S + select SND_SOC_AD1836 + help + Say Y if you want to add support for SoC audio on BF5xx STAMP/EZKIT. + +config SND_BF5XX_SOC_AD193X + tristate "SoC AD193X Audio support for Blackfin" + depends on SND_BF5XX_I2S && SND_SOC_I2C_AND_SPI + select SND_BF5XX_SOC_I2S + select SND_SOC_AD193X_I2C if I2C + select SND_SOC_AD193X_SPI if SPI_MASTER + help + Say Y if you want to add support for AD193X codec on Blackfin. + This driver supports AD1936, AD1937, AD1938 and AD1939. config SND_BF5XX_SOC_AD73311 tristate "SoC AD73311 Audio support for Blackfin" @@ -35,6 +120,10 @@ config SND_BFIN_AD73311_SE config SND_BF5XX_AC97 tristate "SoC AC97 Audio for the ADI BF5xx chip" depends on BLACKFIN + select AC97_BUS + select SND_SOC_AC97_BUS + select SND_BF5XX_SOC_SPORT + select SND_BF5XX_SOC_AC97 help Say Y or M if you want to add support for codecs attached to the Blackfin SPORT (synchronous serial ports) interface in slot 16 @@ -42,7 +131,7 @@ config SND_BF5XX_AC97 You will also need to select the audio interfaces to support below. Note: - AC97 codecs which do not implment the slot-16 mode will not function + AC97 codecs which do not implement the slot-16 mode will not function properly with this driver. This driver is known to work with the Analog Devices line of AC97 codecs. @@ -62,48 +151,55 @@ config SND_BF5XX_MULTICHAN_SUPPORT Say y if you want AC97 driver to support up to 5.1 channel audio. this mode will consume much more memory for DMA. -config SND_BF5XX_SOC_SPORT - tristate - -config SND_BF5XX_SOC_I2S - tristate - select SND_BF5XX_SOC_SPORT +config SND_BF5XX_HAVE_COLD_RESET + bool "BOARD has COLD Reset GPIO" + depends on SND_BF5XX_AC97 + default y if BFIN548_EZKIT + default n if !BFIN548_EZKIT -config SND_BF5XX_SOC_AC97 - tristate - select AC97_BUS - select SND_SOC_AC97_BUS - select SND_BF5XX_SOC_SPORT +config SND_BF5XX_RESET_GPIO_NUM + int "Set a GPIO for cold reset" + depends on SND_BF5XX_HAVE_COLD_RESET + range 0 159 + default 19 if BFIN548_EZKIT + default 5 if BFIN537_STAMP + default 0 + help + Set the correct GPIO for RESET the sound chip. config SND_BF5XX_SOC_AD1980 - tristate "SoC AD1980/1 Audio support for BF5xx" + tristate "SoC AD1980/1 Audio support for BF5xx (Obsolete)" depends on SND_BF5XX_AC97 select SND_BF5XX_SOC_AC97 select SND_SOC_AD1980 help Say Y if you want to add support for SoC audio on BF5xx STAMP/EZKIT. + Warning: + Because Analog Devices Inc. discontinued the ad1980 sound chip since + Sep. 2009, this ad1980 driver is not maintained, tested and supported + by ADI now. + +config SND_BF5XX_SOC_SPORT + tristate + +config SND_BF6XX_SOC_SPORT + tristate + +config SND_BF5XX_SOC_I2S + tristate + +config SND_BF6XX_SOC_I2S + tristate + +config SND_BF5XX_SOC_AC97 + tristate + config SND_BF5XX_SPORT_NUM int "Set a SPORT for Sound chip" - depends on (SND_BF5XX_I2S || SND_BF5XX_AC97) + depends on (SND_BF5XX_SOC_SPORT || SND_BF6XX_SOC_SPORT) range 0 3 if BF54x range 0 1 if !BF54x default 0 help Set the correct SPORT for sound chip. - -config SND_BF5XX_HAVE_COLD_RESET - bool "BOARD has COLD Reset GPIO" - depends on SND_BF5XX_AC97 - default y if BFIN548_EZKIT - default n if !BFIN548_EZKIT - -config SND_BF5XX_RESET_GPIO_NUM - int "Set a GPIO for cold reset" - depends on SND_BF5XX_HAVE_COLD_RESET - range 0 159 - default 19 if BFIN548_EZKIT - default 5 if BFIN537_STAMP - default 0 - help - Set the correct GPIO for RESET the sound chip. diff --git a/sound/soc/blackfin/Makefile b/sound/soc/blackfin/Makefile index 97bb37a6359..f21e948b2e9 100644 --- a/sound/soc/blackfin/Makefile +++ b/sound/soc/blackfin/Makefile @@ -2,20 +2,38 @@ snd-bf5xx-ac97-objs := bf5xx-ac97-pcm.o snd-bf5xx-i2s-objs := bf5xx-i2s-pcm.o snd-soc-bf5xx-sport-objs := bf5xx-sport.o +snd-soc-bf6xx-sport-objs := bf6xx-sport.o snd-soc-bf5xx-ac97-objs := bf5xx-ac97.o snd-soc-bf5xx-i2s-objs := bf5xx-i2s.o +snd-soc-bf6xx-i2s-objs := bf6xx-i2s.o obj-$(CONFIG_SND_BF5XX_AC97) += snd-bf5xx-ac97.o obj-$(CONFIG_SND_BF5XX_I2S) += snd-bf5xx-i2s.o obj-$(CONFIG_SND_BF5XX_SOC_SPORT) += snd-soc-bf5xx-sport.o +obj-$(CONFIG_SND_BF6XX_SOC_SPORT) += snd-soc-bf6xx-sport.o obj-$(CONFIG_SND_BF5XX_SOC_AC97) += snd-soc-bf5xx-ac97.o obj-$(CONFIG_SND_BF5XX_SOC_I2S) += snd-soc-bf5xx-i2s.o +obj-$(CONFIG_SND_BF6XX_SOC_I2S) += snd-soc-bf6xx-i2s.o # Blackfin Machine Support +snd-ad1836-objs := bf5xx-ad1836.o snd-ad1980-objs := bf5xx-ad1980.o snd-ssm2602-objs := bf5xx-ssm2602.o snd-ad73311-objs := bf5xx-ad73311.o +snd-ad193x-objs := bf5xx-ad193x.o +snd-soc-bfin-eval-adau1373-objs := bfin-eval-adau1373.o +snd-soc-bfin-eval-adau1x61-objs := bfin-eval-adau1x61.o +snd-soc-bfin-eval-adau1x81-objs := bfin-eval-adau1x81.o +snd-soc-bfin-eval-adau1701-objs := bfin-eval-adau1701.o +snd-soc-bfin-eval-adav80x-objs := bfin-eval-adav80x.o +obj-$(CONFIG_SND_BF5XX_SOC_AD1836) += snd-ad1836.o obj-$(CONFIG_SND_BF5XX_SOC_AD1980) += snd-ad1980.o obj-$(CONFIG_SND_BF5XX_SOC_SSM2602) += snd-ssm2602.o obj-$(CONFIG_SND_BF5XX_SOC_AD73311) += snd-ad73311.o +obj-$(CONFIG_SND_BF5XX_SOC_AD193X) += snd-ad193x.o +obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAU1373) += snd-soc-bfin-eval-adau1373.o +obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAU1X61) += snd-soc-bfin-eval-adau1x61.o +obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAU1X81) += snd-soc-bfin-eval-adau1x81.o +obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAU1701) += snd-soc-bfin-eval-adau1701.o +obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAV80X) += snd-soc-bfin-eval-adav80x.o diff --git a/sound/soc/blackfin/bf5xx-ac97-pcm.c b/sound/soc/blackfin/bf5xx-ac97-pcm.c index 8067cfafa3a..cdb8ee75ded 100644 --- a/sound/soc/blackfin/bf5xx-ac97-pcm.c +++ b/sound/soc/blackfin/bf5xx-ac97-pcm.c @@ -29,8 +29,8 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/platform_device.h> -#include <linux/slab.h> #include <linux/dma-mapping.h> +#include <linux/gfp.h> #include <sound/core.h> #include <sound/pcm.h> @@ -39,7 +39,6 @@ #include <asm/dma.h> -#include "bf5xx-ac97-pcm.h" #include "bf5xx-ac97.h" #include "bf5xx-sport.h" @@ -108,7 +107,6 @@ static const struct snd_pcm_hardware bf5xx_pcm_hardware = { #endif SNDRV_PCM_INFO_BLOCK_TRANSFER, - .formats = SNDRV_PCM_FMTBIT_S16_LE, .period_bytes_min = 32, .period_bytes_max = 0x10000, .periods_min = 1, @@ -243,6 +241,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; @@ -297,7 +298,7 @@ static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel, } #endif -struct snd_pcm_ops bf5xx_pcm_ac97_ops = { +static struct snd_pcm_ops bf5xx_pcm_ac97_ops = { .open = bf5xx_pcm_open, .ioctl = snd_pcm_lib_ioctl, .hw_params = bf5xx_pcm_hw_params, @@ -314,6 +315,9 @@ 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 @@ -349,9 +353,7 @@ static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) sport_handle->tx_dma_buf = dma_alloc_coherent(NULL, \ size, &sport_handle->tx_dma_phy, GFP_KERNEL); if (!sport_handle->tx_dma_buf) { - pr_err("Failed to allocate memory for tx dma \ - buf - Please increase uncached DMA \ - memory region\n"); + pr_err("Failed to allocate memory for tx dma buf - Please increase uncached DMA memory region\n"); return -ENOMEM; } else memset(sport_handle->tx_dma_buf, 0, size); @@ -362,9 +364,7 @@ static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) sport_handle->rx_dma_buf = dma_alloc_coherent(NULL, \ size, &sport_handle->rx_dma_phy, GFP_KERNEL); if (!sport_handle->rx_dma_buf) { - pr_err("Failed to allocate memory for rx dma \ - buf - Please increase uncached DMA \ - memory region\n"); + pr_err("Failed to allocate memory for rx dma buf - Please increase uncached DMA memory region\n"); return -ENOMEM; } else memset(sport_handle->rx_dma_buf, 0, size); @@ -381,6 +381,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 @@ -409,31 +412,27 @@ 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_32BIT_MASK; - -int bf5xx_pcm_ac97_new(struct snd_card *card, struct snd_soc_dai *dai, - struct snd_pcm *pcm) +static int bf5xx_pcm_ac97_new(struct snd_soc_pcm_runtime *rtd) { - int ret = 0; + struct snd_card *card = rtd->card->snd_card; + struct snd_pcm *pcm = rtd->pcm; + int ret; pr_debug("%s enter\n", __func__); - if (!card->dev->dma_mask) - card->dev->dma_mask = &bf5xx_pcm_dmamask; - if (!card->dev->coherent_dma_mask) - card->dev->coherent_dma_mask = DMA_32BIT_MASK; + ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32)); + if (ret) + return ret; - if (dai->playback.channels_min) { + if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { ret = bf5xx_pcm_preallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK); if (ret) goto out; } - if (dai->capture.channels_min) { + if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { ret = bf5xx_pcm_preallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_CAPTURE); if (ret) @@ -443,25 +442,34 @@ int bf5xx_pcm_ac97_new(struct snd_card *card, struct snd_soc_dai *dai, return ret; } -struct snd_soc_platform bf5xx_ac97_soc_platform = { - .name = "bf5xx-audio", - .pcm_ops = &bf5xx_pcm_ac97_ops, +static struct snd_soc_platform_driver bf5xx_ac97_soc_platform = { + .ops = &bf5xx_pcm_ac97_ops, .pcm_new = bf5xx_pcm_ac97_new, .pcm_free = bf5xx_pcm_free_dma_buffers, }; -EXPORT_SYMBOL_GPL(bf5xx_ac97_soc_platform); -static int __init bfin_ac97_init(void) +static int bf5xx_soc_platform_probe(struct platform_device *pdev) { - return snd_soc_register_platform(&bf5xx_ac97_soc_platform); + return snd_soc_register_platform(&pdev->dev, &bf5xx_ac97_soc_platform); } -module_init(bfin_ac97_init); -static void __exit bfin_ac97_exit(void) +static int bf5xx_soc_platform_remove(struct platform_device *pdev) { - snd_soc_unregister_platform(&bf5xx_ac97_soc_platform); + snd_soc_unregister_platform(&pdev->dev); + return 0; } -module_exit(bfin_ac97_exit); + +static struct platform_driver bf5xx_pcm_driver = { + .driver = { + .name = "bfin-ac97-pcm-audio", + .owner = THIS_MODULE, + }, + + .probe = bf5xx_soc_platform_probe, + .remove = bf5xx_soc_platform_remove, +}; + +module_platform_driver(bf5xx_pcm_driver); MODULE_AUTHOR("Cliff Cai"); MODULE_DESCRIPTION("ADI Blackfin AC97 PCM DMA module"); diff --git a/sound/soc/blackfin/bf5xx-ac97-pcm.h b/sound/soc/blackfin/bf5xx-ac97-pcm.h deleted file mode 100644 index 350125a0ae2..00000000000 --- a/sound/soc/blackfin/bf5xx-ac97-pcm.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * linux/sound/arm/bf5xx-ac97-pcm.h -- ALSA PCM interface for the Blackfin - * - * Copyright 2007 Analog Device Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef _BF5XX_AC97_PCM_H -#define _BF5XX_AC97_PCM_H - -struct bf5xx_pcm_dma_params { - char *name; /* stream identifier */ -}; - -struct bf5xx_gpio { - u32 sys; - u32 rx; - u32 tx; - u32 clk; - u32 frm; -}; - -/* platform data */ -extern struct snd_soc_platform bf5xx_ac97_soc_platform; - -#endif diff --git a/sound/soc/blackfin/bf5xx-ac97.c b/sound/soc/blackfin/bf5xx-ac97.c index 3be2be60576..e82eb373a73 100644 --- a/sound/soc/blackfin/bf5xx-ac97.c +++ b/sound/soc/blackfin/bf5xx-ac97.c @@ -16,6 +16,7 @@ #include <linux/interrupt.h> #include <linux/wait.h> #include <linux/delay.h> +#include <linux/slab.h> #include <sound/core.h> #include <sound/pcm.h> @@ -31,74 +32,16 @@ #include "bf5xx-sport.h" #include "bf5xx-ac97.h" -#if defined(CONFIG_BF54x) -#define PIN_REQ_SPORT_0 {P_SPORT0_TFS, P_SPORT0_DTPRI, P_SPORT0_TSCLK, \ - P_SPORT0_RFS, P_SPORT0_DRPRI, P_SPORT0_RSCLK, 0} +/* Anomaly notes: + * 05000250 - AD1980 is running in TDM mode and RFS/TFS are generated by SPORT + * contrtoller. But, RFSDIV and TFSDIV are always set to 16*16-1, + * while the max AC97 data size is 13*16. The DIV is always larger + * than data size. AD73311 and ad2602 are not running in TDM mode. + * AD1836 and AD73322 depend on external RFS/TFS only. So, this + * anomaly does not affect blackfin sound drivers. +*/ -#define PIN_REQ_SPORT_1 {P_SPORT1_TFS, P_SPORT1_DTPRI, P_SPORT1_TSCLK, \ - P_SPORT1_RFS, P_SPORT1_DRPRI, P_SPORT1_RSCLK, 0} - -#define PIN_REQ_SPORT_2 {P_SPORT2_TFS, P_SPORT2_DTPRI, P_SPORT2_TSCLK, \ - P_SPORT2_RFS, P_SPORT2_DRPRI, P_SPORT2_RSCLK, 0} - -#define PIN_REQ_SPORT_3 {P_SPORT3_TFS, P_SPORT3_DTPRI, P_SPORT3_TSCLK, \ - P_SPORT3_RFS, P_SPORT3_DRPRI, P_SPORT3_RSCLK, 0} -#else -#define PIN_REQ_SPORT_0 {P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS, \ - P_SPORT0_DRPRI, P_SPORT0_RSCLK, 0} - -#define PIN_REQ_SPORT_1 {P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS, \ - P_SPORT1_DRPRI, P_SPORT1_RSCLK, 0} -#endif - -static int *cmd_count; -static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM; - -static u16 sport_req[][7] = { - PIN_REQ_SPORT_0, -#ifdef PIN_REQ_SPORT_1 - PIN_REQ_SPORT_1, -#endif -#ifdef PIN_REQ_SPORT_2 - PIN_REQ_SPORT_2, -#endif -#ifdef PIN_REQ_SPORT_3 - PIN_REQ_SPORT_3, -#endif - }; - -static struct sport_param sport_params[4] = { - { - .dma_rx_chan = CH_SPORT0_RX, - .dma_tx_chan = CH_SPORT0_TX, - .err_irq = IRQ_SPORT0_ERROR, - .regs = (struct sport_register *)SPORT0_TCR1, - }, -#ifdef PIN_REQ_SPORT_1 - { - .dma_rx_chan = CH_SPORT1_RX, - .dma_tx_chan = CH_SPORT1_TX, - .err_irq = IRQ_SPORT1_ERROR, - .regs = (struct sport_register *)SPORT1_TCR1, - }, -#endif -#ifdef PIN_REQ_SPORT_2 - { - .dma_rx_chan = CH_SPORT2_RX, - .dma_tx_chan = CH_SPORT2_TX, - .err_irq = IRQ_SPORT2_ERROR, - .regs = (struct sport_register *)SPORT2_TCR1, - }, -#endif -#ifdef PIN_REQ_SPORT_3 - { - .dma_rx_chan = CH_SPORT3_RX, - .dma_tx_chan = CH_SPORT3_TX, - .err_irq = IRQ_SPORT3_ERROR, - .regs = (struct sport_register *)SPORT3_TCR1, - } -#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) @@ -156,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; @@ -177,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); @@ -201,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) { @@ -219,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) @@ -260,26 +198,24 @@ static void bf5xx_ac97_cold_reset(struct snd_ac97 *ac97) #endif } -struct snd_ac97_bus_ops soc_ac97_ops = { +static struct snd_ac97_bus_ops bf5xx_ac97_ops = { .read = bf5xx_ac97_read, .write = bf5xx_ac97_write, .warm_reset = bf5xx_ac97_warm_reset, .reset = bf5xx_ac97_cold_reset, }; -EXPORT_SYMBOL_GPL(soc_ac97_ops); #ifdef CONFIG_PM static int bf5xx_ac97_suspend(struct snd_soc_dai *dai) { - struct sport_device *sport = - (struct sport_device *)dai->private_data; + struct sport_device *sport = snd_soc_dai_get_drvdata(dai); pr_debug("%s : sport %d\n", __func__, dai->id); if (!dai->active) return 0; - if (dai->capture.active) + if (dai->capture_active) sport_rx_stop(sport); - if (dai->playback.active) + if (dai->playback_active) sport_tx_stop(sport); return 0; } @@ -287,35 +223,34 @@ static int bf5xx_ac97_suspend(struct snd_soc_dai *dai) static int bf5xx_ac97_resume(struct snd_soc_dai *dai) { int ret; - struct sport_device *sport = - (struct sport_device *)dai->private_data; + struct sport_device *sport = snd_soc_dai_get_drvdata(dai); pr_debug("%s : sport %d\n", __func__, dai->id); if (!dai->active) return 0; - ret = sport_set_multichannel(sport_handle, 16, 0x1F, 1); +#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT) + ret = sport_set_multichannel(sport, 16, 0x3FF, 0x3FF, 1); +#else + ret = sport_set_multichannel(sport, 16, 0x1F, 0x1F, 1); +#endif if (ret) { pr_err("SPORT is busy!\n"); return -EBUSY; } - ret = sport_config_rx(sport_handle, IRFS, 0xF, 0, (16*16-1)); + ret = sport_config_rx(sport, IRFS, 0xF, 0, (16*16-1)); if (ret) { pr_err("SPORT is busy!\n"); return -EBUSY; } - ret = sport_config_tx(sport_handle, ITFS, 0xF, 0, (16*16-1)); + ret = sport_config_tx(sport, ITFS, 0xF, 0, (16*16-1)); if (ret) { pr_err("SPORT is busy!\n"); return -EBUSY; } - if (dai->capture.active) - sport_rx_start(sport); - if (dai->playback.active) - sport_tx_start(sport); return 0; } @@ -324,38 +259,63 @@ static int bf5xx_ac97_resume(struct snd_soc_dai *dai) #define bf5xx_ac97_resume NULL #endif -static int bf5xx_ac97_probe(struct platform_device *pdev, - struct snd_soc_dai *dai) +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, }, +}; + +static const struct snd_soc_component_driver bfin_ac97_component = { + .name = "bfin-ac97", +}; + +static int asoc_bfin_ac97_probe(struct platform_device *pdev) { - int ret = 0; - cmd_count = (int *)get_zeroed_page(GFP_KERNEL); - if (cmd_count == NULL) - return -ENOMEM; - - if (peripheral_request_list(&sport_req[sport_num][0], "soc-audio")) { - pr_err("Requesting Peripherals failed\n"); - ret = -EFAULT; - goto peripheral_err; - } + struct sport_device *sport_handle; + int ret; #ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET /* Request PB3 as reset pin */ - if (gpio_request(CONFIG_SND_BF5XX_RESET_GPIO_NUM, "SND_AD198x RESET")) { - pr_err("Failed to request GPIO_%d for reset\n", - CONFIG_SND_BF5XX_RESET_GPIO_NUM); - ret = -1; - goto gpio_err; + ret = devm_gpio_request_one(&pdev->dev, + CONFIG_SND_BF5XX_RESET_GPIO_NUM, + GPIOF_OUT_INIT_HIGH, "SND_AD198x RESET"); + if (ret) { + dev_err(&pdev->dev, + "Failed to request GPIO_%d for reset: %d\n", + CONFIG_SND_BF5XX_RESET_GPIO_NUM, ret); + return ret; } - 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*/ - ret = sport_set_multichannel(sport_handle, 16, 0x1F, 1); +#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT) + ret = sport_set_multichannel(sport_handle, 16, 0x3FF, 0x3FF, 1); +#else + ret = sport_set_multichannel(sport_handle, 16, 0x1F, 0x1F, 1); +#endif if (ret) { pr_err("SPORT is busy!\n"); ret = -EBUSY; @@ -376,72 +336,53 @@ static int bf5xx_ac97_probe(struct platform_device *pdev, goto sport_config_err; } + ret = snd_soc_set_ac97_ops(&bf5xx_ac97_ops); + if (ret != 0) { + dev_err(&pdev->dev, "Failed to set AC'97 ops: %d\n", ret); + goto sport_config_err; + } + + ret = snd_soc_register_component(&pdev->dev, &bfin_ac97_component, + &bfin_ac97_dai, 1); + 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); -#endif -gpio_err: - peripheral_free_list(&sport_req[sport_num][0]); -peripheral_err: - free_page((unsigned long)cmd_count); - cmd_count = NULL; + snd_soc_set_ac97_ops(NULL); return ret; } -static void bf5xx_ac97_remove(struct platform_device *pdev, - struct snd_soc_dai *dai) +static int asoc_bfin_ac97_remove(struct platform_device *pdev) { - free_page((unsigned long)cmd_count); - cmd_count = NULL; - peripheral_free_list(&sport_req[sport_num][0]); -#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET - gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM); -#endif -} + struct sport_device *sport_handle = platform_get_drvdata(pdev); -struct snd_soc_dai bfin_ac97_dai = { - .name = "bf5xx-ac97", - .id = 0, - .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); + snd_soc_unregister_component(&pdev->dev); + sport_done(sport_handle); + snd_soc_set_ac97_ops(NULL); -static int __init bfin_ac97_init(void) -{ - return snd_soc_register_dai(&bfin_ac97_dai); + return 0; } -module_init(bfin_ac97_init); -static void __exit bfin_ac97_exit(void) -{ - snd_soc_unregister_dai(&bfin_ac97_dai); -} -module_exit(bfin_ac97_exit); +static struct platform_driver asoc_bfin_ac97_driver = { + .driver = { + .name = "bfin-ac97", + .owner = THIS_MODULE, + }, + + .probe = asoc_bfin_ac97_probe, + .remove = asoc_bfin_ac97_remove, +}; + +module_platform_driver(asoc_bfin_ac97_driver); MODULE_AUTHOR("Roy Huang"); MODULE_DESCRIPTION("AC97 driver for ADI Blackfin"); diff --git a/sound/soc/blackfin/bf5xx-ac97.h b/sound/soc/blackfin/bf5xx-ac97.h index 3f2a911fe0c..a680fdc9bb4 100644 --- a/sound/soc/blackfin/bf5xx-ac97.h +++ b/sound/soc/blackfin/bf5xx-ac97.h @@ -1,5 +1,5 @@ /* - * linux/sound/arm/bf5xx-ac97.h + * sound/soc/blackfin/bf5xx-ac97.h * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -9,8 +9,6 @@ #ifndef _BF5XX_AC97_H #define _BF5XX_AC97_H -extern struct snd_ac97_bus_ops bf5xx_ac97_ops; -extern struct snd_ac97 *ac97; /* Frame format in memory, only support stereo currently */ struct ac97_frame { u16 ac97_tag; /* slot 0 */ @@ -50,8 +48,6 @@ struct ac97_frame { #define TAG_PCM_SR 0x0080 #define TAG_PCM_LFE 0x0040 -extern struct snd_soc_dai bfin_ac97_dai; - void bf5xx_pcm_to_ac97(struct ac97_frame *dst, const __u16 *src, \ size_t count, unsigned int chan_mask); diff --git a/sound/soc/blackfin/bf5xx-ad1836.c b/sound/soc/blackfin/bf5xx-ad1836.c new file mode 100644 index 00000000000..8fcfc4ec3a5 --- /dev/null +++ b/sound/soc/blackfin/bf5xx-ad1836.c @@ -0,0 +1,119 @@ +/* + * File: sound/soc/blackfin/bf5xx-ad1836.c + * Author: Barry Song <Barry.Song@analog.com> + * + * Created: Aug 4 2009 + * Description: Board driver for ad1836 sound chip + * + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * 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. + * + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/pcm_params.h> + +#include <asm/blackfin.h> +#include <asm/cacheflush.h> +#include <asm/irq.h> +#include <asm/dma.h> +#include <asm/portmux.h> + +#include "../codecs/ad1836.h" + +static struct snd_soc_card bf5xx_ad1836; + +static int bf5xx_ad1836_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + unsigned int channel_map[] = {0, 4, 1, 5, 2, 6, 3, 7}; + int ret = 0; + + /* set cpu DAI channel mapping */ + ret = snd_soc_dai_set_channel_map(cpu_dai, ARRAY_SIZE(channel_map), + channel_map, ARRAY_SIZE(channel_map), channel_map); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0xFF, 0xFF, 8, 32); + if (ret < 0) + return ret; + + return 0; +} + +#define BF5XX_AD1836_DAIFMT (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_IF | \ + SND_SOC_DAIFMT_CBM_CFM) + +static struct snd_soc_dai_link bf5xx_ad1836_dai = { + .name = "ad1836", + .stream_name = "AD1836", + .codec_dai_name = "ad1836-hifi", + .platform_name = "bfin-i2s-pcm-audio", + .dai_fmt = BF5XX_AD1836_DAIFMT, + .init = bf5xx_ad1836_init, +}; + +static struct snd_soc_card bf5xx_ad1836 = { + .name = "bfin-ad1836", + .owner = THIS_MODULE, + .dai_link = &bf5xx_ad1836_dai, + .num_links = 1, +}; + +static int bf5xx_ad1836_driver_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &bf5xx_ad1836; + const char **link_name; + int ret; + + link_name = pdev->dev.platform_data; + if (!link_name) { + dev_err(&pdev->dev, "No platform data supplied\n"); + return -EINVAL; + } + bf5xx_ad1836_dai.cpu_dai_name = link_name[0]; + bf5xx_ad1836_dai.codec_name = link_name[1]; + + card->dev = &pdev->dev; + platform_set_drvdata(pdev, card); + + ret = snd_soc_register_card(card); + if (ret) + dev_err(&pdev->dev, "Failed to register card\n"); + return ret; +} + +static int bf5xx_ad1836_driver_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + + snd_soc_unregister_card(card); + return 0; +} + +static struct platform_driver bf5xx_ad1836_driver = { + .driver = { + .name = "bfin-snd-ad1836", + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + }, + .probe = bf5xx_ad1836_driver_probe, + .remove = bf5xx_ad1836_driver_remove, +}; +module_platform_driver(bf5xx_ad1836_driver); + +/* Module information */ +MODULE_AUTHOR("Barry Song"); +MODULE_DESCRIPTION("ALSA SoC AD1836 board driver"); +MODULE_LICENSE("GPL"); + diff --git a/sound/soc/blackfin/bf5xx-ad193x.c b/sound/soc/blackfin/bf5xx-ad193x.c new file mode 100644 index 00000000000..603ad1f2b9b --- /dev/null +++ b/sound/soc/blackfin/bf5xx-ad193x.c @@ -0,0 +1,131 @@ +/* + * File: sound/soc/blackfin/bf5xx-ad193x.c + * Author: Barry Song <Barry.Song@analog.com> + * + * Created: Thur June 4 2009 + * Description: Board driver for ad193x sound chip + * + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * 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, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/pcm_params.h> + +#include <asm/blackfin.h> +#include <asm/cacheflush.h> +#include <asm/irq.h> +#include <asm/dma.h> +#include <asm/portmux.h> + +#include "../codecs/ad193x.h" + +static struct snd_soc_card bf5xx_ad193x; + +static int bf5xx_ad193x_link_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int ret; + + /* set the codec system clock for DAC and ADC */ + ret = snd_soc_dai_set_sysclk(codec_dai, 0, 24576000, SND_SOC_CLOCK_IN); + if (ret < 0) + return ret; + + /* set codec DAI slots, 8 channels, all channels are enabled */ + ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xFF, 0xFF, 8, 32); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0xFF, 0xFF, 8, 32); + if (ret < 0) + return ret; + + return 0; +} + +#define BF5XX_AD193X_DAIFMT (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_IF | \ + SND_SOC_DAIFMT_CBM_CFM) + +static struct snd_soc_dai_link bf5xx_ad193x_dai[] = { + { + .name = "ad193x", + .stream_name = "AD193X", + .cpu_dai_name = "bfin-i2s.0", + .codec_dai_name ="ad193x-hifi", + .platform_name = "bfin-i2s-pcm-audio", + .codec_name = "spi0.5", + .dai_fmt = BF5XX_AD193X_DAIFMT, + .init = bf5xx_ad193x_link_init, + }, + { + .name = "ad193x", + .stream_name = "AD193X", + .cpu_dai_name = "bfin-i2s.1", + .codec_dai_name ="ad193x-hifi", + .platform_name = "bfin-i2s-pcm-audio", + .codec_name = "spi0.5", + .dai_fmt = BF5XX_AD193X_DAIFMT, + .init = bf5xx_ad193x_link_init, + }, +}; + +static struct snd_soc_card bf5xx_ad193x = { + .name = "bfin-ad193x", + .owner = THIS_MODULE, + .dai_link = &bf5xx_ad193x_dai[CONFIG_SND_BF5XX_SPORT_NUM], + .num_links = 1, +}; + +static struct platform_device *bfxx_ad193x_snd_device; + +static int __init bf5xx_ad193x_init(void) +{ + int ret; + + bfxx_ad193x_snd_device = platform_device_alloc("soc-audio", -1); + if (!bfxx_ad193x_snd_device) + return -ENOMEM; + + platform_set_drvdata(bfxx_ad193x_snd_device, &bf5xx_ad193x); + ret = platform_device_add(bfxx_ad193x_snd_device); + + if (ret) + platform_device_put(bfxx_ad193x_snd_device); + + return ret; +} + +static void __exit bf5xx_ad193x_exit(void) +{ + platform_device_unregister(bfxx_ad193x_snd_device); +} + +module_init(bf5xx_ad193x_init); +module_exit(bf5xx_ad193x_exit); + +/* Module information */ +MODULE_AUTHOR("Barry Song"); +MODULE_DESCRIPTION("ALSA SoC AD193X board driver"); +MODULE_LICENSE("GPL"); + diff --git a/sound/soc/blackfin/bf5xx-ad1980.c b/sound/soc/blackfin/bf5xx-ad1980.c index d8f59127377..3450e8f9080 100644 --- a/sound/soc/blackfin/bf5xx-ad1980.c +++ b/sound/soc/blackfin/bf5xx-ad1980.c @@ -26,6 +26,14 @@ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +/* + * WARNING: + * + * Because Analog Devices Inc. discontinued the ad1980 sound chip since + * Sep. 2009, this ad1980 driver is not maintained, tested and supported + * by ADI now. + */ + #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/device.h> @@ -39,46 +47,37 @@ #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->dai->cpu_dai; - - pr_debug("%s enter\n", __func__); - cpu_dai->private_data = 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 = &bfin_ac97_dai, - .codec_dai = &ad1980_dai, - .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 = "bf5xx-board", - .platform = &bf5xx_ac97_soc_platform, - .dai_link = &bf5xx_board_dai, + .name = "bfin-ad1980", + .owner = THIS_MODULE, + .dai_link = &bf5xx_board_dai[CONFIG_SND_BF5XX_SPORT_NUM], .num_links = 1, }; -static struct snd_soc_device bf5xx_board_snd_devdata = { - .card = &bf5xx_board, - .codec_dev = &soc_codec_dev_ad1980, -}; - static struct platform_device *bf5xx_board_snd_device; static int __init bf5xx_board_init(void) @@ -89,8 +88,7 @@ static int __init bf5xx_board_init(void) if (!bf5xx_board_snd_device) return -ENOMEM; - platform_set_drvdata(bf5xx_board_snd_device, &bf5xx_board_snd_devdata); - bf5xx_board_snd_devdata.dev = &bf5xx_board_snd_device->dev; + platform_set_drvdata(bf5xx_board_snd_device, &bf5xx_board); ret = platform_device_add(bf5xx_board_snd_device); if (ret) @@ -109,5 +107,5 @@ module_exit(bf5xx_board_exit); /* Module information */ MODULE_AUTHOR("Cliff Cai"); -MODULE_DESCRIPTION("ALSA SoC AD1980/1 BF5xx board"); +MODULE_DESCRIPTION("ALSA SoC AD1980/1 BF5xx board (Obsolete)"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/blackfin/bf5xx-ad73311.c b/sound/soc/blackfin/bf5xx-ad73311.c index 7f2a5e19907..786bbdd96e7 100644 --- a/sound/soc/blackfin/bf5xx-ad73311.c +++ b/sound/soc/blackfin/bf5xx-ad73311.c @@ -35,7 +35,6 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/pcm_params.h> #include <asm/blackfin.h> @@ -46,8 +45,6 @@ #include "../codecs/ad73311.h" #include "bf5xx-sport.h" -#include "bf5xx-i2s-pcm.h" -#include "bf5xx-i2s.h" #if CONFIG_SND_BF5XX_SPORT_NUM == 0 #define bfin_write_SPORT_TCR1 bfin_write_SPORT0_TCR1 @@ -114,7 +111,7 @@ static int snd_ad73311_configure(void) SSYNC(); /* When TUVF is set, the data is already send out */ - while (!(status & TUVF) && count++ < 10000) { + while (!(status & TUVF) && ++count < 10000) { udelay(1); status = bfin_read_SPORT_STAT(); SSYNC(); @@ -123,14 +120,14 @@ static int snd_ad73311_configure(void) SSYNC(); local_irq_enable(); - if (count == 10000) { + if (count >= 10000) { printk(KERN_ERR "ad73311: failed to configure codec\n"); return -1; } return 0; } -static int bf5xx_probe(struct platform_device *pdev) +static int bf5xx_probe(struct snd_soc_card *card) { int err; if (gpio_request(GPIO_SE, "AD73311_SE")) { @@ -147,79 +144,54 @@ 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->dai->cpu_dai; - - pr_debug("%s enter\n", __func__); - cpu_dai->private_data = sport_handle; - return 0; -} - -static int bf5xx_ad73311_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; - int ret = 0; - - pr_debug("%s rate %d format %x\n", __func__, params_rate(params), - params_format(params)); - - /* set cpu DAI configuration */ - ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A | - SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); - if (ret < 0) - return ret; - - return 0; -} - - -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 = &bf5xx_i2s_dai, - .codec_dai = &ad73311_dai, - .ops = &bf5xx_ad73311_ops, +#define BF5XX_AD7311_DAI_FMT (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF | \ + SND_SOC_DAIFMT_CBM_CFM) + +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", + .dai_fmt = BF5XX_AD7311_DAI_FMT, + }, + { + .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", + .dai_fmt = BF5XX_AD7311_DAI_FMT, + }, }; static struct snd_soc_card bf5xx_ad73311 = { - .name = "bf5xx_ad73311", - .platform = &bf5xx_i2s_soc_platform, + .name = "bfin-ad73311", + .owner = THIS_MODULE, .probe = bf5xx_probe, - .dai_link = &bf5xx_ad73311_dai, + .dai_link = &bf5xx_ad73311_dai[CONFIG_SND_BF5XX_SPORT_NUM], .num_links = 1, }; -static struct snd_soc_device bf5xx_ad73311_snd_devdata = { - .card = &bf5xx_ad73311, - .codec_dev = &soc_codec_dev_ad73311, -}; - -static struct platform_device *bf52x_ad73311_snd_device; +static struct platform_device *bf5xx_ad73311_snd_device; static int __init bf5xx_ad73311_init(void) { int ret; pr_debug("%s enter\n", __func__); - bf52x_ad73311_snd_device = platform_device_alloc("soc-audio", -1); - if (!bf52x_ad73311_snd_device) + bf5xx_ad73311_snd_device = platform_device_alloc("soc-audio", -1); + if (!bf5xx_ad73311_snd_device) return -ENOMEM; - platform_set_drvdata(bf52x_ad73311_snd_device, &bf5xx_ad73311_snd_devdata); - bf5xx_ad73311_snd_devdata.dev = &bf52x_ad73311_snd_device->dev; - ret = platform_device_add(bf52x_ad73311_snd_device); + platform_set_drvdata(bf5xx_ad73311_snd_device, &bf5xx_ad73311); + ret = platform_device_add(bf5xx_ad73311_snd_device); if (ret) - platform_device_put(bf52x_ad73311_snd_device); + platform_device_put(bf5xx_ad73311_snd_device); return ret; } @@ -227,7 +199,7 @@ static int __init bf5xx_ad73311_init(void) static void __exit bf5xx_ad73311_exit(void) { pr_debug("%s enter\n", __func__); - platform_device_unregister(bf52x_ad73311_snd_device); + platform_device_unregister(bf5xx_ad73311_snd_device); } module_init(bf5xx_ad73311_init); diff --git a/sound/soc/blackfin/bf5xx-i2s-pcm.c b/sound/soc/blackfin/bf5xx-i2s-pcm.c index 53d290b3ea4..a3881c4381c 100644 --- a/sound/soc/blackfin/bf5xx-i2s-pcm.c +++ b/sound/soc/blackfin/bf5xx-i2s-pcm.c @@ -29,8 +29,8 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/platform_device.h> -#include <linux/slab.h> #include <linux/dma-mapping.h> +#include <linux/gfp.h> #include <sound/core.h> #include <sound/pcm.h> @@ -39,9 +39,8 @@ #include <asm/dma.h> -#include "bf5xx-i2s-pcm.h" -#include "bf5xx-i2s.h" #include "bf5xx-sport.h" +#include "bf5xx-i2s-pcm.h" static void bf5xx_dma_irq(void *data) { @@ -51,12 +50,8 @@ static void bf5xx_dma_irq(void *data) static const struct snd_pcm_hardware bf5xx_pcm_hardware = { .info = SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BLOCK_TRANSFER, - .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S32_LE, .period_bytes_min = 32, .period_bytes_max = 0x10000, .periods_min = 1, @@ -68,10 +63,16 @@ static const struct snd_pcm_hardware bf5xx_pcm_hardware = { static int bf5xx_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - size_t size = bf5xx_pcm_hardware.buffer_bytes_max; - snd_pcm_lib_malloc_pages(substream, size); + struct snd_soc_pcm_runtime *rtd = substream->private_data; + unsigned int buffer_size = params_buffer_bytes(params); + struct bf5xx_i2s_pcm_data *dma_data; - return 0; + dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + + if (dma_data->tdm_mode) + buffer_size = buffer_size / params_channels(params) * 8; + + return snd_pcm_lib_malloc_pages(substream, buffer_size); } static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream) @@ -83,9 +84,16 @@ static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream) static int bf5xx_pcm_prepare(struct snd_pcm_substream *substream) { + struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; struct sport_device *sport = runtime->private_data; int period_bytes = frames_to_bytes(runtime, runtime->period_size); + struct bf5xx_i2s_pcm_data *dma_data; + + dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + + if (dma_data->tdm_mode) + period_bytes = period_bytes / runtime->channels * 8; pr_debug("%s enter\n", __func__); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { @@ -132,37 +140,70 @@ static int bf5xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream) { + struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; struct sport_device *sport = runtime->private_data; unsigned int diff; snd_pcm_uframes_t frames; + struct bf5xx_i2s_pcm_data *dma_data; + + dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + pr_debug("%s enter\n", __func__); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { diff = sport_curr_offset_tx(sport); - frames = bytes_to_frames(substream->runtime, diff); } else { diff = sport_curr_offset_rx(sport); - frames = bytes_to_frames(substream->runtime, diff); } + + /* + * TX at least can report one frame beyond the end of the + * buffer if we hit the wraparound case - clamp to within the + * buffer as the ALSA APIs require. + */ + if (diff == snd_pcm_lib_buffer_bytes(substream)) + diff = 0; + + frames = bytes_to_frames(substream->runtime, diff); + if (dma_data->tdm_mode) + frames = frames * runtime->channels / 8; + return frames; } 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; + struct bf5xx_i2s_pcm_data *dma_data; int ret; + dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + pr_debug("%s enter\n", __func__); + snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware); + if (dma_data->tdm_mode) + runtime->hw.buffer_bytes_max /= 4; + else + runtime->hw.info |= SNDRV_PCM_INFO_MMAP; - ret = snd_pcm_hw_constraint_integer(runtime, \ + ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); 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; } @@ -184,116 +225,143 @@ static int bf5xx_pcm_mmap(struct snd_pcm_substream *substream, return 0 ; } -struct snd_pcm_ops bf5xx_pcm_i2s_ops = { - .open = bf5xx_pcm_open, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = bf5xx_pcm_hw_params, - .hw_free = bf5xx_pcm_hw_free, - .prepare = bf5xx_pcm_prepare, - .trigger = bf5xx_pcm_trigger, - .pointer = bf5xx_pcm_pointer, - .mmap = bf5xx_pcm_mmap, -}; - -static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) +static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel, + snd_pcm_uframes_t pos, void *buf, snd_pcm_uframes_t count) { - 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; - - buf->dev.type = SNDRV_DMA_TYPE_DEV; - buf->dev.dev = pcm->card->dev; - buf->private_data = NULL; - buf->area = dma_alloc_coherent(pcm->card->dev, size, - &buf->addr, GFP_KERNEL); - if (!buf->area) { - pr_err("Failed to allocate dma memory \ - Please increase uncached DMA memory region\n"); - return -ENOMEM; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + unsigned int sample_size = runtime->sample_bits / 8; + struct bf5xx_i2s_pcm_data *dma_data; + unsigned int i; + void *src, *dst; + + dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + + if (dma_data->tdm_mode) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + src = buf; + dst = runtime->dma_area; + dst += pos * sample_size * 8; + + while (count--) { + for (i = 0; i < runtime->channels; i++) { + memcpy(dst + dma_data->map[i] * + sample_size, src, sample_size); + src += sample_size; + } + dst += 8 * sample_size; + } + } else { + src = runtime->dma_area; + src += pos * sample_size * 8; + dst = buf; + + while (count--) { + for (i = 0; i < runtime->channels; i++) { + memcpy(dst, src + dma_data->map[i] * + sample_size, sample_size); + dst += sample_size; + } + src += 8 * sample_size; + } + } + } else { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + src = buf; + dst = runtime->dma_area; + dst += frames_to_bytes(runtime, pos); + } else { + src = runtime->dma_area; + src += frames_to_bytes(runtime, pos); + dst = buf; + } + + memcpy(dst, src, frames_to_bytes(runtime, count)); } - buf->bytes = size; - - 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; } -static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm) +static int bf5xx_pcm_silence(struct snd_pcm_substream *substream, + int channel, snd_pcm_uframes_t pos, snd_pcm_uframes_t count) { - struct snd_pcm_substream *substream; - struct snd_dma_buffer *buf; - int stream; - - for (stream = 0; stream < 2; stream++) { - substream = pcm->streams[stream].substream; - if (!substream) - continue; - - buf = &substream->dma_buffer; - if (!buf->area) - continue; - dma_free_coherent(NULL, buf->bytes, buf->area, 0); - buf->area = NULL; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + unsigned int sample_size = runtime->sample_bits / 8; + void *buf = runtime->dma_area; + struct bf5xx_i2s_pcm_data *dma_data; + unsigned int offset, size; + + dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + + if (dma_data->tdm_mode) { + offset = pos * 8 * sample_size; + size = count * 8 * sample_size; + } else { + offset = frames_to_bytes(runtime, pos); + size = frames_to_bytes(runtime, count); } - if (sport_handle) - sport_done(sport_handle); + + snd_pcm_format_set_silence(runtime->format, buf + offset, size); + + return 0; } -static u64 bf5xx_pcm_dmamask = DMA_32BIT_MASK; +static struct snd_pcm_ops bf5xx_pcm_i2s_ops = { + .open = bf5xx_pcm_open, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = bf5xx_pcm_hw_params, + .hw_free = bf5xx_pcm_hw_free, + .prepare = bf5xx_pcm_prepare, + .trigger = bf5xx_pcm_trigger, + .pointer = bf5xx_pcm_pointer, + .mmap = bf5xx_pcm_mmap, + .copy = bf5xx_pcm_copy, + .silence = bf5xx_pcm_silence, +}; -int bf5xx_pcm_i2s_new(struct snd_card *card, struct snd_soc_dai *dai, - struct snd_pcm *pcm) +static int bf5xx_pcm_i2s_new(struct snd_soc_pcm_runtime *rtd) { - int ret = 0; + struct snd_card *card = rtd->card->snd_card; + size_t size = bf5xx_pcm_hardware.buffer_bytes_max; + int ret; pr_debug("%s enter\n", __func__); - if (!card->dev->dma_mask) - card->dev->dma_mask = &bf5xx_pcm_dmamask; - if (!card->dev->coherent_dma_mask) - card->dev->coherent_dma_mask = DMA_32BIT_MASK; - - if (dai->playback.channels_min) { - ret = bf5xx_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_PLAYBACK); - if (ret) - goto out; - } + ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32)); + if (ret) + return ret; - if (dai->capture.channels_min) { - ret = bf5xx_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_CAPTURE); - if (ret) - goto out; - } - out: - return ret; + return snd_pcm_lib_preallocate_pages_for_all(rtd->pcm, + SNDRV_DMA_TYPE_DEV, card->dev, size, size); } -struct snd_soc_platform bf5xx_i2s_soc_platform = { - .name = "bf5xx-audio", - .pcm_ops = &bf5xx_pcm_i2s_ops, +static struct snd_soc_platform_driver bf5xx_i2s_soc_platform = { + .ops = &bf5xx_pcm_i2s_ops, .pcm_new = bf5xx_pcm_i2s_new, - .pcm_free = bf5xx_pcm_free_dma_buffers, }; -EXPORT_SYMBOL_GPL(bf5xx_i2s_soc_platform); -static int __init bfin_i2s_init(void) +static int bfin_i2s_soc_platform_probe(struct platform_device *pdev) { - return snd_soc_register_platform(&bf5xx_i2s_soc_platform); + return snd_soc_register_platform(&pdev->dev, &bf5xx_i2s_soc_platform); } -module_init(bfin_i2s_init); -static void __exit bfin_i2s_exit(void) +static int bfin_i2s_soc_platform_remove(struct platform_device *pdev) { - snd_soc_unregister_platform(&bf5xx_i2s_soc_platform); + snd_soc_unregister_platform(&pdev->dev); + return 0; } -module_exit(bfin_i2s_exit); + +static struct platform_driver bfin_i2s_pcm_driver = { + .driver = { + .name = "bfin-i2s-pcm-audio", + .owner = THIS_MODULE, + }, + + .probe = bfin_i2s_soc_platform_probe, + .remove = bfin_i2s_soc_platform_remove, +}; + +module_platform_driver(bfin_i2s_pcm_driver); MODULE_AUTHOR("Cliff Cai"); MODULE_DESCRIPTION("ADI Blackfin I2S PCM DMA module"); diff --git a/sound/soc/blackfin/bf5xx-i2s-pcm.h b/sound/soc/blackfin/bf5xx-i2s-pcm.h index 4d4609a97c5..1f0435249f8 100644 --- a/sound/soc/blackfin/bf5xx-i2s-pcm.h +++ b/sound/soc/blackfin/bf5xx-i2s-pcm.h @@ -1,29 +1,17 @@ /* - * linux/sound/arm/bf5xx-i2s-pcm.h -- ALSA PCM interface for the Blackfin - * - * Copyright 2007 Analog Device Inc. - * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ -#ifndef _BF5XX_I2S_PCM_H -#define _BF5XX_I2S_PCM_H +#ifndef _BF5XX_TDM_PCM_H +#define _BF5XX_TDM_PCM_H -struct bf5xx_pcm_dma_params { - char *name; /* stream identifier */ -}; +#define BFIN_TDM_DAI_MAX_SLOTS 8 -struct bf5xx_gpio { - u32 sys; - u32 rx; - u32 tx; - u32 clk; - u32 frm; +struct bf5xx_i2s_pcm_data { + unsigned int map[BFIN_TDM_DAI_MAX_SLOTS]; + bool tdm_mode; }; -/* platform data */ -extern struct snd_soc_platform bf5xx_i2s_soc_platform; - #endif diff --git a/sound/soc/blackfin/bf5xx-i2s.c b/sound/soc/blackfin/bf5xx-i2s.c index d1d95d2393f..39d774839b3 100644 --- a/sound/soc/blackfin/bf5xx-i2s.c +++ b/sound/soc/blackfin/bf5xx-i2s.c @@ -42,75 +42,48 @@ #include <linux/gpio.h> #include "bf5xx-sport.h" -#include "bf5xx-i2s.h" +#include "bf5xx-i2s-pcm.h" struct bf5xx_i2s_port { u16 tcr1; u16 rcr1; u16 tcr2; u16 rcr2; - int counter; -}; + int configured; -static struct bf5xx_i2s_port bf5xx_i2s; -static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM; + unsigned int slots; + unsigned int tx_mask; + unsigned int rx_mask; -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, - } + struct bf5xx_i2s_pcm_data tx_dma_data; + struct bf5xx_i2s_pcm_data rx_dma_data; }; -/* - * 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 - * ssm2602 using Port G and EMAC concurrently. - */ -#ifdef CONFIG_BF527_SPORT0_PORTF -#define LOCAL_SPORT0_TFS (P_SPORT0_TFS) -#else -#define LOCAL_SPORT0_TFS (0) -#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; break; default: - printk(KERN_ERR "%s: Unknown DAI format type\n", __func__); + dev_err(cpu_dai->dev, "%s: Unknown DAI format type\n", + __func__); ret = -EINVAL; break; } @@ -124,7 +97,8 @@ static int bf5xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, ret = -EINVAL; break; default: - printk(KERN_ERR "%s: Unknown DAI master type\n", __func__); + dev_err(cpu_dai->dev, "%s: Unknown DAI master type\n", + __func__); ret = -EINVAL; break; } @@ -132,43 +106,40 @@ static int bf5xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, return ret; } -static int bf5xx_i2s_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - pr_debug("%s enter\n", __func__); - - /*this counter is used for counting how many pcm streams are opened*/ - bf5xx_i2s.counter++; - return 0; -} - 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; + break; 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.counter == 1) { + 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 @@ -177,17 +148,18 @@ static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream, * * CPU DAI:slave mode. */ - 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"); + dev_err(dai->dev, "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"); + dev_err(dai->dev, "SPORT is busy!\n"); return -EBUSY; } } @@ -198,80 +170,111 @@ 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) { - pr_debug("%s enter\n", __func__); - bf5xx_i2s.counter--; + struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai); + struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data; + + dev_dbg(dai->dev, "%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 platform_device *pdev, - struct snd_soc_dai *dai) +static int bf5xx_i2s_set_channel_map(struct snd_soc_dai *dai, + unsigned int tx_num, unsigned int *tx_slot, + unsigned int rx_num, unsigned int *rx_slot) { - 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; + struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai); + struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data; + unsigned int tx_mapped = 0, rx_mapped = 0; + unsigned int slot; + int i; + + if ((tx_num > BFIN_TDM_DAI_MAX_SLOTS) || + (rx_num > BFIN_TDM_DAI_MAX_SLOTS)) + return -EINVAL; + + for (i = 0; i < tx_num; i++) { + slot = tx_slot[i]; + if ((slot < BFIN_TDM_DAI_MAX_SLOTS) && + (!(tx_mapped & (1 << slot)))) { + bf5xx_i2s->tx_dma_data.map[i] = slot; + tx_mapped |= 1 << slot; + } else + return -EINVAL; } - - /* 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; + for (i = 0; i < rx_num; i++) { + slot = rx_slot[i]; + if ((slot < BFIN_TDM_DAI_MAX_SLOTS) && + (!(rx_mapped & (1 << slot)))) { + bf5xx_i2s->rx_dma_data.map[i] = slot; + rx_mapped |= 1 << slot; + } else + return -EINVAL; } return 0; } -static void bf5xx_i2s_remove(struct snd_soc_dai *dai) +static int bf5xx_i2s_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int width) { - pr_debug("%s enter\n", __func__); - peripheral_free_list(&sport_req[sport_num][0]); + struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai); + struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data; + + if (slots % 8 != 0 || slots > 8) + return -EINVAL; + + if (width != 32) + return -EINVAL; + + bf5xx_i2s->slots = slots; + bf5xx_i2s->tx_mask = tx_mask; + bf5xx_i2s->rx_mask = rx_mask; + + bf5xx_i2s->tx_dma_data.tdm_mode = slots != 0; + bf5xx_i2s->rx_dma_data.tdm_mode = slots != 0; + + return sport_set_multichannel(sport_handle, slots, tx_mask, rx_mask, 0); } #ifdef CONFIG_PM static int bf5xx_i2s_suspend(struct snd_soc_dai *dai) { - struct sport_device *sport = - (struct sport_device *)dai->private_data; + struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai); - pr_debug("%s : sport %d\n", __func__, dai->id); - if (!dai->active) - return 0; - if (dai->capture.active) - sport_rx_stop(sport); - if (dai->playback.active) - sport_tx_stop(sport); + dev_dbg(dai->dev, "%s : sport %d\n", __func__, dai->id); + + if (dai->capture_active) + sport_rx_stop(sport_handle); + if (dai->playback_active) + sport_tx_stop(sport_handle); return 0; } -static int bf5xx_i2s_resume(struct platform_device *pdev, - 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; - struct sport_device *sport = - (struct sport_device *)dai->private_data; - pr_debug("%s : sport %d\n", __func__, dai->id); - if (!dai->active) - return 0; + dev_dbg(dai->dev, "%s : sport %d\n", __func__, dai->id); - ret = sport_config_rx(sport_handle, RFSR | RCKFE, RSFSE|0x1f, 0, 0); + ret = sport_config_rx(sport_handle, bf5xx_i2s->rcr1, + bf5xx_i2s->rcr2, 0, 0); if (ret) { - pr_err("SPORT is busy!\n"); + dev_err(dai->dev, "SPORT is busy!\n"); return -EBUSY; } - ret = sport_config_tx(sport_handle, TFSR | TCKFE, TSFSE|0x1f, 0, 0); + ret = sport_config_tx(sport_handle, bf5xx_i2s->tcr1, + bf5xx_i2s->tcr2, 0, 0); if (ret) { - pr_err("SPORT is busy!\n"); + dev_err(dai->dev, "SPORT is busy!\n"); return -EBUSY; } - if (dai->capture.active) - sport_rx_start(sport); - if (dai->playback.active) - sport_tx_start(sport); - return 0; + return sport_set_multichannel(sport_handle, bf5xx_i2s->slots, + bf5xx_i2s->tx_mask, bf5xx_i2s->rx_mask, 0); } #else @@ -279,51 +282,108 @@ static int bf5xx_i2s_resume(struct platform_device *pdev, #define bf5xx_i2s_resume NULL #endif +static int bf5xx_i2s_dai_probe(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; + unsigned int i; + + for (i = 0; i < BFIN_TDM_DAI_MAX_SLOTS; i++) { + bf5xx_i2s->tx_dma_data.map[i] = i; + bf5xx_i2s->rx_dma_data.map[i] = i; + } + + dai->playback_dma_data = &bf5xx_i2s->tx_dma_data; + dai->capture_dma_data = &bf5xx_i2s->rx_dma_data; + + return 0; +} + #define BF5XX_I2S_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \ SNDRV_PCM_RATE_96000) -#define BF5XX_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |\ - SNDRV_PCM_FMTBIT_S32_LE) +#define BF5XX_I2S_FORMATS \ + (SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops bf5xx_i2s_dai_ops = { + .shutdown = bf5xx_i2s_shutdown, + .hw_params = bf5xx_i2s_hw_params, + .set_fmt = bf5xx_i2s_set_dai_fmt, + .set_tdm_slot = bf5xx_i2s_set_tdm_slot, + .set_channel_map = bf5xx_i2s_set_channel_map, +}; -struct snd_soc_dai bf5xx_i2s_dai = { - .name = "bf5xx-i2s", - .id = 0, - .probe = bf5xx_i2s_probe, - .remove = bf5xx_i2s_remove, +static struct snd_soc_dai_driver bf5xx_i2s_dai = { + .probe = bf5xx_i2s_dai_probe, .suspend = bf5xx_i2s_suspend, .resume = bf5xx_i2s_resume, .playback = { - .channels_min = 1, - .channels_max = 2, + .channels_min = 2, + .channels_max = 8, .rates = BF5XX_I2S_RATES, .formats = BF5XX_I2S_FORMATS,}, .capture = { - .channels_min = 1, - .channels_max = 2, + .channels_min = 2, + .channels_max = 8, .rates = BF5XX_I2S_RATES, .formats = BF5XX_I2S_FORMATS,}, - .ops = { - .startup = bf5xx_i2s_startup, - .shutdown = bf5xx_i2s_shutdown, - .hw_params = bf5xx_i2s_hw_params, - .set_fmt = bf5xx_i2s_set_dai_fmt, - }, + .ops = &bf5xx_i2s_dai_ops, +}; + +static const struct snd_soc_component_driver bf5xx_i2s_component = { + .name = "bf5xx-i2s", }; -EXPORT_SYMBOL_GPL(bf5xx_i2s_dai); -static int __init bfin_i2s_init(void) +static int bf5xx_i2s_probe(struct platform_device *pdev) { - return snd_soc_register_dai(&bf5xx_i2s_dai); + struct sport_device *sport_handle; + int ret; + + /* configure SPORT for I2S */ + sport_handle = sport_init(pdev, 4, 8 * sizeof(u32), + sizeof(struct bf5xx_i2s_port)); + if (!sport_handle) + return -ENODEV; + + /* register with the ASoC layers */ + ret = snd_soc_register_component(&pdev->dev, &bf5xx_i2s_component, + &bf5xx_i2s_dai, 1); + if (ret) { + dev_err(&pdev->dev, "Failed to register DAI: %d\n", ret); + sport_done(sport_handle); + return ret; + } + + return 0; } -module_init(bfin_i2s_init); -static void __exit bfin_i2s_exit(void) +static int bf5xx_i2s_remove(struct platform_device *pdev) { - snd_soc_unregister_dai(&bf5xx_i2s_dai); + struct sport_device *sport_handle = platform_get_drvdata(pdev); + + dev_dbg(&pdev->dev, "%s enter\n", __func__); + + snd_soc_unregister_component(&pdev->dev); + sport_done(sport_handle); + + return 0; } -module_exit(bfin_i2s_exit); + +static struct platform_driver bfin_i2s_driver = { + .probe = bf5xx_i2s_probe, + .remove = bf5xx_i2s_remove, + .driver = { + .name = "bfin-i2s", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(bfin_i2s_driver); /* Module information */ MODULE_AUTHOR("Cliff Cai"); diff --git a/sound/soc/blackfin/bf5xx-i2s.h b/sound/soc/blackfin/bf5xx-i2s.h deleted file mode 100644 index 7107d1a0b06..00000000000 --- a/sound/soc/blackfin/bf5xx-i2s.h +++ /dev/null @@ -1,14 +0,0 @@ -/* - * linux/sound/arm/bf5xx-i2s.h - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef _BF5XX_I2S_H -#define _BF5XX_I2S_H - -extern struct snd_soc_dai bf5xx_i2s_dai; - -#endif diff --git a/sound/soc/blackfin/bf5xx-sport.c b/sound/soc/blackfin/bf5xx-sport.c index 3b99e484d55..9dfa1241ea6 100644 --- a/sound/soc/blackfin/bf5xx-sport.c +++ b/sound/soc/blackfin/bf5xx-sport.c @@ -33,6 +33,7 @@ #include <linux/dma-mapping.h> #include <linux/gpio.h> #include <linux/bug.h> +#include <linux/module.h> #include <asm/portmux.h> #include <asm/dma.h> #include <asm/blackfin.h> @@ -42,15 +43,13 @@ /* 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, - int tdm_count, u32 mask, int packed) + int tdm_count, u32 tx_mask, u32 rx_mask, int packed) { - pr_debug("%s tdm_count=%d mask:0x%08x packed=%d\n", __func__, - tdm_count, mask, packed); + pr_debug("%s tdm_count=%d tx_mask:0x%08x rx_mask:0x%08x packed=%d\n", + __func__, tdm_count, tx_mask, rx_mask, packed); if ((sport->regs->tcr1 & TSPEN) || (sport->regs->rcr1 & RSPEN)) return -EBUSY; @@ -66,8 +65,8 @@ int sport_set_multichannel(struct sport_device *sport, sport->regs->mcmc2 = FRAME_DELAY | MCMEN | \ (packed ? (MCDTXPE|MCDRXPE) : 0); - sport->regs->mtcs0 = mask; - sport->regs->mrcs0 = mask; + sport->regs->mtcs0 = tx_mask; + sport->regs->mrcs0 = rx_mask; sport->regs->mtcs1 = 0; sport->regs->mrcs1 = 0; sport->regs->mtcs2 = 0; @@ -133,7 +132,7 @@ static void setup_desc(struct dmasg *desc, void *buf, int fragcount, int i; for (i = 0; i < fragcount; ++i) { - desc[i].next_desc_addr = (unsigned long)&(desc[i + 1]); + desc[i].next_desc_addr = &(desc[i + 1]); desc[i].start_addr = (unsigned long)buf + i*fragsize; desc[i].cfg = cfg; desc[i].x_count = x_count; @@ -143,12 +142,12 @@ static void setup_desc(struct dmasg *desc, void *buf, int fragcount, } /* make circular */ - desc[fragcount-1].next_desc_addr = (unsigned long)desc; + desc[fragcount-1].next_desc_addr = desc; - pr_debug("setup desc: desc0=%p, next0=%lx, desc1=%p," - "next1=%lx\nx_count=%x,y_count=%x,addr=0x%lx,cfs=0x%x\n", - &(desc[0]), desc[0].next_desc_addr, - &(desc[1]), desc[1].next_desc_addr, + pr_debug("setup desc: desc0=%p, next0=%p, desc1=%p," + "next1=%p\nx_count=%x,y_count=%x,addr=0x%lx,cfs=0x%x\n", + desc, desc[0].next_desc_addr, + desc+1, desc[1].next_desc_addr, desc[0].x_count, desc[0].y_count, desc[0].start_addr, desc[0].cfg); } @@ -180,26 +179,25 @@ static inline int sport_hook_rx_dummy(struct sport_device *sport) struct dmasg *desc, temp_desc; unsigned long flags; - BUG_ON(sport->dummy_rx_desc == NULL); - BUG_ON(sport->curr_rx_desc == sport->dummy_rx_desc); + if (WARN_ON(!sport->dummy_rx_desc) || + WARN_ON(sport->curr_rx_desc == sport->dummy_rx_desc)) + return -EINVAL; /* Maybe the dummy buffer descriptor ring is damaged */ - sport->dummy_rx_desc->next_desc_addr = \ - (unsigned long)(sport->dummy_rx_desc+1); + sport->dummy_rx_desc->next_desc_addr = sport->dummy_rx_desc + 1; local_irq_save(flags); - desc = (struct dmasg *)get_dma_next_desc_ptr(sport->dma_rx_chan); + desc = get_dma_next_desc_ptr(sport->dma_rx_chan); /* Copy the descriptor which will be damaged to backup */ temp_desc = *desc; - desc->x_count = 0xa; + desc->x_count = sport->dummy_count / 2; desc->y_count = 0; - desc->next_desc_addr = (unsigned long)(sport->dummy_rx_desc); + desc->next_desc_addr = sport->dummy_rx_desc; local_irq_restore(flags); /* Waiting for dummy buffer descriptor is already hooked*/ while ((get_dma_curr_desc_ptr(sport->dma_rx_chan) - - sizeof(struct dmasg)) != - (unsigned long)sport->dummy_rx_desc) - ; + sizeof(struct dmasg)) != sport->dummy_rx_desc) + continue; sport->curr_rx_desc = sport->dummy_rx_desc; /* Restore the damaged descriptor */ *desc = temp_desc; @@ -210,14 +208,12 @@ static inline int sport_hook_rx_dummy(struct sport_device *sport) static inline int sport_rx_dma_start(struct sport_device *sport, int dummy) { if (dummy) { - sport->dummy_rx_desc->next_desc_addr = \ - (unsigned long) sport->dummy_rx_desc; + sport->dummy_rx_desc->next_desc_addr = sport->dummy_rx_desc; sport->curr_rx_desc = sport->dummy_rx_desc; } else sport->curr_rx_desc = sport->dma_rx_desc; - set_dma_next_desc_addr(sport->dma_rx_chan, \ - (unsigned long)(sport->curr_rx_desc)); + set_dma_next_desc_addr(sport->dma_rx_chan, sport->curr_rx_desc); set_dma_x_count(sport->dma_rx_chan, 0); set_dma_x_modify(sport->dma_rx_chan, 0); set_dma_config(sport->dma_rx_chan, (DMAFLOW_LARGE | NDSIZE_9 | \ @@ -231,14 +227,12 @@ static inline int sport_rx_dma_start(struct sport_device *sport, int dummy) static inline int sport_tx_dma_start(struct sport_device *sport, int dummy) { if (dummy) { - sport->dummy_tx_desc->next_desc_addr = \ - (unsigned long) sport->dummy_tx_desc; + sport->dummy_tx_desc->next_desc_addr = sport->dummy_tx_desc; sport->curr_tx_desc = sport->dummy_tx_desc; } else sport->curr_tx_desc = sport->dma_tx_desc; - set_dma_next_desc_addr(sport->dma_tx_chan, \ - (unsigned long)(sport->curr_tx_desc)); + set_dma_next_desc_addr(sport->dma_tx_chan, sport->curr_tx_desc); set_dma_x_count(sport->dma_tx_chan, 0); set_dma_x_modify(sport->dma_tx_chan, 0); set_dma_config(sport->dma_tx_chan, @@ -257,15 +251,14 @@ int sport_rx_start(struct sport_device *sport) return -EBUSY; if (sport->tx_run) { /* tx is running, rx is not running */ - BUG_ON(sport->dma_rx_desc == NULL); - BUG_ON(sport->curr_rx_desc != sport->dummy_rx_desc); + if (WARN_ON(!sport->dma_rx_desc) || + WARN_ON(sport->curr_rx_desc != sport->dummy_rx_desc)) + return -EINVAL; local_irq_save(flags); while ((get_dma_curr_desc_ptr(sport->dma_rx_chan) - - sizeof(struct dmasg)) != - (unsigned long)sport->dummy_rx_desc) - ; - sport->dummy_rx_desc->next_desc_addr = - (unsigned long)(sport->dma_rx_desc); + sizeof(struct dmasg)) != sport->dummy_rx_desc) + continue; + sport->dummy_rx_desc->next_desc_addr = sport->dma_rx_desc; local_irq_restore(flags); sport->curr_rx_desc = sport->dma_rx_desc; } else { @@ -307,26 +300,25 @@ static inline int sport_hook_tx_dummy(struct sport_device *sport) struct dmasg *desc, temp_desc; unsigned long flags; - BUG_ON(sport->dummy_tx_desc == NULL); - BUG_ON(sport->curr_tx_desc == sport->dummy_tx_desc); + if (WARN_ON(!sport->dummy_tx_desc) || + WARN_ON(sport->curr_tx_desc == sport->dummy_tx_desc)) + return -EINVAL; - sport->dummy_tx_desc->next_desc_addr = \ - (unsigned long)(sport->dummy_tx_desc+1); + sport->dummy_tx_desc->next_desc_addr = sport->dummy_tx_desc + 1; /* Shorten the time on last normal descriptor */ local_irq_save(flags); - desc = (struct dmasg *)get_dma_next_desc_ptr(sport->dma_tx_chan); + desc = get_dma_next_desc_ptr(sport->dma_tx_chan); /* Store the descriptor which will be damaged */ temp_desc = *desc; - desc->x_count = 0xa; + desc->x_count = sport->dummy_count / 2; desc->y_count = 0; - desc->next_desc_addr = (unsigned long)(sport->dummy_tx_desc); + desc->next_desc_addr = sport->dummy_tx_desc; local_irq_restore(flags); /* Waiting for dummy buffer descriptor is already hooked*/ while ((get_dma_curr_desc_ptr(sport->dma_tx_chan) - \ - sizeof(struct dmasg)) != \ - (unsigned long)sport->dummy_tx_desc) - ; + sizeof(struct dmasg)) != sport->dummy_tx_desc) + continue; sport->curr_tx_desc = sport->dummy_tx_desc; /* Restore the damaged descriptor */ *desc = temp_desc; @@ -336,22 +328,21 @@ static inline int sport_hook_tx_dummy(struct sport_device *sport) int sport_tx_start(struct sport_device *sport) { - unsigned flags; + unsigned long flags; pr_debug("%s: tx_run:%d, rx_run:%d\n", __func__, sport->tx_run, sport->rx_run); if (sport->tx_run) return -EBUSY; if (sport->rx_run) { - BUG_ON(sport->dma_tx_desc == NULL); - BUG_ON(sport->curr_tx_desc != sport->dummy_tx_desc); + if (WARN_ON(!sport->dma_tx_desc) || + WARN_ON(sport->curr_tx_desc != sport->dummy_tx_desc)) + return -EINVAL; /* Hook the normal buffer descriptor */ local_irq_save(flags); while ((get_dma_curr_desc_ptr(sport->dma_tx_chan) - - sizeof(struct dmasg)) != - (unsigned long)sport->dummy_tx_desc) - ; - sport->dummy_tx_desc->next_desc_addr = - (unsigned long)(sport->dma_tx_desc); + sizeof(struct dmasg)) != sport->dummy_tx_desc) + continue; + sport->dummy_tx_desc->next_desc_addr = sport->dma_tx_desc; local_irq_restore(flags); sport->curr_tx_desc = sport->dma_tx_desc; } else { @@ -536,19 +527,17 @@ static int sport_config_rx_dummy(struct sport_device *sport) unsigned config; pr_debug("%s entered\n", __func__); -#if L1_DATA_A_LENGTH != 0 - desc = (struct dmasg *) l1_data_sram_alloc(2 * sizeof(*desc)); -#else - { + if (L1_DATA_A_LENGTH) + desc = l1_data_sram_zalloc(2 * sizeof(*desc)); + else { dma_addr_t addr; desc = dma_alloc_coherent(NULL, 2 * sizeof(*desc), &addr, 0); + memset(desc, 0, 2 * sizeof(*desc)); } -#endif if (desc == NULL) { pr_err("Failed to allocate memory for dummy rx desc\n"); return -ENOMEM; } - memset(desc, 0, 2 * sizeof(*desc)); sport->dummy_rx_desc = desc; desc->start_addr = (unsigned long)sport->dummy_buf; config = DMAFLOW_LARGE | NDSIZE_9 | compute_wdsize(sport->wdsize) @@ -559,8 +548,8 @@ static int sport_config_rx_dummy(struct sport_device *sport) desc->y_count = 0; desc->y_modify = 0; memcpy(desc+1, desc, sizeof(*desc)); - desc->next_desc_addr = (unsigned long)(desc+1); - desc[1].next_desc_addr = (unsigned long)desc; + desc->next_desc_addr = desc + 1; + desc[1].next_desc_addr = desc; return 0; } @@ -571,19 +560,17 @@ static int sport_config_tx_dummy(struct sport_device *sport) pr_debug("%s entered\n", __func__); -#if L1_DATA_A_LENGTH != 0 - desc = (struct dmasg *) l1_data_sram_alloc(2 * sizeof(*desc)); -#else - { + if (L1_DATA_A_LENGTH) + desc = l1_data_sram_zalloc(2 * sizeof(*desc)); + else { dma_addr_t addr; desc = dma_alloc_coherent(NULL, 2 * sizeof(*desc), &addr, 0); + memset(desc, 0, 2 * sizeof(*desc)); } -#endif if (!desc) { pr_err("Failed to allocate memory for dummy tx desc\n"); return -ENOMEM; } - memset(desc, 0, 2 * sizeof(*desc)); sport->dummy_tx_desc = desc; desc->start_addr = (unsigned long)sport->dummy_buf + \ sport->dummy_count; @@ -595,8 +582,8 @@ static int sport_config_tx_dummy(struct sport_device *sport) desc->y_count = 0; desc->y_modify = 0; memcpy(desc+1, desc, sizeof(*desc)); - desc->next_desc_addr = (unsigned long)(desc+1); - desc[1].next_desc_addr = (unsigned long)desc; + desc->next_desc_addr = desc + 1; + desc[1].next_desc_addr = desc; return 0; } @@ -784,7 +771,8 @@ static irqreturn_t err_handler(int irq, void *dev_id) int sport_set_rx_callback(struct sport_device *sport, void (*rx_callback)(void *), void *rx_data) { - BUG_ON(rx_callback == NULL); + if (WARN_ON(!rx_callback)) + return -EINVAL; sport->rx_callback = rx_callback; sport->rx_data = rx_data; @@ -795,7 +783,8 @@ EXPORT_SYMBOL(sport_set_rx_callback); int sport_set_tx_callback(struct sport_device *sport, void (*tx_callback)(void *), void *tx_data) { - BUG_ON(tx_callback == NULL); + if (WARN_ON(!tx_callback)) + return -EINVAL; sport->tx_callback = tx_callback; sport->tx_data = tx_data; @@ -806,7 +795,8 @@ EXPORT_SYMBOL(sport_set_tx_callback); int sport_set_err_callback(struct sport_device *sport, void (*err_callback)(void *), void *err_data) { - BUG_ON(err_callback == NULL); + if (WARN_ON(!err_callback)) + return -EINVAL; sport->err_callback = err_callback; sport->err_data = err_data; @@ -814,88 +804,165 @@ 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; + if (WARN_ON(param.wdsize == 0 || param.dummy_count == 0)) return NULL; + + 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; + } + + sport = kzalloc(sizeof(*sport), GFP_KERNEL); + if (!sport) { + dev_err(dev, "failed to allocate for sport device\n"); + goto __init_err0; } - 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->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; -#if L1_DATA_A_LENGTH != 0 - sport->dummy_buf = l1_data_sram_alloc(dummy_count * 2); -#else - sport->dummy_buf = kmalloc(dummy_count * 2, GFP_KERNEL); -#endif + 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(param.dummy_count * 2); + else + 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; } - memset(sport->dummy_buf, 0, dummy_count * 2); 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); @@ -903,6 +970,8 @@ __init_err2: free_dma(sport->dma_rx_chan); __init_err1: kfree(sport); +__init_err0: + peripheral_free_list(param.pin_req); return NULL; } EXPORT_SYMBOL(sport_init); @@ -935,10 +1004,12 @@ 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); + /* * It is only used to send several bytes when dma is not enabled * sport controller is configured but not enabled. @@ -1029,4 +1100,3 @@ EXPORT_SYMBOL(sport_send_and_recv); MODULE_AUTHOR("Roy Huang"); MODULE_DESCRIPTION("SPORT driver for ADI Blackfin"); MODULE_LICENSE("GPL"); - diff --git a/sound/soc/blackfin/bf5xx-sport.h b/sound/soc/blackfin/bf5xx-sport.h index 2e63dea73e9..9fc2192feb3 100644 --- a/sound/soc/blackfin/bf5xx-sport.h +++ b/sound/soc/blackfin/bf5xx-sport.h @@ -1,5 +1,5 @@ /* - * File: bf5xx_ac97_sport.h + * File: bf5xx_sport.h * Based on: * Author: Roy Huang <roy.huang@analog.com> * @@ -33,41 +33,18 @@ #include <linux/types.h> #include <linux/wait.h> #include <linux/workqueue.h> +#include <linux/platform_device.h> #include <asm/dma.h> - -struct sport_register { - u16 tcr1; u16 reserved0; - u16 tcr2; u16 reserved1; - u16 tclkdiv; u16 reserved2; - u16 tfsdiv; u16 reserved3; - u32 tx; - u32 reserved_l0; - u32 rx; - u32 reserved_l1; - u16 rcr1; u16 reserved4; - u16 rcr2; u16 reserved5; - u16 rclkdiv; u16 reserved6; - u16 rfsdiv; u16 reserved7; - u16 stat; u16 reserved8; - u16 chnl; u16 reserved9; - u16 mcmc1; u16 reserved10; - u16 mcmc2; u16 reserved11; - u32 mtcs0; - u32 mtcs1; - u32 mtcs2; - u32 mtcs3; - u32 mrcs0; - u32 mrcs1; - u32 mrcs2; - u32 mrcs3; -}; +#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; @@ -129,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); @@ -148,7 +128,7 @@ void sport_done(struct sport_device *sport); /* note: multichannel is in units of 8 channels, tdm_count is number of channels * NOT / 8 ! all channels are enabled by default */ int sport_set_multichannel(struct sport_device *sport, int tdm_count, - u32 mask, int packed); + u32 tx_mask, u32 rx_mask, int packed); int sport_config_rx(struct sport_device *sport, unsigned int rcr1, unsigned int rcr2, diff --git a/sound/soc/blackfin/bf5xx-ssm2602.c b/sound/soc/blackfin/bf5xx-ssm2602.c index bc0cdded711..9c19ccc936e 100644 --- a/sound/soc/blackfin/bf5xx-ssm2602.c +++ b/sound/soc/blackfin/bf5xx-ssm2602.c @@ -33,7 +33,6 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/pcm_params.h> #include <asm/dma.h> @@ -41,32 +40,11 @@ #include <linux/gpio.h> #include "../codecs/ssm2602.h" #include "bf5xx-sport.h" -#include "bf5xx-i2s-pcm.h" -#include "bf5xx-i2s.h" static struct snd_soc_card bf5xx_ssm2602; -static int bf5xx_ssm2602_startup(struct snd_pcm_substream *substream) +static int bf5xx_ssm2602_dai_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; - - pr_debug("%s enter\n", __func__); - cpu_dai->private_data = sport_handle; - return 0; -} - -static int bf5xx_ssm2602_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; - unsigned int clk = 0; - int ret = 0; - - pr_debug("%s rate %d format %x\n", __func__, params_rate(params), - params_format(params)); /* * If you are using a crystal source which frequency is not 12MHz * then modify the below case statement with frequency of the crystal. @@ -74,98 +52,60 @@ static int bf5xx_ssm2602_hw_params(struct snd_pcm_substream *substream, * If you are using the SPORT to generate clocking then this is * where to do it. */ - - switch (params_rate(params)) { - case 8000: - case 16000: - case 48000: - case 96000: - case 11025: - case 22050: - case 44100: - clk = 12000000; - break; - } - - /* - * CODEC is master for BCLK and LRC in this configuration. - */ - - /* set codec DAI configuration */ - ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); - if (ret < 0) - return ret; - /* set cpu DAI configuration */ - ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); - if (ret < 0) - return ret; - - ret = snd_soc_dai_set_sysclk(codec_dai, SSM2602_SYSCLK, clk, + return snd_soc_dai_set_sysclk(rtd->codec_dai, SSM2602_SYSCLK, 12000000, SND_SOC_CLOCK_IN); - if (ret < 0) - return ret; - - return 0; } -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 = &bf5xx_i2s_dai, - .codec_dai = &ssm2602_dai, - .ops = &bf5xx_ssm2602_ops, -}; - -/* - * SSM2602 2 wire address is determined by CSB - * state during powerup. - * low = 0x1a - * high = 0x1b - */ - -static struct ssm2602_setup_data bf5xx_ssm2602_setup = { - .i2c_bus = 0, - .i2c_address = 0x1b, +/* CODEC is master for BCLK and LRC in this configuration. */ +#define BF5XX_SSM2602_DAIFMT (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | \ + SND_SOC_DAIFMT_CBM_CFM) + +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", + .init = bf5xx_ssm2602_dai_init, + .dai_fmt = BF5XX_SSM2602_DAIFMT, + }, + { + .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", + .init = bf5xx_ssm2602_dai_init, + .dai_fmt = BF5XX_SSM2602_DAIFMT, + }, }; static struct snd_soc_card bf5xx_ssm2602 = { - .name = "bf5xx_ssm2602", - .platform = &bf5xx_i2s_soc_platform, - .dai_link = &bf5xx_ssm2602_dai, + .name = "bfin-ssm2602", + .owner = THIS_MODULE, + .dai_link = &bf5xx_ssm2602_dai[CONFIG_SND_BF5XX_SPORT_NUM], .num_links = 1, }; -static struct snd_soc_device bf5xx_ssm2602_snd_devdata = { - .card = &bf5xx_ssm2602, - .codec_dev = &soc_codec_dev_ssm2602, - .codec_data = &bf5xx_ssm2602_setup, -}; - -static struct platform_device *bf52x_ssm2602_snd_device; +static struct platform_device *bf5xx_ssm2602_snd_device; static int __init bf5xx_ssm2602_init(void) { int ret; pr_debug("%s enter\n", __func__); - bf52x_ssm2602_snd_device = platform_device_alloc("soc-audio", -1); - if (!bf52x_ssm2602_snd_device) + bf5xx_ssm2602_snd_device = platform_device_alloc("soc-audio", -1); + if (!bf5xx_ssm2602_snd_device) return -ENOMEM; - platform_set_drvdata(bf52x_ssm2602_snd_device, - &bf5xx_ssm2602_snd_devdata); - bf5xx_ssm2602_snd_devdata.dev = &bf52x_ssm2602_snd_device->dev; - ret = platform_device_add(bf52x_ssm2602_snd_device); + platform_set_drvdata(bf5xx_ssm2602_snd_device, &bf5xx_ssm2602); + ret = platform_device_add(bf5xx_ssm2602_snd_device); if (ret) - platform_device_put(bf52x_ssm2602_snd_device); + platform_device_put(bf5xx_ssm2602_snd_device); return ret; } @@ -173,7 +113,7 @@ static int __init bf5xx_ssm2602_init(void) static void __exit bf5xx_ssm2602_exit(void) { pr_debug("%s enter\n", __func__); - platform_device_unregister(bf52x_ssm2602_snd_device); + platform_device_unregister(bf5xx_ssm2602_snd_device); } module_init(bf5xx_ssm2602_init); diff --git a/sound/soc/blackfin/bf6xx-i2s.c b/sound/soc/blackfin/bf6xx-i2s.c new file mode 100644 index 00000000000..5810a0603f2 --- /dev/null +++ b/sound/soc/blackfin/bf6xx-i2s.c @@ -0,0 +1,240 @@ +/* + * bf6xx-i2s.c - Analog Devices BF6XX i2s interface driver + * + * Copyright (c) 2012 Analog Devices Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/device.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> + +#include "bf6xx-sport.h" + +struct sport_params param; + +static int bfin_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, + unsigned int fmt) +{ + struct sport_device *sport = snd_soc_dai_get_drvdata(cpu_dai); + struct device *dev = &sport->pdev->dev; + int ret = 0; + + param.spctl &= ~(SPORT_CTL_OPMODE | SPORT_CTL_CKRE | SPORT_CTL_FSR + | SPORT_CTL_LFS | SPORT_CTL_LAFS); + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + param.spctl |= SPORT_CTL_OPMODE | SPORT_CTL_CKRE + | SPORT_CTL_LFS; + break; + case SND_SOC_DAIFMT_DSP_A: + param.spctl |= SPORT_CTL_FSR; + break; + case SND_SOC_DAIFMT_LEFT_J: + param.spctl |= SPORT_CTL_OPMODE | SPORT_CTL_LFS + | SPORT_CTL_LAFS; + break; + default: + dev_err(dev, "%s: Unknown DAI format type\n", __func__); + ret = -EINVAL; + break; + } + + param.spctl &= ~(SPORT_CTL_ICLK | SPORT_CTL_IFS); + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + break; + case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBM_CFS: + case SND_SOC_DAIFMT_CBS_CFM: + ret = -EINVAL; + break; + default: + dev_err(dev, "%s: Unknown DAI master type\n", __func__); + ret = -EINVAL; + break; + } + + return ret; +} + +static int bfin_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct sport_device *sport = snd_soc_dai_get_drvdata(dai); + struct device *dev = &sport->pdev->dev; + int ret = 0; + + param.spctl &= ~SPORT_CTL_SLEN; + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S8: + param.spctl |= 0x70; + sport->wdsize = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + param.spctl |= 0xf0; + sport->wdsize = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + param.spctl |= 0x170; + sport->wdsize = 3; + break; + case SNDRV_PCM_FORMAT_S32_LE: + param.spctl |= 0x1f0; + sport->wdsize = 4; + break; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = sport_set_tx_params(sport, ¶m); + if (ret) { + dev_err(dev, "SPORT tx is busy!\n"); + return ret; + } + } else { + ret = sport_set_rx_params(sport, ¶m); + if (ret) { + dev_err(dev, "SPORT rx is busy!\n"); + return ret; + } + } + return 0; +} + +#ifdef CONFIG_PM +static int bfin_i2s_suspend(struct snd_soc_dai *dai) +{ + struct sport_device *sport = snd_soc_dai_get_drvdata(dai); + + if (dai->capture_active) + sport_rx_stop(sport); + if (dai->playback_active) + sport_tx_stop(sport); + return 0; +} + +static int bfin_i2s_resume(struct snd_soc_dai *dai) +{ + struct sport_device *sport = snd_soc_dai_get_drvdata(dai); + struct device *dev = &sport->pdev->dev; + int ret; + + ret = sport_set_tx_params(sport, ¶m); + if (ret) { + dev_err(dev, "SPORT tx is busy!\n"); + return ret; + } + ret = sport_set_rx_params(sport, ¶m); + if (ret) { + dev_err(dev, "SPORT rx is busy!\n"); + return ret; + } + + return 0; +} + +#else +#define bfin_i2s_suspend NULL +#define bfin_i2s_resume NULL +#endif + +#define BFIN_I2S_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \ + SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_96000) + +#define BFIN_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_ops bfin_i2s_dai_ops = { + .hw_params = bfin_i2s_hw_params, + .set_fmt = bfin_i2s_set_dai_fmt, +}; + +static struct snd_soc_dai_driver bfin_i2s_dai = { + .suspend = bfin_i2s_suspend, + .resume = bfin_i2s_resume, + .playback = { + .channels_min = 1, + .channels_max = 2, + .rates = BFIN_I2S_RATES, + .formats = BFIN_I2S_FORMATS, + }, + .capture = { + .channels_min = 1, + .channels_max = 2, + .rates = BFIN_I2S_RATES, + .formats = BFIN_I2S_FORMATS, + }, + .ops = &bfin_i2s_dai_ops, +}; + +static const struct snd_soc_component_driver bfin_i2s_component = { + .name = "bfin-i2s", +}; + +static int bfin_i2s_probe(struct platform_device *pdev) +{ + struct sport_device *sport; + struct device *dev = &pdev->dev; + int ret; + + sport = sport_create(pdev); + if (!sport) + return -ENODEV; + + /* register with the ASoC layers */ + ret = snd_soc_register_component(dev, &bfin_i2s_component, + &bfin_i2s_dai, 1); + if (ret) { + dev_err(dev, "Failed to register DAI: %d\n", ret); + sport_delete(sport); + return ret; + } + platform_set_drvdata(pdev, sport); + + return 0; +} + +static int bfin_i2s_remove(struct platform_device *pdev) +{ + struct sport_device *sport = platform_get_drvdata(pdev); + + snd_soc_unregister_component(&pdev->dev); + sport_delete(sport); + + return 0; +} + +static struct platform_driver bfin_i2s_driver = { + .probe = bfin_i2s_probe, + .remove = bfin_i2s_remove, + .driver = { + .name = "bfin-i2s", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(bfin_i2s_driver); + +MODULE_DESCRIPTION("Analog Devices BF6XX i2s interface driver"); +MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/blackfin/bf6xx-sport.c b/sound/soc/blackfin/bf6xx-sport.c new file mode 100644 index 00000000000..dfb744381c4 --- /dev/null +++ b/sound/soc/blackfin/bf6xx-sport.c @@ -0,0 +1,429 @@ +/* + * bf6xx_sport.c Analog Devices BF6XX SPORT driver + * + * Copyright (c) 2012 Analog Devices Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include <asm/blackfin.h> +#include <asm/dma.h> +#include <asm/portmux.h> + +#include "bf6xx-sport.h" + +int sport_set_tx_params(struct sport_device *sport, + struct sport_params *params) +{ + if (sport->tx_regs->spctl & SPORT_CTL_SPENPRI) + return -EBUSY; + sport->tx_regs->spctl = params->spctl | SPORT_CTL_SPTRAN; + sport->tx_regs->div = params->div; + SSYNC(); + return 0; +} +EXPORT_SYMBOL(sport_set_tx_params); + +int sport_set_rx_params(struct sport_device *sport, + struct sport_params *params) +{ + if (sport->rx_regs->spctl & SPORT_CTL_SPENPRI) + return -EBUSY; + sport->rx_regs->spctl = params->spctl & ~SPORT_CTL_SPTRAN; + sport->rx_regs->div = params->div; + SSYNC(); + return 0; +} +EXPORT_SYMBOL(sport_set_rx_params); + +static int compute_wdsize(size_t wdsize) +{ + switch (wdsize) { + case 1: + return WDSIZE_8 | PSIZE_8; + case 2: + return WDSIZE_16 | PSIZE_16; + default: + return WDSIZE_32 | PSIZE_32; + } +} + +void sport_tx_start(struct sport_device *sport) +{ + set_dma_next_desc_addr(sport->tx_dma_chan, sport->tx_desc); + set_dma_config(sport->tx_dma_chan, DMAFLOW_LIST | DI_EN + | compute_wdsize(sport->wdsize) | NDSIZE_6); + enable_dma(sport->tx_dma_chan); + sport->tx_regs->spctl |= SPORT_CTL_SPENPRI; + SSYNC(); +} +EXPORT_SYMBOL(sport_tx_start); + +void sport_rx_start(struct sport_device *sport) +{ + set_dma_next_desc_addr(sport->rx_dma_chan, sport->rx_desc); + set_dma_config(sport->rx_dma_chan, DMAFLOW_LIST | DI_EN | WNR + | compute_wdsize(sport->wdsize) | NDSIZE_6); + enable_dma(sport->rx_dma_chan); + sport->rx_regs->spctl |= SPORT_CTL_SPENPRI; + SSYNC(); +} +EXPORT_SYMBOL(sport_rx_start); + +void sport_tx_stop(struct sport_device *sport) +{ + sport->tx_regs->spctl &= ~SPORT_CTL_SPENPRI; + SSYNC(); + disable_dma(sport->tx_dma_chan); +} +EXPORT_SYMBOL(sport_tx_stop); + +void sport_rx_stop(struct sport_device *sport) +{ + sport->rx_regs->spctl &= ~SPORT_CTL_SPENPRI; + SSYNC(); + disable_dma(sport->rx_dma_chan); +} +EXPORT_SYMBOL(sport_rx_stop); + +void sport_set_tx_callback(struct sport_device *sport, + void (*tx_callback)(void *), void *tx_data) +{ + sport->tx_callback = tx_callback; + sport->tx_data = tx_data; +} +EXPORT_SYMBOL(sport_set_tx_callback); + +void sport_set_rx_callback(struct sport_device *sport, + void (*rx_callback)(void *), void *rx_data) +{ + sport->rx_callback = rx_callback; + sport->rx_data = rx_data; +} +EXPORT_SYMBOL(sport_set_rx_callback); + +static void setup_desc(struct dmasg *desc, void *buf, int fragcount, + size_t fragsize, unsigned int cfg, + unsigned int count, size_t wdsize) +{ + + int i; + + for (i = 0; i < fragcount; ++i) { + desc[i].next_desc_addr = &(desc[i + 1]); + desc[i].start_addr = (unsigned long)buf + i*fragsize; + desc[i].cfg = cfg; + desc[i].x_count = count; + desc[i].x_modify = wdsize; + desc[i].y_count = 0; + desc[i].y_modify = 0; + } + + /* make circular */ + desc[fragcount-1].next_desc_addr = desc; +} + +int sport_config_tx_dma(struct sport_device *sport, void *buf, + int fragcount, size_t fragsize) +{ + unsigned int count; + unsigned int cfg; + dma_addr_t addr; + + count = fragsize/sport->wdsize; + + if (sport->tx_desc) + dma_free_coherent(NULL, sport->tx_desc_size, + sport->tx_desc, 0); + + sport->tx_desc = dma_alloc_coherent(NULL, + fragcount * sizeof(struct dmasg), &addr, 0); + sport->tx_desc_size = fragcount * sizeof(struct dmasg); + if (!sport->tx_desc) + return -ENOMEM; + + sport->tx_buf = buf; + sport->tx_fragsize = fragsize; + sport->tx_frags = fragcount; + cfg = DMAFLOW_LIST | DI_EN | compute_wdsize(sport->wdsize) | NDSIZE_6; + + setup_desc(sport->tx_desc, buf, fragcount, fragsize, + cfg|DMAEN, count, sport->wdsize); + + return 0; +} +EXPORT_SYMBOL(sport_config_tx_dma); + +int sport_config_rx_dma(struct sport_device *sport, void *buf, + int fragcount, size_t fragsize) +{ + unsigned int count; + unsigned int cfg; + dma_addr_t addr; + + count = fragsize/sport->wdsize; + + if (sport->rx_desc) + dma_free_coherent(NULL, sport->rx_desc_size, + sport->rx_desc, 0); + + sport->rx_desc = dma_alloc_coherent(NULL, + fragcount * sizeof(struct dmasg), &addr, 0); + sport->rx_desc_size = fragcount * sizeof(struct dmasg); + if (!sport->rx_desc) + return -ENOMEM; + + sport->rx_buf = buf; + sport->rx_fragsize = fragsize; + sport->rx_frags = fragcount; + cfg = DMAFLOW_LIST | DI_EN | compute_wdsize(sport->wdsize) + | WNR | NDSIZE_6; + + setup_desc(sport->rx_desc, buf, fragcount, fragsize, + cfg|DMAEN, count, sport->wdsize); + + return 0; +} +EXPORT_SYMBOL(sport_config_rx_dma); + +unsigned long sport_curr_offset_tx(struct sport_device *sport) +{ + unsigned long curr = get_dma_curr_addr(sport->tx_dma_chan); + + return (unsigned char *)curr - sport->tx_buf; +} +EXPORT_SYMBOL(sport_curr_offset_tx); + +unsigned long sport_curr_offset_rx(struct sport_device *sport) +{ + unsigned long curr = get_dma_curr_addr(sport->rx_dma_chan); + + return (unsigned char *)curr - sport->rx_buf; +} +EXPORT_SYMBOL(sport_curr_offset_rx); + +static irqreturn_t sport_tx_irq(int irq, void *dev_id) +{ + struct sport_device *sport = dev_id; + static unsigned long status; + + status = get_dma_curr_irqstat(sport->tx_dma_chan); + if (status & (DMA_DONE|DMA_ERR)) { + clear_dma_irqstat(sport->tx_dma_chan); + SSYNC(); + } + if (sport->tx_callback) + sport->tx_callback(sport->tx_data); + return IRQ_HANDLED; +} + +static irqreturn_t sport_rx_irq(int irq, void *dev_id) +{ + struct sport_device *sport = dev_id; + unsigned long status; + + status = get_dma_curr_irqstat(sport->rx_dma_chan); + if (status & (DMA_DONE|DMA_ERR)) { + clear_dma_irqstat(sport->rx_dma_chan); + SSYNC(); + } + if (sport->rx_callback) + sport->rx_callback(sport->rx_data); + return IRQ_HANDLED; +} + +static irqreturn_t sport_err_irq(int irq, void *dev_id) +{ + struct sport_device *sport = dev_id; + struct device *dev = &sport->pdev->dev; + + if (sport->tx_regs->spctl & SPORT_CTL_DERRPRI) + dev_err(dev, "sport error: TUVF\n"); + if (sport->rx_regs->spctl & SPORT_CTL_DERRPRI) + dev_err(dev, "sport error: ROVF\n"); + + return IRQ_HANDLED; +} + +static int sport_get_resource(struct sport_device *sport) +{ + struct platform_device *pdev = sport->pdev; + struct device *dev = &pdev->dev; + struct bfin_snd_platform_data *pdata = dev->platform_data; + struct resource *res; + + if (!pdata) { + dev_err(dev, "No platform data\n"); + return -ENODEV; + } + sport->pin_req = pdata->pin_req; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "No tx MEM resource\n"); + return -ENODEV; + } + sport->tx_regs = (struct sport_register *)res->start; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) { + dev_err(dev, "No rx MEM resource\n"); + return -ENODEV; + } + sport->rx_regs = (struct sport_register *)res->start; + + res = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (!res) { + dev_err(dev, "No tx DMA resource\n"); + return -ENODEV; + } + sport->tx_dma_chan = res->start; + + res = platform_get_resource(pdev, IORESOURCE_DMA, 1); + if (!res) { + dev_err(dev, "No rx DMA resource\n"); + return -ENODEV; + } + sport->rx_dma_chan = res->start; + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + dev_err(dev, "No tx error irq resource\n"); + return -ENODEV; + } + sport->tx_err_irq = res->start; + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 1); + if (!res) { + dev_err(dev, "No rx error irq resource\n"); + return -ENODEV; + } + sport->rx_err_irq = res->start; + + return 0; +} + +static int sport_request_resource(struct sport_device *sport) +{ + struct device *dev = &sport->pdev->dev; + int ret; + + ret = peripheral_request_list(sport->pin_req, "soc-audio"); + if (ret) { + dev_err(dev, "Unable to request sport pin\n"); + return ret; + } + + ret = request_dma(sport->tx_dma_chan, "SPORT TX Data"); + if (ret) { + dev_err(dev, "Unable to allocate DMA channel for sport tx\n"); + goto err_tx_dma; + } + set_dma_callback(sport->tx_dma_chan, sport_tx_irq, sport); + + ret = request_dma(sport->rx_dma_chan, "SPORT RX Data"); + if (ret) { + dev_err(dev, "Unable to allocate DMA channel for sport rx\n"); + goto err_rx_dma; + } + set_dma_callback(sport->rx_dma_chan, sport_rx_irq, sport); + + ret = request_irq(sport->tx_err_irq, sport_err_irq, + 0, "SPORT TX ERROR", sport); + if (ret) { + dev_err(dev, "Unable to allocate tx error IRQ for sport\n"); + goto err_tx_irq; + } + + ret = request_irq(sport->rx_err_irq, sport_err_irq, + 0, "SPORT RX ERROR", sport); + if (ret) { + dev_err(dev, "Unable to allocate rx error IRQ for sport\n"); + goto err_rx_irq; + } + + return 0; +err_rx_irq: + free_irq(sport->tx_err_irq, sport); +err_tx_irq: + free_dma(sport->rx_dma_chan); +err_rx_dma: + free_dma(sport->tx_dma_chan); +err_tx_dma: + peripheral_free_list(sport->pin_req); + return ret; +} + +static void sport_free_resource(struct sport_device *sport) +{ + free_irq(sport->rx_err_irq, sport); + free_irq(sport->tx_err_irq, sport); + free_dma(sport->rx_dma_chan); + free_dma(sport->tx_dma_chan); + peripheral_free_list(sport->pin_req); +} + +struct sport_device *sport_create(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct sport_device *sport; + int ret; + + sport = kzalloc(sizeof(*sport), GFP_KERNEL); + if (!sport) { + dev_err(dev, "Unable to allocate memory for sport device\n"); + return NULL; + } + sport->pdev = pdev; + + ret = sport_get_resource(sport); + if (ret) { + kfree(sport); + return NULL; + } + + ret = sport_request_resource(sport); + if (ret) { + kfree(sport); + return NULL; + } + + dev_dbg(dev, "SPORT create success\n"); + return sport; +} +EXPORT_SYMBOL(sport_create); + +void sport_delete(struct sport_device *sport) +{ + if (sport->tx_desc) + dma_free_coherent(NULL, sport->tx_desc_size, + sport->tx_desc, 0); + if (sport->rx_desc) + dma_free_coherent(NULL, sport->rx_desc_size, + sport->rx_desc, 0); + sport_free_resource(sport); + kfree(sport); +} +EXPORT_SYMBOL(sport_delete); + +MODULE_DESCRIPTION("Analog Devices BF6XX SPORT driver"); +MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/blackfin/bf6xx-sport.h b/sound/soc/blackfin/bf6xx-sport.h new file mode 100644 index 00000000000..307d193cfce --- /dev/null +++ b/sound/soc/blackfin/bf6xx-sport.h @@ -0,0 +1,82 @@ +/* + * bf6xx_sport - Analog Devices BF6XX SPORT driver + * + * Copyright (c) 2012 Analog Devices Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _BF6XX_SPORT_H_ +#define _BF6XX_SPORT_H_ + +#include <linux/platform_device.h> +#include <asm/bfin_sport3.h> + +struct sport_device { + struct platform_device *pdev; + const unsigned short *pin_req; + struct sport_register *tx_regs; + struct sport_register *rx_regs; + int tx_dma_chan; + int rx_dma_chan; + int tx_err_irq; + int rx_err_irq; + + void (*tx_callback)(void *data); + void *tx_data; + void (*rx_callback)(void *data); + void *rx_data; + + struct dmasg *tx_desc; + struct dmasg *rx_desc; + unsigned int tx_desc_size; + unsigned int rx_desc_size; + unsigned char *tx_buf; + unsigned char *rx_buf; + unsigned int tx_fragsize; + unsigned int rx_fragsize; + unsigned int tx_frags; + unsigned int rx_frags; + unsigned int wdsize; +}; + +struct sport_params { + u32 spctl; + u32 div; +}; + +struct sport_device *sport_create(struct platform_device *pdev); +void sport_delete(struct sport_device *sport); +int sport_set_tx_params(struct sport_device *sport, + struct sport_params *params); +int sport_set_rx_params(struct sport_device *sport, + struct sport_params *params); +void sport_tx_start(struct sport_device *sport); +void sport_rx_start(struct sport_device *sport); +void sport_tx_stop(struct sport_device *sport); +void sport_rx_stop(struct sport_device *sport); +void sport_set_tx_callback(struct sport_device *sport, + void (*tx_callback)(void *), void *tx_data); +void sport_set_rx_callback(struct sport_device *sport, + void (*rx_callback)(void *), void *rx_data); +int sport_config_tx_dma(struct sport_device *sport, void *buf, + int fragcount, size_t fragsize); +int sport_config_rx_dma(struct sport_device *sport, void *buf, + int fragcount, size_t fragsize); +unsigned long sport_curr_offset_tx(struct sport_device *sport); +unsigned long sport_curr_offset_rx(struct sport_device *sport); + + + +#endif diff --git a/sound/soc/blackfin/bfin-eval-adau1373.c b/sound/soc/blackfin/bfin-eval-adau1373.c new file mode 100644 index 00000000000..4ef9683bcad --- /dev/null +++ b/sound/soc/blackfin/bfin-eval-adau1373.c @@ -0,0 +1,184 @@ +/* + * Machine driver for EVAL-ADAU1373 on Analog Devices bfin + * evaluation boards. + * + * Copyright 2011 Analog Devices Inc. + * Author: Lars-Peter Clausen <lars@metafoo.de> + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/module.h> +#include <linux/device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/pcm_params.h> + +#include "../codecs/adau1373.h" + +static const struct snd_soc_dapm_widget bfin_eval_adau1373_dapm_widgets[] = { + SND_SOC_DAPM_LINE("Line In1", NULL), + SND_SOC_DAPM_LINE("Line In2", NULL), + SND_SOC_DAPM_LINE("Line In3", NULL), + SND_SOC_DAPM_LINE("Line In4", NULL), + + SND_SOC_DAPM_LINE("Line Out1", NULL), + SND_SOC_DAPM_LINE("Line Out2", NULL), + SND_SOC_DAPM_LINE("Stereo Out", NULL), + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_HP("Earpiece", NULL), + SND_SOC_DAPM_SPK("Speaker", NULL), +}; + +static const struct snd_soc_dapm_route bfin_eval_adau1373_dapm_routes[] = { + { "AIN1L", NULL, "Line In1" }, + { "AIN1R", NULL, "Line In1" }, + { "AIN2L", NULL, "Line In2" }, + { "AIN2R", NULL, "Line In2" }, + { "AIN3L", NULL, "Line In3" }, + { "AIN3R", NULL, "Line In3" }, + { "AIN4L", NULL, "Line In4" }, + { "AIN4R", NULL, "Line In4" }, + + /* MICBIAS can be connected via a jumper to the line-in jack, since w + don't know which one is going to be used, just power both. */ + { "Line In1", NULL, "MICBIAS1" }, + { "Line In2", NULL, "MICBIAS1" }, + { "Line In3", NULL, "MICBIAS1" }, + { "Line In4", NULL, "MICBIAS1" }, + { "Line In1", NULL, "MICBIAS2" }, + { "Line In2", NULL, "MICBIAS2" }, + { "Line In3", NULL, "MICBIAS2" }, + { "Line In4", NULL, "MICBIAS2" }, + + { "Line Out1", NULL, "LOUT1L" }, + { "Line Out1", NULL, "LOUT1R" }, + { "Line Out2", NULL, "LOUT2L" }, + { "Line Out2", NULL, "LOUT2R" }, + { "Headphone", NULL, "HPL" }, + { "Headphone", NULL, "HPR" }, + { "Earpiece", NULL, "EP" }, + { "Speaker", NULL, "SPKL" }, + { "Stereo Out", NULL, "SPKR" }, +}; + +static int bfin_eval_adau1373_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int ret; + int pll_rate; + + switch (params_rate(params)) { + case 48000: + case 8000: + case 12000: + case 16000: + case 24000: + case 32000: + pll_rate = 48000 * 1024; + break; + case 44100: + case 7350: + case 11025: + case 14700: + case 22050: + case 29400: + pll_rate = 44100 * 1024; + break; + default: + return -EINVAL; + } + + ret = snd_soc_dai_set_pll(codec_dai, ADAU1373_PLL1, + ADAU1373_PLL_SRC_MCLK1, 12288000, pll_rate); + if (ret) + return ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, ADAU1373_CLK_SRC_PLL1, pll_rate, + SND_SOC_CLOCK_IN); + + return ret; +} + +static int bfin_eval_adau1373_codec_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *codec_dai = rtd->codec_dai; + unsigned int pll_rate = 48000 * 1024; + int ret; + + ret = snd_soc_dai_set_pll(codec_dai, ADAU1373_PLL1, + ADAU1373_PLL_SRC_MCLK1, 12288000, pll_rate); + if (ret) + return ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, ADAU1373_CLK_SRC_PLL1, pll_rate, + SND_SOC_CLOCK_IN); + + return ret; +} +static struct snd_soc_ops bfin_eval_adau1373_ops = { + .hw_params = bfin_eval_adau1373_hw_params, +}; + +static struct snd_soc_dai_link bfin_eval_adau1373_dai = { + .name = "adau1373", + .stream_name = "adau1373", + .cpu_dai_name = "bfin-i2s.0", + .codec_dai_name = "adau1373-aif1", + .platform_name = "bfin-i2s-pcm-audio", + .codec_name = "adau1373.0-001a", + .ops = &bfin_eval_adau1373_ops, + .init = bfin_eval_adau1373_codec_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM, +}; + +static struct snd_soc_card bfin_eval_adau1373 = { + .name = "bfin-eval-adau1373", + .owner = THIS_MODULE, + .dai_link = &bfin_eval_adau1373_dai, + .num_links = 1, + + .dapm_widgets = bfin_eval_adau1373_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(bfin_eval_adau1373_dapm_widgets), + .dapm_routes = bfin_eval_adau1373_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(bfin_eval_adau1373_dapm_routes), +}; + +static int bfin_eval_adau1373_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &bfin_eval_adau1373; + + card->dev = &pdev->dev; + + return snd_soc_register_card(&bfin_eval_adau1373); +} + +static int bfin_eval_adau1373_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + + snd_soc_unregister_card(card); + + return 0; +} + +static struct platform_driver bfin_eval_adau1373_driver = { + .driver = { + .name = "bfin-eval-adau1373", + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + }, + .probe = bfin_eval_adau1373_probe, + .remove = bfin_eval_adau1373_remove, +}; + +module_platform_driver(bfin_eval_adau1373_driver); + +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_DESCRIPTION("ALSA SoC bfin adau1373 driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:bfin-eval-adau1373"); diff --git a/sound/soc/blackfin/bfin-eval-adau1701.c b/sound/soc/blackfin/bfin-eval-adau1701.c new file mode 100644 index 00000000000..3b55081a96c --- /dev/null +++ b/sound/soc/blackfin/bfin-eval-adau1701.c @@ -0,0 +1,124 @@ +/* + * Machine driver for EVAL-ADAU1701MINIZ on Analog Devices bfin + * evaluation boards. + * + * Copyright 2011 Analog Devices Inc. + * Author: Lars-Peter Clausen <lars@metafoo.de> + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/module.h> +#include <linux/device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/pcm_params.h> + +#include "../codecs/adau1701.h" + +static const struct snd_soc_dapm_widget bfin_eval_adau1701_dapm_widgets[] = { + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_LINE("Line Out", NULL), + SND_SOC_DAPM_LINE("Line In", NULL), +}; + +static const struct snd_soc_dapm_route bfin_eval_adau1701_dapm_routes[] = { + { "Speaker", NULL, "OUT0" }, + { "Speaker", NULL, "OUT1" }, + { "Line Out", NULL, "OUT2" }, + { "Line Out", NULL, "OUT3" }, + + { "IN0", NULL, "Line In" }, + { "IN1", NULL, "Line In" }, +}; + +static int bfin_eval_adau1701_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, ADAU1701_CLK_SRC_OSC, 12288000, + SND_SOC_CLOCK_IN); + + return ret; +} + +static struct snd_soc_ops bfin_eval_adau1701_ops = { + .hw_params = bfin_eval_adau1701_hw_params, +}; + +#define BFIN_EVAL_ADAU1701_DAI_FMT (SND_SOC_DAIFMT_I2S | \ + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM) + +static struct snd_soc_dai_link bfin_eval_adau1701_dai[] = { + { + .name = "adau1701", + .stream_name = "adau1701", + .cpu_dai_name = "bfin-i2s.0", + .codec_dai_name = "adau1701", + .platform_name = "bfin-i2s-pcm-audio", + .codec_name = "adau1701.0-0034", + .ops = &bfin_eval_adau1701_ops, + .dai_fmt = BFIN_EVAL_ADAU1701_DAI_FMT, + }, + { + .name = "adau1701", + .stream_name = "adau1701", + .cpu_dai_name = "bfin-i2s.1", + .codec_dai_name = "adau1701", + .platform_name = "bfin-i2s-pcm-audio", + .codec_name = "adau1701.0-0034", + .ops = &bfin_eval_adau1701_ops, + .dai_fmt = BFIN_EVAL_ADAU1701_DAI_FMT, + }, +}; + +static struct snd_soc_card bfin_eval_adau1701 = { + .name = "bfin-eval-adau1701", + .owner = THIS_MODULE, + .dai_link = &bfin_eval_adau1701_dai[CONFIG_SND_BF5XX_SPORT_NUM], + .num_links = 1, + + .dapm_widgets = bfin_eval_adau1701_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(bfin_eval_adau1701_dapm_widgets), + .dapm_routes = bfin_eval_adau1701_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(bfin_eval_adau1701_dapm_routes), +}; + +static int bfin_eval_adau1701_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &bfin_eval_adau1701; + + card->dev = &pdev->dev; + + return snd_soc_register_card(&bfin_eval_adau1701); +} + +static int bfin_eval_adau1701_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + + snd_soc_unregister_card(card); + + return 0; +} + +static struct platform_driver bfin_eval_adau1701_driver = { + .driver = { + .name = "bfin-eval-adau1701", + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + }, + .probe = bfin_eval_adau1701_probe, + .remove = bfin_eval_adau1701_remove, +}; + +module_platform_driver(bfin_eval_adau1701_driver); + +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_DESCRIPTION("ALSA SoC bfin ADAU1701 driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:bfin-eval-adau1701"); diff --git a/sound/soc/blackfin/bfin-eval-adau1x61.c b/sound/soc/blackfin/bfin-eval-adau1x61.c new file mode 100644 index 00000000000..3011906f9d3 --- /dev/null +++ b/sound/soc/blackfin/bfin-eval-adau1x61.c @@ -0,0 +1,142 @@ +/* + * Machine driver for EVAL-ADAU1x61MINIZ on Analog Devices bfin + * evaluation boards. + * + * Copyright 2011-2014 Analog Devices Inc. + * Author: Lars-Peter Clausen <lars@metafoo.de> + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/module.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/pcm_params.h> + +#include "../codecs/adau17x1.h" + +static const struct snd_soc_dapm_widget bfin_eval_adau1x61_dapm_widgets[] = { + SND_SOC_DAPM_LINE("In 1", NULL), + SND_SOC_DAPM_LINE("In 2", NULL), + SND_SOC_DAPM_LINE("In 3-4", NULL), + + SND_SOC_DAPM_LINE("Diff Out L", NULL), + SND_SOC_DAPM_LINE("Diff Out R", NULL), + SND_SOC_DAPM_LINE("Stereo Out", NULL), + SND_SOC_DAPM_HP("Capless HP Out", NULL), +}; + +static const struct snd_soc_dapm_route bfin_eval_adau1x61_dapm_routes[] = { + { "LAUX", NULL, "In 3-4" }, + { "RAUX", NULL, "In 3-4" }, + { "LINP", NULL, "In 1" }, + { "LINN", NULL, "In 1"}, + { "RINP", NULL, "In 2" }, + { "RINN", NULL, "In 2" }, + + { "In 1", NULL, "MICBIAS" }, + { "In 2", NULL, "MICBIAS" }, + + { "Capless HP Out", NULL, "LHP" }, + { "Capless HP Out", NULL, "RHP" }, + { "Diff Out L", NULL, "LOUT" }, + { "Diff Out R", NULL, "ROUT" }, + { "Stereo Out", NULL, "LOUT" }, + { "Stereo Out", NULL, "ROUT" }, +}; + +static int bfin_eval_adau1x61_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int pll_rate; + int ret; + + switch (params_rate(params)) { + case 48000: + case 8000: + case 12000: + case 16000: + case 24000: + case 32000: + case 96000: + pll_rate = 48000 * 1024; + break; + case 44100: + case 7350: + case 11025: + case 14700: + case 22050: + case 29400: + case 88200: + pll_rate = 44100 * 1024; + break; + default: + return -EINVAL; + } + + ret = snd_soc_dai_set_pll(codec_dai, ADAU17X1_PLL, + ADAU17X1_PLL_SRC_MCLK, 12288000, pll_rate); + if (ret) + return ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, ADAU17X1_CLK_SRC_PLL, pll_rate, + SND_SOC_CLOCK_IN); + + return ret; +} + +static const struct snd_soc_ops bfin_eval_adau1x61_ops = { + .hw_params = bfin_eval_adau1x61_hw_params, +}; + +static struct snd_soc_dai_link bfin_eval_adau1x61_dai = { + .name = "adau1x61", + .stream_name = "adau1x61", + .cpu_dai_name = "bfin-i2s.0", + .codec_dai_name = "adau-hifi", + .platform_name = "bfin-i2s-pcm-audio", + .codec_name = "adau1761.0-0038", + .ops = &bfin_eval_adau1x61_ops, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM, +}; + +static struct snd_soc_card bfin_eval_adau1x61 = { + .name = "bfin-eval-adau1x61", + .driver_name = "eval-adau1x61", + .dai_link = &bfin_eval_adau1x61_dai, + .num_links = 1, + + .dapm_widgets = bfin_eval_adau1x61_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(bfin_eval_adau1x61_dapm_widgets), + .dapm_routes = bfin_eval_adau1x61_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(bfin_eval_adau1x61_dapm_routes), + .fully_routed = true, +}; + +static int bfin_eval_adau1x61_probe(struct platform_device *pdev) +{ + bfin_eval_adau1x61.dev = &pdev->dev; + + return devm_snd_soc_register_card(&pdev->dev, &bfin_eval_adau1x61); +} + +static struct platform_driver bfin_eval_adau1x61_driver = { + .driver = { + .name = "bfin-eval-adau1x61", + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + }, + .probe = bfin_eval_adau1x61_probe, +}; +module_platform_driver(bfin_eval_adau1x61_driver); + +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_DESCRIPTION("ALSA SoC bfin adau1x61 driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:bfin-eval-adau1x61"); diff --git a/sound/soc/blackfin/bfin-eval-adau1x81.c b/sound/soc/blackfin/bfin-eval-adau1x81.c new file mode 100644 index 00000000000..5c380f6aed1 --- /dev/null +++ b/sound/soc/blackfin/bfin-eval-adau1x81.c @@ -0,0 +1,130 @@ +/* + * Machine driver for EVAL-ADAU1x81 on Analog Devices bfin + * evaluation boards. + * + * Copyright 2011-2014 Analog Devices Inc. + * Author: Lars-Peter Clausen <lars@metafoo.de> + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/module.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/pcm_params.h> + +#include "../codecs/adau17x1.h" + +static const struct snd_soc_dapm_widget bfin_eval_adau1x81_dapm_widgets[] = { + SND_SOC_DAPM_LINE("Stereo In", NULL), + SND_SOC_DAPM_LINE("Beep", NULL), + + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_HP("Headphone", NULL), +}; + +static const struct snd_soc_dapm_route bfin_eval_adau1x81_dapm_routes[] = { + { "BEEP", NULL, "Beep" }, + { "LMIC", NULL, "Stereo In" }, + { "LMIC", NULL, "Stereo In" }, + + { "Headphone", NULL, "AOUTL" }, + { "Headphone", NULL, "AOUTR" }, + { "Speaker", NULL, "SP" }, +}; + +static int bfin_eval_adau1x81_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int pll_rate; + int ret; + + switch (params_rate(params)) { + case 48000: + case 8000: + case 12000: + case 16000: + case 24000: + case 32000: + case 96000: + pll_rate = 48000 * 1024; + break; + case 44100: + case 7350: + case 11025: + case 14700: + case 22050: + case 29400: + case 88200: + pll_rate = 44100 * 1024; + break; + default: + return -EINVAL; + } + + ret = snd_soc_dai_set_pll(codec_dai, ADAU17X1_PLL, + ADAU17X1_PLL_SRC_MCLK, 12288000, pll_rate); + if (ret) + return ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, ADAU17X1_CLK_SRC_PLL, pll_rate, + SND_SOC_CLOCK_IN); + + return ret; +} + +static const struct snd_soc_ops bfin_eval_adau1x81_ops = { + .hw_params = bfin_eval_adau1x81_hw_params, +}; + +static struct snd_soc_dai_link bfin_eval_adau1x81_dai = { + .name = "adau1x81", + .stream_name = "adau1x81", + .cpu_dai_name = "bfin-i2s.0", + .codec_dai_name = "adau-hifi", + .platform_name = "bfin-i2s-pcm-audio", + .codec_name = "adau1781.0-0038", + .ops = &bfin_eval_adau1x81_ops, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM, +}; + +static struct snd_soc_card bfin_eval_adau1x81 = { + .name = "bfin-eval-adau1x81", + .driver_name = "eval-adau1x81", + .dai_link = &bfin_eval_adau1x81_dai, + .num_links = 1, + + .dapm_widgets = bfin_eval_adau1x81_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(bfin_eval_adau1x81_dapm_widgets), + .dapm_routes = bfin_eval_adau1x81_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(bfin_eval_adau1x81_dapm_routes), + .fully_routed = true, +}; + +static int bfin_eval_adau1x81_probe(struct platform_device *pdev) +{ + bfin_eval_adau1x81.dev = &pdev->dev; + + return devm_snd_soc_register_card(&pdev->dev, &bfin_eval_adau1x81); +} + +static struct platform_driver bfin_eval_adau1x81_driver = { + .driver = { + .name = "bfin-eval-adau1x81", + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + }, + .probe = bfin_eval_adau1x81_probe, +}; +module_platform_driver(bfin_eval_adau1x81_driver); + +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_DESCRIPTION("ALSA SoC bfin adau1x81 driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:bfin-eval-adau1x81"); diff --git a/sound/soc/blackfin/bfin-eval-adav80x.c b/sound/soc/blackfin/bfin-eval-adav80x.c new file mode 100644 index 00000000000..3b1b61a4481 --- /dev/null +++ b/sound/soc/blackfin/bfin-eval-adav80x.c @@ -0,0 +1,156 @@ +/* + * Machine driver for EVAL-ADAV801 and EVAL-ADAV803 on Analog Devices bfin + * evaluation boards. + * + * Copyright 2011 Analog Devices Inc. + * Author: Lars-Peter Clausen <lars@metafoo.de> + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/module.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> + +#include "../codecs/adav80x.h" + +static const struct snd_soc_dapm_widget bfin_eval_adav80x_dapm_widgets[] = { + SND_SOC_DAPM_LINE("Line Out", NULL), + SND_SOC_DAPM_LINE("Line In", NULL), +}; + +static const struct snd_soc_dapm_route bfin_eval_adav80x_dapm_routes[] = { + { "Line Out", NULL, "VOUTL" }, + { "Line Out", NULL, "VOUTR" }, + + { "VINL", NULL, "Line In" }, + { "VINR", NULL, "Line In" }, +}; + +static int bfin_eval_adav80x_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int ret; + + ret = snd_soc_dai_set_pll(codec_dai, ADAV80X_PLL1, ADAV80X_PLL_SRC_XTAL, + 27000000, params_rate(params) * 256); + if (ret) + return ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, ADAV80X_CLK_PLL1, + params_rate(params) * 256, SND_SOC_CLOCK_IN); + + return ret; +} + +static int bfin_eval_adav80x_codec_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *codec_dai = rtd->codec_dai; + + snd_soc_dai_set_sysclk(codec_dai, ADAV80X_CLK_SYSCLK1, 0, + SND_SOC_CLOCK_OUT); + snd_soc_dai_set_sysclk(codec_dai, ADAV80X_CLK_SYSCLK2, 0, + SND_SOC_CLOCK_OUT); + snd_soc_dai_set_sysclk(codec_dai, ADAV80X_CLK_SYSCLK3, 0, + SND_SOC_CLOCK_OUT); + + snd_soc_dai_set_sysclk(codec_dai, ADAV80X_CLK_XTAL, 2700000, 0); + + return 0; +} + +static struct snd_soc_ops bfin_eval_adav80x_ops = { + .hw_params = bfin_eval_adav80x_hw_params, +}; + +static struct snd_soc_dai_link bfin_eval_adav80x_dais[] = { + { + .name = "adav80x", + .stream_name = "ADAV80x HiFi", + .cpu_dai_name = "bfin-i2s.0", + .codec_dai_name = "adav80x-hifi", + .platform_name = "bfin-i2s-pcm-audio", + .init = bfin_eval_adav80x_codec_init, + .ops = &bfin_eval_adav80x_ops, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM, + }, +}; + +static struct snd_soc_card bfin_eval_adav80x = { + .name = "bfin-eval-adav80x", + .owner = THIS_MODULE, + .dai_link = bfin_eval_adav80x_dais, + .num_links = ARRAY_SIZE(bfin_eval_adav80x_dais), + + .dapm_widgets = bfin_eval_adav80x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(bfin_eval_adav80x_dapm_widgets), + .dapm_routes = bfin_eval_adav80x_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(bfin_eval_adav80x_dapm_routes), +}; + +enum bfin_eval_adav80x_type { + BFIN_EVAL_ADAV801, + BFIN_EVAL_ADAV803, +}; + +static int bfin_eval_adav80x_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &bfin_eval_adav80x; + const char *codec_name; + + switch (platform_get_device_id(pdev)->driver_data) { + case BFIN_EVAL_ADAV801: + codec_name = "spi0.1"; + break; + case BFIN_EVAL_ADAV803: + codec_name = "adav803.0-0034"; + break; + default: + return -EINVAL; + } + + bfin_eval_adav80x_dais[0].codec_name = codec_name; + + card->dev = &pdev->dev; + + return snd_soc_register_card(&bfin_eval_adav80x); +} + +static int bfin_eval_adav80x_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + + snd_soc_unregister_card(card); + + return 0; +} + +static const struct platform_device_id bfin_eval_adav80x_ids[] = { + { "bfin-eval-adav801", BFIN_EVAL_ADAV801 }, + { "bfin-eval-adav803", BFIN_EVAL_ADAV803 }, + { }, +}; +MODULE_DEVICE_TABLE(platform, bfin_eval_adav80x_ids); + +static struct platform_driver bfin_eval_adav80x_driver = { + .driver = { + .name = "bfin-eval-adav80x", + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + }, + .probe = bfin_eval_adav80x_probe, + .remove = bfin_eval_adav80x_remove, + .id_table = bfin_eval_adav80x_ids, +}; + +module_platform_driver(bfin_eval_adav80x_driver); + +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_DESCRIPTION("ALSA SoC bfin adav80x driver"); +MODULE_LICENSE("GPL"); |
