n810.c 9.4 KB
Newer Older
1 2 3 4 5
/*
 * n810.c  --  SoC audio for Nokia N810
 *
 * Copyright (C) 2008 Nokia Corporation
 *
6
 * Contact: Jarkko Nikula <jarkko.nikula@bitmer.com>
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
 *
 * 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.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 *
 */

#include <linux/clk.h>
25
#include <linux/i2c.h>
26 27 28 29 30 31
#include <linux/platform_device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>

#include <asm/mach-types.h>
32
#include <mach/hardware.h>
33
#include <linux/gpio.h>
34
#include <plat/mcbsp.h>
35 36 37 38

#include "omap-mcbsp.h"
#include "omap-pcm.h"

39 40
#define N810_HEADSET_AMP_GPIO	10
#define N810_SPEAKER_AMP_GPIO	101
41

42 43 44 45 46 47 48
enum {
	N810_JACK_DISABLED,
	N810_JACK_HP,
	N810_JACK_HS,
	N810_JACK_MIC,
};

49 50 51 52 53 54
static struct clk *sys_clkout2;
static struct clk *sys_clkout2_src;
static struct clk *func96m_clk;

static int n810_spk_func;
static int n810_jack_func;
55
static int n810_dmic_func;
56 57 58

static void n810_ext_control(struct snd_soc_codec *codec)
{
L
Liam Girdwood 已提交
59
	struct snd_soc_dapm_context *dapm = &codec->dapm;
60 61 62 63 64 65 66 67 68 69 70 71 72
	int hp = 0, line1l = 0;

	switch (n810_jack_func) {
	case N810_JACK_HS:
		line1l = 1;
	case N810_JACK_HP:
		hp = 1;
		break;
	case N810_JACK_MIC:
		line1l = 1;
		break;
	}

73
	if (n810_spk_func)
L
Liam Girdwood 已提交
74
		snd_soc_dapm_enable_pin(dapm, "Ext Spk");
75
	else
L
Liam Girdwood 已提交
76
		snd_soc_dapm_disable_pin(dapm, "Ext Spk");
77

78
	if (hp)
L
Liam Girdwood 已提交
79
		snd_soc_dapm_enable_pin(dapm, "Headphone Jack");
80
	else
L
Liam Girdwood 已提交
81
		snd_soc_dapm_disable_pin(dapm, "Headphone Jack");
82
	if (line1l)
L
Liam Girdwood 已提交
83
		snd_soc_dapm_enable_pin(dapm, "LINE1L");
84
	else
L
Liam Girdwood 已提交
85
		snd_soc_dapm_disable_pin(dapm, "LINE1L");
86 87

	if (n810_dmic_func)
L
Liam Girdwood 已提交
88
		snd_soc_dapm_enable_pin(dapm, "DMic");
89
	else
L
Liam Girdwood 已提交
90
		snd_soc_dapm_disable_pin(dapm, "DMic");
91

L
Liam Girdwood 已提交
92
	snd_soc_dapm_sync(dapm);
93 94 95 96
}

static int n810_startup(struct snd_pcm_substream *substream)
{
97
	struct snd_pcm_runtime *runtime = substream->runtime;
98
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
99
	struct snd_soc_codec *codec = rtd->codec;
100

101 102 103
	snd_pcm_hw_constraint_minmax(runtime,
				     SNDRV_PCM_HW_PARAM_CHANNELS, 2, 2);

104 105 106 107 108 109 110 111 112 113 114 115 116
	n810_ext_control(codec);
	return clk_enable(sys_clkout2);
}

static void n810_shutdown(struct snd_pcm_substream *substream)
{
	clk_disable(sys_clkout2);
}

static int n810_hw_params(struct snd_pcm_substream *substream,
	struct snd_pcm_hw_params *params)
{
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
117
	struct snd_soc_dai *codec_dai = rtd->codec_dai;
118 119 120
	int err;

	/* Set the codec system clock for DAC and ADC */
121
	err = snd_soc_dai_set_sysclk(codec_dai, 0, 12000000,
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
					    SND_SOC_CLOCK_IN);

	return err;
}

static struct snd_soc_ops n810_ops = {
	.startup = n810_startup,
	.hw_params = n810_hw_params,
	.shutdown = n810_shutdown,
};

static int n810_get_spk(struct snd_kcontrol *kcontrol,
			struct snd_ctl_elem_value *ucontrol)
{
	ucontrol->value.integer.value[0] = n810_spk_func;

	return 0;
}

static int n810_set_spk(struct snd_kcontrol *kcontrol,
			struct snd_ctl_elem_value *ucontrol)
{
	struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);

	if (n810_spk_func == ucontrol->value.integer.value[0])
		return 0;

	n810_spk_func = ucontrol->value.integer.value[0];
	n810_ext_control(codec);

	return 1;
}

