1/* 2 * SoC audio for BCM947XX Board 3 * 4 * Copyright (C) 2010, Broadcom Corporation. All Rights Reserved. 5 * 6 * Permission to use, copy, modify, and/or distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 13 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 15 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 16 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 * 18 * $Id: bcm947xx.c,v 1.1 2010/05/13 23:46:27 Exp $ 19 */ 20 21 22#include <linux/module.h> 23#include <linux/moduleparam.h> 24#include <linux/timer.h> 25#include <linux/interrupt.h> 26#include <linux/platform_device.h> 27#include <sound/driver.h> 28#include <sound/core.h> 29#include <sound/pcm.h> 30#include <sound/soc.h> 31#include <sound/soc-dapm.h> 32#include <linux/i2c-gpio.h> 33 34#include <typedefs.h> 35#include <bcmdevs.h> 36#include <pcicfg.h> 37#include <hndsoc.h> 38#include <osl.h> 39#include <bcmutils.h> 40#include <siutils.h> 41#include <sbhnddma.h> 42#include <hnddma.h> 43#include <i2s_core.h> 44 45#include <bcmnvram.h> 46 47 48#include "../codecs/wm8955.h" 49#include "bcm947xx-pcm.h" 50#include "bcm947xx-i2s.h" 51 52#define BCM947XX_AP_DEBUG 0 53#if BCM947XX_AP_DEBUG 54#define DBG(x...) printk(KERN_ERR x) 55#else 56#define DBG(x...) 57#endif 58 59 60/* MCLK in Hz - to bcm947xx & Wolfson 8955 */ 61#define BCM947XX_MCLK_FREQ 20000000 /* 20 MHz */ 62#define BCM947XX_NVRAM_XTAL_FREQ "xtalfreq" 63 64#define SDA_GPIO_NVRAM_NAME "i2c_sda_gpio" 65#define SCL_GPIO_NVRAM_NAME "i2c_scl_gpio" 66 67 68 69 70static int bcm947xx_startup(struct snd_pcm_substream *substream) 71{ 72 //struct snd_soc_pcm_runtime *rtd = substream->private_data; 73 //struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai; 74 //struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai; 75 int ret = 0; 76 77 DBG("%s:\n", __FUNCTION__); 78 79 return ret; 80} 81 82static void bcm947xx_shutdown(struct snd_pcm_substream *substream) 83{ 84 DBG("%s\n", __FUNCTION__); 85 return; 86} 87 88static int bcm947xx_hw_params(struct snd_pcm_substream *substream, 89 struct snd_pcm_hw_params *params) 90{ 91 struct snd_soc_pcm_runtime *rtd = substream->private_data; 92 struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai; 93 struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai; 94 unsigned int fmt; 95 int freq = 11289600; 96 int mclk_input = BCM947XX_MCLK_FREQ; 97 char *tmp; 98 int ret = 0; 99 100 fmt = SND_SOC_DAIFMT_I2S | /* I2S mode audio */ 101 SND_SOC_DAIFMT_NB_NF | /* BCLK not inverted and normal LRCLK polarity */ 102 SND_SOC_DAIFMT_CBM_CFM; /* BCM947xx is I2S Slave */ 103 104 /* set codec DAI configuration */ 105 DBG("%s: calling set_fmt with fmt 0x%x\n", __FUNCTION__, fmt); 106 ret = codec_dai->dai_ops.set_fmt(codec_dai, fmt); 107 if (ret < 0) 108 return ret; 109 110 /* set cpu DAI configuration */ 111 ret = cpu_dai->dai_ops.set_fmt(cpu_dai, fmt); 112 if (ret < 0) 113 return ret; 114 115 /* We need to derive the correct pll output frequency */ 116 /* These two PLL frequencies should cover the sample rates we'll be asked to use */ 117 if (freq % params_rate(params)) 118 freq = 12288000; 119 if (freq % params_rate(params)) 120 DBG("%s: Error, PLL not configured for this sample rate %d\n", __FUNCTION__, 121 params_rate(params)); 122 123 tmp = nvram_get(BCM947XX_NVRAM_XTAL_FREQ); 124 125 /* Try to get xtal frequency from NVRAM, otherwise we'll just use our default */ 126 if (tmp && (strlen(tmp) > 0)) { 127 mclk_input = simple_strtol(tmp, NULL, 10); 128 mclk_input *= 1000; /* NVRAM param is in kHz, we want MHz */ 129 } 130 131 /* set up the PLL in codec */ 132 ret = codec_dai->dai_ops.set_pll(codec_dai, 0, mclk_input, freq); 133 if (ret < 0) { 134 DBG("%s: Error CODEC DAI set_pll returned %d\n", __FUNCTION__, ret); 135 return ret; 136 } 137 /* set the codec system clock for DAC and ADC */ 138 ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8955_SYSCLK, freq, 139 SND_SOC_CLOCK_IN); 140 DBG("%s: codec set_sysclk returned %d\n", __FUNCTION__, ret); 141 if (ret < 0) 142 return ret; 143 144 /* set the I2S system clock as input (unused) */ 145 ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, BCM947XX_I2S_SYSCLK, freq, 146 SND_SOC_CLOCK_IN); 147 148 DBG("%s: cpu set_sysclk returned %d\n", __FUNCTION__, ret); 149 if (ret < 0) 150 return ret; 151 152 return 0; 153} 154 155static struct snd_soc_ops bcm947xx_ops = { 156 .startup = bcm947xx_startup, 157 .hw_params = bcm947xx_hw_params, 158 .shutdown = bcm947xx_shutdown, 159}; 160 161/* 162 * Logic for a wm8955 163 */ 164static int bcm947xx_wm8955_init(struct snd_soc_codec *codec) 165{ 166 DBG("%s\n", __FUNCTION__); 167 168 snd_soc_dapm_sync_endpoints(codec); 169 170 return 0; 171} 172 173/* bcm947xx digital audio interface glue - connects codec <--> CPU */ 174static struct snd_soc_dai_link bcm947xx_dai = { 175 .name = "WM8955", 176 .stream_name = "WM8955", 177 .cpu_dai = &bcm947xx_i2s_dai, 178 .codec_dai = &wm8955_dai, 179 .init = bcm947xx_wm8955_init, 180 .ops = &bcm947xx_ops, 181}; 182 183/* bcm947xx audio machine driver */ 184static struct snd_soc_machine snd_soc_machine_bcm947xx = { 185 .name = "bcm947xx", 186 .dai_link = &bcm947xx_dai, 187 .num_links = 1, 188}; 189 190/* bcm947xx audio private data */ 191static struct wm8955_setup_data bcm947xx_wm8955_setup = { 192 .i2c_address = 0x1a, /* 2wire / I2C interface */ 193}; 194 195/* bcm947xx audio subsystem */ 196static struct snd_soc_device bcm947xx_snd_devdata = { 197 .machine = &snd_soc_machine_bcm947xx, 198 .platform = &bcm947xx_soc_platform, 199 .codec_dev = &soc_codec_dev_wm8955, 200 .codec_data = &bcm947xx_wm8955_setup, 201}; 202 203static struct platform_device *bcm947xx_snd_device; 204 205static int machine_is_bcm947xx(void) 206{ 207 DBG("%s\n", __FUNCTION__); 208 return 1; 209} 210 211static struct i2c_gpio_platform_data i2c_gpio_data = { 212 /* Will be replaced with board specific values from NVRAM */ 213 .sda_pin = 4, 214 .scl_pin = 5, 215}; 216 217static struct platform_device i2c_gpio_device = { 218 .name = "i2c-gpio", 219 .id = 0, 220 .dev = { 221 .platform_data = &i2c_gpio_data, 222 }, 223}; 224 225static int __init bcm947xx_init(void) 226{ 227 char *tmp; 228 int ret; 229 230 DBG("%s\n", __FUNCTION__); 231 232 if (!machine_is_bcm947xx()) 233 return -ENODEV; 234 235 tmp = nvram_get(SDA_GPIO_NVRAM_NAME); 236 237 if (tmp && (strlen(tmp) > 0)) 238 i2c_gpio_data.sda_pin = simple_strtol(tmp, NULL, 10); 239 else { 240 printk(KERN_ERR "%s: NVRAM variable %s missing for I2C interface\n", 241 __FUNCTION__, SDA_GPIO_NVRAM_NAME); 242 return -ENODEV; 243 } 244 245 tmp = nvram_get(SCL_GPIO_NVRAM_NAME); 246 247 if (tmp && (strlen(tmp) > 0)) 248 i2c_gpio_data.scl_pin = simple_strtol(tmp, NULL, 10); 249 else { 250 printk(KERN_ERR "%s: NVRAM variable %s missing for I2C interface\n", 251 __FUNCTION__, SCL_GPIO_NVRAM_NAME); 252 return -ENODEV; 253 } 254 255 ret = platform_device_register(&i2c_gpio_device); 256 if (ret) { 257 platform_device_put(&i2c_gpio_device); 258 return ret; 259 } 260 261 bcm947xx_snd_device = platform_device_alloc("soc-audio", -1); 262 if (!bcm947xx_snd_device) 263 return -ENOMEM; 264 265 platform_set_drvdata(bcm947xx_snd_device, &bcm947xx_snd_devdata); 266 bcm947xx_snd_devdata.dev = &bcm947xx_snd_device->dev; 267 ret = platform_device_add(bcm947xx_snd_device); 268 269 if (ret) { 270 platform_device_put(bcm947xx_snd_device); 271 } 272 273 return ret; 274} 275 276static void __exit bcm947xx_exit(void) 277{ 278 DBG("%s\n", __FUNCTION__); 279 platform_device_unregister(bcm947xx_snd_device); 280} 281 282module_init(bcm947xx_init); 283module_exit(bcm947xx_exit); 284 285/* Module information */ 286MODULE_DESCRIPTION("ALSA SoC BCM947XX"); 287MODULE_LICENSE("GPL"); 288