mfld_machine.c 11.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
/*
 *  mfld_machine.c - ASoc Machine driver for Intel Medfield MID platform
 *
 *  Copyright (C) 2010 Intel Corp
 *  Author: Vinod Koul <vinod.koul@intel.com>
 *  Author: Harsha Priya <priya.harsha@intel.com>
 *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; version 2 of the License.
 *
 *  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.,
 *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 */

#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/init.h>
#include <linux/device.h>
#include <linux/slab.h>
30
#include <linux/io.h>
31
#include <linux/module.h>
32 33 34
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
35
#include <sound/jack.h>
36 37 38 39 40
#include "../codecs/sn95031.h"

#define MID_MONO 1
#define MID_STEREO 2
#define MID_MAX_CAP 5
41 42 43 44 45 46 47 48 49 50 51 52
#define MFLD_JACK_INSERT 0x04

enum soc_mic_bias_zones {
	MFLD_MV_START = 0,
	/* mic bias volutage range for Headphones*/
	MFLD_MV_HP = 400,
	/* mic bias volutage range for American Headset*/
	MFLD_MV_AM_HS = 650,
	/* mic bias volutage range for Headset*/
	MFLD_MV_HS = 2000,
	MFLD_MV_UNDEFINED,
};
53 54 55

static unsigned int	hs_switch;
static unsigned int	lo_dac;
56
static struct snd_soc_codec *mfld_codec;
57

58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
struct mfld_mc_private {
	void __iomem *int_base;
	u8 interrupt_status;
};

struct snd_soc_jack mfld_jack;

/*Headset jack detection DAPM pins */
static struct snd_soc_jack_pin mfld_jack_pins[] = {
	{
		.pin = "Headphones",
		.mask = SND_JACK_HEADPHONE,
	},
	{
		.pin = "AMIC1",
		.mask = SND_JACK_MICROPHONE,
	},
};

77 78 79 80 81 82
/* jack detection voltage zones */
static struct snd_soc_jack_zone mfld_zones[] = {
	{MFLD_MV_START, MFLD_MV_AM_HS, SND_JACK_HEADPHONE},
	{MFLD_MV_AM_HS, MFLD_MV_HS, SND_JACK_HEADSET},
};

83
/* sound card controls */
84
static const char * const headset_switch_text[] = {"Earpiece", "Headset"};
85

86
static const char * const lo_text[] = {"Vibra", "Headset", "IHF", "None"};
87 88 89 90 91 92 93 94 95 96

static const struct soc_enum headset_enum =
	SOC_ENUM_SINGLE_EXT(2, headset_switch_text);

static const struct soc_enum lo_enum =
	SOC_ENUM_SINGLE_EXT(4, lo_text);

static int headset_get_switch(struct snd_kcontrol *kcontrol,
	struct snd_ctl_elem_value *ucontrol)
{
97
	ucontrol->value.enumerated.item[0] = hs_switch;
98 99 100 101 102 103
	return 0;
}

static int headset_set_switch(struct snd_kcontrol *kcontrol,
	struct snd_ctl_elem_value *ucontrol)
{
104 105
	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
	struct snd_soc_dapm_context *dapm = &card->dapm;
106

107
	if (ucontrol->value.enumerated.item[0] == hs_switch)
108 109
		return 0;

110 111
	snd_soc_dapm_mutex_lock(dapm);

112
	if (ucontrol->value.enumerated.item[0]) {
113
		pr_debug("hs_set HS path\n");
114 115
		snd_soc_dapm_enable_pin_unlocked(dapm, "Headphones");
		snd_soc_dapm_disable_pin_unlocked(dapm, "EPOUT");
116 117
	} else {
		pr_debug("hs_set EP path\n");
118 119
		snd_soc_dapm_disable_pin_unlocked(dapm, "Headphones");
		snd_soc_dapm_enable_pin_unlocked(dapm, "EPOUT");
120
	}
121 122 123 124 125

	snd_soc_dapm_sync_unlocked(dapm);

	snd_soc_dapm_mutex_unlock(dapm);

126
	hs_switch = ucontrol->value.enumerated.item[0];
127 128 129 130

	return 0;
}