static int n810_get_jack(struct snd_kcontrol *kcontrol,
			 struct snd_ctl_elem_value *ucontrol)
{
	ucontrol->value.integer.value[0] = n810_jack_func;

	return 0;
}

static int n810_set_jack(struct snd_kcontrol *kcontrol,
			 struct snd_ctl_elem_value *ucontrol)
{
	struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);

	if (n810_jack_func == ucontrol->value.integer.value[0])
		return 0;

	n810_jack_func = ucontrol->value.integer.value[0];
	n810_ext_control(codec);

	return 1;
}

177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
static int n810_get_input(struct snd_kcontrol *kcontrol,
			  struct snd_ctl_elem_value *ucontrol)
{
	ucontrol->value.integer.value[0] = n810_dmic_func;

	return 0;
}

static int n810_set_input(struct snd_kcontrol *kcontrol,
			  struct snd_ctl_elem_value *ucontrol)
{
	struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);

	if (n810_dmic_func == ucontrol->value.integer.value[0])
		return 0;

	n810_dmic_func = ucontrol->value.integer.value[0];
	n810_ext_control(codec);

	return 1;
}

199 200 201 202
static int n810_spk_event(struct snd_soc_dapm_widget *w,
			  struct snd_kcontrol *k, int event)
{
	if (SND_SOC_DAPM_EVENT_ON(event))
203
		gpio_set_value(N810_SPEAKER_AMP_GPIO, 1);
204
	else
205
		gpio_set_value(N810_SPEAKER_AMP_GPIO, 0);
206 207 208 209 210 211 212 213

	return 0;
}

static int n810_jack_event(struct snd_soc_dapm_widget *w,
			   struct snd_kcontrol *k, int event)
{
	if (SND_SOC_DAPM_EVENT_ON(event))
214
		gpio_set_value(N810_HEADSET_AMP_GPIO, 1);
215
	else
216
		gpio_set_value(N810_HEADSET_AMP_GPIO, 0);
217 218 219 220 221 222 223

	return 0;
}

static const struct snd_soc_dapm_widget aic33_dapm_widgets[] = {
	SND_SOC_DAPM_SPK("Ext Spk", n810_spk_event),
	SND_SOC_DAPM_HP("Headphone Jack", n810_jack_event),
224
	SND_SOC_DAPM_MIC("DMic", NULL),
225 226
};

227
static const struct snd_soc_dapm_route audio_map[] = {
228 229 230 231 232
	{"Headphone Jack", NULL, "HPLOUT"},
	{"Headphone Jack", NULL, "HPROUT"},

	{"Ext Spk", NULL, "LLOUT"},
	{"Ext Spk", NULL, "RLOUT"},
233 234 235

	{"DMic Rate 64", NULL, "Mic Bias 2V"},
	{"Mic Bias 2V", NULL, "DMic"},
236 237 238
};

static const char *spk_function[] = {"Off", "On"};
239
static const char *jack_function[] = {"Off", "Headphone", "Headset", "Mic"};
240
static const char *input_function[] = {"ADC", "Digital Mic"};
241
static const struct soc_enum n810_enum[] = {
242 243
	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(spk_function), spk_function),
	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(jack_function), jack_function),
244
	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(input_function), input_function),
245 246 247 248 249 250 251
};

static const struct snd_kcontrol_new aic33_n810_controls[] = {
	SOC_ENUM_EXT("Speaker Function", n810_enum[0],
		     n810_get_spk, n810_set_spk),
	SOC_ENUM_EXT("Jack Function", n810_enum[1],
		     n810_get_jack, n810_set_jack),
252 253
	SOC_ENUM_EXT("Input Select",  n810_enum[2],
		     n810_get_input, n810_set_input),
254 255
};

256
static int n810_aic33_init(struct snd_soc_pcm_runtime *rtd)
257
{
258
	struct snd_soc_codec *codec = rtd->codec;
L
Liam Girdwood 已提交
259
	struct snd_soc_dapm_context *dapm = &codec->dapm;
260 261

	/* Not connected */
L
Liam Girdwood 已提交
262 263 264 265 266 267 268 269
	snd_soc_dapm_nc_pin(dapm, "MONO_LOUT");
	snd_soc_dapm_nc_pin(dapm, "HPLCOM");
	snd_soc_dapm_nc_pin(dapm, "HPRCOM");
	snd_soc_dapm_nc_pin(dapm, "MIC3L");
	snd_soc_dapm_nc_pin(dapm, "MIC3R");
	snd_soc_dapm_nc_pin(dapm, "LINE1R");
	snd_soc_dapm_nc_pin(dapm, "LINE2L");
	snd_soc_dapm_nc_pin(dapm, "LINE2R");
270

271
	return 0;
272 273 274 275 276 277
}

