Skip to content

Commit bc39e16

Browse files
author
Src User
committed
Major fix for headphone/headset - apparently the prepare op can be
called multiple times - but the setup verbs need to be sent only once, in particular for headphones/headset. This should restore headphone/headset working to state before later kernels, in particular version 6 kernels, with pipewire as the primary audio daemon.
1 parent c23322e commit bc39e16

File tree

5 files changed

+106
-29
lines changed

5 files changed

+106
-29
lines changed

patch_cirrus/patch_cirrus_apple.h

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,9 @@ struct cs8409_apple_spec {
541541
int play_init;
542542
int capture_init;
543543

544+
int play_init_count;
545+
int capture_init_count;
546+
544547
// new item to limit times we redo unmute/play
545548
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
546549
struct timespec64 last_play_time;
@@ -701,9 +704,9 @@ static int cs_8409_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
701704
{
702705
struct hda_gen_spec *spec = codec->spec;
703706
int err;
704-
mycodec_dbg(codec, "cs_8409_playback_pcm_prepare\n");
707+
mycodec_info(codec, "cs_8409_playback_pcm_prepare\n");
705708

706-
mycodec_dbg(codec, "cs_8409_playback_pcm_prepare: NID=0x%x, stream=0x%x, format=0x%x\n",
709+
mycodec_info(codec, "cs_8409_playback_pcm_prepare: NID=0x%x, stream=0x%x, format=0x%x\n",
707710
hinfo->nid, stream_tag, format);
708711

709712
cs_8409_pcm_playback_pre_prepare_hook(hinfo, codec, stream_tag, format, substream,
@@ -727,6 +730,7 @@ static int cs_8409_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
727730
if (!err)
728731
if (spec->pcm_playback_hook)
729732
spec->pcm_playback_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_PREPARE);
733+
mycodec_info(codec, "cs_8409_playback_pcm_prepare end\n");
730734
return err;
731735
}
732736

@@ -747,9 +751,9 @@ static int cs_8409_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
747751
{
748752
struct cs8409_apple_spec *spec = codec->spec;
749753

750-
mycodec_dbg(codec, "cs_8409_capture_pcm_prepare\n");
754+
mycodec_info(codec, "cs_8409_capture_pcm_prepare\n");
751755

752-
mycodec_dbg(codec, "cs_8409_capture_pcm_prepare: NID=0x%x, stream=0x%x, format=0x%x\n",
756+
mycodec_info(codec, "cs_8409_capture_pcm_prepare: NID=0x%x, stream=0x%x, format=0x%x\n",
753757
hinfo->nid, stream_tag, format);
754758

755759

@@ -803,6 +807,8 @@ static int cs_8409_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
803807
if (spec->gen.pcm_capture_hook)
804808
spec->gen.pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_PREPARE);
805809

810+
mycodec_info(codec, "cs_8409_capture_pcm_prepare end\n");
811+
806812
return 0;
807813
}
808814

@@ -3069,6 +3075,9 @@ static int patch_cs8409_apple(struct hda_codec *codec)
30693075
spec->play_init = 0;
30703076
spec->capture_init = 0;
30713077

3078+
spec->play_init_count = 0;
3079+
spec->capture_init_count = 0;
3080+
30723081
// init the last play time
30733082
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
30743083
ktime_get_real_ts64(&(spec->last_play_time));

patch_cirrus/patch_cirrus_new84.h

Lines changed: 81 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1223,6 +1223,23 @@ void cs_8409_capture_cleanup(struct hda_codec *codec)
12231223

12241224
}
12251225

1226+
// routine to clear unsol list
1227+
static void cs_8409_clear_external_device_unsolicited_responses(struct hda_codec *codec)
1228+
{
1229+
struct cs8409_apple_spec *spec = codec->spec;
1230+
struct unsol_item *unsol_entry = NULL;
1231+
struct unsol_item *unsol_temp = NULL;
1232+
if (!list_empty(&spec->unsol_list)) {
1233+
codec_info(codec, "cs_8409_clear_external_device_unsolicited_responses UNSOL start\n");
1234+
list_for_each_entry_safe(unsol_entry, unsol_temp, &spec->unsol_list, list)
1235+
{
1236+
list_del_init(&unsol_entry->list);
1237+
spec->unsol_items_prealloc_used[unsol_entry->idx] = 0;
1238+
memset(unsol_entry, 0, sizeof(struct unsol_item));
1239+
}
1240+
codec_info(codec, "cs_8409_clear_external_device_unsolicited_responses UNSOL end\n");
1241+
}
1242+
}
12261243

12271244
static void cs_8409_cs42l83_unsolicited_response_finalize(struct hda_codec *codec, unsigned int res);
12281245

@@ -1486,7 +1503,17 @@ static void cs_8409_pcm_playback_pre_prepare_hook(struct hda_pcm_stream *hinfo,
14861503
struct timespec curtim;
14871504
getnstimeofday(&curtim);
14881505
#endif
1489-
myprintk("snd_hda_intel: command cs_8409_pcm_playback_pre_prepare_hook HOOK PREPARE init %d last %lld cur %lld",spec->play_init,spec->last_play_time.tv_sec,curtim.tv_sec);
1506+
spec->play_init_count++;
1507+
myprintk("snd_hda_intel: command cs_8409_pcm_playback_pre_prepare_hook HOOK PREPARE init %d %d last %lld cur %lld",spec->play_init,spec->play_init_count,spec->last_play_time.tv_sec,curtim.tv_sec);
1508+
//dump_stack();
1509+
// for some reason this is being called twice within short time so setup is being done twice
1510+
// with no intervening cleanup
1511+
// which may be introducing glitches in the headset/headphone stream - which it is!!!
1512+
// try suppressing any further calls
1513+
// well great apparently its a feature this function can be called multiple times!!
1514+
// looking at examples and Alsa driver docs it appears the idea of this function is to set
1515+
// the stream parameters - so lets let it set the stream parameters every time
1516+
// but only do the main apple setup once
14901517
if (1) {
14911518
struct hda_cvt_setup_apple *p = NULL;
14921519
//int power_chk = 0;
@@ -1510,6 +1537,10 @@ static void cs_8409_pcm_playback_pre_prepare_hook(struct hda_pcm_stream *hinfo,
15101537

15111538
hda_check_power_state(codec, 0x1a, 1);
15121539
hda_check_power_state(codec, 0x3c, 1);
1540+
}
1541+
1542+
if (spec->play_init_count == 1) {
1543+
struct hda_cvt_setup_apple *p = NULL;
15131544

15141545
// for the moment have junky test here
15151546
if (spec->jack_present)
@@ -1607,17 +1638,24 @@ static void cs_8409_playback_pcm_hook(struct hda_pcm_stream *hinfo, struct hda_c
16071638
//struct hda_cvt_setup_apple *p = NULL;
16081639
myprintk("snd_hda_intel: command cs_8409_playback_pcm_hook open");
16091640

1641+
spec->play_init_count = 0;
1642+
16101643
myprintk("snd_hda_intel: command cs_8409_playback_pcm_hook open end");
16111644
} else if (action == HDA_GEN_PCM_ACT_PREPARE) {
16121645
// so this comes AFTER the stream format, frequency setup verbs are sent for the pcm stream
1646+
// note that this can be called multiple times apparently
1647+
// not clear what if any the differences are for those multiple calls
1648+
// - does mean we need to ensure we only do most operations once
1649+
// (most of the work is done in the pre prepare function)
16131650
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
16141651
struct timespec64 curtim;
16151652
ktime_get_real_ts64(&curtim);
16161653
#else
16171654
struct timespec curtim;
16181655
getnstimeofday(&curtim);
16191656
#endif
1620-
myprintk("snd_hda_intel: command cs_8409_playback_pcm_hook HOOK PREPARE init %d last %lld cur %lld",spec->play_init,spec->last_play_time.tv_sec,curtim.tv_sec);
1657+
myprintk("snd_hda_intel: command cs_8409_playback_pcm_hook HOOK PREPARE init %d %d last %lld cur %lld",spec->play_init,spec->play_init_count,spec->last_play_time.tv_sec,curtim.tv_sec);
1658+
//dump_stack();
16211659
//int power_chk = 0;
16221660
//power_chk = snd_hda_codec_read(codec, codec->core.afg, 0, AC_VERB_GET_POWER_STATE, 0);
16231661
//myprintk("snd_hda_intel: command cs_8409_playback_pcm_hook power check 0x01 2 %d", power_chk);
@@ -1628,6 +1666,7 @@ static void cs_8409_playback_pcm_hook(struct hda_pcm_stream *hinfo, struct hda_c
16281666
{
16291667
codec_info(codec, "cs_8409_playback_pcm_hook - performing UNSOL responses\n");
16301668
cs_8409_perform_external_device_unsolicited_responses(codec);
1669+
//cs_8409_clear_external_device_unsolicited_responses(codec);
16311670
}
16321671
spec->playing = 1;
16331672
myprintk("snd_hda_intel: command cs_8409_playback_pcm_hook HOOK PREPARE end");
@@ -1657,6 +1696,7 @@ static void cs_8409_playback_pcm_hook(struct hda_pcm_stream *hinfo, struct hda_c
16571696
codec_info(codec, "cs_8409_playback_pcm_hook - performing UNSOL responses\n");
16581697
cs_8409_perform_external_device_unsolicited_responses(codec);
16591698
}
1699+
spec->play_init_count = 0;
16601700
// not sure of this position yet
16611701
spec->playing = 0;
16621702
power_chk = snd_hda_codec_read(codec, codec->core.afg, 0, AC_VERB_GET_POWER_STATE, 0);
@@ -1677,7 +1717,14 @@ static void cs_8409_pcm_capture_pre_prepare_hook(struct hda_pcm_stream *hinfo, s
16771717
struct cs8409_apple_spec *spec = codec->spec;
16781718

16791719
if (action == HDA_GEN_PCM_ACT_PREPARE) {
1680-
myprintk("snd_hda_intel: command cs_8409_pcm_capture_pre_prepare_hook HOOK PREPARE init %d",spec->capture_init);
1720+
spec->capture_init_count++;
1721+
myprintk("snd_hda_intel: command cs_8409_pcm_capture_pre_prepare_hook HOOK PREPARE init %d %d",spec->capture_init,spec->capture_init_count);
1722+
1723+
// for some reason this is being called twice within short time
1724+
// well great apparently its a feature this function can be called multiple times!!
1725+
// looking at examples and Alsa driver docs it appears the idea of this function is to set
1726+
// the stream parameters - so lets let it set the stream parameters every time
1727+
// but only do the main apple setup once
16811728

16821729
// so the first action for internal mike recording (via Quicktime)
16831730
// is a headphone sense
@@ -1697,32 +1744,36 @@ static void cs_8409_pcm_capture_pre_prepare_hook(struct hda_pcm_stream *hinfo, s
16971744
//cs_8409_setup_stream_format(codec, hinfo->nid, stream_tag, format);
16981745
cs_8409_store_stream_format(codec, hinfo->nid, stream_tag, format);
16991746

1747+
// ensure the setup is only done once
1748+
if (spec->capture_init_count == 1) {
17001749

1701-
// for the moment have junky test here
1702-
if (spec->jack_present) {
1703-
spec->block_unsol = 1;
1704-
if (spec->have_mike)
1705-
{
1706-
// so it seems if we have a headset mike we always enable the
1707-
// headphones even if just capturing
1708-
if (spec->headset_play_format_setup_needed)
1709-
{
1710-
cs_8409_headplay_setup(codec);
1711-
spec->headset_play_format_setup_needed = 0;
1712-
}
1713-
if (spec->headset_capture_format_setup_needed)
1750+
// for the moment have junky test here
1751+
if (spec->jack_present) {
1752+
spec->block_unsol = 1;
1753+
if (spec->have_mike)
17141754
{
1715-
cs_8409_headcapture_setup(codec);
1716-
spec->headset_capture_format_setup_needed = 0;
1755+
// so it seems if we have a headset mike we always enable the
1756+
// headphones even if just capturing
1757+
if (spec->headset_play_format_setup_needed)
1758+
{
1759+
cs_8409_headplay_setup(codec);
1760+
spec->headset_play_format_setup_needed = 0;
1761+
}
1762+
if (spec->headset_capture_format_setup_needed)
1763+
{
1764+
cs_8409_headcapture_setup(codec);
1765+
spec->headset_capture_format_setup_needed = 0;
1766+
}
17171767
}
1768+
// I think this is impossible - this would say we tried to capture
1769+
// using a headset without mike
1770+
// NOTE - still not fixed linein/lineout working - this may need
1771+
// changing here
17181772
}
1719-
// I think this is impossible - this would say we tried to capture
1720-
// using a headset without mike
1721-
// NOTE - still not fixed linein/lineout working - this may need
1722-
// changing here
1773+
else
1774+
cs_8409_capture_setup(codec);
17231775
}
1724-
else
1725-
cs_8409_capture_setup(codec);
1776+
17261777
myprintk("snd_hda_intel: command cs_8409_capture_pcm_hook setup capture called");
17271778

17281779
spec->capturing = 0;
@@ -1776,10 +1827,15 @@ static void cs_8409_capture_pcm_hook(struct hda_pcm_stream *hinfo, struct hda_co
17761827
if (action == HDA_GEN_PCM_ACT_OPEN) {
17771828
//struct hda_cvt_setup_apple *p = NULL;
17781829
myprintk("snd_hda_intel: command cs_8409_capture_pcm_hook open");
1830+
spec->capture_init_count = 0;
17791831

17801832
myprintk("snd_hda_intel: command cs_8409_capture_pcm_hook open end");
17811833
} else if (action == HDA_GEN_PCM_ACT_PREPARE) {
17821834
// so this comes AFTER the stream format, frequency setup verbs are sent for the pcm stream
1835+
// note that this can be called multiple times apparently
1836+
// not clear what if any the differences are for those multiple calls
1837+
// - does mean we need to ensure we only do most operations once
1838+
// (most of the work is done in the pre prepare function)
17831839
myprintk("snd_hda_intel: command cs_8409_capture_pcm_hook HOOK PREPARE init %d",spec->capture_init);
17841840
// this is where we need to finally unset the block_unsol
17851841
// - which also means this is where we should check for unsolicited responses
@@ -1827,6 +1883,7 @@ static void cs_8409_capture_pcm_hook(struct hda_pcm_stream *hinfo, struct hda_co
18271883
codec_info(codec, "cs_8409_capture_pcm_hook - performing UNSOL responses\n");
18281884
cs_8409_perform_external_device_unsolicited_responses(codec);
18291885
}
1886+
spec->capture_init_count = 0;
18301887
// not sure of this position yet
18311888
spec->capturing = 0;
18321889
power_chk = snd_hda_codec_read(codec, codec->core.afg, 0, AC_VERB_GET_POWER_STATE, 0);

patch_cirrus/patch_cirrus_real84_i2c.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -946,6 +946,7 @@ static void cs42l83_configure_int_mclk(struct hda_codec *codec)
946946

947947
cs_8409_vendor_i2cWrite(codec, 0x90, 0x1208, 0x0000, 1); // snd_hda
948948

949+
mycodec_i2c_local_info(codec, "cs42l83_configure_int_mclk end\n");
949950
}
950951

951952
static void cs42l83_headset_power_on_on_nouse(struct hda_codec *codec)
@@ -1014,6 +1015,8 @@ static void cs42l83_power_onoff(struct hda_codec *codec, bool onflag)
10141015
cs_8409_vendor_i2cWrite(codec, 0x90, 0x1107, 0x0000, 1); // snd_hda
10151016
cs_8409_vendor_i2cWrite(codec, 0x90, 0x1501, 0x0000, 1); // snd_hda
10161017
}
1018+
1019+
mycodec_i2c_local_info(codec, "cs42l83_power_onoff end\n");
10171020
}
10181021

10191022
static void cs42l83_configure_serial_port(struct hda_codec *codec)
@@ -1062,6 +1065,8 @@ static void cs42l83_configure_serial_port(struct hda_codec *codec)
10621065
cs_8409_vendor_i2cRead(codec, 0x90, 0x1208, 1); // snd_hda
10631066
cs_8409_vendor_i2cWrite(codec, 0x90, 0x1208, 0x0002, 1); // snd_hda
10641067

1068+
1069+
mycodec_i2c_local_info(codec, "cs42l83_configure_serial_port end\n");
10651070
}
10661071

10671072
static void cs42l83_output_set_input_sample_rate(struct hda_codec *codec)

patch_cirrus/patch_cs8409.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,9 @@ struct cs8409_spec {
473473
int play_init;
474474
int capture_init;
475475

476+
int play_init_count;
477+
int capture_init_count;
478+
476479

477480
// new item to limit times we redo unmute/play
478481
struct timespec64 last_play_time;

patch_patch_cs8409.h.diff

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,7 @@ index d0b725c..d8b5232 100644
430430
unsigned int suspended:1;
431431
unsigned int paged:1;
432432
unsigned int last_page;
433-
@@ -339,6 +365,126 @@
433+
@@ -339,6 +365,129 @@
434434
unsigned int init_done:1;
435435
unsigned int build_ctrl_done:1;
436436

@@ -542,6 +542,9 @@ index d0b725c..d8b5232 100644
542542
+ int play_init;
543543
+ int capture_init;
544544
+
545+
+ int play_init_count;
546+
+ int capture_init_count;
547+
+
545548
+
546549
+ // new item to limit times we redo unmute/play
547550
+ struct timespec64 last_play_time;

0 commit comments

Comments
 (0)