1/* 2 * File: sound/soc/blackfin/bf5xx-ad73311.c 3 * Author: Cliff Cai <Cliff.Cai@analog.com> 4 * 5 * Created: Thur Sep 25 2008 6 * Description: Board driver for ad73311 sound chip 7 * 8 * Modified: 9 * Copyright 2008 Analog Devices Inc. 10 * 11 * Bugs: Enter bugs at http://blackfin.uclinux.org/ 12 * 13 * This program is free software; you can redistribute it and/or modify 14 * it under the terms of the GNU General Public License as published by 15 * the Free Software Foundation; either version 2 of the License, or 16 * (at your option) any later version. 17 * 18 * This program is distributed in the hope that it will be useful, 19 * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 * GNU General Public License for more details. 22 * 23 * You should have received a copy of the GNU General Public License 24 * along with this program; if not, see the file COPYING, or write 25 * to the Free Software Foundation, Inc., 26 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 27 */ 28 29#include <linux/module.h> 30#include <linux/moduleparam.h> 31#include <linux/device.h> 32#include <linux/delay.h> 33#include <linux/gpio.h> 34 35#include <sound/core.h> 36#include <sound/pcm.h> 37#include <sound/soc.h> 38#include <sound/soc-dapm.h> 39#include <sound/pcm_params.h> 40 41#include <asm/blackfin.h> 42#include <asm/cacheflush.h> 43#include <asm/irq.h> 44#include <asm/dma.h> 45#include <asm/portmux.h> 46 47#include "../codecs/ad73311.h" 48#include "bf5xx-sport.h" 49#include "bf5xx-i2s-pcm.h" 50#include "bf5xx-i2s.h" 51 52#if CONFIG_SND_BF5XX_SPORT_NUM == 0 53#define bfin_write_SPORT_TCR1 bfin_write_SPORT0_TCR1 54#define bfin_read_SPORT_TCR1 bfin_read_SPORT0_TCR1 55#define bfin_write_SPORT_TCR2 bfin_write_SPORT0_TCR2 56#define bfin_write_SPORT_TX16 bfin_write_SPORT0_TX16 57#define bfin_read_SPORT_STAT bfin_read_SPORT0_STAT 58#else 59#define bfin_write_SPORT_TCR1 bfin_write_SPORT1_TCR1 60#define bfin_read_SPORT_TCR1 bfin_read_SPORT1_TCR1 61#define bfin_write_SPORT_TCR2 bfin_write_SPORT1_TCR2 62#define bfin_write_SPORT_TX16 bfin_write_SPORT1_TX16 63#define bfin_read_SPORT_STAT bfin_read_SPORT1_STAT 64#endif 65 66#define GPIO_SE CONFIG_SND_BFIN_AD73311_SE 67 68static struct snd_soc_card bf5xx_ad73311; 69 70static int snd_ad73311_startup(void) 71{ 72 pr_debug("%s enter\n", __func__); 73 74 /* Pull up SE pin on AD73311L */ 75 gpio_set_value(GPIO_SE, 1); 76 return 0; 77} 78 79static int snd_ad73311_configure(void) 80{ 81 unsigned short ctrl_regs[6]; 82 unsigned short status = 0; 83 int count = 0; 84 85 /* DMCLK = MCLK = 16.384 MHz 86 * SCLK = DMCLK/8 = 2.048 MHz 87 * Sample Rate = DMCLK/2048 = 8 KHz 88 */ 89 ctrl_regs[0] = AD_CONTROL | AD_WRITE | CTRL_REG_B | REGB_MCDIV(0) | \ 90 REGB_SCDIV(0) | REGB_DIRATE(0); 91 ctrl_regs[1] = AD_CONTROL | AD_WRITE | CTRL_REG_C | REGC_PUDEV | \ 92 REGC_PUADC | REGC_PUDAC | REGC_PUREF | REGC_REFUSE ; 93 ctrl_regs[2] = AD_CONTROL | AD_WRITE | CTRL_REG_D | REGD_OGS(2) | \ 94 REGD_IGS(2); 95 ctrl_regs[3] = AD_CONTROL | AD_WRITE | CTRL_REG_E | REGE_DA(0x1f); 96 ctrl_regs[4] = AD_CONTROL | AD_WRITE | CTRL_REG_F | REGF_SEEN ; 97 ctrl_regs[5] = AD_CONTROL | AD_WRITE | CTRL_REG_A | REGA_MODE_DATA; 98 99 local_irq_disable(); 100 snd_ad73311_startup(); 101 udelay(1); 102 103 bfin_write_SPORT_TCR1(TFSR); 104 bfin_write_SPORT_TCR2(0xF); 105 SSYNC(); 106 107 /* SPORT Tx Register is a 8 x 16 FIFO, all the data can be put to 108 * FIFO before enable SPORT to transfer the data 109 */ 110 for (count = 0; count < 6; count++) 111 bfin_write_SPORT_TX16(ctrl_regs[count]); 112 SSYNC(); 113 bfin_write_SPORT_TCR1(bfin_read_SPORT_TCR1() | TSPEN); 114 SSYNC(); 115 116 /* When TUVF is set, the data is already send out */ 117 while (!(status & TUVF) && ++count < 10000) { 118 udelay(1); 119 status = bfin_read_SPORT_STAT(); 120 SSYNC(); 121 } 122 bfin_write_SPORT_TCR1(bfin_read_SPORT_TCR1() & ~TSPEN); 123 SSYNC(); 124 local_irq_enable(); 125 126 if (count >= 10000) { 127 printk(KERN_ERR "ad73311: failed to configure codec\n"); 128 return -1; 129 } 130 return 0; 131} 132 133static int bf5xx_probe(struct platform_device *pdev) 134{ 135 int err; 136 if (gpio_request(GPIO_SE, "AD73311_SE")) { 137 printk(KERN_ERR "%s: Failed ro request GPIO_%d\n", __func__, GPIO_SE); 138 return -EBUSY; 139 } 140 141 gpio_direction_output(GPIO_SE, 0); 142 143 err = snd_ad73311_configure(); 144 if (err < 0) 145 return -EFAULT; 146 147 return 0; 148} 149 150static int bf5xx_ad73311_startup(struct snd_pcm_substream *substream) 151{ 152 struct snd_soc_pcm_runtime *rtd = substream->private_data; 153 struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; 154 155 pr_debug("%s enter\n", __func__); 156 cpu_dai->private_data = sport_handle; 157 return 0; 158} 159 160static int bf5xx_ad73311_hw_params(struct snd_pcm_substream *substream, 161 struct snd_pcm_hw_params *params) 162{ 163 struct snd_soc_pcm_runtime *rtd = substream->private_data; 164 struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; 165 int ret = 0; 166 167 pr_debug("%s rate %d format %x\n", __func__, params_rate(params), 168 params_format(params)); 169 170 /* set cpu DAI configuration */ 171 ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A | 172 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); 173 if (ret < 0) 174 return ret; 175 176 return 0; 177} 178 179 180static struct snd_soc_ops bf5xx_ad73311_ops = { 181 .startup = bf5xx_ad73311_startup, 182 .hw_params = bf5xx_ad73311_hw_params, 183}; 184 185static struct snd_soc_dai_link bf5xx_ad73311_dai = { 186 .name = "ad73311", 187 .stream_name = "AD73311", 188 .cpu_dai = &bf5xx_i2s_dai, 189 .codec_dai = &ad73311_dai, 190 .ops = &bf5xx_ad73311_ops, 191}; 192 193static struct snd_soc_card bf5xx_ad73311 = { 194 .name = "bf5xx_ad73311", 195 .platform = &bf5xx_i2s_soc_platform, 196 .probe = bf5xx_probe, 197 .dai_link = &bf5xx_ad73311_dai, 198 .num_links = 1, 199}; 200 201static struct snd_soc_device bf5xx_ad73311_snd_devdata = { 202 .card = &bf5xx_ad73311, 203 .codec_dev = &soc_codec_dev_ad73311, 204}; 205 206static struct platform_device *bf5xx_ad73311_snd_device; 207 208static int __init bf5xx_ad73311_init(void) 209{ 210 int ret; 211 212 pr_debug("%s enter\n", __func__); 213 bf5xx_ad73311_snd_device = platform_device_alloc("soc-audio", -1); 214 if (!bf5xx_ad73311_snd_device) 215 return -ENOMEM; 216 217 platform_set_drvdata(bf5xx_ad73311_snd_device, &bf5xx_ad73311_snd_devdata); 218 bf5xx_ad73311_snd_devdata.dev = &bf5xx_ad73311_snd_device->dev; 219 ret = platform_device_add(bf5xx_ad73311_snd_device); 220 221 if (ret) 222 platform_device_put(bf5xx_ad73311_snd_device); 223 224 return ret; 225} 226 227static void __exit bf5xx_ad73311_exit(void) 228{ 229 pr_debug("%s enter\n", __func__); 230 platform_device_unregister(bf5xx_ad73311_snd_device); 231} 232 233module_init(bf5xx_ad73311_init); 234module_exit(bf5xx_ad73311_exit); 235 236/* Module information */ 237MODULE_AUTHOR("Cliff Cai"); 238MODULE_DESCRIPTION("ALSA SoC AD73311 Blackfin"); 239MODULE_LICENSE("GPL"); 240