aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kvm/trace.h
blob: 1357d7cf4ec86d3d4c05d4ec8d13a701db8ff2d8 (plain) (blame)
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
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
#if !defined(_TRACE_KVM_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_KVM_H

#include <linux/tracepoint.h>

#undef TRACE_SYSTEM
#define TRACE_SYSTEM kvm

/*
 * Tracepoint for guest mode entry.
 */
TRACE_EVENT(kvm_entry,
	TP_PROTO(unsigned int vcpu_id),
	TP_ARGS(vcpu_id),

	TP_STRUCT__entry(
		__field(	unsigned int,	vcpu_id		)
	),

	TP_fast_assign(
		__entry->vcpu_id	= vcpu_id;
	),

	TP_printk("vcpu %u", __entry->vcpu_id)
);

/*
 * Tracepoint for hypercall.
 */
TRACE_EVENT(kvm_hypercall,
	TP_PROTO(unsigned long nr, unsigned long a0, unsigned long a1,
		 unsigned long a2, unsigned long a3),
	TP_ARGS(nr, a0, a1, a2, a3),

	TP_STRUCT__entry(
		__field(	unsigned long, 	nr		)
		__field(	unsigned long,	a0		)
		__field(	unsigned long,	a1		)
		__field(	unsigned long,	a2		)
		__field(	unsigned long,	a3		)
	),

	TP_fast_assign(
		__entry->nr		= nr;
		__entry->a0		= a0;
		__entry->a1		= a1;
		__entry->a2		= a2;
		__entry->a3		= a3;
	),

	TP_printk("nr 0x%lx a0 0x%lx a1 0x%lx a2 0x%lx a3 0x%lx",
		 __entry->nr, __entry->a0, __entry->a1,  __entry->a2,
		 __entry->a3)
);

/*
 * Tracepoint for hypercall.
 */
TRACE_EVENT(kvm_hv_hypercall,
	TP_PROTO(__u16 code, bool fast, __u16 rep_cnt, __u16 rep_idx,
		 __u64 ingpa, __u64 outgpa),
	TP_ARGS(code, fast, rep_cnt, rep_idx, ingpa, outgpa),

	TP_STRUCT__entry(
		__field(	__u16, 		code		)
		__field(	bool,		fast		)
		__field(	__u16,		rep_cnt		)
		__field(	__u16,		rep_idx		)
		__field(	__u64,		ingpa		)
		__field(	__u64,		outgpa		)
	),

	TP_fast_assign(
		__entry->code		= code;
		__entry->fast		= fast;
		__entry->rep_cnt	= rep_cnt;
		__entry->rep_idx	= rep_idx;
		__entry->ingpa		= ingpa;
		__entry->outgpa		= outgpa;
	),

	TP_printk("code 0x%x %s cnt 0x%x idx 0x%x in 0x%llx out 0x%llx",
		  __entry->code, __entry->fast ? "fast" : "slow",
		  __entry->rep_cnt, __entry->rep_idx,  __entry->ingpa,
		  __entry->outgpa)
);

/*
 * Tracepoint for PIO.
 */
TRACE_EVENT(kvm_pio,
	TP_PROTO(unsigned int rw, unsigned int port, unsigned int size,
		 unsigned int count),
	TP_ARGS(rw, port, size, count),

	TP_STRUCT__entry(
		__field(	unsigned int, 	rw		)
		__field(	unsigned int, 	port		)
		__field(	unsigned int, 	size		)
		__field(	unsigned int,	count		)
	),

	TP_fast_assign(
		__entry->rw		= rw;
		__entry->port		= port;
		__entry->size		= size;
		__entry->count		= count;
	),

	TP_printk("pio_%s at 0x%x size %d count %d",
		  __entry->rw ? "write" : "read",
		  __entry->port, __entry->size, __entry->count)
);

/*
 * Tracepoint for cpuid.
 */
TRACE_EVENT(kvm_cpuid,
	TP_PROTO(unsigned int function, unsigned long rax, unsigned long rbx,
		 unsigned long rcx, unsigned long rdx),
	TP_ARGS(function, rax, rbx, rcx, rdx),

	TP_STRUCT__entry(
		__field(	unsigned int,	function	)
		__field(	unsigned long,	rax		)
		__field(	unsigned long,	rbx		)
		__field(	unsigned long,	rcx		)
		__field(	unsigned long,	rdx		)
	),

	TP_fast_assign(
		__entry->function	= function;
		__entry->rax		= rax;
		__entry->rbx		= rbx;
		__entry->rcx		= rcx;
		__entry->rdx		= rdx;
	),

	TP_printk("func %x rax %lx rbx %lx rcx %lx rdx %lx",
		  __entry->function, __entry->rax,
		  __entry->rbx, __entry->rcx, __entry->rdx)
);

#define AREG(x) { APIC_##x, "APIC_" #x }

#define kvm_trace_symbol_apic						    \
	AREG(ID), AREG(LVR), AREG(TASKPRI), AREG(ARBPRI), AREG(PROCPRI),    \
	AREG(EOI), AREG(RRR), AREG(LDR), AREG(DFR), AREG(SPIV), AREG(ISR),  \
	AREG(TMR), AREG(IRR), AREG(ESR), AREG(ICR), AREG(ICR2), AREG(LVTT), \
	AREG(LVTTHMR), AREG(LVTPC), AREG(LVT0), AREG(LVT1), AREG(LVTERR),   \
	AREG(TMICT), AREG(TMCCT), AREG(TDCR), AREG(SELF_IPI), AREG(EFEAT),  \
	AREG(ECTRL)
/*
 * Tracepoint for apic access.
 */
TRACE_EVENT(kvm_apic,
	TP_PROTO(unsigned int rw, unsigned int reg, unsigned int val),
	TP_ARGS(rw, reg, val),

	TP_STRUCT__entry(
		__field(	unsigned int,	rw		)
		__field(	unsigned int,	reg		)
		__field(	unsigned int,	val		)
	),

	TP_fast_assign(
		__entry->rw		= rw;
		__entry->reg		= reg;
		__entry->val		= val;
	),

	TP_printk("apic_%s %s = 0x%x",
		  __entry->rw ? "write" : "read",
		  __print_symbolic(__entry->reg, kvm_trace_symbol_apic),
		  __entry->val)
);

#define trace_kvm_apic_read(reg, val)		trace_kvm_apic(0, reg, val)
#define trace_kvm_apic_write(reg, val)		trace_kvm_apic(1, reg, val)

#define KVM_ISA_VMX   1
#define KVM_ISA_SVM   2

/*
 * Tracepoint for kvm guest exit:
 */
TRACE_EVENT(kvm_exit,
	TP_PROTO(unsigned int exit_reason, struct kvm_vcpu *vcpu, u32 isa),
	TP_ARGS(exit_reason, vcpu, isa),

	TP_STRUCT__entry(
		__field(	unsigned int,	exit_reason	)
		__field(	unsigned long,	guest_rip	)
		__field(	u32,	        isa             )
		__field(	u64,	        info1           )
		__field(	u64,	        info2           )
	),

	TP_fast_assign(
		__entry->exit_reason	= exit_reason;
		__entry->guest_rip	= kvm_rip_read(vcpu);
		__entry->isa            = isa;
		kvm_x86_ops->get_exit_info(vcpu, &__entry->info1,
					   &__entry->info2);
	),

	TP_printk("reason %s rip 0x%lx info %llx %llx",
		 ftrace_print_symbols_seq(p, __entry->exit_reason,
					  kvm_x86_ops->exit_reasons_str),
		 __entry->guest_rip, __entry->info1, __entry->info2)
);

/*
 * Tracepoint for kvm interrupt injection:
 */
TRACE_EVENT(kvm_inj_virq,
	TP_PROTO(unsigned int irq),
	TP_ARGS(irq),

	TP_STRUCT__entry(
		__field(	unsigned int,	irq		)
	),

	TP_fast_assign(
		__entry->irq		= irq;
	),

	TP_printk("irq %u", __entry->irq)
);

#define EXS(x) { x##_VECTOR, "#" #x }

#define kvm_trace_sym_exc						\
	EXS(DE), EXS(DB), EXS(BP), EXS(OF), EXS(BR), EXS(UD), EXS(NM),	\
	EXS(DF), EXS(TS), EXS(NP), EXS(SS), EXS(GP), EXS(PF),		\
	EXS(MF), EXS(MC)

/*
 * Tracepoint for kvm interrupt injection:
 */
TRACE_EVENT(kvm_inj_exception,
	TP_PROTO(unsigned exception, bool has_error, unsigned error_code),
	TP_ARGS(exception, has_error, error_code),

	TP_STRUCT__entry(
		__field(	u8,	exception	)
		__field(	u8,	has_error	)
		__field(	u32,	error_code	)
	),

	TP_fast_assign(
		__entry->exception	= exception;
		__entry->has_error	= has_error;
		__entry->error_code	= error_code;
	),

	TP_printk("%s (0x%x)",
		  __print_symbolic(__entry->exception, kvm_trace_sym_exc),
		  /* FIXME: don't print error_code if not present */
		  __entry->has_error ? __entry->error_code : 0)
);

/*
 * Tracepoint for page fault.
 */
TRACE_EVENT(kvm_page_fault,
	TP_PROTO(unsigned long fault_address, unsigned int error_code),
	TP_ARGS(fault_address, error_code),

	TP_STRUCT__entry(
		__field(	unsigned long,	fault_address	)
		__field(	unsigned int,	error_code	)
	),

	TP_fast_assign(
		__entry->fault_address	= fault_address;
		__entry->error_code	= error_code;
	),

	TP_printk("address %lx error_code %x",
		  __entry->fault_address, __entry->error_code)
);

/*
 * Tracepoint for guest MSR access.
 */
TRACE_EVENT(kvm_msr,
	TP_PROTO(unsigned write, u32 ecx, u64 data, bool exception),
	TP_ARGS(write, ecx, data, exception),

	TP_STRUCT__entry(
		__field(	unsigned,	write		)
		__field(	u32,		ecx		)
		__field(	u64,		data		)
		__field(	u8,		exception	)
	),

	TP_fast_assign(
		__entry->write		= write;
		__entry->ecx		= ecx;
		__entry->data		= data;
		__entry->exception	= exception;
	),

	TP_printk("msr_%s %x = 0x%llx%s",
		  __entry->write ? "write" : "read",
		  __entry->ecx, __entry->data,
		  __entry->exception ? " (#GP)" : "")
);

#define trace_kvm_msr_read(ecx, data)      trace_kvm_msr(0, ecx, data, false)
#define trace_kvm_msr_write(ecx, data)     trace_kvm_msr(1, ecx, data, false)
#define trace_kvm_msr_read_ex(ecx)         trace_kvm_msr(0, ecx, 0, true)
#define trace_kvm_msr_write_ex(ecx, data)  trace_kvm_msr(1, ecx, data, true)

/*
 * Tracepoint for guest CR access.
 */
TRACE_EVENT(kvm_cr,
	TP_PROTO(unsigned int rw, unsigned int cr, unsigned long val),
	TP_ARGS(rw, cr, val),

	TP_STRUCT__entry(
		__field(	unsigned int,	rw		)
		__field(	unsigned int,	cr		)
		__field(	unsigned long,	val		)
	),

	TP_fast_assign(
		__entry->rw		= rw;
		__entry->cr		= cr;
		__entry->val		= val;
	),

	TP_printk("cr_%s %x = 0x%lx",
		  __entry->rw ? "write" : "read",
		  __entry->cr, __entry->val)
);

#define trace_kvm_cr_read(cr, val)		trace_kvm_cr(0, cr, val)
#define trace_kvm_cr_write(cr, val)		trace_kvm_cr(1, cr, val)

