Path: blob/master/sound/soc/mid-x86/mfld_machine.c
10819 views
/*1* mfld_machine.c - ASoc Machine driver for Intel Medfield MID platform2*3* Copyright (C) 2010 Intel Corp4* Author: Vinod Koul <[email protected]>5* Author: Harsha Priya <[email protected]>6* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~7*8* This program is free software; you can redistribute it and/or modify9* it under the terms of the GNU General Public License as published by10* the Free Software Foundation; version 2 of the License.11*12* This program is distributed in the hope that it will be useful, but13* WITHOUT ANY WARRANTY; without even the implied warranty of14* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU15* General Public License for more details.16*17* You should have received a copy of the GNU General Public License along18* with this program; if not, write to the Free Software Foundation, Inc.,19* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.20*21* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~22*/2324#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt2526#include <linux/init.h>27#include <linux/device.h>28#include <linux/slab.h>29#include <linux/io.h>30#include <sound/pcm.h>31#include <sound/pcm_params.h>32#include <sound/soc.h>33#include <sound/jack.h>34#include "../codecs/sn95031.h"3536#define MID_MONO 137#define MID_STEREO 238#define MID_MAX_CAP 539#define MFLD_JACK_INSERT 0x044041enum soc_mic_bias_zones {42MFLD_MV_START = 0,43/* mic bias volutage range for Headphones*/44MFLD_MV_HP = 400,45/* mic bias volutage range for American Headset*/46MFLD_MV_AM_HS = 650,47/* mic bias volutage range for Headset*/48MFLD_MV_HS = 2000,49MFLD_MV_UNDEFINED,50};5152static unsigned int hs_switch;53static unsigned int lo_dac;5455struct mfld_mc_private {56struct platform_device *socdev;57void __iomem *int_base;58struct snd_soc_codec *codec;59u8 interrupt_status;60};6162struct snd_soc_jack mfld_jack;6364/*Headset jack detection DAPM pins */65static struct snd_soc_jack_pin mfld_jack_pins[] = {66{67.pin = "Headphones",68.mask = SND_JACK_HEADPHONE,69},70{71.pin = "AMIC1",72.mask = SND_JACK_MICROPHONE,73},74};7576/* jack detection voltage zones */77static struct snd_soc_jack_zone mfld_zones[] = {78{MFLD_MV_START, MFLD_MV_AM_HS, SND_JACK_HEADPHONE},79{MFLD_MV_AM_HS, MFLD_MV_HS, SND_JACK_HEADSET},80};8182/* sound card controls */83static const char *headset_switch_text[] = {"Earpiece", "Headset"};8485static const char *lo_text[] = {"Vibra", "Headset", "IHF", "None"};8687static const struct soc_enum headset_enum =88SOC_ENUM_SINGLE_EXT(2, headset_switch_text);8990static const struct soc_enum lo_enum =91SOC_ENUM_SINGLE_EXT(4, lo_text);9293static int headset_get_switch(struct snd_kcontrol *kcontrol,94struct snd_ctl_elem_value *ucontrol)95{96ucontrol->value.integer.value[0] = hs_switch;97return 0;98}99100static int headset_set_switch(struct snd_kcontrol *kcontrol,101struct snd_ctl_elem_value *ucontrol)102{103struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);104105if (ucontrol->value.integer.value[0] == hs_switch)106return 0;107108if (ucontrol->value.integer.value[0]) {109pr_debug("hs_set HS path\n");110snd_soc_dapm_enable_pin(&codec->dapm, "Headphones");111snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT");112} else {113pr_debug("hs_set EP path\n");114snd_soc_dapm_disable_pin(&codec->dapm, "Headphones");115snd_soc_dapm_enable_pin(&codec->dapm, "EPOUT");116}117snd_soc_dapm_sync(&codec->dapm);118hs_switch = ucontrol->value.integer.value[0];119120return 0;121}122123static void lo_enable_out_pins(struct snd_soc_codec *codec)124{125snd_soc_dapm_enable_pin(&codec->dapm, "IHFOUTL");126snd_soc_dapm_enable_pin(&codec->dapm, "IHFOUTR");127snd_soc_dapm_enable_pin(&codec->dapm, "LINEOUTL");128snd_soc_dapm_enable_pin(&codec->dapm, "LINEOUTR");129snd_soc_dapm_enable_pin(&codec->dapm, "VIB1OUT");130snd_soc_dapm_enable_pin(&codec->dapm, "VIB2OUT");131if (hs_switch) {132snd_soc_dapm_enable_pin(&codec->dapm, "Headphones");133snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT");134} else {135snd_soc_dapm_disable_pin(&codec->dapm, "Headphones");136snd_soc_dapm_enable_pin(&codec->dapm, "EPOUT");137}138}139140static int lo_get_switch(struct snd_kcontrol *kcontrol,141struct snd_ctl_elem_value *ucontrol)142{143ucontrol->value.integer.value[0] = lo_dac;144return 0;145}146147static int lo_set_switch(struct snd_kcontrol *kcontrol,148struct snd_ctl_elem_value *ucontrol)149{150struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);151152if (ucontrol->value.integer.value[0] == lo_dac)153return 0;154155/* we dont want to work with last state of lineout so just enable all156* pins and then disable pins not required157*/158lo_enable_out_pins(codec);159switch (ucontrol->value.integer.value[0]) {160case 0:161pr_debug("set vibra path\n");162snd_soc_dapm_disable_pin(&codec->dapm, "VIB1OUT");163snd_soc_dapm_disable_pin(&codec->dapm, "VIB2OUT");164snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0);165break;166167case 1:168pr_debug("set hs path\n");169snd_soc_dapm_disable_pin(&codec->dapm, "Headphones");170snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT");171snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x22);172break;173174case 2:175pr_debug("set spkr path\n");176snd_soc_dapm_disable_pin(&codec->dapm, "IHFOUTL");177snd_soc_dapm_disable_pin(&codec->dapm, "IHFOUTR");178snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x44);179break;180181case 3:182pr_debug("set null path\n");183snd_soc_dapm_disable_pin(&codec->dapm, "LINEOUTL");184snd_soc_dapm_disable_pin(&codec->dapm, "LINEOUTR");185snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x66);186break;187}188snd_soc_dapm_sync(&codec->dapm);189lo_dac = ucontrol->value.integer.value[0];190return 0;191}192193static const struct snd_kcontrol_new mfld_snd_controls[] = {194SOC_ENUM_EXT("Playback Switch", headset_enum,195headset_get_switch, headset_set_switch),196SOC_ENUM_EXT("Lineout Mux", lo_enum,197lo_get_switch, lo_set_switch),198};199200static const struct snd_soc_dapm_widget mfld_widgets[] = {201SND_SOC_DAPM_HP("Headphones", NULL),202SND_SOC_DAPM_MIC("Mic", NULL),203};204205static const struct snd_soc_dapm_route mfld_map[] = {206{"Headphones", NULL, "HPOUTR"},207{"Headphones", NULL, "HPOUTL"},208{"Mic", NULL, "AMIC1"},209};210211static void mfld_jack_check(unsigned int intr_status)212{213struct mfld_jack_data jack_data;214215jack_data.mfld_jack = &mfld_jack;216jack_data.intr_id = intr_status;217218sn95031_jack_detection(&jack_data);219/* TODO: add american headset detection post gpiolib support */220}221222static int mfld_init(struct snd_soc_pcm_runtime *runtime)223{224struct snd_soc_codec *codec = runtime->codec;225struct snd_soc_dapm_context *dapm = &codec->dapm;226int ret_val;227228/* Add jack sense widgets */229snd_soc_dapm_new_controls(dapm, mfld_widgets, ARRAY_SIZE(mfld_widgets));230231/* Set up the map */232snd_soc_dapm_add_routes(dapm, mfld_map, ARRAY_SIZE(mfld_map));233234/* always connected */235snd_soc_dapm_enable_pin(dapm, "Headphones");236snd_soc_dapm_enable_pin(dapm, "Mic");237snd_soc_dapm_sync(dapm);238239ret_val = snd_soc_add_controls(codec, mfld_snd_controls,240ARRAY_SIZE(mfld_snd_controls));241if (ret_val) {242pr_err("soc_add_controls failed %d", ret_val);243return ret_val;244}245/* default is earpiece pin, userspace sets it explcitly */246snd_soc_dapm_disable_pin(dapm, "Headphones");247/* default is lineout NC, userspace sets it explcitly */248snd_soc_dapm_disable_pin(dapm, "LINEOUTL");249snd_soc_dapm_disable_pin(dapm, "LINEOUTR");250lo_dac = 3;251hs_switch = 0;252/* we dont use linein in this so set to NC */253snd_soc_dapm_disable_pin(dapm, "LINEINL");254snd_soc_dapm_disable_pin(dapm, "LINEINR");255snd_soc_dapm_sync(dapm);256257/* Headset and button jack detection */258ret_val = snd_soc_jack_new(codec, "Intel(R) MID Audio Jack",259SND_JACK_HEADSET | SND_JACK_BTN_0 |260SND_JACK_BTN_1, &mfld_jack);261if (ret_val) {262pr_err("jack creation failed\n");263return ret_val;264}265266ret_val = snd_soc_jack_add_pins(&mfld_jack,267ARRAY_SIZE(mfld_jack_pins), mfld_jack_pins);268if (ret_val) {269pr_err("adding jack pins failed\n");270return ret_val;271}272ret_val = snd_soc_jack_add_zones(&mfld_jack,273ARRAY_SIZE(mfld_zones), mfld_zones);274if (ret_val) {275pr_err("adding jack zones failed\n");276return ret_val;277}278279/* we want to check if anything is inserted at boot,280* so send a fake event to codec and it will read adc281* to find if anything is there or not */282mfld_jack_check(MFLD_JACK_INSERT);283return ret_val;284}285286struct snd_soc_dai_link mfld_msic_dailink[] = {287{288.name = "Medfield Headset",289.stream_name = "Headset",290.cpu_dai_name = "Headset-cpu-dai",291.codec_dai_name = "SN95031 Headset",292.codec_name = "sn95031",293.platform_name = "sst-platform",294.init = mfld_init,295},296{297.name = "Medfield Speaker",298.stream_name = "Speaker",299.cpu_dai_name = "Speaker-cpu-dai",300.codec_dai_name = "SN95031 Speaker",301.codec_name = "sn95031",302.platform_name = "sst-platform",303.init = NULL,304},305{306.name = "Medfield Vibra",307.stream_name = "Vibra1",308.cpu_dai_name = "Vibra1-cpu-dai",309.codec_dai_name = "SN95031 Vibra1",310.codec_name = "sn95031",311.platform_name = "sst-platform",312.init = NULL,313},314{315.name = "Medfield Haptics",316.stream_name = "Vibra2",317.cpu_dai_name = "Vibra2-cpu-dai",318.codec_dai_name = "SN95031 Vibra2",319.codec_name = "sn95031",320.platform_name = "sst-platform",321.init = NULL,322},323};324325/* SoC card */326static struct snd_soc_card snd_soc_card_mfld = {327.name = "medfield_audio",328.dai_link = mfld_msic_dailink,329.num_links = ARRAY_SIZE(mfld_msic_dailink),330};331332static irqreturn_t snd_mfld_jack_intr_handler(int irq, void *dev)333{334struct mfld_mc_private *mc_private = (struct mfld_mc_private *) dev;335336memcpy_fromio(&mc_private->interrupt_status,337((void *)(mc_private->int_base)),338sizeof(u8));339return IRQ_WAKE_THREAD;340}341342static irqreturn_t snd_mfld_jack_detection(int irq, void *data)343{344struct mfld_mc_private *mc_drv_ctx = (struct mfld_mc_private *) data;345346if (mfld_jack.codec == NULL)347return IRQ_HANDLED;348mfld_jack_check(mc_drv_ctx->interrupt_status);349350return IRQ_HANDLED;351}352353static int __devinit snd_mfld_mc_probe(struct platform_device *pdev)354{355int ret_val = 0, irq;356struct mfld_mc_private *mc_drv_ctx;357struct resource *irq_mem;358359pr_debug("snd_mfld_mc_probe called\n");360361/* retrive the irq number */362irq = platform_get_irq(pdev, 0);363364/* audio interrupt base of SRAM location where365* interrupts are stored by System FW */366mc_drv_ctx = kzalloc(sizeof(*mc_drv_ctx), GFP_ATOMIC);367if (!mc_drv_ctx) {368pr_err("allocation failed\n");369return -ENOMEM;370}371372irq_mem = platform_get_resource_byname(373pdev, IORESOURCE_MEM, "IRQ_BASE");374if (!irq_mem) {375pr_err("no mem resource given\n");376ret_val = -ENODEV;377goto unalloc;378}379mc_drv_ctx->int_base = ioremap_nocache(irq_mem->start,380resource_size(irq_mem));381if (!mc_drv_ctx->int_base) {382pr_err("Mapping of cache failed\n");383ret_val = -ENOMEM;384goto unalloc;385}386/* register for interrupt */387ret_val = request_threaded_irq(irq, snd_mfld_jack_intr_handler,388snd_mfld_jack_detection,389IRQF_SHARED, pdev->dev.driver->name, mc_drv_ctx);390if (ret_val) {391pr_err("cannot register IRQ\n");392goto unalloc;393}394/* register the soc card */395snd_soc_card_mfld.dev = &pdev->dev;396ret_val = snd_soc_register_card(&snd_soc_card_mfld);397if (ret_val) {398pr_debug("snd_soc_register_card failed %d\n", ret_val);399goto freeirq;400}401platform_set_drvdata(pdev, mc_drv_ctx);402pr_debug("successfully exited probe\n");403return ret_val;404405freeirq:406free_irq(irq, mc_drv_ctx);407unalloc:408kfree(mc_drv_ctx);409return ret_val;410}411412static int __devexit snd_mfld_mc_remove(struct platform_device *pdev)413{414struct mfld_mc_private *mc_drv_ctx = platform_get_drvdata(pdev);415416pr_debug("snd_mfld_mc_remove called\n");417free_irq(platform_get_irq(pdev, 0), mc_drv_ctx);418snd_soc_unregister_card(&snd_soc_card_mfld);419kfree(mc_drv_ctx);420platform_set_drvdata(pdev, NULL);421return 0;422}423424static struct platform_driver snd_mfld_mc_driver = {425.driver = {426.owner = THIS_MODULE,427.name = "msic_audio",428},429.probe = snd_mfld_mc_probe,430.remove = __devexit_p(snd_mfld_mc_remove),431};432433static int __init snd_mfld_driver_init(void)434{435pr_debug("snd_mfld_driver_init called\n");436return platform_driver_register(&snd_mfld_mc_driver);437}438module_init(snd_mfld_driver_init);439440static void __exit snd_mfld_driver_exit(void)441{442pr_debug("snd_mfld_driver_exit called\n");443platform_driver_unregister(&snd_mfld_mc_driver);444}445module_exit(snd_mfld_driver_exit);446447MODULE_DESCRIPTION("ASoC Intel(R) MID Machine driver");448MODULE_AUTHOR("Vinod Koul <[email protected]>");449MODULE_AUTHOR("Harsha Priya <[email protected]>");450MODULE_LICENSE("GPL v2");451MODULE_ALIAS("platform:msic-audio");452453454