linux/sound/soc/mid-x86/mfld_machine.c
<<
>>
Prefs
   1/*
   2 *  mfld_machine.c - ASoc Machine driver for Intel Medfield MID platform
   3 *
   4 *  Copyright (C) 2010 Intel Corp
   5 *  Author: Vinod Koul <vinod.koul@intel.com>
   6 *  Author: Harsha Priya <priya.harsha@intel.com>
   7 *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   8 *
   9 *  This program is free software; you can redistribute it and/or modify
  10 *  it under the terms of the GNU General Public License as published by
  11 *  the Free Software Foundation; version 2 of the License.
  12 *
  13 *  This program is distributed in the hope that it will be useful, but
  14 *  WITHOUT ANY WARRANTY; without even the implied warranty of
  15 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  16 *  General Public License for more details.
  17 *
  18 *  You should have received a copy of the GNU General Public License along
  19 *  with this program; if not, write to the Free Software Foundation, Inc.,
  20 *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  21 *
  22 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  23 */
  24
  25#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  26
  27#include <linux/init.h>
  28#include <linux/device.h>
  29#include <linux/slab.h>
  30#include <linux/io.h>
  31#include <linux/module.h>
  32#include <sound/pcm.h>
  33#include <sound/pcm_params.h>
  34#include <sound/soc.h>
  35#include <sound/jack.h>
  36#include "../codecs/sn95031.h"
  37
  38#define MID_MONO 1
  39#define MID_STEREO 2
  40#define MID_MAX_CAP 5
  41#define MFLD_JACK_INSERT 0x04
  42
  43enum soc_mic_bias_zones {
  44        MFLD_MV_START = 0,
  45        /* mic bias volutage range for Headphones*/
  46        MFLD_MV_HP = 400,
  47        /* mic bias volutage range for American Headset*/
  48        MFLD_MV_AM_HS = 650,
  49        /* mic bias volutage range for Headset*/
  50        MFLD_MV_HS = 2000,
  51        MFLD_MV_UNDEFINED,
  52};
  53
  54static unsigned int     hs_switch;
  55static unsigned int     lo_dac;
  56
  57struct mfld_mc_private {
  58        void __iomem *int_base;
  59        u8 interrupt_status;
  60};
  61
  62struct snd_soc_jack mfld_jack;
  63
  64/*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};
  75
  76/* 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};
  81
  82/* sound card controls */
  83static const char *headset_switch_text[] = {"Earpiece", "Headset"};
  84
  85static const char *lo_text[] = {"Vibra", "Headset", "IHF", "None"};
  86
  87static const struct soc_enum headset_enum =
  88        SOC_ENUM_SINGLE_EXT(2, headset_switch_text);
  89
  90static const struct soc_enum lo_enum =
  91        SOC_ENUM_SINGLE_EXT(4, lo_text);
  92
  93static int headset_get_switch(struct snd_kcontrol *kcontrol,
  94        struct snd_ctl_elem_value *ucontrol)
  95{
  96        ucontrol->value.integer.value[0] = hs_switch;
  97        return 0;
  98}
  99
 100static int headset_set_switch(struct snd_kcontrol *kcontrol,
 101        struct snd_ctl_elem_value *ucontrol)
 102{
 103        struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);
 104
 105        if (ucontrol->value.integer.value[0] == hs_switch)
 106                return 0;
 107
 108        if (ucontrol->value.integer.value[0]) {
 109                pr_debug("hs_set HS path\n");
 110                snd_soc_dapm_enable_pin(&codec->dapm, "Headphones");
 111                snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT");
 112        } else {
 113                pr_debug("hs_set EP path\n");
 114                snd_soc_dapm_disable_pin(&codec->dapm, "Headphones");
 115                snd_soc_dapm_enable_pin(&codec->dapm, "EPOUT");
 116        }
 117        snd_soc_dapm_sync(&codec->dapm);
 118        hs_switch = ucontrol->value.integer.value[0];
 119
 120        return 0;
 121}
 122
 123static void lo_enable_out_pins(struct snd_soc_codec *codec)
 124{
 125        snd_soc_dapm_enable_pin(&codec->dapm, "IHFOUTL");
 126        snd_soc_dapm_enable_pin(&codec->dapm, "IHFOUTR");
 127        snd_soc_dapm_enable_pin(&codec->dapm, "LINEOUTL");
 128        snd_soc_dapm_enable_pin(&codec->dapm, "LINEOUTR");
 129        snd_soc_dapm_enable_pin(&codec->dapm, "VIB1OUT");
 130        snd_soc_dapm_enable_pin(&codec->dapm, "VIB2OUT");
 131        if (hs_switch) {
 132                snd_soc_dapm_enable_pin(&codec->dapm, "Headphones");
 133                snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT");
 134        } else {
 135                snd_soc_dapm_disable_pin(&codec->dapm, "Headphones");
 136                snd_soc_dapm_enable_pin(&codec->dapm, "EPOUT");
 137        }
 138}
 139
 140static int lo_get_switch(struct snd_kcontrol *kcontrol,
 141        struct snd_ctl_elem_value *ucontrol)
 142{
 143        ucontrol->value.integer.value[0] = lo_dac;
 144        return 0;
 145}
 146
 147static int lo_set_switch(struct snd_kcontrol *kcontrol,
 148        struct snd_ctl_elem_value *ucontrol)
 149{
 150        struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);
 151
 152        if (ucontrol->value.integer.value[0] == lo_dac)
 153                return 0;
 154
 155        /* we dont want to work with last state of lineout so just enable all
 156         * pins and then disable pins not required
 157         */
 158        lo_enable_out_pins(codec);
 159        switch (ucontrol->value.integer.value[0]) {
 160        case 0:
 161                pr_debug("set vibra path\n");
 162                snd_soc_dapm_disable_pin(&codec->dapm, "VIB1OUT");
 163                snd_soc_dapm_disable_pin(&codec->dapm, "VIB2OUT");
 164                snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0);
 165                break;
 166
 167        case 1:
 168                pr_debug("set hs  path\n");
 169                snd_soc_dapm_disable_pin(&codec->dapm, "Headphones");
 170                snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT");
 171                snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x22);
 172                break;
 173
 174        case 2:
 175                pr_debug("set spkr path\n");
 176                snd_soc_dapm_disable_pin(&codec->dapm, "IHFOUTL");
 177                snd_soc_dapm_disable_pin(&codec->dapm, "IHFOUTR");
 178                snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x44);
 179                break;
 180
 181        case 3:
 182                pr_debug("set null path\n");
 183                snd_soc_dapm_disable_pin(&codec->dapm, "LINEOUTL");
 184                snd_soc_dapm_disable_pin(&codec->dapm, "LINEOUTR");
 185                snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x66);
 186                break;
 187        }
 188        snd_soc_dapm_sync(&codec->dapm);
 189        lo_dac = ucontrol->value.integer.value[0];
 190        return 0;
 191}
 192
 193static const struct snd_kcontrol_new mfld_snd_controls[] = {
 194        SOC_ENUM_EXT("Playback Switch", headset_enum,
 195                        headset_get_switch, headset_set_switch),
 196        SOC_ENUM_EXT("Lineout Mux", lo_enum,
 197                        lo_get_switch, lo_set_switch),
 198};
 199
 200static const struct snd_soc_dapm_widget mfld_widgets[] = {
 201        SND_SOC_DAPM_HP("Headphones", NULL),
 202        SND_SOC_DAPM_MIC("Mic", NULL),
 203};
 204
 205static const struct snd_soc_dapm_route mfld_map[] = {
 206        {"Headphones", NULL, "HPOUTR"},
 207        {"Headphones", NULL, "HPOUTL"},
 208        {"Mic", NULL, "AMIC1"},
 209};
 210
 211static void mfld_jack_check(unsigned int intr_status)
 212{
 213        struct mfld_jack_data jack_data;
 214
 215        jack_data.mfld_jack = &mfld_jack;
 216        jack_data.intr_id = intr_status;
 217
 218        sn95031_jack_detection(&jack_data);
 219        /* TODO: add american headset detection post gpiolib support */
 220}
 221
 222static int mfld_init(struct snd_soc_pcm_runtime *runtime)
 223{
 224        struct snd_soc_codec *codec = runtime->codec;
 225        struct snd_soc_dapm_context *dapm = &codec->dapm;
 226        int ret_val;
 227
 228        /* Add jack sense widgets */
 229        snd_soc_dapm_new_controls(dapm, mfld_widgets, ARRAY_SIZE(mfld_widgets));
 230
 231        /* Set up the map */
 232        snd_soc_dapm_add_routes(dapm, mfld_map, ARRAY_SIZE(mfld_map));
 233
 234        /* always connected */
 235        snd_soc_dapm_enable_pin(dapm, "Headphones");
 236        snd_soc_dapm_enable_pin(dapm, "Mic");
 237
 238        ret_val = snd_soc_add_codec_controls(codec, mfld_snd_controls,
 239                                ARRAY_SIZE(mfld_snd_controls));
 240        if (ret_val) {
 241                pr_err("soc_add_controls failed %d", ret_val);
 242                return ret_val;
 243        }
 244        /* default is earpiece pin, userspace sets it explcitly */
 245        snd_soc_dapm_disable_pin(dapm, "Headphones");
 246        /* default is lineout NC, userspace sets it explcitly */
 247        snd_soc_dapm_disable_pin(dapm, "LINEOUTL");
 248        snd_soc_dapm_disable_pin(dapm, "LINEOUTR");
 249        lo_dac = 3;
 250        hs_switch = 0;
 251        /* we dont use linein in this so set to NC */
 252        snd_soc_dapm_disable_pin(dapm, "LINEINL");
 253        snd_soc_dapm_disable_pin(dapm, "LINEINR");
 254
 255        /* Headset and button jack detection */
 256        ret_val = snd_soc_jack_new(codec, "Intel(R) MID Audio Jack",
 257                        SND_JACK_HEADSET | SND_JACK_BTN_0 |
 258                        SND_JACK_BTN_1, &mfld_jack);
 259        if (ret_val) {
 260                pr_err("jack creation failed\n");
 261                return ret_val;
 262        }
 263
 264        ret_val = snd_soc_jack_add_pins(&mfld_jack,
 265                        ARRAY_SIZE(mfld_jack_pins), mfld_jack_pins);
 266        if (ret_val) {
 267                pr_err("adding jack pins failed\n");
 268                return ret_val;
 269        }
 270        ret_val = snd_soc_jack_add_zones(&mfld_jack,
 271                        ARRAY_SIZE(mfld_zones), mfld_zones);
 272        if (ret_val) {
 273                pr_err("adding jack zones failed\n");
 274                return ret_val;
 275        }
 276
 277        /* we want to check if anything is inserted at boot,
 278         * so send a fake event to codec and it will read adc
 279         * to find if anything is there or not */
 280        mfld_jack_check(MFLD_JACK_INSERT);
 281        return ret_val;
 282}
 283
 284static struct snd_soc_dai_link mfld_msic_dailink[] = {
 285        {
 286                .name = "Medfield Headset",
 287                .stream_name = "Headset",
 288                .cpu_dai_name = "Headset-cpu-dai",
 289                .codec_dai_name = "SN95031 Headset",
 290                .codec_name = "sn95031",
 291                .platform_name = "sst-platform",
 292                .init = mfld_init,
 293        },
 294        {
 295                .name = "Medfield Speaker",
 296                .stream_name = "Speaker",
 297                .cpu_dai_name = "Speaker-cpu-dai",
 298                .codec_dai_name = "SN95031 Speaker",
 299                .codec_name = "sn95031",
 300                .platform_name = "sst-platform",
 301                .init = NULL,
 302        },
 303        {
 304                .name = "Medfield Vibra",
 305                .stream_name = "Vibra1",
 306                .cpu_dai_name = "Vibra1-cpu-dai",
 307                .codec_dai_name = "SN95031 Vibra1",
 308                .codec_name = "sn95031",
 309                .platform_name = "sst-platform",
 310                .init = NULL,
 311        },
 312        {
 313                .name = "Medfield Haptics",
 314                .stream_name = "Vibra2",
 315                .cpu_dai_name = "Vibra2-cpu-dai",
 316                .codec_dai_name = "SN95031 Vibra2",
 317                .codec_name = "sn95031",
 318                .platform_name = "sst-platform",
 319                .init = NULL,
 320        },
 321};
 322
 323/* SoC card */
 324static struct snd_soc_card snd_soc_card_mfld = {
 325        .name = "medfield_audio",
 326        .owner = THIS_MODULE,
 327        .dai_link = mfld_msic_dailink,
 328        .num_links = ARRAY_SIZE(mfld_msic_dailink),
 329};
 330
 331static irqreturn_t snd_mfld_jack_intr_handler(int irq, void *dev)
 332{
 333        struct mfld_mc_private *mc_private = (struct mfld_mc_private *) dev;
 334
 335        memcpy_fromio(&mc_private->interrupt_status,
 336                        ((void *)(mc_private->int_base)),
 337                        sizeof(u8));
 338        return IRQ_WAKE_THREAD;
 339}
 340
 341static irqreturn_t snd_mfld_jack_detection(int irq, void *data)
 342{
 343        struct mfld_mc_private *mc_drv_ctx = (struct mfld_mc_private *) data;
 344
 345        if (mfld_jack.codec == NULL)
 346                return IRQ_HANDLED;
 347        mfld_jack_check(mc_drv_ctx->interrupt_status);
 348
 349        return IRQ_HANDLED;
 350}
 351
 352static int __devinit snd_mfld_mc_probe(struct platform_device *pdev)
 353{
 354        int ret_val = 0, irq;
 355        struct mfld_mc_private *mc_drv_ctx;
 356        struct resource *irq_mem;
 357
 358        pr_debug("snd_mfld_mc_probe called\n");
 359
 360        /* retrive the irq number */
 361        irq = platform_get_irq(pdev, 0);
 362
 363        /* audio interrupt base of SRAM location where
 364         * interrupts are stored by System FW */
 365        mc_drv_ctx = kzalloc(sizeof(*mc_drv_ctx), GFP_ATOMIC);
 366        if (!mc_drv_ctx) {
 367                pr_err("allocation failed\n");
 368                return -ENOMEM;
 369        }
 370
 371        irq_mem = platform_get_resource_byname(
 372                                pdev, IORESOURCE_MEM, "IRQ_BASE");
 373        if (!irq_mem) {
 374                pr_err("no mem resource given\n");
 375                ret_val = -ENODEV;
 376                goto unalloc;
 377        }
 378        mc_drv_ctx->int_base = ioremap_nocache(irq_mem->start,
 379                                        resource_size(irq_mem));
 380        if (!mc_drv_ctx->int_base) {
 381                pr_err("Mapping of cache failed\n");
 382                ret_val = -ENOMEM;
 383                goto unalloc;
 384        }
 385        /* register for interrupt */
 386        ret_val = request_threaded_irq(irq, snd_mfld_jack_intr_handler,
 387                        snd_mfld_jack_detection,
 388                        IRQF_SHARED, pdev->dev.driver->name, mc_drv_ctx);
 389        if (ret_val) {
 390                pr_err("cannot register IRQ\n");
 391                goto unalloc;
 392        }
 393        /* register the soc card */
 394        snd_soc_card_mfld.dev = &pdev->dev;
 395        ret_val = snd_soc_register_card(&snd_soc_card_mfld);
 396        if (ret_val) {
 397                pr_debug("snd_soc_register_card failed %d\n", ret_val);
 398                goto freeirq;
 399        }
 400        platform_set_drvdata(pdev, mc_drv_ctx);
 401        pr_debug("successfully exited probe\n");
 402        return ret_val;
 403
 404freeirq:
 405        free_irq(irq, mc_drv_ctx);
 406unalloc:
 407        kfree(mc_drv_ctx);
 408        return ret_val;
 409}
 410
 411static int __devexit snd_mfld_mc_remove(struct platform_device *pdev)
 412{
 413        struct mfld_mc_private *mc_drv_ctx = platform_get_drvdata(pdev);
 414
 415        pr_debug("snd_mfld_mc_remove called\n");
 416        free_irq(platform_get_irq(pdev, 0), mc_drv_ctx);
 417        snd_soc_unregister_card(&snd_soc_card_mfld);
 418        kfree(mc_drv_ctx);
 419        platform_set_drvdata(pdev, NULL);
 420        return 0;
 421}
 422
 423static struct platform_driver snd_mfld_mc_driver = {
 424        .driver = {
 425                .owner = THIS_MODULE,
 426                .name = "msic_audio",
 427        },
 428        .probe = snd_mfld_mc_probe,
 429        .remove = __devexit_p(snd_mfld_mc_remove),
 430};
 431
 432module_platform_driver(snd_mfld_mc_driver);
 433
 434MODULE_DESCRIPTION("ASoC Intel(R) MID Machine driver");
 435MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
 436MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
 437MODULE_LICENSE("GPL v2");
 438MODULE_ALIAS("platform:msic-audio");
 439