TRACE_EVENT(kvm_pic_set_irq,
	    TP_PROTO(__u8 chip, __u8 pin, __u8 elcr, __u8 imr, bool coalesced),
	    TP_ARGS(chip, pin, elcr, imr, coalesced),

	TP_STRUCT__entry(
		__field(	__u8,		chip		)
		__field(	__u8,		pin		)
		__field(	__u8,		elcr		)
		__field(	__u8,		imr		)
		__field(	bool,		coalesced	)
	),

	TP_fast_assign(
		__entry->chip		= chip;
		__entry->pin		= pin;
		__entry->elcr		= elcr;
		__entry->imr		= imr;
		__entry->coalesced	= coalesced;
	),

	TP_printk("chip %u pin %u (%s%s)%s",
		  __entry->chip, __entry->pin,
		  (__entry->elcr & (1 << __entry->pin)) ? "level":"edge",
		  (__entry->imr & (1 << __entry->pin)) ? "|masked":"",
		  __entry->coalesced ? " (coalesced)" : "")
);

#define kvm_apic_dst_shorthand		\
	{0x0, "dst"},			\
	{0x1, "self"},			\
	{0x2, "all"},			\
	{0x3, "all-but-self"}

TRACE_EVENT(kvm_apic_ipi,
	    TP_PROTO(__u32 icr_low, __u32 dest_id),
	    TP_ARGS(icr_low, dest_id),

	TP_STRUCT__entry(
		__field(	__u32,		icr_low		)
		__field(	__u32,		dest_id		)
	),

	TP_fast_assign(
		__entry->icr_low	= icr_low;
		__entry->dest_id	= dest_id;
	),

	TP_printk("dst %x vec %u (%s|%s|%s|%s|%s)",
		  __entry->dest_id, (u8)__entry->icr_low,
		  __print_symbolic((__entry->icr_low >> 8 & 0x7),
				   kvm_deliver_mode),
		  (__entry->icr_low & (1<<11)) ? "logical" : "physical",
		  (__entry->icr_low & (1<<14)) ? "assert" : "de-assert",
		  (__entry->icr_low & (1<<15)) ? "level" : "edge",
		  __print_symbolic((__entry->icr_low >> 18 & 0x3),
				   kvm_apic_dst_shorthand))
);

TRACE_EVENT(kvm_apic_accept_irq,
	    TP_PROTO(__u32 apicid, __u16 dm, __u8 tm, __u8 vec, bool coalesced),
	    TP_ARGS(apicid, dm, tm, vec, coalesced),

	TP_STRUCT__entry(
		__field(	__u32,		apicid		)
		__field(	__u16,		dm		)
		__field(	__u8,		tm		)
		__field(	__u8,		vec		)
		__field(	bool,		coalesced	)
	),

	TP_fast_assign(
		__entry->apicid		= apicid;
		__entry->dm		= dm;
		__entry->tm		= tm;
		__entry->vec		= vec;
		__entry->coalesced	= coalesced;
	),

	TP_printk("apicid %x vec %u (%s|%s)%s",
		  __entry->apicid, __entry->vec,
		  __print_symbolic((__entry->dm >> 8 & 0x7), kvm_deliver_mode),
		  __entry->tm ? "level" : "edge",
		  __entry->coalesced ? " (coalesced)" : "")
);

/*
 * Tracepoint for nested VMRUN
 */
TRACE_EVENT(kvm_nested_vmrun,
	    TP_PROTO(__u64 rip, __u64 vmcb, __u64 nested_rip, __u32 int_ctl,
		     __u32 event_inj, bool npt),
	    TP_ARGS(rip, vmcb, nested_rip, int_ctl, event_inj, npt),

	TP_STRUCT__entry(
		__field(	__u64,		rip		)
		__field(	__u64,		vmcb		)
		__field(	__u64,		nested_rip	)
		__field(	__u32,		int_ctl		)
		__field(	__u32,		event_inj	)
		__field(	bool,		npt		)
	),

	TP_fast_assign(
		__entry->rip		= rip;
		__entry->vmcb		= vmcb;
		__entry->nested_rip	= nested_rip;
		__entry->int_ctl	= int_ctl;
		__entry->event_inj	= event_inj;
		__entry->npt		= npt;
	),

	TP_printk("rip: 0x%016llx vmcb: 0x%016llx nrip: 0x%016llx int_ctl: 0x%08x "
		  "event_inj: 0x%08x npt: %s",
		__entry->rip, __entry->vmcb, __entry->nested_rip,
		__entry->int_ctl, __entry->event_inj,
		__entry->npt ? "on" : "off")
);

TRACE_EVENT(kvm_nested_intercepts,
	    TP_PROTO(__u16 cr_read, __u16 cr_write, __u32 exceptions, __u64 intercept),
	    TP_ARGS(cr_read, cr_write, exceptions, intercept),

	TP_STRUCT__entry(
		__field(	__u16,		cr_read		)
		__field(	__u16,		cr_write	)
		__field(	__u32,		exceptions	)
		__field(	__u64,		intercept	)
	),

	TP_fast_assign(
		__entry->cr_read	= cr_read;
		__entry->cr_write	= cr_write;
		__entry->exceptions	= exceptions;
		__entry->intercept	= intercept;
	),

	TP_printk("cr_read: %04x cr_write: %04x excp: %08x intercept: %016llx",
		__entry->cr_read, __entry->cr_write, __entry->exceptions,
		__entry->intercept)
);
/*
 * Tracepoint for #VMEXIT while nested
 */
TRACE_EVENT(kvm_nested_vmexit,
	    TP_PROTO(__u64 rip, __u32 exit_code,
		     __u64 exit_info1, __u64 exit_info2,
		     __u32 exit_int_info, __u32 exit_int_info_err),
	    TP_ARGS(rip, exit_code, exit_info1, exit_info2,
		    exit_int_info, exit_int_info_err),

	TP_STRUCT__entry(
		__field(	__u64,		rip			)
		__field(	__u32,		exit_code		)
		__field(	__u64,		exit_info1		)
		__field(	__u64,		exit_info2		)
		__field(	__u32,		exit_int_info		)
		__field(	__u32,		exit_int_info_err	)
	),

	TP_fast_assign(
		__entry->rip			= rip;
		__entry->exit_code		= exit_code;
		__entry->exit_info1		= exit_info1;
		__entry->exit_info2		= exit_info2;
		__entry->exit_int_info		= exit_int_info;
		__entry->exit_int_info_err	= exit_int_info_err;
	),
	TP_printk("rip: 0x%016llx reason: %s ext_inf1: 0x%016llx "
		  "ext_inf2: 0x%016llx ext_int: 0x%08x ext_int_err: 0x%08x",
		  __entry->rip,
		  ftrace_print_symbols_seq(p, __entry->exit_code,
					   kvm_x86_ops->exit_reasons_str),
		  __entry->exit_info1, __entry->exit_info2,
		  __entry->exit_int_info, __entry->exit_int_info_err)
);

/*
 * Tracepoint for #VMEXIT reinjected to the guest
 */
TRACE_EVENT(kvm_nested_vmexit_inject,
	    TP_PROTO(__u32 exit_code,
		     __u64 exit_info1, __u64 exit_info2,
		     __u32 exit_int_info, __u32 exit_int_info_err),
	    TP_ARGS(exit_code, exit_info1, exit_info2,
		    exit_int_info, exit_int_info_err),

	TP_STRUCT__entry(
		__field(	__u32,		exit_code		)
		__field(	__u64,		exit_info1		)
		__field(	__u64,		exit_info2		)
		__field(	__u32,		exit_int_info		)
		__field(	__u32,		exit_int_info_err	)
	),

	TP_fast_assign(
		__entry->exit_code		= exit_code;
		__entry->exit_info1		= exit_info1;
		__entry->exit_info2		= exit_info2;
		__entry->exit_int_info		= exit_int_info;
		__entry->exit_int_info_err	= exit_int_info_err;
	),

	TP_printk("reason: %s ext_inf1: 0x%016llx "
		  "ext_inf2: 0x%016llx ext_int: 0x%08x ext_int_err: 0x%08x",
		  ftrace_print_symbols_seq(p, __entry->exit_code,
					   kvm_x86_ops->exit_reasons_str),
		__entry->exit_info1, __entry->exit_info2,
		__entry->exit_int_info, __entry->exit_int_info_err)
);

/*
 * Tracepoint for nested #vmexit because of interrupt pending
 */
TRACE_EVENT(kvm_nested_intr_vmexit,
	    TP_PROTO(__u64 rip),
	    TP_ARGS(rip),

	TP_STRUCT__entry(
		__field(	__u64,	rip	)
	),

	TP_fast_assign(
		__entry->rip	=	rip
	),

	TP_printk("rip: 0x%016llx", __entry->rip)
);

/*
 * Tracepoint for nested #vmexit because of interrupt pending
 */
TRACE_EVENT(kvm_invlpga,
	    TP_PROTO(__u64 rip, int asid, u64 address),
	    TP_ARGS(rip, asid, address),

	TP_STRUCT__entry(
		__field(	__u64,	rip	)
		__field(	int,	asid	)
		__field(	__u64,	address	)
	),

	TP_fast_assign(
		__entry->rip		=	rip;
		__entry->asid		=	asid;
		__entry->address	=	address;
	),

	TP_printk("rip: 0x%016llx asid: %d address: 0x%016llx",
		  __entry->rip, __entry->asid, __entry->address)
);

/*
 * Tracepoint for nested #vmexit because of interrupt pending
 */
TRACE_EVENT(kvm_skinit,
	    TP_PROTO(__u64 rip, __u32 slb),
	    TP_ARGS(rip, slb),

	TP_STRUCT__entry(
		__field(	__u64,	rip	)
		__field(	__u32,	slb	)
	),

	TP_fast_assign(
		__entry->rip		=	rip;
		__entry->slb		=	slb;
	),

	TP_printk("rip: 0x%016llx slb: 0x%08x",
		  __entry->rip, __entry->slb)
);

#define __print_insn(insn, ilen) ({		                 \
	int i;							 \
	const char *ret = p->buffer + p->len;			 \
								 \
	for (i = 0; i < ilen; ++i)				 \
		trace_seq_printf(p, " %02x", insn[i]);		 \
	trace_seq_printf(p, "%c", 0);				 \
	ret;							 \
	})

#define KVM_EMUL_INSN_F_CR0_PE (1 << 0)
#define KVM_EMUL_INSN_F_EFL_VM (1 << 1)
#define KVM_EMUL_INSN_F_CS_D   (1 << 2)
#define KVM_EMUL_INSN_F_CS_L   (1 << 3)

#define kvm_trace_symbol_emul_flags	                  \
	{ 0,   			    "real" },		  \
	{ KVM_EMUL_INSN_F_CR0_PE			  \
	  | KVM_EMUL_INSN_F_EFL_VM, "vm16" },		  \
	{ KVM_EMUL_INSN_F_CR0_PE,   "prot16" },		  \
	{ KVM_EMUL_INSN_F_CR0_PE			  \
	  | KVM_EMUL_INSN_F_CS_D,   "prot32" },		  \
	{ KVM_EMUL_INSN_F_CR0_PE			  \
	  | KVM_EMUL_INSN_F_CS_L,   "prot64" }

#define kei_decode_mode(mode) ({			\
	u8 flags = 0xff;				\
	switch (mode) {					\
	case X86EMUL_MODE_REAL:				\
		flags = 0;				\
		break;					\
	case X86EMUL_MODE_VM86:				\
		flags = KVM_EMUL_INSN_F_EFL_VM;		\
		break;					\
	case X86EMUL_MODE_PROT16:			\
		flags = KVM_EMUL_INSN_F_CR0_PE;		\
		break;					\
	case X86EMUL_MODE_PROT32:			\
		flags = KVM_EMUL_INSN_F_CR0_PE		\
			| KVM_EMUL_INSN_F_CS_D;		\
		break;					\
	case X86EMUL_MODE_PROT64:			\
		flags = KVM_EMUL_INSN_F_CR0_PE		\
			| KVM_EMUL_INSN_F_CS_L;		\
		break;					\
	}						\
	flags;						\
	})

