| /* |
| * ASoC driver for Lyrtech SFFSDR board. |
| * |
| * Author: Hugo Villeneuve |
| * Copyright (C) 2008 Lyrtech inc |
| * |
| * Based on ASoC driver for TI DAVINCI EVM platform, original copyright follow: |
| * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com> |
| * |
| * 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. |
| */ |
| |
| #include <linux/module.h> |
| #include <linux/moduleparam.h> |
| #include <linux/timer.h> |
| #include <linux/interrupt.h> |
| #include <linux/platform_device.h> |
| #include <linux/gpio.h> |
| #include <sound/core.h> |
| #include <sound/pcm.h> |
| #include <sound/soc.h> |
| |
| #include <asm/dma.h> |
| #include <asm/mach-types.h> |
| #ifdef CONFIG_SFFSDR_FPGA |
| #include <asm/plat-sffsdr/sffsdr-fpga.h> |
| #endif |
| |
| #include <mach/edma.h> |
| |
| #include "../codecs/pcm3008.h" |
| #include "davinci-pcm.h" |
| #include "davinci-i2s.h" |
| |
| /* |
| * CLKX and CLKR are the inputs for the Sample Rate Generator. |
| * FSX and FSR are outputs, driven by the sample Rate Generator. |
| */ |
| #define AUDIO_FORMAT (SND_SOC_DAIFMT_DSP_B | \ |
| SND_SOC_DAIFMT_CBM_CFS | \ |
| SND_SOC_DAIFMT_IB_NF) |
| |
| static int sffsdr_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->cpu_dai; |
| int fs; |
| int ret = 0; |
| |
| /* Fsref can be 32000, 44100 or 48000. */ |
| fs = params_rate(params); |
| |
| #ifndef CONFIG_SFFSDR_FPGA |
| /* Without the FPGA module, the Fs is fixed at 44100 Hz */ |
| if (fs != 44100) { |
| pr_debug("warning: only 44.1 kHz is supported without SFFSDR FPGA module\n"); |
| return -EINVAL; |
| } |
| #endif |
| |
| /* set cpu DAI configuration */ |
| ret = snd_soc_dai_set_fmt(cpu_dai, AUDIO_FORMAT); |
| if (ret < 0) |
| return ret; |
| |
| pr_debug("sffsdr_hw_params: rate = %d Hz\n", fs); |
| |
| #ifndef CONFIG_SFFSDR_FPGA |
| return 0; |
| #else |
| return sffsdr_fpga_set_codec_fs(fs); |
| #endif |
| } |
| |
| static struct snd_soc_ops sffsdr_ops = { |
| .hw_params = sffsdr_hw_params, |
| }; |
| |
| /* davinci-sffsdr digital audio interface glue - connects codec <--> CPU */ |
| static struct snd_soc_dai_link sffsdr_dai = { |
| .name = "PCM3008", /* Codec name */ |
| .stream_name = "PCM3008 HiFi", |
| .cpu_dai_name = "davinci-mcbsp", |
| .codec_dai_name = "pcm3008-hifi", |
| .codec_name = "pcm3008-codec", |
| .platform_name = "davinci-pcm-audio", |
| .ops = &sffsdr_ops, |
| }; |
| |
| /* davinci-sffsdr audio machine driver */ |
| static struct snd_soc_card snd_soc_sffsdr = { |
| .name = "DaVinci SFFSDR", |
| .owner = THIS_MODULE, |
| .dai_link = &sffsdr_dai, |
| .num_links = 1, |
| }; |
| |
| /* sffsdr audio private data */ |
| static struct pcm3008_setup_data sffsdr_pcm3008_setup = { |
| .dem0_pin = GPIO(45), |
| .dem1_pin = GPIO(46), |
| .pdad_pin = GPIO(47), |
| .pdda_pin = GPIO(38), |
| }; |
| |
| struct platform_device pcm3008_codec = { |
| .name = "pcm3008-codec", |
| .id = 0, |
| .dev = { |
| .platform_data = &sffsdr_pcm3008_setup, |
| }, |
| }; |
| |
| static struct resource sffsdr_snd_resources[] = { |
| { |
| .start = DAVINCI_MCBSP_BASE, |
| .end = DAVINCI_MCBSP_BASE + SZ_8K - 1, |
| .flags = IORESOURCE_MEM, |
| }, |
| }; |
| |
| static struct evm_snd_platform_data sffsdr_snd_data = { |
| .tx_dma_ch = DAVINCI_DMA_MCBSP_TX, |
| .rx_dma_ch = DAVINCI_DMA_MCBSP_RX, |
| }; |
| |
| static struct platform_device *sffsdr_snd_device; |
| |
| static int __init sffsdr_init(void) |
| { |
| int ret; |
| |
| if (!machine_is_sffsdr()) |
| return -EINVAL; |
| |
| platform_device_register(&pcm3008_codec); |
| |
| sffsdr_snd_device = platform_device_alloc("soc-audio", 0); |
| if (!sffsdr_snd_device) { |
| printk(KERN_ERR "platform device allocation failed\n"); |
| return -ENOMEM; |
| } |
| |
| platform_set_drvdata(sffsdr_snd_device, &snd_soc_sffsdr); |
| platform_device_add_data(sffsdr_snd_device, &sffsdr_snd_data, |
| sizeof(sffsdr_snd_data)); |
| |
| ret = platform_device_add_resources(sffsdr_snd_device, |
| sffsdr_snd_resources, |
| ARRAY_SIZE(sffsdr_snd_resources)); |
| if (ret) { |
| printk(KERN_ERR "platform device add resources failed\n"); |
| goto error; |
| } |
| |
| ret = platform_device_add(sffsdr_snd_device); |
| if (ret) |
| goto error; |
| |
| return ret; |
| |
| error: |
| platform_device_put(sffsdr_snd_device); |
| return ret; |
| } |
| |
| static void __exit sffsdr_exit(void) |
| { |
| platform_device_unregister(sffsdr_snd_device); |
| platform_device_unregister(&pcm3008_codec); |
| } |
| |
| module_init(sffsdr_init); |
| module_exit(sffsdr_exit); |
| |
| MODULE_AUTHOR("Hugo Villeneuve"); |
| MODULE_DESCRIPTION("Lyrtech SFFSDR ASoC driver"); |
| MODULE_LICENSE("GPL"); |