diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 748fca78131c..9ea5660e1a3a 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -1172,6 +1172,59 @@ static int dspio_write_multiple(struct hda_codec *codec, return status; } +static int dspio_read(struct hda_codec *codec, unsigned int *data) +{ + int status; + + status = dspio_send(codec, VENDOR_DSPIO_SCP_POST_READ_DATA, 0); + if (status == -EIO) + return status; + + status = dspio_send(codec, VENDOR_DSPIO_STATUS, 0); + if (status == -EIO || + status == VENDOR_STATUS_DSPIO_SCP_RESPONSE_QUEUE_EMPTY) + return -EIO; + + *data = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0, + VENDOR_DSPIO_SCP_READ_DATA, 0); + + return 0; +} + +static int dspio_read_multiple(struct hda_codec *codec, unsigned int *buffer, + unsigned int *buf_size, unsigned int size_count) +{ + int status = 0; + unsigned int size = *buf_size; + unsigned int count; + unsigned int skip_count; + unsigned int dummy; + + if ((buffer == NULL)) + return -1; + + count = 0; + while (count < size && count < size_count) { + status = dspio_read(codec, buffer++); + if (status != 0) + break; + count++; + } + + skip_count = count; + if (status == 0) { + while (skip_count < size) { + status = dspio_read(codec, &dummy); + if (status != 0) + break; + skip_count++; + } + } + *buf_size = count; + + return status; +} + /* * Construct the SCP header using corresponding fields */ @@ -1231,6 +1284,38 @@ struct scp_msg { unsigned int data[SCP_MAX_DATA_WORDS]; }; +static void dspio_clear_response_queue(struct hda_codec *codec) +{ + unsigned int dummy = 0; + int status = -1; + + /* clear all from the response queue */ + do { + status = dspio_read(codec, &dummy); + } while (status == 0); +} + +static int dspio_get_response_data(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + unsigned int data = 0; + unsigned int count; + + if (dspio_read(codec, &data) < 0) + return -EIO; + + if ((data & 0x00ffffff) == spec->wait_scp_header) { + spec->scp_resp_header = data; + spec->scp_resp_count = data >> 27; + count = spec->wait_num_data; + dspio_read_multiple(codec, spec->scp_resp_data, + &spec->scp_resp_count, count); + return 0; + } + + return -EIO; +} + /* * Send SCP message to DSP */ @@ -3743,6 +3828,12 @@ static int ca0132_build_controls(struct hda_codec *codec) return 0; } +static void ca0132_init_unsol(struct hda_codec *codec) +{ + snd_hda_jack_detect_enable(codec, UNSOL_TAG_HP, UNSOL_TAG_HP); + snd_hda_jack_detect_enable(codec, UNSOL_TAG_AMIC1, UNSOL_TAG_AMIC1); +} + static void refresh_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir) { unsigned int caps; @@ -4152,6 +4243,47 @@ static void ca0132_download_dsp(struct hda_codec *codec) ca0132_set_dsp_msr(codec, true); } +static void ca0132_process_dsp_response(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + + snd_printdd(KERN_INFO "ca0132_process_dsp_response\n"); + if (spec->wait_scp) { + if (dspio_get_response_data(codec) >= 0) + spec->wait_scp = 0; + } + + dspio_clear_response_queue(codec); +} + +static void ca0132_unsol_event(struct hda_codec *codec, unsigned int res) +{ + snd_printdd(KERN_INFO "ca0132_unsol_event: 0x%x\n", res); + + + if (((res >> AC_UNSOL_RES_TAG_SHIFT) & 0x3f) == UNSOL_TAG_DSP) { + ca0132_process_dsp_response(codec); + } else { + res = snd_hda_jack_get_action(codec, + (res >> AC_UNSOL_RES_TAG_SHIFT) & 0x3f); + + snd_printdd(KERN_INFO "snd_hda_jack_get_action: 0x%x\n", res); + + switch (res) { + case UNSOL_TAG_HP: + ca0132_select_out(codec); + snd_hda_jack_report_sync(codec); + break; + case UNSOL_TAG_AMIC1: + ca0132_select_mic(codec); + snd_hda_jack_report_sync(codec); + break; + default: + break; + } + } +} + static int ca0132_init(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; @@ -4187,9 +4319,13 @@ static int ca0132_init(struct hda_codec *codec) for (i = 0; i < spec->num_init_verbs; i++) snd_hda_sequence_write(codec, spec->init_verbs[i]); + ca0132_init_unsol(codec); + ca0132_select_out(codec); ca0132_select_mic(codec); + snd_hda_jack_report_sync(codec); + snd_hda_power_down(codec); return 0; @@ -4211,11 +4347,13 @@ static struct hda_codec_ops ca0132_patch_ops = { .build_pcms = ca0132_build_pcms, .init = ca0132_init, .free = ca0132_free, + .unsol_event = ca0132_unsol_event, }; static int patch_ca0132(struct hda_codec *codec) { struct ca0132_spec *spec; + int err; snd_printdd("patch_ca0132\n"); @@ -4237,6 +4375,10 @@ static int patch_ca0132(struct hda_codec *codec) ca0132_config(codec); + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); + if (err < 0) + return err; + codec->patch_ops = ca0132_patch_ops; return 0;