-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathpatch_cirrus_apple.h
3083 lines (2438 loc) · 116 KB
/
patch_cirrus_apple.h
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
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
//#include <linux/ctype.h>
#include <linux/timer.h>
#include <linux/bitops.h>
#include <linux/version.h>
//#include "hda_generic.h"
//#include "patch_cs8409.h"
// define some explicit debugging print functions
// under flag control so can be easily turned off
#ifdef MYSOUNDDEBUGFULL
#define mycodec_info(codec, fmt, args...) \
dev_info(hda_codec_dev(codec), fmt, ##args)
#define mycodec_i2c_info(codec, fmt, args...) \
dev_info(hda_codec_dev(codec), fmt, ##args)
#define mydev_info(codecdev, fmt, args...) \
dev_info(codecdev, fmt, ##args)
#define mycodec_dbg(codec, fmt, args...) \
dev_info(hda_codec_dev(codec), fmt, ##args)
#define myprintk_dbg(fmt, args...) \
printk(fmt, ##args)
#define myprintk(fmt, args...) \
printk(fmt, ##args)
#else
#define mycodec_dbg(...)
#define myprintk_dbg(...)
#ifdef MYSOUNDDEBUG
#define mycodec_info(codec, fmt, args...) \
dev_info(hda_codec_dev(codec), fmt, ##args)
#define mycodec_i2c_info(codec, fmt, args...) \
dev_info(hda_codec_dev(codec), fmt, ##args)
#define mydev_info(codecdev, fmt, args...) \
dev_info(codecdev, fmt, ##args)
#define myprintk(fmt, args...) \
printk(fmt, ##args)
#else
#define mycodec_info(...)
#define mycodec_i2c_info(...)
#define mydev_info(...)
#define myprintk(...)
#endif
#endif
/*
info for enum for checking initial assignment
enum {
AC_JACK_LINE_OUT, 0x0
AC_JACK_SPEAKER, 0x1
AC_JACK_HP_OUT, 0x2
AC_JACK_CD, 0x3
AC_JACK_SPDIF_OUT, 0x4
AC_JACK_DIG_OTHER_OUT, 0x5
AC_JACK_MODEM_LINE_SIDE, 0x6
AC_JACK_MODEM_HAND_SIDE, 0x7
AC_JACK_LINE_IN, 0x8
AC_JACK_AUX, 0x9
AC_JACK_MIC_IN, 0xa
AC_JACK_TELEPHONY, 0xb
AC_JACK_SPDIF_IN, 0xc
AC_JACK_DIG_OTHER_IN, 0xd
AC_JACK_OTHER = 0xf, 0xf
};
*/
/*
it appears from the cs42l42 definitions in patch_cs8409.h that
each nid is associated with a specific Audio Serial Port
nids as used in Apple
output
speaker 0x002 -> 0x024 CS8409_PIN_ASP1_OUT_A -> CS8409_PIN_ASP1_TRANSMITTER_A
speaker 0x003 -> 0x025 CS8409_PIN_ASP1_OUT_B -> CS8409_PIN_ASP1_TRANSMITTER_B
headphones 0x00a -> 0x02c CS8409_PIN_ASP2_OUT_A -> CS8409_PIN_ASP2_TRANSMITTER_A
input
headset mike 0x03c -> 0x01a CS8409_PIN_ASP2_RECEIVER_A -> CS8409_PIN_ASP2_IN_A
macbook pros
internal mike 0x044 -> 0x022 CS8409_PIN_DMIC1_IN -> CS8409_PIN_DMIC1
linein 0x045 -> 0x023 CS8409_PIN_DMIC2_IN -> CS8409_PIN_DMIC2
imacs
linein 0x044 -> 0x022 CS8409_PIN_DMIC1_IN -> CS8409_PIN_DMIC1
internal mike 0x045 -> 0x023 CS8409_PIN_DMIC2_IN -> CS8409_PIN_DMIC2
*/
/* CS8409 */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0)
// for the moment leave my old definition names but map to the new module enum values
#define CS8409_IDX_DEV_CFG CS8409_PIN_AFG
#define CS8409_VENDOR_NID CS8409_PIN_VENDOR_WIDGET
#define CS8409_BEEP_NID CS8409_PIN_BEEP_GEN
#else
#define CS8409_IDX_DEV_CFG 0x01
#define CS8409_VENDOR_NID 0x47
#define CS8409_BEEP_NID 0x46
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0)
// nid devs based on Dell fixups
#define CS8409_CS42L83_HP_PIN_NID CS8409_PIN_ASP2_TRANSMITTER_A
#define CS8409_CS42L83_HP_MIC_PIN_NID CS8409_PIN_ASP2_RECEIVER_A
#define CS8409_CS42L83_MACBOOK_MIC_PIN_NID CS8409_PIN_DMIC1_IN
#define CS8409_CS42L83_MACBOOK_LINEIN_PIN_NID CS8409_PIN_DMIC2_IN
#define CS8409_CS42L83_IMAC_MIC_PIN_NID CS8409_PIN_DMIC2_IN
#define CS8409_CS42L83_IMAC_LINEIN_PIN_NID CS8409_PIN_DMIC1_IN
#define CS8409_CS42L83_MACBOOK_LINEIN_ADC_PIN_NID CS8409_PIN_DMIC2
#define CS8409_CS42L83_IMAC_LINEIN_ADC_PIN_NID CS8409_PIN_DMIC1
// add in coding based on Dell fixups
#define CS42L83_I2C_ADDR 0x90 // for some reason given as (0x48 << 1)
#define CS8409_CS42L83_RESET 0x02 // gpio interrupt mask - see cs42l83_external_control_GPIO
#define CS8409_CS42L83_INT 0x01 // gpio interrupt mask
#else
#define CS8409_CS42L83_HP_PIN_NID 0x2c
#define CS8409_CS42L83_HP_MIC_PIN_NID 0x3c
#define CS8409_CS42L83_MACBOOK_MIC_PIN_NID 0x44
#define CS8409_CS42L83_MACBOOK_LINEIN_PIN_NID 0x45
#define CS8409_CS42L83_IMAC_MIC_PIN_NID 0x45
#define CS8409_CS42L83_IMAC_LINEIN_PIN_NID 0x44
#define CS8409_CS42L83_MACBOOK_LINEIN_ADC_PIN_NID 0x23
#define CS8409_CS42L83_IMAC_LINEIN_ADC_PIN_NID 0x22
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0)
// this is a list of i2c commands for init
static const struct cs8409_i2c_param cs42l83_init_reg_seq[] = {
//{ 0x0000, 0x00 },
};
struct sub_codec cs8409_cs42l83_codec = {
.addr = CS42L83_I2C_ADDR,
.reset_gpio = CS8409_CS42L83_RESET,
.irq_mask = CS8409_CS42L83_INT,
.init_seq = cs42l83_init_reg_seq,
.init_seq_num = ARRAY_SIZE(cs42l83_init_reg_seq),
.hp_jack_in = 0,
.mic_jack_in = 0,
.linein_jack_in = 0,
.paged = 1,
.suspended = 1,
.no_type_dect = 0,
};
// define the fixed stream format
const struct hda_pcm_stream cs42l83_apple_pcm_analog_playback = {
.rates = SNDRV_PCM_RATE_44100, /* fixed rate */
};
const struct hda_pcm_stream cs42l83_apple_pcm_analog_capture = {
.rates = SNDRV_PCM_RATE_44100, /* fixed rate */
};
#endif
/*
register mask values collated from Microsoft cs4208_39.inf file plus the new Dell cs8409 module
CS8409_DEV_CFG1, // reg 0x00
0xb008 +PLL1/2_EN, +I2C_EN - .inf file
0xb000 +PLL1/2_EN - .inf file
0x9008 +PLL1_EN, +I2C_EN - .inf file
0x9000 +PLL1_EN - .inf file
CS8409_DEV_CFG2, // reg 0x01
0x0006 ASP1/2_EN = 0, ASP1/2_STP = 1
0x0066 ASP1/2_EN = 1, ASP1/2_STP = 1
0x0022 ASP1_EN = 1, ASP1_STP = 1 ?
0x0044 ASP2_EN = 1, ASP2_STP = 1 ?
0x0200 seen in Apple but unknown
CS8409_DEV_CFG3, // reg 0x02
0x0a80 ASP1/2_BUS_IDLE=10, +GPIO_I2C
0x0280 seen in Apple ASP1_BUS_IDLE=10?, +GPIO_I2C
CS8409_ASP1_CLK_CTRL1, // reg 0x03
0x8000 ASP1: LCHI = 00h
CS8409_ASP1_CLK_CTRL2, // reg 0x04
0x28ff ASP1: MC/SC_SRCSEL=PLL1, LCPR=FFh
CS8409_ASP1_CLK_CTRL3, // reg 0x05
0x0062 ASP1: MCEN=0, FSD=011, SCPOL_IN/OUT=0, SCDIV=1:4
0b0110 0b0010
so FSD = 0x0060
0x005a ASP1: MCEN = 0, FSD = 010, SCPOL_IN/OUT = 1, SCDIV = 1:4
0b0101 0b1010
so FSD = 0x0040, SCPOL_IN/OUT = 0x0010 or 0x0008?, SCDIV = 0x0002
0x0001 seen in Apple but what bit is this?? MCEN??
CS8409_ASP2_CLK_CTRL1, // reg 0x06
0x8000 ASP1: LCHI = 00h
CS8409_ASP2_CLK_CTRL2, // reg 0x07
0x283f ASP2: MC/SC_SRCSEL=PLL1, LCPR=3Fh
CS8409_ASP2_CLK_CTRL3, // reg 0x08
0x805c ASP2: 5050=1, MCEN=0, FSD=010, SCPOL_IN/OUT=1, SCDIV=1:16
0b0101 0b1100
5050 = 0x8000?, FSD = 0x0040, SCPOL_IN/OUT = 0x0010 or 0x0008?, SCDIV=1:16 = 0x0004?
CS8409_DMIC_CFG, // reg 0x09
0x0023 DMIC1_MO=10b, DMIC1/2_SR=1
0x0033 seen in Apple
0x00b3 seen in Apple
0x01b3 seen in Apple
CS8409_BEEP_CFG, // reg 0x0a
ASP2_Rx_NULL_INS_RMV, // reg 0x11
0x0001 seen in Apple
ASP2_Rx_RATE1, // reg 0x12
0xaccc seen in Apple
ASP2_Tx_NULL_INS_RMV, // reg 0x14
0x0100 seen in Apple
ASP2_Tx_RATE1, // reg 0x15
0xaaaa seen in Apple
ASP1_SYNC_CTRL, // reg 0x17
0x0000 seen in Apple sync converters
0x0001 seen in Apple sync converters
0x0002 seen in Apple sync converters
0x0003 seen in Apple sync converters
ASP2_SYNC_CTRL, // reg 0x18
0x0000 seen in Apple sync converters
the following registers are defined per output nid
from A to H for nid 0x02 to 0x09 ASP1
from A to H for nid 0x0a to 0x11 ASP2
the following registers are defined per input nid
from A to H for nid 0x12 to 0x19 ASP1
from A to H for nid 0x1a to 0x21 ASP2
ASP1_A_TX_CTRL1 // reg 0x19
nide 0x02
0x0800 seen in Apple - on
0x8800 seen in Apple - off
ASP1_A_TX_CTRL2 // reg 0x1a
nide 0x02
0x0820 seen in Apple - on
0x8820 seen in Apple - off
ASP1_B_TX_CTRL1, // reg 0x1b
nide 0x03
0x0840 seen in Apple - on
0x8840 seen in Apple - off
ASP1_B_TX_CTRL2, // reg 0x1c
nide 0x03
0x0860 seen in Apple - on
0x8860 seen in Apple - off
ASP2_A_TX_CTRL1, // reg 0x29
nide 0x0a
0x0800 seen in Apple - on
0x8800 seen in Apple - off
ASP2_A_TX_CTRL2, // reg 0x2a
nide 0x0a
0x0820 seen in Apple - on
0x8820 seen in Apple - off
ASP2_A_RX_CTRL1, // reg 0x49
nide 0x1a
0x0800 seen in Apple - on
0x8800 seen in Apple - off
ASP2_A_RX_CTRL2, // reg 0x4a
nide 0x1a
0x0820 seen in Apple - on
0x8820 seen in Apple - off
CS8409_ASP1_INTRN_STS, // reg 0x6b
0x001f seen in Apple
CS8409_ASP2_INTRN_STS, // reg 0x6c
0x001f seen in Apple
CS8409_ASP_UNS_RESP_MASK, // reg 0x71
0x400f seen in Apple
0x800f seen in Apple
0xc00f seen in Apple
CS8409_PAD_CFG_SLW_RATE_CTRL // reg 0x82
it appears to contain 2 sort of separate items - the ASP1 and ASP2 enables and the DMIC1/DMIC2 SCL enables
0xfc03 ASP1/2_xxx_EN=1, ASP1/2_MCLK_EN=0, DMIC1/2_SCL_EN=1 (was DMIC1_SCL_EN in comments but thinks thats wrong given below)
0xfc01 (ASP1/2_xxx_EN = 1, ASP1/2_MCLK_EN = 0, DMIC1_SCL_EN = 1)
0xff03 (ASP1/2_xxx_EN = 1, DMIC1/2_SCL_EN = 1)
0xfd02 (ASP1/2_xxx_EN = 1, ASP1_MCLK_EN = 1, ASP2_MCLK_EN = 0, DMIC2_SCL_EN = 1)
0xfe03 (ASP1/2_xxx_EN = 1, ASP1_MCLK_EN = 0, ASP2_MCLK_EN = 1, DMIC1/2_SCL_EN = 1)
so (ASP1_MCLK_EN is 0x0100 and ASP2_MCLK_EN is 0x0200)
from the OSX codes we seem to have
0xa800 ASP2_xxx_EN = 1, ASP1/2_MCLK_EN = 0
0x5400 ASP1_xxx_EN = 1, ASP1/2_MCLK_EN = 0
*/
/*
Interrupt analysis (mainly from cs42l42 manual)
actual button presses are 0x01, 0x02 and button release 0x10
for 0x1b7c 0x02 is a short release for buttons, 0x08 is reserved
the mask bits for 0x1b7a seem to be 0xe7 for button detect defining 0x18 as the button detect interrupt(s)
and 0xdc for actual button interrupts
(0x1b79 is mask, 0x1b7b status; 0x1b7a is mask, 0x1b7c is presumed status, 0x131b is mask, 0x1308 status,
0x1320 is mask, 0x130f status)
0x1b79 Detect Interrupt Mask 1
0x80 M_HSBIAS_SENSE
0x40 M_TIP_SENSE_PLUG
0x20 M_TIP_SENSE_UNPLUG
0x1b7b Detect Interrupt Status 1 - assumed - not documented in cs42l42 but listed in fig 4-45
0x80 M_HSBIAS_SENSE - assumed
0x40 M_TIP_SENSE_PLUG - assumed
0x20 M_TIP_SENSE_UNPLUG - assumed
0x1b7a Detect Interrupt Mask 2 (Buttons)
cs42l42 values documented:
0x80 M_DETECT_TRUE_FALSE
0x40 M_DETECT_FALSE_TRUE
0x04 M_HSBIAS_HIZ
0x02 M_SHORT_RELEASE
0x01 M_SHORT_DETECTED
OSX button masks seen are 0xff, 0xe7 and 0xdc
so for 0xe7 mask status bits are 0x18
so for 0xdc mask status bits are 0x23
bits 0x38 are not documented for cs42l42
0xe7 is used for button detection
0xdc is used for button responses
0x1b7c Detect Interrupt Status 2 (Buttons)
- cs42l42 documents a 0x130a as Detect Interrupt Status 2
no such register ever used on OSX
assuming this is a difference for the cs42l83
0x80 M_DETECT_TRUE_FALSE
0x40 M_DETECT_FALSE_TRUE
0x04 M_HSBIAS_HIZ
0x02 M_SHORT_RELEASE
0x01 M_SHORT_DETECTED
OSX status values seen
0x0a after 0xe7 mask set
0x40 pre configure button response mask
0x04 pre disable button interrupts
0x1b78 Detect Status 2 - seems to be associated with buttons and/or mike
0x02 HS_TRUE - cs42l42
0x01 SHORT_TRUE - cs42l42
OSX values seen
0x40 button detect
0x02 mike sense
new values seen for imacs with non-Apple headsets
0x20 button detect??
0x131b Codec Interrupt Mask
0x02 M_HSDET_AUTO_DONE
0x01 M_PDN_DONE
0x1308 Codec Interrupt Status
0x02 M_HSDET_AUTO_DONE
0x01 M_PDN_DONE
0x1320 Tip/Ring Sense Plug/Unplug Interrupt Mask
0x08 M_TS_UNPLUG
0x04 M_TS_PLUG
0x02 M_RS_UNPLUG
0x01 M_RS_PLUG
0x130f Tip/Ring Sense Plug/Unplug Interrupt Status
0x08 M_TS_UNPLUG
0x04 M_TS_PLUG
0x02 M_RS_UNPLUG
0x01 M_RS_PLUG
register 0x1b7b:
TIP_SENSE_PLUG 0x40
TIP_SENSE_UNPLUG 0x20
BUTTON_DOWN_PRESS 0x1
BUTTON_UP_PRESS 0x2
BUTTON_RELEASE 0x10
BUTTONS (BUTTON_UP_PRESS | BUTTON_DOWN_PRESS)
register 0x1b7c:
BUTTON_DETECT_MAIN 0x18 // we only see 0x08 but the mask allows for these 2 bits
register 0x1b78:
BUTTON_DETECT_MASK 0x60 // now seen 2 bits used - plausibly depends if Apple with buttons (0x40) or non-Apple with buttons (0x20)
BUTTON_DETECT1 0x40
BUTTON_DETECT2 0x20
MIKE_CONNECT 0x02
register 0x131b:
HSDET_AUTO_DONE 0x02
PDN_DONE 0x01
*/
/*
*/
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 13, 0)
struct unsol_item {
struct list_head list;
unsigned int idx;
unsigned int res;
};
struct hda_cvt_setup_apple {
hda_nid_t nid;
u8 stream_tag;
u8 channel_id;
u16 format_id;
unsigned char active; /* cvt is currently used */
unsigned char dirty; /* setups should be cleared */
};
struct cs8409_apple_spec {
struct hda_gen_spec gen;
unsigned int gpio_mask;
unsigned int gpio_dir;
unsigned int gpio_data;
unsigned int gpio_eapd_hp; /* EAPD GPIO bit for headphones */
unsigned int gpio_eapd_speaker; /* EAPD GPIO bit for speakers */
/* CS421x */
unsigned int spdif_detect:1;
unsigned int spdif_present:1;
unsigned int sense_b:1;
hda_nid_t vendor_nid;
/* digital beep */
hda_nid_t beep_nid;
/* for MBP SPDIF control */
int (*spdif_sw_put)(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
// so it appears we have "concurrency" in the linux HDA code
// in that if unsolicited responses occur which perform extensive verbs
// the hda verbs are intermixed with eg extensive start playback verbs
// on OSX we appear to have blocks of verbs during which unsolicited responses
// are logged but the unsolicited verbs occur after the verb block
// this flag is used to flag such verb blocks and the list will store the
// responses
// we use a pre-allocated list - if we have more than 10 outstanding unsols
// we will drop
// not clear if mutexes would be the way to go
int block_unsol;
struct list_head unsol_list;
struct unsol_item unsol_items_prealloc[10];
int unsol_items_prealloc_used[10];
// add in specific nids for the intmike and linein as they seem to swap
// between macbook pros (14,3) and imacs (18,3)
int intmike_nid;
int linein_nid;
int intmike_adc_nid;
int linein_amp_nid;
// the following flag bits also need swapping
int reg9_intmike_dmic_mo;
int reg9_linein_dmic_mo;
int reg82_intmike_dmic_scl;
int reg82_linein_dmic_scl;
// add explicit stream format store entries as per hda_codec using a local definition
// of hda_cvt_setup (which is local to hda_codec.c)
// also use explicit nid versions
// (except that means either need explicit functions for each nid or have to lookup
// nid each time want to use in a generic function with nid argument)
struct hda_cvt_setup_apple nid_0x02;
struct hda_cvt_setup_apple nid_0x03;
struct hda_cvt_setup_apple nid_0x0a;
struct hda_cvt_setup_apple nid_0x22;
struct hda_cvt_setup_apple nid_0x23;
struct hda_cvt_setup_apple nid_0x1a;
// new item to deal with jack presence as Apple seems to have barfed
// the HDA spec by using a separate headphone chip
int jack_present;
// save the type of headphone connected
int headset_type;
// if headphone has mike or not
int have_mike;
// if headphone has buttons or not
int have_buttons;
// current stream channel count
int stream_channels;
// set when playing for plug/unplug events while playing
int playing;
// set when capturing for plug/unplug events while capturing
int capturing;
// changing coding - OSX sets up the format on plugin
// then does some minimal setup when start play
// initial coding delayed any format setup till actually play
// this works for no mike but not for mike - we need to initialize
// the mike on plugin
// this flag will be set when we have done the format setup
// so know if need to do it on play or not
// now need 2 flags - one for play and one for capture
int headset_play_format_setup_needed;
int headset_capture_format_setup_needed;
int headset_presetup_done;
int use_data;
// this is new item for dealing with headset plugins
// so can distinguish which phase we are in if have multiple interrupts
// now primarily used to indicate if booted with headset plugged in
int headset_phase;
// another dirty hack item to manage the different headset enable codes
int headset_enable;
int play_init;
int capture_init;
// new item to limit times we redo unmute/play
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
struct timespec64 last_play_time;
#else
struct timespec last_play_time;
#endif
// record the first play time - we have a problem there
// some initial plays that I dont understand - so skip any setup
// till sometime after the first play
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
struct timespec64 first_play_time;
#else
struct timespec first_play_time;
#endif
};
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0)
#define cs8409_apple_spec cs8409_spec
#endif
// coding copied from hda_generic.c to print the nid path details
#define debug_badness(fmt, ...) \
mycodec_dbg(codec, fmt, ##__VA_ARGS__)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0)
static void print_nid_path(struct hda_codec *codec,
const char *pfx, struct nid_path *path)
{
char buf[40];
char *pos = buf;
int i;
*pos = 0;
for (i = 0; i < path->depth; i++)
pos += scnprintf(pos, sizeof(buf) - (pos - buf), "%s%02x",
pos != buf ? ":" : "",
path->path[i]);
mycodec_dbg(codec, "%s path: depth=%d '%s'\n", pfx, path->depth, buf);
}
#else
static void print_nid_path(struct hda_codec *codec,
const char *pfx, struct nid_path *path);
#endif
static inline void print_nid_path_idx(struct hda_codec *codec,
const char *pfx, int idx)
{
struct nid_path *path;
path = snd_hda_get_path_from_idx(codec, idx);
if (path)
print_nid_path(codec, pfx, path);
}
static void debug_show_configs(struct hda_codec *codec,
struct auto_pin_cfg *cfg)
{
struct hda_gen_spec *spec = codec->spec;
static const char * const lo_type[3] = { "LO", "SP", "HP" };
int i;
debug_badness("debug_show_configs start\n");
debug_badness("multi_outs = %x/%x/%x/%x : %x/%x/%x/%x (type %s)\n",
cfg->line_out_pins[0], cfg->line_out_pins[1],
cfg->line_out_pins[2], cfg->line_out_pins[3],
spec->multiout.dac_nids[0],
spec->multiout.dac_nids[1],
spec->multiout.dac_nids[2],
spec->multiout.dac_nids[3],
lo_type[cfg->line_out_type]);
for (i = 0; i < cfg->line_outs; i++)
print_nid_path_idx(codec, " out", spec->out_paths[i]);
if (spec->multi_ios > 0)
debug_badness("multi_ios(%d) = %x/%x : %x/%x\n",
spec->multi_ios,
spec->multi_io[0].pin, spec->multi_io[1].pin,
spec->multi_io[0].dac, spec->multi_io[1].dac);
for (i = 0; i < spec->multi_ios; i++)
print_nid_path_idx(codec, " mio",
spec->out_paths[cfg->line_outs + i]);
if (cfg->hp_outs)
debug_badness("hp_outs = %x/%x/%x/%x : %x/%x/%x/%x\n",
cfg->hp_pins[0], cfg->hp_pins[1],
cfg->hp_pins[2], cfg->hp_pins[3],
spec->multiout.hp_out_nid[0],
spec->multiout.hp_out_nid[1],
spec->multiout.hp_out_nid[2],
spec->multiout.hp_out_nid[3]);
for (i = 0; i < cfg->hp_outs; i++)
print_nid_path_idx(codec, " hp ", spec->hp_paths[i]);
if (cfg->speaker_outs)
debug_badness("spk_outs = %x/%x/%x/%x : %x/%x/%x/%x\n",
cfg->speaker_pins[0], cfg->speaker_pins[1],
cfg->speaker_pins[2], cfg->speaker_pins[3],
spec->multiout.extra_out_nid[0],
spec->multiout.extra_out_nid[1],
spec->multiout.extra_out_nid[2],
spec->multiout.extra_out_nid[3]);
for (i = 0; i < cfg->speaker_outs; i++)
print_nid_path_idx(codec, " spk", spec->speaker_paths[i]);
for (i = 0; i < 3; i++)
print_nid_path_idx(codec, " mix", spec->aamix_out_paths[i]);
debug_badness("debug_show_configs end\n");
}
// attempt at an explicit setup ie not generic
//#include "patch_cirrus_explicit.h"
static void cs_8409_pcm_playback_pre_prepare_hook(struct hda_pcm_stream *hinfo, struct hda_codec *codec,
unsigned int stream_tag, unsigned int format, struct snd_pcm_substream *substream,
int action);
// this is a copy from playback_pcm_prepare in hda_generic.c
// initially I needed to do the Apple setup BEFORE the snd_hda_multi_out_analog_prepare
// in order to overwrite the Apple setup with the actual format/stream id
// NOTA BENE - if playback_pcm_prepare is changed in hda_generic.c then
// those changes must be re-implemented here
// we need this order because snd_hda_multi_out_analog_prepare writes the
// the format and stream id's to the audio nodes
//// so far we have left the Apple setup of the nodes format and stream id's in
// now updated to set the actual format where Apple does the format/stream id setup
// Apples format is very specifically S24_3LE (24 bit), 4 channel, 44.1 kHz
// S24_3LE seems to be very difficult to create so best Ive done is
// S24_LE (24 in 32 bits) or S32_LE
// it seems the digital setup is able to handle this with the Apple TDM
// setup but if we use the normal prepare hook order this overrwites
// the node linux 0x2, 0x3 setup with the Apple setup which leads to noise
// (the HDA specs say the node format setup must match the data)
// if we do the Apple setup and then the snd_hda_multi_out_analog_prepare
// the nodes will have the slightly different but working format
// with proper update of stream format at same point as in Apple log we need to pass
// the actual playback format as passed to this routine to our new "hook"
// cs_8409_pcm_playback_pre_prepare_hook
// to define the cached format correctly in that routine
// so far my analysis is that hinfo stores the stream format in the kernel format style
// but what is passed to cs_8409_playback_pcm_prepare is the format in HDA style
// not yet figured how to convert from kernel format style to HDA style
static int cs_8409_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
unsigned int stream_tag,
unsigned int format,
struct snd_pcm_substream *substream)
{
struct hda_gen_spec *spec = codec->spec;
int err;
mycodec_dbg(codec, "cs_8409_playback_pcm_prepare\n");
mycodec_dbg(codec, "cs_8409_playback_pcm_prepare: NID=0x%x, stream=0x%x, format=0x%x\n",
hinfo->nid, stream_tag, format);
cs_8409_pcm_playback_pre_prepare_hook(hinfo, codec, stream_tag, format, substream,
HDA_GEN_PCM_ACT_PREPARE);
// now dont think we need this - we now explicitly copy the 1st 2 channels to 2nd 2 channels
// if given 2 channel input
// plus all headset output is also explicitly being done
// well thats unexpected - if we comment this we loose headphone output
//err = snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
// stream_tag, format, substream);
err = 0;
// we cant call directly as call_pcm_playback_hook is local to hda_generic.c
//if (!err)
// call_pcm_playback_hook(hinfo, codec, substream,
// HDA_GEN_PCM_ACT_PREPARE);
// but its a trivial function - at least for the moment!!
if (err)
mycodec_dbg(codec, "cs_8409_playback_pcm_prepare err %d\n", err);
if (!err)
if (spec->pcm_playback_hook)
spec->pcm_playback_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_PREPARE);
return err;
}
static void cs_8409_pcm_capture_pre_prepare_hook(struct hda_pcm_stream *hinfo, struct hda_codec *codec,
unsigned int stream_tag, unsigned int format, struct snd_pcm_substream *substream,
int action);
// this is a copy from capture_pcm_prepare in hda_generic.c
// NOTA BENE - if capture_pcm_prepare is changed in hda_generic.c then
// those changes must be re-implemented here
static int cs_8409_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
unsigned int stream_tag,
unsigned int format,
struct snd_pcm_substream *substream)
{
struct cs8409_apple_spec *spec = codec->spec;
mycodec_dbg(codec, "cs_8409_capture_pcm_prepare\n");
mycodec_dbg(codec, "cs_8409_capture_pcm_prepare: NID=0x%x, stream=0x%x, format=0x%x\n",
hinfo->nid, stream_tag, format);
cs_8409_pcm_capture_pre_prepare_hook(hinfo, codec, stream_tag, format, substream,
HDA_GEN_PCM_ACT_PREPARE);
// we have a problem - this has to handle 2 different types of stream - the internal mike
// and the external headset mike (cs42l83)
// NOTE - the following snd_hda_codec_stream no longer do anything
// we have already set the stream data in the pre prepare hook
// - so as the format here is same (or at least should be!!) as that setup there is no format difference to that
// cached and snd_hda_coded_setup_stream does nothing
if (hinfo->nid == spec->intmike_adc_nid)
{
// so this is getting stranger and stranger
// the most valid recording is S24_3LE (0x4031) - except that the data we get out is S32_LE (low byte 0)
// - so it doesnt play right - and it messes with arecords vumeter
// (S32_LE is officially 0x4041 - but using that format doesnt seem to have valid data - audio very low)
//// so now try forcing the format here to 0x4031
//// well that fails miserably - the format mismatch stops data totally
// it now appears we get the same data with either 0x4031 or 0x4041 - both are low volume
// - however scaling (normalizing) in audacity we get the right sound with similar quality to OSX
// so now think the low volume is right - and OSX must be scaling/processing the data in CoreAudio
// (is the internal mike a fake 24 bits - ie its actually 16 bits but stuffed in the low end of the
// 24 bits - hence low volume - preliminary scaling attempts in audacity suggest this might be true!!)
snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format);
}
else if (hinfo->nid == 0x1a)
{
// do we need a pre-prepare function??
// maybe for this the external mike ie cs42l83 input
snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format);
}
else
dev_info(hda_codec_dev(codec), "cs_8409_capture_pcm_prepare - UNIMPLEMENTED input nid 0x%x\n",hinfo->nid);
// we cant call directly as call_pcm_capture_hook is local to hda_generic.c
//call_pcm_capture_hook(hinfo, codec, substream,
// HDA_GEN_PCM_ACT_PREPARE);
// but its a trivial function - at least for the moment!!
// note this hook if defined also needs to switch between the 2 versions of input!!
if (spec->gen.pcm_capture_hook)
spec->gen.pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_PREPARE);
return 0;
}
// another copied routine as this is local to hda_jack.c
static struct hda_jack_tbl *
cs8409_any_jack_tbl_get_from_nid(struct hda_codec *codec, hda_nid_t nid)
{
struct hda_jack_tbl *jack = codec->jacktbl.list;
int i;
if (!nid || !jack)
return NULL;
for (i = 0; i < codec->jacktbl.used; i++, jack++)
if (jack->nid == nid)
return jack;
return NULL;
}
// another copied routine as this is local to hda_jack.c
static struct hda_jack_tbl *
cs_8409_hda_jack_tbl_new(struct hda_codec *codec, hda_nid_t nid, int dev_id)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
struct hda_jack_tbl *jack = snd_hda_jack_tbl_get_mst(codec, nid, dev_id);
#else
struct hda_jack_tbl *jack = snd_hda_jack_tbl_get(codec, nid);
#endif
struct hda_jack_tbl *existing_nid_jack = cs8409_any_jack_tbl_get_from_nid(codec, nid);
WARN_ON(dev_id != 0 && !codec->dp_mst);
if (jack)
return jack;
jack = snd_array_new(&codec->jacktbl);
if (!jack)
return NULL;
jack->nid = nid;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
jack->dev_id = dev_id;
#endif
jack->jack_dirty = 1;
if (existing_nid_jack) {
jack->tag = existing_nid_jack->tag;
/*
* Copy jack_detect from existing_nid_jack to avoid
* snd_hda_jack_detect_enable_callback_mst() making multiple
* SET_UNSOLICITED_ENABLE calls on the same pin.
*/
jack->jack_detect = existing_nid_jack->jack_detect;
} else {
jack->tag = codec->jacktbl.used;
}
//// use this to prevent AC_VERB_GET_PIN_SENSE (f09) verbs being sent - not seen in OSX logs
// well it does but it also makes the jack seem permanently connected
// now going with Dell style fixup
//jack->phantom_jack = 1;
return jack;
}
// another copied routine as this is local to hda_jack.c
static struct hda_jack_callback *
cs_8409_find_callback_from_list(struct hda_jack_tbl *jack,
hda_jack_callback_fn func)
{
struct hda_jack_callback *cb;
if (!func)
return NULL;
for (cb = jack->callback; cb; cb = cb->next) {
if (cb->func == func)
return cb;
}
return NULL;
}
// quick debug callback list function
void cs_8409_dump_callback(struct hda_codec *codec)
{
struct hda_jack_tbl *jack = codec->jacktbl.list;
int i;
struct hda_jack_callback *cb;
for (i = 0; i < codec->jacktbl.used; i++, jack++) {
for (cb = jack->callback; cb; cb = cb->next) {
printk("snd_hda_intel: cs_8409_dump_callback jack num %d nid 0x%02x tag 0x%08x func %pF\n",i,jack->nid,jack->tag,cb->func);
}
}
return;
}
// copy of snd_hda_jack_detect_enable_callback code so doesnt send AC_VERB_SET_UNSOLICITED_ENABLE
// for Apple there are no AC_VERB_SET_UNSOLICITED_ENABLE verbs sent for 8409
// it appears unsolicited response is pre-enabled
// but we need to fix this to setup the callback on such responses
// note that the current (>5.13) callback does not have a tag argument
struct hda_jack_callback *
cs_8409_hda_jack_detect_enable_callback(struct hda_codec *codec, hda_nid_t nid, int dev_id, int tag,
hda_jack_callback_fn func)
{
struct hda_jack_tbl *jack;
struct hda_jack_callback *callback = NULL;
int err;
myprintk("snd_hda_intel: cs_8409_hda_jack_detect_enable_callback nid 0x%02x dev_id %d tag 0x%02x\n", nid, dev_id, tag);
jack = cs_8409_hda_jack_tbl_new(codec, nid, dev_id);
if (!jack)
return ERR_PTR(-ENOMEM);
callback = cs_8409_find_callback_from_list(jack, func);
if (func && !callback) {
callback = kzalloc(sizeof(*callback), GFP_KERNEL);
if (!callback)
return ERR_PTR(-ENOMEM);
callback->func = func;
callback->nid = jack->nid;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
callback->dev_id = jack->dev_id;
#endif
callback->next = jack->callback;
jack->callback = callback;
}
if (jack->jack_detect)
return callback; /* already registered */
jack->jack_detect = 1;
// update the tag - linux code just counted the number of jacks set up
// for a tag - now done in cs_8409_hda_jack_tbl_new
// DANGEROUS - we do NOT check for unique tag here!!
if (tag != 0)
jack->tag = tag;
if (codec->jackpoll_interval > 0)
return callback; /* No unsol if we're polling instead */
// apparently we dont need to send this
//err = snd_hda_codec_write_cache(codec, nid, 0,
// AC_VERB_SET_UNSOLICITED_ENABLE,
// AC_USRSP_EN | jack->tag);
//if (err < 0)
// return ERR_PTR(err);
return callback;
}
// this is a copy of local routine call_jack_callback from hda_jack.c
static void cs_8409_apple_call_jack_callback(struct hda_codec *codec, unsigned int res,
struct hda_jack_tbl *jack)
{
struct hda_jack_callback *cb;
for (cb = jack->callback; cb; cb = cb->next) {
cb->jack = jack;
cb->unsol_res = res;
cb->func(codec, cb);
}
if (jack->gated_jack) {
struct hda_jack_tbl *gated =
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
snd_hda_jack_tbl_get_mst(codec, jack->gated_jack, jack->dev_id);
#else
snd_hda_jack_tbl_get(codec, jack->gated_jack);
#endif
if (gated) {
for (cb = gated->callback; cb; cb = cb->next) {
cb->jack = jack;
cb->unsol_res = res;
cb->func(codec, cb);
}
}
}
}
#ifdef APPLE_CHANNEL_MAP
// new attempt to solve the channel map issue
static const struct snd_pcm_chmap_elem cs_8409_chmap[] = {
{ .channels = 2,
.map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } },
{ .channels = 4,
.map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_RL,
SNDRV_CHMAP_FR, SNDRV_CHMAP_RR } },
{ }
};
// this adds controls which I have no idea what they do
static void cs_8409_add_chmap_ctls(struct hda_codec *codec)
{
int err = 0;
struct hda_pcm *pcm;
mycodec_dbg(codec, "cs_8409_add_chmap_ctls enter");
list_for_each_entry(pcm, &codec->pcm_list_head, list) {
struct hda_pcm_stream *hinfo =