--- sound/pci/hda/hda_codec.c.orig 2012-12-11 04:30:57.000000000 +0100 +++ sound/pci/hda/hda_codec.c 2013-02-10 15:40:47.174035742 +0100 @@ -246,6 +246,25 @@ } /** + * 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; +} +EXPORT_SYMBOL_HDA(snd_hda_codec_wallclock); + +/** * snd_hda_codec_read - send a command and get the response * @codec: the HDA codec * @nid: NID to send the command @@ -682,6 +701,22 @@ 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); + } + } +} +EXPORT_SYMBOL_HDA(snd_hda_codec_remove_notify_all); + /* * destructor */ @@ -1390,7 +1425,8 @@ } if (codec->preset && codec->preset->patch) { err = codec->preset->patch(codec); - goto patched; + if (err != -ENOSYS) + goto patched; } /* call the default parser */ @@ -2345,13 +2381,19 @@ { struct hda_bus *bus = codec->bus; struct snd_card *card = bus->card; - int i; + int i, err; if (snd_hda_lock_devices(bus) < 0) return -EBUSY; /* OK, let it free */ + if (codec->patch_ops.exit) { + err = codec->patch_ops.exit(codec); + if (err < 0) + return err; + } + #ifdef CONFIG_PM cancel_delayed_work_sync(&codec->power_work); codec->power_on = 0; --- sound/pci/hda/hda_codec.h.orig 2012-12-11 04:30:57.000000000 +0100 +++ sound/pci/hda/hda_codec.h 2013-02-10 15:36:18.504608488 +0100 @@ -607,6 +607,10 @@ int (*command)(struct hda_bus *bus, unsigned int cmd); /* get a response from the last command */ unsigned int (*get_response)(struct hda_bus *bus, unsigned int addr); + /* 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 *); /* attach a PCM stream */ @@ -705,6 +709,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); void (*set_power_state)(struct hda_codec *codec, hda_nid_t fg, @@ -905,11 +910,13 @@ 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); int snd_hda_codec_configure(struct hda_codec *codec); /* * 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); --- sound/pci/hda/hda_intel.c.orig 2013-02-10 15:28:26.900347850 +0100 +++ sound/pci/hda/hda_intel.c 2013-02-10 15:42:22.855900165 +0100 @@ -58,6 +58,7 @@ #include #include #include "hda_codec.h" +#include "hda_local.h" static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; @@ -708,6 +709,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, WALLCLK); +} + +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 */ @@ -1627,6 +1642,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; bus_temp.ops.attach_pcm = azx_attach_pcm_stream; bus_temp.ops.bus_reset = azx_bus_reset; #ifdef CONFIG_PM @@ -3450,6 +3467,9 @@ 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); if (pci_dev_run_wake(pci)) pm_runtime_get_noresume(&pci->dev); --- sound/pci/hda/patch_conexant.c.orig 2013-02-10 15:28:26.674345810 +0100 +++ sound/pci/hda/patch_conexant.c 2013-02-10 15:36:18.509608533 +0100 @@ -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-2010 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 @@ -53,6 +55,14 @@ #define AUTO_MIC_PORTB (1 << 1) #define AUTO_MIC_PORTC (1 << 2) +#if defined(__i386__) +#define __shimcall__ __attribute__((regparm(0))) +#else +#define __shimcall__ +#endif + +//#define CONFIG_SND_DEBUG + struct pin_dac_pair { hda_nid_t pin; hda_nid_t dac; @@ -118,7 +128,7 @@ int num_channel_mode; /* PCM information */ - struct hda_pcm pcm_rec[2]; /* used in build_pcms() */ + struct hda_pcm pcm_rec[3]; /* used in build_pcms() */ unsigned int spdif_route; @@ -164,6 +174,14 @@ unsigned int beep_amp; + /* 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; + /* extra EAPD pins */ unsigned int num_eapds; hda_nid_t eapds[4]; @@ -257,7 +275,42 @@ return 0; } +#if defined(__i386__) +#define __shimcall__ __attribute__((regparm(0))) +#else +#define __shimcall__ +#endif + +//#define CONFIG_SND_DEBUG +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 const struct hda_pcm_stream conexant_pcm_analog_playback = { .substreams = 1, @@ -337,38 +390,48 @@ }, }; +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, + }, +}; + static int conexant_build_pcms(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; struct hda_pcm *info = spec->pcm_rec; - 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]; - if (spec->capture_stream) - info->stream[SNDRV_PCM_STREAM_CAPTURE] = *spec->capture_stream; - else { - if (codec->vendor_id == 0x14f15051) - info->stream[SNDRV_PCM_STREAM_CAPTURE] = - cx5051_pcm_analog_capture; - else { - 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 (spec->multiout.dig_out_nid) { + if (codec->afg) { + 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]; + if (codec->vendor_id == 0x14f15051) + info->stream[SNDRV_PCM_STREAM_CAPTURE] = + cx5051_pcm_analog_capture; + else + 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]; info++; codec->num_pcms++; + } + + if (spec->multiout.dig_out_nid) { info->name = "Conexant Digital"; info->pcm_type = HDA_PCM_TYPE_SPDIF; info->stream[SNDRV_PCM_STREAM_PLAYBACK] = @@ -383,6 +446,23 @@ } if (spec->slave_dig_outs[0]) codec->slave_dig_outs = spec->slave_dig_outs; + + 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->pcm_type = HDA_PCM_TYPE_MODEM; + + info++; + codec->num_pcms++; } return 0; @@ -433,16 +513,63 @@ snd_hda_codec_set_power_to_all(codec, fg, power_state, true); } +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: Conexant HSF modem detected but driver not present\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; +} + static void conexant_free(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; @@ -451,6 +578,106 @@ 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_bHdaEvent); + //printk(KERN_DEBUG"%s: calling cbHdaEvent=%p ctx=%p\n", __FUNCTION__, spec->modem_cHdaEvent, 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); + } + + 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 const struct snd_kcontrol_new cxt_capture_mixers[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -557,7 +784,13 @@ .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 .set_power_state = conexant_set_power, }; @@ -569,6 +802,38 @@ #endif static int patch_conexant_auto(struct hda_codec *codec); + +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; + 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: Conexant HSF modem detected but driver not present\n", __FUNCTION__); + codec->spec = NULL; + memset(&codec->patch_ops, 0, sizeof(codec->patch_ops)); + kfree(spec); + return -ENOSYS; + } + + return 0; +} + /* * EAPD control * the private value = nid | (invert << 8) @@ -740,13 +1005,15 @@ static void cxt5045_hp_automic(struct hda_codec *codec) { static const 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 const 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; @@ -776,15 +1043,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; } } @@ -1172,6 +1443,15 @@ if (spec->beep_amp) snd_hda_attach_beep_device(codec, spec->beep_amp); + 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: Conexant HSF modem detected but driver not present\n", __FUNCTION__); + } + return 0; } @@ -1210,7 +1490,7 @@ * the headphone jack */ bits = (!spec->hp_present && spec->cur_eapd) ? 0 : HDA_AMP_MUTE; - /* NOTE: Conexat codec needs the index for *OUTPUT* amp of + /* NOTE: Conexant codec needs the index for *OUTPUT* amp of * pin widgets unlike other codecs. In this case, we need to * set index 0x01 for the volume from the mixer amp 0x19. */ @@ -1561,6 +1841,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: Conexant HSF modem detected but driver not present\n", __FUNCTION__); + } + spec->vmaster_nid = 0x13; switch (codec->subsystem_id >> 16) { @@ -1677,6 +1967,9 @@ case CXT5051_PORTC_EVENT: cxt5051_portc_automic(codec); break; + default: + conexant_unsol_event(codec, res); + break; } } @@ -1951,6 +2244,15 @@ if (spec->beep_amp) snd_hda_attach_beep_device(codec, spec->beep_amp); + 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: Conexant HSF modem detected but driver not present\n", __FUNCTION__); + } + return 0; } @@ -4539,6 +4841,420 @@ /* */ +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].pcm_type != HDA_PCM_TYPE_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 +}; + +/* + */ + static const struct hda_codec_preset snd_hda_preset_conexant[] = { { .id = 0x14f15045, .name = "CX20549 (Venice)", .patch = patch_cxt5045 }, @@ -4556,6 +5272,12 @@ .patch = patch_cxt5066 }, { .id = 0x14f1506c, .name = "CX20588", .patch = patch_cxt5066 }, + { .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 }, { .id = 0x14f1506e, .name = "CX20590", .patch = patch_cxt5066 }, { .id = 0x14f15097, .name = "CX20631", @@ -4596,6 +5318,9 @@ MODULE_ALIAS("snd-hda-codec-id:14f15067"); MODULE_ALIAS("snd-hda-codec-id:14f15068"); MODULE_ALIAS("snd-hda-codec-id:14f15069"); +MODULE_ALIAS("snd-hda-codec-id:14f12bfa"); +MODULE_ALIAS("snd-hda-codec-id:14f12c06"); +MODULE_ALIAS("snd-hda-codec-id:14f1*"); MODULE_ALIAS("snd-hda-codec-id:14f1506c"); MODULE_ALIAS("snd-hda-codec-id:14f1506e"); MODULE_ALIAS("snd-hda-codec-id:14f15097"); @@ -4614,7 +5339,7 @@ MODULE_ALIAS("snd-hda-codec-id:14f15115"); MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Conexant HD-audio codec"); +MODULE_DESCRIPTION("Conexant HD-audio and modem codec"); static struct hda_codec_preset_list conexant_list = { .preset = snd_hda_preset_conexant,