upload tizen1.0 source
[kernel/linux-2.6.36.git] / sound / soc / s3c24xx / slp7_mc1n2.c
1 /*
2  * slp7_mc1n2.c
3  *
4  * Copyright (C) 2011 Samsung Electronics Co.Ltd
5  * Author: Chanwoo Choi <cw00.choi@samsung.com>
6  *
7  *  This program is free software; you can redistribute  it and/or modify it
8  *  under  the terms of  the GNU General  Public License as published by the
9  *  Free Software Foundation;  either version 2 of the  License, or (at your
10  *  option) any later version.
11  *
12  */
13
14 #include <linux/module.h>
15 #include <linux/moduleparam.h>
16 #include <linux/io.h>
17 #include <linux/platform_device.h>
18 #include <linux/jack.h>
19 #include <linux/pm_runtime.h>
20 #include <sound/soc.h>
21 #include <sound/soc-dapm.h>
22 #include <sound/jack.h>
23 #include <sound/mc1n2/mc1n2_priv.h>
24
25 #include <asm/mach-types.h>
26 #include <mach/gpio.h>
27 #include <plat/gpio-cfg.h>
28 #include <plat/adc.h>
29
30 #include "../codecs/mc1n2/mc1n2.h"
31 #include "mc1n2_path.h"
32 #include "s3c-dma.h"
33 #include "s3c64xx-i2s.h"
34 #include "../../../arch/arm/mach-s5pv310/gpio-mobile.h"
35 #include "../../../arch/arm/mach-s5pv310/gpio-slp7.h"
36
37 #define MC1N2_HIFI_DAI  0               /* I2S Interface */
38 #define MC1N2_VOICE_DAI 3               /* PCM interface */
39 #define MC1N2_BT_DAI    5               /* PCM interface */
40 #define MC1N2_DAI_NUM   6
41
42 #define MC1N2_VOICE_STREAM_NAME         "Voice"
43 #define MC1N2_BT_VOICE_STREAM_NAME      "BT Voice"
44
45 /*
46  * XXX Don't include the gpio directly at drivers
47  * Use the mobile gpio function
48  */
49 #define SLP7_MICBIAS_EN         MOBILE_GPIO(MICBIAS_EN)
50 #define SLP7_DET_3_5            MOBILE_GPIO(DET_3_5)
51 #define SLP7_EAR_MICBIAS_EN     MOBILE_GPIO(EAR_MICBIAS_EN)
52 #define SLP7_EAR_SEND_END       MOBILE_GPIO(EAR_SEND_END)
53
54 #define IS_JACK(val, idx)       ( \
55                                 ((val >= jack_tbl[idx].adc_start1) && \
56                                 (val <= (jack_tbl[idx].adc_start1 + \
57                                          jack_tbl[idx].adc_length1))) \
58                                 || ((val >= jack_tbl[idx].adc_start2) && \
59                                 (val <= (jack_tbl[idx].adc_start2 + \
60                                          jack_tbl[idx].adc_length2))) \
61                                 )
62 #define JACK_DETECT(val)        (!val)
63
64 #define JACK_UEVENT_NAME        "earjack"
65 #define JACK_KEY_UEVENT_NAME    "earkey"
66 #define SND_JACK_NONE           0x00
67 #define ADC_LOOP_COUNT          5
68 #define ADC_CHANNEL_EAR_ADC_3_5 3
69
70 extern struct snd_soc_platform s3c_dma_wrapper;
71
72 extern void s5pv310_enable_on_suspend(unsigned int);
73
74 static struct snd_soc_card slp7;
75 static struct platform_device *slp7_snd_device;
76
77 static int slp7_jack_status_check(void);
78
79 static struct s3c_adc_request slp7_jack_adc_request;
80
81 /* 3.5 pie jack */
82 static struct snd_soc_jack slp7_jack;
83
84 /* 3.5 pie jack detection DAPM pins */
85 static struct snd_soc_jack_pin slp7_jack_pins[] = {
86         {
87                 .pin = "Headset Mic",
88                 .mask = SND_JACK_MICROPHONE,
89         }, {
90                 .pin = "Headset Stereophone",
91                 .mask = SND_JACK_HEADPHONE,
92         },
93 };
94
95 /* 3.5 pie jack detection gpios */
96 static struct snd_soc_jack_gpio slp7_jack_gpios[] = {
97         {
98                 /* .gpio filled below */
99                 .name = "DET_3.5",
100                 .report = SND_JACK_HEADSET,
101                 .debounce_time = 50,
102                 .jack_status_check = slp7_jack_status_check,
103         },
104 };
105
106 enum {
107         JACK_NONE,
108         JACK_HEADPHONE,
109         JACK_HEADSET,
110         JACK_EAR_SEND_END,
111         JACK_KEY_NOP1,
112         JACK_KEY_NOP2,
113         JACK_TYPE_NUM
114 };
115
116 const static struct {
117         /* The scope of ADC value according to jack type */
118         int adc_start1;
119         int adc_length1;
120         int adc_start2;
121         int adc_length2;
122
123         int jack_type;
124 } jack_tbl[] = {
125         /* JACK */
126         {-1,    -1,     -1,     -1,     SND_JACK_NONE},
127         {0,     10,     -1,     -1,     SND_JACK_HEADPHONE},
128         {30,    300,    -1,     -1,     SND_JACK_HEADSET},
129
130         /* JACK's key */
131         {-1,    -1,     -1,     -1,     SND_JACK_BTN_0},
132         {-1,    -1,     -1,     -1,     SND_JACK_BTN_1},
133         {-1,    -1,     -1,     -1,     SND_JACK_BTN_2},
134 };
135
136 struct jack_key_data {
137         struct work_struct work;
138         int gpio;
139         int state;
140 } slp7_jack_key;
141
142 /*
143  * Read ADC value 
144  */
145 static int slp7_jack_get_adc(void)
146 {
147         int i, adc = 0;
148
149         /* Read EAR_ADC_3.5 */
150         for (i = 0; i < ADC_LOOP_COUNT; i++)
151                 adc = s3c_adc_get(&slp7_jack_adc_request);
152         return adc;
153 }
154
155 /*
156  * Get the state of jack when irq is happened
157  * - DET_3.5            : Jack is attatched/detached
158  * - EAR_SEND_END       : Key is pressed
159  */
160 static int slp7_jack_status_get(void)
161 {
162         int adc, det, i;
163         int jack_status = SND_JACK_HEADSET;
164
165         /* check jact status */
166         adc = slp7_jack_get_adc();
167         det = JACK_DETECT(gpio_get_value(SLP7_DET_3_5));
168
169         printk(KERN_DEBUG "ADC : %d, DET_3.5 : %d\n", adc, det);
170
171         if (det) {
172                 for (i = 0 ; i < JACK_TYPE_NUM ; i++ ) {
173                         if (IS_JACK(adc, i)) {
174                                 jack_status = jack_tbl[i].jack_type;
175                                 break;
176                         }
177                 }
178         } else
179                 jack_status = jack_tbl[JACK_NONE].jack_type;
180
181         return jack_status;
182 }
183
184 /*
185  * Set the state of jack when irq is happened
186  * - DET_3.5            : Jack is attatched/detached
187  * - EAR_SEND_END       : Key is pressed
188  */
189 static void slp7_jack_status_set(int jack) {
190         int idx;
191
192         if (jack < SND_JACK_BTN_0) {
193                 /* JACK */
194                 switch (jack) {
195                         case SND_JACK_NONE:
196                         printk(KERN_NOTICE "Jack is detached\n");
197                         idx = JACK_NONE;
198                         break;
199                 case SND_JACK_HEADPHONE:
200                         printk(KERN_NOTICE "Jack is Earjack3\n");
201                         idx = JACK_HEADPHONE;
202                         break;
203                 case SND_JACK_HEADSET:
204                         printk(KERN_NOTICE "Jack is Earjack4\n");
205                         idx = JACK_HEADSET;
206                         break;
207                 default:
208                         printk(KERN_NOTICE "Jack is unknown\n");
209                         return;
210                 }
211                 jack_event_handler(JACK_UEVENT_NAME, jack_tbl[idx].jack_type);
212
213         } else {
214                 /* JACK Key */
215                 switch (jack) {
216                 case SND_JACK_BTN_0:
217                         printk(KERN_NOTICE "EAR_SEND_END is pressed\n");
218                         idx = JACK_EAR_SEND_END;
219                         break;
220                 case SND_JACK_BTN_1:
221                 case SND_JACK_BTN_2:
222                 default:
223                         printk(KERN_NOTICE "The key is unknown\n");
224                         return;
225                 }
226                 jack_event_handler(JACK_KEY_UEVENT_NAME, jack_tbl[idx].jack_type);
227         }
228 }
229
230 /*      
231  * Callback function for "DET_3.5"
232  */
233 static int slp7_jack_status_check(void)
234 {
235         int jack_status;
236
237         jack_status = slp7_jack_status_get();
238         slp7_jack_status_set(jack_status);
239         return jack_status;
240 }
241
242 /*
243  * Sysfs to set/get the state of jack on user-space     
244  */
245 static ssize_t slp7_jack_show(struct device *dev,
246                 struct device_attribute *attr, char *buf)
247 {
248         return sprintf(buf, "%d\n", slp7_jack_status_get());
249 }
250
251 static ssize_t slp7_jack_store(struct device *dev,
252                 struct device_attribute *attr, const char *buf, size_t count)
253 {
254         unsigned int value;
255
256         strict_strtoul(buf, 10, (unsigned long *)&value);
257         slp7_jack_status_set(value);
258         snd_soc_jack_report(&slp7_jack, value, slp7_jack_gpios[0].report);
259
260         return count;
261 }
262 static DEVICE_ATTR(jack_status, 0664, slp7_jack_show, slp7_jack_store);
263
264 /*
265  * Callback function of jack key
266  * - EAR_SEND_END       : SND_JACK_BTN_0
267  * - NONE_KEY           : SND_JACK_BTN_1
268  * - NONE_KEY           : SND_JACK_BTN_2
269  */
270 static void slp7_jack_key_work(struct work_struct *work)
271 {
272         return;
273 }
274 static irqreturn_t slp7_jack_key_handler(int irq, void *dev_id)
275 {
276         struct jack_key_data *data = dev_id;
277
278         schedule_work(&data->work);
279
280         return IRQ_HANDLED;
281 }
282
283 /*
284  * Init jack and key
285  */
286 static int slp7_jack_init(void)
287 {
288         int gpio, ret;
289
290         /* - MICBIAS_EN is low */
291         gpio = SLP7_MICBIAS_EN;
292         gpio_request(gpio, "MICBIAS_EN");       /* XMDMADDR_4 */
293         gpio_direction_output(gpio, 0);
294         gpio_export(gpio, 0);
295
296         /* - EAR_MICBIAS_EN is low */
297         gpio = SLP7_EAR_MICBIAS_EN;
298         gpio_request(gpio, "EAR_MICBIAS_EN");   /* XMDMADDR_12 */
299         gpio_direction_output(gpio, 0);
300         gpio_export(gpio, 0);
301
302         /* sysfs for jack */
303         ret = device_create_file(&slp7_snd_device->dev,
304                         &dev_attr_jack_status);
305         if (ret < 0) {
306                 dev_warn(&slp7_snd_device->dev,
307                         "Failed to add jack_status sysfs files\n");
308                 goto sysfs_failed;
309         }
310
311         /* Init jack key */
312         /* - EAR_SEND_END */
313         /* FIXME: When DET_3.5 is high, EAR_SEND_END irq happened.
314            But, the state of DET_3.5 is low with earjack,
315            so EAR_SEND_END irq haven't happened. */
316         INIT_WORK(&slp7_jack_key.work, slp7_jack_key_work);
317
318         slp7_jack_key.gpio = SLP7_EAR_SEND_END;
319         gpio = slp7_jack_key.gpio;
320         gpio_request(gpio, "EAR_SEND_END");
321         gpio_direction_input(gpio);
322
323         ret = request_irq(gpio_to_irq(gpio), slp7_jack_key_handler,
324                         IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
325                         "EAR_SEND_END", &slp7_jack_key);
326         s3c_gpio_setpull(gpio, S3C_GPIO_PULL_NONE);
327         if (ret < 0) {
328                 dev_warn(&slp7_snd_device->dev,
329                         "Failed to request hadndler of EAR_SEND_END\n");
330                 goto irq_failed;
331         }
332
333         /* Set ADC channel for reading value */
334         slp7_jack_adc_request.channel = ADC_CHANNEL_EAR_ADC_3_5;
335
336         return 0;
337
338 irq_failed:
339         gpio_free(slp7_jack_key.gpio);
340
341 sysfs_failed:
342         gpio_free(SLP7_MICBIAS_EN);
343         gpio_free(SLP7_EAR_MICBIAS_EN);
344
345         return ret;
346 }
347
348 static struct mc1n2_setup s5pv310_mc1n2_setup = {
349          {  /* MCDRV_INIT_INFO */
350            MCDRV_CKSEL_CMOS, /* bCkSel */
351            28,                /* bDivR0 20MHz*/ //28 for 24, 19 for 20
352            86,               /* bDivF0 20MHz*/ //86 for 24, 70 for 20
353            28,                /* bDivR1*/
354            86,                /* bDivF1*/
355            0,                /* bRange0*/
356            0,                /* bRange1*/
357            0,                /* bBypass*/
358            MCDRV_DAHIZ_LOW,  /* bDioSdo0Hiz */
359            MCDRV_DAHIZ_LOW,  /* bDioSdo1Hiz */
360            MCDRV_DAHIZ_LOW,  /* bDioSdo2Hiz */
361            MCDRV_DAHIZ_LOW,  /* bDioClk0Hiz */
362            MCDRV_DAHIZ_LOW,  /* bDioClk1Hiz */
363            MCDRV_DAHIZ_LOW,  /* bDioClk2Hiz */
364            MCDRV_PCMHIZ_HIZ, /* bPcmHiz */
365            MCDRV_LINE_STEREO,/* bLineIn1Dif */
366            0,                /* bLineIn2Dif */
367            MCDRV_LINE_STEREO,/* bLineOut1Dif */
368            MCDRV_LINE_STEREO,/* bLineOUt2Dif */
369            MCDRV_SPMN_ON,    /* bSpmn */
370            MCDRV_MIC_DIF,    /* bMic1Sng */
371            MCDRV_MIC_DIF,    /* bMic2Sng */
372            MCDRV_MIC_DIF,    /* bMic3Sng */
373            MCDRV_POWMODE_NORMAL, /* bPowerMode */
374            MCDRV_SPHIZ_PULLDOWN, /* bSpHiz */
375            MCDRV_LDO_ON,     /* bLdo */
376            MCDRV_PAD_GPIO,   /* bPad0Func */
377            MCDRV_PAD_GPIO,   /* bPad1Func */
378            MCDRV_PAD_GPIO,   /* bPad2Func */
379            MCDRV_OUTLEV_4,   /* bAvddLev */
380            0,                /* bVrefLev */
381            MCDRV_DCLGAIN_12, /* bDclGain */
382            MCDRV_DCLLIMIT_0, /* bDclLimit */
383            0,                /* bCpMod */
384            0,                /* bReserved1 */
385            0,                /* bReserved2 */
386            0,                /* bReserved3 */
387            0,                /* bReserved4 */
388            0,                /* bReserved5 */
389            {                 /* sWaitTime */
390              130000,         /* dAdHpf */
391              25000,          /* dMic1Cin */
392              25000,          /* dMic2Cin */
393              25000,          /* dMic3Cin */
394              25000,          /* dLine1Cin */
395              25000,          /* dLine2Cin */
396              5000,           /* dVrefRdy1 */
397              15000,          /* dVrefRdy2 */
398              9000,           /* dHpRdy */
399              13000,          /* dSpRdy */
400              0,              /* dPdm */
401              1000,           /* dAnaRdyInterval */
402              1000,           /* dSvolInterval */
403              1000,           /* dAnaRdyTimeOut */
404              1000            /* dSvolTimeOut */
405            }
406          }, /* MCDRV_INIT_INFO end */
407          {  /* pcm_extend */
408            0, 0, 0
409          }, /* pcm_extend end */
410          {  /* pcm_hiz_redge */
411            MCDRV_PCMHIZTIM_FALLING, MCDRV_PCMHIZTIM_FALLING, MCDRV_PCMHIZTIM_FALLING
412          }, /* pcm_hiz_redge end */
413          {  /* pcm_hperiod */
414            1, 1, 1
415          }, /* pcm_hperiod end */
416          {  /* slot */
417            {{0,1},{0,1}},
418            {{0,1},{0,1}},
419            {{0,1},{0,1}}
420          }  /* slot end */
421 };
422
423 static int slp7_get_enable_on_suspend(struct snd_kcontrol *kcontrol,
424                 struct snd_ctl_elem_value *ucontrol)
425 {
426         struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
427
428         ucontrol->value.integer.value[0] = codec->enable_on_suspend;
429
430         return 0;
431 }
432 static int slp7_set_enable_on_suspend(struct snd_kcontrol *kcontrol,
433                 struct snd_ctl_elem_value *ucontrol)
434 {
435         struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
436
437         if (codec->enable_on_suspend == ucontrol->value.integer.value[0])
438                 return 0;
439
440         codec->enable_on_suspend = ucontrol->value.integer.value[0];
441         s5pv310_enable_on_suspend(ucontrol->value.integer.value[0]);
442
443         return 0;
444 }
445
446 /* Enable sound codec to support setting of sound-path in user-space */
447 static int slp7_get_enable_codec(struct snd_kcontrol *kcontrol,
448                 struct snd_ctl_elem_value *ucontrol)
449 {
450         int suspended;
451
452         suspended = pm_runtime_suspended(mc1n2_dai[0].dev);
453         ucontrol->value.integer.value[0] = !suspended;
454
455         return 0;
456 }
457 static int slp7_set_enable_codec(struct snd_kcontrol *kcontrol,
458                 struct snd_ctl_elem_value *ucontrol)
459 {
460         pm_runtime_get_sync(mc1n2_dai[0].dev);
461
462         return 0;
463 }
464
465 /* Disable sound codec to support setting of sound-path in user-space */
466 static int slp7_get_disable_codec(struct snd_kcontrol *kcontrol,
467                 struct snd_ctl_elem_value *ucontrol)
468 {
469         int suspended;
470
471         suspended = pm_runtime_suspended(mc1n2_dai[0].dev);
472         ucontrol->value.integer.value[0] = !suspended;
473
474         return 0;
475 }
476 static int slp7_set_disable_codec(struct snd_kcontrol *kcontrol,
477                 struct snd_ctl_elem_value *ucontrol)
478 {
479         if (pm_runtime_suspended(mc1n2_dai[0].dev)) {
480                 WARN(true, "%s: The power state of sound is suspended.", __func__);
481                 return -EPERM;
482         }
483
484         pm_runtime_put(mc1n2_dai[0].dev);
485         return 0;
486 }
487
488
489 /* FIXME: Add debug kcontrol to show codec registers */
490 static int set_codec_reg(struct snd_kcontrol *kcontrol,
491                 struct snd_ctl_elem_value *ucontrol)
492 {
493         struct snd_soc_codec *codec = kcontrol->private_data;
494
495         mc1n2_show_reg(codec);
496
497         return 0;
498 }
499
500 static const struct snd_kcontrol_new slp7_controls[] = {
501         /* This kcontrol controls the operation state of codec during sleep.
502          * If this kcontrol is enabled, its function must be correctly
503          * operated on sleep. */
504         SOC_SINGLE_BOOL_EXT("Enable on Suspend", 0,
505                         slp7_get_enable_on_suspend,
506                         slp7_set_enable_on_suspend),
507         /* This kcontrol controls the power of codec. */
508         SOC_SINGLE_BOOL_EXT("Enable Codec", 0,
509                         slp7_get_enable_codec,
510                         slp7_set_enable_codec),
511         SOC_SINGLE_BOOL_EXT("Disable Codec", 0,
512                         slp7_get_disable_codec,
513                         slp7_set_disable_codec),
514         /* FIXME: This kcontrol displays the register dump of codec to user in user-space,
515          * this kcontrol should be removed to kcontrol list. */
516         SOC_SINGLE_BOOL_EXT("codec_reg", 0,
517                         NULL, set_codec_reg),
518 };
519
520 static const struct snd_soc_dapm_widget slp7_dapm_widgets[] = {
521         SND_SOC_DAPM_SPK("Left Speaker", NULL),
522         SND_SOC_DAPM_SPK("Right Speaker", NULL),
523         SND_SOC_DAPM_SPK("Receiver", NULL),
524         SND_SOC_DAPM_HP("Headset Stereophone", NULL),
525         SND_SOC_DAPM_MIC("Headset Mic", NULL),
526         SND_SOC_DAPM_MIC("Main Mic", NULL),
527         SND_SOC_DAPM_MIC("2nd Mic", NULL),
528         SND_SOC_DAPM_LINE("Radio In", NULL),
529 };
530
531 static const struct snd_soc_dapm_route slp7_dapm_routes[] = {
532         {"Left Speaker", NULL, "SPOUTLN"},
533         {"Left Speaker", NULL, "SPOUTLP"},
534         {"Right Speaker", NULL, "SPOUTRN"},
535         {"Right Speaker", NULL, "SPOUTRP"},
536
537         {"Receiver", NULL, "RCOUTN"},
538         {"Receiver", NULL, "RCOUTP"},
539
540         {"Headset Stereophone", NULL, "HPOUTL"},
541         {"Headset Stereophone", NULL, "HPOUTR"},
542
543         {"MIC1", NULL, "Main Mic"},
544         {"MIC2", NULL, "Headset Mic"},
545         {"MIC3", NULL, "2nd Mic"},
546         {"LINEIN", NULL, "Radio In"},
547 };
548
549 static int slp7_mc1n2_init(struct snd_soc_codec *codec)
550 {
551         int ret;
552
553         /* add slp7 specific kcontorls */
554         ret = snd_soc_add_controls(codec, slp7_controls,
555                         ARRAY_SIZE(slp7_controls));
556
557         if (ret < 0)
558                 return ret;
559
560         /* add slp7 specific widgets */
561         snd_soc_dapm_new_controls(codec, slp7_dapm_widgets,
562                         ARRAY_SIZE(slp7_dapm_widgets));
563
564         /* set up slp7 specific audio routes */
565         snd_soc_dapm_add_routes(codec, slp7_dapm_routes,
566                         ARRAY_SIZE(slp7_dapm_routes));
567
568         snd_soc_dapm_sync(codec);
569
570         /* Init jack and key */
571         ret = slp7_jack_init();
572         if (ret)
573                 return ret;
574
575         /* Headset jack detection */
576         ret = snd_soc_jack_new(&slp7, "Headset Jack",
577                         SND_JACK_HEADSET, &slp7_jack);
578         if (ret)
579                 return ret;
580
581         ret = snd_soc_jack_add_pins(&slp7_jack, ARRAY_SIZE(slp7_jack_pins),
582                         slp7_jack_pins);
583         if (ret)
584                 return ret;
585
586         slp7_jack_gpios[0].gpio = SLP7_DET_3_5;
587         ret = snd_soc_jack_add_gpios(&slp7_jack, ARRAY_SIZE(slp7_jack_gpios),
588                         slp7_jack_gpios);
589         if (ret)
590                 return ret;
591
592         /* FIXME: I don't completely remove following code which set default
593          * sound path(ap_to_speaker) to test local rootfs on party server.
594          * If you want to listen booting music, you executes "set_sound_path()"
595          * function. */
596         set_sound_path(codec, CODEC_START, DAPM_ON);
597
598         return 0;
599 }
600
601 int slp7_startup(struct snd_pcm_substream *substream)
602 {
603         pm_runtime_get_sync(mc1n2_dai[0].dev);
604
605         return 0;
606 }
607
608 void slp7_shutdown(struct snd_pcm_substream *substream)
609 {
610         if (pm_runtime_suspended(mc1n2_dai[0].dev)) {
611                 WARN(true, "%s: The power state of sound is suspended.", __func__);
612                 return;
613         }
614         pm_runtime_put(mc1n2_dai[0].dev);
615 }
616
617 static int slp7_hifi_hw_params(struct snd_pcm_substream *substream,
618                 struct snd_pcm_hw_params *params)
619 {
620         struct snd_soc_pcm_runtime *rtd = substream->private_data;
621         struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
622         struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
623         int ret = 0;
624
625         /* set the cpu DAI configuration */
626         ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
627                         SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
628         if (ret < 0)
629                 return ret;
630
631         /* set the cpu system clock
632          * - Use audio-bus clock instead of I2S clock from AudioSS
633          * as the source clock of I2S*/
634         ret = snd_soc_dai_set_sysclk(cpu_dai, S3C64XX_CLKSRC_PCLK, 0,
635                         SND_SOC_CLOCK_IN);
636         if (ret < 0)
637                 return ret;
638
639         /* set codec DAI configuration */
640         ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
641                         SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
642         if (ret < 0)
643                 return ret;
644
645         /* set the codec clock divide */
646         ret = snd_soc_dai_set_clkdiv(codec_dai,
647                         MC1N2_BCLK_MULT, MC1N2_LRCK_X64);
648         if (ret < 0)
649                 return ret;
650
651         return 0;
652 }
653
654 static struct snd_soc_ops slp7_hifi_ops = {
655         .startup        = slp7_startup,
656         .shutdown       = slp7_shutdown,
657         .hw_params      = slp7_hifi_hw_params,
658 };
659
660 static int slp7_voice_hw_params(struct snd_pcm_substream *substream,
661                 struct snd_pcm_hw_params *params)
662 {
663         struct snd_soc_pcm_runtime *rtd = substream->private_data;
664         struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
665         int ret = 0;
666
667         if (params_rate(params) != 8000)
668                 return -EINVAL;
669
670         /* set codec DAI configuration */
671         ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_A |
672                         SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
673         if (ret < 0)
674                 return ret;
675
676         /* set the codec clock divide */
677         ret = snd_soc_dai_set_clkdiv(codec_dai,
678                         MC1N2_BCLK_MULT, MCDRV_BCKFS_32);
679         if (ret < 0)
680                 return ret;
681
682         return 0;
683 }
684
685 static struct snd_soc_ops slp7_voice_ops = {
686         .startup        = slp7_startup,
687         .shutdown       = slp7_shutdown,
688         .hw_params      = slp7_voice_hw_params,
689 };
690
691 static int slp7_bt_voice_hw_params(struct snd_pcm_substream *substream,
692                 struct snd_pcm_hw_params *params)
693 {
694         struct snd_soc_pcm_runtime *rtd = substream->private_data;
695         struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
696         int ret = 0;
697
698         if (params_rate(params) != 8000)
699                 return -EINVAL;
700
701         /* set codec DAI configuration */
702         ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_A |
703                         SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
704         if (ret < 0)
705                 return ret;
706
707         /* set the codec clock divide */
708         ret = snd_soc_dai_set_clkdiv(codec_dai,
709                         MC1N2_BCLK_MULT, MCDRV_BCKFS_32);
710         if (ret < 0)
711                 return ret;
712
713         return 0;
714 }
715
716 static struct snd_soc_ops slp7_bt_voice_ops = {
717         .startup        = slp7_startup,
718         .shutdown       = slp7_shutdown,
719         .hw_params      = slp7_bt_voice_hw_params,
720 };
721
722 static int slp7_hwmixing_hw_params(struct snd_pcm_substream *substream,
723                 struct snd_pcm_hw_params *params)
724 {
725         struct snd_soc_pcm_runtime *rtd = substream->private_data;
726         struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
727         struct snd_soc_dai *cpu_dai = &s3c64xx_i2s_v4_dai;
728         int ret = 0;
729
730         /* set the cpu DAI configuration */
731         ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
732                         SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
733         if (ret < 0)
734                 return ret;
735
736         /* set the cpu system clock */
737         ret = snd_soc_dai_set_sysclk(cpu_dai, S3C64XX_CLKSRC_PCLK, 0,
738                         SND_SOC_CLOCK_OUT);
739         if (ret < 0)
740                 return ret;
741
742         /* set codec DAI configuration */
743         ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
744                         SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
745         if (ret < 0)
746                 return ret;
747
748         /* set the codec clock divide */
749         ret = snd_soc_dai_set_clkdiv(codec_dai,
750                         MC1N2_BCLK_MULT, MC1N2_LRCK_X64);
751         if (ret < 0)
752                 return ret;
753
754         return 0;
755 }
756
757 static struct snd_soc_ops slp7_hwmixing_ops = {
758         .startup        = slp7_startup,
759         .shutdown       = slp7_shutdown,
760         .hw_params      = slp7_hwmixing_hw_params,
761 };
762
763 #define MC1N2_PCM_RATE (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000)
764 #define MC1N2_PCM_FORMATS \
765         (SNDRV_PCM_FMTBIT_S8 | \
766          SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \
767          SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_MU_LAW)
768
769 static struct snd_soc_dai voice_dai = {
770         .name = "MC1N2 Voice",
771         .id = 0,
772         .playback = {
773                 .channels_min = 1,
774                 .channels_max = 2,
775                 .rates = MC1N2_PCM_RATE,
776                 .formats = MC1N2_PCM_FORMATS,},
777         .capture = {
778                 .channels_min = 1,
779                 .channels_max = 2,
780                 .rates = MC1N2_PCM_RATE,
781                 .formats = MC1N2_PCM_FORMATS,},
782 };
783
784 static struct snd_soc_dai_link slp7_dai[] = {
785 {
786         .name = "MC1N2 HiFi",
787         .stream_name = MC1N2_HIFI_STREAM_NAME,
788         .cpu_dai = &s3c64xx_i2s_v4_dai,
789         .codec_dai = &mc1n2_dai[MC1N2_HIFI_DAI],
790         .init = slp7_mc1n2_init,
791         .ops = &slp7_hifi_ops,
792 }, {
793         .name = "MC1N2 Voice",
794         .stream_name = MC1N2_VOICE_STREAM_NAME,
795         .cpu_dai = &voice_dai,
796         .codec_dai = &mc1n2_dai[MC1N2_VOICE_DAI],
797         .ops = &slp7_voice_ops,
798 }, {
799         .name = "MC1N2 HiFi Secondary",
800         .stream_name = MC1N2_HIFI_SEC_STREAM_NAME,
801         .cpu_dai = &s3c64xx_i2s_v4_dai,
802         .codec_dai = &mc1n2_dai[MC1N2_HIFI_DAI],
803         .ops = &slp7_hwmixing_ops,
804 }, {
805         .name = "MC1N2 Bluetooth",
806         .stream_name = MC1N2_BT_VOICE_STREAM_NAME,
807         .cpu_dai = &voice_dai,
808         .codec_dai = &mc1n2_dai[MC1N2_BT_DAI],
809         .ops = &slp7_bt_voice_ops,
810 },
811 };
812
813 static struct snd_soc_card slp7 = {
814         .name = "slp7",
815         .platform = &s3c_dma_wrapper,
816         .dai_link = slp7_dai,
817         .num_links = ARRAY_SIZE(slp7_dai),
818 };
819
820 static struct snd_soc_device slp7_snd_devdata = {
821         .card = &slp7,
822         .codec_dev = &soc_codec_dev_mc1n2,
823         .codec_data = &s5pv310_mc1n2_setup,
824 };
825
826 static int __init slp7_init(void)
827 {
828         int ret;
829
830         if (!machine_is_slp7_c210() && !machine_is_slp10_c210() && !machine_is_nuri())
831                 return -ENODEV;
832
833         /* register voice DAI here */
834         ret = snd_soc_register_dai(&voice_dai);
835         if (ret)
836                 return ret;
837
838         slp7_snd_device = platform_device_alloc("soc-audio", -1);
839         if (!slp7_snd_device)
840                 return -ENOMEM;
841
842         platform_set_drvdata(slp7_snd_device, &slp7_snd_devdata);
843         slp7_snd_devdata.dev = &slp7_snd_device->dev;
844         ret = platform_device_add(slp7_snd_device);
845
846         if (ret) {
847                 platform_device_put(slp7_snd_device);
848         }
849
850         return ret;
851 }
852
853 static void __exit slp7_exit(void)
854 {
855         platform_device_unregister(slp7_snd_device);
856 }
857
858 module_init(slp7_init);
859 module_exit(slp7_exit);
860
861 /* Module information */
862 MODULE_DESCRIPTION("ALSA SoC MC1N2 SLP7(C210)");
863 MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>");
864 MODULE_LICENSE("GPL");