| 
									
										
										
										
											2011-01-04 20:16:50 +05:30
										 |  |  | /*
 | 
					
						
							|  |  |  |  *  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>
 | 
					
						
							| 
									
										
										
										
											2011-02-09 21:44:33 +05:30
										 |  |  | #include <linux/io.h>
 | 
					
						
							| 
									
										
										
										
											2011-07-15 12:38:28 -04:00
										 |  |  | #include <linux/module.h>
 | 
					
						
							| 
									
										
										
										
											2011-01-04 20:16:50 +05:30
										 |  |  | #include <sound/pcm.h>
 | 
					
						
							|  |  |  | #include <sound/pcm_params.h>
 | 
					
						
							|  |  |  | #include <sound/soc.h>
 | 
					
						
							| 
									
										
										
										
											2011-02-09 21:44:33 +05:30
										 |  |  | #include <sound/jack.h>
 | 
					
						
							| 
									
										
										
										
											2011-01-04 20:16:50 +05:30
										 |  |  | #include "../codecs/sn95031.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define MID_MONO 1
 | 
					
						
							|  |  |  | #define MID_STEREO 2
 | 
					
						
							|  |  |  | #define MID_MAX_CAP 5
 | 
					
						
							| 
									
										
										
										
											2011-02-09 21:44:33 +05:30
										 |  |  | #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, | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2011-01-04 20:16:50 +05:30
										 |  |  | 
 | 
					
						
							|  |  |  | static unsigned int	hs_switch; | 
					
						
							|  |  |  | static unsigned int	lo_dac; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-02-09 21:44:33 +05:30
										 |  |  | 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, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-02-10 12:58:01 +05:30
										 |  |  | /* 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}, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-01-04 20:16:50 +05:30
										 |  |  | /* sound card controls */ | 
					
						
							|  |  |  | static const char *headset_switch_text[] = {"Earpiece", "Headset"}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const char *lo_text[] = {"Vibra", "Headset", "IHF", "None"}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	ucontrol->value.integer.value[0] = hs_switch; | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int headset_set_switch(struct snd_kcontrol *kcontrol, | 
					
						
							|  |  |  | 	struct snd_ctl_elem_value *ucontrol) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ucontrol->value.integer.value[0] == hs_switch) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ucontrol->value.integer.value[0]) { | 
					
						
							|  |  |  | 		pr_debug("hs_set HS path\n"); | 
					
						
							| 
									
										
										
										
											2011-02-09 21:44:33 +05:30
										 |  |  | 		snd_soc_dapm_enable_pin(&codec->dapm, "Headphones"); | 
					
						
							| 
									
										
										
										
											2011-01-04 20:16:50 +05:30
										 |  |  | 		snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT"); | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		pr_debug("hs_set EP path\n"); | 
					
						
							| 
									
										
										
										
											2011-02-09 21:44:33 +05:30
										 |  |  | 		snd_soc_dapm_disable_pin(&codec->dapm, "Headphones"); | 
					
						
							| 
									
										
										
										
											2011-01-04 20:16:50 +05:30
										 |  |  | 		snd_soc_dapm_enable_pin(&codec->dapm, "EPOUT"); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	snd_soc_dapm_sync(&codec->dapm); | 
					
						
							|  |  |  | 	hs_switch = ucontrol->value.integer.value[0]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void lo_enable_out_pins(struct snd_soc_codec *codec) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	snd_soc_dapm_enable_pin(&codec->dapm, "IHFOUTL"); | 
					
						
							|  |  |  | 	snd_soc_dapm_enable_pin(&codec->dapm, "IHFOUTR"); | 
					
						
							|  |  |  | 	snd_soc_dapm_enable_pin(&codec->dapm, "LINEOUTL"); | 
					
						
							|  |  |  | 	snd_soc_dapm_enable_pin(&codec->dapm, "LINEOUTR"); | 
					
						
							|  |  |  | 	snd_soc_dapm_enable_pin(&codec->dapm, "VIB1OUT"); | 
					
						
							|  |  |  | 	snd_soc_dapm_enable_pin(&codec->dapm, "VIB2OUT"); | 
					
						
							|  |  |  | 	if (hs_switch) { | 
					
						
							| 
									
										
										
										
											2011-02-09 21:44:33 +05:30
										 |  |  | 		snd_soc_dapm_enable_pin(&codec->dapm, "Headphones"); | 
					
						
							| 
									
										
										
										
											2011-01-04 20:16:50 +05:30
										 |  |  | 		snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT"); | 
					
						
							|  |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2011-02-09 21:44:33 +05:30
										 |  |  | 		snd_soc_dapm_disable_pin(&codec->dapm, "Headphones"); | 
					
						
							| 
									
										
										
										
											2011-01-04 20:16:50 +05:30
										 |  |  | 		snd_soc_dapm_enable_pin(&codec->dapm, "EPOUT"); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int lo_get_switch(struct snd_kcontrol *kcontrol, | 
					
						
							|  |  |  | 	struct snd_ctl_elem_value *ucontrol) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	ucontrol->value.integer.value[0] = lo_dac; | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int lo_set_switch(struct snd_kcontrol *kcontrol, | 
					
						
							|  |  |  | 	struct snd_ctl_elem_value *ucontrol) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ucontrol->value.integer.value[0] == lo_dac) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* we dont want to work with last state of lineout so just enable all
 | 
					
						
							|  |  |  | 	 * pins and then disable pins not required | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	lo_enable_out_pins(codec); | 
					
						
							|  |  |  | 	switch (ucontrol->value.integer.value[0]) { | 
					
						
							|  |  |  | 	case 0: | 
					
						
							|  |  |  | 		pr_debug("set vibra path\n"); | 
					
						
							|  |  |  | 		snd_soc_dapm_disable_pin(&codec->dapm, "VIB1OUT"); | 
					
						
							|  |  |  | 		snd_soc_dapm_disable_pin(&codec->dapm, "VIB2OUT"); | 
					
						
							|  |  |  | 		snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case 1: | 
					
						
							|  |  |  | 		pr_debug("set hs  path\n"); | 
					
						
							| 
									
										
										
										
											2011-02-09 21:44:33 +05:30
										 |  |  | 		snd_soc_dapm_disable_pin(&codec->dapm, "Headphones"); | 
					
						
							| 
									
										
										
										
											2011-01-04 20:16:50 +05:30
										 |  |  | 		snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT"); | 
					
						
							|  |  |  | 		snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x22); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case 2: | 
					
						
							|  |  |  | 		pr_debug("set spkr path\n"); | 
					
						
							|  |  |  | 		snd_soc_dapm_disable_pin(&codec->dapm, "IHFOUTL"); | 
					
						
							|  |  |  | 		snd_soc_dapm_disable_pin(&codec->dapm, "IHFOUTR"); | 
					
						
							|  |  |  | 		snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x44); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case 3: | 
					
						
							|  |  |  | 		pr_debug("set null path\n"); | 
					
						
							|  |  |  | 		snd_soc_dapm_disable_pin(&codec->dapm, "LINEOUTL"); | 
					
						
							|  |  |  | 		snd_soc_dapm_disable_pin(&codec->dapm, "LINEOUTR"); | 
					
						
							|  |  |  | 		snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x66); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	snd_soc_dapm_sync(&codec->dapm); | 
					
						
							|  |  |  | 	lo_dac = ucontrol->value.integer.value[0]; | 
					
						
							|  |  |  | 	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), | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-02-09 21:44:33 +05:30
										 |  |  | 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; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	jack_data.mfld_jack = &mfld_jack; | 
					
						
							|  |  |  | 	jack_data.intr_id = intr_status; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	sn95031_jack_detection(&jack_data); | 
					
						
							|  |  |  | 	/* TODO: add american headset detection post gpiolib support */ | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-01-04 20:16:50 +05:30
										 |  |  | static int mfld_init(struct snd_soc_pcm_runtime *runtime) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct snd_soc_codec *codec = runtime->codec; | 
					
						
							|  |  |  | 	struct snd_soc_dapm_context *dapm = &codec->dapm; | 
					
						
							|  |  |  | 	int ret_val; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-02-09 21:44:33 +05:30
										 |  |  | 	/* Add jack sense widgets */ | 
					
						
							|  |  |  | 	snd_soc_dapm_new_controls(dapm, mfld_widgets, ARRAY_SIZE(mfld_widgets)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Set up the map */ | 
					
						
							|  |  |  | 	snd_soc_dapm_add_routes(dapm, mfld_map, ARRAY_SIZE(mfld_map)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* always connected */ | 
					
						
							|  |  |  | 	snd_soc_dapm_enable_pin(dapm, "Headphones"); | 
					
						
							|  |  |  | 	snd_soc_dapm_enable_pin(dapm, "Mic"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-02-03 17:43:09 +00:00
										 |  |  | 	ret_val = snd_soc_add_codec_controls(codec, mfld_snd_controls, | 
					
						
							| 
									
										
										
										
											2011-01-04 20:16:50 +05:30
										 |  |  | 				ARRAY_SIZE(mfld_snd_controls)); | 
					
						
							|  |  |  | 	if (ret_val) { | 
					
						
							|  |  |  | 		pr_err("soc_add_controls failed %d", ret_val); | 
					
						
							|  |  |  | 		return ret_val; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	/* default is earpiece pin, userspace sets it explcitly */ | 
					
						
							| 
									
										
										
										
											2011-02-09 21:44:33 +05:30
										 |  |  | 	snd_soc_dapm_disable_pin(dapm, "Headphones"); | 
					
						
							| 
									
										
										
										
											2011-01-04 20:16:50 +05:30
										 |  |  | 	/* 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; | 
					
						
							| 
									
										
										
										
											2011-01-28 22:28:32 +05:30
										 |  |  | 	/* we dont use linein in this so set to NC */ | 
					
						
							|  |  |  | 	snd_soc_dapm_disable_pin(dapm, "LINEINL"); | 
					
						
							|  |  |  | 	snd_soc_dapm_disable_pin(dapm, "LINEINR"); | 
					
						
							| 
									
										
										
										
											2011-02-09 21:44:33 +05:30
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Headset and button jack detection */ | 
					
						
							|  |  |  | 	ret_val = snd_soc_jack_new(codec, "Intel(R) MID Audio Jack", | 
					
						
							|  |  |  | 			SND_JACK_HEADSET | SND_JACK_BTN_0 | | 
					
						
							|  |  |  | 			SND_JACK_BTN_1, &mfld_jack); | 
					
						
							|  |  |  | 	if (ret_val) { | 
					
						
							|  |  |  | 		pr_err("jack creation failed\n"); | 
					
						
							|  |  |  | 		return ret_val; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret_val = snd_soc_jack_add_pins(&mfld_jack, | 
					
						
							|  |  |  | 			ARRAY_SIZE(mfld_jack_pins), mfld_jack_pins); | 
					
						
							|  |  |  | 	if (ret_val) { | 
					
						
							|  |  |  | 		pr_err("adding jack pins failed\n"); | 
					
						
							|  |  |  | 		return ret_val; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2011-02-10 12:58:01 +05:30
										 |  |  | 	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; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2011-02-09 21:44:33 +05:30
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* 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; | 
					
						
							| 
									
										
										
										
											2011-01-04 20:16:50 +05:30
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-14 19:23:01 +08:00
										 |  |  | static struct snd_soc_dai_link mfld_msic_dailink[] = { | 
					
						
							| 
									
										
										
										
											2011-01-04 20:16:50 +05:30
										 |  |  | 	{ | 
					
						
							|  |  |  | 		.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", | 
					
						
							| 
									
										
										
										
											2012-08-16 17:10:42 +05:30
										 |  |  | 		.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", | 
					
						
							| 
									
										
										
										
											2011-01-04 20:16:50 +05:30
										 |  |  | 		.init = NULL, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* SoC card */ | 
					
						
							|  |  |  | static struct snd_soc_card snd_soc_card_mfld = { | 
					
						
							|  |  |  | 	.name = "medfield_audio", | 
					
						
							| 
									
										
										
										
											2011-12-23 14:50:17 +08:00
										 |  |  | 	.owner = THIS_MODULE, | 
					
						
							| 
									
										
										
										
											2011-01-04 20:16:50 +05:30
										 |  |  | 	.dai_link = mfld_msic_dailink, | 
					
						
							|  |  |  | 	.num_links = ARRAY_SIZE(mfld_msic_dailink), | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-02-09 21:44:33 +05:30
										 |  |  | 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; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (mfld_jack.codec == NULL) | 
					
						
							|  |  |  | 		return IRQ_HANDLED; | 
					
						
							|  |  |  | 	mfld_jack_check(mc_drv_ctx->interrupt_status); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return IRQ_HANDLED; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-07 09:26:25 -05:00
										 |  |  | static int snd_mfld_mc_probe(struct platform_device *pdev) | 
					
						
							| 
									
										
										
										
											2011-01-04 20:16:50 +05:30
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2011-02-09 21:44:33 +05:30
										 |  |  | 	int ret_val = 0, irq; | 
					
						
							|  |  |  | 	struct mfld_mc_private *mc_drv_ctx; | 
					
						
							|  |  |  | 	struct resource *irq_mem; | 
					
						
							| 
									
										
										
										
											2011-01-04 20:16:50 +05:30
										 |  |  | 
 | 
					
						
							|  |  |  | 	pr_debug("snd_mfld_mc_probe called\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-02-09 21:44:33 +05:30
										 |  |  | 	/* retrive the irq number */ | 
					
						
							|  |  |  | 	irq = platform_get_irq(pdev, 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* audio interrupt base of SRAM location where
 | 
					
						
							|  |  |  | 	 * interrupts are stored by System FW */ | 
					
						
							| 
									
										
										
										
											2013-06-25 10:16:56 +08:00
										 |  |  | 	mc_drv_ctx = devm_kzalloc(&pdev->dev, sizeof(*mc_drv_ctx), GFP_ATOMIC); | 
					
						
							| 
									
										
										
										
											2011-02-09 21:44:33 +05:30
										 |  |  | 	if (!mc_drv_ctx) { | 
					
						
							|  |  |  | 		pr_err("allocation failed\n"); | 
					
						
							| 
									
										
										
										
											2011-01-04 20:16:50 +05:30
										 |  |  | 		return -ENOMEM; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2011-02-09 21:44:33 +05:30
										 |  |  | 
 | 
					
						
							|  |  |  | 	irq_mem = platform_get_resource_byname( | 
					
						
							|  |  |  | 				pdev, IORESOURCE_MEM, "IRQ_BASE"); | 
					
						
							|  |  |  | 	if (!irq_mem) { | 
					
						
							|  |  |  | 		pr_err("no mem resource given\n"); | 
					
						
							| 
									
										
										
										
											2013-06-25 10:16:56 +08:00
										 |  |  | 		return -ENODEV; | 
					
						
							| 
									
										
										
										
											2011-02-09 21:44:33 +05:30
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-06-25 10:16:56 +08:00
										 |  |  | 	mc_drv_ctx->int_base = devm_ioremap_nocache(&pdev->dev, irq_mem->start, | 
					
						
							|  |  |  | 						    resource_size(irq_mem)); | 
					
						
							| 
									
										
										
										
											2011-02-09 21:44:33 +05:30
										 |  |  | 	if (!mc_drv_ctx->int_base) { | 
					
						
							|  |  |  | 		pr_err("Mapping of cache failed\n"); | 
					
						
							| 
									
										
										
										
											2013-06-25 10:16:56 +08:00
										 |  |  | 		return -ENOMEM; | 
					
						
							| 
									
										
										
										
											2011-02-09 21:44:33 +05:30
										 |  |  | 	} | 
					
						
							|  |  |  | 	/* register for interrupt */ | 
					
						
							| 
									
										
										
										
											2013-06-25 10:16:56 +08:00
										 |  |  | 	ret_val = devm_request_threaded_irq(&pdev->dev, irq, | 
					
						
							|  |  |  | 			snd_mfld_jack_intr_handler, | 
					
						
							| 
									
										
										
										
											2011-02-09 21:44:33 +05:30
										 |  |  | 			snd_mfld_jack_detection, | 
					
						
							|  |  |  | 			IRQF_SHARED, pdev->dev.driver->name, mc_drv_ctx); | 
					
						
							|  |  |  | 	if (ret_val) { | 
					
						
							|  |  |  | 		pr_err("cannot register IRQ\n"); | 
					
						
							| 
									
										
										
										
											2013-06-25 10:16:56 +08:00
										 |  |  | 		return ret_val; | 
					
						
							| 
									
										
										
										
											2011-02-09 21:44:33 +05:30
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2011-02-15 18:28:55 +05:30
										 |  |  | 	/* register the soc card */ | 
					
						
							|  |  |  | 	snd_soc_card_mfld.dev = &pdev->dev; | 
					
						
							| 
									
										
										
										
											2013-09-17 09:49:04 +05:30
										 |  |  | 	ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_mfld); | 
					
						
							| 
									
										
										
										
											2011-01-04 20:16:50 +05:30
										 |  |  | 	if (ret_val) { | 
					
						
							| 
									
										
										
										
											2011-02-15 18:28:55 +05:30
										 |  |  | 		pr_debug("snd_soc_register_card failed %d\n", ret_val); | 
					
						
							| 
									
										
										
										
											2013-06-25 10:16:56 +08:00
										 |  |  | 		return ret_val; | 
					
						
							| 
									
										
										
										
											2011-01-04 20:16:50 +05:30
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2011-02-09 21:44:33 +05:30
										 |  |  | 	platform_set_drvdata(pdev, mc_drv_ctx); | 
					
						
							| 
									
										
										
										
											2011-01-04 20:16:50 +05:30
										 |  |  | 	pr_debug("successfully exited probe\n"); | 
					
						
							| 
									
										
										
										
											2013-06-25 10:16:56 +08:00
										 |  |  | 	return 0; | 
					
						
							| 
									
										
										
										
											2011-01-04 20:16:50 +05:30
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct platform_driver snd_mfld_mc_driver = { | 
					
						
							|  |  |  | 	.driver = { | 
					
						
							|  |  |  | 		.owner = THIS_MODULE, | 
					
						
							|  |  |  | 		.name = "msic_audio", | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	.probe = snd_mfld_mc_probe, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-11-24 11:51:56 +08:00
										 |  |  | module_platform_driver(snd_mfld_mc_driver); | 
					
						
							| 
									
										
										
										
											2011-01-04 20:16:50 +05:30
										 |  |  | 
 | 
					
						
							|  |  |  | 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"); |