summaryrefslogtreecommitdiff
path: root/target/linux/olpc/patches/110-olpc_sound.patch
blob: 421ab283bc7237de84b96537d8cad1ffcf6e3f93 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
diff --git a/include/sound/ac97_codec.h b/include/sound/ac97_codec.h
index 246ac23..874a2c8 100644
--- a/include/sound/ac97_codec.h
+++ b/include/sound/ac97_codec.h
@@ -281,10 +281,12 @@
 /* specific - Analog Devices */
 #define AC97_AD_TEST		0x5a	/* test register */
 #define AC97_AD_TEST2		0x5c	/* undocumented test register 2 */
+#define AC97_AD_HPFD_SHIFT	12	/* High Pass Filter Disable */
 #define AC97_AD_CODEC_CFG	0x70	/* codec configuration */
 #define AC97_AD_JACK_SPDIF	0x72	/* Jack Sense & S/PDIF */
 #define AC97_AD_SERIAL_CFG	0x74	/* Serial Configuration */
 #define AC97_AD_MISC		0x76	/* Misc Control Bits */
+#define AC97_AD_VREFD_SHIFT	2	/* V_REFOUT Disable (AD1888) */
 
 /* specific - Cirrus Logic */
 #define AC97_CSR_ACMODE		0x5e	/* AC Mode Register */
diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c
index bbed644..090e852 100644
--- a/sound/pci/ac97/ac97_codec.c
+++ b/sound/pci/ac97/ac97_codec.c
@@ -49,7 +49,7 @@ module_param(enable_loopback, bool, 0444);
 MODULE_PARM_DESC(enable_loopback, "Enable AC97 ADC/DAC Loopback Control");
 
 #ifdef CONFIG_SND_AC97_POWER_SAVE
-static int power_save;
+static int power_save = 1;
 module_param(power_save, bool, 0644);
 MODULE_PARM_DESC(power_save, "Enable AC97 power-saving control");
 #endif
diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c
index 581ebba..06637f7 100644
--- a/sound/pci/ac97/ac97_patch.c
+++ b/sound/pci/ac97/ac97_patch.c
@@ -1973,8 +1973,9 @@ static const struct snd_kcontrol_new snd_ac97_ad1888_controls[] = {
 		.get = snd_ac97_ad1888_lohpsel_get,
 		.put = snd_ac97_ad1888_lohpsel_put
 	},
-	AC97_SINGLE("V_REFOUT Enable", AC97_AD_MISC, 2, 1, 1),
-	AC97_SINGLE("High Pass Filter Enable", AC97_AD_TEST2, 12, 1, 1),
+	AC97_SINGLE("V_REFOUT Enable", AC97_AD_MISC, AC97_AD_VREFD_SHIFT, 1, 1),
+	AC97_SINGLE("High Pass Filter Enable", AC97_AD_TEST2,
+			AC97_AD_HPFD_SHIFT, 1, 1),
 	AC97_SINGLE("Spread Front to Surround and Center/LFE", AC97_AD_MISC, 7, 1, 0),
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
diff --git a/sound/pci/cs5535audio/Makefile b/sound/pci/cs5535audio/Makefile
index ad947b4..3866c4d 100644
--- a/sound/pci/cs5535audio/Makefile
+++ b/sound/pci/cs5535audio/Makefile
@@ -8,5 +8,9 @@ ifeq ($(CONFIG_PM),y)
 snd-cs5535audio-objs += cs5535audio_pm.o
 endif
 
+ifdef CONFIG_OLPC
+snd-cs5535audio-objs += cs5535audio_olpc.o
+endif
+
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_CS5535AUDIO) += snd-cs5535audio.o
diff --git a/sound/pci/cs5535audio/cs5535audio.c b/sound/pci/cs5535audio/cs5535audio.c
index b8e75ef..389d9da 100644
--- a/sound/pci/cs5535audio/cs5535audio.c
+++ b/sound/pci/cs5535audio/cs5535audio.c
@@ -145,7 +145,7 @@ static unsigned short snd_cs5535audio_ac97_codec_read(struct snd_ac97 *ac97,
 	return snd_cs5535audio_codec_read(cs5535au, reg);
 }
 
