~ubuntu-branches/ubuntu/precise/linux-lowlatency/precise

« back to all changes in this revision

Viewing changes to sound/soc/mid-x86/mfld_machine.c

  • Committer: Package Import Robot
  • Author(s): Alessio Igor Bogani
  • Date: 2011-10-26 11:13:05 UTC
  • Revision ID: package-import@ubuntu.com-20111026111305-tz023xykf0i6eosh
Tags: upstream-3.2.0
ImportĀ upstreamĀ versionĀ 3.2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
 
43
enum 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
 
 
54
static unsigned int     hs_switch;
 
55
static unsigned int     lo_dac;
 
56
 
 
57
struct mfld_mc_private {
 
58
        void __iomem *int_base;
 
59
        u8 interrupt_status;
 
60
};
 
61
 
 
62
struct snd_soc_jack mfld_jack;
 
63
 
 
64
/*Headset jack detection DAPM pins */
 
65
static 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 */
 
77
static 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 */
 
83
static const char *headset_switch_text[] = {"Earpiece", "Headset"};
 
84
 
 
85
static const char *lo_text[] = {"Vibra", "Headset", "IHF", "None"};
 
86
 
 
87
static const struct soc_enum headset_enum =
 
88
        SOC_ENUM_SINGLE_EXT(2, headset_switch_text);
 
89
 
 
90
static const struct soc_enum lo_enum =
 
91
        SOC_ENUM_SINGLE_EXT(4, lo_text);
 
92
 
 
93
static 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
 
 
100
static 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
 
 
123
static 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
 
 
140
static 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
 
 
147
static 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
 
 
193
static 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
 
 
200
static const struct snd_soc_dapm_widget mfld_widgets[] = {
 
201
        SND_SOC_DAPM_HP("Headphones", NULL),
 
202
        SND_SOC_DAPM_MIC("Mic", NULL),
 
203
};
 
204
 
 
205
static const struct snd_soc_dapm_route mfld_map[] = {
 
206
        {"Headphones", NULL, "HPOUTR"},
 
207
        {"Headphones", NULL, "HPOUTL"},
 
208
        {"Mic", NULL, "AMIC1"},
 
209
};
 
210
 
 
211
static 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
 
 
222
static 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_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
 
 
284
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 */
 
324
static struct snd_soc_card snd_soc_card_mfld = {
 
325
        .name = "medfield_audio",
 
326
        .dai_link = mfld_msic_dailink,
 
327
        .num_links = ARRAY_SIZE(mfld_msic_dailink),
 
328
};
 
329
 
 
330
static irqreturn_t snd_mfld_jack_intr_handler(int irq, void *dev)
 
331
{
 
332
        struct mfld_mc_private *mc_private = (struct mfld_mc_private *) dev;
 
333
 
 
334
        memcpy_fromio(&mc_private->interrupt_status,
 
335
                        ((void *)(mc_private->int_base)),
 
336
                        sizeof(u8));
 
337
        return IRQ_WAKE_THREAD;
 
338
}
 
339
 
 
340
static irqreturn_t snd_mfld_jack_detection(int irq, void *data)
 
341
{
 
342
        struct mfld_mc_private *mc_drv_ctx = (struct mfld_mc_private *) data;
 
343
 
 
344
        if (mfld_jack.codec == NULL)
 
345
                return IRQ_HANDLED;
 
346
        mfld_jack_check(mc_drv_ctx->interrupt_status);
 
347
 
 
348
        return IRQ_HANDLED;
 
349
}
 
350
 
 
351
static int __devinit snd_mfld_mc_probe(struct platform_device *pdev)
 