TRACE_EVENT(kvm_emulate_insn,
	TP_PROTO(struct kvm_vcpu *vcpu, __u8 failed),
	TP_ARGS(vcpu, failed),

	TP_STRUCT__entry(
		__field(    __u64, rip                       )
		__field(    __u32, csbase                    )
		__field(    __u8,  len                       )
		__array(    __u8,  insn,    15	             )
		__field(    __u8,  flags       	   	     )
		__field(    __u8,  failed                    )
		),

	TP_fast_assign(
		__entry->rip = vcpu->arch.emulate_ctxt.decode.fetch.start;
		__entry->csbase = kvm_x86_ops->get_segment_base(vcpu, VCPU_SREG_CS);
		__entry->len = vcpu->arch.emulate_ctxt.decode.eip
			       - vcpu->arch.emulate_ctxt.decode.fetch.start;
		memcpy(__entry->insn,
		       vcpu->arch.emulate_ctxt.decode.fetch.data,
		       15);
		__entry->flags = kei_decode_mode(vcpu->arch.emulate_ctxt.mode);
		__entry->failed = failed;
		),

	TP_printk("%x:%llx:%s (%s)%s",
		  __entry->csbase, __entry->rip,
		  __print_insn(__entry->insn, __entry->len),
		  __print_symbolic(__entry->flags,
				   kvm_trace_symbol_emul_flags),
		  __entry->failed ? " failed" : ""
		)
	);

#define trace_kvm_emulate_insn_start(vcpu) trace_kvm_emulate_insn(vcpu, 0)
#define trace_kvm_emulate_insn_failed(vcpu) trace_kvm_emulate_insn(vcpu, 1)

#endif /* _TRACE_KVM_H */

#undef TRACE_INCLUDE_PATH
#define TRACE_INCLUDE_PATH arch/x86/kvm
#undef TRACE_INCLUDE_FILE
#define TRACE_INCLUDE_FILE trace