-static int snd_cs5535audio_mixer(struct cs5535audio *cs5535au)
+static int __devinit snd_cs5535audio_mixer(struct cs5535audio *cs5535au)
 {
 	struct snd_card *card = cs5535au->card;
 	struct snd_ac97_bus *pbus;
@@ -160,10 +160,14 @@ static int snd_cs5535audio_mixer(struct cs5535audio *cs5535au)
 		return err;
 
 	memset(&ac97, 0, sizeof(ac97));
-	ac97.scaps = AC97_SCAP_AUDIO|AC97_SCAP_SKIP_MODEM;
+	ac97.scaps = AC97_SCAP_AUDIO | AC97_SCAP_SKIP_MODEM
+			| AC97_SCAP_POWER_SAVE;
 	ac97.private_data = cs5535au;
 	ac97.pci = cs5535au->pci;
 
+	/* olpc_prequirks is dummied out if not olpc */
+	olpc_prequirks(card, &ac97);
+
 	if ((err = snd_ac97_mixer(pbus, &ac97, &cs5535au->ac97)) < 0) {
 		snd_printk(KERN_ERR "mixer failed\n");
 		return err;
@@ -171,6 +175,12 @@ static int snd_cs5535audio_mixer(struct cs5535audio *cs5535au)
 
 	snd_ac97_tune_hardware(cs5535au->ac97, ac97_quirks, ac97_quirk);
 
+	/* olpc_quirks is dummied out if not olpc */
+	if (( err = olpc_quirks(card, cs5535au->ac97)) < 0) {
+		snd_printk(KERN_ERR "olpc quirks failed\n");
+		return err;
+	}
+
 	return 0;
 }
 
@@ -206,7 +216,6 @@ static void process_bm1_irq(struct cs5535audio *cs5535au)
 static irqreturn_t snd_cs5535audio_interrupt(int irq, void *dev_id)
 {
 	u16 acc_irq_stat;
-	u8 bm_stat;
 	unsigned char count;
 	struct cs5535audio *cs5535au = dev_id;
 
@@ -217,7 +226,7 @@ static irqreturn_t snd_cs5535audio_interrupt(int irq, void *dev_id)
 
 	if (!acc_irq_stat)
 		return IRQ_NONE;
-	for (count = 0; count < 10; count++) {
+	for (count = 0; count < 4; count++) {
 		if (acc_irq_stat & (1 << count)) {
 			switch (count) {
 			case IRQ_STS:
@@ -232,26 +241,9 @@ static irqreturn_t snd_cs5535audio_interrupt(int irq, void *dev_id)
 			case BM1_IRQ_STS:
 				process_bm1_irq(cs5535au);
 				break;
-			case BM2_IRQ_STS:
-				bm_stat = cs_readb(cs5535au, ACC_BM2_STATUS);
-				break;
-			case BM3_IRQ_STS:
-				bm_stat = cs_readb(cs5535au, ACC_BM3_STATUS);
-				break;
-			case BM4_IRQ_STS:
-				bm_stat = cs_readb(cs5535au, ACC_BM4_STATUS);
-				break;
-			case BM5_IRQ_STS:
-				bm_stat = cs_readb(cs5535au, ACC_BM5_STATUS);
-				break;
-			case BM6_IRQ_STS:
-				bm_stat = cs_readb(cs5535au, ACC_BM6_STATUS);
-				break;
-			case BM7_IRQ_STS:
-				bm_stat = cs_readb(cs5535au, ACC_BM7_STATUS);
-				break;
 			default:
-				snd_printk(KERN_ERR "Unexpected irq src\n");
+				snd_printk(KERN_ERR "Unexpected irq src: "
+						"0x%x\n", acc_irq_stat);
 				break;
 			}
 		}
diff --git a/sound/pci/cs5535audio/cs5535audio.h b/sound/pci/cs5535audio/cs5535audio.h
index 4fd1f31..ff82f10 100644
--- a/sound/pci/cs5535audio/cs5535audio.h
+++ b/sound/pci/cs5535audio/cs5535audio.h
@@ -16,57 +16,28 @@
 #define ACC_IRQ_STATUS			0x12
 #define ACC_BM0_CMD			0x20
 #define ACC_BM1_CMD			0x28
-#define ACC_BM2_CMD			0x30
-#define ACC_BM3_CMD			0x38
-#define ACC_BM4_CMD			0x40
-#define ACC_BM5_CMD			0x48
-#define ACC_BM6_CMD			0x50
-#define ACC_BM7_CMD			0x58
 #define ACC_BM0_PRD			0x24
 #define ACC_BM1_PRD			0x2C
-#define ACC_BM2_PRD			0x34
-#define ACC_BM3_PRD			0x3C
-#define ACC_BM4_PRD			0x44
-#define ACC_BM5_PRD			0x4C
-#define ACC_BM6_PRD			0x54
-#define ACC_BM7_PRD			0x5C
 #define ACC_BM0_STATUS			0x21
 #define ACC_BM1_STATUS			0x29
-#define ACC_BM2_STATUS			0x31
-#define ACC_BM3_STATUS			0x39
-#define ACC_BM4_STATUS			0x41
-#define ACC_BM5_STATUS			0x49
-#define ACC_BM6_STATUS			0x51
-#define ACC_BM7_STATUS			0x59
 #define ACC_BM0_PNTR			0x60
 #define ACC_BM1_PNTR			0x64
-#define ACC_BM2_PNTR			0x68
-#define ACC_BM3_PNTR			0x6C
-#define ACC_BM4_PNTR			0x70
-#define ACC_BM5_PNTR			0x74
-#define ACC_BM6_PNTR			0x78
-#define ACC_BM7_PNTR			0x7C
+
 /* acc_codec bar0 reg bits */
 /* ACC_IRQ_STATUS */
 #define IRQ_STS 			0
 #define WU_IRQ_STS 			1
 #define BM0_IRQ_STS 			2
 #define BM1_IRQ_STS 			3
-#define BM2_IRQ_STS 			4
-#define BM3_IRQ_STS 			5
-#define BM4_IRQ_STS 			6
-#define BM5_IRQ_STS		 	7
-#define BM6_IRQ_STS 			8
-#define BM7_IRQ_STS 			9
 /* ACC_BMX_STATUS */
 #define EOP				(1<<0)
 #define BM_EOP_ERR			(1<<1)
 /* ACC_BMX_CTL */
-#define BM_CTL_EN			0x00000001
-#define BM_CTL_PAUSE			0x00000011
-#define BM_CTL_DIS			0x00000000
-#define BM_CTL_BYTE_ORD_LE		0x00000000
-#define BM_CTL_BYTE_ORD_BE		0x00000100
+#define BM_CTL_EN			0x01
+#define BM_CTL_PAUSE			0x03
+#define BM_CTL_DIS			0x00
+#define BM_CTL_BYTE_ORD_LE		0x00
+#define BM_CTL_BYTE_ORD_BE		0x04
 /* cs5535 specific ac97 codec register defines */
 #define CMD_MASK			0xFF00FFFF
 #define CMD_NEW				0x00010000
@@ -106,8 +77,8 @@ struct cs5535audio_dma {
 	struct snd_pcm_substream *substream;
 	unsigned int buf_addr, buf_bytes;
 	unsigned int period_bytes, periods;
-	int suspended;
 	u32 saved_prd;
+	int pcm_open_flag;
 };
 
 struct cs5535audio {
@@ -123,8 +94,21 @@ struct cs5535audio {
 	struct cs5535audio_dma dmas[NUM_CS5535AUDIO_DMAS];
 };
 
+#ifdef CONFIG_PM
 int snd_cs5535audio_suspend(struct pci_dev *pci, pm_message_t state);
 int snd_cs5535audio_resume(struct pci_dev *pci);
+#endif
+
+#ifdef CONFIG_OLPC
+void olpc_prequirks(struct snd_card *card, struct snd_ac97_template *ac97) __devinit;
+int olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97) __devinit;
+int olpc_ai_enable(struct snd_ac97 *ac97, u8 val);
+#else
+#define olpc_prequirks(arg,arg2)	do {} while (0)
+#define olpc_quirks(arg,arg2)		(0)
+#define olpc_ai_enable(a, v) (0)
+#endif
+
 int __devinit snd_cs5535audio_pcm(struct cs5535audio *cs5535audio);
 
 #endif /* __SOUND_CS5535AUDIO_H */
diff --git a/sound/pci/cs5535audio/cs5535audio_pcm.c b/sound/pci/cs5535audio/cs5535audio_pcm.c
index 5450a9e..d23f8ea 100644
--- a/sound/pci/cs5535audio/cs5535audio_pcm.c
+++ b/sound/pci/cs5535audio/cs5535audio_pcm.c
@@ -164,6 +164,7 @@ static int cs5535audio_build_dma_packets(struct cs5535audio *cs5535au,
 	jmpprd_addr = cpu_to_le32(lastdesc->addr +
 				  (sizeof(struct cs5535audio_dma_desc)*periods));
 
+	dma->substream = substream;
 	dma->period_bytes = period_bytes;
 	dma->periods = periods;
 	spin_lock_irq(&cs5535au->reg_lock);
@@ -241,6 +242,7 @@ static void cs5535audio_clear_dma_packets(struct cs5535audio *cs5535au,
 {
 	snd_dma_free_pages(&dma->desc_buf);
 	dma->desc_buf.area = NULL;
+	dma->substream = NULL;
 }
 
 static int snd_cs5535audio_hw_params(struct snd_pcm_substream *substream,
@@ -260,6 +262,9 @@ static int snd_cs5535audio_hw_params(struct snd_pcm_substream *substream,
 	err = cs5535audio_build_dma_packets(cs5535au, dma, substream,
 					    params_periods(hw_params),
 					    params_period_bytes(hw_params));
+	if (!err)
+		dma->pcm_open_flag = 1;
+
 	return err;
 }
 
@@ -268,6 +273,15 @@ static int snd_cs5535audio_hw_free(struct snd_pcm_substream *substream)
 	struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
 	struct cs5535audio_dma *dma = substream->runtime->private_data;
 
+	if (dma->pcm_open_flag) {
+		if (substream == cs5535au->playback_substream)
+			snd_ac97_update_power(cs5535au->ac97,
+					AC97_PCM_FRONT_DAC_RATE, 0);
+		else
+			snd_ac97_update_power(cs5535au->ac97,
+					AC97_PCM_LR_ADC_RATE, 0);
+		dma->pcm_open_flag = 0;
+	}
 	cs5535audio_clear_dma_packets(cs5535au, dma, substream);
 	return snd_pcm_lib_free_pages(substream);
 }
@@ -298,14 +312,12 @@ static int snd_cs5535audio_trigger(struct snd_pcm_substream *substream, int cmd)
 		break;
 	case SNDRV_PCM_TRIGGER_RESUME:
 		dma->ops->enable_dma(cs5535au);
-		dma->suspended = 0;
 		break;
 	case SNDRV_PCM_TRIGGER_STOP:
 		dma->ops->disable_dma(cs5535au);
 		break;
 	case SNDRV_PCM_TRIGGER_SUSPEND:
 		dma->ops->disable_dma(cs5535au);
-		dma->suspended = 1;
 		break;
 	default:
 		snd_printk(KERN_ERR "unhandled trigger\n");
@@ -344,6 +356,7 @@ static int snd_cs5535audio_capture_open(struct snd_pcm_substream *substream)
 	int err;
 	struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
 	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_ac97 *ac97 = cs5535au->ac97;
 
 	runtime->hw = snd_cs5535audio_capture;
 	cs5535au->capture_substream = substream;
@@ -352,11 +365,29 @@ static int snd_cs5535audio_capture_open(struct snd_pcm_substream *substream)
 	if ((err = snd_pcm_hw_constraint_integer(runtime,
 					 SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
 		return err;
-	return 0;
+
+#ifdef CONFIG_OLPC
+	/* Disable Analog Input */
+	olpc_ai_enable(ac97, 0);
+	/* Enable V_ref bias while recording. */
+	snd_ac97_update_bits(ac97, AC97_AD_MISC, 1<<AC97_AD_VREFD_SHIFT, 0);
+#endif
+	return err;
 }
 
 static int snd_cs5535audio_capture_close(struct snd_pcm_substream *substream)
 {
+#ifdef CONFIG_OLPC
+	struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
+	struct snd_ac97 *ac97 = cs5535au->ac97;
+
+	/* Disable Analog Input */
+	olpc_ai_enable(ac97, 0);
+	/* Disable V_ref bias. */
+	snd_ac97_update_bits(ac97, AC97_AD_MISC, 1<<AC97_AD_VREFD_SHIFT,
+			1<<AC97_AD_VREFD_SHIFT);
+#endif
+
 	return 0;
 }
 
diff --git a/sound/pci/cs5535audio/cs5535audio_pm.c b/sound/pci/cs5535audio/cs5535audio_pm.c
index 3e4d198..838708f 100644
--- a/sound/pci/cs5535audio/cs5535audio_pm.c
+++ b/sound/pci/cs5535audio/cs5535audio_pm.c
@@ -64,18 +64,21 @@ int snd_cs5535audio_suspend(struct pci_dev *pci, pm_message_t state)
 	int i;
 
 	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+	snd_pcm_suspend_all(cs5535au->pcm);
+	snd_ac97_suspend(cs5535au->ac97);
 	for (i = 0; i < NUM_CS5535AUDIO_DMAS; i++) {
 		struct cs5535audio_dma *dma = &cs5535au->dmas[i];
-		if (dma && dma->substream && !dma->suspended) 
+		if (dma && dma->substream)
 			dma->saved_prd = dma->ops->read_prd(cs5535au);
 	}
-	snd_pcm_suspend_all(cs5535au->pcm);
-	snd_ac97_suspend(cs5535au->ac97);
 	/* save important regs, then disable aclink in hw */
 	snd_cs5535audio_stop_hardware(cs5535au);
 
+	if (pci_save_state(pci)) {
+		printk(KERN_ERR "cs5535audio: pci_save_state failed!\n");
+		return -EIO;
+	}
 	pci_disable_device(pci);
-	pci_save_state(pci);
 	pci_set_power_state(pci, pci_choose_state(pci, state));
 	return 0;
 }
@@ -89,7 +92,12 @@ int snd_cs5535audio_resume(struct pci_dev *pci)
 	int i;
 
 	pci_set_power_state(pci, PCI_D0);
-	pci_restore_state(pci);
+	if (pci_restore_state(pci) < 0) {
+		printk(KERN_ERR "cs5535audio: pci_restore_state failed, "
+		       "disabling device\n");
+		snd_card_disconnect(card);
+		return -EIO;
+	}
 	if (pci_enable_device(pci) < 0) {
 		printk(KERN_ERR "cs5535audio: pci_enable_device failed, "
 		       "disabling device\n");
@@ -112,17 +120,17 @@ int snd_cs5535audio_resume(struct pci_dev *pci)
 	if (!timeout)
 		snd_printk(KERN_ERR "Failure getting AC Link ready\n");
 
-	/* we depend on ac97 to perform the codec power up */
-	snd_ac97_resume(cs5535au->ac97);
 	/* set up rate regs, dma. actual initiation is done in trig */
 	for (i = 0; i < NUM_CS5535AUDIO_DMAS; i++) {
 		struct cs5535audio_dma *dma = &cs5535au->dmas[i];
-		if (dma && dma->substream && dma->suspended) {
+		if (dma && dma->substream) {
 			dma->substream->ops->prepare(dma->substream);
 			dma->ops->setup_prd(cs5535au, dma->saved_prd);
 		}
 	}
-		
+
+	/* we depend on ac97 to perform the codec power up */
+	snd_ac97_resume(cs5535au->ac97);
 	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
 
 	return 0;