352
{
 
353
        int ret_val = 0, irq;
 
354
        struct mfld_mc_private *mc_drv_ctx;
 
355
        struct resource *irq_mem;
 
356
 
 
357
        pr_debug("snd_mfld_mc_probe called\n");
 
358
 
 
359
        /* retrive the irq number */
 
360
        irq = platform_get_irq(pdev, 0);
 
361
 
 
362
        /* audio interrupt base of SRAM location where
 
363
         * interrupts are stored by System FW */
 
364
        mc_drv_ctx = kzalloc(sizeof(*mc_drv_ctx), GFP_ATOMIC);
 
365
        if (!mc_drv_ctx) {
 
366
                pr_err("allocation failed\n");
 
367
                return -ENOMEM;
 
368
        }
 
369
 
 
370
        irq_mem = platform_get_resource_byname(
 
371
                                pdev, IORESOURCE_MEM, "IRQ_BASE");
 
372
        if (!irq_mem) {
 
373
                pr_err("no mem resource given\n");
 
374
                ret_val = -ENODEV;
 
375
                goto unalloc;
 
376
        }
 
377
        mc_drv_ctx->int_base = ioremap_nocache(irq_mem->start,
 
378
                                        resource_size(irq_mem));
 
379
        if (!mc_drv_ctx->int_base) {
 
380
                pr_err("Mapping of cache failed\n");
 
381
                ret_val = -ENOMEM;
 
382
                goto unalloc;
 
383
        }
 
384
        /* register for interrupt */
 
385
        ret_val = request_threaded_irq(irq, snd_mfld_jack_intr_handler,
 
386
                        snd_mfld_jack_detection,
 
387
                        IRQF_SHARED, pdev->dev.driver->name, mc_drv_ctx);
 
388
        if (ret_val) {
 
389
                pr_err("cannot register IRQ\n");
 
390
                goto unalloc;
 
391
        }
 
392
        /* register the soc card */
 
393
        snd_soc_card_mfld.dev = &pdev->dev;
 
394
        ret_val = snd_soc_register_card(&snd_soc_card_mfld);
 
395
        if (ret_val) {
 
396
                pr_debug("snd_soc_register_card failed %d\n", ret_val);
 
397
                goto freeirq;
 
398
        }
 
399
        platform_set_drvdata(pdev, mc_drv_ctx);
 
400
        pr_debug("successfully exited probe\n");
 
401
        return ret_val;
 
402
 
 
403
freeirq:
 
404
        free_irq(irq, mc_drv_ctx);
 
405
unalloc:
 
406
        kfree(mc_drv_ctx);
 
407
        return ret_val;
 
408
}
 
409
 
 
410
static int __devexit snd_mfld_mc_remove(struct platform_device *pdev)
 
411
{
 
412
        struct mfld_mc_private *mc_drv_ctx = platform_get_drvdata(pdev);
 
413
 
 
414
        pr_debug("snd_mfld_mc_remove called\n");
 
415
        free_irq(platform_get_irq(pdev, 0), mc_drv_ctx);
 
416
        snd_soc_unregister_card(&snd_soc_card_mfld);
 
417
        kfree(mc_drv_ctx);
 
418
        platform_set_drvdata(pdev, NULL);
 
419
        return 0;
 
420
}
 
421
 
 
422
static struct platform_driver snd_mfld_mc_driver = {
 
423
        .driver = {
 
424
                .owner = THIS_MODULE,
 
425
                .name = "msic_audio",
 
426
        },
 
427
        .probe = snd_mfld_mc_probe,
 
428
        .remove = __devexit_p(snd_mfld_mc_remove),
 
429
};
 
430
 
 
431
static int __init snd_mfld_driver_init(void)
 
432
{
 
433
        pr_debug("snd_mfld_driver_init called\n");
 
434
        return platform_driver_register(&snd_mfld_mc_driver);
 
435
}
 
436
module_init(snd_mfld_driver_init);
 
437
 
 
438
static void __exit snd_mfld_driver_exit(void)
 
439
{
 
440
        pr_debug("snd_mfld_driver_exit called\n");
 
441
        platform_driver_unregister(&snd_mfld_mc_driver);
 
442
}
 
443
module_exit(snd_mfld_driver_exit);
 
444
 
 
445
MODULE_DESCRIPTION("ASoC Intel(R) MID Machine driver");
 
446
MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
 
447
MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
 
448
MODULE_LICENSE("GPL v2");
 
449
MODULE_ALIAS("platform:msic-audio");