/* This part must be outside protection */
#include <trace/define_trace.h>
hl ppc">#define RME96_IO_CONTROL_REGISTER 0x20000 #define RME96_IO_ADDITIONAL_REG 0x20004 #define RME96_IO_CONFIRM_PLAY_IRQ 0x20008 #define RME96_IO_CONFIRM_REC_IRQ 0x2000C #define RME96_IO_SET_PLAY_POS 0x40000 #define RME96_IO_RESET_PLAY_POS 0x4FFFC #define RME96_IO_SET_REC_POS 0x50000 #define RME96_IO_RESET_REC_POS 0x5FFFC #define RME96_IO_GET_PLAY_POS 0x20000 #define RME96_IO_GET_REC_POS 0x30000 /* Write control register bits */ #define RME96_WCR_START (1 << 0) #define RME96_WCR_START_2 (1 << 1) #define RME96_WCR_GAIN_0 (1 << 2) #define RME96_WCR_GAIN_1 (1 << 3) #define RME96_WCR_MODE24 (1 << 4) #define RME96_WCR_MODE24_2 (1 << 5) #define RME96_WCR_BM (1 << 6) #define RME96_WCR_BM_2 (1 << 7) #define RME96_WCR_ADAT (1 << 8) #define RME96_WCR_FREQ_0 (1 << 9) #define RME96_WCR_FREQ_1 (1 << 10) #define RME96_WCR_DS (1 << 11) #define RME96_WCR_PRO (1 << 12) #define RME96_WCR_EMP (1 << 13) #define RME96_WCR_SEL (1 << 14) #define RME96_WCR_MASTER (1 << 15) #define RME96_WCR_PD (1 << 16) #define RME96_WCR_INP_0 (1 << 17) #define RME96_WCR_INP_1 (1 << 18) #define RME96_WCR_THRU_0 (1 << 19) #define RME96_WCR_THRU_1 (1 << 20) #define RME96_WCR_THRU_2 (1 << 21) #define RME96_WCR_THRU_3 (1 << 22) #define RME96_WCR_THRU_4 (1 << 23) #define RME96_WCR_THRU_5 (1 << 24) #define RME96_WCR_THRU_6 (1 << 25) #define RME96_WCR_THRU_7 (1 << 26) #define RME96_WCR_DOLBY (1 << 27) #define RME96_WCR_MONITOR_0 (1 << 28) #define RME96_WCR_MONITOR_1 (1 << 29) #define RME96_WCR_ISEL (1 << 30) #define RME96_WCR_IDIS (1 << 31) #define RME96_WCR_BITPOS_GAIN_0 2 #define RME96_WCR_BITPOS_GAIN_1 3 #define RME96_WCR_BITPOS_FREQ_0 9 #define RME96_WCR_BITPOS_FREQ_1 10 #define RME96_WCR_BITPOS_INP_0 17 #define RME96_WCR_BITPOS_INP_1 18 #define RME96_WCR_BITPOS_MONITOR_0 28 #define RME96_WCR_BITPOS_MONITOR_1 29 /* Read control register bits */ #define RME96_RCR_AUDIO_ADDR_MASK 0xFFFF #define RME96_RCR_IRQ_2 (1 << 16) #define RME96_RCR_T_OUT (1 << 17) #define RME96_RCR_DEV_ID_0 (1 << 21) #define RME96_RCR_DEV_ID_1 (1 << 22) #define RME96_RCR_LOCK (1 << 23) #define RME96_RCR_VERF (1 << 26) #define RME96_RCR_F0 (1 << 27) #define RME96_RCR_F1 (1 << 28) #define RME96_RCR_F2 (1 << 29) #define RME96_RCR_AUTOSYNC (1 << 30) #define RME96_RCR_IRQ (1 << 31) #define RME96_RCR_BITPOS_F0 27 #define RME96_RCR_BITPOS_F1 28 #define RME96_RCR_BITPOS_F2 29 /* Additonal register bits */ #define RME96_AR_WSEL (1 << 0) #define RME96_AR_ANALOG (1 << 1) #define RME96_AR_FREQPAD_0 (1 << 2) #define RME96_AR_FREQPAD_1 (1 << 3) #define RME96_AR_FREQPAD_2 (1 << 4) #define RME96_AR_PD2 (1 << 5) #define RME96_AR_DAC_EN (1 << 6) #define RME96_AR_CLATCH (1 << 7) #define RME96_AR_CCLK (1 << 8) #define RME96_AR_CDATA (1 << 9) #define RME96_AR_BITPOS_F0 2 #define RME96_AR_BITPOS_F1 3 #define RME96_AR_BITPOS_F2 4 /* Monitor tracks */ #define RME96_MONITOR_TRACKS_1_2 0 #define RME96_MONITOR_TRACKS_3_4 1 #define RME96_MONITOR_TRACKS_5_6 2 #define RME96_MONITOR_TRACKS_7_8 3 /* Attenuation */ #define RME96_ATTENUATION_0 0 #define RME96_ATTENUATION_6 1 #define RME96_ATTENUATION_12 2 #define RME96_ATTENUATION_18 3 /* Input types */ #define RME96_INPUT_OPTICAL 0 #define RME96_INPUT_COAXIAL 1 #define RME96_INPUT_INTERNAL 2 #define RME96_INPUT_XLR 3 #define RME96_INPUT_ANALOG 4 /* Clock modes */ #define RME96_CLOCKMODE_SLAVE 0 #define RME96_CLOCKMODE_MASTER 1 #define RME96_CLOCKMODE_WORDCLOCK 2 /* Block sizes in bytes */ #define RME96_SMALL_BLOCK_SIZE 2048 #define RME96_LARGE_BLOCK_SIZE 8192 /* Volume control */ #define RME96_AD1852_VOL_BITS 14 #define RME96_AD1855_VOL_BITS 10 /* * PCI vendor/device ids, could in the future be defined in <linux/pci.h>, * therefore #ifndef is used. */ #ifndef PCI_VENDOR_ID_XILINX #define PCI_VENDOR_ID_XILINX 0x10ee #endif #ifndef PCI_DEVICE_ID_DIGI96 #define PCI_DEVICE_ID_DIGI96 0x3fc0 #endif #ifndef PCI_DEVICE_ID_DIGI96_8 #define PCI_DEVICE_ID_DIGI96_8 0x3fc1 #endif #ifndef PCI_DEVICE_ID_DIGI96_8_PRO #define PCI_DEVICE_ID_DIGI96_8_PRO 0x3fc2 #endif #ifndef PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST #define PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST 0x3fc3 #endif typedef struct snd_rme96 { spinlock_t lock; int irq; unsigned long port; void __iomem *iobase; u32 wcreg; /* cached write control register value */ u32 wcreg_spdif; /* S/PDIF setup */ u32 wcreg_spdif_stream; /* S/PDIF setup (temporary) */ u32 rcreg; /* cached read control register value */ u32 areg; /* cached additional register value */ u16 vol[2]; /* cached volume of analog output */ u8 rev; /* card revision number */ snd_pcm_substream_t *playback_substream; snd_pcm_substream_t *capture_substream; int playback_frlog; /* log2 of framesize */ int capture_frlog; size_t playback_periodsize; /* in bytes, zero if not used */ size_t capture_periodsize; /* in bytes, zero if not used */ snd_card_t *card; snd_pcm_t *spdif_pcm; snd_pcm_t *adat_pcm; struct pci_dev *pci; snd_kcontrol_t *spdif_ctl; } rme96_t; static struct pci_device_id snd_rme96_ids[] = { { PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_DIGI96, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, { PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_DIGI96_8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, { PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_DIGI96_8_PRO, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, { PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, { 0, } }; MODULE_DEVICE_TABLE(pci, snd_rme96_ids); #define RME96_ISPLAYING(rme96) ((rme96)->wcreg & RME96_WCR_START) #define RME96_ISRECORDING(rme96) ((rme96)->wcreg & RME96_WCR_START_2) #define RME96_HAS_ANALOG_IN(rme96) ((rme96)->pci->device == PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST) #define RME96_HAS_ANALOG_OUT(rme96) ((rme96)->pci->device == PCI_DEVICE_ID_DIGI96_8_PRO || \ (rme96)->pci->device == PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST) #define RME96_DAC_IS_1852(rme96) (RME96_HAS_ANALOG_OUT(rme96) && (rme96)->rev >= 4) #define RME96_DAC_IS_1855(rme96) (((rme96)->pci->device == PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST && (rme96)->rev < 4) || \ ((rme96)->pci->device == PCI_DEVICE_ID_DIGI96_8_PRO && (rme96)->rev == 2)) #define RME96_185X_MAX_OUT(rme96) ((1 << (RME96_DAC_IS_1852(rme96) ? RME96_AD1852_VOL_BITS : RME96_AD1855_VOL_BITS)) - 1) static int snd_rme96_playback_prepare(snd_pcm_substream_t *substream); static int snd_rme96_capture_prepare(snd_pcm_substream_t *substream); static int snd_rme96_playback_trigger(snd_pcm_substream_t *substream, int cmd); static int snd_rme96_capture_trigger(snd_pcm_substream_t *substream, int cmd); static snd_pcm_uframes_t snd_rme96_playback_pointer(snd_pcm_substream_t *substream); static snd_pcm_uframes_t snd_rme96_capture_pointer(snd_pcm_substream_t *substream); static void __devinit snd_rme96_proc_init(rme96_t *rme96); static int snd_rme96_create_switches(snd_card_t *card, rme96_t *rme96); static int snd_rme96_getinputtype(rme96_t *rme96); static inline unsigned int snd_rme96_playback_ptr(rme96_t *rme96) { return (readl(rme96->iobase + RME96_IO_GET_PLAY_POS) & RME96_RCR_AUDIO_ADDR_MASK) >> rme96->playback_frlog; } static inline unsigned int snd_rme96_capture_ptr(rme96_t *rme96) { return (readl(rme96->iobase + RME96_IO_GET_REC_POS) & RME96_RCR_AUDIO_ADDR_MASK) >> rme96->capture_frlog; } static int snd_rme96_ratecode(int rate) { switch (rate) { case 32000: return SNDRV_PCM_RATE_32000; case 44100: return SNDRV_PCM_RATE_44100; case 48000: return SNDRV_PCM_RATE_48000; case 64000: return SNDRV_PCM_RATE_64000; case 88200: return SNDRV_PCM_RATE_88200; case 96000: return SNDRV_PCM_RATE_96000; } return 0; } static int snd_rme96_playback_silence(snd_pcm_substream_t *substream, int channel, /* not used (interleaved data) */ snd_pcm_uframes_t pos, snd_pcm_uframes_t count) { rme96_t *rme96 = snd_pcm_substream_chip(substream); count <<= rme96->playback_frlog; pos <<= rme96->playback_frlog; memset_io(rme96->iobase + RME96_IO_PLAY_BUFFER + pos, 0, count); return 0; } static int snd_rme96_playback_copy(snd_pcm_substream_t *substream, int channel, /* not used (interleaved data) */ snd_pcm_uframes_t pos, void __user *src, snd_pcm_uframes_t count) { rme96_t *rme96 = snd_pcm_substream_chip(substream); count <<= rme96->playback_frlog; pos <<= rme96->playback_frlog; copy_from_user_toio(rme96->iobase + RME96_IO_PLAY_BUFFER + pos, src, count); return 0; } static int snd_rme96_capture_copy(snd_pcm_substream_t *substream, int channel, /* not used (interleaved data) */ snd_pcm_uframes_t pos, void __user *dst, snd_pcm_uframes_t count) { rme96_t *rme96 = snd_pcm_substream_chip(substream); count <<= rme96->capture_frlog; pos <<= rme96->capture_frlog; copy_to_user_fromio(dst, rme96->iobase + RME96_IO_REC_BUFFER + pos, count); return 0; } /* * Digital output capabilites (S/PDIF) */ static snd_pcm_hardware_t snd_rme96_playback_spdif_info = { .info = (SNDRV_PCM_INFO_MMAP_IOMEM | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE), .rates = (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000), .rate_min = 32000, .rate_max = 96000, .channels_min = 2, .channels_max = 2, .buffer_bytes_max = RME96_BUFFER_SIZE, .period_bytes_min = RME96_SMALL_BLOCK_SIZE, .period_bytes_max = RME96_LARGE_BLOCK_SIZE, .periods_min = RME96_BUFFER_SIZE / RME96_LARGE_BLOCK_SIZE, .periods_max = RME96_BUFFER_SIZE / RME96_SMALL_BLOCK_SIZE, .fifo_size = 0, }; /* * Digital input capabilites (S/PDIF) */ static snd_pcm_hardware_t snd_rme96_capture_spdif_info = { .info = (SNDRV_PCM_INFO_MMAP_IOMEM | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE), .rates = (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000), .rate_min = 32000, .rate_max = 96000, .channels_min = 2, .channels_max = 2, .buffer_bytes_max = RME96_BUFFER_SIZE, .period_bytes_min = RME96_SMALL_BLOCK_SIZE, .period_bytes_max = RME96_LARGE_BLOCK_SIZE, .periods_min = RME96_BUFFER_SIZE / RME96_LARGE_BLOCK_SIZE, .periods_max = RME96_BUFFER_SIZE / RME96_SMALL_BLOCK_SIZE, .fifo_size = 0, }; /* * Digital output capabilites (ADAT) */ static snd_pcm_hardware_t snd_rme96_playback_adat_info = { .info = (SNDRV_PCM_INFO_MMAP_IOMEM | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE), .rates = (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000), .rate_min = 44100, .rate_max = 48000, .channels_min = 8, .channels_max = 8, .buffer_bytes_max = RME96_BUFFER_SIZE, .period_bytes_min = RME96_SMALL_BLOCK_SIZE, .period_bytes_max = RME96_LARGE_BLOCK_SIZE, .periods_min = RME96_BUFFER_SIZE / RME96_LARGE_BLOCK_SIZE, .periods_max = RME96_BUFFER_SIZE / RME96_SMALL_BLOCK_SIZE, .fifo_size = 0, }; /* * Digital input capabilites (ADAT) */ static snd_pcm_hardware_t snd_rme96_capture_adat_info = { .info = (SNDRV_PCM_INFO_MMAP_IOMEM | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE), .rates = (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000), .rate_min = 44100, .rate_max = 48000, .channels_min = 8, .channels_max = 8, .buffer_bytes_max = RME96_BUFFER_SIZE, .period_bytes_min = RME96_SMALL_BLOCK_SIZE, .period_bytes_max = RME96_LARGE_BLOCK_SIZE, .periods_min = RME96_BUFFER_SIZE / RME96_LARGE_BLOCK_SIZE, .periods_max = RME96_BUFFER_SIZE / RME96_SMALL_BLOCK_SIZE, .fifo_size = 0, }; /* * The CDATA, CCLK and CLATCH bits can be used to write to the SPI interface * of the AD1852 or AD1852 D/A converter on the board. CDATA must be set up * on the falling edge of CCLK and be stable on the rising edge. The rising * edge of CLATCH after the last data bit clocks in the whole data word. * A fast processor could probably drive the SPI interface faster than the * DAC can handle (3MHz for the 1855, unknown for the 1852). The udelay(1) * limits the data rate to 500KHz and only causes a delay of 33 microsecs. * * NOTE: increased delay from 1 to 10, since there where problems setting * the volume. */ static void snd_rme96_write_SPI(rme96_t *rme96, u16 val) { int i; for (i = 0; i < 16; i++) { if (val & 0x8000) { rme96->areg |= RME96_AR_CDATA; } else { rme96->areg &= ~RME96_AR_CDATA; } rme96->areg &= ~(RME96_AR_CCLK | RME96_AR_CLATCH); writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); udelay(10); rme96->areg |= RME96_AR_CCLK; writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); udelay(10); val <<= 1; } rme96->areg &= ~(RME96_AR_CCLK | RME96_AR_CDATA); rme96->areg |= RME96_AR_CLATCH; writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); udelay(10); rme96->areg &= ~RME96_AR_CLATCH; writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); } static void snd_rme96_apply_dac_volume(rme96_t *rme96) { if (RME96_DAC_IS_1852(rme96)) { snd_rme96_write_SPI(rme96, (rme96->vol[0] << 2) | 0x0); snd_rme96_write_SPI(rme96, (rme96->vol[1] << 2) | 0x2); } else if (RME96_DAC_IS_1855(rme96)) { snd_rme96_write_SPI(rme96, (rme96->vol[0] & 0x3FF) | 0x000); snd_rme96_write_SPI(rme96, (rme96->vol[1] & 0x3FF) | 0x400); } } static void snd_rme96_reset_dac(rme96_t *rme96) { writel(rme96->wcreg | RME96_WCR_PD, rme96->iobase + RME96_IO_CONTROL_REGISTER); writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); } static int snd_rme96_getmontracks(rme96_t *rme96) { return ((rme96->wcreg >> RME96_WCR_BITPOS_MONITOR_0) & 1) + (((rme96->wcreg >> RME96_WCR_BITPOS_MONITOR_1) & 1) << 1); } static int snd_rme96_setmontracks(rme96_t *rme96, int montracks) { if (montracks & 1) { rme96->wcreg |= RME96_WCR_MONITOR_0; } else { rme96->wcreg &= ~RME96_WCR_MONITOR_0; } if (montracks & 2) { rme96->wcreg |= RME96_WCR_MONITOR_1; } else { rme96->wcreg &= ~RME96_WCR_MONITOR_1; } writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); return 0; } static int snd_rme96_getattenuation(rme96_t *rme96) { return ((rme96->wcreg >> RME96_WCR_BITPOS_GAIN_0) & 1) + (((rme96->wcreg >> RME96_WCR_BITPOS_GAIN_1) & 1) << 1); } static int snd_rme96_setattenuation(rme96_t *rme96, int attenuation) { switch (attenuation) { case 0: rme96->wcreg = (rme96->wcreg & ~RME96_WCR_GAIN_0) & ~RME96_WCR_GAIN_1; break; case 1: rme96->wcreg = (rme96->wcreg | RME96_WCR_GAIN_0) & ~RME96_WCR_GAIN_1; break; case 2: rme96->wcreg = (rme96->wcreg & ~RME96_WCR_GAIN_0) | RME96_WCR_GAIN_1; break; case 3: rme96->wcreg = (rme96->wcreg | RME96_WCR_GAIN_0) | RME96_WCR_GAIN_1; break; default: return -EINVAL; } writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); return 0; } static int snd_rme96_capture_getrate(rme96_t *rme96, int *is_adat) { int n, rate; *is_adat = 0; if (rme96->areg & RME96_AR_ANALOG) { /* Analog input, overrides S/PDIF setting */ n = ((rme96->areg >> RME96_AR_BITPOS_F0) & 1) + (((rme96->areg >> RME96_AR_BITPOS_F1) & 1) << 1); switch (n) { case 1: rate = 32000; break; case 2: rate = 44100; break; case 3: rate = 48000; break; default: return -1; } return (rme96->areg & RME96_AR_BITPOS_F2) ? rate << 1 : rate; } rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER); if (rme96->rcreg & RME96_RCR_LOCK) { /* ADAT rate */ *is_adat = 1; if (rme96->rcreg & RME96_RCR_T_OUT) { return 48000; } return 44100; } if (rme96->rcreg & RME96_RCR_VERF) { return -1; } /* S/PDIF rate */ n = ((rme96->rcreg >> RME96_RCR_BITPOS_F0) & 1) + (((rme96->rcreg >> RME96_RCR_BITPOS_F1) & 1) << 1) + (((rme96->rcreg >> RME96_RCR_BITPOS_F2) & 1) << 2); switch (n) { case 0: if (rme96->rcreg & RME96_RCR_T_OUT) { return 64000; } return -1; case 3: return 96000; case 4: return 88200; case 5: return 48000; case 6: return 44100; case 7: return 32000; default: break; } return -1; } static int snd_rme96_playback_getrate(rme96_t *rme96) { int rate, dummy; if (!(rme96->wcreg & RME96_WCR_MASTER) && snd_rme96_getinputtype(rme96) != RME96_INPUT_ANALOG && (rate = snd_rme96_capture_getrate(rme96, &dummy)) > 0) { /* slave clock */ return rate; } rate = ((rme96->wcreg >> RME96_WCR_BITPOS_FREQ_0) & 1) + (((rme96->wcreg >> RME96_WCR_BITPOS_FREQ_1) & 1) << 1); switch (rate) { case 1: rate = 32000; break; case 2: rate = 44100; break; case 3: rate = 48000; break; default: return -1; } return (rme96->wcreg & RME96_WCR_DS) ? rate << 1 : rate; } static int snd_rme96_playback_setrate(rme96_t *rme96, int rate) { int ds; ds = rme96->wcreg & RME96_WCR_DS; switch (rate) { case 32000: rme96->wcreg &= ~RME96_WCR_DS; rme96->wcreg = (rme96->wcreg | RME96_WCR_FREQ_0) & ~RME96_WCR_FREQ_1; break; case 44100: rme96->wcreg &= ~RME96_WCR_DS; rme96->wcreg = (rme96->wcreg | RME96_WCR_FREQ_1) & ~RME96_WCR_FREQ_0; break; case 48000: rme96->wcreg &= ~RME96_WCR_DS; rme96->wcreg = (rme96->wcreg | RME96_WCR_FREQ_0) | RME96_WCR_FREQ_1; break; case 64000: rme96->wcreg |= RME96_WCR_DS; rme96->wcreg = (rme96->wcreg | RME96_WCR_FREQ_0) & ~RME96_WCR_FREQ_1; break; case 88200: rme96->wcreg |= RME96_WCR_DS; rme96->wcreg = (rme96->wcreg | RME96_WCR_FREQ_1) & ~RME96_WCR_FREQ_0; break; case 96000: rme96->wcreg |= RME96_WCR_DS; rme96->wcreg = (rme96->wcreg | RME96_WCR_FREQ_0) | RME96_WCR_FREQ_1; break; default: return -EINVAL; } if ((!ds && rme96->wcreg & RME96_WCR_DS) || (ds && !(rme96->wcreg & RME96_WCR_DS))) { /* change to/from double-speed: reset the DAC (if available) */ snd_rme96_reset_dac(rme96); } else { writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); } return 0; } static int snd_rme96_capture_analog_setrate(rme96_t *rme96, int rate) { switch (rate) { case 32000: rme96->areg = ((rme96->areg | RME96_AR_FREQPAD_0) & ~RME96_AR_FREQPAD_1) & ~RME96_AR_FREQPAD_2; break; case 44100: rme96->areg = ((rme96->areg & ~RME96_AR_FREQPAD_0) | RME96_AR_FREQPAD_1) & ~RME96_AR_FREQPAD_2; break; case 48000: rme96->areg = ((rme96->areg | RME96_AR_FREQPAD_0) | RME96_AR_FREQPAD_1) & ~RME96_AR_FREQPAD_2; break; case 64000: if (rme96->rev < 4) { return -EINVAL; } rme96->areg = ((rme96->areg | RME96_AR_FREQPAD_0) & ~RME96_AR_FREQPAD_1) | RME96_AR_FREQPAD_2; break; case 88200: if (rme96->rev < 4) { return -EINVAL; } rme96->areg = ((rme96->areg & ~RME96_AR_FREQPAD_0) | RME96_AR_FREQPAD_1) | RME96_AR_FREQPAD_2; break; case 96000: rme96->areg = ((rme96->areg | RME96_AR_FREQPAD_0) | RME96_AR_FREQPAD_1) | RME96_AR_FREQPAD_2; break; default: return -EINVAL; } writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); return 0; } static int snd_rme96_setclockmode(rme96_t *rme96, int mode) { switch (mode) { case RME96_CLOCKMODE_SLAVE: /* AutoSync */ rme96->wcreg &= ~RME96_WCR_MASTER; rme96->areg &= ~RME96_AR_WSEL; break; case RME96_CLOCKMODE_MASTER: /* Internal */ rme96->wcreg |= RME96_WCR_MASTER; rme96->areg &= ~RME96_AR_WSEL; break; case RME96_CLOCKMODE_WORDCLOCK: /* Word clock is a master mode */ rme96->wcreg |= RME96_WCR_MASTER; rme96->areg |= RME96_AR_WSEL; break; default: return -EINVAL; } writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); return 0; } static int snd_rme96_getclockmode(rme96_t *rme96) { if (rme96->areg & RME96_AR_WSEL) { return RME96_CLOCKMODE_WORDCLOCK; } return (rme96->wcreg & RME96_WCR_MASTER) ? RME96_CLOCKMODE_MASTER : RME96_CLOCKMODE_SLAVE; } static int snd_rme96_setinputtype(rme96_t *rme96, int type) { int n; switch (type) { case RME96_INPUT_OPTICAL: rme96->wcreg = (rme96->wcreg & ~RME96_WCR_INP_0) & ~RME96_WCR_INP_1; break; case RME96_INPUT_COAXIAL: rme96->wcreg = (rme96->wcreg | RME96_WCR_INP_0) & ~RME96_WCR_INP_1; break; case RME96_INPUT_INTERNAL: rme96->wcreg = (rme96->wcreg & ~RME96_WCR_INP_0) | RME96_WCR_INP_1; break; case RME96_INPUT_XLR: if ((rme96->pci->device != PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST && rme96->pci->device != PCI_DEVICE_ID_DIGI96_8_PRO) || (rme96->pci->device == PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST && rme96->rev > 4)) { /* Only Digi96/8 PRO and Digi96/8 PAD supports XLR */ return -EINVAL; } rme96->wcreg = (rme96->wcreg | RME96_WCR_INP_0) | RME96_WCR_INP_1; break; case RME96_INPUT_ANALOG: if (!RME96_HAS_ANALOG_IN(rme96)) { return -EINVAL; } rme96->areg |= RME96_AR_ANALOG; writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); if (rme96->rev < 4) { /* * Revision less than 004 does not support 64 and * 88.2 kHz */ if (snd_rme96_capture_getrate(rme96, &n) == 88200) { snd_rme96_capture_analog_setrate(rme96, 44100); } if (snd_rme96_capture_getrate(rme96, &n) == 64000) { snd_rme96_capture_analog_setrate(rme96, 32000); } } return 0; default: return -EINVAL; } if (type != RME96_INPUT_ANALOG && RME96_HAS_ANALOG_IN(rme96)) { rme96->areg &= ~RME96_AR_ANALOG; writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); } writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); return 0; } static int snd_rme96_getinputtype(rme96_t *rme96) { if (rme96->areg & RME96_AR_ANALOG) { return RME96_INPUT_ANALOG; } return ((rme96->wcreg >> RME96_WCR_BITPOS_INP_0) & 1) + (((rme96->wcreg >> RME96_WCR_BITPOS_INP_1) & 1) << 1); } static void snd_rme96_setframelog(rme96_t *rme96, int n_channels, int is_playback) { int frlog; if (n_channels == 2) { frlog = 1; } else { /* assume 8 channels */ frlog = 3; } if (is_playback) { frlog += (rme96->wcreg & RME96_WCR_MODE24) ? 2 : 1; rme96->playback_frlog = frlog; } else { frlog += (rme96->wcreg & RME96_WCR_MODE24_2) ? 2 : 1; rme96->capture_frlog = frlog; } } static int snd_rme96_playback_setformat(rme96_t *rme96, int format) { switch (format) { case SNDRV_PCM_FORMAT_S16_LE: rme96->wcreg &= ~RME96_WCR_MODE24; break; case SNDRV_PCM_FORMAT_S32_LE: rme96->wcreg |= RME96_WCR_MODE24; break; default: return -EINVAL; } writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); return 0; } static int snd_rme96_capture_setformat(rme96_t *rme96, int format) { switch (format) { case SNDRV_PCM_FORMAT_S16_LE: rme96->wcreg &= ~RME96_WCR_MODE24_2; break; case SNDRV_PCM_FORMAT_S32_LE: rme96->wcreg |= RME96_WCR_MODE24_2; break; default: return -EINVAL; } writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); return 0; } static void snd_rme96_set_period_properties(rme96_t *rme96, size_t period_bytes) { switch (period_bytes) { case RME96_LARGE_BLOCK_SIZE: rme96->wcreg &= ~RME96_WCR_ISEL; break; case RME96_SMALL_BLOCK_SIZE: rme96->wcreg |= RME96_WCR_ISEL; break; default: snd_BUG(); break; } rme96->wcreg &= ~RME96_WCR_IDIS; writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); } static int snd_rme96_playback_hw_params(snd_pcm_substream_t *substream, snd_pcm_hw_params_t *params) { rme96_t *rme96 = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; int err, rate, dummy; runtime->dma_area = (void __force *)(rme96->iobase + RME96_IO_PLAY_BUFFER); runtime->dma_addr = rme96->port + RME96_IO_PLAY_BUFFER; runtime->dma_bytes = RME96_BUFFER_SIZE; spin_lock_irq(&rme96->lock); if (!(rme96->wcreg & RME96_WCR_MASTER) && snd_rme96_getinputtype(rme96) != RME96_INPUT_ANALOG && (rate = snd_rme96_capture_getrate(rme96, &dummy)) > 0) { /* slave clock */ if ((int)params_rate(params) != rate) { spin_unlock_irq(&rme96->lock); return -EIO; } } else if ((err = snd_rme96_playback_setrate(rme96, params_rate(params))) < 0) { spin_unlock_irq(&rme96->lock); return err; } if ((err = snd_rme96_playback_setformat(rme96, params_format(params))) < 0) { spin_unlock_irq(&rme96->lock); return err; } snd_rme96_setframelog(rme96, params_channels(params), 1); if (rme96->capture_periodsize != 0) { if (params_period_size(params) << rme96->playback_frlog != rme96->capture_periodsize) { spin_unlock_irq(&rme96->lock); return -EBUSY; } } rme96->playback_periodsize = params_period_size(params) << rme96->playback_frlog; snd_rme96_set_period_properties(rme96, rme96->playback_periodsize); /* S/PDIF setup */ if ((rme96->wcreg & RME96_WCR_ADAT) == 0) { rme96->wcreg &= ~(RME96_WCR_PRO | RME96_WCR_DOLBY | RME96_WCR_EMP); writel(rme96->wcreg |= rme96->wcreg_spdif_stream, rme96->iobase + RME96_IO_CONTROL_REGISTER); } spin_unlock_irq(&rme96->lock); return 0; } static int snd_rme96_capture_hw_params(snd_pcm_substream_t *substream, snd_pcm_hw_params_t *params) { rme96_t *rme96 = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; int err, isadat, rate; runtime->dma_area = (void __force *)(rme96->iobase + RME96_IO_REC_BUFFER); runtime->dma_addr = rme96->port + RME96_IO_REC_BUFFER; runtime->dma_bytes = RME96_BUFFER_SIZE; spin_lock_irq(&rme96->lock); if ((err = snd_rme96_capture_setformat(rme96, params_format(params))) < 0) { spin_unlock_irq(&rme96->lock); return err; } if (snd_rme96_getinputtype(rme96) == RME96_INPUT_ANALOG) { if ((err = snd_rme96_capture_analog_setrate(rme96, params_rate(params))) < 0) { spin_unlock_irq(&rme96->lock); return err; } } else if ((rate = snd_rme96_capture_getrate(rme96, &isadat)) > 0) { if ((int)params_rate(params) != rate) { spin_unlock_irq(&rme96->lock); return -EIO; } if ((isadat && runtime->hw.channels_min == 2) || (!isadat && runtime->hw.channels_min == 8)) { spin_unlock_irq(&rme96->lock); return -EIO; } } snd_rme96_setframelog(rme96, params_channels(params), 0); if (rme96->playback_periodsize != 0) { if (params_period_size(params) << rme96->capture_frlog != rme96->playback_periodsize) { spin_unlock_irq(&rme96->lock); return -EBUSY; } } rme96->capture_periodsize = params_period_size(params) << rme96->capture_frlog; snd_rme96_set_period_properties(rme96, rme96->capture_periodsize); spin_unlock_irq(&rme96->lock); return 0; } static void snd_rme96_playback_start(rme96_t *rme96, int from_pause) { if (!from_pause) { writel(0, rme96->iobase + RME96_IO_RESET_PLAY_POS); } rme96->wcreg |= RME96_WCR_START; writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); } static void snd_rme96_capture_start(rme96_t *rme96, int from_pause) { if (!from_pause) { writel(0, rme96->iobase + RME96_IO_RESET_REC_POS); } rme96->wcreg |= RME96_WCR_START_2; writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); } static void snd_rme96_playback_stop(rme96_t *rme96) { /* * Check if there is an unconfirmed IRQ, if so confirm it, or else * the hardware will not stop generating interrupts */ rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER); if (rme96->rcreg & RME96_RCR_IRQ) { writel(0, rme96->iobase + RME96_IO_CONFIRM_PLAY_IRQ); } rme96->wcreg &= ~RME96_WCR_START; writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); } static void snd_rme96_capture_stop(rme96_t *rme96) { rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER); if (rme96->rcreg & RME96_RCR_IRQ_2) { writel(0, rme96->iobase + RME96_IO_CONFIRM_REC_IRQ); } rme96->wcreg &= ~RME96_WCR_START_2; writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); } static irqreturn_t snd_rme96_interrupt(int irq, void *dev_id, struct pt_regs *regs) { rme96_t *rme96 = (rme96_t *)dev_id; rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER); /* fastpath out, to ease interrupt sharing */ if (!((rme96->rcreg & RME96_RCR_IRQ) || (rme96->rcreg & RME96_RCR_IRQ_2))) { return IRQ_NONE; } if (rme96->rcreg & RME96_RCR_IRQ) { /* playback */ snd_pcm_period_elapsed(rme96->playback_substream); writel(0, rme96->iobase + RME96_IO_CONFIRM_PLAY_IRQ); } if (rme96->rcreg & RME96_RCR_IRQ_2) { /* capture */ snd_pcm_period_elapsed(rme96->capture_substream); writel(0, rme96->iobase + RME96_IO_CONFIRM_REC_IRQ); } return IRQ_HANDLED; } static unsigned int period_bytes[] = { RME96_SMALL_BLOCK_SIZE, RME96_LARGE_BLOCK_SIZE }; static snd_pcm_hw_constraint_list_t hw_constraints_period_bytes = { .count = ARRAY_SIZE(period_bytes), .list = period_bytes, .mask = 0 }; static int snd_rme96_playback_spdif_open(snd_pcm_substream_t *substream) { int rate, dummy; rme96_t *rme96 = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; snd_pcm_set_sync(substream); spin_lock_irq(&rme96->lock); if (rme96->playback_substream != NULL) { spin_unlock_irq(&rme96->lock); return -EBUSY; } rme96->wcreg &= ~RME96_WCR_ADAT; writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); rme96->playback_substream = substream; spin_unlock_irq(&rme96->lock); runtime->hw = snd_rme96_playback_spdif_info; if (!(rme96->wcreg & RME96_WCR_MASTER) && snd_rme96_getinputtype(rme96) != RME96_INPUT_ANALOG && (rate = snd_rme96_capture_getrate(rme96, &dummy)) > 0) { /* slave clock */ runtime->hw.rates = snd_rme96_ratecode(rate); runtime->hw.rate_min = rate; runtime->hw.rate_max = rate; } snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE); snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes); rme96->wcreg_spdif_stream = rme96->wcreg_spdif; rme96->spdif_ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; snd_ctl_notify(rme96->card, SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO, &rme96->spdif_ctl->id); return 0; } static int snd_rme96_capture_spdif_open(snd_pcm_substream_t *substream) { int isadat, rate; rme96_t *rme96 = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; snd_pcm_set_sync(substream); runtime->hw = snd_rme96_capture_spdif_info; if (snd_rme96_getinputtype(rme96) != RME96_INPUT_ANALOG && (rate = snd_rme96_capture_getrate(rme96, &isadat)) > 0) { if (isadat) { return -EIO; } runtime->hw.rates = snd_rme96_ratecode(rate); runtime->hw.rate_min = rate; runtime->hw.rate_max = rate; } spin_lock_irq(&rme96->lock); if (rme96->capture_substream != NULL) { spin_unlock_irq(&rme96->lock); return -EBUSY; } rme96->capture_substream = substream; spin_unlock_irq(&rme96->lock); snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE); snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes); return 0; } static int snd_rme96_playback_adat_open(snd_pcm_substream_t *substream) { int rate, dummy; rme96_t *rme96 = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; snd_pcm_set_sync(substream); spin_lock_irq(&rme96->lock); if (rme96->playback_substream != NULL) { spin_unlock_irq(&rme96->lock); return -EBUSY; } rme96->wcreg |= RME96_WCR_ADAT; writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); rme96->playback_substream = substream; spin_unlock_irq(&rme96->lock); runtime->hw = snd_rme96_playback_adat_info; if (!(rme96->wcreg & RME96_WCR_MASTER) && snd_rme96_getinputtype(rme96) != RME96_INPUT_ANALOG && (rate = snd_rme96_capture_getrate(rme96, &dummy)) > 0) { /* slave clock */ runtime->hw.rates = snd_rme96_ratecode(rate); runtime->hw.rate_min = rate; runtime->hw.rate_max = rate; } snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE); snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes); return 0; } static int snd_rme96_capture_adat_open(snd_pcm_substream_t *substream) { int isadat, rate; rme96_t *rme96 = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; snd_pcm_set_sync(substream); runtime->hw = snd_rme96_capture_adat_info; if (snd_rme96_getinputtype(rme96) == RME96_INPUT_ANALOG) { /* makes no sense to use analog input. Note that analog expension cards AEB4/8-I are RME96_INPUT_INTERNAL */ return -EIO; } if ((rate = snd_rme96_capture_getrate(rme96, &isadat)) > 0) { if (!isadat) { return -EIO; } runtime->hw.rates = snd_rme96_ratecode(rate); runtime->hw.rate_min = rate; runtime->hw.rate_max = rate; } spin_lock_irq(&rme96->lock); if (rme96->capture_substream != NULL) { spin_unlock_irq(&rme96->lock); return -EBUSY; } rme96->capture_substream = substream; spin_unlock_irq(&rme96->lock); snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE); snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes); return 0; } static int snd_rme96_playback_close(snd_pcm_substream_t *substream) { rme96_t *rme96 = snd_pcm_substream_chip(substream); int spdif = 0; spin_lock_irq(&rme96->lock); if (RME96_ISPLAYING(rme96)) { snd_rme96_playback_stop(rme96); } rme96->playback_substream = NULL; rme96->playback_periodsize = 0; spdif = (rme96->wcreg & RME96_WCR_ADAT) == 0; spin_unlock_irq(&rme96->lock); if (spdif) { rme96->spdif_ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; snd_ctl_notify(rme96->card, SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO, &rme96->spdif_ctl->id); } return 0; } static int snd_rme96_capture_close(snd_pcm_substream_t *substream) { rme96_t *rme96 = snd_pcm_substream_chip(substream); spin_lock_irq(&rme96->lock); if (RME96_ISRECORDING(rme96)) { snd_rme96_capture_stop(rme96); } rme96->capture_substream = NULL; rme96->capture_periodsize = 0; spin_unlock_irq(&rme96->lock); return 0; } static int snd_rme96_playback_prepare(snd_pcm_substream_t *substream) { rme96_t *rme96 = snd_pcm_substream_chip(substream); spin_lock_irq(&rme96->lock); if (RME96_ISPLAYING(rme96)) { snd_rme96_playback_stop(rme96); } writel(0, rme96->iobase + RME96_IO_RESET_PLAY_POS); spin_unlock_irq(&rme96->lock); return 0; } static int snd_rme96_capture_prepare(snd_pcm_substream_t *substream) { rme96_t *rme96 = snd_pcm_substream_chip(substream); spin_lock_irq(&rme96->lock); if (RME96_ISRECORDING(rme96)) { snd_rme96_capture_stop(rme96); } writel(0, rme96->iobase + RME96_IO_RESET_REC_POS); spin_unlock_irq(&rme96->lock); return 0; } static int snd_rme96_playback_trigger(snd_pcm_substream_t *substream, int cmd) { rme96_t *rme96 = snd_pcm_substream_chip(substream); switch (cmd) { case SNDRV_PCM_TRIGGER_START: if (!RME96_ISPLAYING(rme96)) { if (substream != rme96->playback_substream) { return -EBUSY; } snd_rme96_playback_start(rme96, 0); } break; case SNDRV_PCM_TRIGGER_STOP: if (RME96_ISPLAYING(rme96)) { if (substream != rme96->playback_substream) { return -EBUSY; } snd_rme96_playback_stop(rme96); } break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: if (RME96_ISPLAYING(rme96)) { snd_rme96_playback_stop(rme96); } break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: if (!RME96_ISPLAYING(rme96)) { snd_rme96_playback_start(rme96, 1); } break; default: return -EINVAL; } return 0; } static int snd_rme96_capture_trigger(snd_pcm_substream_t *substream, int cmd) { rme96_t *rme96 = snd_pcm_substream_chip(substream); switch (cmd) { case SNDRV_PCM_TRIGGER_START: if (!RME96_ISRECORDING(rme96)) { if (substream != rme96->capture_substream) { return -EBUSY; } snd_rme96_capture_start(rme96, 0); } break; case SNDRV_PCM_TRIGGER_STOP: if (RME96_ISRECORDING(rme96)) { if (substream != rme96->capture_substream) { return -EBUSY; } snd_rme96_capture_stop(rme96); } break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: if (RME96_ISRECORDING(rme96)) { snd_rme96_capture_stop(rme96); } break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: if (!RME96_ISRECORDING(rme96)) { snd_rme96_capture_start(rme96, 1); } break; default: return -EINVAL; } return 0; } static snd_pcm_uframes_t snd_rme96_playback_pointer(snd_pcm_substream_t *substream) { rme96_t *rme96 = snd_pcm_substream_chip(substream); return snd_rme96_playback_ptr(rme96); } static snd_pcm_uframes_t snd_rme96_capture_pointer(snd_pcm_substream_t *substream) { rme96_t *rme96 = snd_pcm_substream_chip(substream); return snd_rme96_capture_ptr(rme96); } static snd_pcm_ops_t snd_rme96_playback_spdif_ops = { .open = snd_rme96_playback_spdif_open, .close = snd_rme96_playback_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_rme96_playback_hw_params, .prepare = snd_rme96_playback_prepare, .trigger = snd_rme96_playback_trigger, .pointer = snd_rme96_playback_pointer, .copy = snd_rme96_playback_copy, .silence = snd_rme96_playback_silence, .mmap = snd_pcm_lib_mmap_iomem, }; static snd_pcm_ops_t snd_rme96_capture_spdif_ops = { .open = snd_rme96_capture_spdif_open, .close = snd_rme96_capture_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_rme96_capture_hw_params, .prepare = snd_rme96_capture_prepare, .trigger = snd_rme96_capture_trigger, .pointer = snd_rme96_capture_pointer, .copy = snd_rme96_capture_copy, .mmap = snd_pcm_lib_mmap_iomem, }; static snd_pcm_ops_t snd_rme96_playback_adat_ops = { .open = snd_rme96_playback_adat_open, .close = snd_rme96_playback_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_rme96_playback_hw_params, .prepare = snd_rme96_playback_prepare, .trigger = snd_rme96_playback_trigger, .pointer = snd_rme96_playback_pointer, .copy = snd_rme96_playback_copy, .silence = snd_rme96_playback_silence, .mmap = snd_pcm_lib_mmap_iomem, }; static snd_pcm_ops_t snd_rme96_capture_adat_ops = { .open = snd_rme96_capture_adat_open, .close = snd_rme96_capture_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_rme96_capture_hw_params, .prepare = snd_rme96_capture_prepare, .trigger = snd_rme96_capture_trigger, .pointer = snd_rme96_capture_pointer, .copy = snd_rme96_capture_copy, .mmap = snd_pcm_lib_mmap_iomem, }; static void snd_rme96_free(void *private_data) { rme96_t *rme96 = (rme96_t *)private_data; if (rme96 == NULL) { return; } if (rme96->irq >= 0) { snd_rme96_playback_stop(rme96); snd_rme96_capture_stop(rme96); rme96->areg &= ~RME96_AR_DAC_EN; writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); free_irq(rme96->irq, (void *)rme96); rme96->irq = -1; } if (rme96->iobase) { iounmap(rme96->iobase); rme96->iobase = NULL; } if (rme96->port) { pci_release_regions(rme96->pci); rme96->port = 0; } pci_disable_device(rme96->pci); } static void snd_rme96_free_spdif_pcm(snd_pcm_t *pcm) { rme96_t *rme96 = (rme96_t *) pcm->private_data; rme96->spdif_pcm = NULL; } static void snd_rme96_free_adat_pcm(snd_pcm_t *pcm) { rme96_t *rme96 = (rme96_t *) pcm->private_data; rme96->adat_pcm = NULL; } static int __devinit snd_rme96_create(rme96_t *rme96) { struct pci_dev *pci = rme96->pci; int err; rme96->irq = -1; spin_lock_init(&rme96->lock); if ((err = pci_enable_device(pci)) < 0) return err; if ((err = pci_request_regions(pci, "RME96")) < 0) return err; rme96->port = pci_resource_start(rme96->pci, 0); if (request_irq(pci->irq, snd_rme96_interrupt, SA_INTERRUPT|SA_SHIRQ, "RME96", (void *)rme96)) { snd_printk("unable to grab IRQ %d\n", pci->irq); return -EBUSY; } rme96->irq = pci->irq; if ((rme96->iobase = ioremap_nocache(rme96->port, RME96_IO_SIZE)) == 0) { snd_printk("unable to remap memory region 0x%lx-0x%lx\n", rme96->port, rme96->port + RME96_IO_SIZE - 1); return -ENOMEM; } /* read the card's revision number */ pci_read_config_byte(pci, 8, &rme96->rev); /* set up ALSA pcm device for S/PDIF */ if ((err = snd_pcm_new(rme96->card, "Digi96 IEC958", 0, 1, 1, &rme96->spdif_pcm)) < 0) { return err; } rme96->spdif_pcm->private_data = rme96; rme96->spdif_pcm->private_free = snd_rme96_free_spdif_pcm; strcpy(rme96->spdif_pcm->name, "Digi96 IEC958"); snd_pcm_set_ops(rme96->spdif_pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_rme96_playback_spdif_ops); snd_pcm_set_ops(rme96->spdif_pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_rme96_capture_spdif_ops); rme96->spdif_pcm->info_flags = 0; /* set up ALSA pcm device for ADAT */ if (pci->device == PCI_DEVICE_ID_DIGI96) { /* ADAT is not available on the base model */ rme96->adat_pcm = NULL; } else { if ((err = snd_pcm_new(rme96->card, "Digi96 ADAT", 1, 1, 1, &rme96->adat_pcm)) < 0) { return err; } rme96->adat_pcm->private_data = rme96; rme96->adat_pcm->private_free = snd_rme96_free_adat_pcm; strcpy(rme96->adat_pcm->name, "Digi96 ADAT"); snd_pcm_set_ops(rme96->adat_pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_rme96_playback_adat_ops); snd_pcm_set_ops(rme96->adat_pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_rme96_capture_adat_ops); rme96->adat_pcm->info_flags = 0; } rme96->playback_periodsize = 0; rme96->capture_periodsize = 0; /* make sure playback/capture is stopped, if by some reason active */ snd_rme96_playback_stop(rme96); snd_rme96_capture_stop(rme96); /* set default values in registers */ rme96->wcreg = RME96_WCR_FREQ_1 | /* set 44.1 kHz playback */ RME96_WCR_SEL | /* normal playback */ RME96_WCR_MASTER | /* set to master clock mode */ RME96_WCR_INP_0; /* set coaxial input */ rme96->areg = RME96_AR_FREQPAD_1; /* set 44.1 kHz analog capture */ writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); /* reset the ADC */ writel(rme96->areg | RME96_AR_PD2, rme96->iobase + RME96_IO_ADDITIONAL_REG); writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); /* reset and enable the DAC (order is important). */ snd_rme96_reset_dac(rme96); rme96->areg |= RME96_AR_DAC_EN; writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); /* reset playback and record buffer pointers */ writel(0, rme96->iobase + RME96_IO_RESET_PLAY_POS); writel(0, rme96->iobase + RME96_IO_RESET_REC_POS); /* reset volume */ rme96->vol[0] = rme96->vol[1] = 0; if (RME96_HAS_ANALOG_OUT(rme96)) { snd_rme96_apply_dac_volume(rme96); } /* init switch interface */ if ((err = snd_rme96_create_switches(rme96->card, rme96)) < 0) { return err; } /* init proc interface */ snd_rme96_proc_init(rme96); return 0; } /* * proc interface */ static void snd_rme96_proc_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer) { int n; rme96_t *rme96 = (rme96_t *)entry->private_data; rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER); snd_iprintf(buffer, rme96->card->longname); snd_iprintf(buffer, " (index #%d)\n", rme96->card->number + 1); snd_iprintf(buffer, "\nGeneral settings\n"); if (rme96->wcreg & RME96_WCR_IDIS) { snd_iprintf(buffer, " period size: N/A (interrupts " "disabled)\n"); } else if (rme96->wcreg & RME96_WCR_ISEL) { snd_iprintf(buffer, " period size: 2048 bytes\n"); } else { snd_iprintf(buffer, " period size: 8192 bytes\n"); } snd_iprintf(buffer, "\nInput settings\n"); switch (snd_rme96_getinputtype(rme96)) { case RME96_INPUT_OPTICAL: snd_iprintf(buffer, " input: optical"); break; case RME96_INPUT_COAXIAL: snd_iprintf(buffer, " input: coaxial"); break; case RME96_INPUT_INTERNAL: snd_iprintf(buffer, " input: internal"); break; case RME96_INPUT_XLR: snd_iprintf(buffer, " input: XLR"); break; case RME96_INPUT_ANALOG: snd_iprintf(buffer, " input: analog"); break; } if (snd_rme96_capture_getrate(rme96, &n) < 0) { snd_iprintf(buffer, "\n sample rate: no valid signal\n"); } else { if (n) { snd_iprintf(buffer, " (8 channels)\n"); } else { snd_iprintf(buffer, " (2 channels)\n"); } snd_iprintf(buffer, " sample rate: %d Hz\n", snd_rme96_capture_getrate(rme96, &n)); } if (rme96->wcreg & RME96_WCR_MODE24_2) { snd_iprintf(buffer, " sample format: 24 bit\n"); } else { snd_iprintf(buffer, " sample format: 16 bit\n"); } snd_iprintf(buffer, "\nOutput settings\n"); if (rme96->wcreg & RME96_WCR_SEL) { snd_iprintf(buffer, " output signal: normal playback\n"); } else { snd_iprintf(buffer, " output signal: same as input\n"); } snd_iprintf(buffer, " sample rate: %d Hz\n", snd_rme96_playback_getrate(rme96)); if (rme96->wcreg & RME96_WCR_MODE24) { snd_iprintf(buffer, " sample format: 24 bit\n"); } else { snd_iprintf(buffer, " sample format: 16 bit\n"); } if (rme96->areg & RME96_AR_WSEL) { snd_iprintf(buffer, " sample clock source: word clock\n"); } else if (rme96->wcreg & RME96_WCR_MASTER) { snd_iprintf(buffer, " sample clock source: internal\n"); } else if (snd_rme96_getinputtype(rme96) == RME96_INPUT_ANALOG) { snd_iprintf(buffer, " sample clock source: autosync (internal anyway due to analog input setting)\n"); } else if (snd_rme96_capture_getrate(rme96, &n) < 0) { snd_iprintf(buffer, " sample clock source: autosync (internal anyway due to no valid signal)\n"); } else { snd_iprintf(buffer, " sample clock source: autosync\n"); } if (rme96->wcreg & RME96_WCR_PRO) { snd_iprintf(buffer, " format: AES/EBU (professional)\n"); } else { snd_iprintf(buffer, " format: IEC958 (consumer)\n"); } if (rme96->wcreg & RME96_WCR_EMP) { snd_iprintf(buffer, " emphasis: on\n"); } else { snd_iprintf(buffer, " emphasis: off\n"); } if (rme96->wcreg & RME96_WCR_DOLBY) { snd_iprintf(buffer, " non-audio (dolby): on\n"); } else { snd_iprintf(buffer, " non-audio (dolby): off\n"); } if (RME96_HAS_ANALOG_IN(rme96)) { snd_iprintf(buffer, "\nAnalog output settings\n"); switch (snd_rme96_getmontracks(rme96)) { case RME96_MONITOR_TRACKS_1_2: snd_iprintf(buffer, " monitored ADAT tracks: 1+2\n"); break; case RME96_MONITOR_TRACKS_3_4: snd_iprintf(buffer, " monitored ADAT tracks: 3+4\n"); break; case RME96_MONITOR_TRACKS_5_6: snd_iprintf(buffer, " monitored ADAT tracks: 5+6\n"); break; case RME96_MONITOR_TRACKS_7_8: snd_iprintf(buffer, " monitored ADAT tracks: 7+8\n"); break; } switch (snd_rme96_getattenuation(rme96)) { case RME96_ATTENUATION_0: snd_iprintf(buffer, " attenuation: 0 dB\n"); break; case RME96_ATTENUATION_6: snd_iprintf(buffer, " attenuation: -6 dB\n"); break; case RME96_ATTENUATION_12: snd_iprintf(buffer, " attenuation: -12 dB\n"); break; case RME96_ATTENUATION_18: snd_iprintf(buffer, " attenuation: -18 dB\n"); break; } snd_iprintf(buffer, " volume left: %u\n", rme96->vol[0]); snd_iprintf(buffer, " volume right: %u\n", rme96->vol[1]); } } static void __devinit snd_rme96_proc_init(rme96_t *rme96) { snd_info_entry_t *entry; if (! snd_card_proc_new(rme96->card, "rme96", &entry)) snd_info_set_text_ops(entry, rme96, 1024, snd_rme96_proc_read); } /* * control interface */ static int snd_rme96_info_loopback_control(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; uinfo->count = 1; uinfo->value.integer.min = 0; uinfo->value.integer.max = 1; return 0; } static int snd_rme96_get_loopback_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { rme96_t *rme96 = snd_kcontrol_chip(kcontrol); spin_lock_irq(&rme96->lock); ucontrol->value.integer.value[0] = rme96->wcreg & RME96_WCR_SEL ? 0 : 1; spin_unlock_irq(&rme96->lock); return 0; } static int snd_rme96_put_loopback_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { rme96_t *rme96 = snd_kcontrol_chip(kcontrol); unsigned int val; int change; val = ucontrol->value.integer.value[0] ? 0 : RME96_WCR_SEL; spin_lock_irq(&rme96->lock); val = (rme96->wcreg & ~RME96_WCR_SEL) | val; change = val != rme96->wcreg; rme96->wcreg = val; writel(val, rme96->iobase + RME96_IO_CONTROL_REGISTER); spin_unlock_irq(&rme96->lock); return change; } static int snd_rme96_info_inputtype_control(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) { static char *_texts[5] = { "Optical", "Coaxial", "Internal", "XLR", "Analog" }; rme96_t *rme96 = snd_kcontrol_chip(kcontrol); char *texts[5] = { _texts[0], _texts[1], _texts[2], _texts[3], _texts[4] }; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; switch (rme96->pci->device) { case PCI_DEVICE_ID_DIGI96: case PCI_DEVICE_ID_DIGI96_8: uinfo->value.enumerated.items = 3; break; case PCI_DEVICE_ID_DIGI96_8_PRO: uinfo->value.enumerated.items = 4; break; case PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST: if (rme96->rev > 4) { /* PST */ uinfo->value.enumerated.items = 4; texts[3] = _texts[4]; /* Analog instead of XLR */ } else { /* PAD */ uinfo->value.enumerated.items = 5; } break; default: snd_BUG(); break; } if (uinfo->value.enumerated.item > uinfo->value.enumerated.items - 1) { uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; } strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); return 0; } static int snd_rme96_get_inputtype_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { rme96_t *rme96 = snd_kcontrol_chip(kcontrol); unsigned int items = 3; spin_lock_irq(&rme96->lock); ucontrol->value.enumerated.item[0] = snd_rme96_getinputtype(rme96); switch (rme96->pci->device) { case PCI_DEVICE_ID_DIGI96: case PCI_DEVICE_ID_DIGI96_8: items = 3; break; case PCI_DEVICE_ID_DIGI96_8_PRO: items = 4; break; case PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST: if (rme96->rev > 4) { /* for handling PST case, (INPUT_ANALOG is moved to INPUT_XLR */ if (ucontrol->value.enumerated.item[0] == RME96_INPUT_ANALOG) { ucontrol->value.enumerated.item[0] = RME96_INPUT_XLR; } items = 4; } else { items = 5; } break; default: snd_BUG(); break; } if (ucontrol->value.enumerated.item[0] >= items) { ucontrol->value.enumerated.item[0] = items - 1; } spin_unlock_irq(&rme96->lock); return 0; } static int snd_rme96_put_inputtype_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { rme96_t *rme96 = snd_kcontrol_chip(kcontrol); unsigned int val; int change, items = 3; switch (rme96->pci->device) { case PCI_DEVICE_ID_DIGI96: case PCI_DEVICE_ID_DIGI96_8: items = 3; break; case PCI_DEVICE_ID_DIGI96_8_PRO: items = 4; break; case PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST: if (rme96->rev > 4) { items = 4; } else { items = 5; } break; default: snd_BUG(); break; } val = ucontrol->value.enumerated.item[0] % items; /* special case for PST */ if (rme96->pci->device == PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST && rme96->rev > 4) { if (val == RME96_INPUT_XLR) { val = RME96_INPUT_ANALOG; } } spin_lock_irq(&rme96->lock); change = (int)val != snd_rme96_getinputtype(rme96); snd_rme96_setinputtype(rme96, val); spin_unlock_irq(&rme96->lock); return change; } static int snd_rme96_info_clockmode_control(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) { static char *texts[3] = { "AutoSync", "Internal", "Word" }; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; uinfo->value.enumerated.items = 3; if (uinfo->value.enumerated.item > 2) { uinfo->value.enumerated.item = 2; } strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); return 0; } static int snd_rme96_get_clockmode_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { rme96_t *rme96 = snd_kcontrol_chip(kcontrol); spin_lock_irq(&rme96->lock); ucontrol->value.enumerated.item[0] = snd_rme96_getclockmode(rme96); spin_unlock_irq(&rme96->lock); return 0; } static int snd_rme96_put_clockmode_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { rme96_t *rme96 = snd_kcontrol_chip(kcontrol); unsigned int val; int change; val = ucontrol->value.enumerated.item[0] % 3; spin_lock_irq(&rme96->lock); change = (int)val != snd_rme96_getclockmode(rme96); snd_rme96_setclockmode(rme96, val); spin_unlock_irq(&rme96->lock); return change; } static int snd_rme96_info_attenuation_control(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) { static char *texts[4] = { "0 dB", "-6 dB", "-12 dB", "-18 dB" }; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; uinfo->value.enumerated.items = 4; if (uinfo->value.enumerated.item > 3) { uinfo->value.enumerated.item = 3; } strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); return 0; } static int snd_rme96_get_attenuation_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { rme96_t *rme96 = snd_kcontrol_chip(kcontrol); spin_lock_irq(&rme96->lock); ucontrol->value.enumerated.item[0] = snd_rme96_getattenuation(rme96); spin_unlock_irq(&rme96->lock); return 0; } static int snd_rme96_put_attenuation_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { rme96_t *rme96 = snd_kcontrol_chip(kcontrol); unsigned int val; int change; val = ucontrol->value.enumerated.item[0] % 4; spin_lock_irq(&rme96->lock); change = (int)val != snd_rme96_getattenuation(rme96); snd_rme96_setattenuation(rme96, val); spin_unlock_irq(&rme96->lock); return change; } static int snd_rme96_info_montracks_control(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) { static char *texts[4] = { "1+2", "3+4", "5+6", "7+8" }; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; uinfo->value.enumerated.items = 4; if (uinfo->value.enumerated.item > 3) { uinfo->value.enumerated.item = 3; } strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); return 0; } static int snd_rme96_get_montracks_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { rme96_t *rme96 = snd_kcontrol_chip(kcontrol); spin_lock_irq(&rme96->lock); ucontrol->value.enumerated.item[0] = snd_rme96_getmontracks(rme96); spin_unlock_irq(&rme96->lock); return 0; } static int snd_rme96_put_montracks_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { rme96_t *rme96 = snd_kcontrol_chip(kcontrol); unsigned int val; int change; val = ucontrol->value.enumerated.item[0] % 4; spin_lock_irq(&rme96->lock); change = (int)val != snd_rme96_getmontracks(rme96); snd_rme96_setmontracks(rme96, val); spin_unlock_irq(&rme96->lock); return change; } static u32 snd_rme96_convert_from_aes(snd_aes_iec958_t *aes) { u32 val = 0; val |= (aes->status[0] & IEC958_AES0_PROFESSIONAL) ? RME96_WCR_PRO : 0; val |= (aes->status[0] & IEC958_AES0_NONAUDIO) ? RME96_WCR_DOLBY : 0; if (val & RME96_WCR_PRO) val |= (aes->status[0] & IEC958_AES0_PRO_EMPHASIS_5015) ? RME96_WCR_EMP : 0; else val |= (aes->status[0] & IEC958_AES0_CON_EMPHASIS_5015) ? RME96_WCR_EMP : 0; return val; } static void snd_rme96_convert_to_aes(snd_aes_iec958_t *aes, u32 val) { aes->status[0] = ((val & RME96_WCR_PRO) ? IEC958_AES0_PROFESSIONAL : 0) | ((val & RME96_WCR_DOLBY) ? IEC958_AES0_NONAUDIO : 0); if (val & RME96_WCR_PRO) aes->status[0] |= (val & RME96_WCR_EMP) ? IEC958_AES0_PRO_EMPHASIS_5015 : 0; else aes->status[0] |= (val & RME96_WCR_EMP) ? IEC958_AES0_CON_EMPHASIS_5015 : 0; } static int snd_rme96_control_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; uinfo->count = 1; return 0; } static int snd_rme96_control_spdif_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { rme96_t *rme96 = snd_kcontrol_chip(kcontrol); snd_rme96_convert_to_aes(&ucontrol->value.iec958, rme96->wcreg_spdif); return 0; } static int snd_rme96_control_spdif_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { rme96_t *rme96 = snd_kcontrol_chip(kcontrol); int change; u32 val; val = snd_rme96_convert_from_aes(&ucontrol->value.iec958); spin_lock_irq(&rme96->lock); change = val != rme96->wcreg_spdif; rme96->wcreg_spdif = val; spin_unlock_irq(&rme96->lock); return change; } static int snd_rme96_control_spdif_stream_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; uinfo->count = 1; return 0; } static int snd_rme96_control_spdif_stream_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { rme96_t *rme96 = snd_kcontrol_chip(kcontrol); snd_rme96_convert_to_aes(&ucontrol->value.iec958, rme96->wcreg_spdif_stream); return 0; } static int snd_rme96_control_spdif_stream_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { rme96_t *rme96 = snd_kcontrol_chip(kcontrol); int change; u32 val; val = snd_rme96_convert_from_aes(&ucontrol->value.iec958); spin_lock_irq(&rme96->lock); change = val != rme96->wcreg_spdif_stream; rme96->wcreg_spdif_stream = val; rme96->wcreg &= ~(RME96_WCR_PRO | RME96_WCR_DOLBY | RME96_WCR_EMP); rme96->wcreg |= val; writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); spin_unlock_irq(&rme96->lock); return change; } static int snd_rme96_control_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; uinfo->count = 1; return 0; } static int snd_rme96_control_spdif_mask_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { ucontrol->value.iec958.status[0] = kcontrol->private_value; return 0; } static int snd_rme96_dac_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) { rme96_t *rme96 = snd_kcontrol_chip(kcontrol); uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 2; uinfo->value.integer.min = 0; uinfo->value.integer.max = RME96_185X_MAX_OUT(rme96); return 0; } static int snd_rme96_dac_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u) { rme96_t *rme96 = snd_kcontrol_chip(kcontrol); spin_lock_irq(&rme96->lock); u->value.integer.value[0] = rme96->vol[0]; u->value.integer.value[1] = rme96->vol[1]; spin_unlock_irq(&rme96->lock); return 0; } static int snd_rme96_dac_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u) { rme96_t *rme96 = snd_kcontrol_chip(kcontrol); int change = 0; if (!RME96_HAS_ANALOG_OUT(rme96)) { return -EINVAL; } spin_lock_irq(&rme96->lock); if (u->value.integer.value[0] != rme96->vol[0]) { rme96->vol[0] = u->value.integer.value[0]; change = 1; } if (u->value.integer.value[1] != rme96->vol[1]) { rme96->vol[1] = u->value.integer.value[1]; change = 1; } if (change) { snd_rme96_apply_dac_volume(rme96); } spin_unlock_irq(&rme96->lock); return change; } static snd_kcontrol_new_t snd_rme96_controls[] = { { .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), .info = snd_rme96_control_spdif_info, .get = snd_rme96_control_spdif_get, .put = snd_rme96_control_spdif_put }, { .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM), .info = snd_rme96_control_spdif_stream_info, .get = snd_rme96_control_spdif_stream_get, .put = snd_rme96_control_spdif_stream_put }, { .access = SNDRV_CTL_ELEM_ACCESS_READ, .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK), .info = snd_rme96_control_spdif_mask_info, .get = snd_rme96_control_spdif_mask_get, .private_value = IEC958_AES0_NONAUDIO | IEC958_AES0_PROFESSIONAL | IEC958_AES0_CON_EMPHASIS }, { .access = SNDRV_CTL_ELEM_ACCESS_READ, .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK), .info = snd_rme96_control_spdif_mask_info, .get = snd_rme96_control_spdif_mask_get, .private_value = IEC958_AES0_NONAUDIO | IEC958_AES0_PROFESSIONAL | IEC958_AES0_PRO_EMPHASIS }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Input Connector", .info = snd_rme96_info_inputtype_control, .get = snd_rme96_get_inputtype_control, .put = snd_rme96_put_inputtype_control }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Loopback Input", .info = snd_rme96_info_loopback_control, .get = snd_rme96_get_loopback_control, .put = snd_rme96_put_loopback_control }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Sample Clock Source", .info = snd_rme96_info_clockmode_control, .get = snd_rme96_get_clockmode_control, .put = snd_rme96_put_clockmode_control }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Monitor Tracks", .info = snd_rme96_info_montracks_control, .get = snd_rme96_get_montracks_control, .put = snd_rme96_put_montracks_control }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Attenuation", .info = snd_rme96_info_attenuation_control, .get = snd_rme96_get_attenuation_control, .put = snd_rme96_put_attenuation_control }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "DAC Playback Volume", .info = snd_rme96_dac_volume_info, .get = snd_rme96_dac_volume_get, .put = snd_rme96_dac_volume_put } }; static int snd_rme96_create_switches(snd_card_t *card, rme96_t *rme96) { int idx, err; snd_kcontrol_t *kctl; for (idx = 0; idx < 7; idx++) { if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_rme96_controls[idx], rme96))) < 0) return err; if (idx == 1) /* IEC958 (S/PDIF) Stream */ rme96->spdif_ctl = kctl; } if (RME96_HAS_ANALOG_OUT(rme96)) { for (idx = 7; idx < 10; idx++) if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_rme96_controls[idx], rme96))) < 0) return err; } return 0; } /* * Card initialisation */ static void snd_rme96_card_free(snd_card_t *card) { snd_rme96_free(card->private_data); } static int __devinit snd_rme96_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) { static int dev; rme96_t *rme96; snd_card_t *card; int err; u8 val; if (dev >= SNDRV_CARDS) { return -ENODEV; } if (!enable[dev]) { dev++; return -ENOENT; } if ((card = snd_card_new(index[dev], id[dev], THIS_MODULE, sizeof(rme96_t))) == NULL) return -ENOMEM; card->private_free = snd_rme96_card_free; rme96 = (rme96_t *)card->private_data; rme96->card = card; rme96->pci = pci; snd_card_set_dev(card, &pci->dev); if ((err = snd_rme96_create(rme96)) < 0) { snd_card_free(card); return err; } strcpy(card->driver, "Digi96"); switch (rme96->pci->device) { case PCI_DEVICE_ID_DIGI96: strcpy(card->shortname, "RME Digi96"); break; case PCI_DEVICE_ID_DIGI96_8: strcpy(card->shortname, "RME Digi96/8"); break; case PCI_DEVICE_ID_DIGI96_8_PRO: strcpy(card->shortname, "RME Digi96/8 PRO"); break; case PCI_DEVICE_ID_DIGI96_8_PAD_OR_PST: pci_read_config_byte(rme96->pci, 8, &val); if (val < 5) { strcpy(card->shortname, "RME Digi96/8 PAD"); } else { strcpy(card->shortname, "RME Digi96/8 PST"); } break; } sprintf(card->longname, "%s at 0x%lx, irq %d", card->shortname, rme96->port, rme96->irq); if ((err = snd_card_register(card)) < 0) { snd_card_free(card); return err; } pci_set_drvdata(pci, card); dev++; return 0; } static void __devexit snd_rme96_remove(struct pci_dev *pci) { snd_card_free(pci_get_drvdata(pci)); pci_set_drvdata(pci, NULL); } static struct pci_driver driver = { .name = "RME Digi96", .id_table = snd_rme96_ids, .probe = snd_rme96_probe, .remove = __devexit_p(snd_rme96_remove), }; static int __init alsa_card_rme96_init(void) { return pci_register_driver(&driver); } static void __exit alsa_card_rme96_exit(void) { pci_unregister_driver(&driver); } module_init(alsa_card_rme96_init) module_exit(alsa_card_rme96_exit)