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/pcm_params.h> 39 40#include <asm/blackfin.h> 41#include <asm/cacheflush.h> 42#include <asm/irq.h> 43#include <asm/dma.h> 44#include <asm/portmux.h> 45 46#include "../codecs/ad73311.h" 47#include "bf5xx-sport.h" 48#include "bf5xx-i2s-pcm.h" 49 50#if CONFIG_SND_BF5XX_SPORT_NUM == 0 51#define bfin_write_SPORT_TCR1 bfin_write_SPORT0_TCR1 52#define bfin_read_SPORT_TCR1 bfin_read_SPORT0_TCR1 53#define bfin_write_SPORT_TCR2 bfin_write_SPORT0_TCR2 54#define bfin_write_SPORT_TX16 bfin_write_SPORT0_TX16 55#define bfin_read_SPORT_STAT bfin_read_SPORT0_STAT 56#else 57#define bfin_write_SPORT_TCR1 bfin_write_SPORT1_TCR1 58#define bfin_read_SPORT_TCR1 bfin_read_SPORT1_TCR1 59#define bfin_write_SPORT_TCR2 bfin_write_SPORT1_TCR2 60#define bfin_write_SPORT_TX16 bfin_write_SPORT1_TX16 61#define bfin_read_SPORT_STAT bfin_read_SPORT1_STAT 62#endif 63 64#define GPIO_SE CONFIG_SND_BFIN_AD73311_SE 65 66static struct snd_soc_card bf5xx_ad73311; 67 68static int snd_ad73311_startup(void) 69{ 70 pr_debug("%s enter\n", __func__); 71 72 /* Pull up SE pin on AD73311L */ 73 gpio_set_value(GPIO_SE, 1); 74 return 0; 75} 76 77static int snd_ad73311_configure(void) 78{ 79 unsigned short ctrl_regs[6]; 80 unsigned short status = 0; 81 int count = 0; 82 83 /* DMCLK = MCLK = 16.384 MHz 84 * SCLK = DMCLK/8 = 2.048 MHz 85 * Sample Rate = DMCLK/2048 = 8 KHz 86 */ 87 ctrl_regs[0] = AD_CONTROL | AD_WRITE | CTRL_REG_B | REGB_MCDIV(0) | \ 88 REGB_SCDIV(0) | REGB_DIRATE(0); 89 ctrl_regs[1] = AD_CONTROL | AD_WRITE | CTRL_REG_C | REGC_PUDEV | \ 90 REGC_PUADC | REGC_PUDAC | REGC_PUREF | REGC_REFUSE ; 91 ctrl_regs[2] = AD_CONTROL | AD_WRITE | CTRL_REG_D | REGD_OGS(2) | \ 92 REGD_IGS(2); 93 ctrl_regs[3] = AD_CONTROL | AD_WRITE | CTRL_REG_E | REGE_DA(0x1f); 94 ctrl_regs[4] = AD_CONTROL | AD_WRITE | CTRL_REG_F | REGF_SEEN ; 95 ctrl_regs[5] = AD_CONTROL | AD_WRITE | CTRL_REG_A | REGA_MODE_DATA; 96 97 local_irq_disable(); 98 snd_ad73311_startup(); 99 udelay(1); 100 101 bfin_write_SPORT_TCR1(TFSR); 102 bfin_write_SPORT_TCR2(0xF); 103 SSYNC(); 104 105 /* SPORT Tx Register is a 8 x 16 FIFO, all the data can be put to 106 * FIFO before enable SPORT to transfer the data 107 */ 108 for (count = 0; count < 6; count++) 109 bfin_write_SPORT_TX16(ctrl_regs[count]); 110 SSYNC(); 111 bfin_write_SPORT_TCR1(bfin_read_SPORT_TCR1() | TSPEN); 112 SSYNC(); 113 114 /* When TUVF is set, the data is already send out */ 115 while (!(status & TUVF) && ++count < 10000) { 116 udelay(1); 117 status = bfin_read_SPORT_STAT(); 118 SSYNC(); 119 } 120 bfin_write_SPORT_TCR1(bfin_read_SPORT_TCR1() & ~TSPEN); 121 SSYNC(); 122 local_irq_enable(); 123 124 if (count >= 10000) { 125 printk(KERN_ERR "ad73311: failed to configure codec\n"); 126 return -1; 127 } 128 return 0; 129} 130 131static int bf5xx_probe(struct platform_device *pdev) 132{ 133 int err; 134 if (gpio_request(GPIO_SE, "AD73311_SE")) { 135 printk(KERN_ERR "%s: Failed ro request GPIO_%d\n", __func__, GPIO_SE); 136 return -EBUSY; 137 } 138 139 gpio_direction_output(GPIO_SE, 0); 140 141 err = snd_ad73311_configure(); 142 if (err < 0) 143 return -EFAULT; 144 145 return 0; 146} 147 148static int bf5xx_ad73311_hw_params(struct snd_pcm_substream *substream, 149 struct snd_pcm_hw_params *params) 150{ 151 struct snd_soc_pcm_runtime *rtd = substream->private_data; 152 struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 153 int ret = 0; 154 155 pr_debug("%s rate %d format %x\n", __func__, params_rate(params), 156 params_format(params)); 157 158 /* set cpu DAI configuration */ 159 ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A | 160 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); 161 if (ret < 0) 162 return ret; 163 164 return 0; 165} 166 167 168static struct snd_soc_ops bf5xx_ad73311_ops = { 169 .hw_params = bf5xx_ad73311_hw_params, 170}; 171 172static struct snd_soc_dai_link bf5xx_ad73311_dai[] = { 173 { 174 .name = "ad73311", 175 .stream_name = "AD73311", 176 .cpu_dai_name = "bfin-i2s.0", 177 .codec_dai_name = "ad73311-hifi", 178 .platform_name = "bfin-i2s-pcm-audio", 179 .codec_name = "ad73311", 180 .ops = &bf5xx_ad73311_ops, 181 }, 182 { 183 .name = "ad73311", 184 .stream_name = "AD73311", 185 .cpu_dai_name = "bfin-i2s.1", 186 .codec_dai_name = "ad73311-hifi", 187 .platform_name = "bfin-i2s-pcm-audio", 188 .codec_name = "ad73311", 189 .ops = &bf5xx_ad73311_ops, 190 }, 191}; 192 193static struct snd_soc_card bf5xx_ad73311 = { 194 .name = "bfin-ad73311", 195 .probe = bf5xx_probe, 196 .dai_link = &bf5xx_ad73311_dai[CONFIG_SND_BF5XX_SPORT_NUM], 197 .num_links = 1, 198}; 199 200static struct platform_device *bf5xx_ad73311_snd_device; 201 202static int __init bf5xx_ad73311_init(void) 203{ 204 int ret; 205 206 pr_debug("%s enter\n", __func__); 207 bf5xx_ad73311_snd_device = platform_device_alloc("soc-audio", -1); 208 if (!bf5xx_ad73311_snd_device) 209 return -ENOMEM; 210 211 platform_set_drvdata(bf5xx_ad73311_snd_device, &bf5xx_ad73311); 212 ret = platform_device_add(bf5xx_ad73311_snd_device); 213 214 if (ret) 215 platform_device_put(bf5xx_ad73311_snd_device); 216 217 return ret; 218} 219 220static void __exit bf5xx_ad73311_exit(void) 221{ 222 pr_debug("%s enter\n", __func__); 223 platform_device_unregister(bf5xx_ad73311_snd_device); 224} 225 226module_init(bf5xx_ad73311_init); 227module_exit(bf5xx_ad73311_exit); 228 229/* Module information */ 230MODULE_AUTHOR("Cliff Cai"); 231MODULE_DESCRIPTION("ALSA SoC AD73311 Blackfin"); 232MODULE_LICENSE("GPL"); 233 234