Skip to content

Commit

Permalink
ALSA: hda/ca0132: Add unsol handler for DSP and jack detection
Browse files Browse the repository at this point in the history
This patch adds the unsolicited response handler for incoming DSP responses and
jack detection reporting, and routines for reading the incoming DSP response.

Signed-off-by: Ian Minett <ian_minett@creativelabs.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
  • Loading branch information
Ian Minett authored and Takashi Iwai committed Jan 15, 2013
1 parent 825315b commit a73d511
Showing 1 changed file with 142 additions and 0 deletions.
142 changes: 142 additions & 0 deletions sound/pci/hda/patch_ca0132.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down Expand Up @@ -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
*/
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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");

Expand All @@ -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;
Expand Down

0 comments on commit a73d511

Please sign in to comment.