131
static void lo_enable_out_pins(struct snd_soc_dapm_context *dapm)
132
{
133 134 135 136 137 138
	snd_soc_dapm_enable_pin_unlocked(dapm, "IHFOUTL");
	snd_soc_dapm_enable_pin_unlocked(dapm, "IHFOUTR");
	snd_soc_dapm_enable_pin_unlocked(dapm, "LINEOUTL");
	snd_soc_dapm_enable_pin_unlocked(dapm, "LINEOUTR");
	snd_soc_dapm_enable_pin_unlocked(dapm, "VIB1OUT");
	snd_soc_dapm_enable_pin_unlocked(dapm, "VIB2OUT");
139
	if (hs_switch) {
140 141
		snd_soc_dapm_enable_pin_unlocked(dapm, "Headphones");
		snd_soc_dapm_disable_pin_unlocked(dapm, "EPOUT");
142
	} else {
143 144
		snd_soc_dapm_disable_pin_unlocked(dapm, "Headphones");
		snd_soc_dapm_enable_pin_unlocked(dapm, "EPOUT");
145 146 147 148 149 150
	}
}

static int lo_get_switch(struct snd_kcontrol *kcontrol,
	struct snd_ctl_elem_value *ucontrol)
{
151
	ucontrol->value.enumerated.item[0] = lo_dac;
152 153 154 155 156 157
	return 0;
}

static int lo_set_switch(struct snd_kcontrol *kcontrol,
	struct snd_ctl_elem_value *ucontrol)
{
158 159
	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
	struct snd_soc_dapm_context *dapm = &card->dapm;
160

161
	if (ucontrol->value.enumerated.item[0] == lo_dac)
162 163
		return 0;

164 165
	snd_soc_dapm_mutex_lock(dapm);

166 167 168
	/* we dont want to work with last state of lineout so just enable all
	 * pins and then disable pins not required
	 */
169
	lo_enable_out_pins(dapm);
170

171
	switch (ucontrol->value.enumerated.item[0]) {
172 173
	case 0:
		pr_debug("set vibra path\n");
174 175
		snd_soc_dapm_disable_pin_unlocked(dapm, "VIB1OUT");
		snd_soc_dapm_disable_pin_unlocked(dapm, "VIB2OUT");
176
		snd_soc_update_bits(mfld_codec, SN95031_LOCTL, 0x66, 0);
177 178 179 180
		break;

	case 1:
		pr_debug("set hs  path\n");
181 182
		snd_soc_dapm_disable_pin_unlocked(dapm, "Headphones");
		snd_soc_dapm_disable_pin_unlocked(dapm, "EPOUT");
183
		snd_soc_update_bits(mfld_codec, SN95031_LOCTL, 0x66, 0x22);
184 185 186 187
		break;

	case 2:
		pr_debug("set spkr path\n");
188 189
		snd_soc_dapm_disable_pin_unlocked(dapm, "IHFOUTL");
		snd_soc_dapm_disable_pin_unlocked(dapm, "IHFOUTR");
190
		snd_soc_update_bits(mfld_codec, SN95031_LOCTL, 0x66, 0x44);
191 192 193 194
		break;

	case 3:
		pr_debug("set null path\n");
195 196
		snd_soc_dapm_disable_pin_unlocked(dapm, "LINEOUTL");
		snd_soc_dapm_disable_pin_unlocked(dapm, "LINEOUTR");
197
		snd_soc_update_bits(mfld_codec, SN95031_LOCTL, 0x66, 0x66);
198 199
		break;
	}
200 201 202 203 204

	snd_soc_dapm_sync_unlocked(dapm);

	snd_soc_dapm_mutex_unlock(dapm);

205
	lo_dac = ucontrol->value.enumerated.item[0];
206 207 208 209 210 211 212 213 214 215
	return 0;
}

static const struct snd_kcontrol_new mfld_snd_controls[] = {
	SOC_ENUM_EXT("Playback Switch", headset_enum,
			headset_get_switch, headset_set_switch),
	SOC_ENUM_EXT("Lineout Mux", lo_enum,
			lo_get_switch, lo_set_switch),
};

