diff options
Diffstat (limited to 'sound')
35 files changed, 3557 insertions, 108 deletions
diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index eabf66af12cd..5d230cee3fa7 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c | |||
@@ -789,13 +789,14 @@ static struct snd_soc_dai_driver atmel_ssc_dai[NUM_SSC_DEVICES] = { | |||
789 | 789 | ||
790 | static __devinit int asoc_ssc_probe(struct platform_device *pdev) | 790 | static __devinit int asoc_ssc_probe(struct platform_device *pdev) |
791 | { | 791 | { |
792 | return snd_soc_register_dais(&pdev->dev, atmel_ssc_dai, | 792 | BUG_ON(pdev->id < 0); |
793 | ARRAY_SIZE(atmel_ssc_dai)); | 793 | BUG_ON(pdev->id >= ARRAY_SIZE(atmel_ssc_dai)); |
794 | return snd_soc_register_dai(&pdev->dev, &atmel_ssc_dai[pdev->id]); | ||
794 | } | 795 | } |
795 | 796 | ||
796 | static int __devexit asoc_ssc_remove(struct platform_device *pdev) | 797 | static int __devexit asoc_ssc_remove(struct platform_device *pdev) |
797 | { | 798 | { |
798 | snd_soc_unregister_dais(&pdev->dev, ARRAY_SIZE(atmel_ssc_dai)); | 799 | snd_soc_unregister_dai(&pdev->dev); |
799 | return 0; | 800 | return 0; |
800 | } | 801 | } |
801 | 802 | ||
@@ -809,6 +810,56 @@ static struct platform_driver asoc_ssc_driver = { | |||
809 | .remove = __devexit_p(asoc_ssc_remove), | 810 | .remove = __devexit_p(asoc_ssc_remove), |
810 | }; | 811 | }; |
811 | 812 | ||
813 | /** | ||
814 | * atmel_ssc_set_audio - Allocate the specified SSC for audio use. | ||
815 | */ | ||
816 | int atmel_ssc_set_audio(int ssc_id) | ||
817 | { | ||
818 | struct ssc_device *ssc; | ||
819 | static struct platform_device *dma_pdev; | ||
820 | struct platform_device *ssc_pdev; | ||
821 | int ret; | ||
822 | |||
823 | if (ssc_id < 0 || ssc_id >= ARRAY_SIZE(atmel_ssc_dai)) | ||
824 | return -EINVAL; | ||
825 | |||
826 | /* Allocate a dummy device for DMA if we don't have one already */ | ||
827 | if (!dma_pdev) { | ||
828 | dma_pdev = platform_device_alloc("atmel-pcm-audio", -1); | ||
829 | if (!dma_pdev) | ||
830 | return -ENOMEM; | ||
831 | |||
832 | ret = platform_device_add(dma_pdev); | ||
833 | if (ret < 0) { | ||
834 | platform_device_put(dma_pdev); | ||
835 | dma_pdev = NULL; | ||
836 | return ret; | ||
837 | } | ||
838 | } | ||
839 | |||
840 | ssc_pdev = platform_device_alloc("atmel-ssc-dai", ssc_id); | ||
841 | if (!ssc_pdev) { | ||
842 | ssc_free(ssc); | ||
843 | return -ENOMEM; | ||
844 | } | ||
845 | |||
846 | /* If we can grab the SSC briefly to parent the DAI device off it */ | ||
847 | ssc = ssc_request(ssc_id); | ||
848 | if (IS_ERR(ssc)) | ||
849 | pr_warn("Unable to parent ASoC SSC DAI on SSC: %ld\n", | ||
850 | PTR_ERR(ssc)); | ||
851 | else | ||
852 | ssc_pdev->dev.parent = &(ssc->pdev->dev); | ||
853 | ssc_free(ssc); | ||
854 | |||
855 | ret = platform_device_add(ssc_pdev); | ||
856 | if (ret < 0) | ||
857 | platform_device_put(ssc_pdev); | ||
858 | |||
859 | return ret; | ||
860 | } | ||
861 | EXPORT_SYMBOL_GPL(atmel_ssc_set_audio); | ||
862 | |||
812 | static int __init snd_atmel_ssc_init(void) | 863 | static int __init snd_atmel_ssc_init(void) |
813 | { | 864 | { |
814 | return platform_driver_register(&asoc_ssc_driver); | 865 | return platform_driver_register(&asoc_ssc_driver); |
diff --git a/sound/soc/atmel/atmel_ssc_dai.h b/sound/soc/atmel/atmel_ssc_dai.h index 392a46953112..5d4f0f9b4d9a 100644 --- a/sound/soc/atmel/atmel_ssc_dai.h +++ b/sound/soc/atmel/atmel_ssc_dai.h | |||
@@ -117,4 +117,6 @@ struct atmel_ssc_info { | |||
117 | struct atmel_ssc_state ssc_state; | 117 | struct atmel_ssc_state ssc_state; |
118 | }; | 118 | }; |
119 | 119 | ||
120 | int atmel_ssc_set_audio(int ssc); | ||
121 | |||
120 | #endif /* _AT91_SSC_DAI_H */ | 122 | #endif /* _AT91_SSC_DAI_H */ |
diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c index 8399ac46cb33..293569dfd0ed 100644 --- a/sound/soc/atmel/sam9g20_wm8731.c +++ b/sound/soc/atmel/sam9g20_wm8731.c | |||
@@ -183,8 +183,8 @@ static struct snd_soc_dai_link at91sam9g20ek_dai = { | |||
183 | .cpu_dai_name = "atmel-ssc-dai.0", | 183 | .cpu_dai_name = "atmel-ssc-dai.0", |
184 | .codec_dai_name = "wm8731-hifi", | 184 | .codec_dai_name = "wm8731-hifi", |
185 | .init = at91sam9g20ek_wm8731_init, | 185 | .init = at91sam9g20ek_wm8731_init, |
186 | .platform_name = "atmel_pcm-audio", | 186 | .platform_name = "atmel-pcm-audio", |
187 | .codec_name = "wm8731-codec.0-001a", | 187 | .codec_name = "wm8731-codec.0-001b", |
188 | .ops = &at91sam9g20ek_ops, | 188 | .ops = &at91sam9g20ek_ops, |
189 | }; | 189 | }; |
190 | 190 | ||
@@ -205,6 +205,12 @@ static int __init at91sam9g20ek_init(void) | |||
205 | if (!(machine_is_at91sam9g20ek() || machine_is_at91sam9g20ek_2mmc())) | 205 | if (!(machine_is_at91sam9g20ek() || machine_is_at91sam9g20ek_2mmc())) |
206 | return -ENODEV; | 206 | return -ENODEV; |
207 | 207 | ||
208 | ret = atmel_ssc_set_audio(0); | ||
209 | if (ret != 0) { | ||
210 | pr_err("Failed to set SSC 0 for audio: %d\n", ret); | ||
211 | return ret; | ||
212 | } | ||
213 | |||
208 | /* | 214 | /* |
209 | * Codec MCLK is supplied by PCK0 - set it up. | 215 | * Codec MCLK is supplied by PCK0 - set it up. |
210 | */ | 216 | */ |
diff --git a/sound/soc/codecs/88pm860x-codec.c b/sound/soc/codecs/88pm860x-codec.c new file mode 100644 index 000000000000..01d19e9f53f9 --- /dev/null +++ b/sound/soc/codecs/88pm860x-codec.c | |||
@@ -0,0 +1,1486 @@ | |||
1 | /* | ||
2 | * 88pm860x-codec.c -- 88PM860x ALSA SoC Audio Driver | ||
3 | * | ||
4 | * Copyright 2010 Marvell International Ltd. | ||
5 | * Author: Haojian Zhuang <haojian.zhuang@marvell.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/i2c.h> | ||
15 | #include <linux/platform_device.h> | ||
16 | #include <linux/mfd/88pm860x.h> | ||
17 | #include <linux/slab.h> | ||
18 | #include <sound/core.h> | ||
19 | #include <sound/pcm.h> | ||
20 | #include <sound/pcm_params.h> | ||
21 | #include <sound/soc.h> | ||
22 | #include <sound/soc-dapm.h> | ||
23 | #include <sound/tlv.h> | ||
24 | #include <sound/initval.h> | ||
25 | #include <sound/jack.h> | ||
26 | |||
27 | #include "88pm860x-codec.h" | ||
28 | |||
29 | #define MAX_NAME_LEN 20 | ||
30 | #define REG_CACHE_SIZE 0x40 | ||
31 | #define REG_CACHE_BASE 0xb0 | ||
32 | |||
33 | /* Status Register 1 (0x01) */ | ||
34 | #define REG_STATUS_1 0x01 | ||
35 | #define MIC_STATUS (1 << 7) | ||
36 | #define HOOK_STATUS (1 << 6) | ||
37 | #define HEADSET_STATUS (1 << 5) | ||
38 | |||
39 | /* Mic Detection Register (0x37) */ | ||
40 | #define REG_MIC_DET 0x37 | ||
41 | #define CONTINUOUS_POLLING (3 << 1) | ||
42 | #define EN_MIC_DET (1 << 0) | ||
43 | #define MICDET_MASK 0x07 | ||
44 | |||
45 | /* Headset Detection Register (0x38) */ | ||
46 | #define REG_HS_DET 0x38 | ||
47 | #define EN_HS_DET (1 << 0) | ||
48 | |||
49 | /* Misc2 Register (0x42) */ | ||
50 | #define REG_MISC2 0x42 | ||
51 | #define AUDIO_PLL (1 << 5) | ||
52 | #define AUDIO_SECTION_RESET (1 << 4) | ||
53 | #define AUDIO_SECTION_ON (1 << 3) | ||
54 | |||
55 | /* PCM Interface Register 2 (0xb1) */ | ||
56 | #define PCM_INF2_BCLK (1 << 6) /* Bit clock polarity */ | ||
57 | #define PCM_INF2_FS (1 << 5) /* Frame Sync polarity */ | ||
58 | #define PCM_INF2_MASTER (1 << 4) /* Master / Slave */ | ||
59 | #define PCM_INF2_18WL (1 << 3) /* 18 / 16 bits */ | ||
60 | #define PCM_GENERAL_I2S 0 | ||
61 | #define PCM_EXACT_I2S 1 | ||
62 | #define PCM_LEFT_I2S 2 | ||
63 | #define PCM_RIGHT_I2S 3 | ||
64 | #define PCM_SHORT_FS 4 | ||
65 | #define PCM_LONG_FS 5 | ||
66 | #define PCM_MODE_MASK 7 | ||
67 | |||
68 | /* I2S Interface Register 4 (0xbe) */ | ||
69 | #define I2S_EQU_BYP (1 << 6) | ||
70 | |||
71 | /* DAC Offset Register (0xcb) */ | ||
72 | #define DAC_MUTE (1 << 7) | ||
73 | #define MUTE_LEFT (1 << 6) | ||
74 | #define MUTE_RIGHT (1 << 2) | ||
75 | |||
76 | /* ADC Analog Register 1 (0xd0) */ | ||
77 | #define REG_ADC_ANA_1 0xd0 | ||
78 | #define MIC1BIAS_MASK 0x60 | ||
79 | |||
80 | /* Earpiece/Speaker Control Register 2 (0xda) */ | ||
81 | #define REG_EAR2 0xda | ||
82 | #define RSYNC_CHANGE (1 << 2) | ||
83 | |||
84 | /* Audio Supplies Register 2 (0xdc) */ | ||
85 | #define REG_SUPPLIES2 0xdc | ||
86 | #define LDO15_READY (1 << 4) | ||
87 | #define LDO15_EN (1 << 3) | ||
88 | #define CPUMP_READY (1 << 2) | ||
89 | #define CPUMP_EN (1 << 1) | ||
90 | #define AUDIO_EN (1 << 0) | ||
91 | #define SUPPLY_MASK (LDO15_EN | CPUMP_EN | AUDIO_EN) | ||
92 | |||
93 | /* Audio Enable Register 1 (0xdd) */ | ||
94 | #define ADC_MOD_RIGHT (1 << 1) | ||
95 | #define ADC_MOD_LEFT (1 << 0) | ||
96 | |||
97 | /* Audio Enable Register 2 (0xde) */ | ||
98 | #define ADC_LEFT (1 << 5) | ||
99 | #define ADC_RIGHT (1 << 4) | ||
100 | |||
101 | /* DAC Enable Register 2 (0xe1) */ | ||
102 | #define DAC_LEFT (1 << 5) | ||
103 | #define DAC_RIGHT (1 << 4) | ||
104 | #define MODULATOR (1 << 3) | ||
105 | |||
106 | /* Shorts Register (0xeb) */ | ||
107 | #define REG_SHORTS 0xeb | ||
108 | #define CLR_SHORT_LO2 (1 << 7) | ||
109 | #define SHORT_LO2 (1 << 6) | ||
110 | #define CLR_SHORT_LO1 (1 << 5) | ||
111 | #define SHORT_LO1 (1 << 4) | ||
112 | #define CLR_SHORT_HS2 (1 << 3) | ||
113 | #define SHORT_HS2 (1 << 2) | ||
114 | #define CLR_SHORT_HS1 (1 << 1) | ||
115 | #define SHORT_HS1 (1 << 0) | ||
116 | |||
117 | /* | ||
118 | * This widget should be just after DAC & PGA in DAPM power-on sequence and | ||
119 | * before DAC & PGA in DAPM power-off sequence. | ||
120 | */ | ||
121 | #define PM860X_DAPM_OUTPUT(wname, wevent) \ | ||
122 | { .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, \ | ||
123 | .shift = 0, .invert = 0, .kcontrols = NULL, \ | ||
124 | .num_kcontrols = 0, .event = wevent, \ | ||
125 | .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD, } | ||
126 | |||
127 | struct pm860x_det { | ||
128 | struct snd_soc_jack *hp_jack; | ||
129 | struct snd_soc_jack *mic_jack; | ||
130 | int hp_det; | ||
131 | int mic_det; | ||
132 | int hook_det; | ||
133 | int hs_shrt; | ||
134 | int lo_shrt; | ||
135 | }; | ||
136 | |||
137 | struct pm860x_priv { | ||
138 | unsigned int sysclk; | ||
139 | unsigned int pcmclk; | ||
140 | unsigned int dir; | ||
141 | unsigned int filter; | ||
142 | struct snd_soc_codec *codec; | ||
143 | struct i2c_client *i2c; | ||
144 | struct pm860x_chip *chip; | ||
145 | struct pm860x_det det; | ||
146 | |||
147 | int irq[4]; | ||
148 | unsigned char name[4][MAX_NAME_LEN]; | ||
149 | unsigned char reg_cache[REG_CACHE_SIZE]; | ||
150 | }; | ||
151 | |||
152 | /* -9450dB to 0dB in 150dB steps ( mute instead of -9450dB) */ | ||
153 | static const DECLARE_TLV_DB_SCALE(dpga_tlv, -9450, 150, 1); | ||
154 | |||
155 | /* -9dB to 0db in 3dB steps */ | ||
156 | static const DECLARE_TLV_DB_SCALE(adc_tlv, -900, 300, 0); | ||
157 | |||
158 | /* {-23, -17, -13.5, -11, -9, -6, -3, 0}dB */ | ||
159 | static const unsigned int mic_tlv[] = { | ||
160 | TLV_DB_RANGE_HEAD(5), | ||
161 | 0, 0, TLV_DB_SCALE_ITEM(-2300, 0, 0), | ||
162 | 1, 1, TLV_DB_SCALE_ITEM(-1700, 0, 0), | ||
163 | 2, 2, TLV_DB_SCALE_ITEM(-1350, 0, 0), | ||
164 | 3, 3, TLV_DB_SCALE_ITEM(-1100, 0, 0), | ||
165 | 4, 7, TLV_DB_SCALE_ITEM(-900, 300, 0), | ||
166 | }; | ||
167 | |||
168 | /* {0, 0, 0, -6, 0, 6, 12, 18}dB */ | ||
169 | static const unsigned int aux_tlv[] = { | ||
170 | TLV_DB_RANGE_HEAD(2), | ||
171 | 0, 2, TLV_DB_SCALE_ITEM(0, 0, 0), | ||
172 | 3, 7, TLV_DB_SCALE_ITEM(-600, 600, 0), | ||
173 | }; | ||
174 | |||
175 | /* {-16, -13, -10, -7, -5.2, -3,3, -2.2, 0}dB, mute instead of -16dB */ | ||
176 | static const unsigned int out_tlv[] = { | ||
177 | TLV_DB_RANGE_HEAD(4), | ||
178 | 0, 3, TLV_DB_SCALE_ITEM(-1600, 300, 1), | ||
179 | 4, 4, TLV_DB_SCALE_ITEM(-520, 0, 0), | ||
180 | 5, 5, TLV_DB_SCALE_ITEM(-330, 0, 0), | ||
181 | 6, 7, TLV_DB_SCALE_ITEM(-220, 220, 0), | ||
182 | }; | ||
183 | |||
184 | static const unsigned int st_tlv[] = { | ||
185 | TLV_DB_RANGE_HEAD(8), | ||
186 | 0, 1, TLV_DB_SCALE_ITEM(-12041, 602, 0), | ||
187 | 2, 3, TLV_DB_SCALE_ITEM(-11087, 250, 0), | ||
188 | 4, 5, TLV_DB_SCALE_ITEM(-10643, 158, 0), | ||
189 | 6, 7, TLV_DB_SCALE_ITEM(-10351, 116, 0), | ||
190 | 8, 9, TLV_DB_SCALE_ITEM(-10133, 92, 0), | ||
191 | 10, 13, TLV_DB_SCALE_ITEM(-9958, 70, 0), | ||
192 | 14, 17, TLV_DB_SCALE_ITEM(-9689, 53, 0), | ||
193 | 18, 271, TLV_DB_SCALE_ITEM(-9484, 37, 0), | ||
194 | }; | ||
195 | |||
196 | /* Sidetone Gain = M * 2^(-5-N) */ | ||
197 | struct st_gain { | ||
198 | unsigned int db; | ||
199 | unsigned int m; | ||
200 | unsigned int n; | ||
201 | }; | ||
202 | |||
203 | static struct st_gain st_table[] = { | ||
204 | {-12041, 1, 15}, {-11439, 1, 14}, {-11087, 3, 15}, {-10837, 1, 13}, | ||
205 | {-10643, 5, 15}, {-10485, 3, 14}, {-10351, 7, 15}, {-10235, 1, 12}, | ||
206 | {-10133, 9, 15}, {-10041, 5, 14}, { -9958, 11, 15}, { -9883, 3, 13}, | ||
207 | { -9813, 13, 15}, { -9749, 7, 14}, { -9689, 15, 15}, { -9633, 1, 11}, | ||
208 | { -9580, 17, 15}, { -9531, 9, 14}, { -9484, 19, 15}, { -9439, 5, 13}, | ||
209 | { -9397, 21, 15}, { -9356, 11, 14}, { -9318, 23, 15}, { -9281, 3, 12}, | ||
210 | { -9245, 25, 15}, { -9211, 13, 14}, { -9178, 27, 15}, { -9147, 7, 13}, | ||
211 | { -9116, 29, 15}, { -9087, 15, 14}, { -9058, 31, 15}, { -9031, 1, 10}, | ||
212 | { -8978, 17, 14}, { -8929, 9, 13}, { -8882, 19, 14}, { -8837, 5, 12}, | ||
213 | { -8795, 21, 14}, { -8754, 11, 13}, { -8716, 23, 14}, { -8679, 3, 11}, | ||
214 | { -8643, 25, 14}, { -8609, 13, 13}, { -8576, 27, 14}, { -8545, 7, 12}, | ||
215 | { -8514, 29, 14}, { -8485, 15, 13}, { -8456, 31, 14}, { -8429, 1, 9}, | ||
216 | { -8376, 17, 13}, { -8327, 9, 12}, { -8280, 19, 13}, { -8235, 5, 11}, | ||
217 | { -8193, 21, 13}, { -8152, 11, 12}, { -8114, 23, 13}, { -8077, 3, 10}, | ||
218 | { -8041, 25, 13}, { -8007, 13, 12}, { -7974, 27, 13}, { -7943, 7, 11}, | ||
219 | { -7912, 29, 13}, { -7883, 15, 12}, { -7854, 31, 13}, { -7827, 1, 8}, | ||
220 | { -7774, 17, 12}, { -7724, 9, 11}, { -7678, 19, 12}, { -7633, 5, 10}, | ||
221 | { -7591, 21, 12}, { -7550, 11, 11}, { -7512, 23, 12}, { -7475, 3, 9}, | ||
222 | { -7439, 25, 12}, { -7405, 13, 11}, { -7372, 27, 12}, { -7341, 7, 10}, | ||
223 | { -7310, 29, 12}, { -7281, 15, 11}, { -7252, 31, 12}, { -7225, 1, 7}, | ||
224 | { -7172, 17, 11}, { -7122, 9, 10}, { -7075, 19, 11}, { -7031, 5, 9}, | ||
225 | { -6989, 21, 11}, { -6948, 11, 10}, { -6910, 23, 11}, { -6873, 3, 8}, | ||
226 | { -6837, 25, 11}, { -6803, 13, 10}, { -6770, 27, 11}, { -6739, 7, 9}, | ||
227 | { -6708, 29, 11}, { -6679, 15, 10}, { -6650, 31, 11}, { -6623, 1, 6}, | ||
228 | { -6570, 17, 10}, { -6520, 9, 9}, { -6473, 19, 10}, { -6429, 5, 8}, | ||
229 | { -6386, 21, 10}, { -6346, 11, 9}, { -6307, 23, 10}, { -6270, 3, 7}, | ||
230 | { -6235, 25, 10}, { -6201, 13, 9}, { -6168, 27, 10}, { -6137, 7, 8}, | ||
231 | { -6106, 29, 10}, { -6077, 15, 9}, { -6048, 31, 10}, { -6021, 1, 5}, | ||
232 | { -5968, 17, 9}, { -5918, 9, 8}, { -5871, 19, 9}, { -5827, 5, 7}, | ||
233 | { -5784, 21, 9}, { -5744, 11, 8}, { -5705, 23, 9}, { -5668, 3, 6}, | ||
234 | { -5633, 25, 9}, { -5599, 13, 8}, { -5566, 27, 9}, { -5535, 7, 7}, | ||
235 | { -5504, 29, 9}, { -5475, 15, 8}, { -5446, 31, 9}, { -5419, 1, 4}, | ||
236 | { -5366, 17, 8}, { -5316, 9, 7}, { -5269, 19, 8}, { -5225, 5, 6}, | ||
237 | { -5182, 21, 8}, { -5142, 11, 7}, { -5103, 23, 8}, { -5066, 3, 5}, | ||
238 | { -5031, 25, 8}, { -4997, 13, 7}, { -4964, 27, 8}, { -4932, 7, 6}, | ||
239 | { -4902, 29, 8}, { -4873, 15, 7}, { -4844, 31, 8}, { -4816, 1, 3}, | ||
240 | { -4764, 17, 7}, { -4714, 9, 6}, { -4667, 19, 7}, { -4623, 5, 5}, | ||
241 | { -4580, 21, 7}, { -4540, 11, 6}, { -4501, 23, 7}, { -4464, 3, 4}, | ||
242 | { -4429, 25, 7}, { -4395, 13, 6}, { -4362, 27, 7}, { -4330, 7, 5}, | ||
243 | { -4300, 29, 7}, { -4270, 15, 6}, { -4242, 31, 7}, { -4214, 1, 2}, | ||
244 | { -4162, 17, 6}, { -4112, 9, 5}, { -4065, 19, 6}, { -4021, 5, 4}, | ||
245 | { -3978, 21, 6}, { -3938, 11, 5}, { -3899, 23, 6}, { -3862, 3, 3}, | ||
246 | { -3827, 25, 6}, { -3793, 13, 5}, { -3760, 27, 6}, { -3728, 7, 4}, | ||
247 | { -3698, 29, 6}, { -3668, 15, 5}, { -3640, 31, 6}, { -3612, 1, 1}, | ||
248 | { -3560, 17, 5}, { -3510, 9, 4}, { -3463, 19, 5}, { -3419, 5, 3}, | ||
249 | { -3376, 21, 5}, { -3336, 11, 4}, { -3297, 23, 5}, { -3260, 3, 2}, | ||
250 | { -3225, 25, 5}, { -3191, 13, 4}, { -3158, 27, 5}, { -3126, 7, 3}, | ||
251 | { -3096, 29, 5}, { -3066, 15, 4}, { -3038, 31, 5}, { -3010, 1, 0}, | ||
252 | { -2958, 17, 4}, { -2908, 9, 3}, { -2861, 19, 4}, { -2816, 5, 2}, | ||
253 | { -2774, 21, 4}, { -2734, 11, 3}, { -2695, 23, 4}, { -2658, 3, 1}, | ||
254 | { -2623, 25, 4}, { -2589, 13, 3}, { -2556, 27, 4}, { -2524, 7, 2}, | ||
255 | { -2494, 29, 4}, { -2464, 15, 3}, { -2436, 31, 4}, { -2408, 2, 0}, | ||
256 | { -2356, 17, 3}, { -2306, 9, 2}, { -2259, 19, 3}, { -2214, 5, 1}, | ||
257 | { -2172, 21, 3}, { -2132, 11, 2}, { -2093, 23, 3}, { -2056, 3, 0}, | ||
258 | { -2021, 25, 3}, { -1987, 13, 2}, { -1954, 27, 3}, { -1922, 7, 1}, | ||
259 | { -1892, 29, 3}, { -1862, 15, 2}, { -1834, 31, 3}, { -1806, 4, 0}, | ||
260 | { -1754, 17, 2}, { -1704, 9, 1}, { -1657, 19, 2}, { -1612, 5, 0}, | ||
261 | { -1570, 21, 2}, { -1530, 11, 1}, { -1491, 23, 2}, { -1454, 6, 0}, | ||
262 | { -1419, 25, 2}, { -1384, 13, 1}, { -1352, 27, 2}, { -1320, 7, 0}, | ||
263 | { -1290, 29, 2}, { -1260, 15, 1}, { -1232, 31, 2}, { -1204, 8, 0}, | ||
264 | { -1151, 17, 1}, { -1102, 9, 0}, { -1055, 19, 1}, { -1010, 10, 0}, | ||
265 | { -968, 21, 1}, { -928, 11, 0}, { -889, 23, 1}, { -852, 12, 0}, | ||
266 | { -816, 25, 1}, { -782, 13, 0}, { -750, 27, 1}, { -718, 14, 0}, | ||
267 | { -688, 29, 1}, { -658, 15, 0}, { -630, 31, 1}, { -602, 16, 0}, | ||
268 | { -549, 17, 0}, { -500, 18, 0}, { -453, 19, 0}, { -408, 20, 0}, | ||
269 | { -366, 21, 0}, { -325, 22, 0}, { -287, 23, 0}, { -250, 24, 0}, | ||
270 | { -214, 25, 0}, { -180, 26, 0}, { -148, 27, 0}, { -116, 28, 0}, | ||
271 | { -86, 29, 0}, { -56, 30, 0}, { -28, 31, 0}, { 0, 0, 0}, | ||
272 | }; | ||
273 | |||
274 | static int pm860x_volatile(unsigned int reg) | ||
275 | { | ||
276 | BUG_ON(reg >= REG_CACHE_SIZE); | ||
277 | |||
278 | switch (reg) { | ||
279 | case PM860X_AUDIO_SUPPLIES_2: | ||
280 | return 1; | ||
281 | } | ||
282 | |||
283 | return 0; | ||
284 | } | ||
285 | |||
286 | static unsigned int pm860x_read_reg_cache(struct snd_soc_codec *codec, | ||
287 | unsigned int reg) | ||
288 | { | ||
289 | unsigned char *cache = codec->reg_cache; | ||
290 | |||
291 | BUG_ON(reg >= REG_CACHE_SIZE); | ||
292 | |||
293 | if (pm860x_volatile(reg)) | ||
294 | return cache[reg]; | ||
295 | |||
296 | reg += REG_CACHE_BASE; | ||
297 | |||
298 | return pm860x_reg_read(codec->control_data, reg); | ||
299 | } | ||
300 | |||
301 | static int pm860x_write_reg_cache(struct snd_soc_codec *codec, | ||
302 | unsigned int reg, unsigned int value) | ||
303 | { | ||
304 | unsigned char *cache = codec->reg_cache; | ||
305 | |||
306 | BUG_ON(reg >= REG_CACHE_SIZE); | ||
307 | |||
308 | if (!pm860x_volatile(reg)) | ||
309 | cache[reg] = (unsigned char)value; | ||
310 | |||
311 | reg += REG_CACHE_BASE; | ||
312 | |||
313 | return pm860x_reg_write(codec->control_data, reg, value); | ||
314 | } | ||
315 | |||
316 | static int snd_soc_get_volsw_2r_st(struct snd_kcontrol *kcontrol, | ||
317 | struct snd_ctl_elem_value *ucontrol) | ||
318 | { | ||
319 | struct soc_mixer_control *mc = | ||
320 | (struct soc_mixer_control *)kcontrol->private_value; | ||
321 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
322 | unsigned int reg = mc->reg; | ||
323 | unsigned int reg2 = mc->rreg; | ||
324 | int val[2], val2[2], i; | ||
325 | |||
326 | val[0] = snd_soc_read(codec, reg) & 0x3f; | ||
327 | val[1] = (snd_soc_read(codec, PM860X_SIDETONE_SHIFT) >> 4) & 0xf; | ||
328 | val2[0] = snd_soc_read(codec, reg2) & 0x3f; | ||
329 | val2[1] = (snd_soc_read(codec, PM860X_SIDETONE_SHIFT)) & 0xf; | ||
330 | |||
331 | for (i = 0; i < ARRAY_SIZE(st_table); i++) { | ||
332 | if ((st_table[i].m == val[0]) && (st_table[i].n == val[1])) | ||
333 | ucontrol->value.integer.value[0] = i; | ||
334 | if ((st_table[i].m == val2[0]) && (st_table[i].n == val2[1])) | ||
335 | ucontrol->value.integer.value[1] = i; | ||
336 | } | ||
337 | return 0; | ||
338 | } | ||
339 | |||
340 | static int snd_soc_put_volsw_2r_st(struct snd_kcontrol *kcontrol, | ||
341 | struct snd_ctl_elem_value *ucontrol) | ||
342 | { | ||
343 | struct soc_mixer_control *mc = | ||
344 | (struct soc_mixer_control *)kcontrol->private_value; | ||
345 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
346 | unsigned int reg = mc->reg; | ||
347 | unsigned int reg2 = mc->rreg; | ||
348 | int err; | ||
349 | unsigned int val, val2; | ||
350 | |||
351 | val = ucontrol->value.integer.value[0]; | ||
352 | val2 = ucontrol->value.integer.value[1]; | ||
353 | |||
354 | err = snd_soc_update_bits(codec, reg, 0x3f, st_table[val].m); | ||
355 | if (err < 0) | ||
356 | return err; | ||
357 | err = snd_soc_update_bits(codec, PM860X_SIDETONE_SHIFT, 0xf0, | ||
358 | st_table[val].n << 4); | ||
359 | if (err < 0) | ||
360 | return err; | ||
361 | |||
362 | err = snd_soc_update_bits(codec, reg2, 0x3f, st_table[val2].m); | ||
363 | if (err < 0) | ||
364 | return err; | ||
365 | err = snd_soc_update_bits(codec, PM860X_SIDETONE_SHIFT, 0x0f, | ||
366 | st_table[val2].n); | ||
367 | return err; | ||
368 | } | ||
369 | |||
370 | static int snd_soc_get_volsw_2r_out(struct snd_kcontrol *kcontrol, | ||
371 | struct snd_ctl_elem_value *ucontrol) | ||
372 | { | ||
373 | struct soc_mixer_control *mc = | ||
374 | (struct soc_mixer_control *)kcontrol->private_value; | ||
375 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
376 | unsigned int reg = mc->reg; | ||
377 | unsigned int reg2 = mc->rreg; | ||
378 | unsigned int shift = mc->shift; | ||
379 | int max = mc->max, val, val2; | ||
380 | unsigned int mask = (1 << fls(max)) - 1; | ||
381 | |||
382 | val = snd_soc_read(codec, reg) >> shift; | ||
383 | val2 = snd_soc_read(codec, reg2) >> shift; | ||
384 | ucontrol->value.integer.value[0] = (max - val) & mask; | ||
385 | ucontrol->value.integer.value[1] = (max - val2) & mask; | ||
386 | |||
387 | return 0; | ||
388 | } | ||
389 | |||
390 | static int snd_soc_put_volsw_2r_out(struct snd_kcontrol *kcontrol, | ||
391 | struct snd_ctl_elem_value *ucontrol) | ||
392 | { | ||
393 | struct soc_mixer_control *mc = | ||
394 | (struct soc_mixer_control *)kcontrol->private_value; | ||
395 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
396 | unsigned int reg = mc->reg; | ||
397 | unsigned int reg2 = mc->rreg; | ||
398 | unsigned int shift = mc->shift; | ||
399 | int max = mc->max; | ||
400 | unsigned int mask = (1 << fls(max)) - 1; | ||
401 | int err; | ||
402 | unsigned int val, val2, val_mask; | ||
403 | |||
404 | val_mask = mask << shift; | ||
405 | val = ((max - ucontrol->value.integer.value[0]) & mask); | ||
406 | val2 = ((max - ucontrol->value.integer.value[1]) & mask); | ||
407 | |||
408 | val = val << shift; | ||
409 | val2 = val2 << shift; | ||
410 | |||
411 | err = snd_soc_update_bits(codec, reg, val_mask, val); | ||
412 | if (err < 0) | ||
413 | return err; | ||
414 | |||
415 | err = snd_soc_update_bits(codec, reg2, val_mask, val2); | ||
416 | return err; | ||
417 | } | ||
418 | |||
419 | /* DAPM Widget Events */ | ||
420 | /* | ||
421 | * A lot registers are belong to RSYNC domain. It requires enabling RSYNC bit | ||
422 | * after updating these registers. Otherwise, these updated registers won't | ||
423 | * be effective. | ||
424 | */ | ||
425 | static int pm860x_rsync_event(struct snd_soc_dapm_widget *w, | ||
426 | struct snd_kcontrol *kcontrol, int event) | ||
427 | { | ||
428 | struct snd_soc_codec *codec = w->codec; | ||
429 | |||
430 | /* | ||
431 | * In order to avoid current on the load, mute power-on and power-off | ||
432 | * should be transients. | ||
433 | * Unmute by DAC_MUTE. It should be unmuted when DAPM sequence is | ||
434 | * finished. | ||
435 | */ | ||
436 | snd_soc_update_bits(codec, PM860X_DAC_OFFSET, DAC_MUTE, 0); | ||
437 | snd_soc_update_bits(codec, PM860X_EAR_CTRL_2, | ||
438 | RSYNC_CHANGE, RSYNC_CHANGE); | ||
439 | return 0; | ||
440 | } | ||
441 | |||
442 | static int pm860x_dac_event(struct snd_soc_dapm_widget *w, | ||
443 | struct snd_kcontrol *kcontrol, int event) | ||
444 | { | ||
445 | struct snd_soc_codec *codec = w->codec; | ||
446 | unsigned int dac = 0; | ||
447 | int data; | ||
448 | |||
449 | if (!strcmp(w->name, "Left DAC")) | ||
450 | dac = DAC_LEFT; | ||
451 | if (!strcmp(w->name, "Right DAC")) | ||
452 | dac = DAC_RIGHT; | ||
453 | switch (event) { | ||
454 | case SND_SOC_DAPM_PRE_PMU: | ||
455 | if (dac) { | ||
456 | /* Auto mute in power-on sequence. */ | ||
457 | dac |= MODULATOR; | ||
458 | snd_soc_update_bits(codec, PM860X_DAC_OFFSET, | ||
459 | DAC_MUTE, DAC_MUTE); | ||
460 | snd_soc_update_bits(codec, PM860X_EAR_CTRL_2, | ||
461 | RSYNC_CHANGE, RSYNC_CHANGE); | ||
462 | /* update dac */ | ||
463 | snd_soc_update_bits(codec, PM860X_DAC_EN_2, | ||
464 | dac, dac); | ||
465 | } | ||
466 | break; | ||
467 | case SND_SOC_DAPM_PRE_PMD: | ||
468 | if (dac) { | ||
469 | /* Auto mute in power-off sequence. */ | ||
470 | snd_soc_update_bits(codec, PM860X_DAC_OFFSET, | ||
471 | DAC_MUTE, DAC_MUTE); | ||
472 | snd_soc_update_bits(codec, PM860X_EAR_CTRL_2, | ||
473 | RSYNC_CHANGE, RSYNC_CHANGE); | ||
474 | /* update dac */ | ||
475 | data = snd_soc_read(codec, PM860X_DAC_EN_2); | ||
476 | data &= ~dac; | ||
477 | if (!(data & (DAC_LEFT | DAC_RIGHT))) | ||
478 | data &= ~MODULATOR; | ||
479 | snd_soc_write(codec, PM860X_DAC_EN_2, data); | ||
480 | } | ||
481 | break; | ||
482 | } | ||
483 | return 0; | ||
484 | } | ||
485 | |||
486 | static const char *pm860x_opamp_texts[] = {"-50%", "-25%", "0%", "75%"}; | ||
487 | |||
488 | static const char *pm860x_pa_texts[] = {"-33%", "0%", "33%", "66%"}; | ||
489 | |||
490 | static const struct soc_enum pm860x_hs1_opamp_enum = | ||
491 | SOC_ENUM_SINGLE(PM860X_HS1_CTRL, 5, 4, pm860x_opamp_texts); | ||
492 | |||
493 | static const struct soc_enum pm860x_hs2_opamp_enum = | ||
494 | SOC_ENUM_SINGLE(PM860X_HS2_CTRL, 5, 4, pm860x_opamp_texts); | ||
495 | |||
496 | static const struct soc_enum pm860x_hs1_pa_enum = | ||
497 | SOC_ENUM_SINGLE(PM860X_HS1_CTRL, 3, 4, pm860x_pa_texts); | ||
498 | |||
499 | static const struct soc_enum pm860x_hs2_pa_enum = | ||
500 | SOC_ENUM_SINGLE(PM860X_HS2_CTRL, 3, 4, pm860x_pa_texts); | ||
501 | |||
502 | static const struct soc_enum pm860x_lo1_opamp_enum = | ||
503 | SOC_ENUM_SINGLE(PM860X_LO1_CTRL, 5, 4, pm860x_opamp_texts); | ||
504 | |||
505 | static const struct soc_enum pm860x_lo2_opamp_enum = | ||
506 | SOC_ENUM_SINGLE(PM860X_LO2_CTRL, 5, 4, pm860x_opamp_texts); | ||
507 | |||
508 | static const struct soc_enum pm860x_lo1_pa_enum = | ||
509 | SOC_ENUM_SINGLE(PM860X_LO1_CTRL, 3, 4, pm860x_pa_texts); | ||
510 | |||
511 | static const struct soc_enum pm860x_lo2_pa_enum = | ||
512 | SOC_ENUM_SINGLE(PM860X_LO2_CTRL, 3, 4, pm860x_pa_texts); | ||
513 | |||
514 | static const struct soc_enum pm860x_spk_pa_enum = | ||
515 | SOC_ENUM_SINGLE(PM860X_EAR_CTRL_1, 5, 4, pm860x_pa_texts); | ||
516 | |||
517 | static const struct soc_enum pm860x_ear_pa_enum = | ||
518 | SOC_ENUM_SINGLE(PM860X_EAR_CTRL_2, 0, 4, pm860x_pa_texts); | ||
519 | |||
520 | static const struct soc_enum pm860x_spk_ear_opamp_enum = | ||
521 | SOC_ENUM_SINGLE(PM860X_EAR_CTRL_1, 3, 4, pm860x_opamp_texts); | ||
522 | |||
523 | static const struct snd_kcontrol_new pm860x_snd_controls[] = { | ||
524 | SOC_DOUBLE_R_TLV("ADC Capture Volume", PM860X_ADC_ANA_2, | ||
525 | PM860X_ADC_ANA_3, 6, 3, 0, adc_tlv), | ||
526 | SOC_DOUBLE_TLV("AUX Capture Volume", PM860X_ADC_ANA_3, 0, 3, 7, 0, | ||
527 | aux_tlv), | ||
528 | SOC_SINGLE_TLV("MIC1 Capture Volume", PM860X_ADC_ANA_2, 0, 7, 0, | ||
529 | mic_tlv), | ||
530 | SOC_SINGLE_TLV("MIC3 Capture Volume", PM860X_ADC_ANA_2, 3, 7, 0, | ||
531 | mic_tlv), | ||
532 | SOC_DOUBLE_R_EXT_TLV("Sidetone Volume", PM860X_SIDETONE_L_GAIN, | ||
533 | PM860X_SIDETONE_R_GAIN, 0, ARRAY_SIZE(st_table)-1, | ||
534 | 0, snd_soc_get_volsw_2r_st, | ||
535 | snd_soc_put_volsw_2r_st, st_tlv), | ||
536 | SOC_SINGLE_TLV("Speaker Playback Volume", PM860X_EAR_CTRL_1, | ||
537 | 0, 7, 0, out_tlv), | ||
538 | SOC_DOUBLE_R_TLV("Line Playback Volume", PM860X_LO1_CTRL, | ||
539 | PM860X_LO2_CTRL, 0, 7, 0, out_tlv), | ||
540 | SOC_DOUBLE_R_TLV("Headset Playback Volume", PM860X_HS1_CTRL, | ||
541 | PM860X_HS2_CTRL, 0, 7, 0, out_tlv), | ||
542 | SOC_DOUBLE_R_EXT_TLV("Hifi Left Playback Volume", | ||
543 | PM860X_HIFIL_GAIN_LEFT, | ||
544 | PM860X_HIFIL_GAIN_RIGHT, 0, 63, 0, | ||
545 | snd_soc_get_volsw_2r_out, | ||
546 | snd_soc_put_volsw_2r_out, dpga_tlv), | ||
547 | SOC_DOUBLE_R_EXT_TLV("Hifi Right Playback Volume", | ||
548 | PM860X_HIFIR_GAIN_LEFT, | ||
549 | PM860X_HIFIR_GAIN_RIGHT, 0, 63, 0, | ||
550 | snd_soc_get_volsw_2r_out, | ||
551 | snd_soc_put_volsw_2r_out, dpga_tlv), | ||
552 | SOC_DOUBLE_R_EXT_TLV("Lofi Playback Volume", PM860X_LOFI_GAIN_LEFT, | ||
553 | PM860X_LOFI_GAIN_RIGHT, 0, 63, 0, | ||
554 | snd_soc_get_volsw_2r_out, | ||
555 | snd_soc_put_volsw_2r_out, dpga_tlv), | ||
556 | SOC_ENUM("Headset1 Operational Amplifier Current", | ||
557 | pm860x_hs1_opamp_enum), | ||
558 | SOC_ENUM("Headset2 Operational Amplifier Current", | ||
559 | pm860x_hs2_opamp_enum), | ||
560 | SOC_ENUM("Headset1 Amplifier Current", pm860x_hs1_pa_enum), | ||
561 | SOC_ENUM("Headset2 Amplifier Current", pm860x_hs2_pa_enum), | ||
562 | SOC_ENUM("Lineout1 Operational Amplifier Current", | ||
563 | pm860x_lo1_opamp_enum), | ||
564 | SOC_ENUM("Lineout2 Operational Amplifier Current", | ||
565 | pm860x_lo2_opamp_enum), | ||
566 | SOC_ENUM("Lineout1 Amplifier Current", pm860x_lo1_pa_enum), | ||
567 | SOC_ENUM("Lineout2 Amplifier Current", pm860x_lo2_pa_enum), | ||
568 | SOC_ENUM("Speaker Operational Amplifier Current", | ||
569 | pm860x_spk_ear_opamp_enum), | ||
570 | SOC_ENUM("Speaker Amplifier Current", pm860x_spk_pa_enum), | ||
571 | SOC_ENUM("Earpiece Amplifier Current", pm860x_ear_pa_enum), | ||
572 | }; | ||
573 | |||
574 | /* | ||
575 | * DAPM Controls | ||
576 | */ | ||
577 | |||
578 | /* PCM Switch / PCM Interface */ | ||
579 | static const struct snd_kcontrol_new pcm_switch_controls = | ||
580 | SOC_DAPM_SINGLE("Switch", PM860X_ADC_EN_2, 0, 1, 0); | ||
581 | |||
582 | /* AUX1 Switch */ | ||
583 | static const struct snd_kcontrol_new aux1_switch_controls = | ||
584 | SOC_DAPM_SINGLE("Switch", PM860X_ANA_TO_ANA, 4, 1, 0); | ||
585 | |||
586 | /* AUX2 Switch */ | ||
587 | static const struct snd_kcontrol_new aux2_switch_controls = | ||
588 | SOC_DAPM_SINGLE("Switch", PM860X_ANA_TO_ANA, 5, 1, 0); | ||
589 | |||
590 | /* Left Ex. PA Switch */ | ||
591 | static const struct snd_kcontrol_new lepa_switch_controls = | ||
592 | SOC_DAPM_SINGLE("Switch", PM860X_DAC_EN_2, 2, 1, 0); | ||
593 | |||
594 | /* Right Ex. PA Switch */ | ||
595 | static const struct snd_kcontrol_new repa_switch_controls = | ||
596 | SOC_DAPM_SINGLE("Switch", PM860X_DAC_EN_2, 1, 1, 0); | ||
597 | |||
598 | /* PCM Mux / Mux7 */ | ||
599 | static const char *aif1_text[] = { | ||
600 | "PCM L", "PCM R", | ||
601 | }; | ||
602 | |||
603 | static const struct soc_enum aif1_enum = | ||
604 | SOC_ENUM_SINGLE(PM860X_PCM_IFACE_3, 6, 2, aif1_text); | ||
605 | |||
606 | static const struct snd_kcontrol_new aif1_mux = | ||
607 | SOC_DAPM_ENUM("PCM Mux", aif1_enum); | ||
608 | |||
609 | /* I2S Mux / Mux9 */ | ||
610 | static const char *i2s_din_text[] = { | ||
611 | "DIN", "DIN1", | ||
612 | }; | ||
613 | |||
614 | static const struct soc_enum i2s_din_enum = | ||
615 | SOC_ENUM_SINGLE(PM860X_I2S_IFACE_3, 1, 2, i2s_din_text); | ||
616 | |||
617 | static const struct snd_kcontrol_new i2s_din_mux = | ||
618 | SOC_DAPM_ENUM("I2S DIN Mux", i2s_din_enum); | ||
619 | |||
620 | /* I2S Mic Mux / Mux8 */ | ||
621 | static const char *i2s_mic_text[] = { | ||
622 | "Ex PA", "ADC", | ||
623 | }; | ||
624 | |||
625 | static const struct soc_enum i2s_mic_enum = | ||
626 | SOC_ENUM_SINGLE(PM860X_I2S_IFACE_3, 4, 2, i2s_mic_text); | ||
627 | |||
628 | static const struct snd_kcontrol_new i2s_mic_mux = | ||
629 | SOC_DAPM_ENUM("I2S Mic Mux", i2s_mic_enum); | ||
630 | |||
631 | /* ADCL Mux / Mux2 */ | ||
632 | static const char *adcl_text[] = { | ||
633 | "ADCR", "ADCL", | ||
634 | }; | ||
635 | |||
636 | static const struct soc_enum adcl_enum = | ||
637 | SOC_ENUM_SINGLE(PM860X_PCM_IFACE_3, 4, 2, adcl_text); | ||
638 | |||
639 | static const struct snd_kcontrol_new adcl_mux = | ||
640 | SOC_DAPM_ENUM("ADC Left Mux", adcl_enum); | ||
641 | |||
642 | /* ADCR Mux / Mux3 */ | ||
643 | static const char *adcr_text[] = { | ||
644 | "ADCL", "ADCR", | ||
645 | }; | ||
646 | |||
647 | static const struct soc_enum adcr_enum = | ||
648 | SOC_ENUM_SINGLE(PM860X_PCM_IFACE_3, 2, 2, adcr_text); | ||
649 | |||
650 | static const struct snd_kcontrol_new adcr_mux = | ||
651 | SOC_DAPM_ENUM("ADC Right Mux", adcr_enum); | ||
652 | |||
653 | /* ADCR EC Mux / Mux6 */ | ||
654 | static const char *adcr_ec_text[] = { | ||
655 | "ADCR", "EC", | ||
656 | }; | ||
657 | |||
658 | static const struct soc_enum adcr_ec_enum = | ||
659 | SOC_ENUM_SINGLE(PM860X_ADC_EN_2, 3, 2, adcr_ec_text); | ||
660 | |||
661 | static const struct snd_kcontrol_new adcr_ec_mux = | ||
662 | SOC_DAPM_ENUM("ADCR EC Mux", adcr_ec_enum); | ||
663 | |||
664 | /* EC Mux / Mux4 */ | ||
665 | static const char *ec_text[] = { | ||
666 | "Left", "Right", "Left + Right", | ||
667 | }; | ||
668 | |||
669 | static const struct soc_enum ec_enum = | ||
670 | SOC_ENUM_SINGLE(PM860X_EC_PATH, 1, 3, ec_text); | ||
671 | |||
672 | static const struct snd_kcontrol_new ec_mux = | ||
673 | SOC_DAPM_ENUM("EC Mux", ec_enum); | ||
674 | |||
675 | static const char *dac_text[] = { | ||
676 | "No input", "Right", "Left", "No input", | ||
677 | }; | ||
678 | |||
679 | /* DAC Headset 1 Mux / Mux10 */ | ||
680 | static const struct soc_enum dac_hs1_enum = | ||
681 | SOC_ENUM_SINGLE(PM860X_ANA_INPUT_SEL_1, 0, 4, dac_text); | ||
682 | |||
683 | static const struct snd_kcontrol_new dac_hs1_mux = | ||
684 | SOC_DAPM_ENUM("DAC HS1 Mux", dac_hs1_enum); | ||
685 | |||
686 | /* DAC Headset 2 Mux / Mux11 */ | ||
687 | static const struct soc_enum dac_hs2_enum = | ||
688 | SOC_ENUM_SINGLE(PM860X_ANA_INPUT_SEL_1, 2, 4, dac_text); | ||
689 | |||
690 | static const struct snd_kcontrol_new dac_hs2_mux = | ||
691 | SOC_DAPM_ENUM("DAC HS2 Mux", dac_hs2_enum); | ||
692 | |||
693 | /* DAC Lineout 1 Mux / Mux12 */ | ||
694 | static const struct soc_enum dac_lo1_enum = | ||
695 | SOC_ENUM_SINGLE(PM860X_ANA_INPUT_SEL_1, 4, 4, dac_text); | ||
696 | |||
697 | static const struct snd_kcontrol_new dac_lo1_mux = | ||
698 | SOC_DAPM_ENUM("DAC LO1 Mux", dac_lo1_enum); | ||
699 | |||
700 | /* DAC Lineout 2 Mux / Mux13 */ | ||
701 | static const struct soc_enum dac_lo2_enum = | ||
702 | SOC_ENUM_SINGLE(PM860X_ANA_INPUT_SEL_1, 6, 4, dac_text); | ||
703 | |||
704 | static const struct snd_kcontrol_new dac_lo2_mux = | ||
705 | SOC_DAPM_ENUM("DAC LO2 Mux", dac_lo2_enum); | ||
706 | |||
707 | /* DAC Spearker Earphone Mux / Mux14 */ | ||
708 | static const struct soc_enum dac_spk_ear_enum = | ||
709 | SOC_ENUM_SINGLE(PM860X_ANA_INPUT_SEL_2, 0, 4, dac_text); | ||
710 | |||
711 | static const struct snd_kcontrol_new dac_spk_ear_mux = | ||
712 | SOC_DAPM_ENUM("DAC SP Mux", dac_spk_ear_enum); | ||
713 | |||
714 | /* Headset 1 Mux / Mux15 */ | ||
715 | static const char *in_text[] = { | ||
716 | "Digital", "Analog", | ||
717 | }; | ||
718 | |||
719 | static const struct soc_enum hs1_enum = | ||
720 | SOC_ENUM_SINGLE(PM860X_ANA_TO_ANA, 0, 2, in_text); | ||
721 | |||
722 | static const struct snd_kcontrol_new hs1_mux = | ||
723 | SOC_DAPM_ENUM("Headset1 Mux", hs1_enum); | ||
724 | |||
725 | /* Headset 2 Mux / Mux16 */ | ||
726 | static const struct soc_enum hs2_enum = | ||
727 | SOC_ENUM_SINGLE(PM860X_ANA_TO_ANA, 1, 2, in_text); | ||
728 | |||
729 | static const struct snd_kcontrol_new hs2_mux = | ||
730 | SOC_DAPM_ENUM("Headset2 Mux", hs2_enum); | ||
731 | |||
732 | /* Lineout 1 Mux / Mux17 */ | ||
733 | static const struct soc_enum lo1_enum = | ||
734 | SOC_ENUM_SINGLE(PM860X_ANA_TO_ANA, 2, 2, in_text); | ||
735 | |||
736 | static const struct snd_kcontrol_new lo1_mux = | ||
737 | SOC_DAPM_ENUM("Lineout1 Mux", lo1_enum); | ||
738 | |||
739 | /* Lineout 2 Mux / Mux18 */ | ||
740 | static const struct soc_enum lo2_enum = | ||
741 | SOC_ENUM_SINGLE(PM860X_ANA_TO_ANA, 3, 2, in_text); | ||
742 | |||
743 | static const struct snd_kcontrol_new lo2_mux = | ||
744 | SOC_DAPM_ENUM("Lineout2 Mux", lo2_enum); | ||
745 | |||
746 | /* Speaker Earpiece Demux */ | ||
747 | static const char *spk_text[] = { | ||
748 | "Earpiece", "Speaker", | ||
749 | }; | ||
750 | |||
751 | static const struct soc_enum spk_enum = | ||
752 | SOC_ENUM_SINGLE(PM860X_ANA_TO_ANA, 6, 2, spk_text); | ||
753 | |||
754 | static const struct snd_kcontrol_new spk_demux = | ||
755 | SOC_DAPM_ENUM("Speaker Earpiece Demux", spk_enum); | ||
756 | |||
757 | /* MIC Mux / Mux1 */ | ||
758 | static const char *mic_text[] = { | ||
759 | "Mic 1", "Mic 2", | ||
760 | }; | ||
761 | |||
762 | static const struct soc_enum mic_enum = | ||
763 | SOC_ENUM_SINGLE(PM860X_ADC_ANA_4, 4, 2, mic_text); | ||
764 | |||
765 | static const struct snd_kcontrol_new mic_mux = | ||
766 | SOC_DAPM_ENUM("MIC Mux", mic_enum); | ||
767 | |||
768 | static const struct snd_soc_dapm_widget pm860x_dapm_widgets[] = { | ||
769 | SND_SOC_DAPM_AIF_IN("PCM SDI", "PCM Playback", 0, | ||
770 | PM860X_ADC_EN_2, 0, 0), | ||
771 | SND_SOC_DAPM_AIF_OUT("PCM SDO", "PCM Capture", 0, | ||
772 | PM860X_PCM_IFACE_3, 1, 1), | ||
773 | |||
774 | |||
775 | SND_SOC_DAPM_AIF_IN("I2S DIN", "I2S Playback", 0, | ||
776 | PM860X_DAC_EN_2, 0, 0), | ||
777 | SND_SOC_DAPM_AIF_IN("I2S DIN1", "I2S Playback", 0, | ||
778 | PM860X_DAC_EN_2, 0, 0), | ||
779 | SND_SOC_DAPM_AIF_OUT("I2S DOUT", "I2S Capture", 0, | ||
780 | PM860X_I2S_IFACE_3, 5, 1), | ||
781 | SND_SOC_DAPM_MUX("I2S Mic Mux", SND_SOC_NOPM, 0, 0, &i2s_mic_mux), | ||
782 | SND_SOC_DAPM_MUX("ADC Left Mux", SND_SOC_NOPM, 0, 0, &adcl_mux), | ||
783 | SND_SOC_DAPM_MUX("ADC Right Mux", SND_SOC_NOPM, 0, 0, &adcr_mux), | ||
784 | SND_SOC_DAPM_MUX("EC Mux", SND_SOC_NOPM, 0, 0, &ec_mux), | ||
785 | SND_SOC_DAPM_MUX("ADCR EC Mux", SND_SOC_NOPM, 0, 0, &adcr_ec_mux), | ||
786 | SND_SOC_DAPM_SWITCH("Left EPA", SND_SOC_NOPM, 0, 0, | ||
787 | &lepa_switch_controls), | ||
788 | SND_SOC_DAPM_SWITCH("Right EPA", SND_SOC_NOPM, 0, 0, | ||
789 | &repa_switch_controls), | ||
790 | |||
791 | SND_SOC_DAPM_REG(snd_soc_dapm_supply, "Left ADC MOD", PM860X_ADC_EN_1, | ||
792 | 0, 1, 1, 0), | ||
793 | SND_SOC_DAPM_REG(snd_soc_dapm_supply, "Right ADC MOD", PM860X_ADC_EN_1, | ||
794 | 1, 1, 1, 0), | ||
795 | SND_SOC_DAPM_ADC("Left ADC", NULL, PM860X_ADC_EN_2, 5, 0), | ||
796 | SND_SOC_DAPM_ADC("Right ADC", NULL, PM860X_ADC_EN_2, 4, 0), | ||
797 | |||
798 | SND_SOC_DAPM_SWITCH("AUX1 Switch", SND_SOC_NOPM, 0, 0, | ||
799 | &aux1_switch_controls), | ||
800 | SND_SOC_DAPM_SWITCH("AUX2 Switch", SND_SOC_NOPM, 0, 0, | ||
801 | &aux2_switch_controls), | ||
802 | |||
803 | SND_SOC_DAPM_MUX("MIC Mux", SND_SOC_NOPM, 0, 0, &mic_mux), | ||
804 | SND_SOC_DAPM_MICBIAS("Mic1 Bias", PM860X_ADC_ANA_1, 2, 0), | ||
805 | SND_SOC_DAPM_MICBIAS("Mic3 Bias", PM860X_ADC_ANA_1, 7, 0), | ||
806 | SND_SOC_DAPM_PGA("MIC1 Volume", PM860X_ADC_EN_1, 2, 0, NULL, 0), | ||
807 | SND_SOC_DAPM_PGA("MIC3 Volume", PM860X_ADC_EN_1, 3, 0, NULL, 0), | ||
808 | SND_SOC_DAPM_PGA("AUX1 Volume", PM860X_ADC_EN_1, 4, 0, NULL, 0), | ||
809 | SND_SOC_DAPM_PGA("AUX2 Volume", PM860X_ADC_EN_1, 5, 0, NULL, 0), | ||
810 | SND_SOC_DAPM_PGA("Sidetone PGA", PM860X_ADC_EN_2, 1, 0, NULL, 0), | ||
811 | SND_SOC_DAPM_PGA("Lofi PGA", PM860X_ADC_EN_2, 2, 0, NULL, 0), | ||
812 | |||
813 | SND_SOC_DAPM_INPUT("AUX1"), | ||
814 | SND_SOC_DAPM_INPUT("AUX2"), | ||
815 | SND_SOC_DAPM_INPUT("MIC1P"), | ||
816 | SND_SOC_DAPM_INPUT("MIC1N"), | ||
817 | SND_SOC_DAPM_INPUT("MIC2P"), | ||
818 | SND_SOC_DAPM_INPUT("MIC2N"), | ||
819 | SND_SOC_DAPM_INPUT("MIC3P"), | ||
820 | SND_SOC_DAPM_INPUT("MIC3N"), | ||
821 | |||
822 | SND_SOC_DAPM_DAC_E("Left DAC", NULL, SND_SOC_NOPM, 0, 0, | ||
823 | pm860x_dac_event, | ||
824 | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), | ||
825 | SND_SOC_DAPM_DAC_E("Right DAC", NULL, SND_SOC_NOPM, 0, 0, | ||
826 | pm860x_dac_event, | ||
827 | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), | ||
828 | |||
829 | SND_SOC_DAPM_MUX("I2S DIN Mux", SND_SOC_NOPM, 0, 0, &i2s_din_mux), | ||
830 | SND_SOC_DAPM_MUX("DAC HS1 Mux", SND_SOC_NOPM, 0, 0, &dac_hs1_mux), | ||
831 | SND_SOC_DAPM_MUX("DAC HS2 Mux", SND_SOC_NOPM, 0, 0, &dac_hs2_mux), | ||
832 | SND_SOC_DAPM_MUX("DAC LO1 Mux", SND_SOC_NOPM, 0, 0, &dac_lo1_mux), | ||
833 | SND_SOC_DAPM_MUX("DAC LO2 Mux", SND_SOC_NOPM, 0, 0, &dac_lo2_mux), | ||
834 | SND_SOC_DAPM_MUX("DAC SP Mux", SND_SOC_NOPM, 0, 0, &dac_spk_ear_mux), | ||
835 | SND_SOC_DAPM_MUX("Headset1 Mux", SND_SOC_NOPM, 0, 0, &hs1_mux), | ||
836 | SND_SOC_DAPM_MUX("Headset2 Mux", SND_SOC_NOPM, 0, 0, &hs2_mux), | ||
837 | SND_SOC_DAPM_MUX("Lineout1 Mux", SND_SOC_NOPM, 0, 0, &lo1_mux), | ||
838 | SND_SOC_DAPM_MUX("Lineout2 Mux", SND_SOC_NOPM, 0, 0, &lo2_mux), | ||
839 | SND_SOC_DAPM_MUX("Speaker Earpiece Demux", SND_SOC_NOPM, 0, 0, | ||
840 | &spk_demux), | ||
841 | |||
842 | |||
843 | SND_SOC_DAPM_PGA("Headset1 PGA", PM860X_DAC_EN_1, 0, 0, NULL, 0), | ||
844 | SND_SOC_DAPM_PGA("Headset2 PGA", PM860X_DAC_EN_1, 1, 0, NULL, 0), | ||
845 | SND_SOC_DAPM_OUTPUT("HS1"), | ||
846 | SND_SOC_DAPM_OUTPUT("HS2"), | ||
847 | SND_SOC_DAPM_PGA("Lineout1 PGA", PM860X_DAC_EN_1, 2, 0, NULL, 0), | ||
848 | SND_SOC_DAPM_PGA("Lineout2 PGA", PM860X_DAC_EN_1, 3, 0, NULL, 0), | ||
849 | SND_SOC_DAPM_OUTPUT("LINEOUT1"), | ||
850 | SND_SOC_DAPM_OUTPUT("LINEOUT2"), | ||
851 | SND_SOC_DAPM_PGA("Earpiece PGA", PM860X_DAC_EN_1, 4, 0, NULL, 0), | ||
852 | SND_SOC_DAPM_OUTPUT("EARP"), | ||
853 | SND_SOC_DAPM_OUTPUT("EARN"), | ||
854 | SND_SOC_DAPM_PGA("Speaker PGA", PM860X_DAC_EN_1, 5, 0, NULL, 0), | ||
855 | SND_SOC_DAPM_OUTPUT("LSP"), | ||
856 | SND_SOC_DAPM_OUTPUT("LSN"), | ||
857 | SND_SOC_DAPM_REG(snd_soc_dapm_supply, "VCODEC", PM860X_AUDIO_SUPPLIES_2, | ||
858 | 0, SUPPLY_MASK, SUPPLY_MASK, 0), | ||
859 | |||
860 | PM860X_DAPM_OUTPUT("RSYNC", pm860x_rsync_event), | ||
861 | }; | ||
862 | |||
863 | static const struct snd_soc_dapm_route audio_map[] = { | ||
864 | /* supply */ | ||
865 | {"Left DAC", NULL, "VCODEC"}, | ||
866 | {"Right DAC", NULL, "VCODEC"}, | ||
867 | {"Left ADC", NULL, "VCODEC"}, | ||
868 | {"Right ADC", NULL, "VCODEC"}, | ||
869 | {"Left ADC", NULL, "Left ADC MOD"}, | ||
870 | {"Right ADC", NULL, "Right ADC MOD"}, | ||
871 | |||
872 | /* PCM/AIF1 Inputs */ | ||
873 | {"PCM SDO", NULL, "ADC Left Mux"}, | ||
874 | {"PCM SDO", NULL, "ADCR EC Mux"}, | ||
875 | |||
876 | /* PCM/AFI2 Outputs */ | ||
877 | {"Lofi PGA", NULL, "PCM SDI"}, | ||
878 | {"Lofi PGA", NULL, "Sidetone PGA"}, | ||
879 | {"Left DAC", NULL, "Lofi PGA"}, | ||
880 | {"Right DAC", NULL, "Lofi PGA"}, | ||
881 | |||
882 | /* I2S/AIF2 Inputs */ | ||
883 | {"MIC Mux", "Mic 1", "MIC1P"}, | ||
884 | {"MIC Mux", "Mic 1", "MIC1N"}, | ||
885 | {"MIC Mux", "Mic 2", "MIC2P"}, | ||
886 | {"MIC Mux", "Mic 2", "MIC2N"}, | ||
887 | {"MIC1 Volume", NULL, "MIC Mux"}, | ||
888 | {"MIC3 Volume", NULL, "MIC3P"}, | ||
889 | {"MIC3 Volume", NULL, "MIC3N"}, | ||
890 | {"Left ADC", NULL, "MIC1 Volume"}, | ||
891 | {"Right ADC", NULL, "MIC3 Volume"}, | ||
892 | {"ADC Left Mux", "ADCR", "Right ADC"}, | ||
893 | {"ADC Left Mux", "ADCL", "Left ADC"}, | ||
894 | {"ADC Right Mux", "ADCL", "Left ADC"}, | ||
895 | {"ADC Right Mux", "ADCR", "Right ADC"}, | ||
896 | {"Left EPA", "Switch", "Left DAC"}, | ||
897 | {"Right EPA", "Switch", "Right DAC"}, | ||
898 | {"EC Mux", "Left", "Left DAC"}, | ||
899 | {"EC Mux", "Right", "Right DAC"}, | ||
900 | {"EC Mux", "Left + Right", "Left DAC"}, | ||
901 | {"EC Mux", "Left + Right", "Right DAC"}, | ||
902 | {"ADCR EC Mux", "ADCR", "ADC Right Mux"}, | ||
903 | {"ADCR EC Mux", "EC", "EC Mux"}, | ||
904 | {"I2S Mic Mux", "Ex PA", "Left EPA"}, | ||
905 | {"I2S Mic Mux", "Ex PA", "Right EPA"}, | ||
906 | {"I2S Mic Mux", "ADC", "ADC Left Mux"}, | ||
907 | {"I2S Mic Mux", "ADC", "ADCR EC Mux"}, | ||
908 | {"I2S DOUT", NULL, "I2S Mic Mux"}, | ||
909 | |||
910 | /* I2S/AIF2 Outputs */ | ||
911 | {"I2S DIN Mux", "DIN", "I2S DIN"}, | ||
912 | {"I2S DIN Mux", "DIN1", "I2S DIN1"}, | ||
913 | {"Left DAC", NULL, "I2S DIN Mux"}, | ||
914 | {"Right DAC", NULL, "I2S DIN Mux"}, | ||
915 | {"DAC HS1 Mux", "Left", "Left DAC"}, | ||
916 | {"DAC HS1 Mux", "Right", "Right DAC"}, | ||
917 | {"DAC HS2 Mux", "Left", "Left DAC"}, | ||
918 | {"DAC HS2 Mux", "Right", "Right DAC"}, | ||
919 | {"DAC LO1 Mux", "Left", "Left DAC"}, | ||
920 | {"DAC LO1 Mux", "Right", "Right DAC"}, | ||
921 | {"DAC LO2 Mux", "Left", "Left DAC"}, | ||
922 | {"DAC LO2 Mux", "Right", "Right DAC"}, | ||
923 | {"Headset1 Mux", "Digital", "DAC HS1 Mux"}, | ||
924 | {"Headset2 Mux", "Digital", "DAC HS2 Mux"}, | ||
925 | {"Lineout1 Mux", "Digital", "DAC LO1 Mux"}, | ||
926 | {"Lineout2 Mux", "Digital", "DAC LO2 Mux"}, | ||
927 | {"Headset1 PGA", NULL, "Headset1 Mux"}, | ||
928 | {"Headset2 PGA", NULL, "Headset2 Mux"}, | ||
929 | {"Lineout1 PGA", NULL, "Lineout1 Mux"}, | ||
930 | {"Lineout2 PGA", NULL, "Lineout2 Mux"}, | ||
931 | {"DAC SP Mux", "Left", "Left DAC"}, | ||
932 | {"DAC SP Mux", "Right", "Right DAC"}, | ||
933 | {"Speaker Earpiece Demux", "Speaker", "DAC SP Mux"}, | ||
934 | {"Speaker PGA", NULL, "Speaker Earpiece Demux"}, | ||
935 | {"Earpiece PGA", NULL, "Speaker Earpiece Demux"}, | ||
936 | |||
937 | {"RSYNC", NULL, "Headset1 PGA"}, | ||
938 | {"RSYNC", NULL, "Headset2 PGA"}, | ||
939 | {"RSYNC", NULL, "Lineout1 PGA"}, | ||
940 | {"RSYNC", NULL, "Lineout2 PGA"}, | ||
941 | {"RSYNC", NULL, "Speaker PGA"}, | ||
942 | {"RSYNC", NULL, "Speaker PGA"}, | ||
943 | {"RSYNC", NULL, "Earpiece PGA"}, | ||
944 | {"RSYNC", NULL, "Earpiece PGA"}, | ||
945 | |||
946 | {"HS1", NULL, "RSYNC"}, | ||
947 | {"HS2", NULL, "RSYNC"}, | ||
948 | {"LINEOUT1", NULL, "RSYNC"}, | ||
949 | {"LINEOUT2", NULL, "RSYNC"}, | ||
950 | {"LSP", NULL, "RSYNC"}, | ||
951 | {"LSN", NULL, "RSYNC"}, | ||
952 | {"EARP", NULL, "RSYNC"}, | ||
953 | {"EARN", NULL, "RSYNC"}, | ||
954 | }; | ||
955 | |||
956 | /* | ||
957 | * Use MUTE_LEFT & MUTE_RIGHT to implement digital mute. | ||
958 | * These bits can also be used to mute. | ||
959 | */ | ||
960 | static int pm860x_digital_mute(struct snd_soc_dai *codec_dai, int mute) | ||
961 | { | ||
962 | struct snd_soc_codec *codec = codec_dai->codec; | ||
963 | int data = 0, mask = MUTE_LEFT | MUTE_RIGHT; | ||
964 | |||
965 | if (mute) | ||
966 | data = mask; | ||
967 | snd_soc_update_bits(codec, PM860X_DAC_OFFSET, mask, data); | ||
968 | snd_soc_update_bits(codec, PM860X_EAR_CTRL_2, | ||
969 | RSYNC_CHANGE, RSYNC_CHANGE); | ||
970 | return 0; | ||
971 | } | ||
972 | |||
973 | static int pm860x_pcm_hw_params(struct snd_pcm_substream *substream, | ||
974 | struct snd_pcm_hw_params *params, | ||
975 | struct snd_soc_dai *dai) | ||
976 | { | ||
977 | struct snd_soc_codec *codec = dai->codec; | ||
978 | unsigned char inf = 0, mask = 0; | ||
979 | |||
980 | /* bit size */ | ||
981 | switch (params_format(params)) { | ||
982 | case SNDRV_PCM_FORMAT_S16_LE: | ||
983 | inf &= ~PCM_INF2_18WL; | ||
984 | break; | ||
985 | case SNDRV_PCM_FORMAT_S18_3LE: | ||
986 | inf |= PCM_INF2_18WL; | ||
987 | break; | ||
988 | default: | ||
989 | return -EINVAL; | ||
990 | } | ||
991 | mask |= PCM_INF2_18WL; | ||
992 | snd_soc_update_bits(codec, PM860X_PCM_IFACE_2, mask, inf); | ||
993 | |||
994 | /* sample rate */ | ||
995 | switch (params_rate(params)) { | ||
996 | case 8000: | ||
997 | inf = 0; | ||
998 | break; | ||
999 | case 16000: | ||
1000 | inf = 3; | ||
1001 | break; | ||
1002 | case 32000: | ||
1003 | inf = 6; | ||
1004 | break; | ||
1005 | case 48000: | ||
1006 | inf = 8; | ||
1007 | break; | ||
1008 | default: | ||
1009 | return -EINVAL; | ||
1010 | } | ||
1011 | snd_soc_update_bits(codec, PM860X_PCM_RATE, 0x0f, inf); | ||
1012 | |||
1013 | return 0; | ||
1014 | } | ||
1015 | |||
1016 | static int pm860x_pcm_set_dai_fmt(struct snd_soc_dai *codec_dai, | ||
1017 | unsigned int fmt) | ||
1018 | { | ||
1019 | struct snd_soc_codec *codec = codec_dai->codec; | ||
1020 | struct pm860x_priv *pm860x = snd_soc_codec_get_drvdata(codec); | ||
1021 | unsigned char inf = 0, mask = 0; | ||
1022 | int ret = -EINVAL; | ||
1023 | |||
1024 | mask |= PCM_INF2_BCLK | PCM_INF2_FS | PCM_INF2_MASTER; | ||
1025 | |||
1026 | /* set master/slave audio interface */ | ||
1027 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | ||
1028 | case SND_SOC_DAIFMT_CBM_CFM: | ||
1029 | case SND_SOC_DAIFMT_CBM_CFS: | ||
1030 | if (pm860x->dir == PM860X_CLK_DIR_OUT) { | ||
1031 | inf |= PCM_INF2_MASTER; | ||
1032 | ret = 0; | ||
1033 | } | ||
1034 | break; | ||
1035 | case SND_SOC_DAIFMT_CBS_CFS: | ||
1036 | if (pm860x->dir == PM860X_CLK_DIR_IN) { | ||
1037 | inf &= ~PCM_INF2_MASTER; | ||
1038 | ret = 0; | ||
1039 | } | ||
1040 | break; | ||
1041 | } | ||
1042 | |||
1043 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
1044 | case SND_SOC_DAIFMT_I2S: | ||
1045 | inf |= PCM_EXACT_I2S; | ||
1046 | ret = 0; | ||
1047 | break; | ||
1048 | } | ||
1049 | mask |= PCM_MODE_MASK; | ||
1050 | if (ret) | ||
1051 | return ret; | ||
1052 | snd_soc_update_bits(codec, PM860X_PCM_IFACE_2, mask, inf); | ||
1053 | return 0; | ||
1054 | } | ||
1055 | |||
1056 | static int pm860x_set_dai_sysclk(struct snd_soc_dai *codec_dai, | ||
1057 | int clk_id, unsigned int freq, int dir) | ||
1058 | { | ||
1059 | struct snd_soc_codec *codec = codec_dai->codec; | ||
1060 | struct pm860x_priv *pm860x = snd_soc_codec_get_drvdata(codec); | ||
1061 | |||
1062 | if (dir == PM860X_CLK_DIR_OUT) | ||
1063 | pm860x->dir = PM860X_CLK_DIR_OUT; | ||
1064 | else { | ||
1065 | pm860x->dir = PM860X_CLK_DIR_IN; | ||
1066 | return -EINVAL; | ||
1067 | } | ||
1068 | |||
1069 | return 0; | ||
1070 | } | ||
1071 | |||
1072 | static int pm860x_i2s_hw_params(struct snd_pcm_substream *substream, | ||
1073 | struct snd_pcm_hw_params *params, | ||
1074 | struct snd_soc_dai *dai) | ||
1075 | { | ||
1076 | struct snd_soc_codec *codec = dai->codec; | ||
1077 | unsigned char inf; | ||
1078 | |||
1079 | /* bit size */ | ||
1080 | switch (params_format(params)) { | ||
1081 | case SNDRV_PCM_FORMAT_S16_LE: | ||
1082 | inf = 0; | ||
1083 | break; | ||
1084 | case SNDRV_PCM_FORMAT_S18_3LE: | ||
1085 | inf = PCM_INF2_18WL; | ||
1086 | break; | ||
1087 | default: | ||
1088 | return -EINVAL; | ||
1089 | } | ||
1090 | snd_soc_update_bits(codec, PM860X_I2S_IFACE_2, PCM_INF2_18WL, inf); | ||
1091 | |||
1092 | /* sample rate */ | ||
1093 | switch (params_rate(params)) { | ||
1094 | case 8000: | ||
1095 | inf = 0; | ||
1096 | break; | ||
1097 | case 11025: | ||
1098 | inf = 1; | ||
1099 | break; | ||
1100 | case 16000: | ||
1101 | inf = 3; | ||
1102 | break; | ||
1103 | case 22050: | ||
1104 | inf = 4; | ||
1105 | break; | ||
1106 | case 32000: | ||
1107 | inf = 6; | ||
1108 | break; | ||
1109 | case 44100: | ||
1110 | inf = 7; | ||
1111 | break; | ||
1112 | case 48000: | ||
1113 | inf = 8; | ||
1114 | break; | ||
1115 | default: | ||
1116 | return -EINVAL; | ||
1117 | } | ||
1118 | snd_soc_update_bits(codec, PM860X_I2S_IFACE_4, 0xf, inf); | ||
1119 | |||
1120 | return 0; | ||
1121 | } | ||
1122 | |||
1123 | static int pm860x_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai, | ||
1124 | unsigned int fmt) | ||
1125 | { | ||
1126 | struct snd_soc_codec *codec = codec_dai->codec; | ||
1127 | struct pm860x_priv *pm860x = snd_soc_codec_get_drvdata(codec); | ||
1128 | unsigned char inf = 0, mask = 0; | ||
1129 | |||
1130 | mask |= PCM_INF2_BCLK | PCM_INF2_FS | PCM_INF2_MASTER; | ||
1131 | |||
1132 | /* set master/slave audio interface */ | ||
1133 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | ||
1134 | case SND_SOC_DAIFMT_CBM_CFM: | ||
1135 | if (pm860x->dir == PM860X_CLK_DIR_OUT) | ||
1136 | inf |= PCM_INF2_MASTER; | ||
1137 | else | ||
1138 | return -EINVAL; | ||
1139 | break; | ||
1140 | case SND_SOC_DAIFMT_CBS_CFS: | ||
1141 | if (pm860x->dir == PM860X_CLK_DIR_IN) | ||
1142 | inf &= ~PCM_INF2_MASTER; | ||
1143 | else | ||
1144 | return -EINVAL; | ||
1145 | break; | ||
1146 | default: | ||
1147 | return -EINVAL; | ||
1148 | } | ||
1149 | |||
1150 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
1151 | case SND_SOC_DAIFMT_I2S: | ||
1152 | inf |= PCM_EXACT_I2S; | ||
1153 | break; | ||
1154 | default: | ||
1155 | return -EINVAL; | ||
1156 | } | ||
1157 | mask |= PCM_MODE_MASK; | ||
1158 | snd_soc_update_bits(codec, PM860X_I2S_IFACE_2, mask, inf); | ||
1159 | return 0; | ||
1160 | } | ||
1161 | |||
1162 | static int pm860x_set_bias_level(struct snd_soc_codec *codec, | ||
1163 | enum snd_soc_bias_level level) | ||
1164 | { | ||
1165 | int data; | ||
1166 | |||
1167 | switch (level) { | ||
1168 | case SND_SOC_BIAS_ON: | ||
1169 | break; | ||
1170 | |||
1171 | case SND_SOC_BIAS_PREPARE: | ||
1172 | break; | ||
1173 | |||
1174 | case SND_SOC_BIAS_STANDBY: | ||
1175 | if (codec->bias_level == SND_SOC_BIAS_OFF) { | ||
1176 | /* Enable Audio PLL & Audio section */ | ||
1177 | data = AUDIO_PLL | AUDIO_SECTION_RESET | ||
1178 | | AUDIO_SECTION_ON; | ||
1179 | pm860x_reg_write(codec->control_data, REG_MISC2, data); | ||
1180 | } | ||
1181 | break; | ||
1182 | |||
1183 | case SND_SOC_BIAS_OFF: | ||
1184 | data = AUDIO_PLL | AUDIO_SECTION_RESET | AUDIO_SECTION_ON; | ||
1185 | pm860x_set_bits(codec->control_data, REG_MISC2, data, 0); | ||
1186 | break; | ||
1187 | } | ||
1188 | codec->bias_level = level; | ||
1189 | return 0; | ||
1190 | } | ||
1191 | |||
1192 | static struct snd_soc_dai_ops pm860x_pcm_dai_ops = { | ||
1193 | .digital_mute = pm860x_digital_mute, | ||
1194 | .hw_params = pm860x_pcm_hw_params, | ||
1195 | .set_fmt = pm860x_pcm_set_dai_fmt, | ||
1196 | .set_sysclk = pm860x_set_dai_sysclk, | ||
1197 | }; | ||
1198 | |||
1199 | static struct snd_soc_dai_ops pm860x_i2s_dai_ops = { | ||
1200 | .digital_mute = pm860x_digital_mute, | ||
1201 | .hw_params = pm860x_i2s_hw_params, | ||
1202 | .set_fmt = pm860x_i2s_set_dai_fmt, | ||
1203 | .set_sysclk = pm860x_set_dai_sysclk, | ||
1204 | }; | ||
1205 | |||
1206 | #define PM860X_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | \ | ||
1207 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000) | ||
1208 | |||
1209 | static struct snd_soc_dai_driver pm860x_dai[] = { | ||
1210 | { | ||
1211 | /* DAI PCM */ | ||
1212 | .name = "88pm860x-pcm", | ||
1213 | .id = 1, | ||
1214 | .playback = { | ||
1215 | .stream_name = "PCM Playback", | ||
1216 | .channels_min = 2, | ||
1217 | .channels_max = 2, | ||
1218 | .rates = PM860X_RATES, | ||
1219 | .formats = SNDRV_PCM_FORMAT_S16_LE | \ | ||
1220 | SNDRV_PCM_FORMAT_S18_3LE, | ||
1221 | }, | ||
1222 | .capture = { | ||
1223 | .stream_name = "PCM Capture", | ||
1224 | .channels_min = 2, | ||
1225 | .channels_max = 2, | ||
1226 | .rates = PM860X_RATES, | ||
1227 | .formats = SNDRV_PCM_FORMAT_S16_LE | \ | ||
1228 | SNDRV_PCM_FORMAT_S18_3LE, | ||
1229 | }, | ||
1230 | .ops = &pm860x_pcm_dai_ops, | ||
1231 | }, { | ||
1232 | /* DAI I2S */ | ||
1233 | .name = "88pm860x-i2s", | ||
1234 | .id = 2, | ||
1235 | .playback = { | ||
1236 | .stream_name = "I2S Playback", | ||
1237 | .channels_min = 2, | ||
1238 | .channels_max = 2, | ||
1239 | .rates = SNDRV_PCM_RATE_8000_48000, | ||
1240 | .formats = SNDRV_PCM_FORMAT_S16_LE | \ | ||
1241 | SNDRV_PCM_FORMAT_S18_3LE, | ||
1242 | }, | ||
1243 | .capture = { | ||
1244 | .stream_name = "I2S Capture", | ||
1245 | .channels_min = 2, | ||
1246 | .channels_max = 2, | ||
1247 | .rates = SNDRV_PCM_RATE_8000_48000, | ||
1248 | .formats = SNDRV_PCM_FORMAT_S16_LE | \ | ||
1249 | SNDRV_PCM_FORMAT_S18_3LE, | ||
1250 | }, | ||
1251 | .ops = &pm860x_i2s_dai_ops, | ||
1252 | }, | ||
1253 | }; | ||
1254 | |||
1255 | static irqreturn_t pm860x_codec_handler(int irq, void *data) | ||
1256 | { | ||
1257 | struct pm860x_priv *pm860x = data; | ||
1258 | int status, shrt, report = 0, mic_report = 0; | ||
1259 | int mask; | ||
1260 | |||
1261 | status = pm860x_reg_read(pm860x->i2c, REG_STATUS_1); | ||
1262 | shrt = pm860x_reg_read(pm860x->i2c, REG_SHORTS); | ||
1263 | mask = pm860x->det.hs_shrt | pm860x->det.hook_det | pm860x->det.lo_shrt | ||
1264 | | pm860x->det.hp_det; | ||
1265 | |||
1266 | if ((pm860x->det.hp_det & SND_JACK_HEADPHONE) | ||
1267 | && (status & HEADSET_STATUS)) | ||
1268 | report |= SND_JACK_HEADPHONE; | ||
1269 | |||
1270 | if ((pm860x->det.mic_det & SND_JACK_MICROPHONE) | ||
1271 | && (status & MIC_STATUS)) | ||
1272 | mic_report |= SND_JACK_MICROPHONE; | ||
1273 | |||
1274 | if (pm860x->det.hs_shrt && (shrt & (SHORT_HS1 | SHORT_HS2))) | ||
1275 | report |= pm860x->det.hs_shrt; | ||
1276 | |||
1277 | if (pm860x->det.hook_det && (status & HOOK_STATUS)) | ||
1278 | report |= pm860x->det.hook_det; | ||
1279 | |||
1280 | if (pm860x->det.lo_shrt && (shrt & (SHORT_LO1 | SHORT_LO2))) | ||
1281 | report |= pm860x->det.lo_shrt; | ||
1282 | |||
1283 | if (report) | ||
1284 | snd_soc_jack_report(pm860x->det.hp_jack, report, mask); | ||
1285 | if (mic_report) | ||
1286 | snd_soc_jack_report(pm860x->det.mic_jack, SND_JACK_MICROPHONE, | ||
1287 | SND_JACK_MICROPHONE); | ||
1288 | |||
1289 | dev_dbg(pm860x->codec->dev, "headphone report:0x%x, mask:%x\n", | ||
1290 | report, mask); | ||
1291 | dev_dbg(pm860x->codec->dev, "microphone report:0x%x\n", mic_report); | ||
1292 | return IRQ_HANDLED; | ||
1293 | } | ||
1294 | |||
1295 | int pm860x_hs_jack_detect(struct snd_soc_codec *codec, | ||
1296 | struct snd_soc_jack *jack, | ||
1297 | int det, int hook, int hs_shrt, int lo_shrt) | ||
1298 | { | ||
1299 | struct pm860x_priv *pm860x = snd_soc_codec_get_drvdata(codec); | ||
1300 | int data; | ||
1301 | |||
1302 | pm860x->det.hp_jack = jack; | ||
1303 | pm860x->det.hp_det = det; | ||
1304 | pm860x->det.hook_det = hook; | ||
1305 | pm860x->det.hs_shrt = hs_shrt; | ||
1306 | pm860x->det.lo_shrt = lo_shrt; | ||
1307 | |||
1308 | if (det & SND_JACK_HEADPHONE) | ||
1309 | pm860x_set_bits(codec->control_data, REG_HS_DET, | ||
1310 | EN_HS_DET, EN_HS_DET); | ||
1311 | /* headset short detect */ | ||
1312 | if (hs_shrt) { | ||
1313 | data = CLR_SHORT_HS2 | CLR_SHORT_HS1; | ||
1314 | pm860x_set_bits(codec->control_data, REG_SHORTS, data, data); | ||
1315 | } | ||
1316 | /* Lineout short detect */ | ||
1317 | if (lo_shrt) { | ||
1318 | data = CLR_SHORT_LO2 | CLR_SHORT_LO1; | ||
1319 | pm860x_set_bits(codec->control_data, REG_SHORTS, data, data); | ||
1320 | } | ||
1321 | |||
1322 | /* sync status */ | ||
1323 | pm860x_codec_handler(0, pm860x); | ||
1324 | return 0; | ||
1325 | } | ||
1326 | EXPORT_SYMBOL_GPL(pm860x_hs_jack_detect); | ||
1327 | |||
1328 | int pm860x_mic_jack_detect(struct snd_soc_codec *codec, | ||
1329 | struct snd_soc_jack *jack, int det) | ||
1330 | { | ||
1331 | struct pm860x_priv *pm860x = snd_soc_codec_get_drvdata(codec); | ||
1332 | |||
1333 | pm860x->det.mic_jack = jack; | ||
1334 | pm860x->det.mic_det = det; | ||
1335 | |||
1336 | if (det & SND_JACK_MICROPHONE) | ||
1337 | pm860x_set_bits(codec->control_data, REG_MIC_DET, | ||
1338 | MICDET_MASK, MICDET_MASK); | ||
1339 | |||
1340 | /* sync status */ | ||
1341 | pm860x_codec_handler(0, pm860x); | ||
1342 | return 0; | ||
1343 | } | ||
1344 | EXPORT_SYMBOL_GPL(pm860x_mic_jack_detect); | ||
1345 | |||
1346 | static int pm860x_probe(struct snd_soc_codec *codec) | ||
1347 | { | ||
1348 | struct pm860x_priv *pm860x = snd_soc_codec_get_drvdata(codec); | ||
1349 | int i, ret; | ||
1350 | |||
1351 | pm860x->codec = codec; | ||
1352 | |||
1353 | codec->control_data = pm860x->i2c; | ||
1354 | |||
1355 | for (i = 0; i < 4; i++) { | ||
1356 | ret = request_threaded_irq(pm860x->irq[i], NULL, | ||
1357 | pm860x_codec_handler, IRQF_ONESHOT, | ||
1358 | pm860x->name[i], pm860x); | ||
1359 | if (ret < 0) { | ||
1360 | dev_err(codec->dev, "Failed to request IRQ!\n"); | ||
1361 | goto out_irq; | ||
1362 | } | ||
1363 | } | ||
1364 | |||
1365 | pm860x_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | ||
1366 | |||
1367 | ret = pm860x_bulk_read(codec->control_data, REG_CACHE_BASE, | ||
1368 | REG_CACHE_SIZE, codec->reg_cache); | ||
1369 | if (ret < 0) { | ||
1370 | dev_err(codec->dev, "Failed to fill register cache: %d\n", | ||
1371 | ret); | ||
1372 | goto out_codec; | ||
1373 | } | ||
1374 | |||
1375 | snd_soc_add_controls(codec, pm860x_snd_controls, | ||
1376 | ARRAY_SIZE(pm860x_snd_controls)); | ||
1377 | snd_soc_dapm_new_controls(codec, pm860x_dapm_widgets, | ||
1378 | ARRAY_SIZE(pm860x_dapm_widgets)); | ||
1379 | snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); | ||
1380 | return 0; | ||
1381 | |||
1382 | out_codec: | ||
1383 | i = 3; | ||
1384 | out_irq: | ||
1385 | for (; i >= 0; i--) | ||
1386 | free_irq(pm860x->irq[i], pm860x); | ||
1387 | return -EINVAL; | ||
1388 | } | ||
1389 | |||
1390 | static int pm860x_remove(struct snd_soc_codec *codec) | ||
1391 | { | ||
1392 | struct pm860x_priv *pm860x = snd_soc_codec_get_drvdata(codec); | ||
1393 | int i; | ||
1394 | |||
1395 | for (i = 3; i >= 0; i--) | ||
1396 | free_irq(pm860x->irq[i], pm860x); | ||
1397 | pm860x_set_bias_level(codec, SND_SOC_BIAS_OFF); | ||
1398 | return 0; | ||
1399 | } | ||
1400 | |||
1401 | static struct snd_soc_codec_driver soc_codec_dev_pm860x = { | ||
1402 | .probe = pm860x_probe, | ||
1403 | .remove = pm860x_remove, | ||
1404 | .read = pm860x_read_reg_cache, | ||
1405 | .write = pm860x_write_reg_cache, | ||
1406 | .reg_cache_size = REG_CACHE_SIZE, | ||
1407 | .reg_word_size = sizeof(u8), | ||
1408 | .set_bias_level = pm860x_set_bias_level, | ||
1409 | }; | ||
1410 | |||
1411 | static int __devinit pm860x_codec_probe(struct platform_device *pdev) | ||
1412 | { | ||
1413 | struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); | ||
1414 | struct pm860x_priv *pm860x; | ||
1415 | struct resource *res; | ||
1416 | int i, ret; | ||
1417 | |||
1418 | pm860x = kzalloc(sizeof(struct pm860x_priv), GFP_KERNEL); | ||
1419 | if (pm860x == NULL) | ||
1420 | return -ENOMEM; | ||
1421 | |||
1422 | pm860x->chip = chip; | ||
1423 | pm860x->i2c = (chip->id == CHIP_PM8607) ? chip->client | ||
1424 | : chip->companion; | ||
1425 | platform_set_drvdata(pdev, pm860x); | ||
1426 | |||
1427 | for (i = 0; i < 4; i++) { | ||
1428 | res = platform_get_resource(pdev, IORESOURCE_IRQ, i); | ||
1429 | if (!res) { | ||
1430 | dev_err(&pdev->dev, "Failed to get IRQ resources\n"); | ||
1431 | goto out; | ||
1432 | } | ||
1433 | pm860x->irq[i] = res->start + chip->irq_base; | ||
1434 | strncpy(pm860x->name[i], res->name, MAX_NAME_LEN); | ||
1435 | } | ||
1436 | |||
1437 | ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_pm860x, | ||
1438 | pm860x_dai, ARRAY_SIZE(pm860x_dai)); | ||
1439 | if (ret) { | ||
1440 | dev_err(&pdev->dev, "Failed to register codec\n"); | ||
1441 | goto out; | ||
1442 | } | ||
1443 | return ret; | ||
1444 | |||
1445 | out: | ||
1446 | platform_set_drvdata(pdev, NULL); | ||
1447 | kfree(pm860x); | ||
1448 | return -EINVAL; | ||
1449 | } | ||
1450 | |||
1451 | static int __devexit pm860x_codec_remove(struct platform_device *pdev) | ||
1452 | { | ||
1453 | struct pm860x_priv *pm860x = platform_get_drvdata(pdev); | ||
1454 | |||
1455 | snd_soc_unregister_codec(&pdev->dev); | ||
1456 | platform_set_drvdata(pdev, NULL); | ||
1457 | kfree(pm860x); | ||
1458 | return 0; | ||
1459 | } | ||
1460 | |||
1461 | static struct platform_driver pm860x_codec_driver = { | ||
1462 | .driver = { | ||
1463 | .name = "88pm860x-codec", | ||
1464 | .owner = THIS_MODULE, | ||
1465 | }, | ||
1466 | .probe = pm860x_codec_probe, | ||
1467 | .remove = __devexit_p(pm860x_codec_remove), | ||
1468 | }; | ||
1469 | |||
1470 | static __init int pm860x_init(void) | ||
1471 | { | ||
1472 | return platform_driver_register(&pm860x_codec_driver); | ||
1473 | } | ||
1474 | module_init(pm860x_init); | ||
1475 | |||
1476 | static __exit void pm860x_exit(void) | ||
1477 | { | ||
1478 | platform_driver_unregister(&pm860x_codec_driver); | ||
1479 | } | ||
1480 | module_exit(pm860x_exit); | ||
1481 | |||
1482 | MODULE_DESCRIPTION("ASoC 88PM860x driver"); | ||
1483 | MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>"); | ||
1484 | MODULE_LICENSE("GPL"); | ||
1485 | MODULE_ALIAS("platform:88pm860x-codec"); | ||
1486 | |||
diff --git a/sound/soc/codecs/88pm860x-codec.h b/sound/soc/codecs/88pm860x-codec.h new file mode 100644 index 000000000000..3364ba4a3607 --- /dev/null +++ b/sound/soc/codecs/88pm860x-codec.h | |||
@@ -0,0 +1,97 @@ | |||
1 | /* | ||
2 | * 88pm860x-codec.h -- 88PM860x ALSA SoC Audio Driver | ||
3 | * | ||
4 | * Copyright 2010 Marvell International Ltd. | ||
5 | * Haojian Zhuang <haojian.zhuang@marvell.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #ifndef __88PM860X_H | ||
13 | #define __88PM860X_H | ||
14 | |||
15 | /* The offset of these registers are 0xb0 */ | ||
16 | #define PM860X_PCM_IFACE_1 0x00 | ||
17 | #define PM860X_PCM_IFACE_2 0x01 | ||
18 | #define PM860X_PCM_IFACE_3 0x02 | ||
19 | #define PM860X_PCM_RATE 0x03 | ||
20 | #define PM860X_EC_PATH 0x04 | ||
21 | #define PM860X_SIDETONE_L_GAIN 0x05 | ||
22 | #define PM860X_SIDETONE_R_GAIN 0x06 | ||
23 | #define PM860X_SIDETONE_SHIFT 0x07 | ||
24 | #define PM860X_ADC_OFFSET_1 0x08 | ||
25 | #define PM860X_ADC_OFFSET_2 0x09 | ||
26 | #define PM860X_DMIC_DELAY 0x0a | ||
27 | |||
28 | #define PM860X_I2S_IFACE_1 0x0b | ||
29 | #define PM860X_I2S_IFACE_2 0x0c | ||
30 | #define PM860X_I2S_IFACE_3 0x0d | ||
31 | #define PM860X_I2S_IFACE_4 0x0e | ||
32 | #define PM860X_EQUALIZER_N0_1 0x0f | ||
33 | #define PM860X_EQUALIZER_N0_2 0x10 | ||
34 | #define PM860X_EQUALIZER_N1_1 0x11 | ||
35 | #define PM860X_EQUALIZER_N1_2 0x12 | ||
36 | #define PM860X_EQUALIZER_D1_1 0x13 | ||
37 | #define PM860X_EQUALIZER_D1_2 0x14 | ||
38 | #define PM860X_LOFI_GAIN_LEFT 0x15 | ||
39 | #define PM860X_LOFI_GAIN_RIGHT 0x16 | ||
40 | #define PM860X_HIFIL_GAIN_LEFT 0x17 | ||
41 | #define PM860X_HIFIL_GAIN_RIGHT 0x18 | ||
42 | #define PM860X_HIFIR_GAIN_LEFT 0x19 | ||
43 | #define PM860X_HIFIR_GAIN_RIGHT 0x1a | ||
44 | #define PM860X_DAC_OFFSET 0x1b | ||
45 | #define PM860X_OFFSET_LEFT_1 0x1c | ||
46 | #define PM860X_OFFSET_LEFT_2 0x1d | ||
47 | #define PM860X_OFFSET_RIGHT_1 0x1e | ||
48 | #define PM860X_OFFSET_RIGHT_2 0x1f | ||
49 | #define PM860X_ADC_ANA_1 0x20 | ||
50 | #define PM860X_ADC_ANA_2 0x21 | ||
51 | #define PM860X_ADC_ANA_3 0x22 | ||
52 | #define PM860X_ADC_ANA_4 0x23 | ||
53 | #define PM860X_ANA_TO_ANA 0x24 | ||
54 | #define PM860X_HS1_CTRL 0x25 | ||
55 | #define PM860X_HS2_CTRL 0x26 | ||
56 | #define PM860X_LO1_CTRL 0x27 | ||
57 | #define PM860X_LO2_CTRL 0x28 | ||
58 | #define PM860X_EAR_CTRL_1 0x29 | ||
59 | #define PM860X_EAR_CTRL_2 0x2a | ||
60 | #define PM860X_AUDIO_SUPPLIES_1 0x2b | ||
61 | #define PM860X_AUDIO_SUPPLIES_2 0x2c | ||
62 | #define PM860X_ADC_EN_1 0x2d | ||
63 | #define PM860X_ADC_EN_2 0x2e | ||
64 | #define PM860X_DAC_EN_1 0x2f | ||
65 | #define PM860X_DAC_EN_2 0x31 | ||
66 | #define PM860X_AUDIO_CAL_1 0x32 | ||
67 | #define PM860X_AUDIO_CAL_2 0x33 | ||
68 | #define PM860X_AUDIO_CAL_3 0x34 | ||
69 | #define PM860X_AUDIO_CAL_4 0x35 | ||
70 | #define PM860X_AUDIO_CAL_5 0x36 | ||
71 | #define PM860X_ANA_INPUT_SEL_1 0x37 | ||
72 | #define PM860X_ANA_INPUT_SEL_2 0x38 | ||
73 | |||
74 | #define PM860X_PCM_IFACE_4 0x39 | ||
75 | #define PM860X_I2S_IFACE_5 0x3a | ||
76 | |||
77 | #define PM860X_SHORTS 0x3b | ||
78 | #define PM860X_PLL_ADJ_1 0x3c | ||
79 | #define PM860X_PLL_ADJ_2 0x3d | ||
80 | |||
81 | /* bits definition */ | ||
82 | #define PM860X_CLK_DIR_IN 0 | ||
83 | #define PM860X_CLK_DIR_OUT 1 | ||
84 | |||
85 | #define PM860X_DET_HEADSET (1 << 0) | ||
86 | #define PM860X_DET_MIC (1 << 1) | ||
87 | #define PM860X_DET_HOOK (1 << 2) | ||
88 | #define PM860X_SHORT_HEADSET (1 << 3) | ||
89 | #define PM860X_SHORT_LINEOUT (1 << 4) | ||
90 | #define PM860X_DET_MASK 0x1F | ||
91 | |||
92 | extern int pm860x_hs_jack_detect(struct snd_soc_codec *, struct snd_soc_jack *, | ||
93 | int, int, int, int); | ||
94 | extern int pm860x_mic_jack_detect(struct snd_soc_codec *, struct snd_soc_jack *, | ||
95 | int); | ||
96 | |||
97 | #endif /* __88PM860X_H */ | ||
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index bfdd92b78fb6..155c1276d1a1 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig | |||
@@ -10,6 +10,7 @@ config SND_SOC_I2C_AND_SPI | |||
10 | 10 | ||
11 | config SND_SOC_ALL_CODECS | 11 | config SND_SOC_ALL_CODECS |
12 | tristate "Build all ASoC CODEC drivers" | 12 | tristate "Build all ASoC CODEC drivers" |
13 | select SND_SOC_88PM860X if MFD_88PM860X | ||
13 | select SND_SOC_L3 | 14 | select SND_SOC_L3 |
14 | select SND_SOC_AC97_CODEC if SND_SOC_AC97_BUS | 15 | select SND_SOC_AC97_CODEC if SND_SOC_AC97_BUS |
15 | select SND_SOC_AD1836 if SPI_MASTER | 16 | select SND_SOC_AD1836 if SPI_MASTER |
@@ -40,6 +41,7 @@ config SND_SOC_ALL_CODECS | |||
40 | select SND_SOC_TWL6040 if TWL4030_CORE | 41 | select SND_SOC_TWL6040 if TWL4030_CORE |
41 | select SND_SOC_UDA134X | 42 | select SND_SOC_UDA134X |
42 | select SND_SOC_UDA1380 if I2C | 43 | select SND_SOC_UDA1380 if I2C |
44 | select SND_SOC_WL1273 if WL1273_CORE | ||
43 | select SND_SOC_WM2000 if I2C | 45 | select SND_SOC_WM2000 if I2C |
44 | select SND_SOC_WM8350 if MFD_WM8350 | 46 | select SND_SOC_WM8350 if MFD_WM8350 |
45 | select SND_SOC_WM8400 if MFD_WM8400 | 47 | select SND_SOC_WM8400 if MFD_WM8400 |
@@ -85,6 +87,9 @@ config SND_SOC_ALL_CODECS | |||
85 | 87 | ||
86 | If unsure select "N". | 88 | If unsure select "N". |
87 | 89 | ||
90 | config SND_SOC_88PM860X | ||
91 | tristate | ||
92 | |||
88 | config SND_SOC_WM_HUBS | 93 | config SND_SOC_WM_HUBS |
89 | tristate | 94 | tristate |
90 | default y if SND_SOC_WM8993=y || SND_SOC_WM8994=y | 95 | default y if SND_SOC_WM8993=y || SND_SOC_WM8994=y |
@@ -189,6 +194,9 @@ config SND_SOC_UDA134X | |||
189 | config SND_SOC_UDA1380 | 194 | config SND_SOC_UDA1380 |
190 | tristate | 195 | tristate |
191 | 196 | ||
197 | config SND_SOC_WL1273 | ||
198 | tristate | ||
199 | |||
192 | config SND_SOC_WM8350 | 200 | config SND_SOC_WM8350 |
193 | tristate | 201 | tristate |
194 | 202 | ||
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 9c3c39fd99ad..10d468e4a1ed 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile | |||
@@ -1,3 +1,4 @@ | |||
1 | snd-soc-88pm860x-objs := 88pm860x-codec.o | ||
1 | snd-soc-ac97-objs := ac97.o | 2 | snd-soc-ac97-objs := ac97.o |
2 | snd-soc-ad1836-objs := ad1836.o | 3 | snd-soc-ad1836-objs := ad1836.o |
3 | snd-soc-ad193x-objs := ad193x.o | 4 | snd-soc-ad193x-objs := ad193x.o |
@@ -26,6 +27,7 @@ snd-soc-twl4030-objs := twl4030.o | |||
26 | snd-soc-twl6040-objs := twl6040.o | 27 | snd-soc-twl6040-objs := twl6040.o |
27 | snd-soc-uda134x-objs := uda134x.o | 28 | snd-soc-uda134x-objs := uda134x.o |
28 | snd-soc-uda1380-objs := uda1380.o | 29 | snd-soc-uda1380-objs := uda1380.o |
30 | snd-soc-wl1273-objs := wl1273.o | ||
29 | snd-soc-wm8350-objs := wm8350.o | 31 | snd-soc-wm8350-objs := wm8350.o |
30 | snd-soc-wm8400-objs := wm8400.o | 32 | snd-soc-wm8400-objs := wm8400.o |
31 | snd-soc-wm8510-objs := wm8510.o | 33 | snd-soc-wm8510-objs := wm8510.o |
@@ -67,6 +69,7 @@ snd-soc-tpa6130a2-objs := tpa6130a2.o | |||
67 | snd-soc-wm2000-objs := wm2000.o | 69 | snd-soc-wm2000-objs := wm2000.o |
68 | snd-soc-wm9090-objs := wm9090.o | 70 | snd-soc-wm9090-objs := wm9090.o |
69 | 71 | ||
72 | obj-$(CONFIG_SND_SOC_88PM860X) += snd-soc-88pm860x.o | ||
70 | obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o | 73 | obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o |
71 | obj-$(CONFIG_SND_SOC_AD1836) += snd-soc-ad1836.o | 74 | obj-$(CONFIG_SND_SOC_AD1836) += snd-soc-ad1836.o |
72 | obj-$(CONFIG_SND_SOC_AD193X) += snd-soc-ad193x.o | 75 | obj-$(CONFIG_SND_SOC_AD193X) += snd-soc-ad193x.o |
@@ -96,6 +99,7 @@ obj-$(CONFIG_SND_SOC_TWL4030) += snd-soc-twl4030.o | |||
96 | obj-$(CONFIG_SND_SOC_TWL6040) += snd-soc-twl6040.o | 99 | obj-$(CONFIG_SND_SOC_TWL6040) += snd-soc-twl6040.o |
97 | obj-$(CONFIG_SND_SOC_UDA134X) += snd-soc-uda134x.o | 100 | obj-$(CONFIG_SND_SOC_UDA134X) += snd-soc-uda134x.o |
98 | obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o | 101 | obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o |
102 | obj-$(CONFIG_SND_SOC_WL1273) += snd-soc-wl1273.o | ||
99 | obj-$(CONFIG_SND_SOC_WM8350) += snd-soc-wm8350.o | 103 | obj-$(CONFIG_SND_SOC_WM8350) += snd-soc-wm8350.o |
100 | obj-$(CONFIG_SND_SOC_WM8400) += snd-soc-wm8400.o | 104 | obj-$(CONFIG_SND_SOC_WM8400) += snd-soc-wm8400.o |
101 | obj-$(CONFIG_SND_SOC_WM8510) += snd-soc-wm8510.o | 105 | obj-$(CONFIG_SND_SOC_WM8510) += snd-soc-wm8510.o |
diff --git a/sound/soc/codecs/cx20442.c b/sound/soc/codecs/cx20442.c index cf4323dbf9c4..e8d27c8f9ba3 100644 --- a/sound/soc/codecs/cx20442.c +++ b/sound/soc/codecs/cx20442.c | |||
@@ -318,7 +318,7 @@ EXPORT_SYMBOL_GPL(v253_ops); | |||
318 | */ | 318 | */ |
319 | 319 | ||
320 | static struct snd_soc_dai_driver cx20442_dai = { | 320 | static struct snd_soc_dai_driver cx20442_dai = { |
321 | .name = "cx20442-hifi", | 321 | .name = "cx20442-voice", |
322 | .playback = { | 322 | .playback = { |
323 | .stream_name = "Playback", | 323 | .stream_name = "Playback", |
324 | .channels_min = 1, | 324 | .channels_min = 1, |
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 43fd9c171742..c07465720cdb 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c | |||
@@ -12,11 +12,11 @@ | |||
12 | * | 12 | * |
13 | * Notes: | 13 | * Notes: |
14 | * The AIC3X is a driver for a low power stereo audio | 14 | * The AIC3X is a driver for a low power stereo audio |
15 | * codecs aic31, aic32, aic33. | 15 | * codecs aic31, aic32, aic33, aic3007. |
16 | * | 16 | * |
17 | * It supports full aic33 codec functionality. | 17 | * It supports full aic33 codec functionality. |
18 | * The compatibility with aic32, aic31 is as follows: | 18 | * The compatibility with aic32, aic31 and aic3007 is as follows: |
19 | * aic32 | aic31 | 19 | * aic32/aic3007 | aic31 |
20 | * --------------------------------------- | 20 | * --------------------------------------- |
21 | * MONO_LOUT -> N/A | MONO_LOUT -> N/A | 21 | * MONO_LOUT -> N/A | MONO_LOUT -> N/A |
22 | * | IN1L -> LINE1L | 22 | * | IN1L -> LINE1L |
@@ -70,6 +70,10 @@ struct aic3x_priv { | |||
70 | unsigned int sysclk; | 70 | unsigned int sysclk; |
71 | int master; | 71 | int master; |
72 | int gpio_reset; | 72 | int gpio_reset; |
73 | #define AIC3X_MODEL_3X 0 | ||
74 | #define AIC3X_MODEL_33 1 | ||
75 | #define AIC3X_MODEL_3007 2 | ||
76 | u16 model; | ||
73 | }; | 77 | }; |
74 | 78 | ||
75 | /* | 79 | /* |
@@ -361,6 +365,14 @@ static const struct snd_kcontrol_new aic3x_snd_controls[] = { | |||
361 | SOC_ENUM("ADC HPF Cut-off", aic3x_enum[ADC_HPF_ENUM]), | 365 | SOC_ENUM("ADC HPF Cut-off", aic3x_enum[ADC_HPF_ENUM]), |
362 | }; | 366 | }; |
363 | 367 | ||
368 | /* | ||
369 | * Class-D amplifier gain. From 0 to 18 dB in 6 dB steps | ||
370 | */ | ||
371 | static DECLARE_TLV_DB_SCALE(classd_amp_tlv, 0, 600, 0); | ||
372 | |||
373 | static const struct snd_kcontrol_new aic3x_classd_amp_gain_ctrl = | ||
374 | SOC_DOUBLE_TLV("Class-D Amplifier Gain", CLASSD_CTRL, 6, 4, 3, 0, classd_amp_tlv); | ||
375 | |||
364 | /* Left DAC Mux */ | 376 | /* Left DAC Mux */ |
365 | static const struct snd_kcontrol_new aic3x_left_dac_mux_controls = | 377 | static const struct snd_kcontrol_new aic3x_left_dac_mux_controls = |
366 | SOC_DAPM_ENUM("Route", aic3x_enum[LDAC_ENUM]); | 378 | SOC_DAPM_ENUM("Route", aic3x_enum[LDAC_ENUM]); |
@@ -589,6 +601,15 @@ static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = { | |||
589 | SND_SOC_DAPM_INPUT("LINE2R"), | 601 | SND_SOC_DAPM_INPUT("LINE2R"), |
590 | }; | 602 | }; |
591 | 603 | ||
604 | static const struct snd_soc_dapm_widget aic3007_dapm_widgets[] = { | ||
605 | /* Class-D outputs */ | ||
606 | SND_SOC_DAPM_PGA("Left Class-D Out", CLASSD_CTRL, 3, 0, NULL, 0), | ||
607 | SND_SOC_DAPM_PGA("Right Class-D Out", CLASSD_CTRL, 2, 0, NULL, 0), | ||
608 | |||
609 | SND_SOC_DAPM_OUTPUT("SPOP"), | ||
610 | SND_SOC_DAPM_OUTPUT("SPOM"), | ||
611 | }; | ||
612 | |||
592 | static const struct snd_soc_dapm_route intercon[] = { | 613 | static const struct snd_soc_dapm_route intercon[] = { |
593 | /* Left Output */ | 614 | /* Left Output */ |
594 | {"Left DAC Mux", "DAC_L1", "Left DAC"}, | 615 | {"Left DAC Mux", "DAC_L1", "Left DAC"}, |
@@ -759,14 +780,30 @@ static const struct snd_soc_dapm_route intercon[] = { | |||
759 | {"GPIO1 dmic modclk", NULL, "DMic Rate 32"}, | 780 | {"GPIO1 dmic modclk", NULL, "DMic Rate 32"}, |
760 | }; | 781 | }; |
761 | 782 | ||
783 | static const struct snd_soc_dapm_route intercon_3007[] = { | ||
784 | /* Class-D outputs */ | ||
785 | {"Left Class-D Out", NULL, "Left Line Out"}, | ||
786 | {"Right Class-D Out", NULL, "Left Line Out"}, | ||
787 | {"SPOP", NULL, "Left Class-D Out"}, | ||
788 | {"SPOM", NULL, "Right Class-D Out"}, | ||
789 | }; | ||
790 | |||
762 | static int aic3x_add_widgets(struct snd_soc_codec *codec) | 791 | static int aic3x_add_widgets(struct snd_soc_codec *codec) |
763 | { | 792 | { |
793 | struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec); | ||
794 | |||
764 | snd_soc_dapm_new_controls(codec, aic3x_dapm_widgets, | 795 | snd_soc_dapm_new_controls(codec, aic3x_dapm_widgets, |
765 | ARRAY_SIZE(aic3x_dapm_widgets)); | 796 | ARRAY_SIZE(aic3x_dapm_widgets)); |
766 | 797 | ||
767 | /* set up audio path interconnects */ | 798 | /* set up audio path interconnects */ |
768 | snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); | 799 | snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); |
769 | 800 | ||
801 | if (aic3x->model == AIC3X_MODEL_3007) { | ||
802 | snd_soc_dapm_new_controls(codec, aic3007_dapm_widgets, | ||
803 | ARRAY_SIZE(aic3007_dapm_widgets)); | ||
804 | snd_soc_dapm_add_routes(codec, intercon_3007, ARRAY_SIZE(intercon_3007)); | ||
805 | } | ||
806 | |||
770 | return 0; | 807 | return 0; |
771 | } | 808 | } |
772 | 809 | ||
@@ -1117,6 +1154,7 @@ static struct snd_soc_dai_driver aic3x_dai = { | |||
1117 | .rates = AIC3X_RATES, | 1154 | .rates = AIC3X_RATES, |
1118 | .formats = AIC3X_FORMATS,}, | 1155 | .formats = AIC3X_FORMATS,}, |
1119 | .ops = &aic3x_dai_ops, | 1156 | .ops = &aic3x_dai_ops, |
1157 | .symmetric_rates = 1, | ||
1120 | }; | 1158 | }; |
1121 | 1159 | ||
1122 | static int aic3x_suspend(struct snd_soc_codec *codec, pm_message_t state) | 1160 | static int aic3x_suspend(struct snd_soc_codec *codec, pm_message_t state) |
@@ -1150,6 +1188,7 @@ static int aic3x_resume(struct snd_soc_codec *codec) | |||
1150 | */ | 1188 | */ |
1151 | static int aic3x_init(struct snd_soc_codec *codec) | 1189 | static int aic3x_init(struct snd_soc_codec *codec) |
1152 | { | 1190 | { |
1191 | struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec); | ||
1153 | int reg; | 1192 | int reg; |
1154 | 1193 | ||
1155 | aic3x_write(codec, AIC3X_PAGE_SELECT, PAGE0_SELECT); | 1194 | aic3x_write(codec, AIC3X_PAGE_SELECT, PAGE0_SELECT); |
@@ -1218,6 +1257,17 @@ static int aic3x_init(struct snd_soc_codec *codec) | |||
1218 | aic3x_write(codec, LINE2L_2_MONOLOPM_VOL, DEFAULT_VOL); | 1257 | aic3x_write(codec, LINE2L_2_MONOLOPM_VOL, DEFAULT_VOL); |
1219 | aic3x_write(codec, LINE2R_2_MONOLOPM_VOL, DEFAULT_VOL); | 1258 | aic3x_write(codec, LINE2R_2_MONOLOPM_VOL, DEFAULT_VOL); |
1220 | 1259 | ||
1260 | if (aic3x->model == AIC3X_MODEL_3007) { | ||
1261 | /* Class-D speaker driver init; datasheet p. 46 */ | ||
1262 | aic3x_write(codec, AIC3X_PAGE_SELECT, 0x0D); | ||
1263 | aic3x_write(codec, 0xD, 0x0D); | ||
1264 | aic3x_write(codec, 0x8, 0x5C); | ||
1265 | aic3x_write(codec, 0x8, 0x5D); | ||
1266 | aic3x_write(codec, 0x8, 0x5C); | ||
1267 | aic3x_write(codec, AIC3X_PAGE_SELECT, 0x00); | ||
1268 | aic3x_write(codec, CLASSD_CTRL, 0); | ||
1269 | } | ||
1270 | |||
1221 | /* off, with power on */ | 1271 | /* off, with power on */ |
1222 | aic3x_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | 1272 | aic3x_set_bias_level(codec, SND_SOC_BIAS_STANDBY); |
1223 | 1273 | ||
@@ -1243,6 +1293,8 @@ static int aic3x_probe(struct snd_soc_codec *codec) | |||
1243 | 1293 | ||
1244 | snd_soc_add_controls(codec, aic3x_snd_controls, | 1294 | snd_soc_add_controls(codec, aic3x_snd_controls, |
1245 | ARRAY_SIZE(aic3x_snd_controls)); | 1295 | ARRAY_SIZE(aic3x_snd_controls)); |
1296 | if (aic3x->model == AIC3X_MODEL_3007) | ||
1297 | snd_soc_add_controls(codec, &aic3x_classd_amp_gain_ctrl, 1); | ||
1246 | 1298 | ||
1247 | aic3x_add_widgets(codec); | 1299 | aic3x_add_widgets(codec); |
1248 | 1300 | ||
@@ -1274,6 +1326,14 @@ static struct snd_soc_codec_driver soc_codec_dev_aic3x = { | |||
1274 | * 0x18, 0x19, 0x1A, 0x1B | 1326 | * 0x18, 0x19, 0x1A, 0x1B |
1275 | */ | 1327 | */ |
1276 | 1328 | ||
1329 | static const struct i2c_device_id aic3x_i2c_id[] = { | ||
1330 | [AIC3X_MODEL_3X] = { "tlv320aic3x", 0 }, | ||
1331 | [AIC3X_MODEL_33] = { "tlv320aic33", 0 }, | ||
1332 | [AIC3X_MODEL_3007] = { "tlv320aic3007", 0 }, | ||
1333 | { } | ||
1334 | }; | ||
1335 | MODULE_DEVICE_TABLE(i2c, aic3x_i2c_id); | ||
1336 | |||
1277 | /* | 1337 | /* |
1278 | * If the i2c layer weren't so broken, we could pass this kind of data | 1338 | * If the i2c layer weren't so broken, we could pass this kind of data |
1279 | * around | 1339 | * around |
@@ -1285,6 +1345,7 @@ static int aic3x_i2c_probe(struct i2c_client *i2c, | |||
1285 | struct aic3x_setup_data *setup = pdata->setup; | 1345 | struct aic3x_setup_data *setup = pdata->setup; |
1286 | struct aic3x_priv *aic3x; | 1346 | struct aic3x_priv *aic3x; |
1287 | int ret, i; | 1347 | int ret, i; |
1348 | const struct i2c_device_id *tbl; | ||
1288 | 1349 | ||
1289 | aic3x = kzalloc(sizeof(struct aic3x_priv), GFP_KERNEL); | 1350 | aic3x = kzalloc(sizeof(struct aic3x_priv), GFP_KERNEL); |
1290 | if (aic3x == NULL) { | 1351 | if (aic3x == NULL) { |
@@ -1305,6 +1366,12 @@ static int aic3x_i2c_probe(struct i2c_client *i2c, | |||
1305 | gpio_direction_output(aic3x->gpio_reset, 0); | 1366 | gpio_direction_output(aic3x->gpio_reset, 0); |
1306 | } | 1367 | } |
1307 | 1368 | ||
1369 | for (tbl = aic3x_i2c_id; tbl->name[0]; tbl++) { | ||
1370 | if (!strcmp(tbl->name, id->name)) | ||
1371 | break; | ||
1372 | } | ||
1373 | aic3x->model = tbl - aic3x_i2c_id; | ||
1374 | |||
1308 | for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++) | 1375 | for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++) |
1309 | aic3x->supplies[i].supply = aic3x_supply_names[i]; | 1376 | aic3x->supplies[i].supply = aic3x_supply_names[i]; |
1310 | 1377 | ||
@@ -1359,13 +1426,6 @@ static int aic3x_i2c_remove(struct i2c_client *client) | |||
1359 | return 0; | 1426 | return 0; |
1360 | } | 1427 | } |
1361 | 1428 | ||
1362 | static const struct i2c_device_id aic3x_i2c_id[] = { | ||
1363 | { "tlv320aic3x", 0 }, | ||
1364 | { "tlv320aic33", 0 }, | ||
1365 | { } | ||
1366 | }; | ||
1367 | MODULE_DEVICE_TABLE(i2c, aic3x_i2c_id); | ||
1368 | |||
1369 | /* machine i2c codec control layer */ | 1429 | /* machine i2c codec control layer */ |
1370 | static struct i2c_driver aic3x_i2c_driver = { | 1430 | static struct i2c_driver aic3x_i2c_driver = { |
1371 | .driver = { | 1431 | .driver = { |
diff --git a/sound/soc/codecs/tlv320aic3x.h b/sound/soc/codecs/tlv320aic3x.h index f6e3d9b42daf..98e44395b662 100644 --- a/sound/soc/codecs/tlv320aic3x.h +++ b/sound/soc/codecs/tlv320aic3x.h | |||
@@ -111,6 +111,8 @@ | |||
111 | #define DACL1_2_MONOLOPM_VOL 75 | 111 | #define DACL1_2_MONOLOPM_VOL 75 |
112 | #define DACR1_2_MONOLOPM_VOL 78 | 112 | #define DACR1_2_MONOLOPM_VOL 78 |
113 | #define MONOLOPM_CTRL 79 | 113 | #define MONOLOPM_CTRL 79 |
114 | /* Class-D speaker driver on tlv320aic3007 */ | ||
115 | #define CLASSD_CTRL 73 | ||
114 | /* Line Output Plus/Minus control registers */ | 116 | /* Line Output Plus/Minus control registers */ |
115 | #define LINE2L_2_LLOPM_VOL 80 | 117 | #define LINE2L_2_LLOPM_VOL 80 |
116 | #define LINE2L_2_RLOPM_VOL 87 | 118 | #define LINE2L_2_RLOPM_VOL 87 |
diff --git a/sound/soc/codecs/wl1273.c b/sound/soc/codecs/wl1273.c new file mode 100644 index 000000000000..0cd590970883 --- /dev/null +++ b/sound/soc/codecs/wl1273.c | |||
@@ -0,0 +1,525 @@ | |||
1 | /* | ||
2 | * ALSA SoC WL1273 codec driver | ||
3 | * | ||
4 | * Author: Matti Aaltonen, <matti.j.aaltonen@nokia.com> | ||
5 | * | ||
6 | * Copyright: (C) 2010 Nokia Corporation | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License | ||
10 | * version 2 as published by the Free Software Foundation. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, but | ||
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
15 | * General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
20 | * 02110-1301 USA | ||
21 | * | ||
22 | */ | ||
23 | |||
24 | #include <linux/mfd/wl1273-core.h> | ||
25 | #include <linux/slab.h> | ||
26 | #include <sound/pcm.h> | ||
27 | #include <sound/pcm_params.h> | ||
28 | #include <sound/soc-dai.h> | ||
29 | #include <sound/soc-dapm.h> | ||
30 | #include <sound/initval.h> | ||
31 | |||
32 | #include "wl1273.h" | ||
33 | |||
34 | enum wl1273_mode { WL1273_MODE_BT, WL1273_MODE_FM_RX, WL1273_MODE_FM_TX }; | ||
35 | |||
36 | /* codec private data */ | ||
37 | struct wl1273_priv { | ||
38 | enum wl1273_mode mode; | ||
39 | struct wl1273_core *core; | ||
40 | unsigned int channels; | ||
41 | }; | ||
42 | |||
43 | static int snd_wl1273_fm_set_i2s_mode(struct wl1273_core *core, | ||
44 | int rate, int width) | ||
45 | { | ||
46 | struct device *dev = &core->i2c_dev->dev; | ||
47 | int r = 0; | ||
48 | u16 mode; | ||
49 | |||
50 | dev_dbg(dev, "rate: %d\n", rate); | ||
51 | dev_dbg(dev, "width: %d\n", width); | ||
52 | |||
53 | mutex_lock(&core->lock); | ||
54 | |||
55 | mode = core->i2s_mode & ~WL1273_IS2_WIDTH & ~WL1273_IS2_RATE; | ||
56 | |||
57 | switch (rate) { | ||
58 | case 48000: | ||
59 | mode |= WL1273_IS2_RATE_48K; | ||
60 | break; | ||
61 | case 44100: | ||
62 | mode |= WL1273_IS2_RATE_44_1K; | ||
63 | break; | ||
64 | case 32000: | ||
65 | mode |= WL1273_IS2_RATE_32K; | ||
66 | break; | ||
67 | case 22050: | ||
68 | mode |= WL1273_IS2_RATE_22_05K; | ||
69 | break; | ||
70 | case 16000: | ||
71 | mode |= WL1273_IS2_RATE_16K; | ||
72 | break; | ||
73 | case 12000: | ||
74 | mode |= WL1273_IS2_RATE_12K; | ||
75 | break; | ||
76 | case 11025: | ||
77 | mode |= WL1273_IS2_RATE_11_025; | ||
78 | break; | ||
79 | case 8000: | ||
80 | mode |= WL1273_IS2_RATE_8K; | ||
81 | break; | ||
82 | default: | ||
83 | dev_err(dev, "Sampling rate: %d not supported\n", rate); | ||
84 | r = -EINVAL; | ||
85 | goto out; | ||
86 | } | ||
87 | |||
88 | switch (width) { | ||
89 | case 16: | ||
90 | mode |= WL1273_IS2_WIDTH_32; | ||
91 | break; | ||
92 | case 20: | ||
93 | mode |= WL1273_IS2_WIDTH_40; | ||
94 | break; | ||
95 | case 24: | ||
96 | mode |= WL1273_IS2_WIDTH_48; | ||
97 | break; | ||
98 | case 25: | ||
99 | mode |= WL1273_IS2_WIDTH_50; | ||
100 | break; | ||
101 | case 30: | ||
102 | mode |= WL1273_IS2_WIDTH_60; | ||
103 | break; | ||
104 | case 32: | ||
105 | mode |= WL1273_IS2_WIDTH_64; | ||
106 | break; | ||
107 | case 40: | ||
108 | mode |= WL1273_IS2_WIDTH_80; | ||
109 | break; | ||
110 | case 48: | ||
111 | mode |= WL1273_IS2_WIDTH_96; | ||
112 | break; | ||
113 | case 64: | ||
114 | mode |= WL1273_IS2_WIDTH_128; | ||
115 | break; | ||
116 | default: | ||
117 | dev_err(dev, "Data width: %d not supported\n", width); | ||
118 | r = -EINVAL; | ||
119 | goto out; | ||
120 | } | ||
121 | |||
122 | dev_dbg(dev, "WL1273_I2S_DEF_MODE: 0x%04x\n", WL1273_I2S_DEF_MODE); | ||
123 | dev_dbg(dev, "core->i2s_mode: 0x%04x\n", core->i2s_mode); | ||
124 | dev_dbg(dev, "mode: 0x%04x\n", mode); | ||
125 | |||
126 | if (core->i2s_mode != mode) { | ||
127 | r = wl1273_fm_write_cmd(core, WL1273_I2S_MODE_CONFIG_SET, mode); | ||
128 | if (r) | ||
129 | goto out; | ||
130 | |||
131 | core->i2s_mode = mode; | ||
132 | r = wl1273_fm_write_cmd(core, WL1273_AUDIO_ENABLE, | ||
133 | WL1273_AUDIO_ENABLE_I2S); | ||
134 | if (r) | ||
135 | goto out; | ||
136 | } | ||
137 | out: | ||
138 | mutex_unlock(&core->lock); | ||
139 | |||
140 | return r; | ||
141 | } | ||
142 | |||
143 | static int snd_wl1273_fm_set_channel_number(struct wl1273_core *core, | ||
144 | int channel_number) | ||
145 | { | ||
146 | struct i2c_client *client = core->i2c_dev; | ||
147 | struct device *dev = &client->dev; | ||
148 | int r = 0; | ||
149 | |||
150 | dev_dbg(dev, "%s\n", __func__); | ||
151 | |||
152 | mutex_lock(&core->lock); | ||
153 | |||
154 | if (core->channel_number == channel_number) | ||
155 | goto out; | ||
156 | |||
157 | if (channel_number == 1 && core->mode == WL1273_MODE_RX) | ||
158 | r = wl1273_fm_write_cmd(core, WL1273_MOST_MODE_SET, | ||
159 | WL1273_RX_MONO); | ||
160 | else if (channel_number == 1 && core->mode == WL1273_MODE_TX) | ||
161 | r = wl1273_fm_write_cmd(core, WL1273_MONO_SET, | ||
162 | WL1273_TX_MONO); | ||
163 | else if (channel_number == 2 && core->mode == WL1273_MODE_RX) | ||
164 | r = wl1273_fm_write_cmd(core, WL1273_MOST_MODE_SET, | ||
165 | WL1273_RX_STEREO); | ||
166 | else if (channel_number == 2 && core->mode == WL1273_MODE_TX) | ||
167 | r = wl1273_fm_write_cmd(core, WL1273_MONO_SET, | ||
168 | WL1273_TX_STEREO); | ||
169 | else | ||
170 | r = -EINVAL; | ||
171 | out: | ||
172 | mutex_unlock(&core->lock); | ||
173 | |||
174 | return r; | ||
175 | } | ||
176 | |||
177 | static int snd_wl1273_get_audio_route(struct snd_kcontrol *kcontrol, | ||
178 | struct snd_ctl_elem_value *ucontrol) | ||
179 | { | ||
180 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
181 | struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(codec); | ||
182 | |||
183 | ucontrol->value.integer.value[0] = wl1273->mode; | ||
184 | |||
185 | return 0; | ||
186 | } | ||
187 | |||
188 | static const char *wl1273_audio_route[] = { "Bt", "FmRx", "FmTx" }; | ||
189 | |||
190 | static int snd_wl1273_set_audio_route(struct snd_kcontrol *kcontrol, | ||
191 | struct snd_ctl_elem_value *ucontrol) | ||
192 | { | ||
193 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
194 | struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(codec); | ||
195 | |||
196 | /* Do not allow changes while stream is running */ | ||
197 | if (codec->active) | ||
198 | return -EPERM; | ||
199 | |||
200 | if (ucontrol->value.integer.value[0] < 0 || | ||
201 | ucontrol->value.integer.value[0] >= ARRAY_SIZE(wl1273_audio_route)) | ||
202 | return -EINVAL; | ||
203 | |||
204 | wl1273->mode = ucontrol->value.integer.value[0]; | ||
205 | |||
206 | return 1; | ||
207 | } | ||
208 | |||
209 | static const struct soc_enum wl1273_enum = | ||
210 | SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(wl1273_audio_route), wl1273_audio_route); | ||
211 | |||
212 | static int snd_wl1273_fm_audio_get(struct snd_kcontrol *kcontrol, | ||
213 | struct snd_ctl_elem_value *ucontrol) | ||
214 | { | ||
215 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
216 | struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(codec); | ||
217 | |||
218 | dev_dbg(codec->dev, "%s: enter.\n", __func__); | ||
219 | |||
220 | ucontrol->value.integer.value[0] = wl1273->core->audio_mode; | ||
221 | |||
222 | return 0; | ||
223 | } | ||
224 | |||
225 | static int snd_wl1273_fm_audio_put(struct snd_kcontrol *kcontrol, | ||
226 | struct snd_ctl_elem_value *ucontrol) | ||
227 | { | ||
228 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
229 | struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(codec); | ||
230 | int val, r = 0; | ||
231 | |||
232 | dev_dbg(codec->dev, "%s: enter.\n", __func__); | ||
233 | |||
234 | val = ucontrol->value.integer.value[0]; | ||
235 | if (wl1273->core->audio_mode == val) | ||
236 | return 0; | ||
237 | |||
238 | r = wl1273_fm_set_audio(wl1273->core, val); | ||
239 | if (r < 0) | ||
240 | return r; | ||
241 | |||
242 | return 1; | ||
243 | } | ||
244 | |||
245 | static const char *wl1273_audio_strings[] = { "Digital", "Analog" }; | ||
246 | |||
247 | static const struct soc_enum wl1273_audio_enum = | ||
248 | SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(wl1273_audio_strings), | ||
249 | wl1273_audio_strings); | ||
250 | |||
251 | static int snd_wl1273_fm_volume_get(struct snd_kcontrol *kcontrol, | ||
252 | struct snd_ctl_elem_value *ucontrol) | ||
253 | { | ||
254 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
255 | struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(codec); | ||
256 | |||
257 | dev_dbg(codec->dev, "%s: enter.\n", __func__); | ||
258 | |||
259 | ucontrol->value.integer.value[0] = wl1273->core->volume; | ||
260 | |||
261 | return 0; | ||
262 | } | ||
263 | |||
264 | static int snd_wl1273_fm_volume_put(struct snd_kcontrol *kcontrol, | ||
265 | struct snd_ctl_elem_value *ucontrol) | ||
266 | { | ||
267 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
268 | struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(codec); | ||
269 | int r; | ||
270 | |||
271 | dev_dbg(codec->dev, "%s: enter.\n", __func__); | ||
272 | |||
273 | r = wl1273_fm_set_volume(wl1273->core, | ||
274 | ucontrol->value.integer.value[0]); | ||
275 | if (r) | ||
276 | return r; | ||
277 | |||
278 | return 1; | ||
279 | } | ||
280 | |||
281 | static const struct snd_kcontrol_new wl1273_controls[] = { | ||
282 | SOC_ENUM_EXT("Codec Mode", wl1273_enum, | ||
283 | snd_wl1273_get_audio_route, snd_wl1273_set_audio_route), | ||
284 | SOC_ENUM_EXT("Audio Switch", wl1273_audio_enum, | ||
285 | snd_wl1273_fm_audio_get, snd_wl1273_fm_audio_put), | ||
286 | SOC_SINGLE_EXT("Volume", 0, 0, WL1273_MAX_VOLUME, 0, | ||
287 | snd_wl1273_fm_volume_get, snd_wl1273_fm_volume_put), | ||
288 | }; | ||
289 | |||
290 | static int wl1273_startup(struct snd_pcm_substream *substream, | ||
291 | struct snd_soc_dai *dai) | ||
292 | { | ||
293 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
294 | struct snd_soc_codec *codec = rtd->codec; | ||
295 | struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(codec); | ||
296 | |||
297 | switch (wl1273->mode) { | ||
298 | case WL1273_MODE_BT: | ||
299 | snd_pcm_hw_constraint_minmax(substream->runtime, | ||
300 | SNDRV_PCM_HW_PARAM_RATE, | ||
301 | 8000, 8000); | ||
302 | snd_pcm_hw_constraint_minmax(substream->runtime, | ||
303 | SNDRV_PCM_HW_PARAM_CHANNELS, 1, 1); | ||
304 | break; | ||
305 | case WL1273_MODE_FM_RX: | ||
306 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
307 | pr_err("Cannot play in RX mode.\n"); | ||
308 | return -EINVAL; | ||
309 | } | ||
310 | break; | ||
311 | case WL1273_MODE_FM_TX: | ||
312 | if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { | ||
313 | pr_err("Cannot capture in TX mode.\n"); | ||
314 | return -EINVAL; | ||
315 | } | ||
316 | break; | ||
317 | default: | ||
318 | return -EINVAL; | ||
319 | break; | ||
320 | } | ||
321 | |||
322 | return 0; | ||
323 | } | ||
324 | |||
325 | static int wl1273_hw_params(struct snd_pcm_substream *substream, | ||
326 | struct snd_pcm_hw_params *params, | ||
327 | struct snd_soc_dai *dai) | ||
328 | { | ||
329 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
330 | struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(rtd->codec); | ||
331 | struct wl1273_core *core = wl1273->core; | ||
332 | unsigned int rate, width, r; | ||
333 | |||
334 | if (params_format(params) != SNDRV_PCM_FORMAT_S16_LE) { | ||
335 | pr_err("Only SNDRV_PCM_FORMAT_S16_LE supported.\n"); | ||
336 | return -EINVAL; | ||
337 | } | ||
338 | |||
339 | rate = params_rate(params); | ||
340 | width = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min; | ||
341 | |||
342 | if (wl1273->mode == WL1273_MODE_BT) { | ||
343 | if (rate != 8000) { | ||
344 | pr_err("Rate %d not supported.\n", params_rate(params)); | ||
345 | return -EINVAL; | ||
346 | } | ||
347 | |||
348 | if (params_channels(params) != 1) { | ||
349 | pr_err("Only mono supported.\n"); | ||
350 | return -EINVAL; | ||
351 | } | ||
352 | |||
353 | return 0; | ||
354 | } | ||
355 | |||
356 | if (wl1273->mode == WL1273_MODE_FM_TX && | ||
357 | substream->stream == SNDRV_PCM_STREAM_CAPTURE) { | ||
358 | pr_err("Only playback supported with TX.\n"); | ||
359 | return -EINVAL; | ||
360 | } | ||
361 | |||
362 | if (wl1273->mode == WL1273_MODE_FM_RX && | ||
363 | substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
364 | pr_err("Only capture supported with RX.\n"); | ||
365 | return -EINVAL; | ||
366 | } | ||
367 | |||
368 | if (wl1273->mode != WL1273_MODE_FM_RX && | ||
369 | wl1273->mode != WL1273_MODE_FM_TX) { | ||
370 | pr_err("Unexpected mode: %d.\n", wl1273->mode); | ||
371 | return -EINVAL; | ||
372 | } | ||
373 | |||
374 | r = snd_wl1273_fm_set_i2s_mode(core, rate, width); | ||
375 | if (r) | ||
376 | return r; | ||
377 | |||
378 | wl1273->channels = params_channels(params); | ||
379 | r = snd_wl1273_fm_set_channel_number(core, wl1273->channels); | ||
380 | if (r) | ||
381 | return r; | ||
382 | |||
383 | return 0; | ||
384 | } | ||
385 | |||
386 | static struct snd_soc_dai_ops wl1273_dai_ops = { | ||
387 | .startup = wl1273_startup, | ||
388 | .hw_params = wl1273_hw_params, | ||
389 | }; | ||
390 | |||
391 | static struct snd_soc_dai_driver wl1273_dai = { | ||
392 | .name = "wl1273-fm", | ||
393 | .playback = { | ||
394 | .stream_name = "Playback", | ||
395 | .channels_min = 1, | ||
396 | .channels_max = 2, | ||
397 | .rates = SNDRV_PCM_RATE_8000_48000, | ||
398 | .formats = SNDRV_PCM_FMTBIT_S16_LE}, | ||
399 | .capture = { | ||
400 | .stream_name = "Capture", | ||
401 | .channels_min = 1, | ||
402 | .channels_max = 2, | ||
403 | .rates = SNDRV_PCM_RATE_8000_48000, | ||
404 | .formats = SNDRV_PCM_FMTBIT_S16_LE}, | ||
405 | .ops = &wl1273_dai_ops, | ||
406 | }; | ||
407 | |||
408 | /* Audio interface format for the soc_card driver */ | ||
409 | int wl1273_get_format(struct snd_soc_codec *codec, unsigned int *fmt) | ||
410 | { | ||
411 | struct wl1273_priv *wl1273; | ||
412 | |||
413 | if (codec == NULL || fmt == NULL) | ||
414 | return -EINVAL; | ||
415 | |||
416 | wl1273 = snd_soc_codec_get_drvdata(codec); | ||
417 | |||
418 | switch (wl1273->mode) { | ||
419 | case WL1273_MODE_FM_RX: | ||
420 | case WL1273_MODE_FM_TX: | ||
421 | *fmt = SND_SOC_DAIFMT_I2S | | ||
422 | SND_SOC_DAIFMT_NB_NF | | ||
423 | SND_SOC_DAIFMT_CBM_CFM; | ||
424 | |||
425 | break; | ||
426 | case WL1273_MODE_BT: | ||
427 | *fmt = SND_SOC_DAIFMT_DSP_A | | ||
428 | SND_SOC_DAIFMT_IB_NF | | ||
429 | SND_SOC_DAIFMT_CBM_CFM; | ||
430 | |||
431 | break; | ||
432 | default: | ||
433 | return -EINVAL; | ||
434 | } | ||
435 | |||
436 | return 0; | ||
437 | } | ||
438 | EXPORT_SYMBOL_GPL(wl1273_get_format); | ||
439 | |||
440 | static int wl1273_probe(struct snd_soc_codec *codec) | ||
441 | { | ||
442 | struct wl1273_core **core = codec->dev->platform_data; | ||
443 | struct wl1273_priv *wl1273; | ||
444 | int r; | ||
445 | |||
446 | dev_dbg(codec->dev, "%s.\n", __func__); | ||
447 | |||
448 | if (!core) { | ||
449 | dev_err(codec->dev, "Platform data is missing.\n"); | ||
450 | return -EINVAL; | ||
451 | } | ||
452 | |||
453 | wl1273 = kzalloc(sizeof(struct wl1273_priv), GFP_KERNEL); | ||
454 | if (wl1273 == NULL) { | ||
455 | dev_err(codec->dev, "Cannot allocate memory.\n"); | ||
456 | return -ENOMEM; | ||
457 | } | ||
458 | |||
459 | wl1273->mode = WL1273_MODE_BT; | ||
460 | wl1273->core = *core; | ||
461 | |||
462 | snd_soc_codec_set_drvdata(codec, wl1273); | ||
463 | mutex_init(&codec->mutex); | ||
464 | |||
465 | r = snd_soc_add_controls(codec, wl1273_controls, | ||
466 | ARRAY_SIZE(wl1273_controls)); | ||
467 | if (r) | ||
468 | kfree(wl1273); | ||
469 | |||
470 | return r; | ||
471 | } | ||
472 | |||
473 | static int wl1273_remove(struct snd_soc_codec *codec) | ||
474 | { | ||
475 | struct wl1273_priv *wl1273 = snd_soc_codec_get_drvdata(codec); | ||
476 | |||
477 | dev_dbg(codec->dev, "%s\n", __func__); | ||
478 | kfree(wl1273); | ||
479 | |||
480 | return 0; | ||
481 | } | ||
482 | |||
483 | static struct snd_soc_codec_driver soc_codec_dev_wl1273 = { | ||
484 | .probe = wl1273_probe, | ||
485 | .remove = wl1273_remove, | ||
486 | }; | ||
487 | |||
488 | static int __devinit wl1273_platform_probe(struct platform_device *pdev) | ||
489 | { | ||
490 | return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wl1273, | ||
491 | &wl1273_dai, 1); | ||
492 | } | ||
493 | |||
494 | static int __devexit wl1273_platform_remove(struct platform_device *pdev) | ||
495 | { | ||
496 | snd_soc_unregister_codec(&pdev->dev); | ||
497 | return 0; | ||
498 | } | ||
499 | |||
500 | MODULE_ALIAS("platform:wl1273-codec"); | ||
501 | |||
502 | static struct platform_driver wl1273_platform_driver = { | ||
503 | .driver = { | ||
504 | .name = "wl1273-codec", | ||
505 | .owner = THIS_MODULE, | ||
506 | }, | ||
507 | .probe = wl1273_platform_probe, | ||
508 | .remove = __devexit_p(wl1273_platform_remove), | ||
509 | }; | ||
510 | |||
511 | static int __init wl1273_init(void) | ||
512 | { | ||
513 | return platform_driver_register(&wl1273_platform_driver); | ||
514 | } | ||
515 | module_init(wl1273_init); | ||
516 | |||
517 | static void __exit wl1273_exit(void) | ||
518 | { | ||
519 | platform_driver_unregister(&wl1273_platform_driver); | ||
520 | } | ||
521 | module_exit(wl1273_exit); | ||
522 | |||
523 | MODULE_AUTHOR("Matti Aaltonen <matti.j.aaltonen@nokia.com>"); | ||
524 | MODULE_DESCRIPTION("ASoC WL1273 codec driver"); | ||
525 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/codecs/wl1273.h b/sound/soc/codecs/wl1273.h new file mode 100644 index 000000000000..14ed027fdcfc --- /dev/null +++ b/sound/soc/codecs/wl1273.h | |||
@@ -0,0 +1,101 @@ | |||
1 | /* | ||
2 | * sound/soc/codec/wl1273.h | ||
3 | * | ||
4 | * ALSA SoC WL1273 codec driver | ||
5 | * | ||
6 | * Copyright (C) Nokia Corporation | ||
7 | * Author: Matti Aaltonen <matti.j.aaltonen@nokia.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License | ||
11 | * version 2 as published by the Free Software Foundation. | ||
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 | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
21 | * 02110-1301 USA | ||
22 | * | ||
23 | */ | ||
24 | |||
25 | #ifndef __WL1273_CODEC_H__ | ||
26 | #define __WL1273_CODEC_H__ | ||
27 | |||
28 | /* I2S protocol, left channel first, data width 16 bits */ | ||
29 | #define WL1273_PCM_DEF_MODE 0x00 | ||
30 | |||
31 | /* Rx */ | ||
32 | #define WL1273_AUDIO_ENABLE_I2S (1 << 0) | ||
33 | #define WL1273_AUDIO_ENABLE_ANALOG (1 << 1) | ||
34 | |||
35 | /* Tx */ | ||
36 | #define WL1273_AUDIO_IO_SET_ANALOG 0 | ||
37 | #define WL1273_AUDIO_IO_SET_I2S 1 | ||
38 | |||
39 | #define WL1273_POWER_SET_OFF 0 | ||
40 | #define WL1273_POWER_SET_FM (1 << 0) | ||
41 | #define WL1273_POWER_SET_RDS (1 << 1) | ||
42 | #define WL1273_POWER_SET_RETENTION (1 << 4) | ||
43 | |||
44 | #define WL1273_PUPD_SET_OFF 0x00 | ||
45 | #define WL1273_PUPD_SET_ON 0x01 | ||
46 | #define WL1273_PUPD_SET_RETENTION 0x10 | ||
47 | |||
48 | /* I2S mode */ | ||
49 | #define WL1273_IS2_WIDTH_32 0x0 | ||
50 | #define WL1273_IS2_WIDTH_40 0x1 | ||
51 | #define WL1273_IS2_WIDTH_22_23 0x2 | ||
52 | #define WL1273_IS2_WIDTH_23_22 0x3 | ||
53 | #define WL1273_IS2_WIDTH_48 0x4 | ||
54 | #define WL1273_IS2_WIDTH_50 0x5 | ||
55 | #define WL1273_IS2_WIDTH_60 0x6 | ||
56 | #define WL1273_IS2_WIDTH_64 0x7 | ||
57 | #define WL1273_IS2_WIDTH_80 0x8 | ||
58 | #define WL1273_IS2_WIDTH_96 0x9 | ||
59 | #define WL1273_IS2_WIDTH_128 0xa | ||
60 | #define WL1273_IS2_WIDTH 0xf | ||
61 | |||
62 | #define WL1273_IS2_FORMAT_STD (0x0 << 4) | ||
63 | #define WL1273_IS2_FORMAT_LEFT (0x1 << 4) | ||
64 | #define WL1273_IS2_FORMAT_RIGHT (0x2 << 4) | ||
65 | #define WL1273_IS2_FORMAT_USER (0x3 << 4) | ||
66 | |||
67 | #define WL1273_IS2_MASTER (0x0 << 6) | ||
68 | #define WL1273_IS2_SLAVEW (0x1 << 6) | ||
69 | |||
70 | #define WL1273_IS2_TRI_AFTER_SENDING (0x0 << 7) | ||
71 | #define WL1273_IS2_TRI_ALWAYS_ACTIVE (0x1 << 7) | ||
72 | |||
73 | #define WL1273_IS2_SDOWS_RR (0x0 << 8) | ||
74 | #define WL1273_IS2_SDOWS_RF (0x1 << 8) | ||
75 | #define WL1273_IS2_SDOWS_FR (0x2 << 8) | ||
76 | #define WL1273_IS2_SDOWS_FF (0x3 << 8) | ||
77 | |||
78 | #define WL1273_IS2_TRI_OPT (0x0 << 10) | ||
79 | #define WL1273_IS2_TRI_ALWAYS (0x1 << 10) | ||
80 | |||
81 | #define WL1273_IS2_RATE_48K (0x0 << 12) | ||
82 | #define WL1273_IS2_RATE_44_1K (0x1 << 12) | ||
83 | #define WL1273_IS2_RATE_32K (0x2 << 12) | ||
84 | #define WL1273_IS2_RATE_22_05K (0x4 << 12) | ||
85 | #define WL1273_IS2_RATE_16K (0x5 << 12) | ||
86 | #define WL1273_IS2_RATE_12K (0x8 << 12) | ||
87 | #define WL1273_IS2_RATE_11_025 (0x9 << 12) | ||
88 | #define WL1273_IS2_RATE_8K (0xa << 12) | ||
89 | #define WL1273_IS2_RATE (0xf << 12) | ||
90 | |||
91 | #define WL1273_I2S_DEF_MODE (WL1273_IS2_WIDTH_32 | \ | ||
92 | WL1273_IS2_FORMAT_STD | \ | ||
93 | WL1273_IS2_MASTER | \ | ||
94 | WL1273_IS2_TRI_AFTER_SENDING | \ | ||
95 | WL1273_IS2_SDOWS_RR | \ | ||
96 | WL1273_IS2_TRI_OPT | \ | ||
97 | WL1273_IS2_RATE_48K) | ||
98 | |||
99 | int wl1273_get_format(struct snd_soc_codec *codec, unsigned int *fmt); | ||
100 | |||
101 | #endif /* End of __WL1273_CODEC_H__ */ | ||
diff --git a/sound/soc/codecs/wm8741.c b/sound/soc/codecs/wm8741.c index 782fe539662b..fdd24da89a1e 100644 --- a/sound/soc/codecs/wm8741.c +++ b/sound/soc/codecs/wm8741.c | |||
@@ -311,7 +311,7 @@ static struct snd_soc_dai_ops wm8741_dai_ops = { | |||
311 | }; | 311 | }; |
312 | 312 | ||
313 | static struct snd_soc_dai_driver wm8741_dai = { | 313 | static struct snd_soc_dai_driver wm8741_dai = { |
314 | .name = "WM8741", | 314 | .name = "wm8741", |
315 | .playback = { | 315 | .playback = { |
316 | .stream_name = "Playback", | 316 | .stream_name = "Playback", |
317 | .channels_min = 2, /* Mono modes not yet supported */ | 317 | .channels_min = 2, /* Mono modes not yet supported */ |
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 76a066e908ed..e03072cade7b 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c | |||
@@ -3316,20 +3316,24 @@ static int wm8994_hw_params(struct snd_pcm_substream *substream, | |||
3316 | bclk_reg = WM8994_AIF1_BCLK; | 3316 | bclk_reg = WM8994_AIF1_BCLK; |
3317 | rate_reg = WM8994_AIF1_RATE; | 3317 | rate_reg = WM8994_AIF1_RATE; |
3318 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK || | 3318 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK || |
3319 | wm8994->lrclk_shared[0]) | 3319 | wm8994->lrclk_shared[0]) { |
3320 | lrclk_reg = WM8994_AIF1DAC_LRCLK; | 3320 | lrclk_reg = WM8994_AIF1DAC_LRCLK; |
3321 | else | 3321 | } else { |
3322 | lrclk_reg = WM8994_AIF1ADC_LRCLK; | 3322 | lrclk_reg = WM8994_AIF1ADC_LRCLK; |
3323 | dev_dbg(codec->dev, "AIF1 using split LRCLK\n"); | ||
3324 | } | ||
3323 | break; | 3325 | break; |
3324 | case 2: | 3326 | case 2: |
3325 | aif1_reg = WM8994_AIF2_CONTROL_1; | 3327 | aif1_reg = WM8994_AIF2_CONTROL_1; |
3326 | bclk_reg = WM8994_AIF2_BCLK; | 3328 | bclk_reg = WM8994_AIF2_BCLK; |
3327 | rate_reg = WM8994_AIF2_RATE; | 3329 | rate_reg = WM8994_AIF2_RATE; |
3328 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK || | 3330 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK || |
3329 | wm8994->lrclk_shared[1]) | 3331 | wm8994->lrclk_shared[1]) { |
3330 | lrclk_reg = WM8994_AIF2DAC_LRCLK; | 3332 | lrclk_reg = WM8994_AIF2DAC_LRCLK; |
3331 | else | 3333 | } else { |
3332 | lrclk_reg = WM8994_AIF2ADC_LRCLK; | 3334 | lrclk_reg = WM8994_AIF2ADC_LRCLK; |
3335 | dev_dbg(codec->dev, "AIF2 using split LRCLK\n"); | ||
3336 | } | ||
3333 | break; | 3337 | break; |
3334 | default: | 3338 | default: |
3335 | return -EINVAL; | 3339 | return -EINVAL; |
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 981868700388..d754d34d68a6 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig | |||
@@ -1,24 +1,36 @@ | |||
1 | config SND_MPC52xx_DMA | 1 | config SND_MPC52xx_DMA |
2 | tristate | 2 | tristate |
3 | 3 | ||
4 | # ASoC platform support for the Freescale MPC8610 SOC. This compiles drivers | 4 | # ASoC platform support for the Freescale PowerPC SOCs that have an SSI and |
5 | # for the SSI and the Elo DMA controller. You will still need to select | 5 | # an Elo DMA controller, such as the MPC8610 and P1022. You will still need to |
6 | # a platform driver and a codec driver. | 6 | # select a platform driver and a codec driver. |
7 | config SND_SOC_MPC8610 | 7 | config SND_SOC_POWERPC_SSI |
8 | tristate | 8 | tristate |
9 | depends on MPC8610 | 9 | depends on FSL_SOC |
10 | 10 | ||
11 | config SND_SOC_MPC8610_HPCD | 11 | config SND_SOC_MPC8610_HPCD |
12 | tristate "ALSA SoC support for the Freescale MPC8610 HPCD board" | 12 | tristate "ALSA SoC support for the Freescale MPC8610 HPCD board" |
13 | # I2C is necessary for the CS4270 driver | 13 | # I2C is necessary for the CS4270 driver |
14 | depends on MPC8610_HPCD && I2C | 14 | depends on MPC8610_HPCD && I2C |
15 | select SND_SOC_MPC8610 | 15 | select SND_SOC_POWERPC_SSI |
16 | select SND_SOC_CS4270 | 16 | select SND_SOC_CS4270 |
17 | select SND_SOC_CS4270_VD33_ERRATA | 17 | select SND_SOC_CS4270_VD33_ERRATA |
18 | default y if MPC8610_HPCD | 18 | default y if MPC8610_HPCD |
19 | help | 19 | help |
20 | Say Y if you want to enable audio on the Freescale MPC8610 HPCD. | 20 | Say Y if you want to enable audio on the Freescale MPC8610 HPCD. |
21 | 21 | ||
22 | config SND_SOC_P1022_DS | ||
23 | tristate "ALSA SoC support for the Freescale P1022 DS board" | ||
24 | # I2C is necessary for the WM8776 driver | ||
25 | depends on P1022_DS && I2C | ||
26 | select SND_SOC_POWERPC_SSI | ||
27 | select SND_SOC_WM8776 | ||
28 | default y if P1022_DS | ||
29 | help | ||
30 | Say Y if you want to enable audio on the Freescale P1022 DS board. | ||
31 | This will also include the Wolfson Microelectronics WM8776 codec | ||
32 | driver. | ||
33 | |||
22 | config SND_SOC_MPC5200_I2S | 34 | config SND_SOC_MPC5200_I2S |
23 | tristate "Freescale MPC5200 PSC in I2S mode driver" | 35 | tristate "Freescale MPC5200 PSC in I2S mode driver" |
24 | depends on PPC_MPC52xx && PPC_BESTCOMM | 36 | depends on PPC_MPC52xx && PPC_BESTCOMM |
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index 7e472a53fcd3..b4a38c0ac58c 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile | |||
@@ -2,10 +2,14 @@ | |||
2 | snd-soc-mpc8610-hpcd-objs := mpc8610_hpcd.o | 2 | snd-soc-mpc8610-hpcd-objs := mpc8610_hpcd.o |
3 | obj-$(CONFIG_SND_SOC_MPC8610_HPCD) += snd-soc-mpc8610-hpcd.o | 3 | obj-$(CONFIG_SND_SOC_MPC8610_HPCD) += snd-soc-mpc8610-hpcd.o |
4 | 4 | ||
5 | # MPC8610 Platform Support | 5 | # P1022 DS Machine Support |
6 | snd-soc-p1022-ds-objs := p1022_ds.o | ||
7 | obj-$(CONFIG_SND_SOC_P1022_DS) += snd-soc-p1022-ds.o | ||
8 | |||
9 | # Freescale PowerPC SSI/DMA Platform Support | ||
6 | snd-soc-fsl-ssi-objs := fsl_ssi.o | 10 | snd-soc-fsl-ssi-objs := fsl_ssi.o |
7 | snd-soc-fsl-dma-objs := fsl_dma.o | 11 | snd-soc-fsl-dma-objs := fsl_dma.o |
8 | obj-$(CONFIG_SND_SOC_MPC8610) += snd-soc-fsl-ssi.o snd-soc-fsl-dma.o | 12 | obj-$(CONFIG_SND_SOC_POWERPC_SSI) += snd-soc-fsl-ssi.o snd-soc-fsl-dma.o |
9 | 13 | ||
10 | # MPC5200 Platform Support | 14 | # MPC5200 Platform Support |
11 | obj-$(CONFIG_SND_MPC52xx_DMA) += mpc5200_dma.o | 15 | obj-$(CONFIG_SND_MPC52xx_DMA) += mpc5200_dma.o |
diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c index 57774cb91ae3..4cf98c03af22 100644 --- a/sound/soc/fsl/fsl_dma.c +++ b/sound/soc/fsl/fsl_dma.c | |||
@@ -23,6 +23,7 @@ | |||
23 | #include <linux/gfp.h> | 23 | #include <linux/gfp.h> |
24 | #include <linux/of_platform.h> | 24 | #include <linux/of_platform.h> |
25 | #include <linux/list.h> | 25 | #include <linux/list.h> |
26 | #include <linux/slab.h> | ||
26 | 27 | ||
27 | #include <sound/core.h> | 28 | #include <sound/core.h> |
28 | #include <sound/pcm.h> | 29 | #include <sound/pcm.h> |
@@ -60,6 +61,7 @@ struct dma_object { | |||
60 | struct snd_soc_platform_driver dai; | 61 | struct snd_soc_platform_driver dai; |
61 | dma_addr_t ssi_stx_phys; | 62 | dma_addr_t ssi_stx_phys; |
62 | dma_addr_t ssi_srx_phys; | 63 | dma_addr_t ssi_srx_phys; |
64 | unsigned int ssi_fifo_depth; | ||
63 | struct ccsr_dma_channel __iomem *channel; | 65 | struct ccsr_dma_channel __iomem *channel; |
64 | unsigned int irq; | 66 | unsigned int irq; |
65 | bool assigned; | 67 | bool assigned; |
@@ -99,6 +101,7 @@ struct fsl_dma_private { | |||
99 | unsigned int irq; | 101 | unsigned int irq; |
100 | struct snd_pcm_substream *substream; | 102 | struct snd_pcm_substream *substream; |
101 | dma_addr_t ssi_sxx_phys; | 103 | dma_addr_t ssi_sxx_phys; |
104 | unsigned int ssi_fifo_depth; | ||
102 | dma_addr_t ld_buf_phys; | 105 | dma_addr_t ld_buf_phys; |
103 | unsigned int current_link; | 106 | unsigned int current_link; |
104 | dma_addr_t dma_buf_phys; | 107 | dma_addr_t dma_buf_phys; |
@@ -303,21 +306,29 @@ static int fsl_dma_new(struct snd_card *card, struct snd_soc_dai *dai, | |||
303 | if (!card->dev->coherent_dma_mask) | 306 | if (!card->dev->coherent_dma_mask) |
304 | card->dev->coherent_dma_mask = fsl_dma_dmamask; | 307 | card->dev->coherent_dma_mask = fsl_dma_dmamask; |
305 | 308 | ||
306 | ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, card->dev, | 309 | /* Some codecs have separate DAIs for playback and capture, so we |
307 | fsl_dma_hardware.buffer_bytes_max, | 310 | * should allocate a DMA buffer only for the streams that are valid. |
308 | &pcm->streams[0].substream->dma_buffer); | 311 | */ |
309 | if (ret) { | 312 | |
310 | dev_err(card->dev, "can't allocate playback dma buffer\n"); | 313 | if (dai->driver->playback.channels_min) { |
311 | return ret; | 314 | ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, card->dev, |
315 | fsl_dma_hardware.buffer_bytes_max, | ||
316 | &pcm->streams[0].substream->dma_buffer); | ||
317 | if (ret) { | ||
318 | dev_err(card->dev, "can't alloc playback dma buffer\n"); | ||
319 | return ret; | ||
320 | } | ||
312 | } | 321 | } |
313 | 322 | ||
314 | ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, card->dev, | 323 | if (dai->driver->capture.channels_min) { |
315 | fsl_dma_hardware.buffer_bytes_max, | 324 | ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, card->dev, |
316 | &pcm->streams[1].substream->dma_buffer); | 325 | fsl_dma_hardware.buffer_bytes_max, |
317 | if (ret) { | 326 | &pcm->streams[1].substream->dma_buffer); |
318 | snd_dma_free_pages(&pcm->streams[0].substream->dma_buffer); | 327 | if (ret) { |
319 | dev_err(card->dev, "can't allocate capture dma buffer\n"); | 328 | snd_dma_free_pages(&pcm->streams[0].substream->dma_buffer); |
320 | return ret; | 329 | dev_err(card->dev, "can't alloc capture dma buffer\n"); |
330 | return ret; | ||
331 | } | ||
321 | } | 332 | } |
322 | 333 | ||
323 | return 0; | 334 | return 0; |
@@ -431,6 +442,7 @@ static int fsl_dma_open(struct snd_pcm_substream *substream) | |||
431 | else | 442 | else |
432 | dma_private->ssi_sxx_phys = dma->ssi_srx_phys; | 443 | dma_private->ssi_sxx_phys = dma->ssi_srx_phys; |
433 | 444 | ||
445 | dma_private->ssi_fifo_depth = dma->ssi_fifo_depth; | ||
434 | dma_private->dma_channel = dma->channel; | 446 | dma_private->dma_channel = dma->channel; |
435 | dma_private->irq = dma->irq; | 447 | dma_private->irq = dma->irq; |
436 | dma_private->substream = substream; | 448 | dma_private->substream = substream; |
@@ -544,11 +556,11 @@ static int fsl_dma_hw_params(struct snd_pcm_substream *substream, | |||
544 | struct device *dev = rtd->platform->dev; | 556 | struct device *dev = rtd->platform->dev; |
545 | 557 | ||
546 | /* Number of bits per sample */ | 558 | /* Number of bits per sample */ |
547 | unsigned int sample_size = | 559 | unsigned int sample_bits = |
548 | snd_pcm_format_physical_width(params_format(hw_params)); | 560 | snd_pcm_format_physical_width(params_format(hw_params)); |
549 | 561 | ||
550 | /* Number of bytes per frame */ | 562 | /* Number of bytes per frame */ |
551 | unsigned int frame_size = 2 * (sample_size / 8); | 563 | unsigned int sample_bytes = sample_bits / 8; |
552 | 564 | ||
553 | /* Bus address of SSI STX register */ | 565 | /* Bus address of SSI STX register */ |
554 | dma_addr_t ssi_sxx_phys = dma_private->ssi_sxx_phys; | 566 | dma_addr_t ssi_sxx_phys = dma_private->ssi_sxx_phys; |
@@ -588,7 +600,7 @@ static int fsl_dma_hw_params(struct snd_pcm_substream *substream, | |||
588 | * that offset here. While we're at it, also tell the DMA controller | 600 | * that offset here. While we're at it, also tell the DMA controller |
589 | * how much data to transfer per sample. | 601 | * how much data to transfer per sample. |
590 | */ | 602 | */ |
591 | switch (sample_size) { | 603 | switch (sample_bits) { |
592 | case 8: | 604 | case 8: |
593 | mr |= CCSR_DMA_MR_DAHTS_1 | CCSR_DMA_MR_SAHTS_1; | 605 | mr |= CCSR_DMA_MR_DAHTS_1 | CCSR_DMA_MR_SAHTS_1; |
594 | ssi_sxx_phys += 3; | 606 | ssi_sxx_phys += 3; |
@@ -602,22 +614,42 @@ static int fsl_dma_hw_params(struct snd_pcm_substream *substream, | |||
602 | break; | 614 | break; |
603 | default: | 615 | default: |
604 | /* We should never get here */ | 616 | /* We should never get here */ |
605 | dev_err(dev, "unsupported sample size %u\n", sample_size); | 617 | dev_err(dev, "unsupported sample size %u\n", sample_bits); |
606 | return -EINVAL; | 618 | return -EINVAL; |
607 | } | 619 | } |
608 | 620 | ||
609 | /* | 621 | /* |
610 | * BWC should always be a multiple of the frame size. BWC determines | 622 | * BWC determines how many bytes are sent/received before the DMA |
611 | * how many bytes are sent/received before the DMA controller checks the | 623 | * controller checks the SSI to see if it needs to stop. BWC should |
612 | * SSI to see if it needs to stop. For playback, the transmit FIFO can | 624 | * always be a multiple of the frame size, so that we always transmit |
613 | * hold three frames, so we want to send two frames at a time. For | 625 | * whole frames. Each frame occupies two slots in the FIFO. The |
614 | * capture, the receive FIFO is triggered when it contains one frame, so | 626 | * parameter for CCSR_DMA_MR_BWC() is rounded down the next power of two |
615 | * we want to receive one frame at a time. | 627 | * (MR[BWC] can only represent even powers of two). |
628 | * | ||
629 | * To simplify the process, we set BWC to the largest value that is | ||
630 | * less than or equal to the FIFO watermark. For playback, this ensures | ||
631 | * that we transfer the maximum amount without overrunning the FIFO. | ||
632 | * For capture, this ensures that we transfer the maximum amount without | ||
633 | * underrunning the FIFO. | ||
634 | * | ||
635 | * f = SSI FIFO depth | ||
636 | * w = SSI watermark value (which equals f - 2) | ||
637 | * b = DMA bandwidth count (in bytes) | ||
638 | * s = sample size (in bytes, which equals frame_size * 2) | ||
639 | * | ||
640 | * For playback, we never transmit more than the transmit FIFO | ||
641 | * watermark, otherwise we might write more data than the FIFO can hold. | ||
642 | * The watermark is equal to the FIFO depth minus two. | ||
643 | * | ||
644 | * For capture, two equations must hold: | ||
645 | * w > f - (b / s) | ||
646 | * w >= b / s | ||
647 | * | ||
648 | * So, b > 2 * s, but b must also be <= s * w. To simplify, we set | ||
649 | * b = s * w, which is equal to | ||
650 | * (dma_private->ssi_fifo_depth - 2) * sample_bytes. | ||
616 | */ | 651 | */ |
617 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 652 | mr |= CCSR_DMA_MR_BWC((dma_private->ssi_fifo_depth - 2) * sample_bytes); |
618 | mr |= CCSR_DMA_MR_BWC(2 * frame_size); | ||
619 | else | ||
620 | mr |= CCSR_DMA_MR_BWC(frame_size); | ||
621 | 653 | ||
622 | out_be32(&dma_channel->mr, mr); | 654 | out_be32(&dma_channel->mr, mr); |
623 | 655 | ||
@@ -864,32 +896,35 @@ static struct snd_pcm_ops fsl_dma_ops = { | |||
864 | .pointer = fsl_dma_pointer, | 896 | .pointer = fsl_dma_pointer, |
865 | }; | 897 | }; |
866 | 898 | ||
867 | static int __devinit fsl_soc_dma_probe(struct of_device *of_dev, | 899 | static int __devinit fsl_soc_dma_probe(struct platform_device *pdev, |
868 | const struct of_device_id *match) | 900 | const struct of_device_id *match) |
869 | { | 901 | { |
870 | struct dma_object *dma; | 902 | struct dma_object *dma; |
871 | struct device_node *np = of_dev->dev.of_node; | 903 | struct device_node *np = pdev->dev.of_node; |
872 | struct device_node *ssi_np; | 904 | struct device_node *ssi_np; |
873 | struct resource res; | 905 | struct resource res; |
906 | const uint32_t *iprop; | ||
874 | int ret; | 907 | int ret; |
875 | 908 | ||
876 | /* Find the SSI node that points to us. */ | 909 | /* Find the SSI node that points to us. */ |
877 | ssi_np = find_ssi_node(np); | 910 | ssi_np = find_ssi_node(np); |
878 | if (!ssi_np) { | 911 | if (!ssi_np) { |
879 | dev_err(&of_dev->dev, "cannot find parent SSI node\n"); | 912 | dev_err(&pdev->dev, "cannot find parent SSI node\n"); |
880 | return -ENODEV; | 913 | return -ENODEV; |
881 | } | 914 | } |
882 | 915 | ||
883 | ret = of_address_to_resource(ssi_np, 0, &res); | 916 | ret = of_address_to_resource(ssi_np, 0, &res); |
884 | of_node_put(ssi_np); | ||
885 | if (ret) { | 917 | if (ret) { |
886 | dev_err(&of_dev->dev, "could not determine device resources\n"); | 918 | dev_err(&pdev->dev, "could not determine resources for %s\n", |
919 | ssi_np->full_name); | ||
920 | of_node_put(ssi_np); | ||
887 | return ret; | 921 | return ret; |
888 | } | 922 | } |
889 | 923 | ||
890 | dma = kzalloc(sizeof(*dma) + strlen(np->full_name), GFP_KERNEL); | 924 | dma = kzalloc(sizeof(*dma) + strlen(np->full_name), GFP_KERNEL); |
891 | if (!dma) { | 925 | if (!dma) { |
892 | dev_err(&of_dev->dev, "could not allocate dma object\n"); | 926 | dev_err(&pdev->dev, "could not allocate dma object\n"); |
927 | of_node_put(ssi_np); | ||
893 | return -ENOMEM; | 928 | return -ENOMEM; |
894 | } | 929 | } |
895 | 930 | ||
@@ -902,9 +937,18 @@ static int __devinit fsl_soc_dma_probe(struct of_device *of_dev, | |||
902 | dma->ssi_stx_phys = res.start + offsetof(struct ccsr_ssi, stx0); | 937 | dma->ssi_stx_phys = res.start + offsetof(struct ccsr_ssi, stx0); |
903 | dma->ssi_srx_phys = res.start + offsetof(struct ccsr_ssi, srx0); | 938 | dma->ssi_srx_phys = res.start + offsetof(struct ccsr_ssi, srx0); |
904 | 939 | ||
905 | ret = snd_soc_register_platform(&of_dev->dev, &dma->dai); | 940 | iprop = of_get_property(ssi_np, "fsl,fifo-depth", NULL); |
941 | if (iprop) | ||
942 | dma->ssi_fifo_depth = *iprop; | ||
943 | else | ||
944 | /* Older 8610 DTs didn't have the fifo-depth property */ | ||
945 | dma->ssi_fifo_depth = 8; | ||
946 | |||
947 | of_node_put(ssi_np); | ||
948 | |||
949 | ret = snd_soc_register_platform(&pdev->dev, &dma->dai); | ||
906 | if (ret) { | 950 | if (ret) { |
907 | dev_err(&of_dev->dev, "could not register platform\n"); | 951 | dev_err(&pdev->dev, "could not register platform\n"); |
908 | kfree(dma); | 952 | kfree(dma); |
909 | return ret; | 953 | return ret; |
910 | } | 954 | } |
@@ -912,16 +956,16 @@ static int __devinit fsl_soc_dma_probe(struct of_device *of_dev, | |||
912 | dma->channel = of_iomap(np, 0); | 956 | dma->channel = of_iomap(np, 0); |
913 | dma->irq = irq_of_parse_and_map(np, 0); | 957 | dma->irq = irq_of_parse_and_map(np, 0); |
914 | 958 | ||
915 | dev_set_drvdata(&of_dev->dev, dma); | 959 | dev_set_drvdata(&pdev->dev, dma); |
916 | 960 | ||
917 | return 0; | 961 | return 0; |
918 | } | 962 | } |
919 | 963 | ||
920 | static int __devexit fsl_soc_dma_remove(struct of_device *of_dev) | 964 | static int __devexit fsl_soc_dma_remove(struct platform_device *pdev) |
921 | { | 965 | { |
922 | struct dma_object *dma = dev_get_drvdata(&of_dev->dev); | 966 | struct dma_object *dma = dev_get_drvdata(&pdev->dev); |
923 | 967 | ||
924 | snd_soc_unregister_platform(&of_dev->dev); | 968 | snd_soc_unregister_platform(&pdev->dev); |
925 | iounmap(dma->channel); | 969 | iounmap(dma->channel); |
926 | irq_dispose_mapping(dma->irq); | 970 | irq_dispose_mapping(dma->irq); |
927 | kfree(dma); | 971 | kfree(dma); |
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 7939c337ed9d..4cc167a7aeb8 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c | |||
@@ -93,6 +93,7 @@ struct fsl_ssi_private { | |||
93 | unsigned int playback; | 93 | unsigned int playback; |
94 | unsigned int capture; | 94 | unsigned int capture; |
95 | int asynchronous; | 95 | int asynchronous; |
96 | unsigned int fifo_depth; | ||
96 | struct snd_soc_dai_driver cpu_dai_drv; | 97 | struct snd_soc_dai_driver cpu_dai_drv; |
97 | struct device_attribute dev_attr; | 98 | struct device_attribute dev_attr; |
98 | struct platform_device *pdev; | 99 | struct platform_device *pdev; |
@@ -337,11 +338,20 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream, | |||
337 | 338 | ||
338 | /* | 339 | /* |
339 | * Set the watermark for transmit FIFI 0 and receive FIFO 0. We | 340 | * Set the watermark for transmit FIFI 0 and receive FIFO 0. We |
340 | * don't use FIFO 1. Since the SSI only supports stereo, the | 341 | * don't use FIFO 1. We program the transmit water to signal a |
341 | * watermark should never be an odd number. | 342 | * DMA transfer if there are only two (or fewer) elements left |
343 | * in the FIFO. Two elements equals one frame (left channel, | ||
344 | * right channel). This value, however, depends on the depth of | ||
345 | * the transmit buffer. | ||
346 | * | ||
347 | * We program the receive FIFO to notify us if at least two | ||
348 | * elements (one frame) have been written to the FIFO. We could | ||
349 | * make this value larger (and maybe we should), but this way | ||
350 | * data will be written to memory as soon as it's available. | ||
342 | */ | 351 | */ |
343 | out_be32(&ssi->sfcsr, | 352 | out_be32(&ssi->sfcsr, |
344 | CCSR_SSI_SFCSR_TFWM0(6) | CCSR_SSI_SFCSR_RFWM0(2)); | 353 | CCSR_SSI_SFCSR_TFWM0(ssi_private->fifo_depth - 2) | |
354 | CCSR_SSI_SFCSR_RFWM0(ssi_private->fifo_depth - 2)); | ||
345 | 355 | ||
346 | /* | 356 | /* |
347 | * We keep the SSI disabled because if we enable it, then the | 357 | * We keep the SSI disabled because if we enable it, then the |
@@ -614,14 +624,15 @@ static void make_lowercase(char *s) | |||
614 | } | 624 | } |
615 | } | 625 | } |
616 | 626 | ||
617 | static int __devinit fsl_ssi_probe(struct of_device *of_dev, | 627 | static int __devinit fsl_ssi_probe(struct platform_device *pdev, |
618 | const struct of_device_id *match) | 628 | const struct of_device_id *match) |
619 | { | 629 | { |
620 | struct fsl_ssi_private *ssi_private; | 630 | struct fsl_ssi_private *ssi_private; |
621 | int ret = 0; | 631 | int ret = 0; |
622 | struct device_attribute *dev_attr = NULL; | 632 | struct device_attribute *dev_attr = NULL; |
623 | struct device_node *np = of_dev->dev.of_node; | 633 | struct device_node *np = pdev->dev.of_node; |
624 | const char *p, *sprop; | 634 | const char *p, *sprop; |
635 | const uint32_t *iprop; | ||
625 | struct resource res; | 636 | struct resource res; |
626 | char name[64]; | 637 | char name[64]; |
627 | 638 | ||
@@ -634,14 +645,14 @@ static int __devinit fsl_ssi_probe(struct of_device *of_dev, | |||
634 | 645 | ||
635 | /* Check for a codec-handle property. */ | 646 | /* Check for a codec-handle property. */ |
636 | if (!of_get_property(np, "codec-handle", NULL)) { | 647 | if (!of_get_property(np, "codec-handle", NULL)) { |
637 | dev_err(&of_dev->dev, "missing codec-handle property\n"); | 648 | dev_err(&pdev->dev, "missing codec-handle property\n"); |
638 | return -ENODEV; | 649 | return -ENODEV; |
639 | } | 650 | } |
640 | 651 | ||
641 | /* We only support the SSI in "I2S Slave" mode */ | 652 | /* We only support the SSI in "I2S Slave" mode */ |
642 | sprop = of_get_property(np, "fsl,mode", NULL); | 653 | sprop = of_get_property(np, "fsl,mode", NULL); |
643 | if (!sprop || strcmp(sprop, "i2s-slave")) { | 654 | if (!sprop || strcmp(sprop, "i2s-slave")) { |
644 | dev_notice(&of_dev->dev, "mode %s is unsupported\n", sprop); | 655 | dev_notice(&pdev->dev, "mode %s is unsupported\n", sprop); |
645 | return -ENODEV; | 656 | return -ENODEV; |
646 | } | 657 | } |
647 | 658 | ||
@@ -650,7 +661,7 @@ static int __devinit fsl_ssi_probe(struct of_device *of_dev, | |||
650 | ssi_private = kzalloc(sizeof(struct fsl_ssi_private) + strlen(p), | 661 | ssi_private = kzalloc(sizeof(struct fsl_ssi_private) + strlen(p), |
651 | GFP_KERNEL); | 662 | GFP_KERNEL); |
652 | if (!ssi_private) { | 663 | if (!ssi_private) { |
653 | dev_err(&of_dev->dev, "could not allocate DAI object\n"); | 664 | dev_err(&pdev->dev, "could not allocate DAI object\n"); |
654 | return -ENOMEM; | 665 | return -ENOMEM; |
655 | } | 666 | } |
656 | 667 | ||
@@ -664,7 +675,7 @@ static int __devinit fsl_ssi_probe(struct of_device *of_dev, | |||
664 | /* Get the addresses and IRQ */ | 675 | /* Get the addresses and IRQ */ |
665 | ret = of_address_to_resource(np, 0, &res); | 676 | ret = of_address_to_resource(np, 0, &res); |
666 | if (ret) { | 677 | if (ret) { |
667 | dev_err(&of_dev->dev, "could not determine device resources\n"); | 678 | dev_err(&pdev->dev, "could not determine device resources\n"); |
668 | kfree(ssi_private); | 679 | kfree(ssi_private); |
669 | return ret; | 680 | return ret; |
670 | } | 681 | } |
@@ -678,25 +689,33 @@ static int __devinit fsl_ssi_probe(struct of_device *of_dev, | |||
678 | else | 689 | else |
679 | ssi_private->cpu_dai_drv.symmetric_rates = 1; | 690 | ssi_private->cpu_dai_drv.symmetric_rates = 1; |
680 | 691 | ||
692 | /* Determine the FIFO depth. */ | ||
693 | iprop = of_get_property(np, "fsl,fifo-depth", NULL); | ||
694 | if (iprop) | ||
695 | ssi_private->fifo_depth = *iprop; | ||
696 | else | ||
697 | /* Older 8610 DTs didn't have the fifo-depth property */ | ||
698 | ssi_private->fifo_depth = 8; | ||
699 | |||
681 | /* Initialize the the device_attribute structure */ | 700 | /* Initialize the the device_attribute structure */ |
682 | dev_attr = &ssi_private->dev_attr; | 701 | dev_attr = &ssi_private->dev_attr; |
683 | dev_attr->attr.name = "statistics"; | 702 | dev_attr->attr.name = "statistics"; |
684 | dev_attr->attr.mode = S_IRUGO; | 703 | dev_attr->attr.mode = S_IRUGO; |
685 | dev_attr->show = fsl_sysfs_ssi_show; | 704 | dev_attr->show = fsl_sysfs_ssi_show; |
686 | 705 | ||
687 | ret = device_create_file(&of_dev->dev, dev_attr); | 706 | ret = device_create_file(&pdev->dev, dev_attr); |
688 | if (ret) { | 707 | if (ret) { |
689 | dev_err(&of_dev->dev, "could not create sysfs %s file\n", | 708 | dev_err(&pdev->dev, "could not create sysfs %s file\n", |
690 | ssi_private->dev_attr.attr.name); | 709 | ssi_private->dev_attr.attr.name); |
691 | goto error; | 710 | goto error; |
692 | } | 711 | } |
693 | 712 | ||
694 | /* Register with ASoC */ | 713 | /* Register with ASoC */ |
695 | dev_set_drvdata(&of_dev->dev, ssi_private); | 714 | dev_set_drvdata(&pdev->dev, ssi_private); |
696 | 715 | ||
697 | ret = snd_soc_register_dai(&of_dev->dev, &ssi_private->cpu_dai_drv); | 716 | ret = snd_soc_register_dai(&pdev->dev, &ssi_private->cpu_dai_drv); |
698 | if (ret) { | 717 | if (ret) { |
699 | dev_err(&of_dev->dev, "failed to register DAI: %d\n", ret); | 718 | dev_err(&pdev->dev, "failed to register DAI: %d\n", ret); |
700 | goto error; | 719 | goto error; |
701 | } | 720 | } |
702 | 721 | ||
@@ -714,20 +733,20 @@ static int __devinit fsl_ssi_probe(struct of_device *of_dev, | |||
714 | make_lowercase(name); | 733 | make_lowercase(name); |
715 | 734 | ||
716 | ssi_private->pdev = | 735 | ssi_private->pdev = |
717 | platform_device_register_data(&of_dev->dev, name, 0, NULL, 0); | 736 | platform_device_register_data(&pdev->dev, name, 0, NULL, 0); |
718 | if (IS_ERR(ssi_private->pdev)) { | 737 | if (IS_ERR(ssi_private->pdev)) { |
719 | ret = PTR_ERR(ssi_private->pdev); | 738 | ret = PTR_ERR(ssi_private->pdev); |
720 | dev_err(&of_dev->dev, "failed to register platform: %d\n", ret); | 739 | dev_err(&pdev->dev, "failed to register platform: %d\n", ret); |
721 | goto error; | 740 | goto error; |
722 | } | 741 | } |
723 | 742 | ||
724 | return 0; | 743 | return 0; |
725 | 744 | ||
726 | error: | 745 | error: |
727 | snd_soc_unregister_dai(&of_dev->dev); | 746 | snd_soc_unregister_dai(&pdev->dev); |
728 | dev_set_drvdata(&of_dev->dev, NULL); | 747 | dev_set_drvdata(&pdev->dev, NULL); |
729 | if (dev_attr) | 748 | if (dev_attr) |
730 | device_remove_file(&of_dev->dev, dev_attr); | 749 | device_remove_file(&pdev->dev, dev_attr); |
731 | irq_dispose_mapping(ssi_private->irq); | 750 | irq_dispose_mapping(ssi_private->irq); |
732 | iounmap(ssi_private->ssi); | 751 | iounmap(ssi_private->ssi); |
733 | kfree(ssi_private); | 752 | kfree(ssi_private); |
@@ -735,16 +754,16 @@ error: | |||
735 | return ret; | 754 | return ret; |
736 | } | 755 | } |
737 | 756 | ||
738 | static int fsl_ssi_remove(struct of_device *of_dev) | 757 | static int fsl_ssi_remove(struct platform_device *pdev) |
739 | { | 758 | { |
740 | struct fsl_ssi_private *ssi_private = dev_get_drvdata(&of_dev->dev); | 759 | struct fsl_ssi_private *ssi_private = dev_get_drvdata(&pdev->dev); |
741 | 760 | ||
742 | platform_device_unregister(ssi_private->pdev); | 761 | platform_device_unregister(ssi_private->pdev); |
743 | snd_soc_unregister_dai(&of_dev->dev); | 762 | snd_soc_unregister_dai(&pdev->dev); |
744 | device_remove_file(&of_dev->dev, &ssi_private->dev_attr); | 763 | device_remove_file(&pdev->dev, &ssi_private->dev_attr); |
745 | 764 | ||
746 | kfree(ssi_private); | 765 | kfree(ssi_private); |
747 | dev_set_drvdata(&of_dev->dev, NULL); | 766 | dev_set_drvdata(&pdev->dev, NULL); |
748 | 767 | ||
749 | return 0; | 768 | return 0; |
750 | } | 769 | } |
diff --git a/sound/soc/fsl/mpc8610_hpcd.c b/sound/soc/fsl/mpc8610_hpcd.c index 38339c158ed9..0d7dcf1e4863 100644 --- a/sound/soc/fsl/mpc8610_hpcd.c +++ b/sound/soc/fsl/mpc8610_hpcd.c | |||
@@ -13,6 +13,7 @@ | |||
13 | #include <linux/module.h> | 13 | #include <linux/module.h> |
14 | #include <linux/interrupt.h> | 14 | #include <linux/interrupt.h> |
15 | #include <linux/of_device.h> | 15 | #include <linux/of_device.h> |
16 | #include <linux/slab.h> | ||
16 | #include <sound/soc.h> | 17 | #include <sound/soc.h> |
17 | #include <asm/fsl_guts.h> | 18 | #include <asm/fsl_guts.h> |
18 | 19 | ||
@@ -323,9 +324,10 @@ static int get_dma_channel(struct device_node *ssi_np, | |||
323 | static int mpc8610_hpcd_probe(struct platform_device *pdev) | 324 | static int mpc8610_hpcd_probe(struct platform_device *pdev) |
324 | { | 325 | { |
325 | struct device *dev = pdev->dev.parent; | 326 | struct device *dev = pdev->dev.parent; |
326 | /* of_dev is the OF device for the SSI node that probed us */ | 327 | /* ssi_pdev is the platform device for the SSI node that probed us */ |
327 | struct of_device *of_dev = container_of(dev, struct of_device, dev); | 328 | struct platform_device *ssi_pdev = |
328 | struct device_node *np = of_dev->dev.of_node; | 329 | container_of(dev, struct platform_device, dev); |
330 | struct device_node *np = ssi_pdev->dev.of_node; | ||
329 | struct device_node *codec_np = NULL; | 331 | struct device_node *codec_np = NULL; |
330 | struct platform_device *sound_device = NULL; | 332 | struct platform_device *sound_device = NULL; |
331 | struct mpc8610_hpcd_data *machine_data; | 333 | struct mpc8610_hpcd_data *machine_data; |
@@ -348,7 +350,7 @@ static int mpc8610_hpcd_probe(struct platform_device *pdev) | |||
348 | if (!machine_data) | 350 | if (!machine_data) |
349 | return -ENOMEM; | 351 | return -ENOMEM; |
350 | 352 | ||
351 | machine_data->dai[0].cpu_dai_name = dev_name(&of_dev->dev); | 353 | machine_data->dai[0].cpu_dai_name = dev_name(&ssi_pdev->dev); |
352 | machine_data->dai[0].ops = &mpc8610_hpcd_ops; | 354 | machine_data->dai[0].ops = &mpc8610_hpcd_ops; |
353 | 355 | ||
354 | /* Determine the codec name, it will be used as the codec DAI name */ | 356 | /* Determine the codec name, it will be used as the codec DAI name */ |
diff --git a/sound/soc/fsl/p1022_ds.c b/sound/soc/fsl/p1022_ds.c new file mode 100644 index 000000000000..f8176e8e1adf --- /dev/null +++ b/sound/soc/fsl/p1022_ds.c | |||
@@ -0,0 +1,590 @@ | |||
1 | /** | ||
2 | * Freescale P1022DS ALSA SoC Machine driver | ||
3 | * | ||
4 | * Author: Timur Tabi <timur@freescale.com> | ||
5 | * | ||
6 | * Copyright 2010 Freescale Semiconductor, Inc. | ||
7 | * | ||
8 | * This file is licensed under the terms of the GNU General Public License | ||
9 | * version 2. This program is licensed "as is" without any warranty of any | ||
10 | * kind, whether express or implied. | ||
11 | */ | ||
12 | |||
13 | #include <linux/module.h> | ||
14 | #include <linux/interrupt.h> | ||
15 | #include <linux/of_device.h> | ||
16 | #include <linux/slab.h> | ||
17 | #include <sound/soc.h> | ||
18 | #include <asm/fsl_guts.h> | ||
19 | |||
20 | #include "fsl_dma.h" | ||
21 | #include "fsl_ssi.h" | ||
22 | |||
23 | /* P1022-specific PMUXCR and DMUXCR bit definitions */ | ||
24 | |||
25 | #define CCSR_GUTS_PMUXCR_UART0_I2C1_MASK 0x0001c000 | ||
26 | #define CCSR_GUTS_PMUXCR_UART0_I2C1_UART0_SSI 0x00010000 | ||
27 | #define CCSR_GUTS_PMUXCR_UART0_I2C1_SSI 0x00018000 | ||
28 | |||
29 | #define CCSR_GUTS_PMUXCR_SSI_DMA_TDM_MASK 0x00000c00 | ||
30 | #define CCSR_GUTS_PMUXCR_SSI_DMA_TDM_SSI 0x00000000 | ||
31 | |||
32 | #define CCSR_GUTS_DMUXCR_PAD 1 /* DMA controller/channel set to pad */ | ||
33 | #define CCSR_GUTS_DMUXCR_SSI 2 /* DMA controller/channel set to SSI */ | ||
34 | |||
35 | /* | ||
36 | * Set the DMACR register in the GUTS | ||
37 | * | ||
38 | * The DMACR register determines the source of initiated transfers for each | ||
39 | * channel on each DMA controller. Rather than have a bunch of repetitive | ||
40 | * macros for the bit patterns, we just have a function that calculates | ||
41 | * them. | ||
42 | * | ||
43 | * guts: Pointer to GUTS structure | ||
44 | * co: The DMA controller (0 or 1) | ||
45 | * ch: The channel on the DMA controller (0, 1, 2, or 3) | ||
46 | * device: The device to set as the target (CCSR_GUTS_DMUXCR_xxx) | ||
47 | */ | ||
48 | static inline void guts_set_dmuxcr(struct ccsr_guts_85xx __iomem *guts, | ||
49 | unsigned int co, unsigned int ch, unsigned int device) | ||
50 | { | ||
51 | unsigned int shift = 16 + (8 * (1 - co) + 2 * (3 - ch)); | ||
52 | |||
53 | clrsetbits_be32(&guts->dmuxcr, 3 << shift, device << shift); | ||
54 | } | ||
55 | |||
56 | /* There's only one global utilities register */ | ||
57 | static phys_addr_t guts_phys; | ||
58 | |||
59 | #define DAI_NAME_SIZE 32 | ||
60 | |||
61 | /** | ||
62 | * machine_data: machine-specific ASoC device data | ||
63 | * | ||
64 | * This structure contains data for a single sound platform device on an | ||
65 | * P1022 DS. Some of the data is taken from the device tree. | ||
66 | */ | ||
67 | struct machine_data { | ||
68 | struct snd_soc_dai_link dai[2]; | ||
69 | struct snd_soc_card card; | ||
70 | unsigned int dai_format; | ||
71 | unsigned int codec_clk_direction; | ||
72 | unsigned int cpu_clk_direction; | ||
73 | unsigned int clk_frequency; | ||
74 | unsigned int ssi_id; /* 0 = SSI1, 1 = SSI2, etc */ | ||
75 | unsigned int dma_id[2]; /* 0 = DMA1, 1 = DMA2, etc */ | ||
76 | unsigned int dma_channel_id[2]; /* 0 = ch 0, 1 = ch 1, etc*/ | ||
77 | char codec_name[DAI_NAME_SIZE]; | ||
78 | char platform_name[2][DAI_NAME_SIZE]; /* One for each DMA channel */ | ||
79 | }; | ||
80 | |||
81 | /** | ||
82 | * p1022_ds_machine_probe: initialize the board | ||
83 | * | ||
84 | * This function is used to initialize the board-specific hardware. | ||
85 | * | ||
86 | * Here we program the DMACR and PMUXCR registers. | ||
87 | */ | ||
88 | static int p1022_ds_machine_probe(struct platform_device *sound_device) | ||
89 | { | ||
90 | struct snd_soc_card *card = platform_get_drvdata(sound_device); | ||
91 | struct machine_data *mdata = | ||
92 | container_of(card, struct machine_data, card); | ||
93 | struct ccsr_guts_85xx __iomem *guts; | ||
94 | |||
95 | guts = ioremap(guts_phys, sizeof(struct ccsr_guts_85xx)); | ||
96 | if (!guts) { | ||
97 | dev_err(card->dev, "could not map global utilities\n"); | ||
98 | return -ENOMEM; | ||
99 | } | ||
100 | |||
101 | /* Enable SSI Tx signal */ | ||
102 | clrsetbits_be32(&guts->pmuxcr, CCSR_GUTS_PMUXCR_UART0_I2C1_MASK, | ||
103 | CCSR_GUTS_PMUXCR_UART0_I2C1_UART0_SSI); | ||
104 | |||
105 | /* Enable SSI Rx signal */ | ||
106 | clrsetbits_be32(&guts->pmuxcr, CCSR_GUTS_PMUXCR_SSI_DMA_TDM_MASK, | ||
107 | CCSR_GUTS_PMUXCR_SSI_DMA_TDM_SSI); | ||
108 | |||
109 | /* Enable DMA Channel for SSI */ | ||
110 | guts_set_dmuxcr(guts, mdata->dma_id[0], mdata->dma_channel_id[0], | ||
111 | CCSR_GUTS_DMUXCR_SSI); | ||
112 | |||
113 | guts_set_dmuxcr(guts, mdata->dma_id[1], mdata->dma_channel_id[1], | ||
114 | CCSR_GUTS_DMUXCR_SSI); | ||
115 | |||
116 | iounmap(guts); | ||
117 | |||
118 | return 0; | ||
119 | } | ||
120 | |||
121 | /** | ||
122 | * p1022_ds_startup: program the board with various hardware parameters | ||
123 | * | ||
124 | * This function takes board-specific information, like clock frequencies | ||
125 | * and serial data formats, and passes that information to the codec and | ||
126 | * transport drivers. | ||
127 | */ | ||
128 | static int p1022_ds_startup(struct snd_pcm_substream *substream) | ||
129 | { | ||
130 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
131 | struct machine_data *mdata = | ||
132 | container_of(rtd->card, struct machine_data, card); | ||
133 | struct device *dev = rtd->card->dev; | ||
134 | int ret = 0; | ||
135 | |||
136 | /* Tell the codec driver what the serial protocol is. */ | ||
137 | ret = snd_soc_dai_set_fmt(rtd->codec_dai, mdata->dai_format); | ||
138 | if (ret < 0) { | ||
139 | dev_err(dev, "could not set codec driver audio format\n"); | ||
140 | return ret; | ||
141 | } | ||
142 | |||
143 | /* | ||
144 | * Tell the codec driver what the MCLK frequency is, and whether it's | ||
145 | * a slave or master. | ||
146 | */ | ||
147 | ret = snd_soc_dai_set_sysclk(rtd->codec_dai, 0, mdata->clk_frequency, | ||
148 | mdata->codec_clk_direction); | ||
149 | if (ret < 0) { | ||
150 | dev_err(dev, "could not set codec driver clock params\n"); | ||
151 | return ret; | ||
152 | } | ||
153 | |||
154 | return 0; | ||
155 | } | ||
156 | |||
157 | /** | ||
158 | * p1022_ds_machine_remove: Remove the sound device | ||
159 | * | ||
160 | * This function is called to remove the sound device for one SSI. We | ||
161 | * de-program the DMACR and PMUXCR register. | ||
162 | */ | ||
163 | static int p1022_ds_machine_remove(struct platform_device *sound_device) | ||
164 | { | ||
165 | struct snd_soc_card *card = platform_get_drvdata(sound_device); | ||
166 | struct machine_data *mdata = | ||
167 | container_of(card, struct machine_data, card); | ||
168 | struct ccsr_guts_85xx __iomem *guts; | ||
169 | |||
170 | guts = ioremap(guts_phys, sizeof(struct ccsr_guts_85xx)); | ||
171 | if (!guts) { | ||
172 | dev_err(card->dev, "could not map global utilities\n"); | ||
173 | return -ENOMEM; | ||
174 | } | ||
175 | |||
176 | /* Restore the signal routing */ | ||
177 | clrbits32(&guts->pmuxcr, CCSR_GUTS_PMUXCR_UART0_I2C1_MASK); | ||
178 | clrbits32(&guts->pmuxcr, CCSR_GUTS_PMUXCR_SSI_DMA_TDM_MASK); | ||
179 | guts_set_dmuxcr(guts, mdata->dma_id[0], mdata->dma_channel_id[0], 0); | ||
180 | guts_set_dmuxcr(guts, mdata->dma_id[1], mdata->dma_channel_id[1], 0); | ||
181 | |||
182 | iounmap(guts); | ||
183 | |||
184 | return 0; | ||
185 | } | ||
186 | |||
187 | /** | ||
188 | * p1022_ds_ops: ASoC machine driver operations | ||
189 | */ | ||
190 | static struct snd_soc_ops p1022_ds_ops = { | ||
191 | .startup = p1022_ds_startup, | ||
192 | }; | ||
193 | |||
194 | /** | ||
195 | * get_node_by_phandle_name - get a node by its phandle name | ||
196 | * | ||
197 | * This function takes a node, the name of a property in that node, and a | ||
198 | * compatible string. Assuming the property is a phandle to another node, | ||
199 | * it returns that node, (optionally) if that node is compatible. | ||
200 | * | ||
201 | * If the property is not a phandle, or the node it points to is not compatible | ||
202 | * with the specific string, then NULL is returned. | ||
203 | */ | ||
204 | static struct device_node *get_node_by_phandle_name(struct device_node *np, | ||
205 | const char *name, const char *compatible) | ||
206 | { | ||
207 | np = of_parse_phandle(np, name, 0); | ||
208 | if (!np) | ||
209 | return NULL; | ||
210 | |||
211 | if (!of_device_is_compatible(np, compatible)) { | ||
212 | of_node_put(np); | ||
213 | return NULL; | ||
214 | } | ||
215 | |||
216 | return np; | ||
217 | } | ||
218 | |||
219 | /** | ||
220 | * get_parent_cell_index -- return the cell-index of the parent of a node | ||
221 | * | ||
222 | * Return the value of the cell-index property of the parent of the given | ||
223 | * node. This is used for DMA channel nodes that need to know the DMA ID | ||
224 | * of the controller they are on. | ||
225 | */ | ||
226 | static int get_parent_cell_index(struct device_node *np) | ||
227 | { | ||
228 | struct device_node *parent = of_get_parent(np); | ||
229 | const u32 *iprop; | ||
230 | int ret = -1; | ||
231 | |||
232 | if (!parent) | ||
233 | return -1; | ||
234 | |||
235 | iprop = of_get_property(parent, "cell-index", NULL); | ||
236 | if (iprop) | ||
237 | ret = *iprop; | ||
238 | |||
239 | of_node_put(parent); | ||
240 | |||
241 | return ret; | ||
242 | } | ||
243 | |||
244 | /** | ||
245 | * codec_node_dev_name - determine the dev_name for a codec node | ||
246 | * | ||
247 | * This function determines the dev_name for an I2C node. This is the name | ||
248 | * that would be returned by dev_name() if this device_node were part of a | ||
249 | * 'struct device' It's ugly and hackish, but it works. | ||
250 | * | ||
251 | * The dev_name for such devices include the bus number and I2C address. For | ||
252 | * example, "cs4270-codec.0-004f". | ||
253 | */ | ||
254 | static int codec_node_dev_name(struct device_node *np, char *buf, size_t len) | ||
255 | { | ||
256 | const u32 *iprop; | ||
257 | int bus, addr; | ||
258 | char temp[DAI_NAME_SIZE]; | ||
259 | |||
260 | of_modalias_node(np, temp, DAI_NAME_SIZE); | ||
261 | |||
262 | iprop = of_get_property(np, "reg", NULL); | ||
263 | if (!iprop) | ||
264 | return -EINVAL; | ||
265 | |||
266 | addr = *iprop; | ||
267 | |||
268 | bus = get_parent_cell_index(np); | ||
269 | if (bus < 0) | ||
270 | return bus; | ||
271 | |||
272 | snprintf(buf, len, "%s-codec.%u-%04x", temp, bus, addr); | ||
273 | |||
274 | return 0; | ||
275 | } | ||
276 | |||
277 | static int get_dma_channel(struct device_node *ssi_np, | ||
278 | const char *compatible, | ||
279 | struct snd_soc_dai_link *dai, | ||
280 | unsigned int *dma_channel_id, | ||
281 | unsigned int *dma_id) | ||
282 | { | ||
283 | struct resource res; | ||
284 | struct device_node *dma_channel_np; | ||
285 | const u32 *iprop; | ||
286 | int ret; | ||
287 | |||
288 | dma_channel_np = get_node_by_phandle_name(ssi_np, compatible, | ||
289 | "fsl,ssi-dma-channel"); | ||
290 | if (!dma_channel_np) | ||
291 | return -EINVAL; | ||
292 | |||
293 | /* Determine the dev_name for the device_node. This code mimics the | ||
294 | * behavior of of_device_make_bus_id(). We need this because ASoC uses | ||
295 | * the dev_name() of the device to match the platform (DMA) device with | ||
296 | * the CPU (SSI) device. It's all ugly and hackish, but it works (for | ||
297 | * now). | ||
298 | * | ||
299 | * dai->platform name should already point to an allocated buffer. | ||
300 | */ | ||
301 | ret = of_address_to_resource(dma_channel_np, 0, &res); | ||
302 | if (ret) | ||
303 | return ret; | ||
304 | snprintf((char *)dai->platform_name, DAI_NAME_SIZE, "%llx.%s", | ||
305 | (unsigned long long) res.start, dma_channel_np->name); | ||
306 | |||
307 | iprop = of_get_property(dma_channel_np, "cell-index", NULL); | ||
308 | if (!iprop) { | ||
309 | of_node_put(dma_channel_np); | ||
310 | return -EINVAL; | ||
311 | } | ||
312 | |||
313 | *dma_channel_id = *iprop; | ||
314 | *dma_id = get_parent_cell_index(dma_channel_np); | ||
315 | of_node_put(dma_channel_np); | ||
316 | |||
317 | return 0; | ||
318 | } | ||
319 | |||
320 | /** | ||
321 | * p1022_ds_probe: platform probe function for the machine driver | ||
322 | * | ||
323 | * Although this is a machine driver, the SSI node is the "master" node with | ||
324 | * respect to audio hardware connections. Therefore, we create a new ASoC | ||
325 | * device for each new SSI node that has a codec attached. | ||
326 | */ | ||
327 | static int p1022_ds_probe(struct platform_device *pdev) | ||
328 | { | ||
329 | struct device *dev = pdev->dev.parent; | ||
330 | /* ssi_pdev is the platform device for the SSI node that probed us */ | ||
331 | struct platform_device *ssi_pdev = | ||
332 | container_of(dev, struct platform_device, dev); | ||
333 | struct device_node *np = ssi_pdev->dev.of_node; | ||
334 | struct device_node *codec_np = NULL; | ||
335 | struct platform_device *sound_device = NULL; | ||
336 | struct machine_data *mdata; | ||
337 | int ret = -ENODEV; | ||
338 | const char *sprop; | ||
339 | const u32 *iprop; | ||
340 | |||
341 | /* Find the codec node for this SSI. */ | ||
342 | codec_np = of_parse_phandle(np, "codec-handle", 0); | ||
343 | if (!codec_np) { | ||
344 | dev_err(dev, "could not find codec node\n"); | ||
345 | return -EINVAL; | ||
346 | } | ||
347 | |||
348 | mdata = kzalloc(sizeof(struct machine_data), GFP_KERNEL); | ||
349 | if (!mdata) | ||
350 | return -ENOMEM; | ||
351 | |||
352 | mdata->dai[0].cpu_dai_name = dev_name(&ssi_pdev->dev); | ||
353 | mdata->dai[0].ops = &p1022_ds_ops; | ||
354 | |||
355 | /* Determine the codec name, it will be used as the codec DAI name */ | ||
356 | ret = codec_node_dev_name(codec_np, mdata->codec_name, DAI_NAME_SIZE); | ||
357 | if (ret) { | ||
358 | dev_err(&pdev->dev, "invalid codec node %s\n", | ||
359 | codec_np->full_name); | ||
360 | ret = -EINVAL; | ||
361 | goto error; | ||
362 | } | ||
363 | mdata->dai[0].codec_name = mdata->codec_name; | ||
364 | |||
365 | /* We register two DAIs per SSI, one for playback and the other for | ||
366 | * capture. We support codecs that have separate DAIs for both playback | ||
367 | * and capture. | ||
368 | */ | ||
369 | memcpy(&mdata->dai[1], &mdata->dai[0], sizeof(struct snd_soc_dai_link)); | ||
370 | |||
371 | /* The DAI names from the codec (snd_soc_dai_driver.name) */ | ||
372 | mdata->dai[0].codec_dai_name = "wm8776-hifi-playback"; | ||
373 | mdata->dai[1].codec_dai_name = "wm8776-hifi-capture"; | ||
374 | |||
375 | /* Get the device ID */ | ||
376 | iprop = of_get_property(np, "cell-index", NULL); | ||
377 | if (!iprop) { | ||
378 | dev_err(&pdev->dev, "cell-index property not found\n"); | ||
379 | ret = -EINVAL; | ||
380 | goto error; | ||
381 | } | ||
382 | mdata->ssi_id = *iprop; | ||
383 | |||
384 | /* Get the serial format and clock direction. */ | ||
385 | sprop = of_get_property(np, "fsl,mode", NULL); | ||
386 | if (!sprop) { | ||
387 | dev_err(&pdev->dev, "fsl,mode property not found\n"); | ||
388 | ret = -EINVAL; | ||
389 | goto error; | ||
390 | } | ||
391 | |||
392 | if (strcasecmp(sprop, "i2s-slave") == 0) { | ||
393 | mdata->dai_format = SND_SOC_DAIFMT_I2S; | ||
394 | mdata->codec_clk_direction = SND_SOC_CLOCK_OUT; | ||
395 | mdata->cpu_clk_direction = SND_SOC_CLOCK_IN; | ||
396 | |||
397 | /* In i2s-slave mode, the codec has its own clock source, so we | ||
398 | * need to get the frequency from the device tree and pass it to | ||
399 | * the codec driver. | ||
400 | */ | ||
401 | iprop = of_get_property(codec_np, "clock-frequency", NULL); | ||
402 | if (!iprop || !*iprop) { | ||
403 | dev_err(&pdev->dev, "codec bus-frequency " | ||
404 | "property is missing or invalid\n"); | ||
405 | ret = -EINVAL; | ||
406 | goto error; | ||
407 | } | ||
408 | mdata->clk_frequency = *iprop; | ||
409 | } else if (strcasecmp(sprop, "i2s-master") == 0) { | ||
410 | mdata->dai_format = SND_SOC_DAIFMT_I2S; | ||
411 | mdata->codec_clk_direction = SND_SOC_CLOCK_IN; | ||
412 | mdata->cpu_clk_direction = SND_SOC_CLOCK_OUT; | ||
413 | } else if (strcasecmp(sprop, "lj-slave") == 0) { | ||
414 | mdata->dai_format = SND_SOC_DAIFMT_LEFT_J; | ||
415 | mdata->codec_clk_direction = SND_SOC_CLOCK_OUT; | ||
416 | mdata->cpu_clk_direction = SND_SOC_CLOCK_IN; | ||
417 | } else if (strcasecmp(sprop, "lj-master") == 0) { | ||
418 | mdata->dai_format = SND_SOC_DAIFMT_LEFT_J; | ||
419 | mdata->codec_clk_direction = SND_SOC_CLOCK_IN; | ||
420 | mdata->cpu_clk_direction = SND_SOC_CLOCK_OUT; | ||
421 | } else if (strcasecmp(sprop, "rj-slave") == 0) { | ||
422 | mdata->dai_format = SND_SOC_DAIFMT_RIGHT_J; | ||
423 | mdata->codec_clk_direction = SND_SOC_CLOCK_OUT; | ||
424 | mdata->cpu_clk_direction = SND_SOC_CLOCK_IN; | ||
425 | } else if (strcasecmp(sprop, "rj-master") == 0) { | ||
426 | mdata->dai_format = SND_SOC_DAIFMT_RIGHT_J; | ||
427 | mdata->codec_clk_direction = SND_SOC_CLOCK_IN; | ||
428 | mdata->cpu_clk_direction = SND_SOC_CLOCK_OUT; | ||
429 | } else if (strcasecmp(sprop, "ac97-slave") == 0) { | ||
430 | mdata->dai_format = SND_SOC_DAIFMT_AC97; | ||
431 | mdata->codec_clk_direction = SND_SOC_CLOCK_OUT; | ||
432 | mdata->cpu_clk_direction = SND_SOC_CLOCK_IN; | ||
433 | } else if (strcasecmp(sprop, "ac97-master") == 0) { | ||
434 | mdata->dai_format = SND_SOC_DAIFMT_AC97; | ||
435 | mdata->codec_clk_direction = SND_SOC_CLOCK_IN; | ||
436 | mdata->cpu_clk_direction = SND_SOC_CLOCK_OUT; | ||
437 | } else { | ||
438 | dev_err(&pdev->dev, | ||
439 | "unrecognized fsl,mode property '%s'\n", sprop); | ||
440 | ret = -EINVAL; | ||
441 | goto error; | ||
442 | } | ||
443 | |||
444 | if (!mdata->clk_frequency) { | ||
445 | dev_err(&pdev->dev, "unknown clock frequency\n"); | ||
446 | ret = -EINVAL; | ||
447 | goto error; | ||
448 | } | ||
449 | |||
450 | /* Find the playback DMA channel to use. */ | ||
451 | mdata->dai[0].platform_name = mdata->platform_name[0]; | ||
452 | ret = get_dma_channel(np, "fsl,playback-dma", &mdata->dai[0], | ||
453 | &mdata->dma_channel_id[0], | ||
454 | &mdata->dma_id[0]); | ||
455 | if (ret) { | ||
456 | dev_err(&pdev->dev, "missing/invalid playback DMA phandle\n"); | ||
457 | goto error; | ||
458 | } | ||
459 | |||
460 | /* Find the capture DMA channel to use. */ | ||
461 | mdata->dai[1].platform_name = mdata->platform_name[1]; | ||
462 | ret = get_dma_channel(np, "fsl,capture-dma", &mdata->dai[1], | ||
463 | &mdata->dma_channel_id[1], | ||
464 | &mdata->dma_id[1]); | ||
465 | if (ret) { | ||
466 | dev_err(&pdev->dev, "missing/invalid capture DMA phandle\n"); | ||
467 | goto error; | ||
468 | } | ||
469 | |||
470 | /* Initialize our DAI data structure. */ | ||
471 | mdata->dai[0].stream_name = "playback"; | ||
472 | mdata->dai[1].stream_name = "capture"; | ||
473 | mdata->dai[0].name = mdata->dai[0].stream_name; | ||
474 | mdata->dai[1].name = mdata->dai[1].stream_name; | ||
475 | |||
476 | mdata->card.probe = p1022_ds_machine_probe; | ||
477 | mdata->card.remove = p1022_ds_machine_remove; | ||
478 | mdata->card.name = pdev->name; /* The platform driver name */ | ||
479 | mdata->card.num_links = 2; | ||
480 | mdata->card.dai_link = mdata->dai; | ||
481 | |||
482 | /* Allocate a new audio platform device structure */ | ||
483 | sound_device = platform_device_alloc("soc-audio", -1); | ||
484 | if (!sound_device) { | ||
485 | dev_err(&pdev->dev, "platform device alloc failed\n"); | ||
486 | ret = -ENOMEM; | ||
487 | goto error; | ||
488 | } | ||
489 | |||
490 | /* Associate the card data with the sound device */ | ||
491 | platform_set_drvdata(sound_device, &mdata->card); | ||
492 | |||
493 | /* Register with ASoC */ | ||
494 | ret = platform_device_add(sound_device); | ||
495 | if (ret) { | ||
496 | dev_err(&pdev->dev, "platform device add failed\n"); | ||
497 | goto error; | ||
498 | } | ||
499 | |||
500 | of_node_put(codec_np); | ||
501 | |||
502 | return 0; | ||
503 | |||
504 | error: | ||
505 | of_node_put(codec_np); | ||
506 | |||
507 | if (sound_device) | ||
508 | platform_device_unregister(sound_device); | ||
509 | |||
510 | kfree(mdata); | ||
511 | |||
512 | return ret; | ||
513 | } | ||
514 | |||
515 | /** | ||
516 | * p1022_ds_remove: remove the platform device | ||
517 | * | ||
518 | * This function is called when the platform device is removed. | ||
519 | */ | ||
520 | static int __devexit p1022_ds_remove(struct platform_device *pdev) | ||
521 | { | ||
522 | struct platform_device *sound_device = dev_get_drvdata(&pdev->dev); | ||
523 | struct snd_soc_card *card = platform_get_drvdata(sound_device); | ||
524 | struct machine_data *mdata = | ||
525 | container_of(card, struct machine_data, card); | ||
526 | |||
527 | platform_device_unregister(sound_device); | ||
528 | |||
529 | kfree(mdata); | ||
530 | sound_device->dev.platform_data = NULL; | ||
531 | |||
532 | dev_set_drvdata(&pdev->dev, NULL); | ||
533 | |||
534 | return 0; | ||
535 | } | ||
536 | |||
537 | static struct platform_driver p1022_ds_driver = { | ||
538 | .probe = p1022_ds_probe, | ||
539 | .remove = __devexit_p(p1022_ds_remove), | ||
540 | .driver = { | ||
541 | /* The name must match the 'model' property in the device tree, | ||
542 | * in lowercase letters, but only the part after that last | ||
543 | * comma. This is because some model properties have a "fsl," | ||
544 | * prefix. | ||
545 | */ | ||
546 | .name = "snd-soc-p1022", | ||
547 | .owner = THIS_MODULE, | ||
548 | }, | ||
549 | }; | ||
550 | |||
551 | /** | ||
552 | * p1022_ds_init: machine driver initialization. | ||
553 | * | ||
554 | * This function is called when this module is loaded. | ||
555 | */ | ||
556 | static int __init p1022_ds_init(void) | ||
557 | { | ||
558 | struct device_node *guts_np; | ||
559 | struct resource res; | ||
560 | |||
561 | pr_info("Freescale P1022 DS ALSA SoC machine driver\n"); | ||
562 | |||
563 | /* Get the physical address of the global utilities registers */ | ||
564 | guts_np = of_find_compatible_node(NULL, NULL, "fsl,p1022-guts"); | ||
565 | if (of_address_to_resource(guts_np, 0, &res)) { | ||
566 | pr_err("p1022-ds: missing/invalid global utilities node\n"); | ||
567 | return -EINVAL; | ||
568 | } | ||
569 | guts_phys = res.start; | ||
570 | of_node_put(guts_np); | ||
571 | |||
572 | return platform_driver_register(&p1022_ds_driver); | ||
573 | } | ||
574 | |||
575 | /** | ||
576 | * p1022_ds_exit: machine driver exit | ||
577 | * | ||
578 | * This function is called when this driver is unloaded. | ||
579 | */ | ||
580 | static void __exit p1022_ds_exit(void) | ||
581 | { | ||
582 | platform_driver_unregister(&p1022_ds_driver); | ||
583 | } | ||
584 | |||
585 | module_init(p1022_ds_init); | ||
586 | module_exit(p1022_ds_exit); | ||
587 | |||
588 | MODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); | ||
589 | MODULE_DESCRIPTION("Freescale P1022 DS ALSA SoC machine driver"); | ||
590 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/sound/soc/imx/imx-ssi.c b/sound/soc/imx/imx-ssi.c index 2601be5a4ed8..26716e9626f4 100644 --- a/sound/soc/imx/imx-ssi.c +++ b/sound/soc/imx/imx-ssi.c | |||
@@ -254,6 +254,9 @@ static int imx_ssi_hw_params(struct snd_pcm_substream *substream, | |||
254 | dma_data = &ssi->dma_params_rx; | 254 | dma_data = &ssi->dma_params_rx; |
255 | } | 255 | } |
256 | 256 | ||
257 | if (ssi->flags & IMX_SSI_SYN) | ||
258 | reg = SSI_STCCR; | ||
259 | |||
257 | snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data); | 260 | snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data); |
258 | 261 | ||
259 | sccr = readl(ssi->base + reg) & ~SSI_STCCR_WL_MASK; | 262 | sccr = readl(ssi->base + reg) & ~SSI_STCCR_WL_MASK; |
diff --git a/sound/soc/omap/ams-delta.c b/sound/soc/omap/ams-delta.c index 9d88efa06e3c..438146addbb8 100644 --- a/sound/soc/omap/ams-delta.c +++ b/sound/soc/omap/ams-delta.c | |||
@@ -584,7 +584,7 @@ static struct snd_soc_dai_link ams_delta_dai_link = { | |||
584 | .name = "CX20442", | 584 | .name = "CX20442", |
585 | .stream_name = "CX20442", | 585 | .stream_name = "CX20442", |
586 | .cpu_dai_name ="omap-mcbsp-dai.0", | 586 | .cpu_dai_name ="omap-mcbsp-dai.0", |
587 | .codec_dai_name = "cx20442-hifi", | 587 | .codec_dai_name = "cx20442-voice", |
588 | .init = ams_delta_cx20442_init, | 588 | .init = ams_delta_cx20442_init, |
589 | .platform_name = "omap-pcm-audio", | 589 | .platform_name = "omap-pcm-audio", |
590 | .codec_name = "cx20442-codec", | 590 | .codec_name = "cx20442-codec", |
diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig index e30c8325f35e..37f191bbfdd9 100644 --- a/sound/soc/pxa/Kconfig +++ b/sound/soc/pxa/Kconfig | |||
@@ -117,6 +117,24 @@ config SND_PXA2XX_SOC_PALM27X | |||
117 | Say Y if you want to add support for SoC audio on | 117 | Say Y if you want to add support for SoC audio on |
118 | Palm T|X, T5, E2 or LifeDrive handheld computer. | 118 | Palm T|X, T5, E2 or LifeDrive handheld computer. |
119 | 119 | ||
120 | config SND_SOC_SAARB | ||
121 | tristate "SoC Audio support for Marvell Saarb" | ||
122 | depends on SND_PXA2XX_SOC && MACH_SAARB | ||
123 | select SND_PXA_SOC_SSP | ||
124 | select SND_SOC_88PM860X | ||
125 | help | ||
126 | Say Y if you want to add support for SoC audio on the | ||
127 | Marvell Saarb reference platform. | ||
128 | |||
129 | config SND_SOC_TAVOREVB3 | ||
130 | tristate "SoC Audio support for Marvell Tavor EVB3" | ||
131 | depends on SND_PXA2XX_SOC && MACH_TAVOREVB3 | ||
132 | select SND_PXA_SOC_SSP | ||
133 | select SND_SOC_88PM860X | ||
134 | help | ||
135 | Say Y if you want to add support for SoC audio on the | ||
136 | Marvell Saarb reference platform. | ||
137 | |||
120 | config SND_SOC_ZYLONITE | 138 | config SND_SOC_ZYLONITE |
121 | tristate "SoC Audio support for Marvell Zylonite" | 139 | tristate "SoC Audio support for Marvell Zylonite" |
122 | depends on SND_PXA2XX_SOC && MACH_ZYLONITE | 140 | depends on SND_PXA2XX_SOC && MACH_ZYLONITE |
diff --git a/sound/soc/pxa/Makefile b/sound/soc/pxa/Makefile index caa03d8f4789..07660165ec8d 100644 --- a/sound/soc/pxa/Makefile +++ b/sound/soc/pxa/Makefile | |||
@@ -19,6 +19,8 @@ snd-soc-e800-objs := e800_wm9712.o | |||
19 | snd-soc-spitz-objs := spitz.o | 19 | snd-soc-spitz-objs := spitz.o |
20 | snd-soc-em-x270-objs := em-x270.o | 20 | snd-soc-em-x270-objs := em-x270.o |
21 | snd-soc-palm27x-objs := palm27x.o | 21 | snd-soc-palm27x-objs := palm27x.o |
22 | snd-soc-saarb-objs := saarb.o | ||
23 | snd-soc-tavorevb3-objs := tavorevb3.o | ||
22 | snd-soc-zylonite-objs := zylonite.o | 24 | snd-soc-zylonite-objs := zylonite.o |
23 | snd-soc-magician-objs := magician.o | 25 | snd-soc-magician-objs := magician.o |
24 | snd-soc-mioa701-objs := mioa701_wm9713.o | 26 | snd-soc-mioa701-objs := mioa701_wm9713.o |
@@ -38,6 +40,8 @@ obj-$(CONFIG_SND_PXA2XX_SOC_PALM27X) += snd-soc-palm27x.o | |||
38 | obj-$(CONFIG_SND_PXA2XX_SOC_MAGICIAN) += snd-soc-magician.o | 40 | obj-$(CONFIG_SND_PXA2XX_SOC_MAGICIAN) += snd-soc-magician.o |
39 | obj-$(CONFIG_SND_PXA2XX_SOC_MIOA701) += snd-soc-mioa701.o | 41 | obj-$(CONFIG_SND_PXA2XX_SOC_MIOA701) += snd-soc-mioa701.o |
40 | obj-$(CONFIG_SND_PXA2XX_SOC_Z2) += snd-soc-z2.o | 42 | obj-$(CONFIG_SND_PXA2XX_SOC_Z2) += snd-soc-z2.o |
43 | obj-$(CONFIG_SND_SOC_SAARB) += snd-soc-saarb.o | ||
44 | obj-$(CONFIG_SND_SOC_TAVOREVB3) += snd-soc-tavorevb3.o | ||
41 | obj-$(CONFIG_SND_SOC_ZYLONITE) += snd-soc-zylonite.o | 45 | obj-$(CONFIG_SND_SOC_ZYLONITE) += snd-soc-zylonite.o |
42 | obj-$(CONFIG_SND_PXA2XX_SOC_IMOTE2) += snd-soc-imote2.o | 46 | obj-$(CONFIG_SND_PXA2XX_SOC_IMOTE2) += snd-soc-imote2.o |
43 | obj-$(CONFIG_SND_SOC_RAUMFELD) += snd-soc-raumfeld.o | 47 | obj-$(CONFIG_SND_SOC_RAUMFELD) += snd-soc-raumfeld.o |
diff --git a/sound/soc/pxa/e740_wm9705.c b/sound/soc/pxa/e740_wm9705.c index f614607b2055..c82cedb602fd 100644 --- a/sound/soc/pxa/e740_wm9705.c +++ b/sound/soc/pxa/e740_wm9705.c | |||
@@ -198,6 +198,9 @@ free_mic_amp_gpio: | |||
198 | static void __exit e740_exit(void) | 198 | static void __exit e740_exit(void) |
199 | { | 199 | { |
200 | platform_device_unregister(e740_snd_device); | 200 | platform_device_unregister(e740_snd_device); |
201 | gpio_free(GPIO_E740_WM9705_nAVDD2); | ||
202 | gpio_free(GPIO_E740_AMP_ON); | ||
203 | gpio_free(GPIO_E740_MIC_ON); | ||
201 | } | 204 | } |
202 | 205 | ||
203 | module_init(e740_init); | 206 | module_init(e740_init); |
diff --git a/sound/soc/pxa/imote2.c b/sound/soc/pxa/imote2.c index 03765fc5ac74..154fc6f23438 100644 --- a/sound/soc/pxa/imote2.c +++ b/sound/soc/pxa/imote2.c | |||
@@ -63,7 +63,7 @@ static struct snd_soc_ops imote2_asoc_ops = { | |||
63 | static struct snd_soc_dai_link imote2_dai = { | 63 | static struct snd_soc_dai_link imote2_dai = { |
64 | .name = "WM8940", | 64 | .name = "WM8940", |
65 | .stream_name = "WM8940", | 65 | .stream_name = "WM8940", |
66 | .cpu_dai_name = "pxa-i2s", | 66 | .cpu_dai_name = "pxa2xx-i2s", |
67 | .codec_dai_name = "wm8940-hifi", | 67 | .codec_dai_name = "wm8940-hifi", |
68 | .platform_name = "pxa-pcm-audio", | 68 | .platform_name = "pxa-pcm-audio", |
69 | .codec_name = "wm8940-codec.0-0034", | 69 | .codec_name = "wm8940-codec.0-0034", |
diff --git a/sound/soc/pxa/magician.c b/sound/soc/pxa/magician.c index 608bc3dd835f..b8207ced4072 100644 --- a/sound/soc/pxa/magician.c +++ b/sound/soc/pxa/magician.c | |||
@@ -437,7 +437,7 @@ static struct snd_soc_dai_link magician_dai[] = { | |||
437 | { | 437 | { |
438 | .name = "uda1380", | 438 | .name = "uda1380", |
439 | .stream_name = "UDA1380 Capture", | 439 | .stream_name = "UDA1380 Capture", |
440 | .cpu_dai_name = "pxa-i2s", | 440 | .cpu_dai_name = "pxa2xx-i2s", |
441 | .codec_dai_name = "uda1380-hifi-capture", | 441 | .codec_dai_name = "uda1380-hifi-capture", |
442 | .platform_name = "pxa-pcm-audio", | 442 | .platform_name = "pxa-pcm-audio", |
443 | .codec_name = "uda1380-codec.0-0018", | 443 | .codec_name = "uda1380-codec.0-0018", |
diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c index fa752f6ec37d..af84ee9c5e11 100644 --- a/sound/soc/pxa/poodle.c +++ b/sound/soc/pxa/poodle.c | |||
@@ -266,7 +266,7 @@ static int poodle_wm8731_init(struct snd_soc_pcm_runtime *rtd) | |||
266 | static struct snd_soc_dai_link poodle_dai = { | 266 | static struct snd_soc_dai_link poodle_dai = { |
267 | .name = "WM8731", | 267 | .name = "WM8731", |
268 | .stream_name = "WM8731", | 268 | .stream_name = "WM8731", |
269 | .cpu_dai_name = "pxa-i2s", | 269 | .cpu_dai_name = "pxa2xx-i2s", |
270 | .codec_dai_name = "wm8731-hifi", | 270 | .codec_dai_name = "wm8731-hifi", |
271 | .platform_name = "pxa-pcm-audio", | 271 | .platform_name = "pxa-pcm-audio", |
272 | .codec_name = "wm8731-codec.0-001a", | 272 | .codec_name = "wm8731-codec.0-001a", |
diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c index 8dfbcda956ff..b439eee462cb 100644 --- a/sound/soc/pxa/pxa-ssp.c +++ b/sound/soc/pxa/pxa-ssp.c | |||
@@ -758,6 +758,7 @@ static int pxa_ssp_remove(struct snd_soc_dai *dai) | |||
758 | struct ssp_priv *priv = snd_soc_dai_get_drvdata(dai); | 758 | struct ssp_priv *priv = snd_soc_dai_get_drvdata(dai); |
759 | 759 | ||
760 | pxa_ssp_free(priv->ssp); | 760 | pxa_ssp_free(priv->ssp); |
761 | kfree(priv); | ||
761 | return 0; | 762 | return 0; |
762 | } | 763 | } |
763 | 764 | ||
diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c index 9c2bafa112ad..ac51c6d25c42 100644 --- a/sound/soc/pxa/pxa2xx-ac97.c +++ b/sound/soc/pxa/pxa2xx-ac97.c | |||
@@ -24,7 +24,6 @@ | |||
24 | #include <mach/dma.h> | 24 | #include <mach/dma.h> |
25 | #include <mach/audio.h> | 25 | #include <mach/audio.h> |
26 | 26 | ||
27 | #include "pxa2xx-pcm.h" | ||
28 | #include "pxa2xx-ac97.h" | 27 | #include "pxa2xx-ac97.h" |
29 | 28 | ||
30 | static void pxa2xx_ac97_warm_reset(struct snd_ac97 *ac97) | 29 | static void pxa2xx_ac97_warm_reset(struct snd_ac97 *ac97) |
diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c index d1b2ca69fd30..11be5952a506 100644 --- a/sound/soc/pxa/pxa2xx-i2s.c +++ b/sound/soc/pxa/pxa2xx-i2s.c | |||
@@ -398,3 +398,4 @@ module_exit(pxa2xx_i2s_exit); | |||
398 | MODULE_AUTHOR("Liam Girdwood, lrg@slimlogic.co.uk"); | 398 | MODULE_AUTHOR("Liam Girdwood, lrg@slimlogic.co.uk"); |
399 | MODULE_DESCRIPTION("pxa2xx I2S SoC Interface"); | 399 | MODULE_DESCRIPTION("pxa2xx I2S SoC Interface"); |
400 | MODULE_LICENSE("GPL"); | 400 | MODULE_LICENSE("GPL"); |
401 | MODULE_ALIAS("platform:pxa2xx-i2s"); | ||
diff --git a/sound/soc/pxa/saarb.c b/sound/soc/pxa/saarb.c new file mode 100644 index 000000000000..d63cb474b4e1 --- /dev/null +++ b/sound/soc/pxa/saarb.c | |||
@@ -0,0 +1,200 @@ | |||
1 | /* | ||
2 | * saarb.c -- SoC audio for saarb | ||
3 | * | ||
4 | * Copyright (C) 2010 Marvell International Ltd. | ||
5 | * Haojian Zhuang <haojian.zhuang@marvell.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <linux/module.h> | ||
13 | #include <linux/moduleparam.h> | ||
14 | #include <linux/device.h> | ||
15 | #include <linux/clk.h> | ||
16 | #include <linux/i2c.h> | ||
17 | #include <sound/core.h> | ||
18 | #include <sound/pcm.h> | ||
19 | #include <sound/pcm_params.h> | ||
20 | #include <sound/soc.h> | ||
21 | #include <sound/soc-dapm.h> | ||
22 | #include <sound/jack.h> | ||
23 | |||
24 | #include <asm/mach-types.h> | ||
25 | |||
26 | #include "../codecs/88pm860x-codec.h" | ||
27 | #include "pxa-ssp.h" | ||
28 | |||
29 | static int saarb_pm860x_init(struct snd_soc_pcm_runtime *rtd); | ||
30 | |||
31 | static struct platform_device *saarb_snd_device; | ||
32 | |||
33 | static struct snd_soc_jack hs_jack, mic_jack; | ||
34 | |||
35 | static struct snd_soc_jack_pin hs_jack_pins[] = { | ||
36 | { .pin = "Headset Stereophone", .mask = SND_JACK_HEADPHONE, }, | ||
37 | }; | ||
38 | |||
39 | static struct snd_soc_jack_pin mic_jack_pins[] = { | ||
40 | { .pin = "Headset Mic 2", .mask = SND_JACK_MICROPHONE, }, | ||
41 | }; | ||
42 | |||
43 | /* saarb machine dapm widgets */ | ||
44 | static const struct snd_soc_dapm_widget saarb_dapm_widgets[] = { | ||
45 | SND_SOC_DAPM_HP("Headphone Stereophone", NULL), | ||
46 | SND_SOC_DAPM_LINE("Lineout Out 1", NULL), | ||
47 | SND_SOC_DAPM_LINE("Lineout Out 2", NULL), | ||
48 | SND_SOC_DAPM_SPK("Ext Speaker", NULL), | ||
49 | SND_SOC_DAPM_MIC("Ext Mic 1", NULL), | ||
50 | SND_SOC_DAPM_MIC("Headset Mic", NULL), | ||
51 | SND_SOC_DAPM_MIC("Ext Mic 3", NULL), | ||
52 | }; | ||
53 | |||
54 | /* saarb machine audio map */ | ||
55 | static const struct snd_soc_dapm_route audio_map[] = { | ||
56 | {"Headset Stereophone", NULL, "HS1"}, | ||
57 | {"Headset Stereophone", NULL, "HS2"}, | ||
58 | |||
59 | {"Ext Speaker", NULL, "LSP"}, | ||
60 | {"Ext Speaker", NULL, "LSN"}, | ||
61 | |||
62 | {"Lineout Out 1", NULL, "LINEOUT1"}, | ||
63 | {"Lineout Out 2", NULL, "LINEOUT2"}, | ||
64 | |||
65 | {"MIC1P", NULL, "Mic1 Bias"}, | ||
66 | {"MIC1N", NULL, "Mic1 Bias"}, | ||
67 | {"Mic1 Bias", NULL, "Ext Mic 1"}, | ||
68 | |||
69 | {"MIC2P", NULL, "Mic1 Bias"}, | ||
70 | {"MIC2N", NULL, "Mic1 Bias"}, | ||
71 | {"Mic1 Bias", NULL, "Headset Mic 2"}, | ||
72 | |||
73 | {"MIC3P", NULL, "Mic3 Bias"}, | ||
74 | {"MIC3N", NULL, "Mic3 Bias"}, | ||
75 | {"Mic3 Bias", NULL, "Ext Mic 3"}, | ||
76 | }; | ||
77 | |||
78 | static int saarb_i2s_hw_params(struct snd_pcm_substream *substream, | ||
79 | struct snd_pcm_hw_params *params) | ||
80 | { | ||
81 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
82 | struct snd_soc_dai *codec_dai = rtd->codec_dai; | ||
83 | struct snd_soc_dai *cpu_dai = rtd->cpu_dai; | ||
84 | int width = snd_pcm_format_physical_width(params_format(params)); | ||
85 | int ret; | ||
86 | |||
87 | ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_NET_PLL, 0, | ||
88 | PM860X_CLK_DIR_OUT); | ||
89 | if (ret < 0) | ||
90 | return ret; | ||
91 | |||
92 | ret = snd_soc_dai_set_sysclk(codec_dai, 0, 0, PM860X_CLK_DIR_OUT); | ||
93 | if (ret < 0) | ||
94 | return ret; | ||
95 | |||
96 | ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | | ||
97 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); | ||
98 | if (ret < 0) | ||
99 | return ret; | ||
100 | ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | | ||
101 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); | ||
102 | if (ret < 0) | ||
103 | return ret; | ||
104 | |||
105 | ret = snd_soc_dai_set_tdm_slot(cpu_dai, 3, 3, 2, width); | ||
106 | |||
107 | return ret; | ||
108 | } | ||
109 | |||
110 | static struct snd_soc_ops saarb_i2s_ops = { | ||
111 | .hw_params = saarb_i2s_hw_params, | ||
112 | }; | ||
113 | |||
114 | static struct snd_soc_dai_link saarb_dai[] = { | ||
115 | { | ||
116 | .name = "88PM860x I2S", | ||
117 | .stream_name = "I2S Audio", | ||
118 | .cpu_dai_name = "pxa-ssp-dai.1", | ||
119 | .codec_dai_name = "88pm860x-i2s", | ||
120 | .platform_name = "pxa-pcm-audio", | ||
121 | .codec_name = "88pm860x-codec", | ||
122 | .init = saarb_pm860x_init, | ||
123 | .ops = &saarb_i2s_ops, | ||
124 | }, | ||
125 | }; | ||
126 | |||
127 | static struct snd_soc_card snd_soc_card_saarb = { | ||
128 | .name = "Saarb", | ||
129 | .dai_link = saarb_dai, | ||
130 | .num_links = ARRAY_SIZE(saarb_dai), | ||
131 | }; | ||
132 | |||
133 | static int saarb_pm860x_init(struct snd_soc_pcm_runtime *rtd) | ||
134 | { | ||
135 | struct snd_soc_codec *codec = rtd->codec; | ||
136 | int ret; | ||
137 | |||
138 | snd_soc_dapm_new_controls(codec, saarb_dapm_widgets, | ||
139 | ARRAY_SIZE(saarb_dapm_widgets)); | ||
140 | snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); | ||
141 | |||
142 | /* connected pins */ | ||
143 | snd_soc_dapm_enable_pin(codec, "Ext Speaker"); | ||
144 | snd_soc_dapm_enable_pin(codec, "Ext Mic 1"); | ||
145 | snd_soc_dapm_enable_pin(codec, "Ext Mic 3"); | ||
146 | snd_soc_dapm_disable_pin(codec, "Headset Mic 2"); | ||
147 | snd_soc_dapm_disable_pin(codec, "Headset Stereophone"); | ||
148 | |||
149 | ret = snd_soc_dapm_sync(codec); | ||
150 | if (ret) | ||
151 | return ret; | ||
152 | |||
153 | /* Headset jack detection */ | ||
154 | snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE | ||
155 | | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2, | ||
156 | &hs_jack); | ||
157 | snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pins), | ||
158 | hs_jack_pins); | ||
159 | snd_soc_jack_new(codec, "Microphone Jack", SND_JACK_MICROPHONE, | ||
160 | &mic_jack); | ||
161 | snd_soc_jack_add_pins(&mic_jack, ARRAY_SIZE(mic_jack_pins), | ||
162 | mic_jack_pins); | ||
163 | |||
164 | /* headphone, microphone detection & headset short detection */ | ||
165 | pm860x_hs_jack_detect(codec, &hs_jack, SND_JACK_HEADPHONE, | ||
166 | SND_JACK_BTN_0, SND_JACK_BTN_1, SND_JACK_BTN_2); | ||
167 | pm860x_mic_jack_detect(codec, &hs_jack, SND_JACK_MICROPHONE); | ||
168 | return 0; | ||
169 | } | ||
170 | |||
171 | static int __init saarb_init(void) | ||
172 | { | ||
173 | int ret; | ||
174 | |||
175 | if (!machine_is_saarb()) | ||
176 | return -ENODEV; | ||
177 | saarb_snd_device = platform_device_alloc("soc-audio", -1); | ||
178 | if (!saarb_snd_device) | ||
179 | return -ENOMEM; | ||
180 | |||
181 | platform_set_drvdata(saarb_snd_device, &snd_soc_card_saarb); | ||
182 | |||
183 | ret = platform_device_add(saarb_snd_device); | ||
184 | if (ret) | ||
185 | platform_device_put(saarb_snd_device); | ||
186 | |||
187 | return ret; | ||
188 | } | ||
189 | |||
190 | static void __exit saarb_exit(void) | ||
191 | { | ||
192 | platform_device_unregister(saarb_snd_device); | ||
193 | } | ||
194 | |||
195 | module_init(saarb_init); | ||
196 | module_exit(saarb_exit); | ||
197 | |||
198 | MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>"); | ||
199 | MODULE_DESCRIPTION("ALSA SoC 88PM860x Saarb"); | ||
200 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/pxa/tavorevb3.c b/sound/soc/pxa/tavorevb3.c new file mode 100644 index 000000000000..248c283fc4df --- /dev/null +++ b/sound/soc/pxa/tavorevb3.c | |||
@@ -0,0 +1,200 @@ | |||
1 | /* | ||
2 | * tavorevb3.c -- SoC audio for Tavor EVB3 | ||
3 | * | ||
4 | * Copyright (C) 2010 Marvell International Ltd. | ||
5 | * Haojian Zhuang <haojian.zhuang@marvell.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <linux/module.h> | ||
13 | #include <linux/moduleparam.h> | ||
14 | #include <linux/device.h> | ||
15 | #include <linux/clk.h> | ||
16 | #include <linux/i2c.h> | ||
17 | #include <sound/core.h> | ||
18 | #include <sound/pcm.h> | ||
19 | #include <sound/pcm_params.h> | ||
20 | #include <sound/soc.h> | ||
21 | #include <sound/soc-dapm.h> | ||
22 | #include <sound/jack.h> | ||
23 | |||
24 | #include <asm/mach-types.h> | ||
25 | |||
26 | #include "../codecs/88pm860x-codec.h" | ||
27 | #include "pxa-ssp.h" | ||
28 | |||
29 | static int evb3_pm860x_init(struct snd_soc_pcm_runtime *rtd); | ||
30 | |||
31 | static struct platform_device *evb3_snd_device; | ||
32 | |||
33 | static struct snd_soc_jack hs_jack, mic_jack; | ||
34 | |||
35 | static struct snd_soc_jack_pin hs_jack_pins[] = { | ||
36 | { .pin = "Headset Stereophone", .mask = SND_JACK_HEADPHONE, }, | ||
37 | }; | ||
38 | |||
39 | static struct snd_soc_jack_pin mic_jack_pins[] = { | ||
40 | { .pin = "Headset Mic 2", .mask = SND_JACK_MICROPHONE, }, | ||
41 | }; | ||
42 | |||
43 | /* tavorevb3 machine dapm widgets */ | ||
44 | static const struct snd_soc_dapm_widget evb3_dapm_widgets[] = { | ||
45 | SND_SOC_DAPM_HP("Headset Stereophone", NULL), | ||
46 | SND_SOC_DAPM_LINE("Lineout Out 1", NULL), | ||
47 | SND_SOC_DAPM_LINE("Lineout Out 2", NULL), | ||
48 | SND_SOC_DAPM_SPK("Ext Speaker", NULL), | ||
49 | SND_SOC_DAPM_MIC("Ext Mic 1", NULL), | ||
50 | SND_SOC_DAPM_MIC("Headset Mic 2", NULL), | ||
51 | SND_SOC_DAPM_MIC("Ext Mic 3", NULL), | ||
52 | }; | ||
53 | |||
54 | /* tavorevb3 machine audio map */ | ||
55 | static const struct snd_soc_dapm_route audio_map[] = { | ||
56 | {"Headset Stereophone", NULL, "HS1"}, | ||
57 | {"Headset Stereophone", NULL, "HS2"}, | ||
58 | |||
59 | {"Ext Speaker", NULL, "LSP"}, | ||
60 | {"Ext Speaker", NULL, "LSN"}, | ||
61 | |||
62 | {"Lineout Out 1", NULL, "LINEOUT1"}, | ||
63 | {"Lineout Out 2", NULL, "LINEOUT2"}, | ||
64 | |||
65 | {"MIC1P", NULL, "Mic1 Bias"}, | ||
66 | {"MIC1N", NULL, "Mic1 Bias"}, | ||
67 | {"Mic1 Bias", NULL, "Ext Mic 1"}, | ||
68 | |||
69 | {"MIC2P", NULL, "Mic1 Bias"}, | ||
70 | {"MIC2N", NULL, "Mic1 Bias"}, | ||
71 | {"Mic1 Bias", NULL, "Headset Mic 2"}, | ||
72 | |||
73 | {"MIC3P", NULL, "Mic3 Bias"}, | ||
74 | {"MIC3N", NULL, "Mic3 Bias"}, | ||
75 | {"Mic3 Bias", NULL, "Ext Mic 3"}, | ||
76 | }; | ||
77 | |||
78 | static int evb3_i2s_hw_params(struct snd_pcm_substream *substream, | ||
79 | struct snd_pcm_hw_params *params) | ||
80 | { | ||
81 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
82 | struct snd_soc_dai *codec_dai = rtd->codec_dai; | ||
83 | struct snd_soc_dai *cpu_dai = rtd->cpu_dai; | ||
84 | int width = snd_pcm_format_physical_width(params_format(params)); | ||
85 | int ret; | ||
86 | |||
87 | ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_NET_PLL, 0, | ||
88 | PM860X_CLK_DIR_OUT); | ||
89 | if (ret < 0) | ||
90 | return ret; | ||
91 | |||
92 | ret = snd_soc_dai_set_sysclk(codec_dai, 0, 0, PM860X_CLK_DIR_OUT); | ||
93 | if (ret < 0) | ||
94 | return ret; | ||
95 | |||
96 | ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | | ||
97 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); | ||
98 | if (ret < 0) | ||
99 | return ret; | ||
100 | |||
101 | ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | | ||
102 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); | ||
103 | if (ret < 0) | ||
104 | return ret; | ||
105 | |||
106 | ret = snd_soc_dai_set_tdm_slot(cpu_dai, 3, 3, 2, width); | ||
107 | return ret; | ||
108 | } | ||
109 | |||
110 | static struct snd_soc_ops evb3_i2s_ops = { | ||
111 | .hw_params = evb3_i2s_hw_params, | ||
112 | }; | ||
113 | |||
114 | static struct snd_soc_dai_link evb3_dai[] = { | ||
115 | { | ||
116 | .name = "88PM860x I2S", | ||
117 | .stream_name = "I2S Audio", | ||
118 | .cpu_dai_name = "pxa-ssp-dai.1", | ||
119 | .codec_dai_name = "88pm860x-i2s", | ||
120 | .platform_name = "pxa-pcm-audio", | ||
121 | .codec_name = "88pm860x-codec", | ||
122 | .init = evb3_pm860x_init, | ||
123 | .ops = &evb3_i2s_ops, | ||
124 | }, | ||
125 | }; | ||
126 | |||
127 | static struct snd_soc_card snd_soc_card_evb3 = { | ||
128 | .name = "Tavor EVB3", | ||
129 | .dai_link = evb3_dai, | ||
130 | .num_links = ARRAY_SIZE(evb3_dai), | ||
131 | }; | ||
132 | |||
133 | static int evb3_pm860x_init(struct snd_soc_pcm_runtime *rtd) | ||
134 | { | ||
135 | struct snd_soc_codec *codec = rtd->codec; | ||
136 | int ret; | ||
137 | |||
138 | snd_soc_dapm_new_controls(codec, evb3_dapm_widgets, | ||
139 | ARRAY_SIZE(evb3_dapm_widgets)); | ||
140 | snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); | ||
141 | |||
142 | /* connected pins */ | ||
143 | snd_soc_dapm_enable_pin(codec, "Ext Speaker"); | ||
144 | snd_soc_dapm_enable_pin(codec, "Ext Mic 1"); | ||
145 | snd_soc_dapm_enable_pin(codec, "Ext Mic 3"); | ||
146 | snd_soc_dapm_disable_pin(codec, "Headset Mic 2"); | ||
147 | snd_soc_dapm_disable_pin(codec, "Headset Stereophone"); | ||
148 | |||
149 | ret = snd_soc_dapm_sync(codec); | ||
150 | if (ret) | ||
151 | return ret; | ||
152 | |||
153 | /* Headset jack detection */ | ||
154 | snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE | ||
155 | | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2, | ||
156 | &hs_jack); | ||
157 | snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pins), | ||
158 | hs_jack_pins); | ||
159 | snd_soc_jack_new(codec, "Microphone Jack", SND_JACK_MICROPHONE, | ||
160 | &mic_jack); | ||
161 | snd_soc_jack_add_pins(&mic_jack, ARRAY_SIZE(mic_jack_pins), | ||
162 | mic_jack_pins); | ||
163 | |||
164 | /* headphone, microphone detection & headset short detection */ | ||
165 | pm860x_hs_jack_detect(codec, &hs_jack, SND_JACK_HEADPHONE, | ||
166 | SND_JACK_BTN_0, SND_JACK_BTN_1, SND_JACK_BTN_2); | ||
167 | pm860x_mic_jack_detect(codec, &hs_jack, SND_JACK_MICROPHONE); | ||
168 | return 0; | ||
169 | } | ||
170 | |||
171 | static int __init tavorevb3_init(void) | ||
172 | { | ||
173 | int ret; | ||
174 | |||
175 | if (!machine_is_tavorevb3()) | ||
176 | return -ENODEV; | ||
177 | evb3_snd_device = platform_device_alloc("soc-audio", -1); | ||
178 | if (!evb3_snd_device) | ||
179 | return -ENOMEM; | ||
180 | |||
181 | platform_set_drvdata(evb3_snd_device, &snd_soc_card_evb3); | ||
182 | |||
183 | ret = platform_device_add(evb3_snd_device); | ||
184 | if (ret) | ||
185 | platform_device_put(evb3_snd_device); | ||
186 | |||
187 | return ret; | ||
188 | } | ||
189 | |||
190 | static void __exit tavorevb3_exit(void) | ||
191 | { | ||
192 | platform_device_unregister(evb3_snd_device); | ||
193 | } | ||
194 | |||
195 | module_init(tavorevb3_init); | ||
196 | module_exit(tavorevb3_exit); | ||
197 | |||
198 | MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>"); | ||
199 | MODULE_DESCRIPTION("ALSA SoC 88PM860x Tavor EVB3"); | ||
200 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/pxa/z2.c b/sound/soc/pxa/z2.c index 704f74b56ab6..4cc841b44182 100644 --- a/sound/soc/pxa/z2.c +++ b/sound/soc/pxa/z2.c | |||
@@ -189,7 +189,7 @@ static struct snd_soc_ops z2_ops = { | |||
189 | static struct snd_soc_dai_link z2_dai = { | 189 | static struct snd_soc_dai_link z2_dai = { |
190 | .name = "wm8750", | 190 | .name = "wm8750", |
191 | .stream_name = "WM8750", | 191 | .stream_name = "WM8750", |
192 | .cpu_dai_name = "pxa-i2s", | 192 | .cpu_dai_name = "pxa2xx-i2s", |
193 | .codec_dai_name = "wm8750-hifi", | 193 | .codec_dai_name = "wm8750-hifi", |
194 | .platform_name = "pxa-pcm-audio", | 194 | .platform_name = "pxa-pcm-audio", |
195 | .codec_name = "wm8750-codec.0-001a", | 195 | .codec_name = "wm8750-codec.0-001a", |
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 3d480eb3555f..65352c7d4b7f 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c | |||
@@ -2916,7 +2916,7 @@ int snd_soc_register_dais(struct device *dev, | |||
2916 | struct snd_soc_dai *dai; | 2916 | struct snd_soc_dai *dai; |
2917 | int i, ret = 0; | 2917 | int i, ret = 0; |
2918 | 2918 | ||
2919 | dev_dbg(dev, "dai register %s #%d\n", dev_name(dev), count); | 2919 | dev_dbg(dev, "dai register %s #%Zu\n", dev_name(dev), count); |
2920 | 2920 | ||
2921 | for (i = 0; i < count; i++) { | 2921 | for (i = 0; i < count; i++) { |
2922 | 2922 | ||
@@ -3122,10 +3122,12 @@ int snd_soc_register_codec(struct device *dev, | |||
3122 | fixup_codec_formats(&dai_drv[i].capture); | 3122 | fixup_codec_formats(&dai_drv[i].capture); |
3123 | } | 3123 | } |
3124 | 3124 | ||
3125 | /* register DAIs */ | 3125 | /* register any DAIs */ |
3126 | ret = snd_soc_register_dais(dev, dai_drv, num_dai); | 3126 | if (num_dai) { |
3127 | if (ret < 0) | 3127 | ret = snd_soc_register_dais(dev, dai_drv, num_dai); |
3128 | if (ret < 0) | ||
3128 | goto error; | 3129 | goto error; |
3130 | } | ||
3129 | 3131 | ||
3130 | mutex_lock(&client_mutex); | 3132 | mutex_lock(&client_mutex); |
3131 | list_add(&codec->list, &codec_list); | 3133 | list_add(&codec->list, &codec_list); |
@@ -3164,8 +3166,9 @@ void snd_soc_unregister_codec(struct device *dev) | |||
3164 | return; | 3166 | return; |
3165 | 3167 | ||
3166 | found: | 3168 | found: |
3167 | for (i = 0; i < codec->num_dai; i++) | 3169 | if (codec->num_dai) |
3168 | snd_soc_unregister_dai(dev); | 3170 | for (i = 0; i < codec->num_dai; i++) |
3171 | snd_soc_unregister_dai(dev); | ||
3169 | 3172 | ||
3170 | mutex_lock(&client_mutex); | 3173 | mutex_lock(&client_mutex); |
3171 | list_del(&codec->list); | 3174 | list_del(&codec->list); |