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