216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
static const struct snd_soc_dapm_widget mfld_widgets[] = {
	SND_SOC_DAPM_HP("Headphones", NULL),
	SND_SOC_DAPM_MIC("Mic", NULL),
};

static const struct snd_soc_dapm_route mfld_map[] = {
	{"Headphones", NULL, "HPOUTR"},
	{"Headphones", NULL, "HPOUTL"},
	{"Mic", NULL, "AMIC1"},
};

static void mfld_jack_check(unsigned int intr_status)
{
	struct mfld_jack_data jack_data;

231 232 233
	if (!mfld_codec)
		return;

234 235 236
	jack_data.mfld_jack = &mfld_jack;
	jack_data.intr_id = intr_status;

237
	sn95031_jack_detection(mfld_codec, &jack_data);
238 239 240
	/* TODO: add american headset detection post gpiolib support */
}

241 242
static int mfld_init(struct snd_soc_pcm_runtime *runtime)
{
243
	struct snd_soc_dapm_context *dapm = &runtime->card->dapm;
244 245 246
	int ret_val;

	/* default is earpiece pin, userspace sets it explcitly */
247
	snd_soc_dapm_disable_pin(dapm, "Headphones");
248 249 250 251 252
	/* default is lineout NC, userspace sets it explcitly */
	snd_soc_dapm_disable_pin(dapm, "LINEOUTL");
	snd_soc_dapm_disable_pin(dapm, "LINEOUTR");
	lo_dac = 3;
	hs_switch = 0;
253 254 255
	/* we dont use linein in this so set to NC */
	snd_soc_dapm_disable_pin(dapm, "LINEINL");
	snd_soc_dapm_disable_pin(dapm, "LINEINR");
256 257

	/* Headset and button jack detection */
258 259 260 261
	ret_val = snd_soc_card_jack_new(runtime->card,
			"Intel(R) MID Audio Jack", SND_JACK_HEADSET |
			SND_JACK_BTN_0 | SND_JACK_BTN_1, &mfld_jack,
			mfld_jack_pins, ARRAY_SIZE(mfld_jack_pins));
262 263 264 265 266
	if (ret_val) {
		pr_err("jack creation failed\n");
		return ret_val;
	}

267 268 269 270 271 272
	ret_val = snd_soc_jack_add_zones(&mfld_jack,
			ARRAY_SIZE(mfld_zones), mfld_zones);
	if (ret_val) {
		pr_err("adding jack zones failed\n");
		return ret_val;
	}
273

274 275
	mfld_codec = runtime->codec;

276 277 278 279 280
	/* we want to check if anything is inserted at boot,
	 * so send a fake event to codec and it will read adc
	 * to find if anything is there or not */
	mfld_jack_check(MFLD_JACK_INSERT);
	return ret_val;
281 282
}

A
Axel Lin 已提交
283
static struct snd_soc_dai_link mfld_msic_dailink[] = {
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317
	{
		.name = "Medfield Headset",
		.stream_name = "Headset",
		.cpu_dai_name = "Headset-cpu-dai",
		.codec_dai_name = "SN95031 Headset",
		.codec_name = "sn95031",
		.platform_name = "sst-platform",
		.init = mfld_init,
	},
	{
		.name = "Medfield Speaker",
		.stream_name = "Speaker",
		.cpu_dai_name = "Speaker-cpu-dai",
		.codec_dai_name = "SN95031 Speaker",
		.codec_name = "sn95031",
		.platform_name = "sst-platform",
		.init = NULL,
	},
	{
		.name = "Medfield Vibra",
		.stream_name = "Vibra1",
		.cpu_dai_name = "Vibra1-cpu-dai",
		.codec_dai_name = "SN95031 Vibra1",
		.codec_name = "sn95031",
		.platform_name = "sst-platform",
		.init = NULL,
	},
	{
		.name = "Medfield Haptics",
		.stream_name = "Vibra2",
		.cpu_dai_name = "Vibra2-cpu-dai",
		.codec_dai_name = "SN95031 Vibra2",
		.codec_name = "sn95031",
		.platform_name = "sst-platform",
318 319 320 321 322 323 324 325 326
		.init = NULL,
	},
	{
		.name = "Medfield Compress",
		.stream_name = "Speaker",
		.cpu_dai_name = "Compress-cpu-dai",
		.codec_dai_name = "SN95031 Speaker",
		.codec_name = "sn95031",
		.platform_name = "sst-platform",
327 328 329 330 331 332 333
		.init = NULL,
	},
};

