--- alsa-driver-1.0.15/alsa-kernel/pci/hda/hda_codec.c 2007/11/17 01:30:52 1.1 +++ alsa-driver-1.0.15/alsa-kernel/pci/hda/hda_codec.c 2007/11/17 01:31:07 @@ -75,6 +75,24 @@ #endif /** + * snd_hda_codec_wallclock - get the wallclock counter + * @codec: the HDA codec + * + * Get the wallclock counter + * + * Returns the obtained value, or -1 for an error. + */ +unsigned int snd_hda_codec_wallclock(struct hda_codec *codec) +{ + u32 res; + if(codec->bus->ops.get_wallclock) + res = codec->bus->ops.get_wallclock(codec); + else + res = (u32)-1; + return res; +} + +/** * snd_hda_codec_read - send a command and get the response * @codec: the HDA codec * @nid: NID to send the command @@ -283,7 +301,7 @@ } /* - * process queueud unsolicited events + * process queued unsolicited events */ static void process_unsol_events(struct work_struct *work) { @@ -329,6 +347,21 @@ return 0; } +void snd_hda_codec_remove_notify_all(struct hda_bus *bus) +{ + struct list_head *p, *n; + + if (! bus) + return; + list_for_each_safe(p, n, &bus->codec_list) { + struct hda_codec *codec = list_entry(p, struct hda_codec, list); + + if (codec && codec->patch_ops.exit) { + codec->patch_ops.exit(codec); + } + } +} + /* * destructor */ @@ -429,6 +462,10 @@ for (tbl = hda_preset_tables; *tbl; tbl++) { for (preset = *tbl; preset->id; preset++) { u32 mask = preset->mask; + if (preset->afg && (preset->afg != codec->afg)) + continue; + if (preset->mfg && (preset->mfg != codec->mfg)) + continue; if (!mask) mask = ~0; if (preset->id == (codec->vendor_id & mask) && @@ -1627,6 +1664,7 @@ snd_hda_codec_write(codec, fg, 0, AC_VERB_SET_POWER_STATE, power_state); + msleep(10); // partial workaround for "hda_intel: azx_get_response timeout, switching to polling mode..." issue nid = codec->start_nid; for (i = 0; i < codec->num_nodes; i++, nid++) { --- alsa-driver-1.0.15/alsa-kernel/pci/hda/hda_codec.h 2007/11/17 01:30:52 1.1 +++ alsa-driver-1.0.15/alsa-kernel/pci/hda/hda_codec.h 2007/11/17 01:31:07 @@ -414,6 +414,10 @@ unsigned int verb, unsigned int parm); /* get a response from the last command */ unsigned int (*get_response)(struct hda_codec *codec); + /* get the wall clock counter */ + u32 (*get_wallclock)(struct hda_codec *codec); + /* get the link position counter */ + u32 (*get_linkpos)(struct snd_pcm_substream *substream); /* free the private data */ void (*private_free)(struct hda_bus *); #ifdef CONFIG_SND_HDA_POWER_SAVE @@ -469,6 +473,7 @@ unsigned int mask; unsigned int subs; unsigned int subs_mask; + hda_nid_t afg, mfg; unsigned int rev; const char *name; int (*patch)(struct hda_codec *codec); @@ -479,6 +484,7 @@ int (*build_controls)(struct hda_codec *codec); int (*build_pcms)(struct hda_codec *codec); int (*init)(struct hda_codec *codec); + int (*exit)(struct hda_codec *codec); void (*free)(struct hda_codec *codec); void (*unsol_event)(struct hda_codec *codec, unsigned int res); #ifdef SND_HDA_NEEDS_RESUME @@ -541,6 +547,7 @@ char *name; struct hda_pcm_stream stream[2]; unsigned int is_modem; /* modem codec? */ + struct snd_pcm *pcm; }; /* codec information */ @@ -606,10 +613,12 @@ struct hda_bus **busp); int snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr, struct hda_codec **codecp); +void snd_hda_codec_remove_notify_all(struct hda_bus *bus); /* * low level functions */ +unsigned int snd_hda_codec_wallclock(struct hda_codec *codec); unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid, int direct, unsigned int verb, unsigned int parm); --- alsa-driver-1.0.15/alsa-kernel/pci/hda/hda_intel.c 2007/11/17 01:30:53 1.1 +++ alsa-driver-1.0.15/alsa-kernel/pci/hda/hda_intel.c 2007/11/17 01:31:07 @@ -48,6 +48,7 @@ #include #include #include "hda_codec.h" +#include "hda_local.h" static int index = SNDRV_DEFAULT_IDX1; @@ -431,6 +432,20 @@ * Interface for HD codec */ +static u32 azx_get_wallclock(struct hda_codec *codec) +{ + struct azx *chip = codec->bus->private_data; + + return azx_readl(chip, WALCLK); +} + +static u32 azx_get_linkpos(struct snd_pcm_substream *substream) +{ + struct azx_dev *azx_dev = get_azx_dev(substream); + + return azx_sd_readl(azx_dev, SD_LPIB); +} + /* * CORB / RIRB interface */ @@ -618,7 +633,8 @@ } udelay(1); } - snd_printd(SFX "send_cmd timeout: IRS=0x%x, val=0x%x\n", + if(printk_ratelimit()) + snd_printd(SFX "send_cmd timeout: IRS=0x%x, val=0x%x\n", azx_readw(chip, IRS), val); return -EIO; } @@ -635,7 +651,8 @@ return azx_readl(chip, IR); udelay(1); } - snd_printd(SFX "get_response timeout: IRS=0x%x\n", + if(printk_ratelimit()) + snd_printd(SFX "get_response timeout: IRS=0x%x\n", azx_readw(chip, IRS)); return (unsigned int)-1; } @@ -1042,6 +1059,8 @@ bus_temp.pci = chip->pci; bus_temp.ops.command = azx_send_cmd; bus_temp.ops.get_response = azx_get_response; + bus_temp.ops.get_wallclock = azx_get_wallclock; + bus_temp.ops.get_linkpos = azx_get_linkpos; #ifdef CONFIG_SND_HDA_POWER_SAVE bus_temp.ops.pm_notify = azx_power_notify; #endif @@ -1384,6 +1403,7 @@ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), 1024 * 64, 1024 * 1024); + cpcm->pcm = pcm; chip->pcm[pcm_dev] = pcm; if (chip->pcm_devs < pcm_dev + 1) chip->pcm_devs = pcm_dev + 1; @@ -1924,6 +1944,10 @@ static void __devexit azx_remove(struct pci_dev *pci) { + struct snd_card *card = pci_get_drvdata(pci); + struct azx *chip = card->private_data; + + snd_hda_codec_remove_notify_all(chip->bus); snd_card_free(pci_get_drvdata(pci)); pci_set_drvdata(pci, NULL); } --- alsa-driver-1.0.15/alsa-kernel/pci/hda/patch_conexant.c 2007/11/17 01:30:53 1.1 +++ alsa-driver-1.0.15/alsa-kernel/pci/hda/patch_conexant.c 2007/11/17 02:14:50 @@ -1,10 +1,12 @@ /* - * HD audio interface patch for Conexant HDA audio codec + * HD audio interface patch for Conexant HDA audio/modem codec * * Copyright (c) 2006 Pototskiy Akex * Takashi Iwai * Tobin Davis * + * Copyright (c) 2005-2007 Linuxant inc. + * * This driver is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -39,6 +41,13 @@ #define CONEXANT_MIC_EVENT 0x38 +#if defined(__i386__) +#define __shimcall__ __attribute__((regparm(0))) +#else +#define __shimcall__ +#endif + +//#define CONFIG_SND_DEBUG struct conexant_spec { @@ -58,6 +67,8 @@ */ unsigned int cur_eapd; unsigned int hp_present; + unsigned int portb_present; + unsigned int portc_present; unsigned int need_dac_fix; /* capture */ @@ -75,7 +86,7 @@ int num_channel_mode; /* PCM information */ - struct hda_pcm pcm_rec[2]; /* used in build_pcms() */ + struct hda_pcm pcm[3]; /* used in build_pcms() */ struct mutex amp_mutex; /* PCM volume/mute control mutex */ unsigned int spdif_route; @@ -87,6 +98,13 @@ struct hda_input_mux private_imux; hda_nid_t private_dac_nids[4]; + /* HSF modem */ + void *modem_devnode; + unsigned int modem_stream_tags[2]; + int modem_do_prepare[2]; + void (*modem_cbHdaEvent)(void *Context, unsigned int res) __shimcall__; + void *modem_cbHdaEventContext; + unsigned char modem_cbHdaTag; }; static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo, @@ -158,8 +176,23 @@ struct snd_pcm_substream *substream) { struct conexant_spec *spec = codec->spec; - snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], - stream_tag, 0, format); + + if (spec->num_adc_nids > 1) + { +// printk(KERN_DEBUG"conexant_capture_pcm_prepare spec->portb_present =%x\n",spec->portb_present); +// printk(KERN_DEBUG"conexant_capture_pcm_prepare spec->portc_present =%x\n",spec->portc_present); +// if(spec->portb_present) +// snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], +// stream_tag, 0, format); +// else + snd_hda_codec_setup_stream(codec, spec->adc_nids[spec->portc_present], + stream_tag, 0, format); + } + else + { + snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], + stream_tag, 0, format); + } return 0; } @@ -168,11 +201,52 @@ struct snd_pcm_substream *substream) { struct conexant_spec *spec = codec->spec; - snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], - 0, 0, 0); + + if (spec->num_adc_nids > 1) + { + snd_hda_codec_setup_stream(codec, spec->adc_nids[0], + 0, 0, 0); + snd_hda_codec_setup_stream(codec, spec->adc_nids[1], + 0, 0, 0); + } + else + { + snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], + 0, 0, 0); + } + return 0; +} + + +static int conexant_modem_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct conexant_spec *spec = codec->spec; + + //printk(KERN_DEBUG"%s: codec=%p stream=%d stream_tag=%x format=0x%x substream=%p\n", __FUNCTION__, codec, substream->stream, stream_tag, format, substream); + + spec->modem_stream_tags[substream->stream] = stream_tag; + return 0; } +static int conexant_modem_pcm_open(struct hda_pcm_stream *hinfo, struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + static unsigned int rates[] = { 16000 }; + static struct snd_pcm_hw_constraint_list hw_constraints_rates = { + .count = ARRAY_SIZE(rates), + .list = rates, + .mask = 0, + }; + + //printk(KERN_DEBUG"%s: codec=%p substream=%p\n", __FUNCTION__, codec, substream); + + return snd_pcm_hw_constraint_list(substream->runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); +} static struct hda_pcm_stream conexant_pcm_analog_playback = { @@ -218,28 +292,52 @@ /* NID is set in alc_build_pcms */ }; +static struct hda_pcm_stream conexant_modem_pcm = { + .substreams = 1, + .channels_min = 1, + .channels_max = 1, + .nid = 0x1, + .rates = SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .maxbps = 16, + .ops = { + .open = conexant_modem_pcm_open, + .prepare = conexant_modem_pcm_prepare, + }, +}; + +/* Used by conexant_build_pcms to flag that a PCM has no playback stream */ +static struct hda_pcm_stream conexant_pcm_null_playback = { + .substreams = 0, + .channels_min = 0, + .channels_max = 0, +}; + static int conexant_build_pcms(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; - struct hda_pcm *info = spec->pcm_rec; + struct hda_pcm *info = spec->pcm; - codec->num_pcms = 1; + codec->num_pcms = 0; codec->pcm_info = info; - info->name = "CONEXANT Analog"; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = conexant_pcm_analog_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = - spec->multiout.max_channels; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = - spec->multiout.dac_nids[0]; - info->stream[SNDRV_PCM_STREAM_CAPTURE] = conexant_pcm_analog_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adc_nids; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; + if(codec->afg) { + info->name = "Conexant Analog Audio"; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = conexant_pcm_analog_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = + spec->multiout.max_channels; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = + spec->multiout.dac_nids[0]; + info->stream[SNDRV_PCM_STREAM_CAPTURE] = conexant_pcm_analog_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; - if (spec->multiout.dig_out_nid) { info++; codec->num_pcms++; - info->name = "Conexant Digital"; + } + + if (spec->multiout.dig_out_nid) { + info->name = "Conexant Digital Audio"; info->stream[SNDRV_PCM_STREAM_PLAYBACK] = conexant_pcm_digital_playback; info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = @@ -250,6 +348,23 @@ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid; } + + info++; + codec->num_pcms++; + } + + if(codec->mfg) { + conexant_modem_pcm.nid = codec->mfg; + + info->name = "Conexant HSF Modem"; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = conexant_modem_pcm; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = conexant_modem_pcm.nid; + info->stream[SNDRV_PCM_STREAM_CAPTURE] = conexant_modem_pcm; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = conexant_modem_pcm.nid; + info->is_modem = 1; + + info++; + codec->num_pcms++; } return 0; @@ -287,13 +402,61 @@ &spec->cur_mux[adc_idx]); } +static void *conexant_hsfmodem_interface_funcs[]; + static int conexant_init(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; - int i; + int i, ret; + + //printk(KERN_DEBUG"%s: codec=%p\n", __FUNCTION__, codec); + if(codec->mfg) { + int (*cnxthwhda_probe)(void *codec, struct device *hwDev, void **ppDevNode, void **ppInterfaceFuncs); + + //snd_hda_codec_write(codec, AC_NODE_ROOT, 0, AC_VERB_SET_CODEC_RESET, 0); + + cnxthwhda_probe = (void*)symbol_request(cnxthwhda_probe); + if(!cnxthwhda_probe) { + printk(KERN_ERR"%s: symbol_request(cnxthwhda_probe) failed\n", __FUNCTION__); + if(!codec->afg) + return -ENOENT; + } else { + ret = cnxthwhda_probe(codec, codec->bus->card->dev, &spec->modem_devnode, conexant_hsfmodem_interface_funcs); + if(ret) { + printk(KERN_ERR"%s: cnxthwhda_probe() failed: %d\n", __FUNCTION__, ret); + symbol_put(cnxthwhda_probe); + if(!codec->afg) + return ret; + } + } + } for (i = 0; i < spec->num_init_verbs; i++) snd_hda_sequence_write(codec, spec->init_verbs[i]); + + return 0; +} + +static int conexant_exit(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + + //printk(KERN_DEBUG"%s: codec=%p spec=%p\n", __FUNCTION__, codec, spec); + + if(codec->mfg && spec && spec->modem_devnode) { + void (*cnxthwhda_remove)(void *ptr); + + cnxthwhda_remove = (void*)symbol_request(cnxthwhda_remove); + if(cnxthwhda_remove) { + cnxthwhda_remove(spec->modem_devnode); + spec->modem_devnode = NULL; + symbol_put(cnxthwhda_remove); + symbol_put(cnxthwhda_probe); + } else { + printk(KERN_ERR"%s: symbol_request(cnxthwhda_remove) failed\n", __FUNCTION__); + } + } + return 0; } @@ -302,15 +465,119 @@ struct conexant_spec *spec = codec->spec; unsigned int i; - if (spec->kctl_alloc) { + if(spec) { + codec->spec = NULL; + memset(&codec->patch_ops, 0, sizeof(codec->patch_ops)); + + if (spec->kctl_alloc) { for (i = 0; i < spec->num_kctl_used; i++) kfree(spec->kctl_alloc[i].name); kfree(spec->kctl_alloc); - } + } + kfree(spec); + } +} + +static void conexant_unsol_event(struct hda_codec *codec, unsigned int res) +{ + struct conexant_spec *spec = codec->spec; + + //printk(KERN_DEBUG"%s: codec=%p res=0x%02x spec=%p cbHdaEvent=%p\n", __FUNCTION__, codec, res, spec, spec->modem_cbHdaEvent); + + if(codec->mfg && spec && spec->modem_cbHdaEvent) { + if(((res >> 26) & 0x3f) == spec->modem_cbHdaTag) { + //printk(KERN_DEBUG"%s: res=0x%02x cbHdaEvent=%p\n", __FUNCTION__, res, spec->modem_cbHdaEvent); + //printk(KERN_DEBUG"%s: calling cbHdaEvent=%p ctx=%p\n", __FUNCTION__, spec->modem_cbHdaEvent, spec->modem_cbHdaEventContext); + spec->modem_cbHdaEvent(spec->modem_cbHdaEventContext, res); + } else { + printk(KERN_DEBUG"%s: ignoring res=0x08%x\n", __FUNCTION__, res); + } + } +} + +typedef struct tagHDAOSHAL { + void *hda_codec; + int bInSuspendResume; +} HDAOSHAL, *PHDAOSHAL; + +typedef struct tagOS_DEVNODE { + void *hwDev; + // intentionally left incomplete +} OS_DEVNODE, *POS_DEVNODE; + +#ifdef CONFIG_PM +static int conexant_suspend(struct hda_codec *codec, pm_message_t state) +{ + struct conexant_spec *spec = codec->spec; + int (*cnxthwhda_suspend)(void *devnode, pm_message_t state); + int ret = 0; + + //printk(KERN_DEBUG"%s: codec=%p spec=%p state=0x%x\n", __FUNCTION__, codec, spec, *((u32 *)&state)); + + if(spec && spec->modem_devnode) { + cnxthwhda_suspend = (void*)symbol_request(cnxthwhda_suspend); + if(!cnxthwhda_suspend) { + printk(KERN_ERR"%s: symbol_request(cnxthwhda_suspend) failed\n", __FUNCTION__); + return -ENOSYS; + } + + if(((POS_DEVNODE)spec->modem_devnode)->hwDev) { + ((PHDAOSHAL)((POS_DEVNODE)spec->modem_devnode)->hwDev)->bInSuspendResume++; + } + ret = cnxthwhda_suspend(spec->modem_devnode, state); + if(((POS_DEVNODE)spec->modem_devnode)->hwDev) { + ((PHDAOSHAL)((POS_DEVNODE)spec->modem_devnode)->hwDev)->bInSuspendResume--; + } + symbol_put(cnxthwhda_suspend); + } - kfree(codec->spec); + return ret; } +static int conexant_resume(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + int ret = 0; + hda_nid_t mfg; + + mfg = codec->mfg; + codec->mfg = 0; + codec->patch_ops.init(codec); + codec->mfg = mfg; + + snd_hda_codec_resume_amp(codec); + snd_hda_codec_resume_cache(codec); + + if(spec && spec->modem_devnode) { + int (*cnxthwhda_resume)(void *devnode); + + //printk(KERN_DEBUG"%s: codec=%p spec=%p\n", __FUNCTION__, codec, spec); + + cnxthwhda_resume = (void*)symbol_request(cnxthwhda_resume); + if(!cnxthwhda_resume) { + printk(KERN_ERR"%s: symbol_request(cnxthwhda_resume) failed\n", __FUNCTION__); + return -ENOSYS; + } + + //snd_hda_codec_write(codec, AC_NODE_ROOT, 0, AC_VERB_SET_CODEC_RESET, 0); + + if(((POS_DEVNODE)spec->modem_devnode)->hwDev) { + ((PHDAOSHAL)((POS_DEVNODE)spec->modem_devnode)->hwDev)->bInSuspendResume++; + } + + ret = cnxthwhda_resume(spec->modem_devnode); + + if(((POS_DEVNODE)spec->modem_devnode)->hwDev) { + ((PHDAOSHAL)((POS_DEVNODE)spec->modem_devnode)->hwDev)->bInSuspendResume--; + } + + symbol_put(cnxthwhda_resume); + } + + return ret; +} +#endif + static int conexant_build_controls(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; @@ -340,9 +607,47 @@ .build_controls = conexant_build_controls, .build_pcms = conexant_build_pcms, .init = conexant_init, + .exit = conexant_exit, .free = conexant_free, + .unsol_event = conexant_unsol_event, +#ifdef CONFIG_PM + .suspend = conexant_suspend, + .resume = conexant_resume, +#endif }; +static int patch_cxthsfmodem(struct hda_codec *codec) +{ + struct conexant_spec *spec; + int (*cnxthwhda_probe)(void *codec, struct device *hwDev, void **ppDevNode); + + //printk(KERN_DEBUG"%s: codec=%p\n", __FUNCTION__, codec); + if(!codec->mfg) { // we only support modems here + return -ENODEV; + } + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return -ENOMEM; + mutex_init(&spec->amp_mutex); + codec->spec = spec; + + codec->patch_ops = conexant_patch_ops; + + cnxthwhda_probe = (void*)symbol_request(cnxthwhda_probe); + if(cnxthwhda_probe) { + symbol_put(cnxthwhda_probe); + } else { + printk(KERN_ERR"%s: symbol_request(cnxthwhda_probe) failed\n", __FUNCTION__); + codec->spec = NULL; + memset(&codec->patch_ops, 0, sizeof(codec->patch_ops)); + kfree(spec); + return -ENOENT; + } + + return 0; +} + /* * EAPD control * the private value = nid | (invert << 8) @@ -439,6 +744,363 @@ #endif /* CONFIG_SND_DEBUG */ +/* Conexant 5051 specific */ +static hda_nid_t cxt5051_dac_nids[1] = { 0x10 }; +static hda_nid_t cxt5051_adc_nids[2] = { 0x14,0x15 }; +static hda_nid_t cxt5051_capsrc_nids[2] = { 0x14,0x15 }; +#define CXT5051_SPDIF_OUT 0x1C +#define CXT5051_PORTB_EVENT 0x38 +#define CXT5051_PORTC_EVENT 0x39 + +static struct hda_channel_mode cxt5051_modes[1] = { + { 2, NULL }, +}; + +static struct hda_input_mux cxt5051_capture_source = { + .num_items = 2, + .items = { + { "IntMic", 0x1 }, + { "LineIn", 0x2 }, + } +}; + +/* turn on/off EAPD (+ mute HP) as a master switch */ +static int cxt5051_hp_master_sw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + static struct hda_verb spk_on[] = { + {0x1A, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {} + }; + static struct hda_verb spk_off[] = { + {0x1A, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + {} + }; + static struct hda_verb hp_on[] = { + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {} + }; + static struct hda_verb hp_off[] = { + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + {} + }; + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct conexant_spec *spec = codec->spec; + unsigned int bits; + + if (!cxt_eapd_put(kcontrol, ucontrol)) + return 0; + + /* toggle internal speakers mute depending of presence of + * the headphone jack + */ + bits = (!spec->hp_present && spec->cur_eapd) ? 0 : 0x80; + if (bits) + snd_hda_sequence_write(codec, spk_off); + else + snd_hda_sequence_write(codec, spk_on); + + bits = (spec->cur_eapd) ? 0 : 0x80; + if (bits) + snd_hda_sequence_write(codec, hp_off); + else + snd_hda_sequence_write(codec, hp_on); + + return 1; +} + +/* bind volumes of both NID 0x10 and 0x11 */ +static int cxt5051_hp_master_vol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + long *valp = ucontrol->value.integer.value; + int change; + + change = snd_hda_codec_amp_update(codec, 0x10, 0, HDA_OUTPUT, 0, + 0x7f, valp[0] & 0x7f); + change |= snd_hda_codec_amp_update(codec, 0x10, 1, HDA_OUTPUT, 0, + 0x7f, valp[1] & 0x7f); + return change; +} + +/* toggle input of built-in and mic jack appropriately */ +static void cxt5051_portb_automic(struct hda_codec *codec) +{ + static struct hda_verb mic_jack_on[] = { + {0x14, AC_VERB_SET_CONNECT_SEL,0x1}, + {} + }; + static struct hda_verb mic_jack_off[] = { + {0x14, AC_VERB_SET_CONNECT_SEL,0x1}, + {} + }; + unsigned int present; + struct conexant_spec *spec = codec->spec; + + present = snd_hda_codec_read(codec, 0x17, 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + printk(KERN_DEBUG"cxt5051_portb_automic present =%x\n",present); + + if (present) + { + spec->portb_present = 1; + snd_hda_sequence_write(codec, mic_jack_on); + } + else + { + spec->portb_present = 0; + snd_hda_sequence_write(codec, mic_jack_off); + } + +} + +static void cxt5051_portc_automic(struct hda_codec *codec) +{ + unsigned int present; + struct conexant_spec *spec = codec->spec; + present = snd_hda_codec_read(codec, 0x18, 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + printk(KERN_DEBUG"cxt5051_portc_automic present =%x\n",present); + + if (present) + spec->portc_present = 1; + else + spec->portc_present = 0; + +} + +/* mute internal speaker if HP is plugged */ +static void cxt5051_hp_automute(struct hda_codec *codec) +{ + static struct hda_verb spk_on[] = { + {0x1A, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {} + }; + static struct hda_verb spk_off[] = { + {0x1A, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + {} + }; + + struct conexant_spec *spec = codec->spec; + unsigned int bits; + + spec->hp_present = snd_hda_codec_read(codec, 0x16, 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + + + bits = (spec->hp_present || !spec->cur_eapd) ? 0x80 : 0; + if (bits) + snd_hda_sequence_write(codec, spk_off); + else + snd_hda_sequence_write(codec, spk_on); + + printk(KERN_DEBUG"cxt5051_hp_automute bits =%x\n",bits); +} + +/* unsolicited event for HP jack sensing */ +static void cxt5051_hp_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + switch (res >> 26) { + case CONEXANT_HP_EVENT: + cxt5051_hp_automute(codec); + break; + case CXT5051_PORTB_EVENT: + cxt5051_portb_automic(codec); + break; + case CXT5051_PORTC_EVENT: + cxt5051_portc_automic(codec); + break; + + default: + conexant_unsol_event(codec, res); + break; + + } +} + +static struct snd_kcontrol_new cxt5051_mixers[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Source", + .info = conexant_mux_enum_info, + .get = conexant_mux_enum_get, + .put = conexant_mux_enum_put + }, +// HDA_CODEC_VOLUME("Int Mic Volume", 0x14, 0x00, HDA_INPUT), +// HDA_CODEC_MUTE("Int Mic Switch", 0x14, 0x00, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Volume", 0x14, 0x00, HDA_INPUT), + HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT), + HDA_CODEC_VOLUME("External Mic Volume", 0x14, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("External Mic Switch", 0x14, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("Docking Mic Volume", 0x15, 0x00, HDA_INPUT), + HDA_CODEC_MUTE("Docking Mic Switch", 0x15, 0x00, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Volume", + .info = snd_hda_mixer_amp_volume_info, + .get = snd_hda_mixer_amp_volume_get, + .put = cxt5051_hp_master_vol_put, + .private_value = HDA_COMPOSE_AMP_VAL(0x10, 3, 0, HDA_OUTPUT), + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .info = cxt_eapd_info, + .get = cxt_eapd_get, + .put = cxt5051_hp_master_sw_put, + .private_value = 0x1A, + }, + + {} +}; + +static struct hda_verb cxt5051_init_verbs[] = { + /* Line in, Mic */ + {0x17, AC_VERB_SET_AMP_GAIN_MUTE,AC_AMP_SET_INPUT|AC_AMP_SET_LEFT + |AC_AMP_SET_RIGHT|0x3}, + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 }, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE,AC_AMP_SET_INPUT|AC_AMP_SET_LEFT + |AC_AMP_SET_RIGHT|0x3}, + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 }, + {0x1D, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1D, AC_VERB_SET_AMP_GAIN_MUTE,AC_AMP_SET_INPUT|AC_AMP_SET_LEFT + |AC_AMP_SET_RIGHT|0x3}, + /* SPK */ + {0x1A, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + {0x1A, AC_VERB_SET_CONNECT_SEL,0x00}, + /* HP, Amp */ + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP|PIN_OUT }, + {0x16, AC_VERB_SET_CONNECT_SEL,0x00}, + /* DAC1 */ + {0x10, AC_VERB_SET_AMP_GAIN_MUTE, + AC_AMP_SET_OUTPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x01}, + {0x10, AC_VERB_SET_AMP_GAIN_MUTE, + AC_AMP_SET_OUTPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x02}, + {0x10, AC_VERB_SET_AMP_GAIN_MUTE, + AC_AMP_SET_OUTPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x03}, + {0x10, AC_VERB_SET_AMP_GAIN_MUTE, + AC_AMP_SET_OUTPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x04}, + /* Record selector: Int mic */ + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, + AC_AMP_SET_INPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x44}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE,AC_AMP_SET_INPUT|AC_AMP_SET_RIGHT| + AC_AMP_SET_LEFT|0x44|1<amp_mutex); + codec->spec = spec; + + spec->multiout.max_channels = 2; + spec->multiout.num_dacs = ARRAY_SIZE(cxt5051_dac_nids); + spec->multiout.dac_nids = cxt5051_dac_nids; + spec->multiout.dig_out_nid = CXT5051_SPDIF_OUT; + spec->num_adc_nids = 2; + spec->adc_nids = cxt5051_adc_nids; + spec->capsrc_nids = cxt5051_capsrc_nids; + spec->input_mux = &cxt5051_capture_source; + spec->num_mixers = 1; + spec->mixers[0] = cxt5051_mixers; + spec->num_init_verbs = 1; + spec->init_verbs[0] = cxt5051_init_verbs; + spec->spdif_route = 0; + spec->num_channel_mode = ARRAY_SIZE(cxt5051_modes), + spec->channel_mode = cxt5051_modes, + spec->portb_present = 0; + spec->portc_present = 0; + + codec->patch_ops = conexant_patch_ops; + + board_config = snd_hda_check_board_config(codec, CXT5051_MODELS, + cxt5051_models, + cxt5051_cfg_tbl); + switch (board_config) { + default: + case CXT5051_LAPTOP: + codec->patch_ops.unsol_event = cxt5051_hp_unsol_event; + spec->input_mux = &cxt5051_capture_source; + spec->num_init_verbs = 2; + spec->init_verbs[1] = cxt5051_mic_wid17_init_verbs; + spec->mixers[0] = cxt5051_mixers; + codec->patch_ops.init = cxt5051_init; + break; + } + + if(codec->mfg) { + int (*cnxthwhda_probe)(void *codec, struct device *hwDev, void **ppDevNode); + cnxthwhda_probe = (void*)symbol_request(cnxthwhda_probe); + if(cnxthwhda_probe) + symbol_put(cnxthwhda_probe); + else + printk(KERN_ERR"%s: symbol_request(cnxthwhda_probe) failed\n", __FUNCTION__); + } + + return 0; +} + + + + /* Conexant 5045 specific */ static hda_nid_t cxt5045_dac_nids[1] = { 0x19 }; @@ -454,7 +1116,7 @@ .num_items = 2, .items = { { "IntMic", 0x1 }, - { "LineIn", 0x2 }, + { "ExtMic", 0x2 }, } }; @@ -496,13 +1158,15 @@ static void cxt5045_hp_automic(struct hda_codec *codec) { static struct hda_verb mic_jack_on[] = { - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | AC_AMP_SET_OUTPUT | AC_AMP_MUTE}, + {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | AC_AMP_SET_OUTPUT}, + {0x1a, AC_VERB_SET_CONNECT_SEL, 0x02}, {} }; static struct hda_verb mic_jack_off[] = { - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, + {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | AC_AMP_SET_OUTPUT | AC_AMP_MUTE}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | AC_AMP_SET_OUTPUT}, + {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, {} }; unsigned int present; @@ -534,14 +1198,19 @@ static void cxt5045_hp_unsol_event(struct hda_codec *codec, unsigned int res) { - res >>= 26; - switch (res) { + switch (res >> 26) { case CONEXANT_HP_EVENT: + //printk("%s: CONEXANT_HP_EVENT\n", __FUNCTION__); cxt5045_hp_automute(codec); break; case CONEXANT_MIC_EVENT: + //printk("%s: CONEXANT_MIC_EVENT\n", __FUNCTION__); cxt5045_hp_automic(codec); break; + default: + //printk("%s: CONEXANT_OTHER_EVENT\n", __FUNCTION__); + conexant_unsol_event(codec, res); + break; } } @@ -572,8 +1241,9 @@ }; static struct hda_verb cxt5045_init_verbs[] = { - /* Line in, Mic */ - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, + /* ExtMic in, IntMic */ + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 }, + {0x12, AC_VERB_SET_CONNECT_SEL, 0x1 }, {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 }, /* HP, Amp */ {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, @@ -627,6 +1297,11 @@ static struct snd_kcontrol_new cxt5045_test_mixer[] = { + HDA_CODEC_VOLUME("Int Mic Volume", 0x1a, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Int Mic Switch", 0x1a, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("Ext Mic Volume", 0x1a, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Ext Mic Switch", 0x1a, 0x02, HDA_INPUT), + /* Output controls */ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x10, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Speaker Playback Switch", 0x10, 0x0, HDA_OUTPUT), @@ -735,8 +1410,9 @@ enum { - CXT5045_LAPTOP, /* Laptops w/ EAPD support */ - CXT5045_FUJITSU, /* Laptops w/ EAPD support */ + CXT5045_LAPTOP_HPSENSE, + CXT5045_LAPTOP_MICSENSE, + CXT5045_LAPTOP_HPMICSENSE, #ifdef CONFIG_SND_DEBUG CXT5045_TEST, #endif @@ -744,23 +1420,32 @@ }; static const char *cxt5045_models[CXT5045_MODELS] = { - [CXT5045_LAPTOP] = "laptop", - [CXT5045_FUJITSU] = "fujitsu", + [CXT5045_LAPTOP_HPSENSE] = "laptop-hpsense", + [CXT5045_LAPTOP_MICSENSE] = "laptop-micsense", + [CXT5045_LAPTOP_HPMICSENSE] = "laptop-hpmicsense", #ifdef CONFIG_SND_DEBUG [CXT5045_TEST] = "test", #endif }; static struct snd_pci_quirk cxt5045_cfg_tbl[] = { - SND_PCI_QUIRK(0x103c, 0x30b7, "HP DV6000Z", CXT5045_LAPTOP), - SND_PCI_QUIRK(0x103c, 0x30bb, "HP DV8000", CXT5045_LAPTOP), - SND_PCI_QUIRK(0x103c, 0x30b2, "HP DV Series", CXT5045_LAPTOP), - SND_PCI_QUIRK(0x103c, 0x30b5, "HP DV2120", CXT5045_LAPTOP), - SND_PCI_QUIRK(0x103c, 0x30cd, "HP DV Series", CXT5045_LAPTOP), - SND_PCI_QUIRK(0x103c, 0x30d9, "HP Spartan", CXT5045_LAPTOP), - SND_PCI_QUIRK(0x1734, 0x10ad, "Fujitsu Si1520", CXT5045_FUJITSU), - SND_PCI_QUIRK(0x1734, 0x10cb, "Fujitsu Si3515", CXT5045_LAPTOP), - SND_PCI_QUIRK(0x8086, 0x2111, "Conexant Reference board", CXT5045_LAPTOP), + SND_PCI_QUIRK(0x103c, 0x30a5, "HP", CXT5045_LAPTOP_HPSENSE), + SND_PCI_QUIRK(0x103c, 0x30b7, "HP DV6000Z", CXT5045_LAPTOP_HPSENSE), + SND_PCI_QUIRK(0x103c, 0x30bb, "HP DV8000", CXT5045_LAPTOP_HPSENSE), + SND_PCI_QUIRK(0x103c, 0x30b2, "HP DV Series", CXT5045_LAPTOP_HPSENSE), + SND_PCI_QUIRK(0x103c, 0x30b5, "HP DV2120", CXT5045_LAPTOP_HPSENSE), + SND_PCI_QUIRK(0x103c, 0x30cd, "HP DV Series", CXT5045_LAPTOP_HPSENSE), + SND_PCI_QUIRK(0x103c, 0x30d5, "HP", CXT5045_LAPTOP_HPSENSE), + SND_PCI_QUIRK(0x103c, 0x30d9, "HP Spartan", CXT5045_LAPTOP_HPSENSE), + SND_PCI_QUIRK(0x1734, 0x10ad, "Fujitsu Si1520", CXT5045_LAPTOP_MICSENSE), + SND_PCI_QUIRK(0x1734, 0x10cb, "Fujitsu Si3515", CXT5045_LAPTOP_HPMICSENSE), + SND_PCI_QUIRK(0x1734, 0x110e, "Fujitsu V5505", CXT5045_LAPTOP_HPSENSE), + SND_PCI_QUIRK(0x1509, 0x1e40, "FIC", CXT5045_LAPTOP_HPMICSENSE), + SND_PCI_QUIRK(0x1509, 0x2f05, "FIC", CXT5045_LAPTOP_HPMICSENSE), + SND_PCI_QUIRK(0x1509, 0x2f06, "FIC", CXT5045_LAPTOP_HPMICSENSE), + SND_PCI_QUIRK(0x1631, 0xc106, "Packard Bell", CXT5045_LAPTOP_HPMICSENSE), + SND_PCI_QUIRK(0x1631, 0xc107, "Packard Bell", CXT5045_LAPTOP_HPMICSENSE), + SND_PCI_QUIRK(0x8086, 0x2111, "Conexant Reference board", CXT5045_LAPTOP_HPSENSE), {} }; @@ -797,8 +1482,9 @@ board_config = snd_hda_check_board_config(codec, CXT5045_MODELS, cxt5045_models, cxt5045_cfg_tbl); + //printk(KERN_DEBUG"%s: board_config=%d\n", __FUNCTION__, board_config); switch (board_config) { - case CXT5045_LAPTOP: + case CXT5045_LAPTOP_HPSENSE: codec->patch_ops.unsol_event = cxt5045_hp_unsol_event; spec->input_mux = &cxt5045_capture_source; spec->num_init_verbs = 2; @@ -806,20 +1492,43 @@ spec->mixers[0] = cxt5045_mixers; codec->patch_ops.init = cxt5045_init; break; - case CXT5045_FUJITSU: + case CXT5045_LAPTOP_MICSENSE: + codec->patch_ops.unsol_event = cxt5045_hp_unsol_event; spec->input_mux = &cxt5045_capture_source; spec->num_init_verbs = 2; spec->init_verbs[1] = cxt5045_mic_sense_init_verbs; spec->mixers[0] = cxt5045_mixers; codec->patch_ops.init = cxt5045_init; break; + default: + case CXT5045_LAPTOP_HPMICSENSE: + codec->patch_ops.unsol_event = cxt5045_hp_unsol_event; + spec->input_mux = &cxt5045_capture_source; + spec->num_init_verbs = 3; + spec->init_verbs[1] = cxt5045_hp_sense_init_verbs; + spec->init_verbs[2] = cxt5045_mic_sense_init_verbs; + spec->mixers[0] = cxt5045_mixers; + codec->patch_ops.init = cxt5045_init; + break; #ifdef CONFIG_SND_DEBUG case CXT5045_TEST: spec->input_mux = &cxt5045_test_capture_source; spec->mixers[0] = cxt5045_test_mixer; spec->init_verbs[0] = cxt5045_test_init_verbs; #endif + break; + + } + + if(codec->mfg) { + int (*cnxthwhda_probe)(void *codec, struct device *hwDev, void **ppDevNode); + cnxthwhda_probe = (void*)symbol_request(cnxthwhda_probe); + if(cnxthwhda_probe) + symbol_put(cnxthwhda_probe); + else + printk(KERN_ERR"%s: symbol_request(cnxthwhda_probe) failed\n", __FUNCTION__); } + return 0; } @@ -928,13 +1637,13 @@ static void cxt5047_hp_automic(struct hda_codec *codec) { static struct hda_verb mic_jack_on[] = { - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | AC_AMP_SET_OUTPUT | AC_AMP_MUTE}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | AC_AMP_SET_OUTPUT}, {} }; static struct hda_verb mic_jack_off[] = { - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | AC_AMP_SET_OUTPUT | AC_AMP_MUTE}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | AC_AMP_SET_OUTPUT}, {} }; unsigned int present; @@ -951,14 +1660,16 @@ static void cxt5047_hp_unsol_event(struct hda_codec *codec, unsigned int res) { - res >>= 26; - switch (res) { + switch (res >> 26) { case CONEXANT_HP_EVENT: cxt5047_hp_automute(codec); break; case CONEXANT_MIC_EVENT: cxt5047_hp_automic(codec); break; + default: + conexant_unsol_event(codec, res); + break; } } @@ -1161,6 +1872,17 @@ .get = conexant_mux_enum_get, .put = conexant_mux_enum_put, }, + HDA_CODEC_VOLUME("Input-1 Volume", 0x1a, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Input-1 Switch", 0x1a, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Input-2 Volume", 0x1a, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Input-2 Switch", 0x1a, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Input-3 Volume", 0x1a, 0x2, HDA_INPUT), + HDA_CODEC_MUTE("Input-3 Switch", 0x1a, 0x2, HDA_INPUT), + HDA_CODEC_VOLUME("Input-4 Volume", 0x1a, 0x3, HDA_INPUT), + HDA_CODEC_MUTE("Input-4 Switch", 0x1a, 0x3, HDA_INPUT), + HDA_CODEC_VOLUME("Input-5 Volume", 0x1a, 0x4, HDA_INPUT), + HDA_CODEC_MUTE("Input-5 Switch", 0x1a, 0x4, HDA_INPUT), + { } /* end */ }; @@ -1316,6 +2038,16 @@ codec->patch_ops.unsol_event = cxt5047_hp_unsol_event; #endif } + + if(codec->mfg) { + int (*cnxthwhda_probe)(void *codec, struct device *hwDev, void **ppDevNode); + cnxthwhda_probe = (void*)symbol_request(cnxthwhda_probe); + if(cnxthwhda_probe) + symbol_put(cnxthwhda_probe); + else + printk(KERN_ERR"%s: symbol_request(cnxthwhda_probe) failed\n", __FUNCTION__); + } + return 0; } @@ -1324,5 +2056,430 @@ .patch = patch_cxt5045 }, { .id = 0x14f15047, .name = "CX20551 (Waikiki)", .patch = patch_cxt5047 }, + { .id = 0x14f15051, .name = "CX20561 (Hermosa)", + .patch = patch_cxt5051 }, + { .id = 0x14f12bfa, .mfg = 2, .name = "HSF", + .patch = patch_cxthsfmodem }, + { .id = 0x14f12c06, .mfg = 2, .name = "HSF", + .patch = patch_cxthsfmodem }, + { .id = 0x14f10000, .mask = 0xffff0000, .mfg = 2, .name = "HSF", + .patch = patch_cxthsfmodem }, {} /* terminator */ }; + + + +typedef enum { + OsHdaStreamStateReset = 0, + OsHdaStreamStateStop = 1, + OsHdaStreamStateRun = 2 +} OSHDA_STREAM_STATE; + +static __shimcall__ +unsigned int conexant_hsfmodem_OsHdaCodecGetAddr(PHDAOSHAL pHdaOsHal) +{ + return ((struct hda_codec *)pHdaOsHal->hda_codec)->addr; +} + +static __shimcall__ +unsigned int conexant_hsfmodem_OsHdaCodecGetVendorId(PHDAOSHAL pHdaOsHal) +{ + return ((struct hda_codec *)pHdaOsHal->hda_codec)->vendor_id; +} + +static __shimcall__ +unsigned int conexant_hsfmodem_OsHdaCodecGetSubsystemId(PHDAOSHAL pHdaOsHal) +{ + return ((struct hda_codec *)pHdaOsHal->hda_codec)->subsystem_id; +} + +static __shimcall__ +unsigned int conexant_hsfmodem_OsHdaCodecGetRevisionId(PHDAOSHAL pHdaOsHal) +{ + return ((struct hda_codec *)pHdaOsHal->hda_codec)->revision_id; +} + +static __shimcall__ +unsigned int conexant_hsfmodem_OsHdaCodecRead(PHDAOSHAL pHdaOsHal, unsigned short nid, int direct, unsigned int verb, unsigned int para) +{ +#if 1 + return snd_hda_codec_read((struct hda_codec *)pHdaOsHal->hda_codec, nid, direct, verb, para); +#else + unsigned int res; + + res = snd_hda_codec_read((struct hda_codec *)pHdaOsHal->hda_codec, nid, direct, verb, para); + + printk(KERN_DEBUG"%s: nid=%x direct=%d verb=0x%x para=0x%x res=0x%08x\n", __FUNCTION__, nid, direct, verb, para, res); + + return res; +#endif +} + +static __shimcall__ +unsigned int conexant_hsfmodem_OsHdaCodecWallclock(PHDAOSHAL pHdaOsHal) +{ + return snd_hda_codec_wallclock((struct hda_codec *)pHdaOsHal->hda_codec); +} + +static __shimcall__ +void conexant_hsfmodem_OsHdaCodecSetEventCallback(PHDAOSHAL pHdaOsHal, void (*cbHdaEvent)(void *Context, unsigned int res), void *cbHdaEventContext, unsigned char *cbHdaTag) +{ + struct conexant_spec *spec = ((struct hda_codec *)pHdaOsHal->hda_codec)->spec; + + *cbHdaTag = ((struct hda_codec *)pHdaOsHal->hda_codec)->mfg; + + spec->modem_cbHdaTag = *cbHdaTag; + spec->modem_cbHdaEventContext = cbHdaEventContext; + spec->modem_cbHdaEvent = (void*)cbHdaEvent; +} + +static __shimcall__ +void conexant_hsfmodem_OsHdaCodecClearEventCallback(PHDAOSHAL pHdaOsHal, unsigned char cbHdaTag) +{ + struct conexant_spec *spec = ((struct hda_codec *)pHdaOsHal->hda_codec)->spec; + + if(spec) { + spec->modem_cbHdaEvent = NULL; + spec->modem_cbHdaEventContext = NULL; + spec->modem_cbHdaTag = 0; + } +} + + +#include + +static int snd_interval_refine_set(struct snd_interval *i, unsigned int val) +{ + struct snd_interval t; + t.empty = 0; + t.min = t.max = val; + t.openmin = t.openmax = 0; + t.integer = 1; + return snd_interval_refine(i, &t); +} + +static int _snd_pcm_hw_param_set(struct snd_pcm_hw_params *params, + snd_pcm_hw_param_t var, unsigned int val, + int dir) +{ + int changed; + if (hw_is_mask(var)) { + struct snd_mask *m = hw_param_mask(params, var); + if (val == 0 && dir < 0) { + changed = -EINVAL; + snd_mask_none(m); + } else { + if (dir > 0) + val++; + else if (dir < 0) + val--; + changed = snd_mask_refine_set(hw_param_mask(params, var), val); + } + } else if (hw_is_interval(var)) { + struct snd_interval *i = hw_param_interval(params, var); + if (val == 0 && dir < 0) { + changed = -EINVAL; + snd_interval_none(i); + } else if (dir == 0) + changed = snd_interval_refine_set(i, val); + else { + struct snd_interval t; + t.openmin = 1; + t.openmax = 1; + t.empty = 0; + t.integer = 0; + if (dir < 0) { + t.min = val - 1; + t.max = val; + } else { + t.min = val; + t.max = val+1; + } + changed = snd_interval_refine(i, &t); + } + } else + return -EINVAL; + if (changed) { + params->cmask |= 1 << var; + params->rmask |= 1 << var; + } + return changed; +} + +static int conexant_modem_snd_pcm_change_params(struct snd_pcm_substream *substream, int hw_param_buffer_bytes) +{ + struct snd_pcm_hw_params *sparams; + int err; + + sparams = kmalloc(sizeof(*sparams), GFP_KERNEL); + if (/*!params ||*/ !sparams) { + return -ENOMEM; + } + + _snd_pcm_hw_params_any(sparams); + _snd_pcm_hw_param_set(sparams, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + hw_param_buffer_bytes, 0); + + snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL); + + if ((err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_HW_PARAMS, sparams)) < 0) { + printk(KERN_ERR"%s: SNDRV_PCM_IOCTL_HW_PARAMS failed (%d)\n", __FUNCTION__, err); + //return err; + } + + return 0; +} + +static int conexant_modem_snd_pcm_prepare_substream(PHDAOSHAL pHdaOsHal, struct snd_pcm_substream *substream) +{ + int err; + struct conexant_spec *spec = ((struct hda_codec *)pHdaOsHal->hda_codec)->spec; + + if(spec->modem_do_prepare[substream->stream]) { + err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_PREPARE, NULL); + if (err < 0) { + printk(KERN_ERR"%s: SNDRV_PCM_IOCTL_PREPARE failed (%d)\n", __FUNCTION__, err); + return err; + } + spec->modem_do_prepare[substream->stream] = 0; + } + + return 0; +} + +static __shimcall__ +int conexant_hsfmodem_OsHdaCodecOpenDMA(PHDAOSHAL pHdaOsHal, int hw_param_buffer_bytes, void **ppPlaybackStream, void **ppCaptureStream) +{ + int i, err; + struct hda_codec *codec = (struct hda_codec *)pHdaOsHal->hda_codec; + struct hda_pcm *info = codec->pcm_info; + struct snd_pcm *pcm; + struct conexant_spec *spec = codec->spec; + + struct snd_pcm_substream *psubstream = NULL, *csubstream = NULL; + static struct file fil; + + for(i = 0; i < codec->num_pcms && !info[i].is_modem; i++); + if(i == codec->num_pcms) { + printk(KERN_ERR"%s: modem pcm not found\n", __FUNCTION__); + return -ENOENT; + } + pcm = info[i].pcm; + + if ((err = snd_pcm_open_substream(pcm, SNDRV_PCM_STREAM_PLAYBACK, &fil, &psubstream)) < 0) { + printk(KERN_ERR"%s: snd_pcm_open_substream STREAM_PLAYBACK failed (%d)\n", __FUNCTION__, err); + return err; + } + + if ((err = snd_pcm_open_substream(pcm, SNDRV_PCM_STREAM_CAPTURE, &fil, &csubstream)) < 0) { + printk(KERN_ERR"%s: snd_pcm_open_substream STREAM_CAPTURE failed (%d)\n", __FUNCTION__, err); + return err; + } + + err = conexant_modem_snd_pcm_change_params(psubstream, hw_param_buffer_bytes); + if (err < 0) { + printk(KERN_ERR"%s: conexant_modem_snd_pcm_change_params STREAM_PLAYBACK failed (%d)\n", __FUNCTION__, err); + } + + err = conexant_modem_snd_pcm_change_params(csubstream, hw_param_buffer_bytes); + if (err < 0) { + printk(KERN_ERR"%s: conexant_modem_snd_pcm_change_params STREAM_CAPTURE failed (%d)\n", __FUNCTION__, err); + } + +#if 0 + printk(KERN_DEBUG"%s: psubstream=%p dma_buffer_p=%p area=%p addr=0x%lx bytes=%d\n", __FUNCTION__, + psubstream, + psubstream->runtime->dma_buffer_p, + psubstream->runtime->dma_area, + (unsigned long)psubstream->runtime->dma_addr, + psubstream->runtime->dma_bytes); + + printk(KERN_DEBUG"%s: csubstream=%p dma_buffer_p=%p area=%p addr=0x%lx bytes=%d\n", __FUNCTION__, + csubstream, + csubstream->runtime->dma_buffer_p, + csubstream->runtime->dma_area, + (unsigned long)csubstream->runtime->dma_addr, + csubstream->runtime->dma_bytes); +#endif + + spec->modem_do_prepare[psubstream->stream] = 1; + spec->modem_do_prepare[csubstream->stream] = 1; + + if ((err = conexant_modem_snd_pcm_prepare_substream(pHdaOsHal, psubstream)) < 0) + return err; + + if ((err = conexant_modem_snd_pcm_prepare_substream(pHdaOsHal, csubstream)) < 0) + return err; + + *ppPlaybackStream = psubstream; + *ppCaptureStream = csubstream; + + return 0; +} + +static void conexant_modem_snd_pcm_close_stream(struct snd_pcm_substream *substream) +{ +#if SNDRV_PCM_VERSION <= SNDRV_PROTOCOL_VERSION(2, 0, 7) + snd_pcm_stream_lock_irq(substream); + if (snd_pcm_running(substream)) + snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); + snd_pcm_stream_unlock_irq(substream); + + if (substream->ops->hw_free != NULL) + substream->ops->hw_free(substream); + + substream->ops->close(substream); +#endif + //printk(KERN_DEBUG"%s: substream=%p refcount=%d\n", __FUNCTION__, substream, substream->ref_count); + snd_pcm_release_substream(substream); +} + +static __shimcall__ +void conexant_hsfmodem_OsHdaCodecCloseDMA(PHDAOSHAL pHdaOsHal, void *pPlaybackStream, void *pCaptureStream) +{ + //printk(KERN_DEBUG"%s: pHdaOsHal=%p pPlaybackStream=%p pCaptureStream=%p\n", __FUNCTION__, pHdaOsHal, pPlaybackStream, pCaptureStream); + if(pHdaOsHal) { + if(pPlaybackStream) + conexant_modem_snd_pcm_close_stream(pPlaybackStream); + if(pCaptureStream) + conexant_modem_snd_pcm_close_stream(pCaptureStream); + } +} + +static __shimcall__ +void conexant_hsfmodem_OsHdaCodecDMAInfo(PHDAOSHAL pHdaOsHal, void *Stream, unsigned char *StreamID, unsigned long *FifoSize, short unsigned int **ppBufAddr) +{ + struct conexant_spec *spec = ((struct hda_codec *)pHdaOsHal->hda_codec)->spec; + struct snd_pcm_substream *substream = (struct snd_pcm_substream *)Stream; + +#if 0 + printk(KERN_DEBUG"%s: substream=%p stream=%u id/tag=%u fifo_size=%u bufAddr=%p\n", __FUNCTION__, + substream, + substream->stream, + spec->modem_stream_tags[substream->stream], + substream->runtime->hw.fifo_size, + substream->runtime->dma_area); +#endif + + *StreamID = spec->modem_stream_tags[substream->stream]; + *FifoSize = substream->runtime->hw.fifo_size; // XXX + *ppBufAddr = (short unsigned int *)substream->runtime->dma_area; +} + +static __shimcall__ +int conexant_hsfmodem_OsHdaCodecSetDMAState(PHDAOSHAL pHdaOsHal, OSHDA_STREAM_STATE streamState, void *pPlaybackStream, void *pCaptureStream) +{ + struct conexant_spec *spec = ((struct hda_codec *)pHdaOsHal->hda_codec)->spec; + struct snd_pcm_substream *psubstream = (struct snd_pcm_substream *)pPlaybackStream; + struct snd_pcm_substream *csubstream = (struct snd_pcm_substream *)pCaptureStream; + int err = 0, cmd; + unsigned long flags, flags2; + + switch(streamState) { + case OsHdaStreamStateRun: +// printk(KERN_DEBUG"%s: Run\n", __FUNCTION__); + if ((err = conexant_modem_snd_pcm_prepare_substream(pHdaOsHal, psubstream)) < 0) + return err; + if ((err = conexant_modem_snd_pcm_prepare_substream(pHdaOsHal, csubstream)) < 0) + return err; + + cmd = SNDRV_PCM_IOCTL_START; + psubstream->runtime->start_threshold = 1; + psubstream->runtime->stop_threshold = psubstream->runtime->boundary; + csubstream->runtime->start_threshold = 1; + csubstream->runtime->stop_threshold = csubstream->runtime->boundary; + break; + case OsHdaStreamStateStop: +// printk(KERN_DEBUG"%s: Stop\n", __FUNCTION__); + cmd = SNDRV_PCM_IOCTL_DROP; + break; + case OsHdaStreamStateReset: +// printk(KERN_DEBUG"%s: Reset\n", __FUNCTION__); + cmd = SNDRV_PCM_IOCTL_RESET; + psubstream->runtime->start_threshold = psubstream->runtime->boundary; + csubstream->runtime->start_threshold = csubstream->runtime->boundary; + break; + default: + printk(KERN_ERR"%s: unknown state %d\n", __FUNCTION__, streamState); + return -ENOSYS; + } + + switch(cmd) { + case SNDRV_PCM_IOCTL_START: + snd_pcm_stream_lock_irqsave(psubstream, flags); + snd_pcm_stream_lock_irqsave(csubstream, flags2); + psubstream->ops->trigger(psubstream, SNDRV_PCM_TRIGGER_START); + csubstream->ops->trigger(csubstream, SNDRV_PCM_TRIGGER_START); + snd_pcm_stream_unlock_irqrestore(csubstream, flags2); + snd_pcm_stream_unlock_irqrestore(psubstream, flags); + break; + case SNDRV_PCM_IOCTL_DROP: + snd_pcm_stream_lock_irqsave(psubstream, flags); + snd_pcm_stream_lock_irqsave(csubstream, flags2); + psubstream->runtime->start_threshold = psubstream->runtime->boundary; + csubstream->runtime->start_threshold = csubstream->runtime->boundary; + psubstream->ops->trigger(psubstream, SNDRV_PCM_TRIGGER_STOP); + csubstream->ops->trigger(csubstream, SNDRV_PCM_TRIGGER_STOP); + snd_pcm_stream_unlock_irqrestore(csubstream, flags2); + snd_pcm_stream_unlock_irqrestore(psubstream, flags); + break; + case SNDRV_PCM_IOCTL_RESET: + //psubstream->ops->trigger(psubstream, SNDRV_PCM_TRIGGER_STOP); + //csubstream->ops->trigger(csubstream, SNDRV_PCM_TRIGGER_STOP); + /*FALLTHROUGH*/ + default: + err = snd_pcm_kernel_ioctl(psubstream, cmd, NULL); + if (err < 0) { + printk(KERN_ERR"%s: snd_pcm_kernel_ioctl (playback) failed (%d)\n", __FUNCTION__, err); + } + + err = snd_pcm_kernel_ioctl(csubstream, cmd, NULL); + if (err < 0) { + printk(KERN_ERR"%s: snd_pcm_kernel_ioctl (capture) failed (%d)\n", __FUNCTION__, err); + } + break; + } + + if(cmd != SNDRV_PCM_IOCTL_START) { + spec->modem_do_prepare[psubstream->stream] = 1; + spec->modem_do_prepare[csubstream->stream] = 1; + } + + return err; +} + +static __shimcall__ +unsigned long conexant_hsfmodem_OsHdaCodecGetDMAPos(PHDAOSHAL pHdaOsHal, void *Stream) +{ + struct hda_codec *codec = (struct hda_codec *)pHdaOsHal->hda_codec; + struct snd_pcm_substream *substream = (struct snd_pcm_substream *)Stream; + int ret; + + if(codec->bus->ops.get_linkpos) + ret = codec->bus->ops.get_linkpos(substream); + else + ret = frames_to_bytes(substream->runtime, substream->ops->pointer(substream)); + + //printk(KERN_DEBUG"%s: substream=%p pos=%ld, ret=%d\n", __FUNCTION__, substream, pos, ret); + + return ret; +} + + +static void *conexant_hsfmodem_interface_funcs[] = { + conexant_hsfmodem_OsHdaCodecGetAddr, + conexant_hsfmodem_OsHdaCodecGetVendorId, + conexant_hsfmodem_OsHdaCodecGetSubsystemId, + conexant_hsfmodem_OsHdaCodecGetRevisionId, + conexant_hsfmodem_OsHdaCodecRead, + conexant_hsfmodem_OsHdaCodecWallclock, + conexant_hsfmodem_OsHdaCodecSetEventCallback, + conexant_hsfmodem_OsHdaCodecClearEventCallback, + conexant_hsfmodem_OsHdaCodecOpenDMA, + conexant_hsfmodem_OsHdaCodecCloseDMA, + conexant_hsfmodem_OsHdaCodecDMAInfo, + conexant_hsfmodem_OsHdaCodecSetDMAState, + conexant_hsfmodem_OsHdaCodecGetDMAPos, + NULL +}; +