/* Digital audio interface glue - connects codec <--> CPU */
static struct snd_soc_dai_link n810_dai = {
	.name = "TLV320AIC33",
	.stream_name = "AIC33",
278 279 280 281
	.cpu_dai_name = "omap-mcbsp-dai.1",
	.platform_name = "omap-pcm-audio",
	.codec_name = "tlv320aic3x-codec.2-0018",
	.codec_dai_name = "tlv320aic3x-hifi",
282 283
	.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
		   SND_SOC_DAIFMT_CBM_CFM,
284 285 286 287 288
	.init = n810_aic33_init,
	.ops = &n810_ops,
};

/* Audio machine driver */
289
static struct snd_soc_card snd_soc_n810 = {
290 291 292
	.name = "N810",
	.dai_link = &n810_dai,
	.num_links = 1,
293 294 295 296 297 298 299

	.controls = aic33_n810_controls,
	.num_controls = ARRAY_SIZE(aic33_n810_controls),
	.dapm_widgets = aic33_dapm_widgets,
	.num_dapm_widgets = ARRAY_SIZE(aic33_dapm_widgets),
	.dapm_routes = audio_map,
	.num_dapm_routes = ARRAY_SIZE(audio_map),
300 301 302 303 304 305 306 307 308
};

static struct platform_device *n810_snd_device;

static int __init n810_soc_init(void)
{
	int err;
	struct device *dev;

309
	if (!(machine_is_nokia_n810() || machine_is_nokia_n810_wimax()))
310 311 312 313 314 315
		return -ENODEV;

	n810_snd_device = platform_device_alloc("soc-audio", -1);
	if (!n810_snd_device)
		return -ENOMEM;

316
	platform_set_drvdata(n810_snd_device, &snd_soc_n810);
317 318 319 320 321 322 323 324 325
	err = platform_device_add(n810_snd_device);
	if (err)
		goto err1;

	dev = &n810_snd_device->dev;

	sys_clkout2_src = clk_get(dev, "sys_clkout2_src");
	if (IS_ERR(sys_clkout2_src)) {
		dev_err(dev, "Could not get sys_clkout2_src clock\n");
326 327
		err = PTR_ERR(sys_clkout2_src);
		goto err2;
328 329 330 331
	}
	sys_clkout2 = clk_get(dev, "sys_clkout2");
	if (IS_ERR(sys_clkout2)) {
		dev_err(dev, "Could not get sys_clkout2\n");
332 333
		err = PTR_ERR(sys_clkout2);
		goto err3;
334 335 336 337 338 339 340 341
	}
	/*
	 * Configure 12 MHz output on SYS_CLKOUT2. Therefore we must use
	 * 96 MHz as its parent in order to get 12 MHz
	 */
	func96m_clk = clk_get(dev, "func_96m_ck");
	if (IS_ERR(func96m_clk)) {
		dev_err(dev, "Could not get func 96M clock\n");
342 343
		err = PTR_ERR(func96m_clk);
		goto err4;
344 345 346 347
	}
	clk_set_parent(sys_clkout2_src, func96m_clk);
	clk_set_rate(sys_clkout2, 12000000);

348 349 350
	BUG_ON((gpio_request(N810_HEADSET_AMP_GPIO, "hs_amp") < 0) ||
	       (gpio_request(N810_SPEAKER_AMP_GPIO, "spk_amp") < 0));

351 352
	gpio_direction_output(N810_HEADSET_AMP_GPIO, 0);
	gpio_direction_output(N810_SPEAKER_AMP_GPIO, 0);
353 354

	return 0;
355
err4:
356
	clk_put(sys_clkout2);
357 358 359
err3:
	clk_put(sys_clkout2_src);
err2:
360 361 362 363 364 365 366 367 368
	platform_device_del(n810_snd_device);
err1:
	platform_device_put(n810_snd_device);

	return err;
}

static void __exit n810_soc_exit(void)
{
369 370
	gpio_free(N810_SPEAKER_AMP_GPIO);
	gpio_free(N810_HEADSET_AMP_GPIO);
371 372 373
	clk_put(sys_clkout2_src);
	clk_put(sys_clkout2);
	clk_put(func96m_clk);
374

375 376 377 378 379 380
	platform_device_unregister(n810_snd_device);
}

module_init(n810_soc_init);
module_exit(n810_soc_exit);

381
MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@bitmer.com>");
382 383
MODULE_DESCRIPTION("ALSA SoC Nokia N810");
MODULE_LICENSE("GPL");