/* SoC card */
static struct snd_soc_card snd_soc_card_mfld = {
	.name = "medfield_audio",
334
	.owner = THIS_MODULE,
335 336
	.dai_link = mfld_msic_dailink,
	.num_links = ARRAY_SIZE(mfld_msic_dailink),
337 338 339 340 341 342 343

	.controls = mfld_snd_controls,
	.num_controls = ARRAY_SIZE(mfld_snd_controls),
	.dapm_widgets = mfld_widgets,
	.num_dapm_widgets = ARRAY_SIZE(mfld_widgets),
	.dapm_routes = mfld_map,
	.num_dapm_routes = ARRAY_SIZE(mfld_map),
344 345
};

346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364
static irqreturn_t snd_mfld_jack_intr_handler(int irq, void *dev)
{
	struct mfld_mc_private *mc_private = (struct mfld_mc_private *) dev;

	memcpy_fromio(&mc_private->interrupt_status,
			((void *)(mc_private->int_base)),
			sizeof(u8));
	return IRQ_WAKE_THREAD;
}

static irqreturn_t snd_mfld_jack_detection(int irq, void *data)
{
	struct mfld_mc_private *mc_drv_ctx = (struct mfld_mc_private *) data;

	mfld_jack_check(mc_drv_ctx->interrupt_status);

	return IRQ_HANDLED;
}

365
static int snd_mfld_mc_probe(struct platform_device *pdev)
366
{
367 368 369
	int ret_val = 0, irq;
	struct mfld_mc_private *mc_drv_ctx;
	struct resource *irq_mem;
370 371 372

	pr_debug("snd_mfld_mc_probe called\n");

373 374 375 376 377
	/* retrive the irq number */
	irq = platform_get_irq(pdev, 0);

	/* audio interrupt base of SRAM location where
	 * interrupts are stored by System FW */
378
	mc_drv_ctx = devm_kzalloc(&pdev->dev, sizeof(*mc_drv_ctx), GFP_ATOMIC);
379 380
	if (!mc_drv_ctx) {
		pr_err("allocation failed\n");
381 382
		return -ENOMEM;
	}
383 384 385 386 387

	irq_mem = platform_get_resource_byname(
				pdev, IORESOURCE_MEM, "IRQ_BASE");
	if (!irq_mem) {
		pr_err("no mem resource given\n");
388
		return -ENODEV;
389
	}
390 391
	mc_drv_ctx->int_base = devm_ioremap_nocache(&pdev->dev, irq_mem->start,
						    resource_size(irq_mem));
392 393
	if (!mc_drv_ctx->int_base) {
		pr_err("Mapping of cache failed\n");
394
		return -ENOMEM;
395 396
	}
	/* register for interrupt */
397 398
	ret_val = devm_request_threaded_irq(&pdev->dev, irq,
			snd_mfld_jack_intr_handler,
399 400 401 402
			snd_mfld_jack_detection,
			IRQF_SHARED, pdev->dev.driver->name, mc_drv_ctx);
	if (ret_val) {
		pr_err("cannot register IRQ\n");
403
		return ret_val;
404
	}
405 406
	/* register the soc card */
	snd_soc_card_mfld.dev = &pdev->dev;
407
	ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_mfld);
408
	if (ret_val) {
409
		pr_debug("snd_soc_register_card failed %d\n", ret_val);
410
		return ret_val;
411
	}
412
	platform_set_drvdata(pdev, mc_drv_ctx);
413
	pr_debug("successfully exited probe\n");
414
	return 0;
415 416 417 418 419 420 421 422 423
}

static struct platform_driver snd_mfld_mc_driver = {
	.driver = {
		.name = "msic_audio",
	},
	.probe = snd_mfld_mc_probe,
};

424
module_platform_driver(snd_mfld_mc_driver);
425 426 427 428 429 430

MODULE_DESCRIPTION("ASoC Intel(R) MID Machine driver");
MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:msic-audio");