From 6ff86a3f33e84c430aeb8ff3b7f68b1c052ec1e9 Mon Sep 17 00:00:00 2001 From: Kailang Yang Date: Fri, 19 Mar 2010 11:14:36 +0100 Subject: ALSA: hda - Take internal mic as Front Mic Add new check for MIC. Do the internal DMIC as the Front MIC. It could solve the default record source index issue. [Fix the check properly using the bitmask by tiwai] Signed-off-by: Kailang Yang Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 0e76ac2b2ace..d72bcff8d271 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -4218,7 +4218,8 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, break; case AC_JACK_MIC_IN: { int preferred, alt; - if (loc == AC_JACK_LOC_FRONT) { + if (loc == AC_JACK_LOC_FRONT || + (loc & 0x30) == AC_JACK_LOC_INTERNAL) { preferred = AUTO_PIN_FRONT_MIC; alt = AUTO_PIN_MIC; } else { -- cgit v1.2.3 From da00c24493bf6ae3772dfe7343dca033ebc75955 Mon Sep 17 00:00:00 2001 From: Kailang Yang Date: Fri, 19 Mar 2010 11:23:45 +0100 Subject: ALSA: hda - Add parse customize define function for Realtek codecs Added alc_auto_parse_customize_define() to parse the Realtek-specific attributes from SKU. Also enable beep controls only when the proper attribute bit is set. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 112 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 96 insertions(+), 16 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 4ec57633af88..245e1afa5896 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -275,6 +275,18 @@ struct alc_mic_route { #define MUX_IDX_UNDEF ((unsigned char)-1) +struct alc_customize_define { + unsigned int sku_cfg; + unsigned char port_connectivity; + unsigned char check_sum; + unsigned char customization; + unsigned char external_amp; + unsigned int enable_pcbeep:1; + unsigned int platform_type:1; + unsigned int swap:1; + unsigned int override:1; +}; + struct alc_spec { /* codec parameterization */ struct snd_kcontrol_new *mixers[5]; /* mixer arrays */ @@ -332,6 +344,7 @@ struct alc_spec { /* dynamic controls, init_verbs and input_mux */ struct auto_pin_cfg autocfg; + struct alc_customize_define cdefine; struct snd_array kctls; struct hda_input_mux private_imux[3]; hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; @@ -1247,6 +1260,62 @@ static void alc_init_auto_mic(struct hda_codec *codec) spec->unsol_event = alc_sku_unsol_event; } +static int alc_auto_parse_customize_define(struct hda_codec *codec) +{ + unsigned int ass, tmp, i; + unsigned nid; + struct alc_spec *spec = codec->spec; + + ass = codec->subsystem_id & 0xffff; + if (ass != codec->bus->pci->subsystem_device && (ass & 1)) + goto do_sku; + + nid = 0x1d; + if (codec->vendor_id == 0x10ec0260) + nid = 0x17; + ass = snd_hda_codec_get_pincfg(codec, nid); + + if (!(ass & 1)) { + printk(KERN_INFO "hda_codec: %s: SKU not ready 0x%08x\n", + codec->chip_name, ass); + return -1; + } + + /* check sum */ + tmp = 0; + for (i = 1; i < 16; i++) { + if ((ass >> i) & 1) + tmp++; + } + if (((ass >> 16) & 0xf) != tmp) + return -1; + + spec->cdefine.port_connectivity = ass >> 30; + spec->cdefine.enable_pcbeep = (ass & 0x100000) >> 20; + spec->cdefine.check_sum = (ass >> 16) & 0xf; + spec->cdefine.customization = ass >> 8; +do_sku: + spec->cdefine.sku_cfg = ass; + spec->cdefine.external_amp = (ass & 0x38) >> 3; + spec->cdefine.platform_type = (ass & 0x4) >> 2; + spec->cdefine.swap = (ass & 0x2) >> 1; + spec->cdefine.override = ass & 0x1; + + snd_printd("SKU: Nid=0x%x sku_cfg=0x%08x\n", + nid, spec->cdefine.sku_cfg); + snd_printd("SKU: port_connectivity=0x%x\n", + spec->cdefine.port_connectivity); + snd_printd("SKU: enable_pcbeep=0x%x\n", spec->cdefine.enable_pcbeep); + snd_printd("SKU: check_sum=0x%08x\n", spec->cdefine.check_sum); + snd_printd("SKU: customization=0x%08x\n", spec->cdefine.customization); + snd_printd("SKU: external_amp=0x%x\n", spec->cdefine.external_amp); + snd_printd("SKU: platform_type=0x%x\n", spec->cdefine.platform_type); + snd_printd("SKU: swap=0x%x\n", spec->cdefine.swap); + snd_printd("SKU: override=0x%x\n", spec->cdefine.override); + + return 0; +} + /* check subsystem ID and set up device-specific initialization; * return 1 if initialized, 0 if invalid SSID */ @@ -3779,7 +3848,6 @@ static struct hda_codec_ops alc_patch_ops = { .reboot_notify = alc_shutup, }; - /* * Test configuration for debugging * @@ -10267,6 +10335,8 @@ static int patch_alc882(struct hda_codec *codec) codec->spec = spec; + alc_auto_parse_customize_define(codec); + switch (codec->vendor_id) { case 0x10ec0882: case 0x10ec0885: @@ -10362,7 +10432,9 @@ static int patch_alc882(struct hda_codec *codec) } set_capture_mixer(codec); - set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); + + if (spec->cdefine.enable_pcbeep) + set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); spec->vmaster_nid = 0x0c; @@ -12146,6 +12218,7 @@ static int patch_alc262(struct hda_codec *codec) snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PROC_COEF, tmp | 0x80); } #endif + alc_auto_parse_customize_define(codec); alc_fix_pll_init(codec, 0x20, 0x0a, 10); @@ -12224,7 +12297,7 @@ static int patch_alc262(struct hda_codec *codec) } if (!spec->cap_mixer && !spec->no_analog) set_capture_mixer(codec); - if (!spec->no_analog) + if (!spec->no_analog && spec->cdefine.enable_pcbeep) set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); spec->vmaster_nid = 0x0c; @@ -14094,6 +14167,8 @@ static int patch_alc269(struct hda_codec *codec) codec->spec = spec; + alc_auto_parse_customize_define(codec); + alc_fix_pll_init(codec, 0x20, 0x04, 15); if ((alc_read_coef_idx(codec, 0) & 0x00f0) == 0x0010){ @@ -14164,7 +14239,8 @@ static int patch_alc269(struct hda_codec *codec) if (!spec->cap_mixer) set_capture_mixer(codec); - set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT); + if (spec->cdefine.enable_pcbeep) + set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT); spec->vmaster_nid = 0x02; @@ -18314,6 +18390,8 @@ static int patch_alc662(struct hda_codec *codec) codec->spec = spec; + alc_auto_parse_customize_define(codec); + alc_fix_pll_init(codec, 0x20, 0x04, 15); if (alc_read_coef_idx(codec, 0)==0x8020){ @@ -18373,18 +18451,20 @@ static int patch_alc662(struct hda_codec *codec) if (!spec->cap_mixer) set_capture_mixer(codec); - switch (codec->vendor_id) { - case 0x10ec0662: - set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); - break; - case 0x10ec0272: - case 0x10ec0663: - case 0x10ec0665: - set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT); - break; - case 0x10ec0273: - set_beep_amp(spec, 0x0b, 0x03, HDA_INPUT); - break; + if (spec->cdefine.enable_pcbeep) { + switch (codec->vendor_id) { + case 0x10ec0662: + set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); + break; + case 0x10ec0272: + case 0x10ec0663: + case 0x10ec0665: + set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT); + break; + case 0x10ec0273: + set_beep_amp(spec, 0x0b, 0x03, HDA_INPUT); + break; + } } spec->vmaster_nid = 0x02; -- cgit v1.2.3 From c027ddcd01c852dfa5880862e47f022d7d9402f5 Mon Sep 17 00:00:00 2001 From: Kailang Yang Date: Fri, 19 Mar 2010 11:33:06 +0100 Subject: ALSA: hda - Add alc_codec_rename() helper Added alc_codec_rename() helper for renaming codec->chip_name. Added Acer-specific codec naming for ALC269/662. [Clean-up and refactoring by tiwai] Signed-off-by: Kailang Yang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 42 +++++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 17 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 245e1afa5896..3d37e2161a57 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -3848,6 +3848,18 @@ static struct hda_codec_ops alc_patch_ops = { .reboot_notify = alc_shutup, }; +/* replace the codec chip_name with the given string */ +static int alc_codec_rename(struct hda_codec *codec, const char *name) +{ + kfree(codec->chip_name); + codec->chip_name = kstrdup(name, GFP_KERNEL); + if (!codec->chip_name) { + alc_free(codec); + return -ENOMEM; + } + return 0; +} + /* * Test configuration for debugging * @@ -14169,17 +14181,15 @@ static int patch_alc269(struct hda_codec *codec) alc_auto_parse_customize_define(codec); - alc_fix_pll_init(codec, 0x20, 0x04, 15); - if ((alc_read_coef_idx(codec, 0) & 0x00f0) == 0x0010){ - kfree(codec->chip_name); - codec->chip_name = kstrdup("ALC259", GFP_KERNEL); - if (!codec->chip_name) { - alc_free(codec); - return -ENOMEM; - } + if (codec->bus->pci->subsystem_vendor == 0x1025 && + spec->cdefine.platform_type == 1) + alc_codec_rename(codec, "ALC271X"); + else + alc_codec_rename(codec, "ALC259"); is_alc269vb = 1; - } + } else + alc_fix_pll_init(codec, 0x20, 0x04, 15); board_config = snd_hda_check_board_config(codec, ALC269_MODEL_LAST, alc269_models, @@ -18394,14 +18404,12 @@ static int patch_alc662(struct hda_codec *codec) alc_fix_pll_init(codec, 0x20, 0x04, 15); - if (alc_read_coef_idx(codec, 0)==0x8020){ - kfree(codec->chip_name); - codec->chip_name = kstrdup("ALC661", GFP_KERNEL); - if (!codec->chip_name) { - alc_free(codec); - return -ENOMEM; - } - } + if (alc_read_coef_idx(codec, 0) == 0x8020) + alc_codec_rename(codec, "ALC661"); + else if ((alc_read_coef_idx(codec, 0) & (1 << 14)) && + codec->bus->pci->subsystem_vendor == 0x1025 && + spec->cdefine.platform_type == 1) + alc_codec_rename(codec, "ALC272X"); board_config = snd_hda_check_board_config(codec, ALC662_MODEL_LAST, alc662_models, -- cgit v1.2.3 From 7fb5622326bf98437e629e94f8710eb12a90b30d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 22 Mar 2010 17:09:47 +0100 Subject: ALSA: hda - Fix uninitialized variable warning in alc_auto_parse_customize_define() Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 3d37e2161a57..3dc2be8f3ca9 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1263,7 +1263,7 @@ static void alc_init_auto_mic(struct hda_codec *codec) static int alc_auto_parse_customize_define(struct hda_codec *codec) { unsigned int ass, tmp, i; - unsigned nid; + unsigned nid = 0; struct alc_spec *spec = codec->spec; ass = codec->subsystem_id & 0xffff; -- cgit v1.2.3 From ae76148114aa2457baa0d340fc506bb7d114cb12 Mon Sep 17 00:00:00 2001 From: Bernhard Urban Date: Tue, 23 Mar 2010 04:12:38 +0100 Subject: ALSA: aureon - Patch for suspend/resume for Terratec Aureon cards. Add proper suspend/resume code for Terratec Aureon cards. Based on ice1724 suspend/resume work of Igor Chernyshev. Fixes bug https://bugtrack.alsa-project.org/alsa-bug/view.php?id=4944 Tested on linux-2.6.32.9 Signed-off-by: Bernhard Urban Signed-off-by: Takashi Iwai --- sound/pci/ice1712/aureon.c | 89 +++++++++++++++++++++++++++++++++------------- 1 file changed, 64 insertions(+), 25 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/ice1712/aureon.c b/sound/pci/ice1712/aureon.c index 9e66f6d306f8..2f6252266a02 100644 --- a/sound/pci/ice1712/aureon.c +++ b/sound/pci/ice1712/aureon.c @@ -1956,11 +1956,10 @@ static int __devinit aureon_add_controls(struct snd_ice1712 *ice) return 0; } - /* - * initialize the chip + * reset the chip */ -static int __devinit aureon_init(struct snd_ice1712 *ice) +static int aureon_reset(struct snd_ice1712 *ice) { static const unsigned short wm_inits_aureon[] = { /* These come first to reduce init pop noise */ @@ -2047,30 +2046,10 @@ static int __devinit aureon_init(struct snd_ice1712 *ice) 0x0605, /* slave, 24bit, MSB on second OSCLK, SDOUT for right channel when OLRCK is high */ (unsigned short)-1 }; - struct aureon_spec *spec; unsigned int tmp; const unsigned short *p; - int err, i; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (!spec) - return -ENOMEM; - ice->spec = spec; - - if (ice->eeprom.subvendor == VT1724_SUBDEVICE_AUREON51_SKY) { - ice->num_total_dacs = 6; - ice->num_total_adcs = 2; - } else { - /* aureon 7.1 and prodigy 7.1 */ - ice->num_total_dacs = 8; - ice->num_total_adcs = 2; - } - - /* to remeber the register values of CS8415 */ - ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL); - if (!ice->akm) - return -ENOMEM; - ice->akm_codecs = 1; + int err; + struct aureon_spec *spec = ice->spec; err = aureon_ac97_init(ice); if (err != 0) @@ -2118,6 +2097,61 @@ static int __devinit aureon_init(struct snd_ice1712 *ice) /* initialize PCA9554 pin directions & set default input */ aureon_pca9554_write(ice, PCA9554_DIR, 0x00); aureon_pca9554_write(ice, PCA9554_OUT, 0x00); /* internal AUX */ + return 0; +} + +/* + * suspend/resume + */ +#ifdef CONFIG_PM +static int aureon_resume(struct snd_ice1712 *ice) +{ + struct aureon_spec *spec = ice->spec; + int err, i; + + err = aureon_reset(ice); + if (err != 0) + return err; + + /* workaround for poking volume with alsamixer after resume: + * just set stored volume again */ + for (i = 0; i < ice->num_total_dacs; i++) + wm_set_vol(ice, i, spec->vol[i], spec->master[i % 2]); + return 0; +} +#endif + +/* + * initialize the chip + */ +static int __devinit aureon_init(struct snd_ice1712 *ice) +{ + struct aureon_spec *spec; + int i, err; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return -ENOMEM; + ice->spec = spec; + + if (ice->eeprom.subvendor == VT1724_SUBDEVICE_AUREON51_SKY) { + ice->num_total_dacs = 6; + ice->num_total_adcs = 2; + } else { + /* aureon 7.1 and prodigy 7.1 */ + ice->num_total_dacs = 8; + ice->num_total_adcs = 2; + } + + /* to remeber the register values of CS8415 */ + ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL); + if (!ice->akm) + return -ENOMEM; + ice->akm_codecs = 1; + + err = aureon_reset(ice); + if (err != 0) + return err; spec->master[0] = WM_VOL_MUTE; spec->master[1] = WM_VOL_MUTE; @@ -2126,6 +2160,11 @@ static int __devinit aureon_init(struct snd_ice1712 *ice) wm_set_vol(ice, i, spec->vol[i], spec->master[i % 2]); } +#ifdef CONFIG_PM + ice->pm_resume = aureon_resume; + ice->pm_suspend_enabled = 1; +#endif + return 0; } -- cgit v1.2.3 From cd508fe58b07499403e806b558c7f15c90442292 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Fri, 26 Mar 2010 10:28:46 +0100 Subject: ALSA: hda-intel - add special 'hwio' model to bypass initialization Using the 'model=hwio' option, the driver bypasses any codec initialization and the reset procedure for codecs is also bypassed. This mode is usefull to enable direct access using hwdep interface (using hdaverb or hda-analyzer tools) and retain codec setup from BIOS. Signed-off-by: Jaroslav Kysela --- sound/pci/hda/hda_codec.c | 10 ++++++++-- sound/pci/hda/hda_intel.c | 20 ++++++++++++-------- 2 files changed, 20 insertions(+), 10 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 0e76ac2b2ace..cf6280bdaa19 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -609,11 +609,15 @@ int /*__devinit*/ snd_hda_bus_new(struct snd_card *card, } EXPORT_SYMBOL_HDA(snd_hda_bus_new); +#define is_hwio_config(codec) \ + (codec->modelname && !strcmp(codec->modelname, "hwio")) #ifdef CONFIG_SND_HDA_GENERIC #define is_generic_config(codec) \ - (codec->modelname && !strcmp(codec->modelname, "generic")) + ((codec->modelname && !strcmp(codec->modelname, "generic")) || \ + is_hwio_config(codec)) #else -#define is_generic_config(codec) 0 +#define is_generic_config(codec) \ + is_hwio_config(codec) #endif #ifdef MODULE @@ -1113,6 +1117,8 @@ int snd_hda_codec_configure(struct hda_codec *codec) } if (is_generic_config(codec)) { + if (is_hwio_config(codec)) + goto patched; err = snd_hda_parse_generic_codec(codec); goto patched; } diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 8b2915631cc3..8d477613bccf 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -858,10 +858,13 @@ static void azx_power_notify(struct hda_bus *bus); #endif /* reset codec link */ -static int azx_reset(struct azx *chip) +static int azx_reset(struct azx *chip, int full_reset) { int count; + if (!full_reset) + goto __skip; + /* clear STATESTS */ azx_writeb(chip, STATESTS, STATESTS_INT_MASK); @@ -887,6 +890,7 @@ static int azx_reset(struct azx *chip) /* Brent Chartrand said to wait >= 540us for codecs to initialize */ msleep(1); + __skip: /* check to see if controller is ready */ if (!azx_readb(chip, GCTL)) { snd_printd(SFX "azx_reset: controller not ready!\n"); @@ -998,13 +1002,13 @@ static void azx_stream_stop(struct azx *chip, struct azx_dev *azx_dev) /* * reset and start the controller registers */ -static void azx_init_chip(struct azx *chip) +static void azx_init_chip(struct azx *chip, int full_reset) { if (chip->initialized) return; /* reset controller */ - azx_reset(chip); + azx_reset(chip, full_reset); /* initialize interrupts */ azx_int_clear(chip); @@ -1348,7 +1352,7 @@ static void azx_bus_reset(struct hda_bus *bus) bus->in_reset = 1; azx_stop_chip(chip); - azx_init_chip(chip); + azx_init_chip(chip, 1); #ifdef CONFIG_PM if (chip->initialized) { int i; @@ -1422,7 +1426,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model) * get back to the sanity state. */ azx_stop_chip(chip); - azx_init_chip(chip); + azx_init_chip(chip, 1); } } } @@ -2112,7 +2116,7 @@ static void azx_power_notify(struct hda_bus *bus) } } if (power_on) - azx_init_chip(chip); + azx_init_chip(chip, 1); else if (chip->running && power_save_controller && !bus->power_keep_link_on) azx_stop_chip(chip); @@ -2182,7 +2186,7 @@ static int azx_resume(struct pci_dev *pci) azx_init_pci(chip); if (snd_hda_codecs_inuse(chip->bus)) - azx_init_chip(chip); + azx_init_chip(chip, 1); snd_hda_resume(chip->bus); snd_power_change_state(card, SNDRV_CTL_POWER_D0); @@ -2573,7 +2577,7 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, /* initialize chip */ azx_init_pci(chip); - azx_init_chip(chip); + azx_init_chip(chip, model[dev] == NULL || strcmp(model[dev], "hwio")); /* codec detection */ if (!chip->codec_mask) { -- cgit v1.2.3 From 0bf0e5a6f304ac1bc93a80cdd68b4d91f3519eb5 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Fri, 26 Mar 2010 10:33:18 +0100 Subject: ALSA: hda-intel - AD1984 thinkpad - add analog beep input control For Lenovo Thinkpad T61/X61, the analog beep input is connected to node 0x20, index 3. Move the digital beep mute/volume controls as "Digital Beep" and create analog beep controls for mentioned node. Signed-off-by: Jaroslav Kysela --- sound/pci/hda/patch_analog.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index e6d1bdff1b6e..262f5b5decf1 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -71,9 +71,10 @@ struct ad198x_spec { struct hda_input_mux private_imux; hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; - unsigned int jack_present :1; - unsigned int inv_jack_detect:1; /* inverted jack-detection */ - unsigned int inv_eapd:1; /* inverted EAPD implementation */ + unsigned int jack_present: 1; + unsigned int inv_jack_detect: 1;/* inverted jack-detection */ + unsigned int inv_eapd: 1; /* inverted EAPD implementation */ + unsigned int analog_beep: 1; /* analog beep input present */ #ifdef CONFIG_SND_HDA_POWER_SAVE struct hda_loopback_check loopback; @@ -165,6 +166,12 @@ static struct snd_kcontrol_new ad_beep_mixer[] = { { } /* end */ }; +static struct snd_kcontrol_new ad_beep2_mixer[] = { + HDA_CODEC_VOLUME("Digital Beep Playback Volume", 0, 0, HDA_OUTPUT), + HDA_CODEC_MUTE_BEEP("Digital Beep Playback Switch", 0, 0, HDA_OUTPUT), + { } /* end */ +}; + #define set_beep_amp(spec, nid, idx, dir) \ ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) /* mono */ #else @@ -203,7 +210,8 @@ static int ad198x_build_controls(struct hda_codec *codec) #ifdef CONFIG_SND_HDA_INPUT_BEEP if (spec->beep_amp) { struct snd_kcontrol_new *knew; - for (knew = ad_beep_mixer; knew->name; knew++) { + knew = spec->analog_beep ? ad_beep2_mixer : ad_beep_mixer; + for ( ; knew->name; knew++) { struct snd_kcontrol *kctl; kctl = snd_ctl_new1(knew, codec); if (!kctl) @@ -3482,6 +3490,8 @@ static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = { HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT), HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT), HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT), + HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT), HDA_CODEC_VOLUME("Docking Mic Playback Volume", 0x20, 0x04, HDA_INPUT), HDA_CODEC_MUTE("Docking Mic Playback Switch", 0x20, 0x04, HDA_INPUT), HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT), @@ -3523,6 +3533,8 @@ static struct hda_verb ad1984_thinkpad_init_verbs[] = { {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* docking mic boost */ {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + /* Analog PC Beeper - allow firmware/ACPI beeps */ + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3) | 0x1a}, /* Analog mixer - docking mic; mute as default */ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* enable EAPD bit */ @@ -3655,6 +3667,7 @@ static int patch_ad1984(struct hda_codec *codec) spec->input_mux = &ad1984_thinkpad_capture_source; spec->mixers[0] = ad1984_thinkpad_mixers; spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs; + spec->analog_beep = 1; break; case AD1984_DELL_DESKTOP: spec->multiout.dig_out_nid = 0; -- cgit v1.2.3 From 10e77ddac0e1f3b9cea04dfe8180f8641f329703 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Fri, 26 Mar 2010 11:04:38 +0100 Subject: ALSA: hda-intel - remove model=hwio , use probe_only=3 instead The probe_only module parameter skips the codec initialization, too. Remove the model=hwio code and use second bit in probe_only to skip the HDA codec reset procedure. Signed-off-by: Jaroslav Kysela --- sound/pci/hda/hda_codec.c | 10 ++-------- sound/pci/hda/hda_intel.c | 4 ++-- 2 files changed, 4 insertions(+), 10 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index cf6280bdaa19..0e76ac2b2ace 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -609,15 +609,11 @@ int /*__devinit*/ snd_hda_bus_new(struct snd_card *card, } EXPORT_SYMBOL_HDA(snd_hda_bus_new); -#define is_hwio_config(codec) \ - (codec->modelname && !strcmp(codec->modelname, "hwio")) #ifdef CONFIG_SND_HDA_GENERIC #define is_generic_config(codec) \ - ((codec->modelname && !strcmp(codec->modelname, "generic")) || \ - is_hwio_config(codec)) + (codec->modelname && !strcmp(codec->modelname, "generic")) #else -#define is_generic_config(codec) \ - is_hwio_config(codec) +#define is_generic_config(codec) 0 #endif #ifdef MODULE @@ -1117,8 +1113,6 @@ int snd_hda_codec_configure(struct hda_codec *codec) } if (is_generic_config(codec)) { - if (is_hwio_config(codec)) - goto patched; err = snd_hda_parse_generic_codec(codec); goto patched; } diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 8d477613bccf..6ce673c17ff2 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2577,7 +2577,7 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, /* initialize chip */ azx_init_pci(chip); - azx_init_chip(chip, model[dev] == NULL || strcmp(model[dev], "hwio")); + azx_init_chip(chip, (probe_only[dev] & 2) == 0); /* codec detection */ if (!chip->codec_mask) { @@ -2666,7 +2666,7 @@ static int __devinit azx_probe(struct pci_dev *pci, goto out_free; } #endif - if (!probe_only[dev]) { + if ((probe_only[dev] & 1) == 0) { err = azx_codec_configure(chip); if (err < 0) goto out_free; -- cgit v1.2.3 From 079e683ebd882655b6d6e0b17989e51bae2d77d6 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Fri, 26 Mar 2010 11:16:59 +0100 Subject: ALSA: hda-intel - probe_only module option is int type now Signed-off-by: Jaroslav Kysela --- sound/pci/hda/hda_intel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 6ce673c17ff2..a33c1c50b310 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -84,7 +84,7 @@ module_param_array(bdl_pos_adj, int, NULL, 0644); MODULE_PARM_DESC(bdl_pos_adj, "BDL position adjustment offset."); module_param_array(probe_mask, int, NULL, 0444); MODULE_PARM_DESC(probe_mask, "Bitmask to probe codecs (default = -1)."); -module_param_array(probe_only, bool, NULL, 0444); +module_param_array(probe_only, int, NULL, 0444); MODULE_PARM_DESC(probe_only, "Only probing and no codec initialization."); module_param(single_cmd, bool, 0444); MODULE_PARM_DESC(single_cmd, "Use single command to communicate with codecs " -- cgit v1.2.3 From 467126460818e287aa92c73232c38453ec2ab013 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 29 Mar 2010 09:19:38 +0200 Subject: ALSA: hda - Report errors when invalid values are passed to snd_hda_amp_*() The values should be in 8 bits. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index d72bcff8d271..d1424e7b9f3d 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1461,6 +1461,8 @@ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, idx)); if (!info) return 0; + if (snd_BUG_ON(mask & ~0xff)) + mask &= 0xff; val &= mask; val |= get_vol_mute(codec, info, nid, ch, direction, idx) & ~mask; if (info->vol[ch] == val) @@ -1486,6 +1488,9 @@ int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid, int direction, int idx, int mask, int val) { int ch, ret = 0; + + if (snd_BUG_ON(mask & ~0xff)) + mask &= 0xff; for (ch = 0; ch < 2; ch++) ret |= snd_hda_codec_amp_update(codec, nid, ch, direction, idx, mask, val); -- cgit v1.2.3 From ad35879aa192e783db2d41c196f48f3fc059ce22 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 30 Mar 2010 18:00:59 +0200 Subject: ALSA: hda - Add mute LED support for HP laptop with ALC269 Some HP laptops have a mute LED that is controlled over the unused MIC2 VREF pin. Implement the LED updater like patch_sigmatel.c for this model. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 1694f60631a1..9a361c2c7336 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -3467,6 +3467,10 @@ static int alc_init(struct hda_codec *codec) if (spec->init_hook) spec->init_hook(codec); +#ifdef CONFIG_SND_HDA_POWER_SAVE + if (codec->patch_ops.check_power_status) + codec->patch_ops.check_power_status(codec, 0x01); +#endif return 0; } @@ -3827,6 +3831,10 @@ static int alc_resume(struct hda_codec *codec) codec->patch_ops.init(codec); snd_hda_codec_resume_amp(codec); snd_hda_codec_resume_cache(codec); +#ifdef CONFIG_SND_HDA_POWER_SAVE + if (codec->patch_ops.check_power_status) + codec->patch_ops.check_power_status(codec, 0x01); +#endif return 0; } #endif @@ -13983,6 +13991,34 @@ static struct hda_pcm_stream alc269_44k_pcm_analog_capture = { /* NID is set in alc_build_pcms */ }; +#ifdef CONFIG_SND_HDA_POWER_SAVE +static int alc269_mic2_for_mute_led(struct hda_codec *codec) +{ + switch (codec->subsystem_id) { + case 0x103c1586: + return 1; + } + return 0; +} + +static int alc269_mic2_mute_check_ps(struct hda_codec *codec, hda_nid_t nid) +{ + /* update mute-LED according to the speaker mute state */ + if (nid == 0x01 || nid == 0x14) { + int pinval; + if (snd_hda_codec_amp_read(codec, 0x14, 0, HDA_OUTPUT, 0) & + HDA_AMP_MUTE) + pinval = 0x24; + else + pinval = 0x20; + /* mic2 vref pin is used for mute LED control */ + snd_hda_codec_write(codec, 0x19, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, pinval); + } + return alc_check_power_status(codec, nid); +} +#endif /* CONFIG_SND_HDA_POWER_SAVE */ + /* * BIOS auto configuration */ @@ -14330,6 +14366,8 @@ static int patch_alc269(struct hda_codec *codec) #ifdef CONFIG_SND_HDA_POWER_SAVE if (!spec->loopback.amplist) spec->loopback.amplist = alc269_loopbacks; + if (alc269_mic2_for_mute_led(codec)) + codec->patch_ops.check_power_status = alc269_mic2_mute_check_ps; #endif return 0; -- cgit v1.2.3 From a68d5a5419d2a15161488b20f740115068e7b666 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 30 Mar 2010 18:03:44 +0200 Subject: ALSA: hda - introduce snd_hda_codec_update_cache() Add a new helper, snd_hda_codec_update_cache(), for reducing the unneeded verbs. This function checks the cached value and skips if it's identical with the given one. Otherwise it works like snd_hda_codec_write_cache(). The alc269 code uses this function as an example. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 68 +++++++++++++++++++++++++++++++++++-------- sound/pci/hda/hda_codec.h | 3 ++ sound/pci/hda/patch_realtek.c | 5 ++-- 3 files changed, 62 insertions(+), 14 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index d1424e7b9f3d..a3d638c8c1fd 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1209,8 +1209,7 @@ static void free_hda_cache(struct hda_cache_rec *cache) } /* query the hash. allocate an entry if not found. */ -static struct hda_cache_head *get_alloc_hash(struct hda_cache_rec *cache, - u32 key) +static struct hda_cache_head *get_hash(struct hda_cache_rec *cache, u32 key) { u16 idx = key % (u16)ARRAY_SIZE(cache->hash); u16 cur = cache->hash[idx]; @@ -1222,17 +1221,27 @@ static struct hda_cache_head *get_alloc_hash(struct hda_cache_rec *cache, return info; cur = info->next; } + return NULL; +} - /* add a new hash entry */ - info = snd_array_new(&cache->buf); - if (!info) - return NULL; - cur = snd_array_index(&cache->buf, info); - info->key = key; - info->val = 0; - info->next = cache->hash[idx]; - cache->hash[idx] = cur; - +/* query the hash. allocate an entry if not found. */ +static struct hda_cache_head *get_alloc_hash(struct hda_cache_rec *cache, + u32 key) +{ + struct hda_cache_head *info = get_hash(cache, key); + if (!info) { + u16 idx, cur; + /* add a new hash entry */ + info = snd_array_new(&cache->buf); + if (!info) + return NULL; + cur = snd_array_index(&cache->buf, info); + info->key = key; + info->val = 0; + idx = key % (u16)ARRAY_SIZE(cache->hash); + info->next = cache->hash[idx]; + cache->hash[idx] = cur; + } return info; } @@ -2721,6 +2730,41 @@ int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid, } EXPORT_SYMBOL_HDA(snd_hda_codec_write_cache); +/** + * snd_hda_codec_update_cache - check cache and write the cmd only when needed + * @codec: the HDA codec + * @nid: NID to send the command + * @direct: direct flag + * @verb: the verb to send + * @parm: the parameter for the verb + * + * This function works like snd_hda_codec_write_cache(), but it doesn't send + * command if the parameter is already identical with the cached value. + * If not, it sends the command and refreshes the cache. + * + * Returns 0 if successful, or a negative error code. + */ +int snd_hda_codec_update_cache(struct hda_codec *codec, hda_nid_t nid, + int direct, unsigned int verb, unsigned int parm) +{ + struct hda_cache_head *c; + u32 key; + + /* parm may contain the verb stuff for get/set amp */ + verb = verb | (parm >> 8); + parm &= 0xff; + key = build_cmd_cache_key(nid, verb); + mutex_lock(&codec->bus->cmd_mutex); + c = get_hash(&codec->cmd_cache, key); + if (c && c->val == parm) { + mutex_unlock(&codec->bus->cmd_mutex); + return 0; + } + mutex_unlock(&codec->bus->cmd_mutex); + return snd_hda_codec_write_cache(codec, nid, direct, verb, parm); +} +EXPORT_SYMBOL_HDA(snd_hda_codec_update_cache); + /** * snd_hda_codec_resume_cache - Resume the all commands from the cache * @codec: HD-audio codec diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index b75da47571e6..49e939e7e5cd 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -885,9 +885,12 @@ int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid, int direct, unsigned int verb, unsigned int parm); void snd_hda_sequence_write_cache(struct hda_codec *codec, const struct hda_verb *seq); +int snd_hda_codec_update_cache(struct hda_codec *codec, hda_nid_t nid, + int direct, unsigned int verb, unsigned int parm); void snd_hda_codec_resume_cache(struct hda_codec *codec); #else #define snd_hda_codec_write_cache snd_hda_codec_write +#define snd_hda_codec_update_cache snd_hda_codec_write #define snd_hda_sequence_write_cache snd_hda_sequence_write #endif diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 9a361c2c7336..8ae48061e8c7 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -14012,8 +14012,9 @@ static int alc269_mic2_mute_check_ps(struct hda_codec *codec, hda_nid_t nid) else pinval = 0x20; /* mic2 vref pin is used for mute LED control */ - snd_hda_codec_write(codec, 0x19, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, pinval); + snd_hda_codec_update_cache(codec, 0x19, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + pinval); } return alc_check_power_status(codec, nid); } -- cgit v1.2.3 From 489008cd58740fe3842822681d33bf87c07c3412 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 7 Apr 2010 09:06:00 +0200 Subject: ALSA: hda - Fix ALC882 DAC connections in auto mode Assign DACs properly to each output. Currently, the front output is bound to HP/speaker outputs blindly, but they should be assigned to individual DACs. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 45 +++++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 19 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index c9c31c51863f..f4ca9b2d8784 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -10191,21 +10191,21 @@ static int alc882_auto_create_input_ctls(struct hda_codec *codec, static void alc882_auto_set_output_and_unmute(struct hda_codec *codec, hda_nid_t nid, int pin_type, - int dac_idx) + hda_nid_t dac) { - /* set as output */ - struct alc_spec *spec = codec->spec; int idx; + printk("XXX set output pin %x, dac %x\n", nid, dac); + /* set as output */ alc_set_pin_output(codec, nid, pin_type); - if (dac_idx >= spec->multiout.num_dacs) - return; - if (spec->multiout.dac_nids[dac_idx] == 0x25) + + if (dac == 0x25) idx = 4; + else if (dac >= 0x02 && dac <= 0x05) + idx = dac - 2; else - idx = spec->multiout.dac_nids[dac_idx] - 2; + return; snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx); - } static void alc882_auto_init_multi_out(struct hda_codec *codec) @@ -10218,22 +10218,29 @@ static void alc882_auto_init_multi_out(struct hda_codec *codec) int pin_type = get_pin_type(spec->autocfg.line_out_type); if (nid) alc882_auto_set_output_and_unmute(codec, nid, pin_type, - i); + spec->multiout.dac_nids[i]); } } static void alc882_auto_init_hp_out(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - hda_nid_t pin; + hda_nid_t pin, dac; pin = spec->autocfg.hp_pins[0]; - if (pin) /* connect to front */ - /* use dac 0 */ - alc882_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); + if (pin) { + dac = spec->multiout.hp_nid; + if (!dac) + dac = spec->multiout.dac_nids[0]; /* to front */ + alc882_auto_set_output_and_unmute(codec, pin, PIN_HP, dac); + } pin = spec->autocfg.speaker_pins[0]; - if (pin) - alc882_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0); + if (pin) { + dac = spec->multiout.extra_out_nid[0]; + if (!dac) + dac = spec->multiout.dac_nids[0]; /* to front */ + alc882_auto_set_output_and_unmute(codec, pin, PIN_OUT, dac); + } } static void alc882_auto_init_analog_input(struct hda_codec *codec) @@ -10347,6 +10354,10 @@ static int alc882_parse_auto_config(struct hda_codec *codec) if (err < 0) return err; err = alc880_auto_create_multi_out_ctls(spec, &spec->autocfg); + if (err < 0) + return err; + err = alc880_auto_create_extra_out(spec, spec->autocfg.hp_pins[0], + "Headphone"); if (err < 0) return err; err = alc880_auto_create_extra_out(spec, @@ -10354,10 +10365,6 @@ static int alc882_parse_auto_config(struct hda_codec *codec) "Speaker"); if (err < 0) return err; - err = alc880_auto_create_extra_out(spec, spec->autocfg.hp_pins[0], - "Headphone"); - if (err < 0) - return err; err = alc882_auto_create_input_ctls(codec, &spec->autocfg); if (err < 0) return err; -- cgit v1.2.3 From 25e8d9b67b206b8bf9bf11699d1a7e67c87c1a2e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 7 Apr 2010 14:53:00 +0200 Subject: ALSA: hda - Remove left-over debug printk in patch_realtek.c Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 1 - 1 file changed, 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index f4ca9b2d8784..836103081925 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -10195,7 +10195,6 @@ static void alc882_auto_set_output_and_unmute(struct hda_codec *codec, { int idx; - printk("XXX set output pin %x, dac %x\n", nid, dac); /* set as output */ alc_set_pin_output(codec, nid, pin_type); -- cgit v1.2.3 From 24e4a1211f691fc671de44685430dbad757d8487 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 13 Apr 2010 11:22:01 +0200 Subject: ALSA: info - Use standard types for info callbacks Use loff_t, size_t and ssize_t for arguments of info callbacks to follow the standard procfs. Signed-off-by: Takashi Iwai --- sound/pci/cs4281.c | 16 ++++++++-------- sound/pci/cs46xx/cs46xx_lib.c | 7 ++++--- sound/pci/emu10k1/emuproc.c | 8 ++++---- sound/pci/mixart/mixart.c | 32 ++++++++++++++++---------------- 4 files changed, 32 insertions(+), 31 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c index 9edc65059e3e..b0bba2e86b12 100644 --- a/sound/pci/cs4281.c +++ b/sound/pci/cs4281.c @@ -1139,10 +1139,10 @@ static void snd_cs4281_proc_read(struct snd_info_entry *entry, snd_iprintf(buffer, "Spurious end IRQs : %u\n", chip->spurious_dtc_irq); } -static long snd_cs4281_BA0_read(struct snd_info_entry *entry, - void *file_private_data, - struct file *file, char __user *buf, - unsigned long count, unsigned long pos) +static ssize_t snd_cs4281_BA0_read(struct snd_info_entry *entry, + void *file_private_data, + struct file *file, char __user *buf, + size_t count, loff_t pos) { long size; struct cs4281 *chip = entry->private_data; @@ -1157,10 +1157,10 @@ static long snd_cs4281_BA0_read(struct snd_info_entry *entry, return size; } -static long snd_cs4281_BA1_read(struct snd_info_entry *entry, - void *file_private_data, - struct file *file, char __user *buf, - unsigned long count, unsigned long pos) +static ssize_t snd_cs4281_BA1_read(struct snd_info_entry *entry, + void *file_private_data, + struct file *file, char __user *buf, + size_t count, loff_t pos) { long size; struct cs4281 *chip = entry->private_data; diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c index 3f99a5e8528c..08117b142381 100644 --- a/sound/pci/cs46xx/cs46xx_lib.c +++ b/sound/pci/cs46xx/cs46xx_lib.c @@ -2657,9 +2657,10 @@ static inline void snd_cs46xx_remove_gameport(struct snd_cs46xx *chip) { } * proc interface */ -static long snd_cs46xx_io_read(struct snd_info_entry *entry, void *file_private_data, - struct file *file, char __user *buf, - unsigned long count, unsigned long pos) +static ssize_t snd_cs46xx_io_read(struct snd_info_entry *entry, + void *file_private_data, + struct file *file, char __user *buf, + size_t count, loff_t pos) { long size; struct snd_cs46xx_region *region = entry->private_data; diff --git a/sound/pci/emu10k1/emuproc.c b/sound/pci/emu10k1/emuproc.c index baa7cd508cd8..347b2415db59 100644 --- a/sound/pci/emu10k1/emuproc.c +++ b/sound/pci/emu10k1/emuproc.c @@ -341,10 +341,10 @@ static void snd_emu10k1_proc_acode_read(struct snd_info_entry *entry, #define TOTAL_SIZE_CODE (0x200*8) #define A_TOTAL_SIZE_CODE (0x400*8) -static long snd_emu10k1_fx8010_read(struct snd_info_entry *entry, - void *file_private_data, - struct file *file, char __user *buf, - unsigned long count, unsigned long pos) +static ssize_t snd_emu10k1_fx8010_read(struct snd_info_entry *entry, + void *file_private_data, + struct file *file, char __user *buf, + size_t count, loff_t pos) { long size; struct snd_emu10k1 *emu = entry->private_data; diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c index 3be8f97c8bc0..b5df78bcc25b 100644 --- a/sound/pci/mixart/mixart.c +++ b/sound/pci/mixart/mixart.c @@ -1102,11 +1102,10 @@ static int snd_mixart_free(struct mixart_mgr *mgr) /* * proc interface */ -static long long snd_mixart_BA0_llseek(struct snd_info_entry *entry, - void *private_file_data, - struct file *file, - long long offset, - int orig) +static loff_t snd_mixart_BA0_llseek(struct snd_info_entry *entry, + void *private_file_data, + struct file *file, + loff_t offset, int orig) { offset = offset & ~3; /* 4 bytes aligned */ @@ -1128,11 +1127,10 @@ static long long snd_mixart_BA0_llseek(struct snd_info_entry *entry, return file->f_pos; } -static long long snd_mixart_BA1_llseek(struct snd_info_entry *entry, - void *private_file_data, - struct file *file, - long long offset, - int orig) +static loff_t snd_mixart_BA1_llseek(struct snd_info_entry *entry, + void *private_file_data, + struct file *file, + loff_t offset, int orig) { offset = offset & ~3; /* 4 bytes aligned */ @@ -1157,9 +1155,10 @@ static long long snd_mixart_BA1_llseek(struct snd_info_entry *entry, /* mixart_BA0 proc interface for BAR 0 - read callback */ -static long snd_mixart_BA0_read(struct snd_info_entry *entry, void *file_private_data, - struct file *file, char __user *buf, - unsigned long count, unsigned long pos) +static ssize_t snd_mixart_BA0_read(struct snd_info_entry *entry, + void *file_private_data, + struct file *file, char __user *buf, + size_t count, loff_t pos) { struct mixart_mgr *mgr = entry->private_data; unsigned long maxsize; @@ -1178,9 +1177,10 @@ static long snd_mixart_BA0_read(struct snd_info_entry *entry, void *file_private /* mixart_BA1 proc interface for BAR 1 - read callback */ -static long snd_mixart_BA1_read(struct snd_info_entry *entry, void *file_private_data, - struct file *file, char __user *buf, - unsigned long count, unsigned long pos) +static ssize_t snd_mixart_BA1_read(struct snd_info_entry *entry, + void *file_private_data, + struct file *file, char __user *buf, + size_t count, loff_t pos) { struct mixart_mgr *mgr = entry->private_data; unsigned long maxsize; -- cgit v1.2.3 From d97e1b78239c7e7e441088e0b644bd3b076002e6 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 13 Apr 2010 11:33:54 +0200 Subject: ALSA: info - Check file position validity in common layer Check the validity of the file position in the common info layer before calling read or write callbacks in assumption that entry->size is set up properly to indicate the max file size. Removed the redundant checks from the callbacks as well. Signed-off-by: Takashi Iwai --- sound/pci/cs4281.c | 24 ++++++------------------ sound/pci/cs46xx/cs46xx_lib.c | 12 +++--------- sound/pci/emu10k1/emuproc.c | 43 ++++++++++++++++++++----------------------- sound/pci/mixart/mixart.c | 12 ------------ 4 files changed, 29 insertions(+), 62 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c index b0bba2e86b12..6772070ed492 100644 --- a/sound/pci/cs4281.c +++ b/sound/pci/cs4281.c @@ -1144,17 +1144,11 @@ static ssize_t snd_cs4281_BA0_read(struct snd_info_entry *entry, struct file *file, char __user *buf, size_t count, loff_t pos) { - long size; struct cs4281 *chip = entry->private_data; - size = count; - if (pos + size > CS4281_BA0_SIZE) - size = (long)CS4281_BA0_SIZE - pos; - if (size > 0) { - if (copy_to_user_fromio(buf, chip->ba0 + pos, size)) - return -EFAULT; - } - return size; + if (copy_to_user_fromio(buf, chip->ba0 + pos, count)) + return -EFAULT; + return count; } static ssize_t snd_cs4281_BA1_read(struct snd_info_entry *entry, @@ -1162,17 +1156,11 @@ static ssize_t snd_cs4281_BA1_read(struct snd_info_entry *entry, struct file *file, char __user *buf, size_t count, loff_t pos) { - long size; struct cs4281 *chip = entry->private_data; - size = count; - if (pos + size > CS4281_BA1_SIZE) - size = (long)CS4281_BA1_SIZE - pos; - if (size > 0) { - if (copy_to_user_fromio(buf, chip->ba1 + pos, size)) - return -EFAULT; - } - return size; + if (copy_to_user_fromio(buf, chip->ba1 + pos, count)) + return -EFAULT; + return count; } static struct snd_info_entry_ops snd_cs4281_proc_ops_BA0 = { diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c index 08117b142381..aad37082cb6e 100644 --- a/sound/pci/cs46xx/cs46xx_lib.c +++ b/sound/pci/cs46xx/cs46xx_lib.c @@ -2662,17 +2662,11 @@ static ssize_t snd_cs46xx_io_read(struct snd_info_entry *entry, struct file *file, char __user *buf, size_t count, loff_t pos) { - long size; struct snd_cs46xx_region *region = entry->private_data; - size = count; - if (pos + (size_t)size > region->size) - size = region->size - pos; - if (size > 0) { - if (copy_to_user_fromio(buf, region->remap_addr + pos, size)) - return -EFAULT; - } - return size; + if (copy_to_user_fromio(buf, region->remap_addr + pos, count)) + return -EFAULT; + return count; } static struct snd_info_entry_ops snd_cs46xx_proc_io_ops = { diff --git a/sound/pci/emu10k1/emuproc.c b/sound/pci/emu10k1/emuproc.c index 347b2415db59..bc38dd4d071f 100644 --- a/sound/pci/emu10k1/emuproc.c +++ b/sound/pci/emu10k1/emuproc.c @@ -346,10 +346,12 @@ static ssize_t snd_emu10k1_fx8010_read(struct snd_info_entry *entry, struct file *file, char __user *buf, size_t count, loff_t pos) { - long size; struct snd_emu10k1 *emu = entry->private_data; unsigned int offset; int tram_addr = 0; + unsigned int *tmp; + long res; + unsigned int idx; if (!strcmp(entry->name, "fx8010_tram_addr")) { offset = TANKMEMADDRREGBASE; @@ -361,30 +363,25 @@ static ssize_t snd_emu10k1_fx8010_read(struct snd_info_entry *entry, } else { offset = emu->audigy ? A_FXGPREGBASE : FXGPREGBASE; } - size = count; - if (pos + size > entry->size) - size = (long)entry->size - pos; - if (size > 0) { - unsigned int *tmp; - long res; - unsigned int idx; - if ((tmp = kmalloc(size + 8, GFP_KERNEL)) == NULL) - return -ENOMEM; - for (idx = 0; idx < ((pos & 3) + size + 3) >> 2; idx++) - if (tram_addr && emu->audigy) { - tmp[idx] = snd_emu10k1_ptr_read(emu, offset + idx + (pos >> 2), 0) >> 11; - tmp[idx] |= snd_emu10k1_ptr_read(emu, 0x100 + idx + (pos >> 2), 0) << 20; - } else - tmp[idx] = snd_emu10k1_ptr_read(emu, offset + idx + (pos >> 2), 0); - if (copy_to_user(buf, ((char *)tmp) + (pos & 3), size)) - res = -EFAULT; - else { - res = size; + + tmp = kmalloc(count + 8, GFP_KERNEL); + if (!tmp) + return -ENOMEM; + for (idx = 0; idx < ((pos & 3) + count + 3) >> 2; idx++) { + unsigned int val; + val = snd_emu10k1_ptr_read(emu, offset + idx + (pos >> 2), 0); + if (tram_addr && emu->audigy) { + val >>= 11; + val |= snd_emu10k1_ptr_read(emu, 0x100 + idx + (pos >> 2), 0) << 20; } - kfree(tmp); - return res; + tmp[idx] = val; } - return 0; + if (copy_to_user(buf, ((char *)tmp) + (pos & 3), count)) + res = -EFAULT; + else + res = count; + kfree(tmp); + return res; } static void snd_emu10k1_proc_voices_read(struct snd_info_entry *entry, diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c index b5df78bcc25b..be95e005c81d 100644 --- a/sound/pci/mixart/mixart.c +++ b/sound/pci/mixart/mixart.c @@ -1161,13 +1161,7 @@ static ssize_t snd_mixart_BA0_read(struct snd_info_entry *entry, size_t count, loff_t pos) { struct mixart_mgr *mgr = entry->private_data; - unsigned long maxsize; - if (pos >= MIXART_BA0_SIZE) - return 0; - maxsize = MIXART_BA0_SIZE - pos; - if (count > maxsize) - count = maxsize; count = count & ~3; /* make sure the read size is a multiple of 4 bytes */ if (copy_to_user_fromio(buf, MIXART_MEM(mgr, pos), count)) return -EFAULT; @@ -1183,13 +1177,7 @@ static ssize_t snd_mixart_BA1_read(struct snd_info_entry *entry, size_t count, loff_t pos) { struct mixart_mgr *mgr = entry->private_data; - unsigned long maxsize; - if (pos > MIXART_BA1_SIZE) - return 0; - maxsize = MIXART_BA1_SIZE - pos; - if (count > maxsize) - count = maxsize; count = count & ~3; /* make sure the read size is a multiple of 4 bytes */ if (copy_to_user_fromio(buf, MIXART_REG(mgr, pos), count)) return -EFAULT; -- cgit v1.2.3 From 73029e0ff18dfac8a1aab1dc188e1e150bbe3adc Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 13 Apr 2010 11:39:47 +0200 Subject: ALSA: info - Implement common llseek for binary mode The llseek implementation is identical for existing driver implementations, so let's merge to the common layer. The same code for the text proc file can be used even for the binary proc file. The driver can provide its own llseek method if needed. Then the common code will be skipped. Signed-off-by: Takashi Iwai --- sound/pci/mixart/mixart.c | 51 ----------------------------------------------- 1 file changed, 51 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c index be95e005c81d..6c3fd4d1c49d 100644 --- a/sound/pci/mixart/mixart.c +++ b/sound/pci/mixart/mixart.c @@ -1102,55 +1102,6 @@ static int snd_mixart_free(struct mixart_mgr *mgr) /* * proc interface */ -static loff_t snd_mixart_BA0_llseek(struct snd_info_entry *entry, - void *private_file_data, - struct file *file, - loff_t offset, int orig) -{ - offset = offset & ~3; /* 4 bytes aligned */ - - switch(orig) { - case SEEK_SET: - file->f_pos = offset; - break; - case SEEK_CUR: - file->f_pos += offset; - break; - case SEEK_END: /* offset is negative */ - file->f_pos = MIXART_BA0_SIZE + offset; - break; - default: - return -EINVAL; - } - if(file->f_pos > MIXART_BA0_SIZE) - file->f_pos = MIXART_BA0_SIZE; - return file->f_pos; -} - -static loff_t snd_mixart_BA1_llseek(struct snd_info_entry *entry, - void *private_file_data, - struct file *file, - loff_t offset, int orig) -{ - offset = offset & ~3; /* 4 bytes aligned */ - - switch(orig) { - case SEEK_SET: - file->f_pos = offset; - break; - case SEEK_CUR: - file->f_pos += offset; - break; - case SEEK_END: /* offset is negative */ - file->f_pos = MIXART_BA1_SIZE + offset; - break; - default: - return -EINVAL; - } - if(file->f_pos > MIXART_BA1_SIZE) - file->f_pos = MIXART_BA1_SIZE; - return file->f_pos; -} /* mixart_BA0 proc interface for BAR 0 - read callback @@ -1186,12 +1137,10 @@ static ssize_t snd_mixart_BA1_read(struct snd_info_entry *entry, static struct snd_info_entry_ops snd_mixart_proc_ops_BA0 = { .read = snd_mixart_BA0_read, - .llseek = snd_mixart_BA0_llseek }; static struct snd_info_entry_ops snd_mixart_proc_ops_BA1 = { .read = snd_mixart_BA1_read, - .llseek = snd_mixart_BA1_llseek }; -- cgit v1.2.3 From 7b2bfdbc0dee5a321b5c02febe157adebd33ab3a Mon Sep 17 00:00:00 2001 From: Jens Taprogge Date: Wed, 14 Apr 2010 23:42:04 +0200 Subject: ALSA: hda - Add initial support for Thinkpad T410s HDA codec attached please find a patch that adds support for at least the T410s HDA codec. Most likely it will also add support for the T410 and T510 based models. The patch was derived from Ideapad support. Support for the laptop's and docking-station output connectors as well as the docking-station microphone connector and the laptops internal devices has been tested. Since it has been developed without a data-sheet available, support for digital outputs and the laptop's microphone input may well be incorrect. Microphone mute functionality is not included: The microphone mute button seems to be reported through thinkpad_acpi key 0000101b. The mute button LED seems to be wired to thinkpad_acpi led number 15. Signed-off-by: Jens Taprogge Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_conexant.c | 153 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 149 insertions(+), 4 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 61682e1d09da..509a1049ff40 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -115,6 +115,7 @@ struct conexant_spec { unsigned int port_d_mode; unsigned int dell_vostro:1; unsigned int ideapad:1; + unsigned int thinkpad:1; unsigned int ext_mic_present; unsigned int recording; @@ -2031,6 +2032,9 @@ static void cxt5066_update_speaker(struct hda_codec *codec) /* Port D (HP/LO) */ pinctl = ((spec->hp_present & 2) && spec->cur_eapd) ? spec->port_d_mode : 0; + /* Mute if Port A is connected on Thinkpad */ + if (spec->thinkpad && (spec->hp_present & 1)) + pinctl = 0; snd_hda_codec_write(codec, 0x1c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl); @@ -2211,6 +2215,50 @@ static void cxt5066_ideapad_automic(struct hda_codec *codec) } } +/* toggle input of built-in digital mic and mic jack appropriately + order is: external mic -> dock mic -> interal mic */ +static void cxt5066_thinkpad_automic(struct hda_codec *codec) +{ + unsigned int ext_present, dock_present; + + static struct hda_verb ext_mic_present[] = { + {0x14, AC_VERB_SET_CONNECT_SEL, 0}, + {0x17, AC_VERB_SET_CONNECT_SEL, 1}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + {} + }; + static struct hda_verb dock_mic_present[] = { + {0x14, AC_VERB_SET_CONNECT_SEL, 0}, + {0x17, AC_VERB_SET_CONNECT_SEL, 0}, + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + {} + }; + static struct hda_verb ext_mic_absent[] = { + {0x14, AC_VERB_SET_CONNECT_SEL, 2}, + {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + {} + }; + + ext_present = snd_hda_jack_detect(codec, 0x1b); + dock_present = snd_hda_jack_detect(codec, 0x1a); + if (ext_present) { + snd_printdd("CXT5066: external microphone detected\n"); + snd_hda_sequence_write(codec, ext_mic_present); + } else if (dock_present) { + snd_printdd("CXT5066: dock microphone detected\n"); + snd_hda_sequence_write(codec, dock_mic_present); + } else { + snd_printdd("CXT5066: external microphone absent\n"); + snd_hda_sequence_write(codec, ext_mic_absent); + } +} + /* mute internal speaker if HP is plugged */ static void cxt5066_hp_automute(struct hda_codec *codec) { @@ -2223,7 +2271,8 @@ static void cxt5066_hp_automute(struct hda_codec *codec) /* Port D */ portD = snd_hda_jack_detect(codec, 0x1c); - spec->hp_present = !!(portA | portD); + spec->hp_present = !!(portA); + spec->hp_present |= portD ? 2 : 0; snd_printdd("CXT5066: hp automute portA=%x portD=%x present=%d\n", portA, portD, spec->hp_present); cxt5066_update_speaker(codec); @@ -2274,6 +2323,20 @@ static void cxt5066_ideapad_event(struct hda_codec *codec, unsigned int res) } } +/* unsolicited event for jack sensing */ +static void cxt5066_thinkpad_event(struct hda_codec *codec, unsigned int res) +{ + snd_printdd("CXT5066_thinkpad: unsol event %x (%x)\n", res, res >> 26); + switch (res >> 26) { + case CONEXANT_HP_EVENT: + cxt5066_hp_automute(codec); + break; + case CONEXANT_MIC_EVENT: + cxt5066_thinkpad_automic(codec); + break; + } +} + static const struct hda_input_mux cxt5066_analog_mic_boost = { .num_items = 5, .items = { @@ -2292,7 +2355,7 @@ static void cxt5066_set_mic_boost(struct hda_codec *codec) AC_VERB_SET_AMP_GAIN_MUTE, AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | AC_AMP_SET_OUTPUT | cxt5066_analog_mic_boost.items[spec->mic_boost].index); - if (spec->ideapad) { + if (spec->ideapad || spec->thinkpad) { /* adjust the internal mic as well...it is not through 0x17 */ snd_hda_codec_write_cache(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE, @@ -2780,6 +2843,64 @@ static struct hda_verb cxt5066_init_verbs_ideapad[] = { { } /* end */ }; +static struct hda_verb cxt5066_init_verbs_thinkpad[] = { + {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port F */ + {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port E */ + + /* Port G: internal speakers */ + {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x1f, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */ + + /* Port A: HP, Amp */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */ + + /* Port B: Mic Dock */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + + /* Port C: Mic */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + + /* Port D: HP Dock, Amp */ + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, + {0x1c, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */ + + /* DAC1 */ + {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + + /* Node 14 connections: 0x17 0x18 0x23 0x24 0x27 */ + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x50}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2) | 0x50}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + {0x14, AC_VERB_SET_CONNECT_SEL, 2}, /* default to internal mic */ + + /* Audio input selector */ + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x2}, + {0x17, AC_VERB_SET_CONNECT_SEL, 1}, /* route ext mic */ + + /* SPDIF route: PCM */ + {0x20, AC_VERB_SET_CONNECT_SEL, 0x0}, + {0x22, AC_VERB_SET_CONNECT_SEL, 0x0}, + + {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + + /* internal microphone */ + {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* enable int mic */ + + /* EAPD */ + {0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ + + /* enable unsolicited events for Port A, B, C and D */ + {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT}, + {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT}, + {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT}, + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT}, + { } /* end */ +}; + static struct hda_verb cxt5066_init_verbs_portd_lo[] = { {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, { } /* end */ @@ -2798,6 +2919,8 @@ static int cxt5066_init(struct hda_codec *codec) cxt5066_vostro_automic(codec); else if (spec->ideapad) cxt5066_ideapad_automic(codec); + else if (spec->thinkpad) + cxt5066_thinkpad_automic(codec); } cxt5066_set_mic_boost(codec); return 0; @@ -2819,20 +2942,22 @@ static int cxt5066_olpc_init(struct hda_codec *codec) } enum { - CXT5066_LAPTOP, /* Laptops w/ EAPD support */ + CXT5066_LAPTOP, /* Laptops w/ EAPD support */ CXT5066_DELL_LAPTOP, /* Dell Laptop */ CXT5066_OLPC_XO_1_5, /* OLPC XO 1.5 */ CXT5066_DELL_VOSTO, /* Dell Vostro 1015i */ CXT5066_IDEAPAD, /* Lenovo IdeaPad U150 */ + CXT5066_THINKPAD, /* Lenovo ThinkPad T410s, others? */ CXT5066_MODELS }; static const char *cxt5066_models[CXT5066_MODELS] = { - [CXT5066_LAPTOP] = "laptop", + [CXT5066_LAPTOP] = "laptop", [CXT5066_DELL_LAPTOP] = "dell-laptop", [CXT5066_OLPC_XO_1_5] = "olpc-xo-1_5", [CXT5066_DELL_VOSTO] = "dell-vostro", [CXT5066_IDEAPAD] = "ideapad", + [CXT5066_THINKPAD] = "thinkpad", }; static struct snd_pci_quirk cxt5066_cfg_tbl[] = { @@ -2843,6 +2968,7 @@ static struct snd_pci_quirk cxt5066_cfg_tbl[] = { SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT5066_OLPC_XO_1_5), SND_PCI_QUIRK(0x1028, 0x0402, "Dell Vostro", CXT5066_DELL_VOSTO), SND_PCI_QUIRK(0x17aa, 0x3a0d, "ideapad", CXT5066_IDEAPAD), + SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo Thinkpad", CXT5066_THINKPAD), {} }; @@ -2947,6 +3073,22 @@ static int patch_cxt5066(struct hda_codec *codec) /* no S/PDIF out */ spec->multiout.dig_out_nid = 0; + /* input source automatically selected */ + spec->input_mux = NULL; + break; + case CXT5066_THINKPAD: + codec->patch_ops.init = cxt5066_init; + codec->patch_ops.unsol_event = cxt5066_thinkpad_event; + spec->mixers[spec->num_mixers++] = cxt5066_mixer_master; + spec->mixers[spec->num_mixers++] = cxt5066_mixers; + spec->init_verbs[0] = cxt5066_init_verbs_thinkpad; + spec->thinkpad = 1; + spec->port_d_mode = PIN_OUT; + spec->mic_boost = 2; /* default 20dB gain */ + + /* no S/PDIF out */ + spec->multiout.dig_out_nid = 0; + /* input source automatically selected */ spec->input_mux = NULL; break; @@ -2969,6 +3111,8 @@ static struct hda_codec_preset snd_hda_preset_conexant[] = { .patch = patch_cxt5066 }, { .id = 0x14f15067, .name = "CX20583 (Pebble HSF)", .patch = patch_cxt5066 }, + { .id = 0x14f15069, .name = "CX20585", + .patch = patch_cxt5066 }, {} /* terminator */ }; @@ -2977,6 +3121,7 @@ MODULE_ALIAS("snd-hda-codec-id:14f15047"); MODULE_ALIAS("snd-hda-codec-id:14f15051"); MODULE_ALIAS("snd-hda-codec-id:14f15066"); MODULE_ALIAS("snd-hda-codec-id:14f15067"); +MODULE_ALIAS("snd-hda-codec-id:14f15069"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Conexant HD-audio codec"); -- cgit v1.2.3 From 719f82d3987aad4cc9f46d19c35f362672545cad Mon Sep 17 00:00:00 2001 From: Eliot Blennerhassett Date: Wed, 21 Apr 2010 18:17:39 +0200 Subject: ALSA: Add support of AudioScience ASI boards Added the support of AudioScience ASI boards. The driver has been tested for years on alsa-driver external tree, now finally got merged to the kernel. Signed-off-by: Eliot Blennerhassett Signed-off-by: Takashi Iwai --- sound/pci/Kconfig | 12 + sound/pci/Makefile | 1 + sound/pci/asihpi/Makefile | 5 + sound/pci/asihpi/asihpi.c | 3002 ++++++++++++++++++++++++++++++ sound/pci/asihpi/hpi.h | 2001 ++++++++++++++++++++ sound/pci/asihpi/hpi6000.c | 1841 +++++++++++++++++++ sound/pci/asihpi/hpi6000.h | 70 + sound/pci/asihpi/hpi6205.c | 2332 +++++++++++++++++++++++ sound/pci/asihpi/hpi6205.h | 93 + sound/pci/asihpi/hpi_internal.h | 1641 +++++++++++++++++ sound/pci/asihpi/hpicmn.c | 643 +++++++ sound/pci/asihpi/hpicmn.h | 64 + sound/pci/asihpi/hpidebug.c | 225 +++ sound/pci/asihpi/hpidebug.h | 385 ++++ sound/pci/asihpi/hpidspcd.c | 172 ++ sound/pci/asihpi/hpidspcd.h | 104 ++ sound/pci/asihpi/hpifunc.c | 3864 +++++++++++++++++++++++++++++++++++++++ sound/pci/asihpi/hpimsginit.c | 130 ++ sound/pci/asihpi/hpimsginit.h | 40 + sound/pci/asihpi/hpimsgx.c | 907 +++++++++ sound/pci/asihpi/hpimsgx.h | 36 + sound/pci/asihpi/hpioctl.c | 484 +++++ sound/pci/asihpi/hpioctl.h | 38 + sound/pci/asihpi/hpios.c | 114 ++ sound/pci/asihpi/hpios.h | 178 ++ sound/pci/asihpi/hpipcida.h | 37 + 26 files changed, 18419 insertions(+) create mode 100644 sound/pci/asihpi/Makefile create mode 100644 sound/pci/asihpi/asihpi.c create mode 100644 sound/pci/asihpi/hpi.h create mode 100644 sound/pci/asihpi/hpi6000.c create mode 100644 sound/pci/asihpi/hpi6000.h create mode 100644 sound/pci/asihpi/hpi6205.c create mode 100644 sound/pci/asihpi/hpi6205.h create mode 100644 sound/pci/asihpi/hpi_internal.h create mode 100644 sound/pci/asihpi/hpicmn.c create mode 100644 sound/pci/asihpi/hpicmn.h create mode 100644 sound/pci/asihpi/hpidebug.c create mode 100644 sound/pci/asihpi/hpidebug.h create mode 100644 sound/pci/asihpi/hpidspcd.c create mode 100644 sound/pci/asihpi/hpidspcd.h create mode 100644 sound/pci/asihpi/hpifunc.c create mode 100644 sound/pci/asihpi/hpimsginit.c create mode 100644 sound/pci/asihpi/hpimsginit.h create mode 100644 sound/pci/asihpi/hpimsgx.c create mode 100644 sound/pci/asihpi/hpimsgx.h create mode 100644 sound/pci/asihpi/hpioctl.c create mode 100644 sound/pci/asihpi/hpioctl.h create mode 100644 sound/pci/asihpi/hpios.c create mode 100644 sound/pci/asihpi/hpios.h create mode 100644 sound/pci/asihpi/hpipcida.h (limited to 'sound/pci') diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index 1298c68d6bf0..517ae65ffc8e 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -58,6 +58,18 @@ config SND_ALI5451 To compile this driver as a module, choose M here: the module will be called snd-ali5451. +config SND_ASIHPI + tristate "AudioScience ASIxxxx" + depends on X86 + select FW_LOADER + select SND_PCM + select SND_HWDEP + help + Say Y here to include support for AudioScience ASI sound cards. + + To compile this driver as a module, choose M here: the module + will be called snd-asihpi. + config SND_ATIIXP tristate "ATI IXP AC97 Controller" select SND_AC97_CODEC diff --git a/sound/pci/Makefile b/sound/pci/Makefile index ecfc609d2b9f..9cf4348ec137 100644 --- a/sound/pci/Makefile +++ b/sound/pci/Makefile @@ -57,6 +57,7 @@ obj-$(CONFIG_SND_VIA82XX_MODEM) += snd-via82xx-modem.o obj-$(CONFIG_SND) += \ ac97/ \ ali5451/ \ + asihpi/ \ au88x0/ \ aw2/ \ ctxfi/ \ diff --git a/sound/pci/asihpi/Makefile b/sound/pci/asihpi/Makefile new file mode 100644 index 000000000000..391830a4556c --- /dev/null +++ b/sound/pci/asihpi/Makefile @@ -0,0 +1,5 @@ +snd-asihpi-objs := asihpi.o hpioctl.o hpimsginit.o\ + hpicmn.o hpifunc.o hpidebug.o hpidspcd.o\ + hpios.o hpi6000.o hpi6205.o hpimsgx.o + +obj-$(CONFIG_SND_ASIHPI) += snd-asihpi.o diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c new file mode 100644 index 000000000000..f74c7372b3d1 --- /dev/null +++ b/sound/pci/asihpi/asihpi.c @@ -0,0 +1,3002 @@ +/* + * Asihpi soundcard + * Copyright (c) by AudioScience Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * The following is not a condition of use, merely a request: + * If you modify this program, particularly if you fix errors, AudioScience Inc + * would appreciate it if you grant us the right to use those modifications + * for any purpose including commercial applications. + */ +/* >0: print Hw params, timer vars. >1: print stream write/copy sizes */ +#define REALLY_VERBOSE_LOGGING 0 + +#if REALLY_VERBOSE_LOGGING +#define VPRINTK1 snd_printd +#else +#define VPRINTK1(...) +#endif + +#if REALLY_VERBOSE_LOGGING > 1 +#define VPRINTK2 snd_printd +#else +#define VPRINTK2(...) +#endif + +#ifndef ASI_STYLE_NAMES +/* not sure how ALSA style name should look */ +#define ASI_STYLE_NAMES 1 +#endif + +#include "hpi_internal.h" +#include "hpimsginit.h" +#include "hpioctl.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("AudioScience inc. "); +MODULE_DESCRIPTION("AudioScience ALSA ASI5000 ASI6000 ASI87xx ASI89xx"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; +static int enable_hpi_hwdep = 1; + +module_param_array(index, int, NULL, S_IRUGO); +MODULE_PARM_DESC(index, "ALSA index value for AudioScience soundcard."); + +module_param_array(id, charp, NULL, S_IRUGO); +MODULE_PARM_DESC(id, "ALSA ID string for AudioScience soundcard."); + +module_param_array(enable, bool, NULL, S_IRUGO); +MODULE_PARM_DESC(enable, "ALSA enable AudioScience soundcard."); + +module_param(enable_hpi_hwdep, bool, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(enable_hpi_hwdep, + "ALSA enable HPI hwdep for AudioScience soundcard "); + +/* identify driver */ +#ifdef KERNEL_ALSA_BUILD +static char *build_info = "built using headers from kernel source"; +module_param(build_info, charp, S_IRUGO); +MODULE_PARM_DESC(build_info, "built using headers from kernel source"); +#else +static char *build_info = "built within ALSA source"; +module_param(build_info, charp, S_IRUGO); +MODULE_PARM_DESC(build_info, "built within ALSA source"); +#endif + +/* set to 1 to dump every control from adapter to log */ +static const int mixer_dump; + +#define DEFAULT_SAMPLERATE 44100 +static int adapter_fs = DEFAULT_SAMPLERATE; + +static struct hpi_hsubsys *ss; /* handle to HPI audio subsystem */ + +/* defaults */ +#define PERIODS_MIN 2 +#define PERIOD_BYTES_MIN 2304 +#define BUFFER_BYTES_MAX (512 * 1024) + +/*#define TIMER_MILLISECONDS 20 +#define FORCE_TIMER_JIFFIES ((TIMER_MILLISECONDS * HZ + 999)/1000) +*/ + +#define MAX_CLOCKSOURCES (HPI_SAMPLECLOCK_SOURCE_LAST + 1 + 7) + +struct clk_source { + int source; + int index; + char *name; +}; + +struct clk_cache { + int count; + int has_local; + struct clk_source s[MAX_CLOCKSOURCES]; +}; + +/* Per card data */ +struct snd_card_asihpi { + struct snd_card *card; + struct pci_dev *pci; + u16 adapter_index; + u32 serial_number; + u16 type; + u16 version; + u16 num_outstreams; + u16 num_instreams; + + u32 h_mixer; + struct clk_cache cc; + + u16 support_mmap; + u16 support_grouping; + u16 support_mrx; + u16 update_interval_frames; + u16 in_max_chans; + u16 out_max_chans; +}; + +/* Per stream data */ +struct snd_card_asihpi_pcm { + struct timer_list timer; + unsigned int respawn_timer; + unsigned int hpi_buffer_attached; + unsigned int pcm_size; + unsigned int pcm_count; + unsigned int bytes_per_sec; + unsigned int pcm_irq_pos; /* IRQ position */ + unsigned int pcm_buf_pos; /* position in buffer */ + struct snd_pcm_substream *substream; + u32 h_stream; + struct hpi_format format; +}; + +/* universal stream verbs work with out or in stream handles */ + +/* Functions to allow driver to give a buffer to HPI for busmastering */ + +static u16 hpi_stream_host_buffer_attach( + struct hpi_hsubsys *hS, + u32 h_stream, /* handle to outstream. */ + u32 size_in_bytes, /* size in bytes of bus mastering buffer */ + u32 pci_address +) +{ + struct hpi_message hm; + struct hpi_response hr; + unsigned int obj = hpi_handle_object(h_stream); + + if (!h_stream) + return HPI_ERROR_INVALID_OBJ; + hpi_init_message_response(&hm, &hr, obj, + obj == HPI_OBJ_OSTREAM ? + HPI_OSTREAM_HOSTBUFFER_ALLOC : + HPI_ISTREAM_HOSTBUFFER_ALLOC); + + hpi_handle_to_indexes(h_stream, &hm.adapter_index, + &hm.obj_index); + + hm.u.d.u.buffer.buffer_size = size_in_bytes; + hm.u.d.u.buffer.pci_address = pci_address; + hm.u.d.u.buffer.command = HPI_BUFFER_CMD_INTERNAL_GRANTADAPTER; + hpi_send_recv(&hm, &hr); + return hr.error; +} + +static u16 hpi_stream_host_buffer_detach( + struct hpi_hsubsys *hS, + u32 h_stream +) +{ + struct hpi_message hm; + struct hpi_response hr; + unsigned int obj = hpi_handle_object(h_stream); + + if (!h_stream) + return HPI_ERROR_INVALID_OBJ; + + hpi_init_message_response(&hm, &hr, obj, + obj == HPI_OBJ_OSTREAM ? + HPI_OSTREAM_HOSTBUFFER_FREE : + HPI_ISTREAM_HOSTBUFFER_FREE); + + hpi_handle_to_indexes(h_stream, &hm.adapter_index, + &hm.obj_index); + hm.u.d.u.buffer.command = HPI_BUFFER_CMD_INTERNAL_REVOKEADAPTER; + hpi_send_recv(&hm, &hr); + return hr.error; +} + +static inline u16 hpi_stream_start(struct hpi_hsubsys *hS, u32 h_stream) +{ + if (hpi_handle_object(h_stream) == HPI_OBJ_OSTREAM) + return hpi_outstream_start(hS, h_stream); + else + return hpi_instream_start(hS, h_stream); +} + +static inline u16 hpi_stream_stop(struct hpi_hsubsys *hS, u32 h_stream) +{ + if (hpi_handle_object(h_stream) == HPI_OBJ_OSTREAM) + return hpi_outstream_stop(hS, h_stream); + else + return hpi_instream_stop(hS, h_stream); +} + +static inline u16 hpi_stream_get_info_ex( + struct hpi_hsubsys *hS, + u32 h_stream, + u16 *pw_state, + u32 *pbuffer_size, + u32 *pdata_in_buffer, + u32 *psample_count, + u32 *pauxiliary_data +) +{ + if (hpi_handle_object(h_stream) == HPI_OBJ_OSTREAM) + return hpi_outstream_get_info_ex(hS, h_stream, pw_state, + pbuffer_size, pdata_in_buffer, + psample_count, pauxiliary_data); + else + return hpi_instream_get_info_ex(hS, h_stream, pw_state, + pbuffer_size, pdata_in_buffer, + psample_count, pauxiliary_data); +} + +static inline u16 hpi_stream_group_add(struct hpi_hsubsys *hS, + u32 h_master, + u32 h_stream) +{ + if (hpi_handle_object(h_master) == HPI_OBJ_OSTREAM) + return hpi_outstream_group_add(hS, h_master, h_stream); + else + return hpi_instream_group_add(hS, h_master, h_stream); +} + +static inline u16 hpi_stream_group_reset(struct hpi_hsubsys *hS, + u32 h_stream) +{ + if (hpi_handle_object(h_stream) == HPI_OBJ_OSTREAM) + return hpi_outstream_group_reset(hS, h_stream); + else + return hpi_instream_group_reset(hS, h_stream); +} + +static inline u16 hpi_stream_group_get_map(struct hpi_hsubsys *hS, + u32 h_stream, u32 *mo, u32 *mi) +{ + if (hpi_handle_object(h_stream) == HPI_OBJ_OSTREAM) + return hpi_outstream_group_get_map(hS, h_stream, mo, mi); + else + return hpi_instream_group_get_map(hS, h_stream, mo, mi); +} + +static u16 handle_error(u16 err, int line, char *filename) +{ + if (err) + printk(KERN_WARNING + "in file %s, line %d: HPI error %d\n", + filename, line, err); + return err; +} + +#define hpi_handle_error(x) handle_error(x, __LINE__, __FILE__) + +/***************************** GENERAL PCM ****************/ +#if REALLY_VERBOSE_LOGGING +static void print_hwparams(struct snd_pcm_hw_params *p) +{ + snd_printd("HWPARAMS \n"); + snd_printd("samplerate %d \n", params_rate(p)); + snd_printd("channels %d \n", params_channels(p)); + snd_printd("format %d \n", params_format(p)); + snd_printd("subformat %d \n", params_subformat(p)); + snd_printd("buffer bytes %d \n", params_buffer_bytes(p)); + snd_printd("period bytes %d \n", params_period_bytes(p)); + snd_printd("access %d \n", params_access(p)); + snd_printd("period_size %d \n", params_period_size(p)); + snd_printd("periods %d \n", params_periods(p)); + snd_printd("buffer_size %d \n", params_buffer_size(p)); +} +#else +#define print_hwparams(x) +#endif + +static snd_pcm_format_t hpi_to_alsa_formats[] = { + -1, /* INVALID */ + SNDRV_PCM_FORMAT_U8, /* HPI_FORMAT_PCM8_UNSIGNED 1 */ + SNDRV_PCM_FORMAT_S16, /* HPI_FORMAT_PCM16_SIGNED 2 */ + -1, /* HPI_FORMAT_MPEG_L1 3 */ + SNDRV_PCM_FORMAT_MPEG, /* HPI_FORMAT_MPEG_L2 4 */ + SNDRV_PCM_FORMAT_MPEG, /* HPI_FORMAT_MPEG_L3 5 */ + -1, /* HPI_FORMAT_DOLBY_AC2 6 */ + -1, /* HPI_FORMAT_DOLBY_AC3 7 */ + SNDRV_PCM_FORMAT_S16_BE,/* HPI_FORMAT_PCM16_BIGENDIAN 8 */ + -1, /* HPI_FORMAT_AA_TAGIT1_HITS 9 */ + -1, /* HPI_FORMAT_AA_TAGIT1_INSERTS 10 */ + SNDRV_PCM_FORMAT_S32, /* HPI_FORMAT_PCM32_SIGNED 11 */ + -1, /* HPI_FORMAT_RAW_BITSTREAM 12 */ + -1, /* HPI_FORMAT_AA_TAGIT1_HITS_EX1 13 */ + SNDRV_PCM_FORMAT_FLOAT, /* HPI_FORMAT_PCM32_FLOAT 14 */ +#if 1 + /* ALSA can't handle 3 byte sample size together with power-of-2 + * constraint on buffer_bytes, so disable this format + */ + -1 +#else + /* SNDRV_PCM_FORMAT_S24_3LE */ /* { HPI_FORMAT_PCM24_SIGNED 15 */ +#endif +}; + + +static int snd_card_asihpi_format_alsa2hpi(snd_pcm_format_t alsa_format, + u16 *hpi_format) +{ + u16 format; + + for (format = HPI_FORMAT_PCM8_UNSIGNED; + format <= HPI_FORMAT_PCM24_SIGNED; format++) { + if (hpi_to_alsa_formats[format] == alsa_format) { + *hpi_format = format; + return 0; + } + } + + snd_printd(KERN_WARNING "failed match for alsa format %d\n", + alsa_format); + *hpi_format = 0; + return -EINVAL; +} + +static void snd_card_asihpi_pcm_samplerates(struct snd_card_asihpi *asihpi, + struct snd_pcm_hardware *pcmhw) +{ + u16 err; + u32 h_control; + u32 sample_rate; + int idx; + unsigned int rate_min = 200000; + unsigned int rate_max = 0; + unsigned int rates = 0; + + if (asihpi->support_mrx) { + rates |= SNDRV_PCM_RATE_CONTINUOUS; + rates |= SNDRV_PCM_RATE_8000_96000; + rate_min = 8000; + rate_max = 100000; + } else { + /* on cards without SRC, + valid rates are determined by sampleclock */ + err = hpi_mixer_get_control(ss, asihpi->h_mixer, + HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0, + HPI_CONTROL_SAMPLECLOCK, &h_control); + if (err) { + snd_printk(KERN_ERR + "no local sampleclock, err %d\n", err); + } + + for (idx = 0; idx < 100; idx++) { + if (hpi_sample_clock_query_local_rate(ss, + h_control, idx, &sample_rate)) { + if (!idx) + snd_printk(KERN_ERR + "local rate query failed\n"); + + break; + } + + rate_min = min(rate_min, sample_rate); + rate_max = max(rate_max, sample_rate); + + switch (sample_rate) { + case 5512: + rates |= SNDRV_PCM_RATE_5512; + break; + case 8000: + rates |= SNDRV_PCM_RATE_8000; + break; + case 11025: + rates |= SNDRV_PCM_RATE_11025; + break; + case 16000: + rates |= SNDRV_PCM_RATE_16000; + break; + case 22050: + rates |= SNDRV_PCM_RATE_22050; + break; + case 32000: + rates |= SNDRV_PCM_RATE_32000; + break; + case 44100: + rates |= SNDRV_PCM_RATE_44100; + break; + case 48000: + rates |= SNDRV_PCM_RATE_48000; + break; + case 64000: + rates |= SNDRV_PCM_RATE_64000; + break; + case 88200: + rates |= SNDRV_PCM_RATE_88200; + break; + case 96000: + rates |= SNDRV_PCM_RATE_96000; + break; + case 176400: + rates |= SNDRV_PCM_RATE_176400; + break; + case 192000: + rates |= SNDRV_PCM_RATE_192000; + break; + default: /* some other rate */ + rates |= SNDRV_PCM_RATE_KNOT; + } + } + } + + /* printk(KERN_INFO "Supported rates %X %d %d\n", + rates, rate_min, rate_max); */ + pcmhw->rates = rates; + pcmhw->rate_min = rate_min; + pcmhw->rate_max = rate_max; +} + +static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_card_asihpi_pcm *dpcm = runtime->private_data; + struct snd_card_asihpi *card = snd_pcm_substream_chip(substream); + int err; + u16 format; + unsigned int bytes_per_sec; + + print_hwparams(params); + err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); + if (err < 0) + return err; + err = snd_card_asihpi_format_alsa2hpi(params_format(params), &format); + if (err) + return err; + + VPRINTK1(KERN_INFO "format %d, %d chans, %d_hz\n", + format, params_channels(params), + params_rate(params)); + + hpi_handle_error(hpi_format_create(&dpcm->format, + params_channels(params), + format, params_rate(params), 0, 0)); + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + if (hpi_instream_reset(ss, dpcm->h_stream) != 0) + return -EINVAL; + + if (hpi_instream_set_format(ss, + dpcm->h_stream, &dpcm->format) != 0) + return -EINVAL; + } + + dpcm->hpi_buffer_attached = 0; + if (card->support_mmap) { + + err = hpi_stream_host_buffer_attach(ss, dpcm->h_stream, + params_buffer_bytes(params), runtime->dma_addr); + if (err == 0) { + snd_printd(KERN_INFO + "stream_host_buffer_attach succeeded %u %lu\n", + params_buffer_bytes(params), + (unsigned long)runtime->dma_addr); + } else { + snd_printd(KERN_INFO + "stream_host_buffer_attach error %d\n", + err); + return -ENOMEM; + } + + err = hpi_stream_get_info_ex(ss, dpcm->h_stream, NULL, + &dpcm->hpi_buffer_attached, + NULL, NULL, NULL); + + snd_printd(KERN_INFO "stream_host_buffer_attach status 0x%x\n", + dpcm->hpi_buffer_attached); + } + bytes_per_sec = params_rate(params) * params_channels(params); + bytes_per_sec *= snd_pcm_format_width(params_format(params)); + bytes_per_sec /= 8; + if (bytes_per_sec <= 0) + return -EINVAL; + + dpcm->bytes_per_sec = bytes_per_sec; + dpcm->pcm_size = params_buffer_bytes(params); + dpcm->pcm_count = params_period_bytes(params); + snd_printd(KERN_INFO "pcm_size=%d, pcm_count=%d, bps=%d\n", + dpcm->pcm_size, dpcm->pcm_count, bytes_per_sec); + + dpcm->pcm_irq_pos = 0; + dpcm->pcm_buf_pos = 0; + return 0; +} + +static void snd_card_asihpi_pcm_timer_start(struct snd_pcm_substream * + substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_card_asihpi_pcm *dpcm = runtime->private_data; + int expiry; + + expiry = (dpcm->pcm_count * HZ / dpcm->bytes_per_sec); + /* wait longer the first time, for samples to propagate */ + expiry = max(expiry, 20); + dpcm->timer.expires = jiffies + expiry; + dpcm->respawn_timer = 1; + add_timer(&dpcm->timer); +} + +static void snd_card_asihpi_pcm_timer_stop(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_card_asihpi_pcm *dpcm = runtime->private_data; + + dpcm->respawn_timer = 0; + del_timer(&dpcm->timer); +} + +static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream, + int cmd) +{ + struct snd_card_asihpi_pcm *dpcm = substream->runtime->private_data; + struct snd_card_asihpi *card = snd_pcm_substream_chip(substream); + struct snd_pcm_substream *s; + u16 e; + + snd_printd("trigger %dstream %d\n", + substream->stream, substream->number); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + snd_pcm_group_for_each_entry(s, substream) { + struct snd_card_asihpi_pcm *ds; + ds = s->runtime->private_data; + + if (snd_pcm_substream_chip(s) != card) + continue; + + if ((s->stream == SNDRV_PCM_STREAM_PLAYBACK) && + (card->support_mmap)) { + /* How do I know how much valid data is present + * in buffer? Just guessing 2 periods, but if + * buffer is bigger it may contain even more + * data?? + */ + unsigned int preload = ds->pcm_count * 2; + VPRINTK2("preload %d\n", preload); + hpi_handle_error(hpi_outstream_write_buf( + ss, ds->h_stream, + &s->runtime->dma_area[0], + preload, + &ds->format)); + } + + if (card->support_grouping) { + VPRINTK1("\t_group %dstream %d\n", s->stream, + s->number); + e = hpi_stream_group_add(ss, + dpcm->h_stream, + ds->h_stream); + if (!e) { + snd_pcm_trigger_done(s, substream); + } else { + hpi_handle_error(e); + break; + } + } else + break; + } + snd_printd("start\n"); + /* start the master stream */ + snd_card_asihpi_pcm_timer_start(substream); + hpi_handle_error(hpi_stream_start(ss, dpcm->h_stream)); + break; + + case SNDRV_PCM_TRIGGER_STOP: + snd_card_asihpi_pcm_timer_stop(substream); + snd_pcm_group_for_each_entry(s, substream) { + if (snd_pcm_substream_chip(s) != card) + continue; + + /*? workaround linked streams don't + transition to SETUP 20070706*/ + s->runtime->status->state = SNDRV_PCM_STATE_SETUP; + + if (card->support_grouping) { + VPRINTK1("\t_group %dstream %d\n", s->stream, + s->number); + snd_pcm_trigger_done(s, substream); + } else + break; + } + snd_printd("stop\n"); + + /* _prepare and _hwparams reset the stream */ + hpi_handle_error(hpi_stream_stop(ss, dpcm->h_stream)); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + hpi_handle_error( + hpi_outstream_reset(ss, dpcm->h_stream)); + + if (card->support_grouping) + hpi_handle_error(hpi_stream_group_reset(ss, + dpcm->h_stream)); + break; + + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + snd_printd("pause release\n"); + hpi_handle_error(hpi_stream_start(ss, dpcm->h_stream)); + snd_card_asihpi_pcm_timer_start(substream); + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + snd_printd("pause\n"); + snd_card_asihpi_pcm_timer_stop(substream); + hpi_handle_error(hpi_stream_stop(ss, dpcm->h_stream)); + break; + default: + snd_printd("\tINVALID\n"); + return -EINVAL; + } + + return 0; +} + +static int +snd_card_asihpi_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_card_asihpi_pcm *dpcm = runtime->private_data; + if (dpcm->hpi_buffer_attached) + hpi_stream_host_buffer_detach(ss, dpcm->h_stream); + + snd_pcm_lib_free_pages(substream); + return 0; +} + +static void snd_card_asihpi_runtime_free(struct snd_pcm_runtime *runtime) +{ + struct snd_card_asihpi_pcm *dpcm = runtime->private_data; + kfree(dpcm); +} + +/*algorithm outline + Without linking degenerates to getting single stream pos etc + Without mmap 2nd loop degenerates to snd_pcm_period_elapsed +*/ +/* +buf_pos=get_buf_pos(s); +for_each_linked_stream(s) { + buf_pos=get_buf_pos(s); + min_buf_pos = modulo_min(min_buf_pos, buf_pos, pcm_size) + new_data = min(new_data, calc_new_data(buf_pos,irq_pos) +} +timer.expires = jiffies + predict_next_period_ready(min_buf_pos); +for_each_linked_stream(s) { + s->buf_pos = min_buf_pos; + if (new_data > pcm_count) { + if (mmap) { + irq_pos = (irq_pos + pcm_count) % pcm_size; + if (playback) { + write(pcm_count); + } else { + read(pcm_count); + } + } + snd_pcm_period_elapsed(s); + } +} +*/ + +/** Minimum of 2 modulo values. Works correctly when the difference between +* the values is less than half the modulus +*/ +static inline unsigned int modulo_min(unsigned int a, unsigned int b, + unsigned long int modulus) +{ + unsigned int result; + if (((a-b) % modulus) < (modulus/2)) + result = b; + else + result = a; + + return result; +} + +/** Timer function, equivalent to interrupt service routine for cards +*/ +static void snd_card_asihpi_timer_function(unsigned long data) +{ + struct snd_card_asihpi_pcm *dpcm = (struct snd_card_asihpi_pcm *)data; + struct snd_card_asihpi *card = snd_pcm_substream_chip(dpcm->substream); + struct snd_pcm_runtime *runtime; + struct snd_pcm_substream *s; + unsigned int newdata = 0; + unsigned int buf_pos, min_buf_pos = 0; + unsigned int remdata, xfercount, next_jiffies; + int first = 1; + u16 state; + u32 buffer_size, data_avail, samples_played, aux; + + /* find minimum newdata and buffer pos in group */ + snd_pcm_group_for_each_entry(s, dpcm->substream) { + struct snd_card_asihpi_pcm *ds = s->runtime->private_data; + runtime = s->runtime; + + if (snd_pcm_substream_chip(s) != card) + continue; + + hpi_handle_error(hpi_stream_get_info_ex(ss, + ds->h_stream, &state, + &buffer_size, &data_avail, + &samples_played, &aux)); + + /* number of bytes in on-card buffer */ + runtime->delay = aux; + + if (state == HPI_STATE_DRAINED) { + snd_printd(KERN_WARNING "outstream %d drained\n", + s->number); + snd_pcm_stop(s, SNDRV_PCM_STATE_XRUN); + return; + } + + if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) { + buf_pos = frames_to_bytes(runtime, samples_played); + } else { + buf_pos = data_avail + ds->pcm_irq_pos; + } + + if (first) { + /* can't statically init min when wrap is involved */ + min_buf_pos = buf_pos; + newdata = (buf_pos - ds->pcm_irq_pos) % ds->pcm_size; + first = 0; + } else { + min_buf_pos = + modulo_min(min_buf_pos, buf_pos, UINT_MAX+1L); + newdata = min( + (buf_pos - ds->pcm_irq_pos) % ds->pcm_size, + newdata); + } + + VPRINTK1("PB timer hw_ptr x%04lX, appl_ptr x%04lX\n", + (unsigned long)frames_to_bytes(runtime, + runtime->status->hw_ptr), + (unsigned long)frames_to_bytes(runtime, + runtime->control->appl_ptr)); + VPRINTK1("%d S=%d, irq=%04X, pos=x%04X, left=x%04X," + " aux=x%04X space=x%04X\n", s->number, + state, ds->pcm_irq_pos, buf_pos, (int)data_avail, + (int)aux, buffer_size-data_avail); + } + + remdata = newdata % dpcm->pcm_count; + xfercount = newdata - remdata; /* a multiple of pcm_count */ + next_jiffies = ((dpcm->pcm_count-remdata) * HZ / dpcm->bytes_per_sec)+1; + next_jiffies = max(next_jiffies, 2U * HZ / 1000U); + dpcm->timer.expires = jiffies + next_jiffies; + VPRINTK1("jif %d buf pos x%04X newdata x%04X xc x%04X\n", + next_jiffies, min_buf_pos, newdata, xfercount); + + snd_pcm_group_for_each_entry(s, dpcm->substream) { + struct snd_card_asihpi_pcm *ds = s->runtime->private_data; + ds->pcm_buf_pos = min_buf_pos; + + if (xfercount) { + if (card->support_mmap) { + ds->pcm_irq_pos = ds->pcm_irq_pos + xfercount; + if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) { + VPRINTK2("write OS%d x%04x\n", + s->number, + ds->pcm_count); + hpi_handle_error( + hpi_outstream_write_buf( + ss, ds->h_stream, + &s->runtime-> + dma_area[0], + xfercount, + &ds->format)); + } else { + VPRINTK2("read IS%d x%04x\n", + s->number, + dpcm->pcm_count); + hpi_handle_error( + hpi_instream_read_buf( + ss, ds->h_stream, + NULL, xfercount)); + } + } /* else R/W will be handled by read/write callbacks */ + snd_pcm_period_elapsed(s); + } + } + + if (dpcm->respawn_timer) + add_timer(&dpcm->timer); +} + +/***************************** PLAYBACK OPS ****************/ +static int snd_card_asihpi_playback_ioctl(struct snd_pcm_substream *substream, + unsigned int cmd, void *arg) +{ + /* snd_printd(KERN_INFO "Playback ioctl %d\n", cmd); */ + return snd_pcm_lib_ioctl(substream, cmd, arg); +} + +static int snd_card_asihpi_playback_prepare(struct snd_pcm_substream * + substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_card_asihpi_pcm *dpcm = runtime->private_data; + + snd_printd(KERN_INFO "playback prepare %d\n", substream->number); + + hpi_handle_error(hpi_outstream_reset(ss, dpcm->h_stream)); + dpcm->pcm_irq_pos = 0; + dpcm->pcm_buf_pos = 0; + + return 0; +} + +static snd_pcm_uframes_t +snd_card_asihpi_playback_pointer(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_card_asihpi_pcm *dpcm = runtime->private_data; + snd_pcm_uframes_t ptr; + + u32 samples_played; + u16 err; + + if (!snd_pcm_stream_linked(substream)) { + /* NOTE, can use samples played for playback position here and + * in timer fn because it LAGS the actual read pointer, and is a + * better representation of actual playout position + */ + err = hpi_outstream_get_info_ex(ss, dpcm->h_stream, NULL, + NULL, NULL, + &samples_played, NULL); + hpi_handle_error(err); + + dpcm->pcm_buf_pos = frames_to_bytes(runtime, samples_played); + } + /* else must return most conservative value found in timer func + * by looping over all streams + */ + + ptr = bytes_to_frames(runtime, dpcm->pcm_buf_pos % dpcm->pcm_size); + VPRINTK2("playback_pointer=%04ld\n", (unsigned long)ptr); + return ptr; +} + +static void snd_card_asihpi_playback_format(struct snd_card_asihpi *asihpi, + u32 h_stream, + struct snd_pcm_hardware *pcmhw) +{ + struct hpi_format hpi_format; + u16 format; + u16 err; + u32 h_control; + u32 sample_rate = 48000; + + /* on cards without SRC, must query at valid rate, + * maybe set by external sync + */ + err = hpi_mixer_get_control(ss, asihpi->h_mixer, + HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0, + HPI_CONTROL_SAMPLECLOCK, &h_control); + + if (!err) + err = hpi_sample_clock_get_sample_rate(ss, h_control, + &sample_rate); + + for (format = HPI_FORMAT_PCM8_UNSIGNED; + format <= HPI_FORMAT_PCM24_SIGNED; format++) { + err = hpi_format_create(&hpi_format, + 2, format, sample_rate, 128000, 0); + if (!err) + err = hpi_outstream_query_format(ss, h_stream, + &hpi_format); + if (!err && (hpi_to_alsa_formats[format] != -1)) + pcmhw->formats |= + (1ULL << hpi_to_alsa_formats[format]); + } +} + +static struct snd_pcm_hardware snd_card_asihpi_playback = { + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = BUFFER_BYTES_MAX, + .period_bytes_min = PERIOD_BYTES_MIN, + .period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN, + .periods_min = PERIODS_MIN, + .periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN, + .fifo_size = 0, +}; + +static int snd_card_asihpi_playback_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_card_asihpi_pcm *dpcm; + struct snd_card_asihpi *card = snd_pcm_substream_chip(substream); + int err; + + dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL); + if (dpcm == NULL) + return -ENOMEM; + + err = + hpi_outstream_open(ss, card->adapter_index, + substream->number, &dpcm->h_stream); + hpi_handle_error(err); + if (err) + kfree(dpcm); + if (err == HPI_ERROR_OBJ_ALREADY_OPEN) + return -EBUSY; + if (err) + return -EIO; + + /*? also check ASI5000 samplerate source + If external, only support external rate. + If internal and other stream playing, cant switch + */ + + init_timer(&dpcm->timer); + dpcm->timer.data = (unsigned long) dpcm; + dpcm->timer.function = snd_card_asihpi_timer_function; + dpcm->substream = substream; + runtime->private_data = dpcm; + runtime->private_free = snd_card_asihpi_runtime_free; + + snd_card_asihpi_playback.channels_max = card->out_max_chans; + /*?snd_card_asihpi_playback.period_bytes_min = + card->out_max_chans * 4096; */ + + snd_card_asihpi_playback_format(card, dpcm->h_stream, + &snd_card_asihpi_playback); + + snd_card_asihpi_pcm_samplerates(card, &snd_card_asihpi_playback); + + snd_card_asihpi_playback.info = SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_DOUBLE | + SNDRV_PCM_INFO_BATCH | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_PAUSE; + + if (card->support_mmap) + snd_card_asihpi_playback.info |= SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID; + + if (card->support_grouping) + snd_card_asihpi_playback.info |= SNDRV_PCM_INFO_SYNC_START; + + /* struct is copied, so can create initializer dynamically */ + runtime->hw = snd_card_asihpi_playback; + + if (card->support_mmap) + err = snd_pcm_hw_constraint_pow2(runtime, 0, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES); + if (err < 0) + return err; + + snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + card->update_interval_frames); + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + card->update_interval_frames * 4, UINT_MAX); + + snd_pcm_set_sync(substream); + + snd_printd(KERN_INFO "playback open\n"); + + return 0; +} + +static int snd_card_asihpi_playback_close(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_card_asihpi_pcm *dpcm = runtime->private_data; + + hpi_handle_error(hpi_outstream_close(ss, dpcm->h_stream)); + snd_printd(KERN_INFO "playback close\n"); + + return 0; +} + +static int snd_card_asihpi_playback_copy(struct snd_pcm_substream *substream, + int channel, + snd_pcm_uframes_t pos, + void __user *src, + snd_pcm_uframes_t count) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_card_asihpi_pcm *dpcm = runtime->private_data; + unsigned int len; + + len = frames_to_bytes(runtime, count); + + if (copy_from_user(runtime->dma_area, src, len)) + return -EFAULT; + + VPRINTK2(KERN_DEBUG "playback copy%d %u bytes\n", + substream->number, len); + + hpi_handle_error(hpi_outstream_write_buf(ss, dpcm->h_stream, + runtime->dma_area, len, &dpcm->format)); + + return 0; +} + +static int snd_card_asihpi_playback_silence(struct snd_pcm_substream * + substream, int channel, + snd_pcm_uframes_t pos, + snd_pcm_uframes_t count) +{ + unsigned int len; + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_card_asihpi_pcm *dpcm = runtime->private_data; + + len = frames_to_bytes(runtime, count); + snd_printd(KERN_INFO "playback silence %u bytes\n", len); + + memset(runtime->dma_area, 0, len); + hpi_handle_error(hpi_outstream_write_buf(ss, dpcm->h_stream, + runtime->dma_area, len, &dpcm->format)); + return 0; +} + +static struct snd_pcm_ops snd_card_asihpi_playback_ops = { + .open = snd_card_asihpi_playback_open, + .close = snd_card_asihpi_playback_close, + .ioctl = snd_card_asihpi_playback_ioctl, + .hw_params = snd_card_asihpi_pcm_hw_params, + .hw_free = snd_card_asihpi_hw_free, + .prepare = snd_card_asihpi_playback_prepare, + .trigger = snd_card_asihpi_trigger, + .pointer = snd_card_asihpi_playback_pointer, + .copy = snd_card_asihpi_playback_copy, + .silence = snd_card_asihpi_playback_silence, +}; + +static struct snd_pcm_ops snd_card_asihpi_playback_mmap_ops = { + .open = snd_card_asihpi_playback_open, + .close = snd_card_asihpi_playback_close, + .ioctl = snd_card_asihpi_playback_ioctl, + .hw_params = snd_card_asihpi_pcm_hw_params, + .hw_free = snd_card_asihpi_hw_free, + .prepare = snd_card_asihpi_playback_prepare, + .trigger = snd_card_asihpi_trigger, + .pointer = snd_card_asihpi_playback_pointer, +}; + +/***************************** CAPTURE OPS ****************/ +static snd_pcm_uframes_t +snd_card_asihpi_capture_pointer(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_card_asihpi_pcm *dpcm = runtime->private_data; + + VPRINTK2("capture pointer %d=%d\n", + substream->number, dpcm->pcm_buf_pos); + /* NOTE Unlike playback can't use actual dwSamplesPlayed + for the capture position, because those samples aren't yet in + the local buffer available for reading. + */ + return bytes_to_frames(runtime, dpcm->pcm_buf_pos % dpcm->pcm_size); +} + +static int snd_card_asihpi_capture_ioctl(struct snd_pcm_substream *substream, + unsigned int cmd, void *arg) +{ + return snd_pcm_lib_ioctl(substream, cmd, arg); +} + +static int snd_card_asihpi_capture_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_card_asihpi_pcm *dpcm = runtime->private_data; + + hpi_handle_error(hpi_instream_reset(ss, dpcm->h_stream)); + dpcm->pcm_irq_pos = 0; + dpcm->pcm_buf_pos = 0; + + snd_printd("capture prepare %d\n", substream->number); + return 0; +} + + + +static void snd_card_asihpi_capture_format(struct snd_card_asihpi *asihpi, + u32 h_stream, + struct snd_pcm_hardware *pcmhw) +{ + struct hpi_format hpi_format; + u16 format; + u16 err; + u32 h_control; + u32 sample_rate = 48000; + + /* on cards without SRC, must query at valid rate, + maybe set by external sync */ + err = hpi_mixer_get_control(ss, asihpi->h_mixer, + HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0, + HPI_CONTROL_SAMPLECLOCK, &h_control); + + if (!err) + err = hpi_sample_clock_get_sample_rate(ss, h_control, + &sample_rate); + + for (format = HPI_FORMAT_PCM8_UNSIGNED; + format <= HPI_FORMAT_PCM24_SIGNED; format++) { + + err = hpi_format_create(&hpi_format, 2, format, + sample_rate, 128000, 0); + if (!err) + err = hpi_instream_query_format(ss, h_stream, + &hpi_format); + if (!err) + pcmhw->formats |= + (1ULL << hpi_to_alsa_formats[format]); + } +} + + +static struct snd_pcm_hardware snd_card_asihpi_capture = { + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = BUFFER_BYTES_MAX, + .period_bytes_min = PERIOD_BYTES_MIN, + .period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN, + .periods_min = PERIODS_MIN, + .periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN, + .fifo_size = 0, +}; + +static int snd_card_asihpi_capture_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_card_asihpi *card = snd_pcm_substream_chip(substream); + struct snd_card_asihpi_pcm *dpcm; + int err; + + dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL); + if (dpcm == NULL) + return -ENOMEM; + + snd_printd("hpi_instream_open adapter %d stream %d\n", + card->adapter_index, substream->number); + + err = hpi_handle_error( + hpi_instream_open(ss, card->adapter_index, + substream->number, &dpcm->h_stream)); + if (err) + kfree(dpcm); + if (err == HPI_ERROR_OBJ_ALREADY_OPEN) + return -EBUSY; + if (err) + return -EIO; + + + init_timer(&dpcm->timer); + dpcm->timer.data = (unsigned long) dpcm; + dpcm->timer.function = snd_card_asihpi_timer_function; + dpcm->substream = substream; + runtime->private_data = dpcm; + runtime->private_free = snd_card_asihpi_runtime_free; + + snd_card_asihpi_capture.channels_max = card->in_max_chans; + snd_card_asihpi_capture_format(card, dpcm->h_stream, + &snd_card_asihpi_capture); + snd_card_asihpi_pcm_samplerates(card, &snd_card_asihpi_capture); + snd_card_asihpi_capture.info = SNDRV_PCM_INFO_INTERLEAVED; + + if (card->support_mmap) + snd_card_asihpi_capture.info |= SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID; + + runtime->hw = snd_card_asihpi_capture; + + if (card->support_mmap) + err = snd_pcm_hw_constraint_pow2(runtime, 0, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES); + if (err < 0) + return err; + + snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + card->update_interval_frames); + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + card->update_interval_frames * 2, UINT_MAX); + + snd_pcm_set_sync(substream); + + return 0; +} + +static int snd_card_asihpi_capture_close(struct snd_pcm_substream *substream) +{ + struct snd_card_asihpi_pcm *dpcm = substream->runtime->private_data; + + hpi_handle_error(hpi_instream_close(ss, dpcm->h_stream)); + return 0; +} + +static int snd_card_asihpi_capture_copy(struct snd_pcm_substream *substream, + int channel, snd_pcm_uframes_t pos, + void __user *dst, snd_pcm_uframes_t count) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_card_asihpi_pcm *dpcm = runtime->private_data; + u32 data_size; + + data_size = frames_to_bytes(runtime, count); + + VPRINTK2("capture copy%d %d bytes\n", substream->number, data_size); + hpi_handle_error(hpi_instream_read_buf(ss, dpcm->h_stream, + runtime->dma_area, data_size)); + + /* Used by capture_pointer */ + dpcm->pcm_irq_pos = dpcm->pcm_irq_pos + data_size; + + if (copy_to_user(dst, runtime->dma_area, data_size)) + return -EFAULT; + + return 0; +} + +static struct snd_pcm_ops snd_card_asihpi_capture_mmap_ops = { + .open = snd_card_asihpi_capture_open, + .close = snd_card_asihpi_capture_close, + .ioctl = snd_card_asihpi_capture_ioctl, + .hw_params = snd_card_asihpi_pcm_hw_params, + .hw_free = snd_card_asihpi_hw_free, + .prepare = snd_card_asihpi_capture_prepare, + .trigger = snd_card_asihpi_trigger, + .pointer = snd_card_asihpi_capture_pointer, +}; + +static struct snd_pcm_ops snd_card_asihpi_capture_ops = { + .open = snd_card_asihpi_capture_open, + .close = snd_card_asihpi_capture_close, + .ioctl = snd_card_asihpi_capture_ioctl, + .hw_params = snd_card_asihpi_pcm_hw_params, + .hw_free = snd_card_asihpi_hw_free, + .prepare = snd_card_asihpi_capture_prepare, + .trigger = snd_card_asihpi_trigger, + .pointer = snd_card_asihpi_capture_pointer, + .copy = snd_card_asihpi_capture_copy +}; + +static int __devinit snd_card_asihpi_pcm_new(struct snd_card_asihpi *asihpi, + int device, int substreams) +{ + struct snd_pcm *pcm; + int err; + + err = snd_pcm_new(asihpi->card, "asihpi PCM", device, + asihpi->num_outstreams, asihpi->num_instreams, + &pcm); + if (err < 0) + return err; + /* pointer to ops struct is stored, dont change ops afterwards! */ + if (asihpi->support_mmap) { + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, + &snd_card_asihpi_playback_mmap_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + &snd_card_asihpi_capture_mmap_ops); + } else { + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, + &snd_card_asihpi_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + &snd_card_asihpi_capture_ops); + } + + pcm->private_data = asihpi; + pcm->info_flags = 0; + strcpy(pcm->name, "asihpi PCM"); + + /*? do we want to emulate MMAP for non-BBM cards? + Jack doesn't work with ALSAs MMAP emulation - WHY NOT? */ + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(asihpi->pci), + 64*1024, BUFFER_BYTES_MAX); + + return 0; +} + +/***************************** MIXER CONTROLS ****************/ +struct hpi_control { + u32 h_control; + u16 control_type; + u16 src_node_type; + u16 src_node_index; + u16 dst_node_type; + u16 dst_node_index; + u16 band; + char name[44]; /* copied to snd_ctl_elem_id.name[44]; */ +}; + +static char *asihpi_tuner_band_names[] = +{ + "invalid", + "AM", + "FM mono", + "TV NTSC-M", + "FM stereo", + "AUX", + "TV PAL BG", + "TV PAL I", + "TV PAL DK", + "TV SECAM", +}; + +compile_time_assert( + (ARRAY_SIZE(asihpi_tuner_band_names) == + (HPI_TUNER_BAND_LAST+1)), + assert_tuner_band_names_size); + +#if ASI_STYLE_NAMES +static char *asihpi_src_names[] = +{ + "no source", + "outstream", + "line_in", + "aes_in", + "tuner", + "RF", + "clock", + "bitstr", + "mic", + "cobranet", + "analog_in", + "adapter", +}; +#else +static char *asihpi_src_names[] = +{ + "no source", + "PCM playback", + "line in", + "digital in", + "tuner", + "RF", + "clock", + "bitstream", + "mic", + "cobranet in", + "analog in", + "adapter", +}; +#endif + +compile_time_assert( + (ARRAY_SIZE(asihpi_src_names) == + (HPI_SOURCENODE_LAST_INDEX-HPI_SOURCENODE_BASE+1)), + assert_src_names_size); + +#if ASI_STYLE_NAMES +static char *asihpi_dst_names[] = +{ + "no destination", + "instream", + "line_out", + "aes_out", + "RF", + "speaker" , + "cobranet", + "analog_out", +}; +#else +static char *asihpi_dst_names[] = +{ + "no destination", + "PCM capture", + "line out", + "digital out", + "RF", + "speaker", + "cobranet out", + "analog out" +}; +#endif + +compile_time_assert( + (ARRAY_SIZE(asihpi_dst_names) == + (HPI_DESTNODE_LAST_INDEX-HPI_DESTNODE_BASE+1)), + assert_dst_names_size); + +static inline int ctl_add(struct snd_card *card, struct snd_kcontrol_new *ctl, + struct snd_card_asihpi *asihpi) +{ + int err; + + err = snd_ctl_add(card, snd_ctl_new1(ctl, asihpi)); + if (err < 0) + return err; + else if (mixer_dump) + snd_printk(KERN_INFO "added %s(%d)\n", ctl->name, ctl->index); + + return 0; +} + +/* Convert HPI control name and location into ALSA control name */ +static void asihpi_ctl_init(struct snd_kcontrol_new *snd_control, + struct hpi_control *hpi_ctl, + char *name) +{ + memset(snd_control, 0, sizeof(*snd_control)); + snd_control->name = hpi_ctl->name; + snd_control->private_value = hpi_ctl->h_control; + snd_control->iface = SNDRV_CTL_ELEM_IFACE_MIXER; + snd_control->index = 0; + + if (hpi_ctl->src_node_type && hpi_ctl->dst_node_type) + sprintf(hpi_ctl->name, "%s%d to %s%d %s", + asihpi_src_names[hpi_ctl->src_node_type], + hpi_ctl->src_node_index, + asihpi_dst_names[hpi_ctl->dst_node_type], + hpi_ctl->dst_node_index, + name); + else if (hpi_ctl->dst_node_type) { + sprintf(hpi_ctl->name, "%s%d %s", + asihpi_dst_names[hpi_ctl->dst_node_type], + hpi_ctl->dst_node_index, + name); + } else { + sprintf(hpi_ctl->name, "%s%d %s", + asihpi_src_names[hpi_ctl->src_node_type], + hpi_ctl->src_node_index, + name); + } +} + +/*------------------------------------------------------------ + Volume controls + ------------------------------------------------------------*/ +#define VOL_STEP_mB 1 +static int snd_asihpi_volume_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + u32 h_control = kcontrol->private_value; + u16 err; + /* native gains are in millibels */ + short min_gain_mB; + short max_gain_mB; + short step_gain_mB; + + err = hpi_volume_query_range(ss, h_control, + &min_gain_mB, &max_gain_mB, &step_gain_mB); + if (err) { + max_gain_mB = 0; + min_gain_mB = -10000; + step_gain_mB = VOL_STEP_mB; + } + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = min_gain_mB / VOL_STEP_mB; + uinfo->value.integer.max = max_gain_mB / VOL_STEP_mB; + uinfo->value.integer.step = step_gain_mB / VOL_STEP_mB; + return 0; +} + +static int snd_asihpi_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u32 h_control = kcontrol->private_value; + short an_gain_mB[HPI_MAX_CHANNELS]; + + hpi_handle_error(hpi_volume_get_gain(ss, h_control, an_gain_mB)); + ucontrol->value.integer.value[0] = an_gain_mB[0] / VOL_STEP_mB; + ucontrol->value.integer.value[1] = an_gain_mB[1] / VOL_STEP_mB; + + return 0; +} + +static int snd_asihpi_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int change; + u32 h_control = kcontrol->private_value; + short an_gain_mB[HPI_MAX_CHANNELS]; + + an_gain_mB[0] = + (ucontrol->value.integer.value[0]) * VOL_STEP_mB; + an_gain_mB[1] = + (ucontrol->value.integer.value[1]) * VOL_STEP_mB; + /* change = asihpi->mixer_volume[addr][0] != left || + asihpi->mixer_volume[addr][1] != right; + */ + change = 1; + hpi_handle_error(hpi_volume_set_gain(ss, h_control, an_gain_mB)); + return change; +} + +static const DECLARE_TLV_DB_SCALE(db_scale_100, -10000, VOL_STEP_mB, 0); + +static int __devinit snd_asihpi_volume_add(struct snd_card_asihpi *asihpi, + struct hpi_control *hpi_ctl) +{ + struct snd_card *card = asihpi->card; + struct snd_kcontrol_new snd_control; + + asihpi_ctl_init(&snd_control, hpi_ctl, "volume"); + snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ; + snd_control.info = snd_asihpi_volume_info; + snd_control.get = snd_asihpi_volume_get; + snd_control.put = snd_asihpi_volume_put; + snd_control.tlv.p = db_scale_100; + + return ctl_add(card, &snd_control, asihpi); +} + +/*------------------------------------------------------------ + Level controls + ------------------------------------------------------------*/ +static int snd_asihpi_level_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + u32 h_control = kcontrol->private_value; + u16 err; + short min_gain_mB; + short max_gain_mB; + short step_gain_mB; + + err = + hpi_level_query_range(ss, h_control, &min_gain_mB, + &max_gain_mB, &step_gain_mB); + if (err) { + max_gain_mB = 2400; + min_gain_mB = -1000; + step_gain_mB = 100; + } + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = min_gain_mB / HPI_UNITS_PER_dB; + uinfo->value.integer.max = max_gain_mB / HPI_UNITS_PER_dB; + uinfo->value.integer.step = step_gain_mB / HPI_UNITS_PER_dB; + return 0; +} + +static int snd_asihpi_level_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u32 h_control = kcontrol->private_value; + short an_gain_mB[HPI_MAX_CHANNELS]; + + hpi_handle_error(hpi_level_get_gain(ss, h_control, an_gain_mB)); + ucontrol->value.integer.value[0] = + an_gain_mB[0] / HPI_UNITS_PER_dB; + ucontrol->value.integer.value[1] = + an_gain_mB[1] / HPI_UNITS_PER_dB; + + return 0; +} + +static int snd_asihpi_level_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int change; + u32 h_control = kcontrol->private_value; + short an_gain_mB[HPI_MAX_CHANNELS]; + + an_gain_mB[0] = + (ucontrol->value.integer.value[0]) * HPI_UNITS_PER_dB; + an_gain_mB[1] = + (ucontrol->value.integer.value[1]) * HPI_UNITS_PER_dB; + /* change = asihpi->mixer_level[addr][0] != left || + asihpi->mixer_level[addr][1] != right; + */ + change = 1; + hpi_handle_error(hpi_level_set_gain(ss, h_control, an_gain_mB)); + return change; +} + +static const DECLARE_TLV_DB_SCALE(db_scale_level, -1000, 100, 0); + +static int __devinit snd_asihpi_level_add(struct snd_card_asihpi *asihpi, + struct hpi_control *hpi_ctl) +{ + struct snd_card *card = asihpi->card; + struct snd_kcontrol_new snd_control; + + /* can't use 'volume' cos some nodes have volume as well */ + asihpi_ctl_init(&snd_control, hpi_ctl, "level"); + snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ; + snd_control.info = snd_asihpi_level_info; + snd_control.get = snd_asihpi_level_get; + snd_control.put = snd_asihpi_level_put; + snd_control.tlv.p = db_scale_level; + + return ctl_add(card, &snd_control, asihpi); +} + +/*------------------------------------------------------------ + AESEBU controls + ------------------------------------------------------------*/ + +/* AESEBU format */ +static char *asihpi_aesebu_format_names[] = +{ + "N/A", + "S/PDIF", + "AES/EBU", +}; + +static int snd_asihpi_aesebu_format_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 3; + + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = + uinfo->value.enumerated.items - 1; + + strcpy(uinfo->value.enumerated.name, + asihpi_aesebu_format_names[uinfo->value.enumerated.item]); + + return 0; +} + +static int snd_asihpi_aesebu_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol, + u16 (*func)(const struct hpi_hsubsys *, u32, u16 *)) +{ + u32 h_control = kcontrol->private_value; + u16 source, err; + + err = func(ss, h_control, &source); + + /* default to N/A */ + ucontrol->value.enumerated.item[0] = 0; + /* return success but set the control to N/A */ + if (err) + return 0; + if (source == HPI_AESEBU_FORMAT_SPDIF) + ucontrol->value.enumerated.item[0] = 1; + if (source == HPI_AESEBU_FORMAT_AESEBU) + ucontrol->value.enumerated.item[0] = 2; + + return 0; +} + +static int snd_asihpi_aesebu_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol, + u16 (*func)(const struct hpi_hsubsys *, u32, u16)) +{ + u32 h_control = kcontrol->private_value; + + /* default to S/PDIF */ + u16 source = HPI_AESEBU_FORMAT_SPDIF; + + if (ucontrol->value.enumerated.item[0] == 1) + source = HPI_AESEBU_FORMAT_SPDIF; + if (ucontrol->value.enumerated.item[0] == 2) + source = HPI_AESEBU_FORMAT_AESEBU; + + if (func(ss, h_control, source) != 0) + return -EINVAL; + + return 1; +} + +static int snd_asihpi_aesebu_rx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { + return snd_asihpi_aesebu_format_get(kcontrol, ucontrol, + HPI_AESEBU__receiver_get_format); +} + +static int snd_asihpi_aesebu_rx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { + return snd_asihpi_aesebu_format_put(kcontrol, ucontrol, + HPI_AESEBU__receiver_set_format); +} + +static int snd_asihpi_aesebu_rxstatus_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0X1F; + uinfo->value.integer.step = 1; + + return 0; +} + +static int snd_asihpi_aesebu_rxstatus_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { + + u32 h_control = kcontrol->private_value; + u16 status; + + hpi_handle_error(HPI_AESEBU__receiver_get_error_status( + ss, h_control, &status)); + ucontrol->value.integer.value[0] = status; + return 0; +} + +static int __devinit snd_asihpi_aesebu_rx_add(struct snd_card_asihpi *asihpi, + struct hpi_control *hpi_ctl) +{ + struct snd_card *card = asihpi->card; + struct snd_kcontrol_new snd_control; + + asihpi_ctl_init(&snd_control, hpi_ctl, "format"); + snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; + snd_control.info = snd_asihpi_aesebu_format_info; + snd_control.get = snd_asihpi_aesebu_rx_format_get; + snd_control.put = snd_asihpi_aesebu_rx_format_put; + + + if (ctl_add(card, &snd_control, asihpi) < 0) + return -EINVAL; + + asihpi_ctl_init(&snd_control, hpi_ctl, "status"); + snd_control.access = + SNDRV_CTL_ELEM_ACCESS_VOLATILE | SNDRV_CTL_ELEM_ACCESS_READ; + snd_control.info = snd_asihpi_aesebu_rxstatus_info; + snd_control.get = snd_asihpi_aesebu_rxstatus_get; + + return ctl_add(card, &snd_control, asihpi); +} + +static int snd_asihpi_aesebu_tx_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { + return snd_asihpi_aesebu_format_get(kcontrol, ucontrol, + HPI_AESEBU__transmitter_get_format); +} + +static int snd_asihpi_aesebu_tx_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { + return snd_asihpi_aesebu_format_put(kcontrol, ucontrol, + HPI_AESEBU__transmitter_set_format); +} + + +static int __devinit snd_asihpi_aesebu_tx_add(struct snd_card_asihpi *asihpi, + struct hpi_control *hpi_ctl) +{ + struct snd_card *card = asihpi->card; + struct snd_kcontrol_new snd_control; + + asihpi_ctl_init(&snd_control, hpi_ctl, "format"); + snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; + snd_control.info = snd_asihpi_aesebu_format_info; + snd_control.get = snd_asihpi_aesebu_tx_format_get; + snd_control.put = snd_asihpi_aesebu_tx_format_put; + + return ctl_add(card, &snd_control, asihpi); +} + +/*------------------------------------------------------------ + Tuner controls + ------------------------------------------------------------*/ + +/* Gain */ + +static int snd_asihpi_tuner_gain_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + u32 h_control = kcontrol->private_value; + u16 err; + short idx; + u16 gain_range[3]; + + for (idx = 0; idx < 3; idx++) { + err = hpi_tuner_query_gain(ss, h_control, + idx, &gain_range[idx]); + if (err != 0) + return err; + } + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = ((int)gain_range[0]) / HPI_UNITS_PER_dB; + uinfo->value.integer.max = ((int)gain_range[1]) / HPI_UNITS_PER_dB; + uinfo->value.integer.step = ((int) gain_range[2]) / HPI_UNITS_PER_dB; + return 0; +} + +static int snd_asihpi_tuner_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + /* + struct snd_card_asihpi *asihpi = snd_kcontrol_chip(kcontrol); + */ + u32 h_control = kcontrol->private_value; + short gain; + + hpi_handle_error(hpi_tuner_get_gain(ss, h_control, &gain)); + ucontrol->value.integer.value[0] = gain / HPI_UNITS_PER_dB; + + return 0; +} + +static int snd_asihpi_tuner_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + /* + struct snd_card_asihpi *asihpi = snd_kcontrol_chip(kcontrol); + */ + u32 h_control = kcontrol->private_value; + short gain; + + gain = (ucontrol->value.integer.value[0]) * HPI_UNITS_PER_dB; + hpi_handle_error(hpi_tuner_set_gain(ss, h_control, gain)); + + return 1; +} + +/* Band */ + +static int asihpi_tuner_band_query(struct snd_kcontrol *kcontrol, + u16 *band_list, u32 len) { + u32 h_control = kcontrol->private_value; + u16 err = 0; + u32 i; + + for (i = 0; i < len; i++) { + err = hpi_tuner_query_band(ss, + h_control, i, &band_list[i]); + if (err != 0) + break; + } + + if (err && (err != HPI_ERROR_INVALID_OBJ_INDEX)) + return -EIO; + + return i; +} + +static int snd_asihpi_tuner_band_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + u16 tuner_bands[HPI_TUNER_BAND_LAST]; + int num_bands = 0; + + num_bands = asihpi_tuner_band_query(kcontrol, tuner_bands, + HPI_TUNER_BAND_LAST); + + if (num_bands < 0) + return num_bands; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = num_bands; + + if (num_bands > 0) { + if (uinfo->value.enumerated.item >= + uinfo->value.enumerated.items) + uinfo->value.enumerated.item = + uinfo->value.enumerated.items - 1; + + strcpy(uinfo->value.enumerated.name, + asihpi_tuner_band_names[ + tuner_bands[uinfo->value.enumerated.item]]); + + } + return 0; +} + +static int snd_asihpi_tuner_band_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u32 h_control = kcontrol->private_value; + /* + struct snd_card_asihpi *asihpi = snd_kcontrol_chip(kcontrol); + */ + u16 band, idx; + u16 tuner_bands[HPI_TUNER_BAND_LAST]; + u32 num_bands = 0; + + num_bands = asihpi_tuner_band_query(kcontrol, tuner_bands, + HPI_TUNER_BAND_LAST); + + hpi_handle_error(hpi_tuner_get_band(ss, h_control, &band)); + + ucontrol->value.enumerated.item[0] = -1; + for (idx = 0; idx < HPI_TUNER_BAND_LAST; idx++) + if (tuner_bands[idx] == band) { + ucontrol->value.enumerated.item[0] = idx; + break; + } + + return 0; +} + +static int snd_asihpi_tuner_band_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + /* + struct snd_card_asihpi *asihpi = snd_kcontrol_chip(kcontrol); + */ + u32 h_control = kcontrol->private_value; + u16 band; + u16 tuner_bands[HPI_TUNER_BAND_LAST]; + u32 num_bands = 0; + + num_bands = asihpi_tuner_band_query(kcontrol, tuner_bands, + HPI_TUNER_BAND_LAST); + + band = tuner_bands[ucontrol->value.enumerated.item[0]]; + hpi_handle_error(hpi_tuner_set_band(ss, h_control, band)); + + return 1; +} + +/* Freq */ + +static int snd_asihpi_tuner_freq_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + u32 h_control = kcontrol->private_value; + u16 err; + u16 tuner_bands[HPI_TUNER_BAND_LAST]; + u16 num_bands = 0, band_iter, idx; + u32 freq_range[3], temp_freq_range[3]; + + num_bands = asihpi_tuner_band_query(kcontrol, tuner_bands, + HPI_TUNER_BAND_LAST); + + freq_range[0] = INT_MAX; + freq_range[1] = 0; + freq_range[2] = INT_MAX; + + for (band_iter = 0; band_iter < num_bands; band_iter++) { + for (idx = 0; idx < 3; idx++) { + err = hpi_tuner_query_frequency(ss, h_control, + idx, tuner_bands[band_iter], + &temp_freq_range[idx]); + if (err != 0) + return err; + } + + /* skip band with bogus stepping */ + if (temp_freq_range[2] <= 0) + continue; + + if (temp_freq_range[0] < freq_range[0]) + freq_range[0] = temp_freq_range[0]; + if (temp_freq_range[1] > freq_range[1]) + freq_range[1] = temp_freq_range[1]; + if (temp_freq_range[2] < freq_range[2]) + freq_range[2] = temp_freq_range[2]; + } + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = ((int)freq_range[0]); + uinfo->value.integer.max = ((int)freq_range[1]); + uinfo->value.integer.step = ((int)freq_range[2]); + return 0; +} + +static int snd_asihpi_tuner_freq_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u32 h_control = kcontrol->private_value; + u32 freq; + + hpi_handle_error(hpi_tuner_get_frequency(ss, h_control, &freq)); + ucontrol->value.integer.value[0] = freq; + + return 0; +} + +static int snd_asihpi_tuner_freq_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u32 h_control = kcontrol->private_value; + u32 freq; + + freq = ucontrol->value.integer.value[0]; + hpi_handle_error(hpi_tuner_set_frequency(ss, h_control, freq)); + + return 1; +} + +/* Tuner control group initializer */ +static int __devinit snd_asihpi_tuner_add(struct snd_card_asihpi *asihpi, + struct hpi_control *hpi_ctl) +{ + struct snd_card *card = asihpi->card; + struct snd_kcontrol_new snd_control; + + snd_control.private_value = hpi_ctl->h_control; + snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; + + if (!hpi_tuner_get_gain(ss, hpi_ctl->h_control, NULL)) { + asihpi_ctl_init(&snd_control, hpi_ctl, "gain"); + snd_control.info = snd_asihpi_tuner_gain_info; + snd_control.get = snd_asihpi_tuner_gain_get; + snd_control.put = snd_asihpi_tuner_gain_put; + + if (ctl_add(card, &snd_control, asihpi) < 0) + return -EINVAL; + } + + asihpi_ctl_init(&snd_control, hpi_ctl, "band"); + snd_control.info = snd_asihpi_tuner_band_info; + snd_control.get = snd_asihpi_tuner_band_get; + snd_control.put = snd_asihpi_tuner_band_put; + + if (ctl_add(card, &snd_control, asihpi) < 0) + return -EINVAL; + + asihpi_ctl_init(&snd_control, hpi_ctl, "freq"); + snd_control.info = snd_asihpi_tuner_freq_info; + snd_control.get = snd_asihpi_tuner_freq_get; + snd_control.put = snd_asihpi_tuner_freq_put; + + return ctl_add(card, &snd_control, asihpi); +} + +/*------------------------------------------------------------ + Meter controls + ------------------------------------------------------------*/ +static int snd_asihpi_meter_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = HPI_MAX_CHANNELS; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0x7FFFFFFF; + return 0; +} + +/* linear values for 10dB steps */ +static int log2lin[] = { + 0x7FFFFFFF, /* 0dB */ + 679093956, + 214748365, + 67909396, + 21474837, + 6790940, + 2147484, /* -60dB */ + 679094, + 214748, /* -80 */ + 67909, + 21475, /* -100 */ + 6791, + 2147, + 679, + 214, + 68, + 21, + 7, + 2 +}; + +static int snd_asihpi_meter_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u32 h_control = kcontrol->private_value; + short an_gain_mB[HPI_MAX_CHANNELS], i; + u16 err; + + err = hpi_meter_get_peak(ss, h_control, an_gain_mB); + + for (i = 0; i < HPI_MAX_CHANNELS; i++) { + if (err) { + ucontrol->value.integer.value[i] = 0; + } else if (an_gain_mB[i] >= 0) { + ucontrol->value.integer.value[i] = + an_gain_mB[i] << 16; + } else { + /* -ve is log value in millibels < -60dB, + * convert to (roughly!) linear, + */ + ucontrol->value.integer.value[i] = + log2lin[an_gain_mB[i] / -1000]; + } + } + return 0; +} + +static int __devinit snd_asihpi_meter_add(struct snd_card_asihpi *asihpi, + struct hpi_control *hpi_ctl, int subidx) +{ + struct snd_card *card = asihpi->card; + struct snd_kcontrol_new snd_control; + + asihpi_ctl_init(&snd_control, hpi_ctl, "meter"); + snd_control.access = + SNDRV_CTL_ELEM_ACCESS_VOLATILE | SNDRV_CTL_ELEM_ACCESS_READ; + snd_control.info = snd_asihpi_meter_info; + snd_control.get = snd_asihpi_meter_get; + + snd_control.index = subidx; + + return ctl_add(card, &snd_control, asihpi); +} + +/*------------------------------------------------------------ + Multiplexer controls + ------------------------------------------------------------*/ +static int snd_card_asihpi_mux_count_sources(struct snd_kcontrol *snd_control) +{ + u32 h_control = snd_control->private_value; + struct hpi_control hpi_ctl; + int s, err; + for (s = 0; s < 32; s++) { + err = hpi_multiplexer_query_source(ss, h_control, s, + &hpi_ctl. + src_node_type, + &hpi_ctl. + src_node_index); + if (err) + break; + } + return s; +} + +static int snd_asihpi_mux_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int err; + u16 src_node_type, src_node_index; + u32 h_control = kcontrol->private_value; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = + snd_card_asihpi_mux_count_sources(kcontrol); + + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = + uinfo->value.enumerated.items - 1; + + err = + hpi_multiplexer_query_source(ss, h_control, + uinfo->value.enumerated.item, + &src_node_type, &src_node_index); + + sprintf(uinfo->value.enumerated.name, "%s %d", + asihpi_src_names[src_node_type - HPI_SOURCENODE_BASE], + src_node_index); + return 0; +} + +static int snd_asihpi_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u32 h_control = kcontrol->private_value; + u16 source_type, source_index; + u16 src_node_type, src_node_index; + int s; + + hpi_handle_error(hpi_multiplexer_get_source(ss, h_control, + &source_type, &source_index)); + /* Should cache this search result! */ + for (s = 0; s < 256; s++) { + if (hpi_multiplexer_query_source(ss, h_control, s, + &src_node_type, &src_node_index)) + break; + + if ((source_type == src_node_type) + && (source_index == src_node_index)) { + ucontrol->value.enumerated.item[0] = s; + return 0; + } + } + snd_printd(KERN_WARNING + "control %x failed to match mux source %hu %hu\n", + h_control, source_type, source_index); + ucontrol->value.enumerated.item[0] = 0; + return 0; +} + +static int snd_asihpi_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int change; + u32 h_control = kcontrol->private_value; + u16 source_type, source_index; + u16 e; + + change = 1; + + e = hpi_multiplexer_query_source(ss, h_control, + ucontrol->value.enumerated.item[0], + &source_type, &source_index); + if (!e) + hpi_handle_error( + hpi_multiplexer_set_source(ss, h_control, + source_type, source_index)); + return change; +} + + +static int __devinit snd_asihpi_mux_add(struct snd_card_asihpi *asihpi, + struct hpi_control *hpi_ctl) +{ + struct snd_card *card = asihpi->card; + struct snd_kcontrol_new snd_control; + +#if ASI_STYLE_NAMES + asihpi_ctl_init(&snd_control, hpi_ctl, "multiplexer"); +#else + asihpi_ctl_init(&snd_control, hpi_ctl, "route"); +#endif + snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; + snd_control.info = snd_asihpi_mux_info; + snd_control.get = snd_asihpi_mux_get; + snd_control.put = snd_asihpi_mux_put; + + return ctl_add(card, &snd_control, asihpi); + +} + +/*------------------------------------------------------------ + Channel mode controls + ------------------------------------------------------------*/ +static int snd_asihpi_cmode_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static char *mode_names[HPI_CHANNEL_MODE_LAST] = { + "normal", "swap", + "from_left", "from_right", + "to_left", "to_right" + }; + + u32 h_control = kcontrol->private_value; + u16 mode; + int i; + + /* HPI channel mode values can be from 1 to 6 + Some adapters only support a contiguous subset + */ + for (i = 0; i < HPI_CHANNEL_MODE_LAST; i++) + if (hpi_channel_mode_query_mode( + ss, h_control, i, &mode)) + break; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = i; + + if (uinfo->value.enumerated.item >= i) + uinfo->value.enumerated.item = i - 1; + + strcpy(uinfo->value.enumerated.name, + mode_names[uinfo->value.enumerated.item]); + + return 0; +} + +static int snd_asihpi_cmode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u32 h_control = kcontrol->private_value; + u16 mode; + + if (hpi_channel_mode_get(ss, h_control, &mode)) + mode = 1; + + ucontrol->value.enumerated.item[0] = mode - 1; + + return 0; +} + +static int snd_asihpi_cmode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int change; + u32 h_control = kcontrol->private_value; + + change = 1; + + hpi_handle_error(hpi_channel_mode_set(ss, h_control, + ucontrol->value.enumerated.item[0] + 1)); + return change; +} + + +static int __devinit snd_asihpi_cmode_add(struct snd_card_asihpi *asihpi, + struct hpi_control *hpi_ctl) +{ + struct snd_card *card = asihpi->card; + struct snd_kcontrol_new snd_control; + + asihpi_ctl_init(&snd_control, hpi_ctl, "channel mode"); + snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; + snd_control.info = snd_asihpi_cmode_info; + snd_control.get = snd_asihpi_cmode_get; + snd_control.put = snd_asihpi_cmode_put; + + return ctl_add(card, &snd_control, asihpi); +} + +/*------------------------------------------------------------ + Sampleclock source controls + ------------------------------------------------------------*/ + +static char *sampleclock_sources[MAX_CLOCKSOURCES] = + { "N/A", "local PLL", "AES/EBU sync", "word external", "word header", + "SMPTE", "AES/EBU in1", "auto", "network", "invalid", + "prev module", + "AES/EBU in2", "AES/EBU in3", "AES/EBU in4", "AES/EBU in5", + "AES/EBU in6", "AES/EBU in7", "AES/EBU in8"}; + + + +static int snd_asihpi_clksrc_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_card_asihpi *asihpi = + (struct snd_card_asihpi *)(kcontrol->private_data); + struct clk_cache *clkcache = &asihpi->cc; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = clkcache->count; + + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = + uinfo->value.enumerated.items - 1; + + strcpy(uinfo->value.enumerated.name, + clkcache->s[uinfo->value.enumerated.item].name); + return 0; +} + +static int snd_asihpi_clksrc_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_card_asihpi *asihpi = + (struct snd_card_asihpi *)(kcontrol->private_data); + struct clk_cache *clkcache = &asihpi->cc; + u32 h_control = kcontrol->private_value; + u16 source, srcindex = 0; + int i; + + ucontrol->value.enumerated.item[0] = 0; + if (hpi_sample_clock_get_source(ss, h_control, &source)) + source = 0; + + if (source == HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT) + if (hpi_sample_clock_get_source_index(ss, h_control, &srcindex)) + srcindex = 0; + + for (i = 0; i < clkcache->count; i++) + if ((clkcache->s[i].source == source) && + (clkcache->s[i].index == srcindex)) + break; + + ucontrol->value.enumerated.item[0] = i; + + return 0; +} + +static int snd_asihpi_clksrc_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_card_asihpi *asihpi = + (struct snd_card_asihpi *)(kcontrol->private_data); + struct clk_cache *clkcache = &asihpi->cc; + int change, item; + u32 h_control = kcontrol->private_value; + + change = 1; + item = ucontrol->value.enumerated.item[0]; + if (item >= clkcache->count) + item = clkcache->count-1; + + hpi_handle_error(hpi_sample_clock_set_source(ss, + h_control, clkcache->s[item].source)); + + if (clkcache->s[item].source == HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT) + hpi_handle_error(hpi_sample_clock_set_source_index(ss, + h_control, clkcache->s[item].index)); + return change; +} + +/*------------------------------------------------------------ + Clkrate controls + ------------------------------------------------------------*/ +/* Need to change this to enumerated control with list of rates */ +static int snd_asihpi_clklocal_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 8000; + uinfo->value.integer.max = 192000; + uinfo->value.integer.step = 100; + + return 0; +} + +static int snd_asihpi_clklocal_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u32 h_control = kcontrol->private_value; + u32 rate; + u16 e; + + e = hpi_sample_clock_get_local_rate(ss, h_control, &rate); + if (!e) + ucontrol->value.integer.value[0] = rate; + else + ucontrol->value.integer.value[0] = 0; + return 0; +} + +static int snd_asihpi_clklocal_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int change; + u32 h_control = kcontrol->private_value; + + /* change = asihpi->mixer_clkrate[addr][0] != left || + asihpi->mixer_clkrate[addr][1] != right; + */ + change = 1; + hpi_handle_error(hpi_sample_clock_set_local_rate(ss, h_control, + ucontrol->value.integer.value[0])); + return change; +} + +static int snd_asihpi_clkrate_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 8000; + uinfo->value.integer.max = 192000; + uinfo->value.integer.step = 100; + + return 0; +} + +static int snd_asihpi_clkrate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u32 h_control = kcontrol->private_value; + u32 rate; + u16 e; + + e = hpi_sample_clock_get_sample_rate(ss, h_control, &rate); + if (!e) + ucontrol->value.integer.value[0] = rate; + else + ucontrol->value.integer.value[0] = 0; + return 0; +} + +static int __devinit snd_asihpi_sampleclock_add(struct snd_card_asihpi *asihpi, + struct hpi_control *hpi_ctl) +{ + struct snd_card *card = asihpi->card; + struct snd_kcontrol_new snd_control; + + struct clk_cache *clkcache = &asihpi->cc; + u32 hSC = hpi_ctl->h_control; + int has_aes_in = 0; + int i, j; + u16 source; + + snd_control.private_value = hpi_ctl->h_control; + + clkcache->has_local = 0; + + for (i = 0; i <= HPI_SAMPLECLOCK_SOURCE_LAST; i++) { + if (hpi_sample_clock_query_source(ss, hSC, + i, &source)) + break; + clkcache->s[i].source = source; + clkcache->s[i].index = 0; + clkcache->s[i].name = sampleclock_sources[source]; + if (source == HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT) + has_aes_in = 1; + if (source == HPI_SAMPLECLOCK_SOURCE_LOCAL) + clkcache->has_local = 1; + } + if (has_aes_in) + /* already will have picked up index 0 above */ + for (j = 1; j < 8; j++) { + if (hpi_sample_clock_query_source_index(ss, hSC, + j, HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT, + &source)) + break; + clkcache->s[i].source = + HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT; + clkcache->s[i].index = j; + clkcache->s[i].name = sampleclock_sources[ + j+HPI_SAMPLECLOCK_SOURCE_LAST]; + i++; + } + clkcache->count = i; + + asihpi_ctl_init(&snd_control, hpi_ctl, "source"); + snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE ; + snd_control.info = snd_asihpi_clksrc_info; + snd_control.get = snd_asihpi_clksrc_get; + snd_control.put = snd_asihpi_clksrc_put; + if (ctl_add(card, &snd_control, asihpi) < 0) + return -EINVAL; + + + if (clkcache->has_local) { + asihpi_ctl_init(&snd_control, hpi_ctl, "local_rate"); + snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE ; + snd_control.info = snd_asihpi_clklocal_info; + snd_control.get = snd_asihpi_clklocal_get; + snd_control.put = snd_asihpi_clklocal_put; + + + if (ctl_add(card, &snd_control, asihpi) < 0) + return -EINVAL; + } + + asihpi_ctl_init(&snd_control, hpi_ctl, "rate"); + snd_control.access = + SNDRV_CTL_ELEM_ACCESS_VOLATILE | SNDRV_CTL_ELEM_ACCESS_READ; + snd_control.info = snd_asihpi_clkrate_info; + snd_control.get = snd_asihpi_clkrate_get; + + return ctl_add(card, &snd_control, asihpi); +} +/*------------------------------------------------------------ + Mixer + ------------------------------------------------------------*/ + +static int __devinit snd_card_asihpi_mixer_new(struct snd_card_asihpi *asihpi) +{ + struct snd_card *card = asihpi->card; + unsigned int idx = 0; + unsigned int subindex = 0; + int err; + struct hpi_control hpi_ctl, prev_ctl; + + if (snd_BUG_ON(!asihpi)) + return -EINVAL; + strcpy(card->mixername, "asihpi mixer"); + + err = + hpi_mixer_open(ss, asihpi->adapter_index, + &asihpi->h_mixer); + hpi_handle_error(err); + if (err) + return -err; + + for (idx = 0; idx < 2000; idx++) { + err = hpi_mixer_get_control_by_index( + ss, asihpi->h_mixer, + idx, + &hpi_ctl.src_node_type, + &hpi_ctl.src_node_index, + &hpi_ctl.dst_node_type, + &hpi_ctl.dst_node_index, + &hpi_ctl.control_type, + &hpi_ctl.h_control); + if (err) { + if (err == HPI_ERROR_CONTROL_DISABLED) { + if (mixer_dump) + snd_printk(KERN_INFO + "disabled HPI control(%d)\n", + idx); + continue; + } else + break; + + } + + hpi_ctl.src_node_type -= HPI_SOURCENODE_BASE; + hpi_ctl.dst_node_type -= HPI_DESTNODE_BASE; + + /* ASI50xx in SSX mode has multiple meters on the same node. + Use subindex to create distinct ALSA controls + for any duplicated controls. + */ + if ((hpi_ctl.control_type == prev_ctl.control_type) && + (hpi_ctl.src_node_type == prev_ctl.src_node_type) && + (hpi_ctl.src_node_index == prev_ctl.src_node_index) && + (hpi_ctl.dst_node_type == prev_ctl.dst_node_type) && + (hpi_ctl.dst_node_index == prev_ctl.dst_node_index)) + subindex++; + else + subindex = 0; + + prev_ctl = hpi_ctl; + + switch (hpi_ctl.control_type) { + case HPI_CONTROL_VOLUME: + err = snd_asihpi_volume_add(asihpi, &hpi_ctl); + break; + case HPI_CONTROL_LEVEL: + err = snd_asihpi_level_add(asihpi, &hpi_ctl); + break; + case HPI_CONTROL_MULTIPLEXER: + err = snd_asihpi_mux_add(asihpi, &hpi_ctl); + break; + case HPI_CONTROL_CHANNEL_MODE: + err = snd_asihpi_cmode_add(asihpi, &hpi_ctl); + break; + case HPI_CONTROL_METER: + err = snd_asihpi_meter_add(asihpi, &hpi_ctl, subindex); + break; + case HPI_CONTROL_SAMPLECLOCK: + err = snd_asihpi_sampleclock_add( + asihpi, &hpi_ctl); + break; + case HPI_CONTROL_CONNECTION: /* ignore these */ + continue; + case HPI_CONTROL_TUNER: + err = snd_asihpi_tuner_add(asihpi, &hpi_ctl); + break; + case HPI_CONTROL_AESEBU_TRANSMITTER: + err = snd_asihpi_aesebu_tx_add(asihpi, &hpi_ctl); + break; + case HPI_CONTROL_AESEBU_RECEIVER: + err = snd_asihpi_aesebu_rx_add(asihpi, &hpi_ctl); + break; + case HPI_CONTROL_VOX: + case HPI_CONTROL_BITSTREAM: + case HPI_CONTROL_MICROPHONE: + case HPI_CONTROL_PARAMETRIC_EQ: + case HPI_CONTROL_COMPANDER: + default: + if (mixer_dump) + snd_printk(KERN_INFO + "untranslated HPI control" + "(%d) %d %d %d %d %d\n", + idx, + hpi_ctl.control_type, + hpi_ctl.src_node_type, + hpi_ctl.src_node_index, + hpi_ctl.dst_node_type, + hpi_ctl.dst_node_index); + continue; + }; + if (err < 0) + return err; + } + if (HPI_ERROR_INVALID_OBJ_INDEX != err) + hpi_handle_error(err); + + snd_printk(KERN_INFO "%d mixer controls found\n", idx); + + return 0; +} + +/*------------------------------------------------------------ + /proc interface + ------------------------------------------------------------*/ + +static void +snd_asihpi_proc_read(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct snd_card_asihpi *asihpi = entry->private_data; + u16 version; + u32 h_control; + u32 rate = 0; + u16 source = 0; + int err; + + snd_iprintf(buffer, "ASIHPI driver proc file\n"); + snd_iprintf(buffer, + "adapter ID=%4X\n_index=%d\n" + "num_outstreams=%d\n_num_instreams=%d\n", + asihpi->type, asihpi->adapter_index, + asihpi->num_outstreams, asihpi->num_instreams); + + version = asihpi->version; + snd_iprintf(buffer, + "serial#=%d\n_hw version %c%d\nDSP code version %03d\n", + asihpi->serial_number, ((version >> 3) & 0xf) + 'A', + version & 0x7, + ((version >> 13) * 100) + ((version >> 7) & 0x3f)); + + err = hpi_mixer_get_control(ss, asihpi->h_mixer, + HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0, + HPI_CONTROL_SAMPLECLOCK, &h_control); + + if (!err) { + err = hpi_sample_clock_get_sample_rate(ss, + h_control, &rate); + err += hpi_sample_clock_get_source(ss, h_control, &source); + + if (!err) + snd_iprintf(buffer, "sample_clock=%d_hz, source %s\n", + rate, sampleclock_sources[source]); + } + +} + + +static void __devinit snd_asihpi_proc_init(struct snd_card_asihpi *asihpi) +{ + struct snd_info_entry *entry; + + if (!snd_card_proc_new(asihpi->card, "info", &entry)) + snd_info_set_text_ops(entry, asihpi, snd_asihpi_proc_read); +} + +/*------------------------------------------------------------ + HWDEP + ------------------------------------------------------------*/ + +static int snd_asihpi_hpi_open(struct snd_hwdep *hw, struct file *file) +{ + if (enable_hpi_hwdep) + return 0; + else + return -ENODEV; + +} + +static int snd_asihpi_hpi_release(struct snd_hwdep *hw, struct file *file) +{ + if (enable_hpi_hwdep) + return asihpi_hpi_release(file); + else + return -ENODEV; +} + +static int snd_asihpi_hpi_ioctl(struct snd_hwdep *hw, struct file *file, + unsigned int cmd, unsigned long arg) +{ + if (enable_hpi_hwdep) + return asihpi_hpi_ioctl(file, cmd, arg); + else + return -ENODEV; +} + + +/* results in /dev/snd/hwC#D0 file for each card with index # + also /proc/asound/hwdep will contain '#-00: asihpi (HPI) for each card' +*/ +static int __devinit snd_asihpi_hpi_new(struct snd_card_asihpi *asihpi, + int device, struct snd_hwdep **rhwdep) +{ + struct snd_hwdep *hw; + int err; + + if (rhwdep) + *rhwdep = NULL; + err = snd_hwdep_new(asihpi->card, "HPI", device, &hw); + if (err < 0) + return err; + strcpy(hw->name, "asihpi (HPI)"); + hw->iface = SNDRV_HWDEP_IFACE_LAST; + hw->ops.open = snd_asihpi_hpi_open; + hw->ops.ioctl = snd_asihpi_hpi_ioctl; + hw->ops.release = snd_asihpi_hpi_release; + hw->private_data = asihpi; + if (rhwdep) + *rhwdep = hw; + return 0; +} + +/*------------------------------------------------------------ + CARD + ------------------------------------------------------------*/ +static int __devinit snd_asihpi_probe(struct pci_dev *pci_dev, + const struct pci_device_id *pci_id) +{ + int err; + + u16 version; + int pcm_substreams; + + struct hpi_adapter *hpi_card; + struct snd_card *card; + struct snd_card_asihpi *asihpi; + + u32 h_control; + u32 h_stream; + + static int dev; + if (dev >= SNDRV_CARDS) + return -ENODEV; + + /* Should this be enable[hpi_card->index] ? */ + if (!enable[dev]) { + dev++; + return -ENOENT; + } + + err = asihpi_adapter_probe(pci_dev, pci_id); + if (err < 0) + return err; + + hpi_card = pci_get_drvdata(pci_dev); + /* first try to give the card the same index as its hardware index */ + err = snd_card_create(hpi_card->index, + id[hpi_card->index], THIS_MODULE, + sizeof(struct snd_card_asihpi), + &card); + if (err < 0) { + /* if that fails, try the default index==next available */ + err = + snd_card_create(index[dev], id[dev], + THIS_MODULE, + sizeof(struct snd_card_asihpi), + &card); + if (err < 0) + return err; + snd_printk(KERN_WARNING + "**** WARNING **** adapter index %d->ALSA index %d\n", + hpi_card->index, card->number); + } + + asihpi = (struct snd_card_asihpi *) card->private_data; + asihpi->card = card; + asihpi->pci = hpi_card->pci; + asihpi->adapter_index = hpi_card->index; + hpi_handle_error(hpi_adapter_get_info(ss, + asihpi->adapter_index, + &asihpi->num_outstreams, + &asihpi->num_instreams, + &asihpi->version, + &asihpi->serial_number, &asihpi->type)); + + version = asihpi->version; + snd_printk(KERN_INFO "adapter ID=%4X index=%d num_outstreams=%d " + "num_instreams=%d S/N=%d\n" + "hw version %c%d DSP code version %03d\n", + asihpi->type, asihpi->adapter_index, + asihpi->num_outstreams, + asihpi->num_instreams, asihpi->serial_number, + ((version >> 3) & 0xf) + 'A', + version & 0x7, + ((version >> 13) * 100) + ((version >> 7) & 0x3f)); + + pcm_substreams = asihpi->num_outstreams; + if (pcm_substreams < asihpi->num_instreams) + pcm_substreams = asihpi->num_instreams; + + err = hpi_adapter_get_property(ss, asihpi->adapter_index, + HPI_ADAPTER_PROPERTY_CAPS1, + NULL, &asihpi->support_grouping); + if (err) + asihpi->support_grouping = 0; + + err = hpi_adapter_get_property(ss, asihpi->adapter_index, + HPI_ADAPTER_PROPERTY_CAPS2, + &asihpi->support_mrx, NULL); + if (err) + asihpi->support_mrx = 0; + + err = hpi_adapter_get_property(ss, asihpi->adapter_index, + HPI_ADAPTER_PROPERTY_INTERVAL, + NULL, &asihpi->update_interval_frames); + if (err) + asihpi->update_interval_frames = 512; + + hpi_handle_error(hpi_instream_open(ss, asihpi->adapter_index, + 0, &h_stream)); + + err = hpi_instream_host_buffer_free(ss, h_stream); + asihpi->support_mmap = (!err); + + hpi_handle_error(hpi_instream_close(ss, h_stream)); + + err = hpi_adapter_get_property(ss, asihpi->adapter_index, + HPI_ADAPTER_PROPERTY_CURCHANNELS, + &asihpi->in_max_chans, &asihpi->out_max_chans); + if (err) { + asihpi->in_max_chans = 2; + asihpi->out_max_chans = 2; + } + + snd_printk(KERN_INFO "supports mmap:%d grouping:%d mrx:%d\n", + asihpi->support_mmap, + asihpi->support_grouping, + asihpi->support_mrx + ); + + + err = snd_card_asihpi_pcm_new(asihpi, 0, pcm_substreams); + if (err < 0) { + snd_printk(KERN_ERR "pcm_new failed\n"); + goto __nodev; + } + err = snd_card_asihpi_mixer_new(asihpi); + if (err < 0) { + snd_printk(KERN_ERR "mixer_new failed\n"); + goto __nodev; + } + + err = hpi_mixer_get_control(ss, asihpi->h_mixer, + HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0, + HPI_CONTROL_SAMPLECLOCK, &h_control); + + if (!err) + err = hpi_sample_clock_set_local_rate( + ss, h_control, adapter_fs); + + snd_asihpi_proc_init(asihpi); + + /* always create, can be enabled or disabled dynamically + by enable_hwdep module param*/ + snd_asihpi_hpi_new(asihpi, 0, NULL); + + if (asihpi->support_mmap) + strcpy(card->driver, "ASIHPI-MMAP"); + else + strcpy(card->driver, "ASIHPI"); + + sprintf(card->shortname, "AudioScience ASI%4X", asihpi->type); + sprintf(card->longname, "%s %i", + card->shortname, asihpi->adapter_index); + err = snd_card_register(card); + if (!err) { + hpi_card->snd_card_asihpi = card; + dev++; + return 0; + } +__nodev: + snd_card_free(card); + snd_printk(KERN_ERR "snd_asihpi_probe error %d\n", err); + return err; + +} + +static void __devexit snd_asihpi_remove(struct pci_dev *pci_dev) +{ + struct hpi_adapter *hpi_card = pci_get_drvdata(pci_dev); + + snd_card_free(hpi_card->snd_card_asihpi); + hpi_card->snd_card_asihpi = NULL; + asihpi_adapter_remove(pci_dev); +} + +static DEFINE_PCI_DEVICE_TABLE(asihpi_pci_tbl) = { + {HPI_PCI_VENDOR_ID_TI, HPI_PCI_DEV_ID_DSP6205, + HPI_PCI_VENDOR_ID_AUDIOSCIENCE, PCI_ANY_ID, 0, 0, + (kernel_ulong_t)HPI_6205}, + {HPI_PCI_VENDOR_ID_TI, HPI_PCI_DEV_ID_PCI2040, + HPI_PCI_VENDOR_ID_AUDIOSCIENCE, PCI_ANY_ID, 0, 0, + (kernel_ulong_t)HPI_6000}, + {0,} +}; +MODULE_DEVICE_TABLE(pci, asihpi_pci_tbl); + +static struct pci_driver driver = { + .name = "asihpi", + .id_table = asihpi_pci_tbl, + .probe = snd_asihpi_probe, + .remove = __devexit_p(snd_asihpi_remove), +#ifdef CONFIG_PM +/* .suspend = snd_asihpi_suspend, + .resume = snd_asihpi_resume, */ +#endif +}; + +static int __init snd_asihpi_init(void) +{ + asihpi_init(); + return pci_register_driver(&driver); +} + +static void __exit snd_asihpi_exit(void) +{ + + pci_unregister_driver(&driver); + asihpi_exit(); +} + +module_init(snd_asihpi_init) +module_exit(snd_asihpi_exit) + diff --git a/sound/pci/asihpi/hpi.h b/sound/pci/asihpi/hpi.h new file mode 100644 index 000000000000..99400de6c075 --- /dev/null +++ b/sound/pci/asihpi/hpi.h @@ -0,0 +1,2001 @@ +/****************************************************************************** + + AudioScience HPI driver + Copyright (C) 1997-2010 AudioScience Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation; + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ +/** \file hpi.h + + AudioScience Hardware Programming Interface (HPI) + public API definition. + + The HPI is a low-level hardware abstraction layer to all + AudioScience digital audio adapters +*/ +/* + You must define one operating system that the HPI is to be compiled under + HPI_OS_WIN32_USER 32bit Windows + HPI_OS_DSP_C6000 DSP TI C6000 (automatically set) + HPI_OS_WDM Windows WDM kernel driver + HPI_OS_LINUX Linux userspace + HPI_OS_LINUX_KERNEL Linux kernel (automatically set) + +(C) Copyright AudioScience Inc. 1998-2010 +******************************************************************************/ +#ifndef _HPI_H_ +#define _HPI_H_ +/* HPI Version +If HPI_VER_MINOR is odd then its a development release not intended for the +public. If HPI_VER_MINOR is even then is a release version +i.e 3.05.02 is a development version +*/ +#define HPI_VERSION_CONSTRUCTOR(maj, min, rel) \ + ((maj << 16) + (min << 8) + rel) + +#define HPI_VER_MAJOR(v) ((int)(v >> 16)) +#define HPI_VER_MINOR(v) ((int)((v >> 8) & 0xFF)) +#define HPI_VER_RELEASE(v) ((int)(v & 0xFF)) + +/* Use single digits for versions less that 10 to avoid octal. */ +#define HPI_VER HPI_VERSION_CONSTRUCTOR(4L, 3, 18) + +/* Library version as documented in hpi-api-versions.txt */ +#define HPI_LIB_VER HPI_VERSION_CONSTRUCTOR(9, 0, 0) + +#include +#define HPI_EXCLUDE_DEPRECATED + +/******************************************************************************/ +/******************************************************************************/ +/******** HPI API DEFINITIONS *****/ +/******************************************************************************/ +/******************************************************************************/ +/*******************************************/ +/** Audio format types +\ingroup stream +*/ +enum HPI_FORMATS { +/** Used internally on adapter. */ + HPI_FORMAT_MIXER_NATIVE = 0, +/** 8-bit unsigned PCM. Windows equivalent is WAVE_FORMAT_PCM. */ + HPI_FORMAT_PCM8_UNSIGNED = 1, +/** 16-bit signed PCM. Windows equivalent is WAVE_FORMAT_PCM. */ + HPI_FORMAT_PCM16_SIGNED = 2, +/** MPEG-1 Layer-1. */ + HPI_FORMAT_MPEG_L1 = 3, +/** MPEG-1 Layer-2. + +Windows equivalent is WAVE_FORMAT_MPEG. + +The following table shows what combinations of mode and bitrate are possible: + + + +

Bitrate (kbs)

+

Mono

+

Stereo,
Joint Stereo or
Dual Channel

+ +
32X_ +
40__ +
48X_ +
56X_ +
64XX +
80X_ +
96XX +
112XX +
128XX +
160XX +
192XX +
224_X +
256-X +
320-X +
384_X +
+*/ + HPI_FORMAT_MPEG_L2 = 4, +/** MPEG-1 Layer-3. +Windows equivalent is WAVE_FORMAT_MPEG. + +The following table shows what combinations of mode and bitrate are possible: + + + +

Bitrate (kbs)

+

Mono
Stereo @ 8,
11.025 and
12kHz*

+

Mono
Stereo @ 16,
22.050 and
24kHz*

+

Mono
Stereo @ 32,
44.1 and
48kHz

+ +
16XX_ +
24XX_ +
32XXX +
40XXX +
48XXX +
56XXX +
64XXX +
80_XX +
96_XX +
112_XX +
128_XX +
144_X_ +
160_XX +
192__X +
224__X +
256-_X +
320-_X +
+\b * Available on the ASI6000 series only +*/ + HPI_FORMAT_MPEG_L3 = 5, +/** Dolby AC-2. */ + HPI_FORMAT_DOLBY_AC2 = 6, +/** Dolbt AC-3. */ + HPI_FORMAT_DOLBY_AC3 = 7, +/** 16-bit PCM big-endian. */ + HPI_FORMAT_PCM16_BIGENDIAN = 8, +/** TAGIT-1 algorithm - hits. */ + HPI_FORMAT_AA_TAGIT1_HITS = 9, +/** TAGIT-1 algorithm - inserts. */ + HPI_FORMAT_AA_TAGIT1_INSERTS = 10, +/** 32-bit signed PCM. Windows equivalent is WAVE_FORMAT_PCM. +Each sample is a 32bit word. The most significant 24 bits contain a 24-bit +sample and the least significant 8 bits are set to 0. +*/ + HPI_FORMAT_PCM32_SIGNED = 11, +/** Raw bitstream - unknown format. */ + HPI_FORMAT_RAW_BITSTREAM = 12, +/** TAGIT-1 algorithm hits - extended. */ + HPI_FORMAT_AA_TAGIT1_HITS_EX1 = 13, +/** 32-bit PCM as an IEEE float. Windows equivalent is WAVE_FORMAT_IEEE_FLOAT. +Each sample is a 32bit word in IEEE754 floating point format. +The range is +1.0 to -1.0, which corresponds to digital fullscale. +*/ + HPI_FORMAT_PCM32_FLOAT = 14, +/** 24-bit PCM signed. Windows equivalent is WAVE_FORMAT_PCM. */ + HPI_FORMAT_PCM24_SIGNED = 15, +/** OEM format 1 - private. */ + HPI_FORMAT_OEM1 = 16, +/** OEM format 2 - private. */ + HPI_FORMAT_OEM2 = 17, +/** Undefined format. */ + HPI_FORMAT_UNDEFINED = 0xffff +}; + +/******************************************* in/out Stream states */ +/*******************************************/ +/** Stream States +\ingroup stream +*/ +enum HPI_STREAM_STATES { + /** State stopped - stream is stopped. */ + HPI_STATE_STOPPED = 1, + /** State playing - stream is playing audio. */ + HPI_STATE_PLAYING = 2, + /** State recording - stream is recording. */ + HPI_STATE_RECORDING = 3, + /** State drained - playing stream ran out of data to play. */ + HPI_STATE_DRAINED = 4, + /** State generate sine - to be implemented. */ + HPI_STATE_SINEGEN = 5, + /** State wait - used for inter-card sync to mean waiting for all + cards to be ready. */ + HPI_STATE_WAIT = 6 +}; +/******************************************* mixer source node types */ +/** Source node types +\ingroup mixer +*/ +enum HPI_SOURCENODES { + /** This define can be used instead of 0 to indicate + that there is no valid source node. A control that + exists on a destination node can be searched for using a source + node value of either 0, or HPI_SOURCENODE_NONE */ + HPI_SOURCENODE_NONE = 100, + /** \deprecated Use HPI_SOURCENODE_NONE instead. */ + HPI_SOURCENODE_BASE = 100, + /** Out Stream (Play) node. */ + HPI_SOURCENODE_OSTREAM = 101, + /** Line in node - could be analog, AES/EBU or network. */ + HPI_SOURCENODE_LINEIN = 102, + HPI_SOURCENODE_AESEBU_IN = 103, /**< AES/EBU input node. */ + HPI_SOURCENODE_TUNER = 104, /**< tuner node. */ + HPI_SOURCENODE_RF = 105, /**< RF input node. */ + HPI_SOURCENODE_CLOCK_SOURCE = 106, /**< clock source node. */ + HPI_SOURCENODE_RAW_BITSTREAM = 107, /**< raw bitstream node. */ + HPI_SOURCENODE_MICROPHONE = 108, /**< microphone node. */ + /** Cobranet input node - + Audio samples come from the Cobranet network and into the device. */ + HPI_SOURCENODE_COBRANET = 109, + HPI_SOURCENODE_ANALOG = 110, /**< analog input node. */ + HPI_SOURCENODE_ADAPTER = 111, /**< adapter node. */ + /* !!!Update this AND hpidebug.h if you add a new sourcenode type!!! */ + HPI_SOURCENODE_LAST_INDEX = 111 /**< largest ID */ + /* AX6 max sourcenode types = 15 */ +}; + +/******************************************* mixer dest node types */ +/** Destination node types +\ingroup mixer +*/ +enum HPI_DESTNODES { + /** This define can be used instead of 0 to indicate + that there is no valid destination node. A control that + exists on a source node can be searched for using a destination + node value of either 0, or HPI_DESTNODE_NONE */ + HPI_DESTNODE_NONE = 200, + /** \deprecated Use HPI_DESTNODE_NONE instead. */ + HPI_DESTNODE_BASE = 200, + /** In Stream (Record) node. */ + HPI_DESTNODE_ISTREAM = 201, + HPI_DESTNODE_LINEOUT = 202, /**< line out node. */ + HPI_DESTNODE_AESEBU_OUT = 203, /**< AES/EBU output node. */ + HPI_DESTNODE_RF = 204, /**< RF output node. */ + HPI_DESTNODE_SPEAKER = 205, /**< speaker output node. */ + /** Cobranet output node - + Audio samples from the device are sent out on the Cobranet network.*/ + HPI_DESTNODE_COBRANET = 206, + HPI_DESTNODE_ANALOG = 207, /**< analog output node. */ + + /* !!!Update this AND hpidebug.h if you add a new destnode type!!! */ + HPI_DESTNODE_LAST_INDEX = 207 /**< largest ID */ + /* AX6 max destnode types = 15 */ +}; + +/*******************************************/ +/** Mixer control types +\ingroup mixer +*/ +enum HPI_CONTROLS { + HPI_CONTROL_GENERIC = 0, /**< generic control. */ + HPI_CONTROL_CONNECTION = 1, /**< A connection between nodes. */ + HPI_CONTROL_VOLUME = 2, /**< volume control - works in dB_fs. */ + HPI_CONTROL_METER = 3, /**< peak meter control. */ + HPI_CONTROL_MUTE = 4, /*mute control - not used at present. */ + HPI_CONTROL_MULTIPLEXER = 5, /**< multiplexer control. */ + + HPI_CONTROL_AESEBU_TRANSMITTER = 6, /**< AES/EBU transmitter control. */ + HPI_CONTROL_AESEBUTX = HPI_CONTROL_AESEBU_TRANSMITTER, + + HPI_CONTROL_AESEBU_RECEIVER = 7, /**< AES/EBU receiver control. */ + HPI_CONTROL_AESEBURX = HPI_CONTROL_AESEBU_RECEIVER, + + HPI_CONTROL_LEVEL = 8, /**< level/trim control - works in d_bu. */ + HPI_CONTROL_TUNER = 9, /**< tuner control. */ +/* HPI_CONTROL_ONOFFSWITCH = 10 */ + HPI_CONTROL_VOX = 11, /**< vox control. */ +/* HPI_CONTROL_AES18_TRANSMITTER = 12 */ +/* HPI_CONTROL_AES18_RECEIVER = 13 */ +/* HPI_CONTROL_AES18_BLOCKGENERATOR = 14 */ + HPI_CONTROL_CHANNEL_MODE = 15, /**< channel mode control. */ + + HPI_CONTROL_BITSTREAM = 16, /**< bitstream control. */ + HPI_CONTROL_SAMPLECLOCK = 17, /**< sample clock control. */ + HPI_CONTROL_MICROPHONE = 18, /**< microphone control. */ + HPI_CONTROL_PARAMETRIC_EQ = 19, /**< parametric EQ control. */ + HPI_CONTROL_EQUALIZER = HPI_CONTROL_PARAMETRIC_EQ, + + HPI_CONTROL_COMPANDER = 20, /**< compander control. */ + HPI_CONTROL_COBRANET = 21, /**< cobranet control. */ + HPI_CONTROL_TONEDETECTOR = 22, /**< tone detector control. */ + HPI_CONTROL_SILENCEDETECTOR = 23, /**< silence detector control. */ + HPI_CONTROL_PAD = 24, /**< tuner PAD control. */ + HPI_CONTROL_SRC = 25, /**< samplerate converter control. */ + HPI_CONTROL_UNIVERSAL = 26, /**< universal control. */ + +/* !!! Update this AND hpidebug.h if you add a new control type!!!*/ + HPI_CONTROL_LAST_INDEX = 26 /** 4 line outs = 1 to 8 channel streams), + 4 lineins -> 1 instream (1 to 8 channel streams) at 0-48kHz. + For more info see the SSX Specification. +*/ + HPI_ADAPTER_MODE_MULTICHANNEL = 9, + +/** 12 outstream mode. +- ASI6514, ASI6614: 2 instreams +- ASI6540,ASI6544: 8 instreams +- ASI6640,ASI6644: 8 instreams +*/ + HPI_ADAPTER_MODE_12OSTREAM = 10, + +/** 9 outstream mode. +- ASI6044: 8 instreams +*/ + HPI_ADAPTER_MODE_9OSTREAM = 11, + +/** mono mode. +- ASI6416: 16 outstreams/instreams +- ASI5402: 2 outstreams/instreams +*/ + HPI_ADAPTER_MODE_MONO = 12, + +/** Low latency mode. +- ASI6416/ASI6316: 1 16 channel outstream and instream +*/ + HPI_ADAPTER_MODE_LOW_LATENCY = 13 +}; + +/* Note, adapters can have more than one capability - +encoding as bitfield is recommended. */ +#define HPI_CAPABILITY_NONE (0) +#define HPI_CAPABILITY_MPEG_LAYER3 (1) + +/* Set this equal to maximum capability index, +Must not be greater than 32 - see axnvdef.h */ +#define HPI_CAPABILITY_MAX 1 +/* #define HPI_CAPABILITY_AAC 2 */ + +/******************************************* STREAM ATTRIBUTES ****/ + +/** MPEG Ancillary Data modes + +The mode for the ancillary data insertion or extraction to operate in. +\ingroup stream +*/ +enum HPI_MPEG_ANC_MODES { + /** the MPEG frames have energy information stored in them (5 bytes per stereo frame, 3 per mono) */ + HPI_MPEG_ANC_HASENERGY = 0, + /** the entire ancillary data field is taken up by data from the Anc data buffer + On encode, the encoder will insert the energy bytes before filling the remainder + of the ancillary data space with data from the ancillary data buffer. + */ + HPI_MPEG_ANC_RAW = 1 +}; + +/** Ancillary Data Alignment +\ingroup instream +*/ +enum HPI_ISTREAM_MPEG_ANC_ALIGNS { + /** data is packed against the end of data, then padded to the end of frame */ + HPI_MPEG_ANC_ALIGN_LEFT = 0, + /** data is packed against the end of the frame */ + HPI_MPEG_ANC_ALIGN_RIGHT = 1 +}; + +/** MPEG modes +MPEG modes - can be used optionally for HPI_FormatCreate() +parameter dwAttributes. + +Using any mode setting other than HPI_MPEG_MODE_DEFAULT +with single channel format will return an error. +\ingroup stream +*/ +enum HPI_MPEG_MODES { +/** Causes the MPEG-1 Layer II bitstream to be recorded +in single_channel mode when the number of channels is 1 and in stereo when the +number of channels is 2. */ + HPI_MPEG_MODE_DEFAULT = 0, + /** Standard stereo without joint-stereo compression */ + HPI_MPEG_MODE_STEREO = 1, + /** Joint stereo */ + HPI_MPEG_MODE_JOINTSTEREO = 2, + /** Left and Right channels are completely independent */ + HPI_MPEG_MODE_DUALCHANNEL = 3 +}; +/******************************************* MIXER ATTRIBUTES ****/ + +/* \defgroup mixer_flags Mixer flags for HPI_MIXER_GET_CONTROL_MULTIPLE_VALUES +{ +*/ +#define HPI_MIXER_GET_CONTROL_MULTIPLE_CHANGED (0) +#define HPI_MIXER_GET_CONTROL_MULTIPLE_RESET (1) +/*}*/ + +/** Commands used by HPI_MixerStore() +\ingroup mixer +*/ +enum HPI_MIXER_STORE_COMMAND { +/** Save all mixer control settings. */ + HPI_MIXER_STORE_SAVE = 1, +/** Restore all controls from saved. */ + HPI_MIXER_STORE_RESTORE = 2, +/** Delete saved control settings. */ + HPI_MIXER_STORE_DELETE = 3, +/** Enable auto storage of some control settings. */ + HPI_MIXER_STORE_ENABLE = 4, +/** Disable auto storage of some control settings. */ + HPI_MIXER_STORE_DISABLE = 5, +/** Save the attributes of a single control. */ + HPI_MIXER_STORE_SAVE_SINGLE = 6 +}; + +/************************************* CONTROL ATTRIBUTE VALUES ****/ +/** Used by mixer plugin enable functions + +E.g. HPI_ParametricEQ_SetState() +\ingroup mixer +*/ +enum HPI_SWITCH_STATES { + HPI_SWITCH_OFF = 0, /**< turn the mixer plugin on. */ + HPI_SWITCH_ON = 1 /**< turn the mixer plugin off. */ +}; + +/* Volume control special gain values */ +/** volumes units are 100ths of a dB +\ingroup volume +*/ +#define HPI_UNITS_PER_dB 100 +/** turns volume control OFF or MUTE +\ingroup volume +*/ +#define HPI_GAIN_OFF (-100 * HPI_UNITS_PER_dB) + +/** value returned for no signal +\ingroup meter +*/ +#define HPI_METER_MINIMUM (-150 * HPI_UNITS_PER_dB) + +/** autofade profiles +\ingroup volume +*/ +enum HPI_VOLUME_AUTOFADES { +/** log fade - dB attenuation changes linearly over time */ + HPI_VOLUME_AUTOFADE_LOG = 2, +/** linear fade - amplitude changes linearly */ + HPI_VOLUME_AUTOFADE_LINEAR = 3 +}; + +/** The physical encoding format of the AESEBU I/O. + +Used in HPI_AESEBU_Transmitter_SetFormat(), HPI_AESEBU_Receiver_SetFormat() +along with related Get and Query functions +\ingroup aestx +*/ +enum HPI_AESEBU_FORMATS { +/** AES/EBU physical format - AES/EBU balanced "professional" */ + HPI_AESEBU_FORMAT_AESEBU = 1, +/** AES/EBU physical format - S/PDIF unbalanced "consumer" */ + HPI_AESEBU_FORMAT_SPDIF = 2 +}; + +/** AES/EBU error status bits + +Returned by HPI_AESEBU_Receiver_GetErrorStatus() +\ingroup aesrx +*/ +enum HPI_AESEBU_ERRORS { +/** bit0: 1 when PLL is not locked */ + HPI_AESEBU_ERROR_NOT_LOCKED = 0x01, +/** bit1: 1 when signal quality is poor */ + HPI_AESEBU_ERROR_POOR_QUALITY = 0x02, +/** bit2: 1 when there is a parity error */ + HPI_AESEBU_ERROR_PARITY_ERROR = 0x04, +/** bit3: 1 when there is a bi-phase coding violation */ + HPI_AESEBU_ERROR_BIPHASE_VIOLATION = 0x08, +/** bit4: 1 when the validity bit is high */ + HPI_AESEBU_ERROR_VALIDITY = 0x10, +/** bit5: 1 when the CRC error bit is high */ + HPI_AESEBU_ERROR_CRC = 0x20 +}; + +/** \addtogroup pad +\{ +*/ +/** The text string containing the station/channel combination. */ +#define HPI_PAD_CHANNEL_NAME_LEN 16 +/** The text string containing the artist. */ +#define HPI_PAD_ARTIST_LEN 64 +/** The text string containing the title. */ +#define HPI_PAD_TITLE_LEN 64 +/** The text string containing the comment. */ +#define HPI_PAD_COMMENT_LEN 256 +/** The PTY when the tuner has not recieved any PTY. */ +#define HPI_PAD_PROGRAM_TYPE_INVALID 0xffff +/** \} */ + +/** Data types for PTY string translation. +\ingroup rds +*/ +enum eHPI_RDS_type { + HPI_RDS_DATATYPE_RDS = 0, /**< RDS bitstream.*/ + HPI_RDS_DATATYPE_RBDS = 1 /**< RBDS bitstream.*/ +}; + +/** Tuner bands + +Used for HPI_Tuner_SetBand(),HPI_Tuner_GetBand() +\ingroup tuner +*/ +enum HPI_TUNER_BAND { + HPI_TUNER_BAND_AM = 1, /**< AM band */ + HPI_TUNER_BAND_FM = 2, /**< FM band (mono) */ + HPI_TUNER_BAND_TV_NTSC_M = 3, /**< NTSC-M TV band*/ + HPI_TUNER_BAND_TV = 3, /* use TV_NTSC_M */ + HPI_TUNER_BAND_FM_STEREO = 4, /**< FM band (stereo) */ + HPI_TUNER_BAND_AUX = 5, /**< auxiliary input */ + HPI_TUNER_BAND_TV_PAL_BG = 6, /**< PAL-B/G TV band*/ + HPI_TUNER_BAND_TV_PAL_I = 7, /**< PAL-I TV band*/ + HPI_TUNER_BAND_TV_PAL_DK = 8, /**< PAL-D/K TV band*/ + HPI_TUNER_BAND_TV_SECAM_L = 9, /**< SECAM-L TV band*/ + HPI_TUNER_BAND_LAST = 9 /**< the index of the last tuner band. */ +}; + +/** Tuner mode attributes + +Used by HPI_Tuner_SetMode(), HPI_Tuner_GetMode() +\ingroup tuner + +*/ +enum HPI_TUNER_MODES { + HPI_TUNER_MODE_RSS = 1, /**< control RSS */ + HPI_TUNER_MODE_RDS = 2 /**< control RBDS/RDS */ +}; + +/** Tuner mode attribute values + +Used by HPI_Tuner_SetMode(), HPI_Tuner_GetMode() +\ingroup tuner +*/ +enum HPI_TUNER_MODE_VALUES { +/* RSS attribute values */ + HPI_TUNER_MODE_RSS_DISABLE = 0, /**< RSS disable */ + HPI_TUNER_MODE_RSS_ENABLE = 1, /**< RSS enable */ + +/* RDS mode attributes */ + HPI_TUNER_MODE_RDS_DISABLE = 0, /**< RDS - disabled */ + HPI_TUNER_MODE_RDS_RDS = 1, /**< RDS - RDS mode */ + HPI_TUNER_MODE_RDS_RBDS = 2 /**< RDS - RBDS mode */ +}; + +/** Tuner Level settings +\ingroup tuner +*/ +enum HPI_TUNER_LEVEL { + HPI_TUNER_LEVEL_AVERAGE = 0, + HPI_TUNER_LEVEL_RAW = 1 +}; + +/** Tuner Status Bits + +These bitfield values are returned by a call to HPI_Tuner_GetStatus(). +Multiple fields are returned from a single call. +\ingroup tuner +*/ +enum HPI_TUNER_STATUS_BITS { + HPI_TUNER_VIDEO_COLOR_PRESENT = 0x0001, /**< video color is present. */ + HPI_TUNER_VIDEO_IS_60HZ = 0x0020, /**< 60 hz video detected. */ + HPI_TUNER_VIDEO_HORZ_SYNC_MISSING = 0x0040, /**< video HSYNC is missing. */ + HPI_TUNER_VIDEO_STATUS_VALID = 0x0100, /**< video status is valid. */ + HPI_TUNER_PLL_LOCKED = 0x1000, /**< the tuner's PLL is locked. */ + HPI_TUNER_FM_STEREO = 0x2000, /**< tuner reports back FM stereo. */ + HPI_TUNER_DIGITAL = 0x0200, /**< tuner reports digital programming. */ + HPI_TUNER_MULTIPROGRAM = 0x0400 /**< tuner reports multiple programs. */ +}; + +/** Channel Modes +Used for HPI_ChannelModeSet/Get() +\ingroup channelmode +*/ +enum HPI_CHANNEL_MODES { +/** Left channel out = left channel in, Right channel out = right channel in. */ + HPI_CHANNEL_MODE_NORMAL = 1, +/** Left channel out = right channel in, Right channel out = left channel in. */ + HPI_CHANNEL_MODE_SWAP = 2, +/** Left channel out = left channel in, Right channel out = left channel in. */ + HPI_CHANNEL_MODE_LEFT_TO_STEREO = 3, +/** Left channel out = right channel in, Right channel out = right channel in.*/ + HPI_CHANNEL_MODE_RIGHT_TO_STEREO = 4, +/** Left channel out = (left channel in + right channel in)/2, + Right channel out = mute. */ + HPI_CHANNEL_MODE_STEREO_TO_LEFT = 5, +/** Left channel out = mute, + Right channel out = (right channel in + left channel in)/2. */ + HPI_CHANNEL_MODE_STEREO_TO_RIGHT = 6, + HPI_CHANNEL_MODE_LAST = 6 +}; + +/** SampleClock source values +\ingroup sampleclock +*/ +enum HPI_SAMPLECLOCK_SOURCES { +/** The sampleclock output is derived from its local samplerate generator. + The local samplerate may be set using HPI_SampleClock_SetLocalRate(). */ + HPI_SAMPLECLOCK_SOURCE_LOCAL = 1, +/** \deprecated Use HPI_SAMPLECLOCK_SOURCE_LOCAL instead */ + HPI_SAMPLECLOCK_SOURCE_ADAPTER = 1, +/** The adapter is clocked from a dedicated AES/EBU SampleClock input.*/ + HPI_SAMPLECLOCK_SOURCE_AESEBU_SYNC = 2, +/** From external wordclock connector */ + HPI_SAMPLECLOCK_SOURCE_WORD = 3, +/** Board-to-board header */ + HPI_SAMPLECLOCK_SOURCE_WORD_HEADER = 4, +/** FUTURE - SMPTE clock. */ + HPI_SAMPLECLOCK_SOURCE_SMPTE = 5, +/** One of the aesebu inputs */ + HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT = 6, +/** \deprecated The first aesebu input with a valid signal +Superseded by separate Auto enable flag +*/ + HPI_SAMPLECLOCK_SOURCE_AESEBU_AUTO = 7, +/** From a network interface e.g. Cobranet or Livewire at either 48 or 96kHz */ + HPI_SAMPLECLOCK_SOURCE_NETWORK = 8, +/** From previous adjacent module (ASI2416 only)*/ + HPI_SAMPLECLOCK_SOURCE_PREV_MODULE = 10, +/*! Update this if you add a new clock source.*/ + HPI_SAMPLECLOCK_SOURCE_LAST = 10 +}; + +/** Equalizer filter types. Used by HPI_ParametricEQ_SetBand() +\ingroup parmeq +*/ +enum HPI_FILTER_TYPE { + HPI_FILTER_TYPE_BYPASS = 0, /**< filter is turned off */ + + HPI_FILTER_TYPE_LOWSHELF = 1, /**< EQ low shelf */ + HPI_FILTER_TYPE_HIGHSHELF = 2, /**< EQ high shelf */ + HPI_FILTER_TYPE_EQ_BAND = 3, /**< EQ gain */ + + HPI_FILTER_TYPE_LOWPASS = 4, /**< standard low pass */ + HPI_FILTER_TYPE_HIGHPASS = 5, /**< standard high pass */ + HPI_FILTER_TYPE_BANDPASS = 6, /**< standard band pass */ + HPI_FILTER_TYPE_BANDSTOP = 7 /**< standard band stop/notch */ +}; + +/** Async Event sources +\ingroup async +*/ +enum ASYNC_EVENT_SOURCES { + HPI_ASYNC_EVENT_GPIO = 1, /**< GPIO event. */ + HPI_ASYNC_EVENT_SILENCE = 2, /**< silence event detected. */ + HPI_ASYNC_EVENT_TONE = 3 /**< tone event detected. */ +}; +/*******************************************/ +/** HPI Error codes + +Almost all HPI functions return an error code +A return value of zero means there was no error. +Otherwise one of these error codes is returned. +Error codes can be converted to a descriptive string using HPI_GetErrorText() + +\note When a new error code is added HPI_GetErrorText() MUST be updated. +\note Codes 1-100 are reserved for driver use +\ingroup utility +*/ +enum HPI_ERROR_CODES { + /** Message type does not exist. */ + HPI_ERROR_INVALID_TYPE = 100, + /** Object type does not exist. */ + HPI_ERROR_INVALID_OBJ = 101, + /** Function does not exist. */ + HPI_ERROR_INVALID_FUNC = 102, + /** The specified object (adapter/Stream) does not exist. */ + HPI_ERROR_INVALID_OBJ_INDEX = 103, + /** Trying to access an object that has not been opened yet. */ + HPI_ERROR_OBJ_NOT_OPEN = 104, + /** Trying to open an already open object. */ + HPI_ERROR_OBJ_ALREADY_OPEN = 105, + /** PCI, ISA resource not valid. */ + HPI_ERROR_INVALID_RESOURCE = 106, + /** GetInfo call from SubSysFindAdapters failed. */ + HPI_ERROR_SUBSYSFINDADAPTERS_GETINFO = 107, + /** Default response was never updated with actual error code. */ + HPI_ERROR_INVALID_RESPONSE = 108, + /** wSize field of response was not updated, + indicating that the message was not processed. */ + HPI_ERROR_PROCESSING_MESSAGE = 109, + /** The network did not respond in a timely manner. */ + HPI_ERROR_NETWORK_TIMEOUT = 110, + /** An HPI handle is invalid (uninitialised?). */ + HPI_ERROR_INVALID_HANDLE = 111, + /** A function or attribute has not been implemented yet. */ + HPI_ERROR_UNIMPLEMENTED = 112, + /** There are too many clients attempting to access a network resource. */ + HPI_ERROR_NETWORK_TOO_MANY_CLIENTS = 113, + /** Response buffer passed to HPI_Message was smaller than returned response */ + HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL = 114, + /** The returned response did not match the sent message */ + HPI_ERROR_RESPONSE_MISMATCH = 115, + + /** Too many adapters.*/ + HPI_ERROR_TOO_MANY_ADAPTERS = 200, + /** Bad adpater. */ + HPI_ERROR_BAD_ADAPTER = 201, + /** Adapter number out of range or not set properly. */ + HPI_ERROR_BAD_ADAPTER_NUMBER = 202, + /** 2 adapters with the same adapter number. */ + HPI_DUPLICATE_ADAPTER_NUMBER = 203, + /** DSP code failed to bootload. */ + HPI_ERROR_DSP_BOOTLOAD = 204, + /** Adapter failed DSP code self test. */ + HPI_ERROR_DSP_SELFTEST = 205, + /** Couldn't find or open the DSP code file. */ + HPI_ERROR_DSP_FILE_NOT_FOUND = 206, + /** Internal DSP hardware error. */ + HPI_ERROR_DSP_HARDWARE = 207, + /** Could not allocate memory in DOS. */ + HPI_ERROR_DOS_MEMORY_ALLOC = 208, + /** Could not allocate memory */ + HPI_ERROR_MEMORY_ALLOC = 208, + /** Failed to correctly load/config PLD .*/ + HPI_ERROR_PLD_LOAD = 209, + /** Unexpected end of file, block length too big etc. */ + HPI_ERROR_DSP_FILE_FORMAT = 210, + + /** Found but could not open DSP code file. */ + HPI_ERROR_DSP_FILE_ACCESS_DENIED = 211, + /** First DSP code section header not found in DSP file. */ + HPI_ERROR_DSP_FILE_NO_HEADER = 212, + /** File read operation on DSP code file failed. */ + HPI_ERROR_DSP_FILE_READ_ERROR = 213, + /** DSP code for adapter family not found. */ + HPI_ERROR_DSP_SECTION_NOT_FOUND = 214, + /** Other OS specific error opening DSP file. */ + HPI_ERROR_DSP_FILE_OTHER_ERROR = 215, + /** Sharing violation opening DSP code file. */ + HPI_ERROR_DSP_FILE_SHARING_VIOLATION = 216, + /** DSP code section header had size == 0. */ + HPI_ERROR_DSP_FILE_NULL_HEADER = 217, + + /** Base number for flash errors. */ + HPI_ERROR_FLASH = 220, + + /** Flash has bad checksum */ + HPI_ERROR_BAD_CHECKSUM = (HPI_ERROR_FLASH + 1), + HPI_ERROR_BAD_SEQUENCE = (HPI_ERROR_FLASH + 2), + HPI_ERROR_FLASH_ERASE = (HPI_ERROR_FLASH + 3), + HPI_ERROR_FLASH_PROGRAM = (HPI_ERROR_FLASH + 4), + HPI_ERROR_FLASH_VERIFY = (HPI_ERROR_FLASH + 5), + HPI_ERROR_FLASH_TYPE = (HPI_ERROR_FLASH + 6), + HPI_ERROR_FLASH_START = (HPI_ERROR_FLASH + 7), + + /** Reserved for OEMs. */ + HPI_ERROR_RESERVED_1 = 290, + + /** Stream does not exist. */ + HPI_ERROR_INVALID_STREAM = 300, + /** Invalid compression format. */ + HPI_ERROR_INVALID_FORMAT = 301, + /** Invalid format samplerate */ + HPI_ERROR_INVALID_SAMPLERATE = 302, + /** Invalid format number of channels. */ + HPI_ERROR_INVALID_CHANNELS = 303, + /** Invalid format bitrate. */ + HPI_ERROR_INVALID_BITRATE = 304, + /** Invalid datasize used for stream read/write. */ + HPI_ERROR_INVALID_DATASIZE = 305, + /** Stream buffer is full during stream write. */ + HPI_ERROR_BUFFER_FULL = 306, + /** Stream buffer is empty during stream read. */ + HPI_ERROR_BUFFER_EMPTY = 307, + /** Invalid datasize used for stream read/write. */ + HPI_ERROR_INVALID_DATA_TRANSFER = 308, + /** Packet ordering error for stream read/write. */ + HPI_ERROR_INVALID_PACKET_ORDER = 309, + + /** Object can't do requested operation in its current + state, eg set format, change rec mux state while recording.*/ + HPI_ERROR_INVALID_OPERATION = 310, + + /** Where an SRG is shared amongst streams, an incompatible samplerate is one + that is different to any currently playing or recording stream. */ + HPI_ERROR_INCOMPATIBLE_SAMPLERATE = 311, + /** Adapter mode is illegal.*/ + HPI_ERROR_BAD_ADAPTER_MODE = 312, + + /** There have been too many attempts to set the adapter's + capabilities (using bad keys), the card should be returned + to ASI if further capabilities updates are required */ + HPI_ERROR_TOO_MANY_CAPABILITY_CHANGE_ATTEMPTS = 313, + /** Streams on different adapters cannot be grouped. */ + HPI_ERROR_NO_INTERADAPTER_GROUPS = 314, + /** Streams on different DSPs cannot be grouped. */ + HPI_ERROR_NO_INTERDSP_GROUPS = 315, + + /** Invalid mixer node for this adapter. */ + HPI_ERROR_INVALID_NODE = 400, + /** Invalid control. */ + HPI_ERROR_INVALID_CONTROL = 401, + /** Invalid control value was passed. */ + HPI_ERROR_INVALID_CONTROL_VALUE = 402, + /** Control attribute not supported by this control. */ + HPI_ERROR_INVALID_CONTROL_ATTRIBUTE = 403, + /** Control is disabled. */ + HPI_ERROR_CONTROL_DISABLED = 404, + /** I2C transaction failed due to a missing ACK. */ + HPI_ERROR_CONTROL_I2C_MISSING_ACK = 405, + /** Control attribute is valid, but not supported by this hardware. */ + HPI_ERROR_UNSUPPORTED_CONTROL_ATTRIBUTE = 406, + /** Control is busy, or coming out of + reset and cannot be accessed at this time. */ + HPI_ERROR_CONTROL_NOT_READY = 407, + + /** Non volatile memory */ + HPI_ERROR_NVMEM_BUSY = 450, + HPI_ERROR_NVMEM_FULL = 451, + HPI_ERROR_NVMEM_FAIL = 452, + + /** I2C */ + HPI_ERROR_I2C_MISSING_ACK = HPI_ERROR_CONTROL_I2C_MISSING_ACK, + HPI_ERROR_I2C_BAD_ADR = 460, + + /** Entity errors */ + HPI_ERROR_ENTITY_TYPE_MISMATCH = 470, + HPI_ERROR_ENTITY_ITEM_COUNT = 471, + HPI_ERROR_ENTITY_TYPE_INVALID = 472, + HPI_ERROR_ENTITY_ROLE_INVALID = 473, + + /* AES18 specific errors were 500..507 */ + + /** custom error to use for debugging */ + HPI_ERROR_CUSTOM = 600, + + /** hpioct32.c can't obtain mutex */ + HPI_ERROR_MUTEX_TIMEOUT = 700, + + /** errors from HPI backends have values >= this */ + HPI_ERROR_BACKEND_BASE = 900, + + /** indicates a cached u16 value is invalid. */ + HPI_ERROR_ILLEGAL_CACHE_VALUE = 0xffff +}; + +/** \defgroup maximums HPI maximum values +\{ +*/ +/** Maximum number of adapters per HPI sub-system + WARNING: modifying this value changes the response structure size.*/ +#define HPI_MAX_ADAPTERS 20 +/** Maximum number of in or out streams per adapter */ +#define HPI_MAX_STREAMS 16 +#define HPI_MAX_CHANNELS 2 /* per stream */ +#define HPI_MAX_NODES 8 /* per mixer ? */ +#define HPI_MAX_CONTROLS 4 /* per node ? */ +/** maximum number of ancillary bytes per MPEG frame */ +#define HPI_MAX_ANC_BYTES_PER_FRAME (64) +#define HPI_STRING_LEN 16 + +/** Velocity units */ +#define HPI_OSTREAM_VELOCITY_UNITS 4096 +/** OutStream timescale units */ +#define HPI_OSTREAM_TIMESCALE_UNITS 10000 +/** OutStream timescale passthrough - turns timescaling on in passthough mode */ +#define HPI_OSTREAM_TIMESCALE_PASSTHROUGH 99999 + +/**\}*/ + +/* ////////////////////////////////////////////////////////////////////// */ +/* STRUCTURES */ +#ifndef DISABLE_PRAGMA_PACK1 +#pragma pack(push, 1) +#endif + +/** Structure containing sample format information. + See also HPI_FormatCreate(). + */ +struct hpi_format { + u32 sample_rate; + /**< 11025, 32000, 44100 ... */ + u32 bit_rate; /**< for MPEG */ + u32 attributes; + /**< Stereo/JointStereo/Mono */ + u16 mode_legacy; + /**< Legacy ancillary mode or idle bit */ + u16 unused; /**< unused */ + u16 channels; /**< 1,2..., (or ancillary mode or idle bit */ + u16 format; /**< HPI_FORMAT_PCM16, _MPEG etc. see #HPI_FORMATS. */ +}; + +struct hpi_anc_frame { + u32 valid_bits_in_this_frame; + u8 b_data[HPI_MAX_ANC_BYTES_PER_FRAME]; +}; + +/** An object for containing a single async event. +*/ +struct hpi_async_event { + u16 event_type; /**< type of event. \sa async_event */ + u16 sequence; /**< sequence number, allows lost event detection */ + u32 state; /**< new state */ + u32 h_object; /**< handle to the object returning the event. */ + union { + struct { + u16 index; /**< GPIO bit index. */ + } gpio; + struct { + u16 node_index; /**< what node is the control on ? */ + u16 node_type; /**< what type of node is the control on ? */ + } control; + } u; +}; + +/*/////////////////////////////////////////////////////////////////////////// */ +/* Public HPI Entity related definitions */ + +struct hpi_entity; + +enum e_entity_type { + entity_type_null, + entity_type_sequence, /* sequence of potentially heterogeneous TLV entities */ + + entity_type_reference, /* refers to a TLV entity or NULL */ + + entity_type_int, /* 32 bit */ + entity_type_float, /* ieee754 binary 32 bit encoding */ + entity_type_double, + + entity_type_cstring, + entity_type_octet, + entity_type_ip4_address, + entity_type_ip6_address, + entity_type_mac_address, + + LAST_ENTITY_TYPE +}; + +enum e_entity_role { + entity_role_null, + entity_role_value, + entity_role_classname, + + entity_role_units, + entity_role_flags, + entity_role_range, + + entity_role_mapping, + entity_role_enum, + + entity_role_instance_of, + entity_role_depends_on, + entity_role_member_of_group, + entity_role_value_constraint, + entity_role_parameter_port, + + entity_role_block, + entity_role_node_group, + entity_role_audio_port, + entity_role_clock_port, + LAST_ENTITY_ROLE +}; + +/* skip host side function declarations for + DSP compile and documentation extraction */ + +struct hpi_hsubsys { + int not_really_used; +}; + +#ifndef DISABLE_PRAGMA_PACK1 +#pragma pack(pop) +#endif + +/*////////////////////////////////////////////////////////////////////////// */ +/* HPI FUNCTIONS */ + +/*/////////////////////////// */ +/* DATA and FORMAT and STREAM */ + +u16 hpi_stream_estimate_buffer_size(struct hpi_format *pF, + u32 host_polling_rate_in_milli_seconds, u32 *recommended_buffer_size); + +/*/////////// */ +/* SUB SYSTEM */ +struct hpi_hsubsys *hpi_subsys_create(void + ); + +void hpi_subsys_free(const struct hpi_hsubsys *ph_subsys); + +u16 hpi_subsys_get_version(const struct hpi_hsubsys *ph_subsys, + u32 *pversion); + +u16 hpi_subsys_get_version_ex(const struct hpi_hsubsys *ph_subsys, + u32 *pversion_ex); + +u16 hpi_subsys_get_info(const struct hpi_hsubsys *ph_subsys, u32 *pversion, + u16 *pw_num_adapters, u16 aw_adapter_list[], u16 list_length); + +u16 hpi_subsys_find_adapters(const struct hpi_hsubsys *ph_subsys, + u16 *pw_num_adapters, u16 aw_adapter_list[], u16 list_length); + +u16 hpi_subsys_get_num_adapters(const struct hpi_hsubsys *ph_subsys, + int *pn_num_adapters); + +u16 hpi_subsys_get_adapter(const struct hpi_hsubsys *ph_subsys, int iterator, + u32 *padapter_index, u16 *pw_adapter_type); + +u16 hpi_subsys_ssx2_bypass(const struct hpi_hsubsys *ph_subsys, u16 bypass); + +u16 hpi_subsys_set_host_network_interface(const struct hpi_hsubsys *ph_subsys, + const char *sz_interface); + +/*///////// */ +/* ADAPTER */ + +u16 hpi_adapter_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index); + +u16 hpi_adapter_close(const struct hpi_hsubsys *ph_subsys, u16 adapter_index); + +u16 hpi_adapter_get_info(const struct hpi_hsubsys *ph_subsys, + u16 adapter_index, u16 *pw_num_outstreams, u16 *pw_num_instreams, + u16 *pw_version, u32 *pserial_number, u16 *pw_adapter_type); + +u16 hpi_adapter_get_module_by_index(const struct hpi_hsubsys *ph_subsys, + u16 adapter_index, u16 module_index, u16 *pw_num_outputs, + u16 *pw_num_inputs, u16 *pw_version, u32 *pserial_number, + u16 *pw_module_type, u32 *ph_module); + +u16 hpi_adapter_set_mode(const struct hpi_hsubsys *ph_subsys, + u16 adapter_index, u32 adapter_mode); + +u16 hpi_adapter_set_mode_ex(const struct hpi_hsubsys *ph_subsys, + u16 adapter_index, u32 adapter_mode, u16 query_or_set); + +u16 hpi_adapter_get_mode(const struct hpi_hsubsys *ph_subsys, + u16 adapter_index, u32 *padapter_mode); + +u16 hpi_adapter_get_assert(const struct hpi_hsubsys *ph_subsys, + u16 adapter_index, u16 *assert_present, char *psz_assert, + u16 *pw_line_number); + +u16 hpi_adapter_get_assert_ex(const struct hpi_hsubsys *ph_subsys, + u16 adapter_index, u16 *assert_present, char *psz_assert, + u32 *pline_number, u16 *pw_assert_on_dsp); + +u16 hpi_adapter_test_assert(const struct hpi_hsubsys *ph_subsys, + u16 adapter_index, u16 assert_id); + +u16 hpi_adapter_enable_capability(const struct hpi_hsubsys *ph_subsys, + u16 adapter_index, u16 capability, u32 key); + +u16 hpi_adapter_self_test(const struct hpi_hsubsys *ph_subsys, + u16 adapter_index); + +u16 hpi_adapter_debug_read(const struct hpi_hsubsys *ph_subsys, + u16 adapter_index, u32 dsp_address, char *p_bytes, int *count_bytes); + +u16 hpi_adapter_set_property(const struct hpi_hsubsys *ph_subsys, + u16 adapter_index, u16 property, u16 paramter1, u16 paramter2); + +u16 hpi_adapter_get_property(const struct hpi_hsubsys *ph_subsys, + u16 adapter_index, u16 property, u16 *pw_paramter1, + u16 *pw_paramter2); + +u16 hpi_adapter_enumerate_property(const struct hpi_hsubsys *ph_subsys, + u16 adapter_index, u16 index, u16 what_to_enumerate, + u16 property_index, u32 *psetting); + +/*////////////// */ +/* NonVol Memory */ +u16 hpi_nv_memory_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index, + u32 *ph_nv_memory, u16 *pw_size_in_bytes); + +u16 hpi_nv_memory_read_byte(const struct hpi_hsubsys *ph_subsys, + u32 h_nv_memory, u16 index, u16 *pw_data); + +u16 hpi_nv_memory_write_byte(const struct hpi_hsubsys *ph_subsys, + u32 h_nv_memory, u16 index, u16 data); + +/*////////////// */ +/* Digital I/O */ +u16 hpi_gpio_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index, + u32 *ph_gpio, u16 *pw_number_input_bits, u16 *pw_number_output_bits); + +u16 hpi_gpio_read_bit(const struct hpi_hsubsys *ph_subsys, u32 h_gpio, + u16 bit_index, u16 *pw_bit_data); + +u16 hpi_gpio_read_all_bits(const struct hpi_hsubsys *ph_subsys, u32 h_gpio, + u16 aw_all_bit_data[4] + ); + +u16 hpi_gpio_write_bit(const struct hpi_hsubsys *ph_subsys, u32 h_gpio, + u16 bit_index, u16 bit_data); + +u16 hpi_gpio_write_status(const struct hpi_hsubsys *ph_subsys, u32 h_gpio, + u16 aw_all_bit_data[4] + ); + +/**********************/ +/* Async Event Object */ +/**********************/ +u16 hpi_async_event_open(const struct hpi_hsubsys *ph_subsys, + u16 adapter_index, u32 *ph_async); + +u16 hpi_async_event_close(const struct hpi_hsubsys *ph_subsys, u32 h_async); + +u16 hpi_async_event_wait(const struct hpi_hsubsys *ph_subsys, u32 h_async, + u16 maximum_events, struct hpi_async_event *p_events, + u16 *pw_number_returned); + +u16 hpi_async_event_get_count(const struct hpi_hsubsys *ph_subsys, + u32 h_async, u16 *pw_count); + +u16 hpi_async_event_get(const struct hpi_hsubsys *ph_subsys, u32 h_async, + u16 maximum_events, struct hpi_async_event *p_events, + u16 *pw_number_returned); + +/*/////////// */ +/* WATCH-DOG */ +u16 hpi_watchdog_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index, + u32 *ph_watchdog); + +u16 hpi_watchdog_set_time(const struct hpi_hsubsys *ph_subsys, u32 h_watchdog, + u32 time_millisec); + +u16 hpi_watchdog_ping(const struct hpi_hsubsys *ph_subsys, u32 h_watchdog); + +/**************/ +/* OUT STREAM */ +/**************/ +u16 hpi_outstream_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index, + u16 outstream_index, u32 *ph_outstream); + +u16 hpi_outstream_close(const struct hpi_hsubsys *ph_subsys, u32 h_outstream); + +u16 hpi_outstream_get_info_ex(const struct hpi_hsubsys *ph_subsys, + u32 h_outstream, u16 *pw_state, u32 *pbuffer_size, u32 *pdata_to_play, + u32 *psamples_played, u32 *pauxiliary_data_to_play); + +u16 hpi_outstream_write_buf(const struct hpi_hsubsys *ph_subsys, + u32 h_outstream, const u8 *pb_write_buf, u32 bytes_to_write, + const struct hpi_format *p_format); + +u16 hpi_outstream_start(const struct hpi_hsubsys *ph_subsys, u32 h_outstream); + +u16 hpi_outstream_wait_start(const struct hpi_hsubsys *ph_subsys, + u32 h_outstream); + +u16 hpi_outstream_stop(const struct hpi_hsubsys *ph_subsys, u32 h_outstream); + +u16 hpi_outstream_sinegen(const struct hpi_hsubsys *ph_subsys, + u32 h_outstream); + +u16 hpi_outstream_reset(const struct hpi_hsubsys *ph_subsys, u32 h_outstream); + +u16 hpi_outstream_query_format(const struct hpi_hsubsys *ph_subsys, + u32 h_outstream, struct hpi_format *p_format); + +u16 hpi_outstream_set_format(const struct hpi_hsubsys *ph_subsys, + u32 h_outstream, struct hpi_format *p_format); + +u16 hpi_outstream_set_punch_in_out(const struct hpi_hsubsys *ph_subsys, + u32 h_outstream, u32 punch_in_sample, u32 punch_out_sample); + +u16 hpi_outstream_set_velocity(const struct hpi_hsubsys *ph_subsys, + u32 h_outstream, short velocity); + +u16 hpi_outstream_ancillary_reset(const struct hpi_hsubsys *ph_subsys, + u32 h_outstream, u16 mode); + +u16 hpi_outstream_ancillary_get_info(const struct hpi_hsubsys *ph_subsys, + u32 h_outstream, u32 *pframes_available); + +u16 hpi_outstream_ancillary_read(const struct hpi_hsubsys *ph_subsys, + u32 h_outstream, struct hpi_anc_frame *p_anc_frame_buffer, + u32 anc_frame_buffer_size_in_bytes, + u32 number_of_ancillary_frames_to_read); + +u16 hpi_outstream_set_time_scale(const struct hpi_hsubsys *ph_subsys, + u32 h_outstream, u32 time_scaleX10000); + +u16 hpi_outstream_host_buffer_allocate(const struct hpi_hsubsys *ph_subsys, + u32 h_outstream, u32 size_in_bytes); + +u16 hpi_outstream_host_buffer_free(const struct hpi_hsubsys *ph_subsys, + u32 h_outstream); + +u16 hpi_outstream_group_add(const struct hpi_hsubsys *ph_subsys, + u32 h_outstream, u32 h_stream); + +u16 hpi_outstream_group_get_map(const struct hpi_hsubsys *ph_subsys, + u32 h_outstream, u32 *poutstream_map, u32 *pinstream_map); + +u16 hpi_outstream_group_reset(const struct hpi_hsubsys *ph_subsys, + u32 h_outstream); + +/*////////// */ +/* IN_STREAM */ +u16 hpi_instream_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index, + u16 instream_index, u32 *ph_instream); + +u16 hpi_instream_close(const struct hpi_hsubsys *ph_subsys, u32 h_instream); + +u16 hpi_instream_query_format(const struct hpi_hsubsys *ph_subsys, + u32 h_instream, const struct hpi_format *p_format); + +u16 hpi_instream_set_format(const struct hpi_hsubsys *ph_subsys, + u32 h_instream, const struct hpi_format *p_format); + +u16 hpi_instream_read_buf(const struct hpi_hsubsys *ph_subsys, u32 h_instream, + u8 *pb_read_buf, u32 bytes_to_read); + +u16 hpi_instream_start(const struct hpi_hsubsys *ph_subsys, u32 h_instream); + +u16 hpi_instream_wait_start(const struct hpi_hsubsys *ph_subsys, + u32 h_instream); + +u16 hpi_instream_stop(const struct hpi_hsubsys *ph_subsys, u32 h_instream); + +u16 hpi_instream_reset(const struct hpi_hsubsys *ph_subsys, u32 h_instream); + +u16 hpi_instream_get_info_ex(const struct hpi_hsubsys *ph_subsys, + u32 h_instream, u16 *pw_state, u32 *pbuffer_size, u32 *pdata_recorded, + u32 *psamples_recorded, u32 *pauxiliary_data_recorded); + +u16 hpi_instream_ancillary_reset(const struct hpi_hsubsys *ph_subsys, + u32 h_instream, u16 bytes_per_frame, u16 mode, u16 alignment, + u16 idle_bit); + +u16 hpi_instream_ancillary_get_info(const struct hpi_hsubsys *ph_subsys, + u32 h_instream, u32 *pframe_space); + +u16 hpi_instream_ancillary_write(const struct hpi_hsubsys *ph_subsys, + u32 h_instream, const struct hpi_anc_frame *p_anc_frame_buffer, + u32 anc_frame_buffer_size_in_bytes, + u32 number_of_ancillary_frames_to_write); + +u16 hpi_instream_host_buffer_allocate(const struct hpi_hsubsys *ph_subsys, + u32 h_instream, u32 size_in_bytes); + +u16 hpi_instream_host_buffer_free(const struct hpi_hsubsys *ph_subsys, + u32 h_instream); + +u16 hpi_instream_group_add(const struct hpi_hsubsys *ph_subsys, + u32 h_instream, u32 h_stream); + +u16 hpi_instream_group_get_map(const struct hpi_hsubsys *ph_subsys, + u32 h_instream, u32 *poutstream_map, u32 *pinstream_map); + +u16 hpi_instream_group_reset(const struct hpi_hsubsys *ph_subsys, + u32 h_instream); + +/*********/ +/* MIXER */ +/*********/ +u16 hpi_mixer_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index, + u32 *ph_mixer); + +u16 hpi_mixer_close(const struct hpi_hsubsys *ph_subsys, u32 h_mixer); + +u16 hpi_mixer_get_control(const struct hpi_hsubsys *ph_subsys, u32 h_mixer, + u16 src_node_type, u16 src_node_type_index, u16 dst_node_type, + u16 dst_node_type_index, u16 control_type, u32 *ph_control); + +u16 hpi_mixer_get_control_by_index(const struct hpi_hsubsys *ph_subsys, + u32 h_mixer, u16 control_index, u16 *pw_src_node_type, + u16 *pw_src_node_index, u16 *pw_dst_node_type, u16 *pw_dst_node_index, + u16 *pw_control_type, u32 *ph_control); + +u16 hpi_mixer_store(const struct hpi_hsubsys *ph_subsys, u32 h_mixer, + enum HPI_MIXER_STORE_COMMAND command, u16 index); +/*************************/ +/* mixer CONTROLS */ +/*************************/ +/*************************/ +/* volume control */ +/*************************/ +u16 hpi_volume_set_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control, + short an_gain0_01dB[HPI_MAX_CHANNELS] + ); + +u16 hpi_volume_get_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control, + short an_gain0_01dB_out[HPI_MAX_CHANNELS] + ); + +#define hpi_volume_get_range hpi_volume_query_range +u16 hpi_volume_query_range(const struct hpi_hsubsys *ph_subsys, u32 h_control, + short *min_gain_01dB, short *max_gain_01dB, short *step_gain_01dB); + +u16 hpi_volume_query_channels(const struct hpi_hsubsys *ph_subsys, + const u32 h_volume, u32 *p_channels); + +u16 hpi_volume_auto_fade(const struct hpi_hsubsys *ph_subsys, u32 h_control, + short an_stop_gain0_01dB[HPI_MAX_CHANNELS], u32 duration_ms); + +u16 hpi_volume_auto_fade_profile(const struct hpi_hsubsys *ph_subsys, + u32 h_control, short an_stop_gain0_01dB[HPI_MAX_CHANNELS], + u32 duration_ms, u16 profile); + +/*************************/ +/* level control */ +/*************************/ +u16 hpi_level_query_range(const struct hpi_hsubsys *ph_subsys, u32 h_control, + short *min_gain_01dB, short *max_gain_01dB, short *step_gain_01dB); + +u16 hpi_level_set_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control, + short an_gain0_01dB[HPI_MAX_CHANNELS] + ); + +u16 hpi_level_get_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control, + short an_gain0_01dB_out[HPI_MAX_CHANNELS] + ); + +/*************************/ +/* meter control */ +/*************************/ +u16 hpi_meter_query_channels(const struct hpi_hsubsys *ph_subsys, + const u32 h_meter, u32 *p_channels); + +u16 hpi_meter_get_peak(const struct hpi_hsubsys *ph_subsys, u32 h_control, + short an_peak0_01dB_out[HPI_MAX_CHANNELS] + ); + +u16 hpi_meter_get_rms(const struct hpi_hsubsys *ph_subsys, u32 h_control, + short an_peak0_01dB_out[HPI_MAX_CHANNELS] + ); + +u16 hpi_meter_set_peak_ballistics(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 attack, u16 decay); + +u16 hpi_meter_set_rms_ballistics(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 attack, u16 decay); + +u16 hpi_meter_get_peak_ballistics(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 *attack, u16 *decay); + +u16 hpi_meter_get_rms_ballistics(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 *attack, u16 *decay); + +/*************************/ +/* channel mode control */ +/*************************/ +u16 hpi_channel_mode_query_mode(const struct hpi_hsubsys *ph_subsys, + const u32 h_mode, const u32 index, u16 *pw_mode); + +u16 hpi_channel_mode_set(const struct hpi_hsubsys *ph_subsys, u32 h_control, + u16 mode); + +u16 hpi_channel_mode_get(const struct hpi_hsubsys *ph_subsys, u32 h_control, + u16 *mode); + +/*************************/ +/* Tuner control */ +/*************************/ +u16 hpi_tuner_query_band(const struct hpi_hsubsys *ph_subsys, + const u32 h_tuner, const u32 index, u16 *pw_band); + +u16 hpi_tuner_set_band(const struct hpi_hsubsys *ph_subsys, u32 h_control, + u16 band); + +u16 hpi_tuner_get_band(const struct hpi_hsubsys *ph_subsys, u32 h_control, + u16 *pw_band); + +u16 hpi_tuner_query_frequency(const struct hpi_hsubsys *ph_subsys, + const u32 h_tuner, const u32 index, const u16 band, u32 *pfreq); + +u16 hpi_tuner_set_frequency(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 freq_ink_hz); + +u16 hpi_tuner_get_frequency(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 *pw_freq_ink_hz); + +u16 hpi_tuner_getRF_level(const struct hpi_hsubsys *ph_subsys, u32 h_control, + short *pw_level); + +u16 hpi_tuner_get_rawRF_level(const struct hpi_hsubsys *ph_subsys, + u32 h_control, short *pw_level); + +u16 hpi_tuner_query_gain(const struct hpi_hsubsys *ph_subsys, + const u32 h_tuner, const u32 index, u16 *pw_gain); + +u16 hpi_tuner_set_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control, + short gain); + +u16 hpi_tuner_get_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control, + short *pn_gain); + +u16 hpi_tuner_get_status(const struct hpi_hsubsys *ph_subsys, u32 h_control, + u16 *pw_status_mask, u16 *pw_status); + +u16 hpi_tuner_set_mode(const struct hpi_hsubsys *ph_subsys, u32 h_control, + u32 mode, u32 value); + +u16 hpi_tuner_get_mode(const struct hpi_hsubsys *ph_subsys, u32 h_control, + u32 mode, u32 *pn_value); + +u16 hpi_tuner_getRDS(const struct hpi_hsubsys *ph_subsys, u32 h_control, + char *p_rds_data); + +u16 hpi_tuner_query_deemphasis(const struct hpi_hsubsys *ph_subsys, + const u32 h_tuner, const u32 index, const u16 band, u32 *pdeemphasis); + +u16 hpi_tuner_set_deemphasis(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 deemphasis); +u16 hpi_tuner_get_deemphasis(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 *pdeemphasis); + +u16 hpi_tuner_query_program(const struct hpi_hsubsys *ph_subsys, + const u32 h_tuner, u32 *pbitmap_program); + +u16 hpi_tuner_set_program(const struct hpi_hsubsys *ph_subsys, u32 h_control, + u32 program); + +u16 hpi_tuner_get_program(const struct hpi_hsubsys *ph_subsys, u32 h_control, + u32 *pprogram); + +u16 hpi_tuner_get_hd_radio_dsp_version(const struct hpi_hsubsys *ph_subsys, + u32 h_control, char *psz_dsp_version, const u32 string_size); + +u16 hpi_tuner_get_hd_radio_sdk_version(const struct hpi_hsubsys *ph_subsys, + u32 h_control, char *psz_sdk_version, const u32 string_size); + +u16 hpi_tuner_get_hd_radio_signal_quality(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 *pquality); + +/****************************/ +/* PADs control */ +/****************************/ + +u16 HPI_PAD__get_channel_name(const struct hpi_hsubsys *ph_subsys, + u32 h_control, char *psz_string, const u32 string_length); + +u16 HPI_PAD__get_artist(const struct hpi_hsubsys *ph_subsys, u32 h_control, + char *psz_string, const u32 string_length); + +u16 HPI_PAD__get_title(const struct hpi_hsubsys *ph_subsys, u32 h_control, + char *psz_string, const u32 string_length); + +u16 HPI_PAD__get_comment(const struct hpi_hsubsys *ph_subsys, u32 h_control, + char *psz_string, const u32 string_length); + +u16 HPI_PAD__get_program_type(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 *ppTY); + +u16 HPI_PAD__get_rdsPI(const struct hpi_hsubsys *ph_subsys, u32 h_control, + u32 *ppI); + +u16 HPI_PAD__get_program_type_string(const struct hpi_hsubsys *ph_subsys, + u32 h_control, const u32 data_type, const u32 pTY, char *psz_string, + const u32 string_length); + +/****************************/ +/* AES/EBU Receiver control */ +/****************************/ +u16 HPI_AESEBU__receiver_query_format(const struct hpi_hsubsys *ph_subsys, + const u32 h_aes_rx, const u32 index, u16 *pw_format); + +u16 HPI_AESEBU__receiver_set_format(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 source); + +u16 HPI_AESEBU__receiver_get_format(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 *pw_source); + +u16 HPI_AESEBU__receiver_get_sample_rate(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 *psample_rate); + +u16 HPI_AESEBU__receiver_get_user_data(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 index, u16 *pw_data); + +u16 HPI_AESEBU__receiver_get_channel_status(const struct hpi_hsubsys + *ph_subsys, u32 h_control, u16 index, u16 *pw_data); + +u16 HPI_AESEBU__receiver_get_error_status(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 *pw_error_data); + +/*******************************/ +/* AES/EBU Transmitter control */ +/*******************************/ +u16 HPI_AESEBU__transmitter_set_sample_rate(const struct hpi_hsubsys + *ph_subsys, u32 h_control, u32 sample_rate); + +u16 HPI_AESEBU__transmitter_set_user_data(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 index, u16 data); + +u16 HPI_AESEBU__transmitter_set_channel_status(const struct hpi_hsubsys + *ph_subsys, u32 h_control, u16 index, u16 data); + +u16 HPI_AESEBU__transmitter_get_channel_status(const struct hpi_hsubsys + *ph_subsys, u32 h_control, u16 index, u16 *pw_data); + +u16 HPI_AESEBU__transmitter_query_format(const struct hpi_hsubsys *ph_subsys, + const u32 h_aes_tx, const u32 index, u16 *pw_format); + +u16 HPI_AESEBU__transmitter_set_format(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 output_format); + +u16 HPI_AESEBU__transmitter_get_format(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 *pw_output_format); + +/***********************/ +/* multiplexer control */ +/***********************/ +u16 hpi_multiplexer_set_source(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 source_node_type, u16 source_node_index); + +u16 hpi_multiplexer_get_source(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 *source_node_type, u16 *source_node_index); + +u16 hpi_multiplexer_query_source(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 index, u16 *source_node_type, + u16 *source_node_index); + +/***************/ +/* VOX control */ +/***************/ +u16 hpi_vox_set_threshold(const struct hpi_hsubsys *ph_subsys, u32 h_control, + short an_gain0_01dB); + +u16 hpi_vox_get_threshold(const struct hpi_hsubsys *ph_subsys, u32 h_control, + short *an_gain0_01dB); + +/*********************/ +/* Bitstream control */ +/*********************/ +u16 hpi_bitstream_set_clock_edge(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 edge_type); + +u16 hpi_bitstream_set_data_polarity(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 polarity); + +u16 hpi_bitstream_get_activity(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 *pw_clk_activity, u16 *pw_data_activity); + +/***********************/ +/* SampleClock control */ +/***********************/ + +u16 hpi_sample_clock_query_source(const struct hpi_hsubsys *ph_subsys, + const u32 h_clock, const u32 index, u16 *pw_source); + +u16 hpi_sample_clock_set_source(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 source); + +u16 hpi_sample_clock_get_source(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 *pw_source); + +u16 hpi_sample_clock_query_source_index(const struct hpi_hsubsys *ph_subsys, + const u32 h_clock, const u32 index, const u32 source, + u16 *pw_source_index); + +u16 hpi_sample_clock_set_source_index(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 source_index); + +u16 hpi_sample_clock_get_source_index(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 *pw_source_index); + +u16 hpi_sample_clock_get_sample_rate(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 *psample_rate); + +u16 hpi_sample_clock_query_local_rate(const struct hpi_hsubsys *ph_subsys, + const u32 h_clock, const u32 index, u32 *psource); + +u16 hpi_sample_clock_set_local_rate(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 sample_rate); + +u16 hpi_sample_clock_get_local_rate(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 *psample_rate); + +u16 hpi_sample_clock_set_auto(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 enable); + +u16 hpi_sample_clock_get_auto(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 *penable); + +u16 hpi_sample_clock_set_local_rate_lock(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 lock); + +u16 hpi_sample_clock_get_local_rate_lock(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 *plock); + +/***********************/ +/* Microphone control */ +/***********************/ +u16 hpi_microphone_set_phantom_power(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 on_off); + +u16 hpi_microphone_get_phantom_power(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 *pw_on_off); + +/******************************* + Parametric Equalizer control +*******************************/ +u16 hpi_parametricEQ__get_info(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 *pw_number_of_bands, u16 *pw_enabled); + +u16 hpi_parametricEQ__set_state(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 on_off); + +u16 hpi_parametricEQ__set_band(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 index, u16 type, u32 frequency_hz, short q100, + short gain0_01dB); + +u16 hpi_parametricEQ__get_band(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 index, u16 *pn_type, u32 *pfrequency_hz, + short *pnQ100, short *pn_gain0_01dB); + +u16 hpi_parametricEQ__get_coeffs(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 index, short coeffs[5] + ); + +/******************************* + Compressor Expander control +*******************************/ + +u16 hpi_compander_set(const struct hpi_hsubsys *ph_subsys, u32 h_control, + u16 attack, u16 decay, short ratio100, short threshold0_01dB, + short makeup_gain0_01dB); + +u16 hpi_compander_get(const struct hpi_hsubsys *ph_subsys, u32 h_control, + u16 *pw_attack, u16 *pw_decay, short *pw_ratio100, + short *pn_threshold0_01dB, short *pn_makeup_gain0_01dB); + +/******************************* + Cobranet HMI control +*******************************/ +u16 hpi_cobranet_hmi_write(const struct hpi_hsubsys *ph_subsys, u32 h_control, + u32 hmi_address, u32 byte_count, u8 *pb_data); + +u16 hpi_cobranet_hmi_read(const struct hpi_hsubsys *ph_subsys, u32 h_control, + u32 hmi_address, u32 max_byte_count, u32 *pbyte_count, u8 *pb_data); + +u16 hpi_cobranet_hmi_get_status(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 *pstatus, u32 *preadable_size, + u32 *pwriteable_size); + +/*Read the current IP address +*/ +u16 hpi_cobranet_getI_paddress(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 *pi_paddress); + +/* Write the current IP address +*/ +u16 hpi_cobranet_setI_paddress(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 i_paddress); + +/* Read the static IP address +*/ +u16 hpi_cobranet_get_staticI_paddress(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 *pi_paddress); + +/* Write the static IP address +*/ +u16 hpi_cobranet_set_staticI_paddress(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 i_paddress); + +/* Read the MAC address +*/ +u16 hpi_cobranet_getMA_caddress(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 *pmAC_MS_bs, u32 *pmAC_LS_bs); + +/******************************* + Tone Detector control +*******************************/ +u16 hpi_tone_detector_get_state(const struct hpi_hsubsys *ph_subsys, u32 hC, + u32 *state); + +u16 hpi_tone_detector_set_enable(const struct hpi_hsubsys *ph_subsys, u32 hC, + u32 enable); + +u16 hpi_tone_detector_get_enable(const struct hpi_hsubsys *ph_subsys, u32 hC, + u32 *enable); + +u16 hpi_tone_detector_set_event_enable(const struct hpi_hsubsys *ph_subsys, + u32 hC, u32 event_enable); + +u16 hpi_tone_detector_get_event_enable(const struct hpi_hsubsys *ph_subsys, + u32 hC, u32 *event_enable); + +u16 hpi_tone_detector_set_threshold(const struct hpi_hsubsys *ph_subsys, + u32 hC, int threshold); + +u16 hpi_tone_detector_get_threshold(const struct hpi_hsubsys *ph_subsys, + u32 hC, int *threshold); + +u16 hpi_tone_detector_get_frequency(const struct hpi_hsubsys *ph_subsys, + u32 hC, u32 index, u32 *frequency); + +/******************************* + Silence Detector control +*******************************/ +u16 hpi_silence_detector_get_state(const struct hpi_hsubsys *ph_subsys, + u32 hC, u32 *state); + +u16 hpi_silence_detector_set_enable(const struct hpi_hsubsys *ph_subsys, + u32 hC, u32 enable); + +u16 hpi_silence_detector_get_enable(const struct hpi_hsubsys *ph_subsys, + u32 hC, u32 *enable); + +u16 hpi_silence_detector_set_event_enable(const struct hpi_hsubsys *ph_subsys, + u32 hC, u32 event_enable); + +u16 hpi_silence_detector_get_event_enable(const struct hpi_hsubsys *ph_subsys, + u32 hC, u32 *event_enable); + +u16 hpi_silence_detector_set_delay(const struct hpi_hsubsys *ph_subsys, + u32 hC, u32 delay); + +u16 hpi_silence_detector_get_delay(const struct hpi_hsubsys *ph_subsys, + u32 hC, u32 *delay); + +u16 hpi_silence_detector_set_threshold(const struct hpi_hsubsys *ph_subsys, + u32 hC, int threshold); + +u16 hpi_silence_detector_get_threshold(const struct hpi_hsubsys *ph_subsys, + u32 hC, int *threshold); + +/******************************* + Universal control +*******************************/ +u16 hpi_entity_find_next(struct hpi_entity *container_entity, + enum e_entity_type type, enum e_entity_role role, int recursive_flag, + struct hpi_entity **current_match); + +u16 hpi_entity_copy_value_from(struct hpi_entity *entity, + enum e_entity_type type, size_t item_count, void *value_dst_p); + +u16 hpi_entity_unpack(struct hpi_entity *entity, enum e_entity_type *type, + size_t *items, enum e_entity_role *role, void **value); + +u16 hpi_entity_alloc_and_pack(const enum e_entity_type type, + const size_t item_count, const enum e_entity_role role, void *value, + struct hpi_entity **entity); + +void hpi_entity_free(struct hpi_entity *entity); + +u16 hpi_universal_info(const struct hpi_hsubsys *ph_subsys, u32 hC, + struct hpi_entity **info); + +u16 hpi_universal_get(const struct hpi_hsubsys *ph_subsys, u32 hC, + struct hpi_entity **value); + +u16 hpi_universal_set(const struct hpi_hsubsys *ph_subsys, u32 hC, + struct hpi_entity *value); + +/*/////////// */ +/* DSP CLOCK */ +/*/////////// */ +u16 hpi_clock_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index, + u32 *ph_dsp_clock); + +u16 hpi_clock_set_time(const struct hpi_hsubsys *ph_subsys, u32 h_clock, + u16 hour, u16 minute, u16 second, u16 milli_second); + +u16 hpi_clock_get_time(const struct hpi_hsubsys *ph_subsys, u32 h_clock, + u16 *pw_hour, u16 *pw_minute, u16 *pw_second, u16 *pw_milli_second); + +/*/////////// */ +/* PROFILE */ +/*/////////// */ +u16 hpi_profile_open_all(const struct hpi_hsubsys *ph_subsys, + u16 adapter_index, u16 profile_index, u32 *ph_profile, + u16 *pw_max_profiles); + +u16 hpi_profile_get(const struct hpi_hsubsys *ph_subsys, u32 h_profile, + u16 index, u16 *pw_seconds, u32 *pmicro_seconds, u32 *pcall_count, + u32 *pmax_micro_seconds, u32 *pmin_micro_seconds); + +u16 hpi_profile_start_all(const struct hpi_hsubsys *ph_subsys, u32 h_profile); + +u16 hpi_profile_stop_all(const struct hpi_hsubsys *ph_subsys, u32 h_profile); + +u16 hpi_profile_get_name(const struct hpi_hsubsys *ph_subsys, u32 h_profile, + u16 index, char *sz_profile_name, u16 profile_name_length); + +u16 hpi_profile_get_utilization(const struct hpi_hsubsys *ph_subsys, + u32 h_profile, u32 *putilization); + +/*//////////////////// */ +/* UTILITY functions */ + +u16 hpi_format_create(struct hpi_format *p_format, u16 channels, u16 format, + u32 sample_rate, u32 bit_rate, u32 attributes); + +/* Until it's verified, this function is for Windows OSs only */ + +#endif /*_H_HPI_ */ +/* +/////////////////////////////////////////////////////////////////////////////// +// See CVS for history. Last complete set in rev 1.146 +//////////////////////////////////////////////////////////////////////////////// +*/ diff --git a/sound/pci/asihpi/hpi6000.c b/sound/pci/asihpi/hpi6000.c new file mode 100644 index 000000000000..9c50931731a5 --- /dev/null +++ b/sound/pci/asihpi/hpi6000.c @@ -0,0 +1,1841 @@ +/****************************************************************************** + + AudioScience HPI driver + Copyright (C) 1997-2010 AudioScience Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation; + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Hardware Programming Interface (HPI) for AudioScience ASI6200 series adapters. + These PCI bus adapters are based on the TI C6711 DSP. + + Exported functions: + void HPI_6000(struct hpi_message *phm, struct hpi_response *phr) + + #defines + HIDE_PCI_ASSERTS to show the PCI asserts + PROFILE_DSP2 get profile data from DSP2 if present (instead of DSP 1) + +(C) Copyright AudioScience Inc. 1998-2003 +*******************************************************************************/ +#define SOURCEFILE_NAME "hpi6000.c" + +#include "hpi_internal.h" +#include "hpimsginit.h" +#include "hpidebug.h" +#include "hpi6000.h" +#include "hpidspcd.h" +#include "hpicmn.h" + +#define HPI_HIF_BASE (0x00000200) /* start of C67xx internal RAM */ +#define HPI_HIF_ADDR(member) \ + (HPI_HIF_BASE + offsetof(struct hpi_hif_6000, member)) +#define HPI_HIF_ERROR_MASK 0x4000 + +/* HPI6000 specific error codes */ + +#define HPI6000_ERROR_BASE 900 +#define HPI6000_ERROR_MSG_RESP_IDLE_TIMEOUT 901 +#define HPI6000_ERROR_MSG_RESP_SEND_MSG_ACK 902 +#define HPI6000_ERROR_MSG_RESP_GET_RESP_ACK 903 +#define HPI6000_ERROR_MSG_GET_ADR 904 +#define HPI6000_ERROR_RESP_GET_ADR 905 +#define HPI6000_ERROR_MSG_RESP_BLOCKWRITE32 906 +#define HPI6000_ERROR_MSG_RESP_BLOCKREAD32 907 +#define HPI6000_ERROR_MSG_INVALID_DSP_INDEX 908 +#define HPI6000_ERROR_CONTROL_CACHE_PARAMS 909 + +#define HPI6000_ERROR_SEND_DATA_IDLE_TIMEOUT 911 +#define HPI6000_ERROR_SEND_DATA_ACK 912 +#define HPI6000_ERROR_SEND_DATA_ADR 913 +#define HPI6000_ERROR_SEND_DATA_TIMEOUT 914 +#define HPI6000_ERROR_SEND_DATA_CMD 915 +#define HPI6000_ERROR_SEND_DATA_WRITE 916 +#define HPI6000_ERROR_SEND_DATA_IDLECMD 917 +#define HPI6000_ERROR_SEND_DATA_VERIFY 918 + +#define HPI6000_ERROR_GET_DATA_IDLE_TIMEOUT 921 +#define HPI6000_ERROR_GET_DATA_ACK 922 +#define HPI6000_ERROR_GET_DATA_CMD 923 +#define HPI6000_ERROR_GET_DATA_READ 924 +#define HPI6000_ERROR_GET_DATA_IDLECMD 925 + +#define HPI6000_ERROR_CONTROL_CACHE_ADDRLEN 951 +#define HPI6000_ERROR_CONTROL_CACHE_READ 952 +#define HPI6000_ERROR_CONTROL_CACHE_FLUSH 953 + +#define HPI6000_ERROR_MSG_RESP_GETRESPCMD 961 +#define HPI6000_ERROR_MSG_RESP_IDLECMD 962 +#define HPI6000_ERROR_MSG_RESP_BLOCKVERIFY32 963 + +/* adapter init errors */ +#define HPI6000_ERROR_UNHANDLED_SUBSYS_ID 930 + +/* can't access PCI2040 */ +#define HPI6000_ERROR_INIT_PCI2040 931 +/* can't access DSP HPI i/f */ +#define HPI6000_ERROR_INIT_DSPHPI 932 +/* can't access internal DSP memory */ +#define HPI6000_ERROR_INIT_DSPINTMEM 933 +/* can't access SDRAM - test#1 */ +#define HPI6000_ERROR_INIT_SDRAM1 934 +/* can't access SDRAM - test#2 */ +#define HPI6000_ERROR_INIT_SDRAM2 935 + +#define HPI6000_ERROR_INIT_VERIFY 938 + +#define HPI6000_ERROR_INIT_NOACK 939 + +#define HPI6000_ERROR_INIT_PLDTEST1 941 +#define HPI6000_ERROR_INIT_PLDTEST2 942 + +/* local defines */ + +#define HIDE_PCI_ASSERTS +#define PROFILE_DSP2 + +/* for PCI2040 i/f chip */ +/* HPI CSR registers */ +/* word offsets from CSR base */ +/* use when io addresses defined as u32 * */ + +#define INTERRUPT_EVENT_SET 0 +#define INTERRUPT_EVENT_CLEAR 1 +#define INTERRUPT_MASK_SET 2 +#define INTERRUPT_MASK_CLEAR 3 +#define HPI_ERROR_REPORT 4 +#define HPI_RESET 5 +#define HPI_DATA_WIDTH 6 + +#define MAX_DSPS 2 +/* HPI registers, spaced 8K bytes = 2K words apart */ +#define DSP_SPACING 0x800 + +#define CONTROL 0x0000 +#define ADDRESS 0x0200 +#define DATA_AUTOINC 0x0400 +#define DATA 0x0600 + +#define TIMEOUT 500000 + +struct dsp_obj { + __iomem u32 *prHPI_control; + __iomem u32 *prHPI_address; + __iomem u32 *prHPI_data; + __iomem u32 *prHPI_data_auto_inc; + char c_dsp_rev; /*A, B */ + u32 control_cache_address_on_dsp; + u32 control_cache_length_on_dsp; + struct hpi_adapter_obj *pa_parent_adapter; +}; + +struct hpi_hw_obj { + __iomem u32 *dw2040_HPICSR; + __iomem u32 *dw2040_HPIDSP; + + u16 num_dsp; + struct dsp_obj ado[MAX_DSPS]; + + u32 message_buffer_address_on_dsp; + u32 response_buffer_address_on_dsp; + u32 pCI2040HPI_error_count; + + struct hpi_control_cache_single control_cache[HPI_NMIXER_CONTROLS]; + struct hpi_control_cache *p_cache; +}; + +static u16 hpi6000_dsp_block_write32(struct hpi_adapter_obj *pao, + u16 dsp_index, u32 hpi_address, u32 *source, u32 count); +static u16 hpi6000_dsp_block_read32(struct hpi_adapter_obj *pao, + u16 dsp_index, u32 hpi_address, u32 *dest, u32 count); + +static short hpi6000_adapter_boot_load_dsp(struct hpi_adapter_obj *pao, + u32 *pos_error_code); +static short hpi6000_check_PCI2040_error_flag(struct hpi_adapter_obj *pao, + u16 read_or_write); +#define H6READ 1 +#define H6WRITE 0 + +static short hpi6000_update_control_cache(struct hpi_adapter_obj *pao, + struct hpi_message *phm); +static short hpi6000_message_response_sequence(struct hpi_adapter_obj *pao, + u16 dsp_index, struct hpi_message *phm, struct hpi_response *phr); + +static void hw_message(struct hpi_adapter_obj *pao, struct hpi_message *phm, + struct hpi_response *phr); + +static short hpi6000_wait_dsp_ack(struct hpi_adapter_obj *pao, u16 dsp_index, + u32 ack_value); + +static short hpi6000_send_host_command(struct hpi_adapter_obj *pao, + u16 dsp_index, u32 host_cmd); + +static void hpi6000_send_dsp_interrupt(struct dsp_obj *pdo); + +static short hpi6000_send_data(struct hpi_adapter_obj *pao, u16 dsp_index, + struct hpi_message *phm, struct hpi_response *phr); + +static short hpi6000_get_data(struct hpi_adapter_obj *pao, u16 dsp_index, + struct hpi_message *phm, struct hpi_response *phr); + +static void hpi_write_word(struct dsp_obj *pdo, u32 address, u32 data); + +static u32 hpi_read_word(struct dsp_obj *pdo, u32 address); + +static void hpi_write_block(struct dsp_obj *pdo, u32 address, u32 *pdata, + u32 length); + +static void hpi_read_block(struct dsp_obj *pdo, u32 address, u32 *pdata, + u32 length); + +static void subsys_create_adapter(struct hpi_message *phm, + struct hpi_response *phr); + +static void subsys_delete_adapter(struct hpi_message *phm, + struct hpi_response *phr); + +static void adapter_get_asserts(struct hpi_adapter_obj *pao, + struct hpi_message *phm, struct hpi_response *phr); + +static short create_adapter_obj(struct hpi_adapter_obj *pao, + u32 *pos_error_code); + +/* local globals */ + +static u16 gw_pci_read_asserts; /* used to count PCI2040 errors */ +static u16 gw_pci_write_asserts; /* used to count PCI2040 errors */ + +static void subsys_message(struct hpi_message *phm, struct hpi_response *phr) +{ + + switch (phm->function) { + case HPI_SUBSYS_OPEN: + case HPI_SUBSYS_CLOSE: + case HPI_SUBSYS_GET_INFO: + case HPI_SUBSYS_DRIVER_UNLOAD: + case HPI_SUBSYS_DRIVER_LOAD: + case HPI_SUBSYS_FIND_ADAPTERS: + /* messages that should not get here */ + phr->error = HPI_ERROR_UNIMPLEMENTED; + break; + case HPI_SUBSYS_CREATE_ADAPTER: + subsys_create_adapter(phm, phr); + break; + case HPI_SUBSYS_DELETE_ADAPTER: + subsys_delete_adapter(phm, phr); + break; + default: + phr->error = HPI_ERROR_INVALID_FUNC; + break; + } +} + +static void control_message(struct hpi_adapter_obj *pao, + struct hpi_message *phm, struct hpi_response *phr) +{ + + switch (phm->function) { + case HPI_CONTROL_GET_STATE: + if (pao->has_control_cache) { + u16 err; + err = hpi6000_update_control_cache(pao, phm); + + if (err) { + phr->error = err; + break; + } + + if (hpi_check_control_cache(((struct hpi_hw_obj *) + pao->priv)->p_cache, phm, + phr)) + break; + } + hw_message(pao, phm, phr); + break; + case HPI_CONTROL_GET_INFO: + hw_message(pao, phm, phr); + break; + case HPI_CONTROL_SET_STATE: + hw_message(pao, phm, phr); + hpi_sync_control_cache(((struct hpi_hw_obj *)pao->priv)-> + p_cache, phm, phr); + break; + default: + phr->error = HPI_ERROR_INVALID_FUNC; + break; + } +} + +static void adapter_message(struct hpi_adapter_obj *pao, + struct hpi_message *phm, struct hpi_response *phr) +{ + switch (phm->function) { + case HPI_ADAPTER_GET_INFO: + hw_message(pao, phm, phr); + break; + case HPI_ADAPTER_GET_ASSERT: + adapter_get_asserts(pao, phm, phr); + break; + case HPI_ADAPTER_OPEN: + case HPI_ADAPTER_CLOSE: + case HPI_ADAPTER_TEST_ASSERT: + case HPI_ADAPTER_SELFTEST: + case HPI_ADAPTER_GET_MODE: + case HPI_ADAPTER_SET_MODE: + case HPI_ADAPTER_FIND_OBJECT: + case HPI_ADAPTER_GET_PROPERTY: + case HPI_ADAPTER_SET_PROPERTY: + case HPI_ADAPTER_ENUM_PROPERTY: + hw_message(pao, phm, phr); + break; + default: + phr->error = HPI_ERROR_INVALID_FUNC; + break; + } +} + +static void outstream_message(struct hpi_adapter_obj *pao, + struct hpi_message *phm, struct hpi_response *phr) +{ + switch (phm->function) { + case HPI_OSTREAM_HOSTBUFFER_ALLOC: + case HPI_OSTREAM_HOSTBUFFER_FREE: + /* Don't let these messages go to the HW function because + * they're called without allocating the spinlock. + * For the HPI6000 adapters the HW would return + * HPI_ERROR_INVALID_FUNC anyway. + */ + phr->error = HPI_ERROR_INVALID_FUNC; + break; + default: + hw_message(pao, phm, phr); + return; + } +} + +static void instream_message(struct hpi_adapter_obj *pao, + struct hpi_message *phm, struct hpi_response *phr) +{ + + switch (phm->function) { + case HPI_ISTREAM_HOSTBUFFER_ALLOC: + case HPI_ISTREAM_HOSTBUFFER_FREE: + /* Don't let these messages go to the HW function because + * they're called without allocating the spinlock. + * For the HPI6000 adapters the HW would return + * HPI_ERROR_INVALID_FUNC anyway. + */ + phr->error = HPI_ERROR_INVALID_FUNC; + break; + default: + hw_message(pao, phm, phr); + return; + } +} + +/************************************************************************/ +/** HPI_6000() + * Entry point from HPIMAN + * All calls to the HPI start here + */ +void HPI_6000(struct hpi_message *phm, struct hpi_response *phr) +{ + struct hpi_adapter_obj *pao = NULL; + + /* subsytem messages get executed by every HPI. */ + /* All other messages are ignored unless the adapter index matches */ + /* an adapter in the HPI */ + HPI_DEBUG_LOG(DEBUG, "O %d,F %x\n", phm->object, phm->function); + + /* if Dsp has crashed then do not communicate with it any more */ + if (phm->object != HPI_OBJ_SUBSYSTEM) { + pao = hpi_find_adapter(phm->adapter_index); + if (!pao) { + HPI_DEBUG_LOG(DEBUG, + " %d,%d refused, for another HPI?\n", + phm->object, phm->function); + return; + } + + if (pao->dsp_crashed >= 10) { + hpi_init_response(phr, phm->object, phm->function, + HPI_ERROR_DSP_HARDWARE); + HPI_DEBUG_LOG(DEBUG, " %d,%d dsp crashed.\n", + phm->object, phm->function); + return; + } + } + /* Init default response including the size field */ + if (phm->function != HPI_SUBSYS_CREATE_ADAPTER) + hpi_init_response(phr, phm->object, phm->function, + HPI_ERROR_PROCESSING_MESSAGE); + + switch (phm->type) { + case HPI_TYPE_MESSAGE: + switch (phm->object) { + case HPI_OBJ_SUBSYSTEM: + subsys_message(phm, phr); + break; + + case HPI_OBJ_ADAPTER: + phr->size = + sizeof(struct hpi_response_header) + + sizeof(struct hpi_adapter_res); + adapter_message(pao, phm, phr); + break; + + case HPI_OBJ_CONTROL: + control_message(pao, phm, phr); + break; + + case HPI_OBJ_OSTREAM: + outstream_message(pao, phm, phr); + break; + + case HPI_OBJ_ISTREAM: + instream_message(pao, phm, phr); + break; + + default: + hw_message(pao, phm, phr); + break; + } + break; + + default: + phr->error = HPI_ERROR_INVALID_TYPE; + break; + } +} + +/************************************************************************/ +/* SUBSYSTEM */ + +/* create an adapter object and initialise it based on resource information + * passed in in the message + * NOTE - you cannot use this function AND the FindAdapters function at the + * same time, the application must use only one of them to get the adapters + */ +static void subsys_create_adapter(struct hpi_message *phm, + struct hpi_response *phr) +{ + /* create temp adapter obj, because we don't know what index yet */ + struct hpi_adapter_obj ao; + struct hpi_adapter_obj *pao; + u32 os_error_code; + short error = 0; + u32 dsp_index = 0; + + HPI_DEBUG_LOG(VERBOSE, "subsys_create_adapter\n"); + + memset(&ao, 0, sizeof(ao)); + + /* this HPI only creates adapters for TI/PCI2040 based devices */ + if (phm->u.s.resource.bus_type != HPI_BUS_PCI) + return; + if (phm->u.s.resource.r.pci->vendor_id != HPI_PCI_VENDOR_ID_TI) + return; + if (phm->u.s.resource.r.pci->device_id != HPI_PCI_DEV_ID_PCI2040) + return; + + ao.priv = kmalloc(sizeof(struct hpi_hw_obj), GFP_KERNEL); + if (!ao.priv) { + HPI_DEBUG_LOG(ERROR, "cant get mem for adapter object\n"); + phr->error = HPI_ERROR_MEMORY_ALLOC; + return; + } + + memset(ao.priv, 0, sizeof(struct hpi_hw_obj)); + /* create the adapter object based on the resource information */ + /*? memcpy(&ao.Pci,&phm->u.s.Resource.r.Pci,sizeof(ao.Pci)); */ + ao.pci = *phm->u.s.resource.r.pci; + + error = create_adapter_obj(&ao, &os_error_code); + if (!error) + error = hpi_add_adapter(&ao); + if (error) { + phr->u.s.data = os_error_code; + kfree(ao.priv); + phr->error = error; + return; + } + /* need to update paParentAdapter */ + pao = hpi_find_adapter(ao.index); + if (!pao) { + /* We just added this adapter, why can't we find it!? */ + HPI_DEBUG_LOG(ERROR, "lost adapter after boot\n"); + phr->error = 950; + return; + } + + for (dsp_index = 0; dsp_index < MAX_DSPS; dsp_index++) { + struct hpi_hw_obj *phw = (struct hpi_hw_obj *)pao->priv; + phw->ado[dsp_index].pa_parent_adapter = pao; + } + + phr->u.s.aw_adapter_list[ao.index] = ao.adapter_type; + phr->u.s.adapter_index = ao.index; + phr->u.s.num_adapters++; + phr->error = 0; +} + +static void subsys_delete_adapter(struct hpi_message *phm, + struct hpi_response *phr) +{ + struct hpi_adapter_obj *pao = NULL; + struct hpi_hw_obj *phw; + + pao = hpi_find_adapter(phm->adapter_index); + if (!pao) + return; + + phw = (struct hpi_hw_obj *)pao->priv; + + if (pao->has_control_cache) + hpi_free_control_cache(phw->p_cache); + + hpi_delete_adapter(pao); + kfree(phw); + + phr->error = 0; +} + +/* this routine is called from SubSysFindAdapter and SubSysCreateAdapter */ +static short create_adapter_obj(struct hpi_adapter_obj *pao, + u32 *pos_error_code) +{ + short boot_error = 0; + u32 dsp_index = 0; + u32 control_cache_size = 0; + u32 control_cache_count = 0; + struct hpi_hw_obj *phw = (struct hpi_hw_obj *)pao->priv; + + /* init error reporting */ + pao->dsp_crashed = 0; + + /* The PCI2040 has the following address map */ + /* BAR0 - 4K = HPI control and status registers on PCI2040 (HPI CSR) */ + /* BAR1 - 32K = HPI registers on DSP */ + phw->dw2040_HPICSR = pao->pci.ap_mem_base[0]; + phw->dw2040_HPIDSP = pao->pci.ap_mem_base[1]; + HPI_DEBUG_LOG(VERBOSE, "csr %p, dsp %p\n", phw->dw2040_HPICSR, + phw->dw2040_HPIDSP); + + /* set addresses for the possible DSP HPI interfaces */ + for (dsp_index = 0; dsp_index < MAX_DSPS; dsp_index++) { + phw->ado[dsp_index].prHPI_control = + phw->dw2040_HPIDSP + (CONTROL + + DSP_SPACING * dsp_index); + + phw->ado[dsp_index].prHPI_address = + phw->dw2040_HPIDSP + (ADDRESS + + DSP_SPACING * dsp_index); + phw->ado[dsp_index].prHPI_data = + phw->dw2040_HPIDSP + (DATA + DSP_SPACING * dsp_index); + + phw->ado[dsp_index].prHPI_data_auto_inc = + phw->dw2040_HPIDSP + (DATA_AUTOINC + + DSP_SPACING * dsp_index); + + HPI_DEBUG_LOG(VERBOSE, "ctl %p, adr %p, dat %p, dat++ %p\n", + phw->ado[dsp_index].prHPI_control, + phw->ado[dsp_index].prHPI_address, + phw->ado[dsp_index].prHPI_data, + phw->ado[dsp_index].prHPI_data_auto_inc); + + phw->ado[dsp_index].pa_parent_adapter = pao; + } + + phw->pCI2040HPI_error_count = 0; + pao->has_control_cache = 0; + + /* Set the default number of DSPs on this card */ + /* This is (conditionally) adjusted after bootloading */ + /* of the first DSP in the bootload section. */ + phw->num_dsp = 1; + + boot_error = hpi6000_adapter_boot_load_dsp(pao, pos_error_code); + if (boot_error) + return boot_error; + + HPI_DEBUG_LOG(INFO, "bootload DSP OK\n"); + + phw->message_buffer_address_on_dsp = 0L; + phw->response_buffer_address_on_dsp = 0L; + + /* get info about the adapter by asking the adapter */ + /* send a HPI_ADAPTER_GET_INFO message */ + { + struct hpi_message hM; + struct hpi_response hR0; /* response from DSP 0 */ + struct hpi_response hR1; /* response from DSP 1 */ + u16 error = 0; + + HPI_DEBUG_LOG(VERBOSE, "send ADAPTER_GET_INFO\n"); + memset(&hM, 0, sizeof(hM)); + hM.type = HPI_TYPE_MESSAGE; + hM.size = sizeof(struct hpi_message); + hM.object = HPI_OBJ_ADAPTER; + hM.function = HPI_ADAPTER_GET_INFO; + hM.adapter_index = 0; + memset(&hR0, 0, sizeof(hR0)); + memset(&hR1, 0, sizeof(hR1)); + hR0.size = sizeof(hR0); + hR1.size = sizeof(hR1); + + error = hpi6000_message_response_sequence(pao, 0, &hM, &hR0); + if (hR0.error) { + HPI_DEBUG_LOG(DEBUG, "message error %d\n", hR0.error); + return hR0.error; + } + if (phw->num_dsp == 2) { + error = hpi6000_message_response_sequence(pao, 1, &hM, + &hR1); + if (error) + return error; + } + pao->adapter_type = hR0.u.a.adapter_type; + pao->index = hR0.u.a.adapter_index; + } + + memset(&phw->control_cache[0], 0, + sizeof(struct hpi_control_cache_single) * + HPI_NMIXER_CONTROLS); + /* Read the control cache length to figure out if it is turned on */ + control_cache_size = + hpi_read_word(&phw->ado[0], + HPI_HIF_ADDR(control_cache_size_in_bytes)); + if (control_cache_size) { + control_cache_count = + hpi_read_word(&phw->ado[0], + HPI_HIF_ADDR(control_cache_count)); + pao->has_control_cache = 1; + + phw->p_cache = + hpi_alloc_control_cache(control_cache_count, + control_cache_size, (struct hpi_control_cache_info *) + &phw->control_cache[0] + ); + } else + pao->has_control_cache = 0; + + HPI_DEBUG_LOG(DEBUG, "get adapter info ASI%04X index %d\n", + pao->adapter_type, pao->index); + pao->open = 0; /* upon creation the adapter is closed */ + return 0; +} + +/************************************************************************/ +/* ADAPTER */ + +static void adapter_get_asserts(struct hpi_adapter_obj *pao, + struct hpi_message *phm, struct hpi_response *phr) +{ +#ifndef HIDE_PCI_ASSERTS + /* if we have PCI2040 asserts then collect them */ + if ((gw_pci_read_asserts > 0) || (gw_pci_write_asserts > 0)) { + phr->u.a.serial_number = + gw_pci_read_asserts * 100 + gw_pci_write_asserts; + phr->u.a.adapter_index = 1; /* assert count */ + phr->u.a.adapter_type = -1; /* "dsp index" */ + strcpy(phr->u.a.sz_adapter_assert, "PCI2040 error"); + gw_pci_read_asserts = 0; + gw_pci_write_asserts = 0; + phr->error = 0; + } else +#endif + hw_message(pao, phm, phr); /*get DSP asserts */ + + return; +} + +/************************************************************************/ +/* LOW-LEVEL */ + +static short hpi6000_adapter_boot_load_dsp(struct hpi_adapter_obj *pao, + u32 *pos_error_code) +{ + struct hpi_hw_obj *phw = (struct hpi_hw_obj *)pao->priv; + short error; + u32 timeout; + u32 read = 0; + u32 i = 0; + u32 data = 0; + u32 j = 0; + u32 test_addr = 0x80000000; + u32 test_data = 0x00000001; + u32 dw2040_reset = 0; + u32 dsp_index = 0; + u32 endian = 0; + u32 adapter_info = 0; + u32 delay = 0; + + struct dsp_code dsp_code; + u16 boot_load_family = 0; + + /* NOTE don't use wAdapterType in this routine. It is not setup yet */ + + switch (pao->pci.subsys_device_id) { + case 0x5100: + case 0x5110: /* ASI5100 revB or higher with C6711D */ + case 0x6100: + case 0x6200: + boot_load_family = HPI_ADAPTER_FAMILY_ASI(0x6200); + break; + case 0x8800: + boot_load_family = HPI_ADAPTER_FAMILY_ASI(0x8800); + break; + default: + return HPI6000_ERROR_UNHANDLED_SUBSYS_ID; + } + + /* reset all DSPs, indicate two DSPs are present + * set RST3-=1 to disconnect HAD8 to set DSP in little endian mode + */ + endian = 0; + dw2040_reset = 0x0003000F; + iowrite32(dw2040_reset, phw->dw2040_HPICSR + HPI_RESET); + + /* read back register to make sure PCI2040 chip is functioning + * note that bits 4..15 are read-only and so should always return zero, + * even though we wrote 1 to them + */ + for (i = 0; i < 1000; i++) + delay = ioread32(phw->dw2040_HPICSR + HPI_RESET); + if (delay != dw2040_reset) { + HPI_DEBUG_LOG(ERROR, "INIT_PCI2040 %x %x\n", dw2040_reset, + delay); + return HPI6000_ERROR_INIT_PCI2040; + } + + /* Indicate that DSP#0,1 is a C6X */ + iowrite32(0x00000003, phw->dw2040_HPICSR + HPI_DATA_WIDTH); + /* set Bit30 and 29 - which will prevent Target aborts from being + * issued upon HPI or GP error + */ + iowrite32(0x60000000, phw->dw2040_HPICSR + INTERRUPT_MASK_SET); + + /* isolate DSP HAD8 line from PCI2040 so that + * Little endian can be set by pullup + */ + dw2040_reset = dw2040_reset & (~(endian << 3)); + iowrite32(dw2040_reset, phw->dw2040_HPICSR + HPI_RESET); + + phw->ado[0].c_dsp_rev = 'B'; /* revB */ + phw->ado[1].c_dsp_rev = 'B'; /* revB */ + + /*Take both DSPs out of reset, setting HAD8 to the correct Endian */ + dw2040_reset = dw2040_reset & (~0x00000001); /* start DSP 0 */ + iowrite32(dw2040_reset, phw->dw2040_HPICSR + HPI_RESET); + dw2040_reset = dw2040_reset & (~0x00000002); /* start DSP 1 */ + iowrite32(dw2040_reset, phw->dw2040_HPICSR + HPI_RESET); + + /* set HAD8 back to PCI2040, now that DSP set to little endian mode */ + dw2040_reset = dw2040_reset & (~0x00000008); + iowrite32(dw2040_reset, phw->dw2040_HPICSR + HPI_RESET); + /*delay to allow DSP to get going */ + for (i = 0; i < 100; i++) + delay = ioread32(phw->dw2040_HPICSR + HPI_RESET); + + /* loop through all DSPs, downloading DSP code */ + for (dsp_index = 0; dsp_index < phw->num_dsp; dsp_index++) { + struct dsp_obj *pdo = &phw->ado[dsp_index]; + + /* configure DSP so that we download code into the SRAM */ + /* set control reg for little endian, HWOB=1 */ + iowrite32(0x00010001, pdo->prHPI_control); + + /* test access to the HPI address register (HPIA) */ + test_data = 0x00000001; + for (j = 0; j < 32; j++) { + iowrite32(test_data, pdo->prHPI_address); + data = ioread32(pdo->prHPI_address); + if (data != test_data) { + HPI_DEBUG_LOG(ERROR, "INIT_DSPHPI %x %x %x\n", + test_data, data, dsp_index); + return HPI6000_ERROR_INIT_DSPHPI; + } + test_data = test_data << 1; + } + +/* if C6713 the setup PLL to generate 225MHz from 25MHz. +* Since the PLLDIV1 read is sometimes wrong, even on a C6713, +* we're going to do this unconditionally +*/ +/* PLLDIV1 should have a value of 8000 after reset */ +/* + if (HpiReadWord(pdo,0x01B7C118) == 0x8000) +*/ + { + /* C6713 datasheet says we cannot program PLL from HPI, + * and indeed if we try to set the PLL multiply from the + * HPI, the PLL does not seem to lock, + * so we enable the PLL and use the default of x 7 + */ + /* bypass PLL */ + hpi_write_word(pdo, 0x01B7C100, 0x0000); + for (i = 0; i < 100; i++) + delay = ioread32(phw->dw2040_HPICSR + + HPI_RESET); + + /* ** use default of PLL x7 ** */ + /* EMIF = 225/3=75MHz */ + hpi_write_word(pdo, 0x01B7C120, 0x8002); + /* peri = 225/2 */ + hpi_write_word(pdo, 0x01B7C11C, 0x8001); + /* cpu = 225/1 */ + hpi_write_word(pdo, 0x01B7C118, 0x8000); + /* ~200us delay */ + for (i = 0; i < 2000; i++) + delay = ioread32(phw->dw2040_HPICSR + + HPI_RESET); + /* PLL not bypassed */ + hpi_write_word(pdo, 0x01B7C100, 0x0001); + /* ~200us delay */ + for (i = 0; i < 2000; i++) + delay = ioread32(phw->dw2040_HPICSR + + HPI_RESET); + } + + /* test r/w to internal DSP memory + * C6711 has L2 cache mapped to 0x0 when reset + * + * revB - because of bug 3.0.1 last HPI read + * (before HPI address issued) must be non-autoinc + */ + /* test each bit in the 32bit word */ + for (i = 0; i < 100; i++) { + test_addr = 0x00000000; + test_data = 0x00000001; + for (j = 0; j < 32; j++) { + hpi_write_word(pdo, test_addr + i, test_data); + data = hpi_read_word(pdo, test_addr + i); + if (data != test_data) { + HPI_DEBUG_LOG(ERROR, + "DSP mem %x %x %x %x\n", + test_addr + i, test_data, + data, dsp_index); + + return HPI6000_ERROR_INIT_DSPINTMEM; + } + test_data = test_data << 1; + } + } + + /* memory map of ASI6200 + 00000000-0000FFFF 16Kx32 internal program + 01800000-019FFFFF Internal peripheral + 80000000-807FFFFF CE0 2Mx32 SDRAM running @ 100MHz + 90000000-9000FFFF CE1 Async peripherals: + + EMIF config + ------------ + Global EMIF control + 0 - + 1 - + 2 - + 3 CLK2EN = 1 CLKOUT2 enabled + 4 CLK1EN = 0 CLKOUT1 disabled + 5 EKEN = 1 <--!! C6713 specific, enables ECLKOUT + 6 - + 7 NOHOLD = 1 external HOLD disabled + 8 HOLDA = 0 HOLDA output is low + 9 HOLD = 0 HOLD input is low + 10 ARDY = 1 ARDY input is high + 11 BUSREQ = 0 BUSREQ output is low + 12,13 Reserved = 1 + */ + hpi_write_word(pdo, 0x01800000, 0x34A8); + + /* EMIF CE0 setup - 2Mx32 Sync DRAM + 31..28 Wr setup + 27..22 Wr strobe + 21..20 Wr hold + 19..16 Rd setup + 15..14 - + 13..8 Rd strobe + 7..4 MTYPE 0011 Sync DRAM 32bits + 3 Wr hold MSB + 2..0 Rd hold + */ + hpi_write_word(pdo, 0x01800008, 0x00000030); + + /* EMIF SDRAM Extension + 31-21 0 + 20 WR2RD = 0 + 19-18 WR2DEAC = 1 + 17 WR2WR = 0 + 16-15 R2WDQM = 2 + 14-12 RD2WR = 4 + 11-10 RD2DEAC = 1 + 9 RD2RD = 1 + 8-7 THZP = 10b + 6-5 TWR = 2-1 = 01b (tWR = 10ns) + 4 TRRD = 0b = 2 ECLK (tRRD = 14ns) + 3-1 TRAS = 5-1 = 100b (Tras=42ns = 5 ECLK) + 1 CAS latency = 3 ECLK + (for Micron 2M32-7 operating at 100Mhz) + */ + + /* need to use this else DSP code crashes */ + hpi_write_word(pdo, 0x01800020, 0x001BDF29); + + /* EMIF SDRAM control - set up for a 2Mx32 SDRAM (512x32x4 bank) + 31 - - + 30 SDBSZ 1 4 bank + 29..28 SDRSZ 00 11 row address pins + 27..26 SDCSZ 01 8 column address pins + 25 RFEN 1 refersh enabled + 24 INIT 1 init SDRAM + 23..20 TRCD 0001 + 19..16 TRP 0001 + 15..12 TRC 0110 + 11..0 - - + */ + /* need to use this else DSP code crashes */ + hpi_write_word(pdo, 0x01800018, 0x47117000); + + /* EMIF SDRAM Refresh Timing */ + hpi_write_word(pdo, 0x0180001C, 0x00000410); + + /*MIF CE1 setup - Async peripherals + @100MHz bus speed, each cycle is 10ns, + 31..28 Wr setup = 1 + 27..22 Wr strobe = 3 30ns + 21..20 Wr hold = 1 + 19..16 Rd setup =1 + 15..14 Ta = 2 + 13..8 Rd strobe = 3 30ns + 7..4 MTYPE 0010 Async 32bits + 3 Wr hold MSB =0 + 2..0 Rd hold = 1 + */ + { + u32 cE1 = + (1L << 28) | (3L << 22) | (1L << 20) | (1L << + 16) | (2L << 14) | (3L << 8) | (2L << 4) | 1L; + hpi_write_word(pdo, 0x01800004, cE1); + } + + /* delay a little to allow SDRAM and DSP to "get going" */ + + for (i = 0; i < 1000; i++) + delay = ioread32(phw->dw2040_HPICSR + HPI_RESET); + + /* test access to SDRAM */ + { + test_addr = 0x80000000; + test_data = 0x00000001; + /* test each bit in the 32bit word */ + for (j = 0; j < 32; j++) { + hpi_write_word(pdo, test_addr, test_data); + data = hpi_read_word(pdo, test_addr); + if (data != test_data) { + HPI_DEBUG_LOG(ERROR, + "DSP dram %x %x %x %x\n", + test_addr, test_data, data, + dsp_index); + + return HPI6000_ERROR_INIT_SDRAM1; + } + test_data = test_data << 1; + } + /* test every Nth address in the DRAM */ +#define DRAM_SIZE_WORDS 0x200000 /*2_mx32 */ +#define DRAM_INC 1024 + test_addr = 0x80000000; + test_data = 0x0; + for (i = 0; i < DRAM_SIZE_WORDS; i = i + DRAM_INC) { + hpi_write_word(pdo, test_addr + i, test_data); + test_data++; + } + test_addr = 0x80000000; + test_data = 0x0; + for (i = 0; i < DRAM_SIZE_WORDS; i = i + DRAM_INC) { + data = hpi_read_word(pdo, test_addr + i); + if (data != test_data) { + HPI_DEBUG_LOG(ERROR, + "DSP dram %x %x %x %x\n", + test_addr + i, test_data, + data, dsp_index); + return HPI6000_ERROR_INIT_SDRAM2; + } + test_data++; + } + + } + + /* write the DSP code down into the DSPs memory */ + /*HpiDspCode_Open(nBootLoadFamily,&DspCode,pdwOsErrorCode); */ + dsp_code.ps_dev = pao->pci.p_os_data; + + error = hpi_dsp_code_open(boot_load_family, &dsp_code, + pos_error_code); + + if (error) + return error; + + while (1) { + u32 length; + u32 address; + u32 type; + u32 *pcode; + + error = hpi_dsp_code_read_word(&dsp_code, &length); + if (error) + break; + if (length == 0xFFFFFFFF) + break; /* end of code */ + + error = hpi_dsp_code_read_word(&dsp_code, &address); + if (error) + break; + error = hpi_dsp_code_read_word(&dsp_code, &type); + if (error) + break; + error = hpi_dsp_code_read_block(length, &dsp_code, + &pcode); + if (error) + break; + error = hpi6000_dsp_block_write32(pao, (u16)dsp_index, + address, pcode, length); + if (error) + break; + } + + if (error) { + hpi_dsp_code_close(&dsp_code); + return error; + } + /* verify that code was written correctly */ + /* this time through, assume no errors in DSP code file/array */ + hpi_dsp_code_rewind(&dsp_code); + while (1) { + u32 length; + u32 address; + u32 type; + u32 *pcode; + + hpi_dsp_code_read_word(&dsp_code, &length); + if (length == 0xFFFFFFFF) + break; /* end of code */ + + hpi_dsp_code_read_word(&dsp_code, &address); + hpi_dsp_code_read_word(&dsp_code, &type); + hpi_dsp_code_read_block(length, &dsp_code, &pcode); + + for (i = 0; i < length; i++) { + data = hpi_read_word(pdo, address); + if (data != *pcode) { + error = HPI6000_ERROR_INIT_VERIFY; + HPI_DEBUG_LOG(ERROR, + "DSP verify %x %x %x %x\n", + address, *pcode, data, + dsp_index); + break; + } + pcode++; + address += 4; + } + if (error) + break; + } + hpi_dsp_code_close(&dsp_code); + if (error) + return error; + + /* zero out the hostmailbox */ + { + u32 address = HPI_HIF_ADDR(host_cmd); + for (i = 0; i < 4; i++) { + hpi_write_word(pdo, address, 0); + address += 4; + } + } + /* write the DSP number into the hostmailbox */ + /* structure before starting the DSP */ + hpi_write_word(pdo, HPI_HIF_ADDR(dsp_number), dsp_index); + + /* write the DSP adapter Info into the */ + /* hostmailbox before starting the DSP */ + if (dsp_index > 0) + hpi_write_word(pdo, HPI_HIF_ADDR(adapter_info), + adapter_info); + + /* step 3. Start code by sending interrupt */ + iowrite32(0x00030003, pdo->prHPI_control); + for (i = 0; i < 10000; i++) + delay = ioread32(phw->dw2040_HPICSR + HPI_RESET); + + /* wait for a non-zero value in hostcmd - + * indicating initialization is complete + * + * Init could take a while if DSP checks SDRAM memory + * Was 200000. Increased to 2000000 for ASI8801 so we + * don't get 938 errors. + */ + timeout = 2000000; + while (timeout) { + do { + read = hpi_read_word(pdo, + HPI_HIF_ADDR(host_cmd)); + } while (--timeout + && hpi6000_check_PCI2040_error_flag(pao, + H6READ)); + + if (read) + break; + /* The following is a workaround for bug #94: + * Bluescreen on install and subsequent boots on a + * DELL PowerEdge 600SC PC with 1.8GHz P4 and + * ServerWorks chipset. Without this delay the system + * locks up with a bluescreen (NOT GPF or pagefault). + */ + else + hpios_delay_micro_seconds(1000); + } + if (timeout == 0) + return HPI6000_ERROR_INIT_NOACK; + + /* read the DSP adapter Info from the */ + /* hostmailbox structure after starting the DSP */ + if (dsp_index == 0) { + /*u32 dwTestData=0; */ + u32 mask = 0; + + adapter_info = + hpi_read_word(pdo, + HPI_HIF_ADDR(adapter_info)); + if (HPI_ADAPTER_FAMILY_ASI + (HPI_HIF_ADAPTER_INFO_EXTRACT_ADAPTER + (adapter_info)) == + HPI_ADAPTER_FAMILY_ASI(0x6200)) + /* all 6200 cards have this many DSPs */ + phw->num_dsp = 2; + + /* test that the PLD is programmed */ + /* and we can read/write 24bits */ +#define PLD_BASE_ADDRESS 0x90000000L /*for ASI6100/6200/8800 */ + + switch (boot_load_family) { + case HPI_ADAPTER_FAMILY_ASI(0x6200): + /* ASI6100/6200 has 24bit path to FPGA */ + mask = 0xFFFFFF00L; + /* ASI5100 uses AX6 code, */ + /* but has no PLD r/w register to test */ + if (HPI_ADAPTER_FAMILY_ASI(pao->pci. + subsys_device_id) == + HPI_ADAPTER_FAMILY_ASI(0x5100)) + mask = 0x00000000L; + break; + case HPI_ADAPTER_FAMILY_ASI(0x8800): + /* ASI8800 has 16bit path to FPGA */ + mask = 0xFFFF0000L; + break; + } + test_data = 0xAAAAAA00L & mask; + /* write to 24 bit Debug register (D31-D8) */ + hpi_write_word(pdo, PLD_BASE_ADDRESS + 4L, test_data); + read = hpi_read_word(pdo, + PLD_BASE_ADDRESS + 4L) & mask; + if (read != test_data) { + HPI_DEBUG_LOG(ERROR, "PLD %x %x\n", test_data, + read); + return HPI6000_ERROR_INIT_PLDTEST1; + } + test_data = 0x55555500L & mask; + hpi_write_word(pdo, PLD_BASE_ADDRESS + 4L, test_data); + read = hpi_read_word(pdo, + PLD_BASE_ADDRESS + 4L) & mask; + if (read != test_data) { + HPI_DEBUG_LOG(ERROR, "PLD %x %x\n", test_data, + read); + return HPI6000_ERROR_INIT_PLDTEST2; + } + } + } /* for numDSP */ + return 0; +} + +#define PCI_TIMEOUT 100 + +static int hpi_set_address(struct dsp_obj *pdo, u32 address) +{ + u32 timeout = PCI_TIMEOUT; + + do { + iowrite32(address, pdo->prHPI_address); + } while (hpi6000_check_PCI2040_error_flag(pdo->pa_parent_adapter, + H6WRITE) + && --timeout); + + if (timeout) + return 0; + + return 1; +} + +/* write one word to the HPI port */ +static void hpi_write_word(struct dsp_obj *pdo, u32 address, u32 data) +{ + if (hpi_set_address(pdo, address)) + return; + iowrite32(data, pdo->prHPI_data); +} + +/* read one word from the HPI port */ +static u32 hpi_read_word(struct dsp_obj *pdo, u32 address) +{ + u32 data = 0; + + if (hpi_set_address(pdo, address)) + return 0; /*? no way to return error */ + + /* take care of errata in revB DSP (2.0.1) */ + data = ioread32(pdo->prHPI_data); + return data; +} + +/* write a block of 32bit words to the DSP HPI port using auto-inc mode */ +static void hpi_write_block(struct dsp_obj *pdo, u32 address, u32 *pdata, + u32 length) +{ + u16 length16 = length - 1; + + if (length == 0) + return; + + if (hpi_set_address(pdo, address)) + return; + + iowrite32_rep(pdo->prHPI_data_auto_inc, pdata, length16); + + /* take care of errata in revB DSP (2.0.1) */ + /* must end with non auto-inc */ + iowrite32(*(pdata + length - 1), pdo->prHPI_data); +} + +/** read a block of 32bit words from the DSP HPI port using auto-inc mode + */ +static void hpi_read_block(struct dsp_obj *pdo, u32 address, u32 *pdata, + u32 length) +{ + u16 length16 = length - 1; + + if (length == 0) + return; + + if (hpi_set_address(pdo, address)) + return; + + ioread32_rep(pdo->prHPI_data_auto_inc, pdata, length16); + + /* take care of errata in revB DSP (2.0.1) */ + /* must end with non auto-inc */ + *(pdata + length - 1) = ioread32(pdo->prHPI_data); +} + +static u16 hpi6000_dsp_block_write32(struct hpi_adapter_obj *pao, + u16 dsp_index, u32 hpi_address, u32 *source, u32 count) +{ + struct dsp_obj *pdo = + &(*(struct hpi_hw_obj *)pao->priv).ado[dsp_index]; + u32 time_out = PCI_TIMEOUT; + int c6711_burst_size = 128; + u32 local_hpi_address = hpi_address; + int local_count = count; + int xfer_size; + u32 *pdata = source; + + while (local_count) { + if (local_count > c6711_burst_size) + xfer_size = c6711_burst_size; + else + xfer_size = local_count; + + time_out = PCI_TIMEOUT; + do { + hpi_write_block(pdo, local_hpi_address, pdata, + xfer_size); + } while (hpi6000_check_PCI2040_error_flag(pao, H6WRITE) + && --time_out); + + if (!time_out) + break; + pdata += xfer_size; + local_hpi_address += sizeof(u32) * xfer_size; + local_count -= xfer_size; + } + + if (time_out) + return 0; + else + return 1; +} + +static u16 hpi6000_dsp_block_read32(struct hpi_adapter_obj *pao, + u16 dsp_index, u32 hpi_address, u32 *dest, u32 count) +{ + struct dsp_obj *pdo = + &(*(struct hpi_hw_obj *)pao->priv).ado[dsp_index]; + u32 time_out = PCI_TIMEOUT; + int c6711_burst_size = 16; + u32 local_hpi_address = hpi_address; + int local_count = count; + int xfer_size; + u32 *pdata = dest; + u32 loop_count = 0; + + while (local_count) { + if (local_count > c6711_burst_size) + xfer_size = c6711_burst_size; + else + xfer_size = local_count; + + time_out = PCI_TIMEOUT; + do { + hpi_read_block(pdo, local_hpi_address, pdata, + xfer_size); + } while (hpi6000_check_PCI2040_error_flag(pao, H6READ) + && --time_out); + if (!time_out) + break; + + pdata += xfer_size; + local_hpi_address += sizeof(u32) * xfer_size; + local_count -= xfer_size; + loop_count++; + } + + if (time_out) + return 0; + else + return 1; +} + +static short hpi6000_message_response_sequence(struct hpi_adapter_obj *pao, + u16 dsp_index, struct hpi_message *phm, struct hpi_response *phr) +{ + struct hpi_hw_obj *phw = (struct hpi_hw_obj *)pao->priv; + struct dsp_obj *pdo = &phw->ado[dsp_index]; + u32 timeout; + u16 ack; + u32 address; + u32 length; + u32 *p_data; + u16 error = 0; + + /* does the DSP we are referencing exist? */ + if (dsp_index >= phw->num_dsp) + return HPI6000_ERROR_MSG_INVALID_DSP_INDEX; + + ack = hpi6000_wait_dsp_ack(pao, dsp_index, HPI_HIF_IDLE); + if (ack & HPI_HIF_ERROR_MASK) { + pao->dsp_crashed++; + return HPI6000_ERROR_MSG_RESP_IDLE_TIMEOUT; + } + pao->dsp_crashed = 0; + + /* send the message */ + + /* get the address and size */ + if (phw->message_buffer_address_on_dsp == 0) { + timeout = TIMEOUT; + do { + address = + hpi_read_word(pdo, + HPI_HIF_ADDR(message_buffer_address)); + phw->message_buffer_address_on_dsp = address; + } while (hpi6000_check_PCI2040_error_flag(pao, H6READ) + && --timeout); + if (!timeout) + return HPI6000_ERROR_MSG_GET_ADR; + } else + address = phw->message_buffer_address_on_dsp; + + /* dwLength = sizeof(struct hpi_message); */ + length = phm->size; + + /* send it */ + p_data = (u32 *)phm; + if (hpi6000_dsp_block_write32(pao, dsp_index, address, p_data, + (u16)length / 4)) + return HPI6000_ERROR_MSG_RESP_BLOCKWRITE32; + + if (hpi6000_send_host_command(pao, dsp_index, HPI_HIF_GET_RESP)) + return HPI6000_ERROR_MSG_RESP_GETRESPCMD; + hpi6000_send_dsp_interrupt(pdo); + + ack = hpi6000_wait_dsp_ack(pao, dsp_index, HPI_HIF_GET_RESP); + if (ack & HPI_HIF_ERROR_MASK) + return HPI6000_ERROR_MSG_RESP_GET_RESP_ACK; + + /* get the address and size */ + if (phw->response_buffer_address_on_dsp == 0) { + timeout = TIMEOUT; + do { + address = + hpi_read_word(pdo, + HPI_HIF_ADDR(response_buffer_address)); + } while (hpi6000_check_PCI2040_error_flag(pao, H6READ) + && --timeout); + phw->response_buffer_address_on_dsp = address; + + if (!timeout) + return HPI6000_ERROR_RESP_GET_ADR; + } else + address = phw->response_buffer_address_on_dsp; + + /* read the length of the response back from the DSP */ + timeout = TIMEOUT; + do { + length = hpi_read_word(pdo, HPI_HIF_ADDR(length)); + } while (hpi6000_check_PCI2040_error_flag(pao, H6READ) && --timeout); + if (!timeout) + length = sizeof(struct hpi_response); + + /* get it */ + p_data = (u32 *)phr; + if (hpi6000_dsp_block_read32(pao, dsp_index, address, p_data, + (u16)length / 4)) + return HPI6000_ERROR_MSG_RESP_BLOCKREAD32; + + /* set i/f back to idle */ + if (hpi6000_send_host_command(pao, dsp_index, HPI_HIF_IDLE)) + return HPI6000_ERROR_MSG_RESP_IDLECMD; + hpi6000_send_dsp_interrupt(pdo); + + error = hpi_validate_response(phm, phr); + return error; +} + +/* have to set up the below defines to match stuff in the MAP file */ + +#define MSG_ADDRESS (HPI_HIF_BASE+0x18) +#define MSG_LENGTH 11 +#define RESP_ADDRESS (HPI_HIF_BASE+0x44) +#define RESP_LENGTH 16 +#define QUEUE_START (HPI_HIF_BASE+0x88) +#define QUEUE_SIZE 0x8000 + +static short hpi6000_send_data_check_adr(u32 address, u32 length_in_dwords) +{ +/*#define CHECKING // comment this line in to enable checking */ +#ifdef CHECKING + if (address < (u32)MSG_ADDRESS) + return 0; + if (address > (u32)(QUEUE_START + QUEUE_SIZE)) + return 0; + if ((address + (length_in_dwords << 2)) > + (u32)(QUEUE_START + QUEUE_SIZE)) + return 0; +#else + (void)address; + (void)length_in_dwords; + return 1; +#endif +} + +static short hpi6000_send_data(struct hpi_adapter_obj *pao, u16 dsp_index, + struct hpi_message *phm, struct hpi_response *phr) +{ + struct dsp_obj *pdo = + &(*(struct hpi_hw_obj *)pao->priv).ado[dsp_index]; + u32 data_sent = 0; + u16 ack; + u32 length, address; + u32 *p_data = (u32 *)phm->u.d.u.data.pb_data; + u16 time_out = 8; + + (void)phr; + + /* round dwDataSize down to nearest 4 bytes */ + while ((data_sent < (phm->u.d.u.data.data_size & ~3L)) + && --time_out) { + ack = hpi6000_wait_dsp_ack(pao, dsp_index, HPI_HIF_IDLE); + if (ack & HPI_HIF_ERROR_MASK) + return HPI6000_ERROR_SEND_DATA_IDLE_TIMEOUT; + + if (hpi6000_send_host_command(pao, dsp_index, + HPI_HIF_SEND_DATA)) + return HPI6000_ERROR_SEND_DATA_CMD; + + hpi6000_send_dsp_interrupt(pdo); + + ack = hpi6000_wait_dsp_ack(pao, dsp_index, HPI_HIF_SEND_DATA); + + if (ack & HPI_HIF_ERROR_MASK) + return HPI6000_ERROR_SEND_DATA_ACK; + + do { + /* get the address and size */ + address = hpi_read_word(pdo, HPI_HIF_ADDR(address)); + /* DSP returns number of DWORDS */ + length = hpi_read_word(pdo, HPI_HIF_ADDR(length)); + } while (hpi6000_check_PCI2040_error_flag(pao, H6READ)); + + if (!hpi6000_send_data_check_adr(address, length)) + return HPI6000_ERROR_SEND_DATA_ADR; + + /* send the data. break data into 512 DWORD blocks (2K bytes) + * and send using block write. 2Kbytes is the max as this is the + * memory window given to the HPI data register by the PCI2040 + */ + + { + u32 len = length; + u32 blk_len = 512; + while (len) { + if (len < blk_len) + blk_len = len; + if (hpi6000_dsp_block_write32(pao, dsp_index, + address, p_data, blk_len)) + return HPI6000_ERROR_SEND_DATA_WRITE; + address += blk_len * 4; + p_data += blk_len; + len -= blk_len; + } + } + + if (hpi6000_send_host_command(pao, dsp_index, HPI_HIF_IDLE)) + return HPI6000_ERROR_SEND_DATA_IDLECMD; + + hpi6000_send_dsp_interrupt(pdo); + + data_sent += length * 4; + } + if (!time_out) + return HPI6000_ERROR_SEND_DATA_TIMEOUT; + return 0; +} + +static short hpi6000_get_data(struct hpi_adapter_obj *pao, u16 dsp_index, + struct hpi_message *phm, struct hpi_response *phr) +{ + struct dsp_obj *pdo = + &(*(struct hpi_hw_obj *)pao->priv).ado[dsp_index]; + u32 data_got = 0; + u16 ack; + u32 length, address; + u32 *p_data = (u32 *)phm->u.d.u.data.pb_data; + + (void)phr; /* this parameter not used! */ + + /* round dwDataSize down to nearest 4 bytes */ + while (data_got < (phm->u.d.u.data.data_size & ~3L)) { + ack = hpi6000_wait_dsp_ack(pao, dsp_index, HPI_HIF_IDLE); + if (ack & HPI_HIF_ERROR_MASK) + return HPI6000_ERROR_GET_DATA_IDLE_TIMEOUT; + + if (hpi6000_send_host_command(pao, dsp_index, + HPI_HIF_GET_DATA)) + return HPI6000_ERROR_GET_DATA_CMD; + hpi6000_send_dsp_interrupt(pdo); + + ack = hpi6000_wait_dsp_ack(pao, dsp_index, HPI_HIF_GET_DATA); + + if (ack & HPI_HIF_ERROR_MASK) + return HPI6000_ERROR_GET_DATA_ACK; + + /* get the address and size */ + do { + address = hpi_read_word(pdo, HPI_HIF_ADDR(address)); + length = hpi_read_word(pdo, HPI_HIF_ADDR(length)); + } while (hpi6000_check_PCI2040_error_flag(pao, H6READ)); + + /* read the data */ + { + u32 len = length; + u32 blk_len = 512; + while (len) { + if (len < blk_len) + blk_len = len; + if (hpi6000_dsp_block_read32(pao, dsp_index, + address, p_data, blk_len)) + return HPI6000_ERROR_GET_DATA_READ; + address += blk_len * 4; + p_data += blk_len; + len -= blk_len; + } + } + + if (hpi6000_send_host_command(pao, dsp_index, HPI_HIF_IDLE)) + return HPI6000_ERROR_GET_DATA_IDLECMD; + hpi6000_send_dsp_interrupt(pdo); + + data_got += length * 4; + } + return 0; +} + +static void hpi6000_send_dsp_interrupt(struct dsp_obj *pdo) +{ + iowrite32(0x00030003, pdo->prHPI_control); /* DSPINT */ +} + +static short hpi6000_send_host_command(struct hpi_adapter_obj *pao, + u16 dsp_index, u32 host_cmd) +{ + struct dsp_obj *pdo = + &(*(struct hpi_hw_obj *)pao->priv).ado[dsp_index]; + u32 timeout = TIMEOUT; + + /* set command */ + do { + hpi_write_word(pdo, HPI_HIF_ADDR(host_cmd), host_cmd); + /* flush the FIFO */ + hpi_set_address(pdo, HPI_HIF_ADDR(host_cmd)); + } while (hpi6000_check_PCI2040_error_flag(pao, H6WRITE) && --timeout); + + /* reset the interrupt bit */ + iowrite32(0x00040004, pdo->prHPI_control); + + if (timeout) + return 0; + else + return 1; +} + +/* if the PCI2040 has recorded an HPI timeout, reset the error and return 1 */ +static short hpi6000_check_PCI2040_error_flag(struct hpi_adapter_obj *pao, + u16 read_or_write) +{ + u32 hPI_error; + + struct hpi_hw_obj *phw = (struct hpi_hw_obj *)pao->priv; + + /* read the error bits from the PCI2040 */ + hPI_error = ioread32(phw->dw2040_HPICSR + HPI_ERROR_REPORT); + if (hPI_error) { + /* reset the error flag */ + iowrite32(0L, phw->dw2040_HPICSR + HPI_ERROR_REPORT); + phw->pCI2040HPI_error_count++; + if (read_or_write == 1) + gw_pci_read_asserts++; /************* inc global */ + else + gw_pci_write_asserts++; + return 1; + } else + return 0; +} + +static short hpi6000_wait_dsp_ack(struct hpi_adapter_obj *pao, u16 dsp_index, + u32 ack_value) +{ + struct dsp_obj *pdo = + &(*(struct hpi_hw_obj *)pao->priv).ado[dsp_index]; + u32 ack = 0L; + u32 timeout; + u32 hPIC = 0L; + + /* wait for host interrupt to signal ack is ready */ + timeout = TIMEOUT; + while (--timeout) { + hPIC = ioread32(pdo->prHPI_control); + if (hPIC & 0x04) /* 0x04 = HINT from DSP */ + break; + } + if (timeout == 0) + return HPI_HIF_ERROR_MASK; + + /* wait for dwAckValue */ + timeout = TIMEOUT; + while (--timeout) { + /* read the ack mailbox */ + ack = hpi_read_word(pdo, HPI_HIF_ADDR(dsp_ack)); + if (ack == ack_value) + break; + if ((ack & HPI_HIF_ERROR_MASK) + && !hpi6000_check_PCI2040_error_flag(pao, H6READ)) + break; + /*for (i=0;i<1000;i++) */ + /* dwPause=i+1; */ + } + if (ack & HPI_HIF_ERROR_MASK) + /* indicates bad read from DSP - + typically 0xffffff is read for some reason */ + ack = HPI_HIF_ERROR_MASK; + + if (timeout == 0) + ack = HPI_HIF_ERROR_MASK; + return (short)ack; +} + +static short hpi6000_update_control_cache(struct hpi_adapter_obj *pao, + struct hpi_message *phm) +{ + const u16 dsp_index = 0; + struct hpi_hw_obj *phw = (struct hpi_hw_obj *)pao->priv; + struct dsp_obj *pdo = &phw->ado[dsp_index]; + u32 timeout; + u32 cache_dirty_flag; + u16 err; + + hpios_dsplock_lock(pao); + + timeout = TIMEOUT; + do { + cache_dirty_flag = + hpi_read_word((struct dsp_obj *)pdo, + HPI_HIF_ADDR(control_cache_is_dirty)); + } while (hpi6000_check_PCI2040_error_flag(pao, H6READ) && --timeout); + if (!timeout) { + err = HPI6000_ERROR_CONTROL_CACHE_PARAMS; + goto unlock; + } + + if (cache_dirty_flag) { + /* read the cached controls */ + u32 address; + u32 length; + + timeout = TIMEOUT; + if (pdo->control_cache_address_on_dsp == 0) { + do { + address = + hpi_read_word((struct dsp_obj *)pdo, + HPI_HIF_ADDR(control_cache_address)); + + length = hpi_read_word((struct dsp_obj *)pdo, + HPI_HIF_ADDR + (control_cache_size_in_bytes)); + } while (hpi6000_check_PCI2040_error_flag(pao, H6READ) + && --timeout); + if (!timeout) { + err = HPI6000_ERROR_CONTROL_CACHE_ADDRLEN; + goto unlock; + } + pdo->control_cache_address_on_dsp = address; + pdo->control_cache_length_on_dsp = length; + } else { + address = pdo->control_cache_address_on_dsp; + length = pdo->control_cache_length_on_dsp; + } + + if (hpi6000_dsp_block_read32(pao, dsp_index, address, + (u32 *)&phw->control_cache[0], + length / sizeof(u32))) { + err = HPI6000_ERROR_CONTROL_CACHE_READ; + goto unlock; + } + do { + hpi_write_word((struct dsp_obj *)pdo, + HPI_HIF_ADDR(control_cache_is_dirty), 0); + /* flush the FIFO */ + hpi_set_address(pdo, HPI_HIF_ADDR(host_cmd)); + } while (hpi6000_check_PCI2040_error_flag(pao, H6WRITE) + && --timeout); + if (!timeout) { + err = HPI6000_ERROR_CONTROL_CACHE_FLUSH; + goto unlock; + } + + } + err = 0; + +unlock: + hpios_dsplock_unlock(pao); + return err; +} + +/** Get dsp index for multi DSP adapters only */ +static u16 get_dsp_index(struct hpi_adapter_obj *pao, struct hpi_message *phm) +{ + u16 ret = 0; + switch (phm->object) { + case HPI_OBJ_ISTREAM: + if (phm->obj_index < 2) + ret = 1; + break; + case HPI_OBJ_PROFILE: + ret = phm->obj_index; + break; + default: + break; + } + return ret; +} + +/** Complete transaction with DSP + +Send message, get response, send or get stream data if any. +*/ +static void hw_message(struct hpi_adapter_obj *pao, struct hpi_message *phm, + struct hpi_response *phr) +{ + u16 error = 0; + u16 dsp_index = 0; + u16 num_dsp = ((struct hpi_hw_obj *)pao->priv)->num_dsp; + hpios_dsplock_lock(pao); + + if (num_dsp < 2) + dsp_index = 0; + else { + dsp_index = get_dsp_index(pao, phm); + + /* is this checked on the DSP anyway? */ + if ((phm->function == HPI_ISTREAM_GROUP_ADD) + || (phm->function == HPI_OSTREAM_GROUP_ADD)) { + struct hpi_message hm; + u16 add_index; + hm.obj_index = phm->u.d.u.stream.stream_index; + hm.object = phm->u.d.u.stream.object_type; + add_index = get_dsp_index(pao, &hm); + if (add_index != dsp_index) { + phr->error = HPI_ERROR_NO_INTERDSP_GROUPS; + return; + } + } + } + error = hpi6000_message_response_sequence(pao, dsp_index, phm, phr); + + /* maybe an error response */ + if (error) { + /* something failed in the HPI/DSP interface */ + phr->error = error; + /* just the header of the response is valid */ + phr->size = sizeof(struct hpi_response_header); + goto err; + } + + if (phr->error != 0) /* something failed in the DSP */ + goto err; + + switch (phm->function) { + case HPI_OSTREAM_WRITE: + case HPI_ISTREAM_ANC_WRITE: + error = hpi6000_send_data(pao, dsp_index, phm, phr); + break; + case HPI_ISTREAM_READ: + case HPI_OSTREAM_ANC_READ: + error = hpi6000_get_data(pao, dsp_index, phm, phr); + break; + case HPI_ADAPTER_GET_ASSERT: + phr->u.a.adapter_index = 0; /* dsp 0 default */ + if (num_dsp == 2) { + if (!phr->u.a.adapter_type) { + /* no assert from dsp 0, check dsp 1 */ + error = hpi6000_message_response_sequence(pao, + 1, phm, phr); + phr->u.a.adapter_index = 1; + } + } + } + + if (error) + phr->error = error; + +err: + hpios_dsplock_unlock(pao); + return; +} diff --git a/sound/pci/asihpi/hpi6000.h b/sound/pci/asihpi/hpi6000.h new file mode 100644 index 000000000000..4c7d507c0ecd --- /dev/null +++ b/sound/pci/asihpi/hpi6000.h @@ -0,0 +1,70 @@ +/***************************************************************************** + + AudioScience HPI driver + Copyright (C) 1997-2010 AudioScience Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation; + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Public declarations for DSP Proramming Interface to TI C6701 + +Shared between hpi6000.c and DSP code + +(C) Copyright AudioScience Inc. 1998-2003 +******************************************************************************/ + +#ifndef _HPI6000_H_ +#define _HPI6000_H_ + +#define HPI_NMIXER_CONTROLS 200 + +/* + * Control caching is always supported in the HPI code. + * The DSP should make sure that dwControlCacheSizeInBytes is initialized to 0 + * during boot to make it in-active. + */ +struct hpi_hif_6000 { + u32 host_cmd; + u32 dsp_ack; + u32 address; + u32 length; + u32 message_buffer_address; + u32 response_buffer_address; + u32 dsp_number; + u32 adapter_info; + u32 control_cache_is_dirty; + u32 control_cache_address; + u32 control_cache_size_in_bytes; + u32 control_cache_count; +}; + +#define HPI_HIF_PACK_ADAPTER_INFO(adapter, version_major, version_minor) \ + ((adapter << 16) | (version_major << 8) | version_minor) +#define HPI_HIF_ADAPTER_INFO_EXTRACT_ADAPTER(adapterinfo) \ + ((adapterinfo >> 16) & 0xffff) +#define HPI_HIF_ADAPTER_INFO_EXTRACT_HWVERSION_MAJOR(adapterinfo) \ + ((adapterinfo >> 8) & 0xff) +#define HPI_HIF_ADAPTER_INFO_EXTRACT_HWVERSION_MINOR(adapterinfo) \ + (adapterinfo & 0xff) + +/* Command/status exchanged between host and DSP */ +#define HPI_HIF_IDLE 0 +#define HPI_HIF_SEND_MSG 1 +#define HPI_HIF_GET_RESP 2 +#define HPI_HIF_DATA_MASK 0x10 +#define HPI_HIF_SEND_DATA 0x13 +#define HPI_HIF_GET_DATA 0x14 +#define HPI_HIF_SEND_DONE 5 +#define HPI_HIF_RESET 9 + +#endif /* _HPI6000_H_ */ diff --git a/sound/pci/asihpi/hpi6205.c b/sound/pci/asihpi/hpi6205.c new file mode 100644 index 000000000000..8df2ff76d944 --- /dev/null +++ b/sound/pci/asihpi/hpi6205.c @@ -0,0 +1,2332 @@ +/****************************************************************************** + + AudioScience HPI driver + Copyright (C) 1997-2010 AudioScience Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation; + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Hardware Programming Interface (HPI) for AudioScience + ASI50xx, AS51xx, ASI6xxx, ASI87xx ASI89xx series adapters. + These PCI and PCIe bus adapters are based on a + TMS320C6205 PCI bus mastering DSP, + and (except ASI50xx) TI TMS320C6xxx floating point DSP + + Exported function: + void HPI_6205(struct hpi_message *phm, struct hpi_response *phr) + +(C) Copyright AudioScience Inc. 1998-2010 +*******************************************************************************/ +#define SOURCEFILE_NAME "hpi6205.c" + +#include "hpi_internal.h" +#include "hpimsginit.h" +#include "hpidebug.h" +#include "hpi6205.h" +#include "hpidspcd.h" +#include "hpicmn.h" + +/*****************************************************************************/ +/* HPI6205 specific error codes */ +#define HPI6205_ERROR_BASE 1000 +/*#define HPI6205_ERROR_MEM_ALLOC 1001 */ +#define HPI6205_ERROR_6205_NO_IRQ 1002 +#define HPI6205_ERROR_6205_INIT_FAILED 1003 +/*#define HPI6205_ERROR_MISSING_DSPCODE 1004 */ +#define HPI6205_ERROR_UNKNOWN_PCI_DEVICE 1005 +#define HPI6205_ERROR_6205_REG 1006 +#define HPI6205_ERROR_6205_DSPPAGE 1007 +#define HPI6205_ERROR_BAD_DSPINDEX 1008 +#define HPI6205_ERROR_C6713_HPIC 1009 +#define HPI6205_ERROR_C6713_HPIA 1010 +#define HPI6205_ERROR_C6713_PLL 1011 +#define HPI6205_ERROR_DSP_INTMEM 1012 +#define HPI6205_ERROR_DSP_EXTMEM 1013 +#define HPI6205_ERROR_DSP_PLD 1014 +#define HPI6205_ERROR_MSG_RESP_IDLE_TIMEOUT 1015 +#define HPI6205_ERROR_MSG_RESP_TIMEOUT 1016 +#define HPI6205_ERROR_6205_EEPROM 1017 +#define HPI6205_ERROR_DSP_EMIF 1018 + +#define hpi6205_error(dsp_index, err) (err) +/*****************************************************************************/ +/* for C6205 PCI i/f */ +/* Host Status Register (HSR) bitfields */ +#define C6205_HSR_INTSRC 0x01 +#define C6205_HSR_INTAVAL 0x02 +#define C6205_HSR_INTAM 0x04 +#define C6205_HSR_CFGERR 0x08 +#define C6205_HSR_EEREAD 0x10 +/* Host-to-DSP Control Register (HDCR) bitfields */ +#define C6205_HDCR_WARMRESET 0x01 +#define C6205_HDCR_DSPINT 0x02 +#define C6205_HDCR_PCIBOOT 0x04 +/* DSP Page Register (DSPP) bitfields, */ +/* defines 4 Mbyte page that BAR0 points to */ +#define C6205_DSPP_MAP1 0x400 + +/* BAR0 maps to prefetchable 4 Mbyte memory block set by DSPP. + * BAR1 maps to non-prefetchable 8 Mbyte memory block + * of DSP memory mapped registers (starting at 0x01800000). + * 0x01800000 is hardcoded in the PCI i/f, so that only the offset from this + * needs to be added to the BAR1 base address set in the PCI config reg + */ +#define C6205_BAR1_PCI_IO_OFFSET (0x027FFF0L) +#define C6205_BAR1_HSR (C6205_BAR1_PCI_IO_OFFSET) +#define C6205_BAR1_HDCR (C6205_BAR1_PCI_IO_OFFSET+4) +#define C6205_BAR1_DSPP (C6205_BAR1_PCI_IO_OFFSET+8) + +/* used to control LED (revA) and reset C6713 (revB) */ +#define C6205_BAR0_TIMER1_CTL (0x01980000L) + +/* For first 6713 in CE1 space, using DA17,16,2 */ +#define HPICL_ADDR 0x01400000L +#define HPICH_ADDR 0x01400004L +#define HPIAL_ADDR 0x01410000L +#define HPIAH_ADDR 0x01410004L +#define HPIDIL_ADDR 0x01420000L +#define HPIDIH_ADDR 0x01420004L +#define HPIDL_ADDR 0x01430000L +#define HPIDH_ADDR 0x01430004L + +#define C6713_EMIF_GCTL 0x01800000 +#define C6713_EMIF_CE1 0x01800004 +#define C6713_EMIF_CE0 0x01800008 +#define C6713_EMIF_CE2 0x01800010 +#define C6713_EMIF_CE3 0x01800014 +#define C6713_EMIF_SDRAMCTL 0x01800018 +#define C6713_EMIF_SDRAMTIMING 0x0180001C +#define C6713_EMIF_SDRAMEXT 0x01800020 + +struct hpi_hw_obj { + /* PCI registers */ + __iomem u32 *prHSR; + __iomem u32 *prHDCR; + __iomem u32 *prDSPP; + + u32 dsp_page; + + struct consistent_dma_area h_locked_mem; + struct bus_master_interface *p_interface_buffer; + + u16 flag_outstream_just_reset[HPI_MAX_STREAMS]; + /* a non-NULL handle means there is an HPI allocated buffer */ + struct consistent_dma_area instream_host_buffers[HPI_MAX_STREAMS]; + struct consistent_dma_area outstream_host_buffers[HPI_MAX_STREAMS]; + /* non-zero size means a buffer exists, may be external */ + u32 instream_host_buffer_size[HPI_MAX_STREAMS]; + u32 outstream_host_buffer_size[HPI_MAX_STREAMS]; + + struct consistent_dma_area h_control_cache; + struct consistent_dma_area h_async_event_buffer; +/* struct hpi_control_cache_single *pControlCache; */ + struct hpi_async_event *p_async_event_buffer; + struct hpi_control_cache *p_cache; +}; + +/*****************************************************************************/ +/* local prototypes */ + +#define check_before_bbm_copy(status, p_bbm_data, l_first_write, l_second_write) + +static int wait_dsp_ack(struct hpi_hw_obj *phw, int state, int timeout_us); + +static void send_dsp_command(struct hpi_hw_obj *phw, int cmd); + +static u16 adapter_boot_load_dsp(struct hpi_adapter_obj *pao, + u32 *pos_error_code); + +static u16 message_response_sequence(struct hpi_adapter_obj *pao, + struct hpi_message *phm, struct hpi_response *phr); + +static void hw_message(struct hpi_adapter_obj *pao, struct hpi_message *phm, + struct hpi_response *phr); + +#define HPI6205_TIMEOUT 1000000 + +static void subsys_create_adapter(struct hpi_message *phm, + struct hpi_response *phr); +static void subsys_delete_adapter(struct hpi_message *phm, + struct hpi_response *phr); + +static u16 create_adapter_obj(struct hpi_adapter_obj *pao, + u32 *pos_error_code); + +static void delete_adapter_obj(struct hpi_adapter_obj *pao); + +static void outstream_host_buffer_allocate(struct hpi_adapter_obj *pao, + struct hpi_message *phm, struct hpi_response *phr); + +static void outstream_host_buffer_get_info(struct hpi_adapter_obj *pao, + struct hpi_message *phm, struct hpi_response *phr); + +static void outstream_host_buffer_free(struct hpi_adapter_obj *pao, + struct hpi_message *phm, struct hpi_response *phr); +static void outstream_write(struct hpi_adapter_obj *pao, + struct hpi_message *phm, struct hpi_response *phr); + +static void outstream_get_info(struct hpi_adapter_obj *pao, + struct hpi_message *phm, struct hpi_response *phr); + +static void outstream_start(struct hpi_adapter_obj *pao, + struct hpi_message *phm, struct hpi_response *phr); + +static void outstream_open(struct hpi_adapter_obj *pao, + struct hpi_message *phm, struct hpi_response *phr); + +static void outstream_reset(struct hpi_adapter_obj *pao, + struct hpi_message *phm, struct hpi_response *phr); + +static void instream_host_buffer_allocate(struct hpi_adapter_obj *pao, + struct hpi_message *phm, struct hpi_response *phr); + +static void instream_host_buffer_get_info(struct hpi_adapter_obj *pao, + struct hpi_message *phm, struct hpi_response *phr); + +static void instream_host_buffer_free(struct hpi_adapter_obj *pao, + struct hpi_message *phm, struct hpi_response *phr); + +static void instream_read(struct hpi_adapter_obj *pao, + struct hpi_message *phm, struct hpi_response *phr); + +static void instream_get_info(struct hpi_adapter_obj *pao, + struct hpi_message *phm, struct hpi_response *phr); + +static void instream_start(struct hpi_adapter_obj *pao, + struct hpi_message *phm, struct hpi_response *phr); + +static u32 boot_loader_read_mem32(struct hpi_adapter_obj *pao, int dsp_index, + u32 address); + +static u16 boot_loader_write_mem32(struct hpi_adapter_obj *pao, int dsp_index, + u32 address, u32 data); + +static u16 boot_loader_config_emif(struct hpi_adapter_obj *pao, + int dsp_index); + +static u16 boot_loader_test_memory(struct hpi_adapter_obj *pao, int dsp_index, + u32 address, u32 length); + +static u16 boot_loader_test_internal_memory(struct hpi_adapter_obj *pao, + int dsp_index); + +static u16 boot_loader_test_external_memory(struct hpi_adapter_obj *pao, + int dsp_index); + +static u16 boot_loader_test_pld(struct hpi_adapter_obj *pao, int dsp_index); + +/*****************************************************************************/ + +static void subsys_message(struct hpi_message *phm, struct hpi_response *phr) +{ + + switch (phm->function) { + case HPI_SUBSYS_OPEN: + case HPI_SUBSYS_CLOSE: + case HPI_SUBSYS_GET_INFO: + case HPI_SUBSYS_DRIVER_UNLOAD: + case HPI_SUBSYS_DRIVER_LOAD: + case HPI_SUBSYS_FIND_ADAPTERS: + /* messages that should not get here */ + phr->error = HPI_ERROR_UNIMPLEMENTED; + break; + case HPI_SUBSYS_CREATE_ADAPTER: + subsys_create_adapter(phm, phr); + break; + case HPI_SUBSYS_DELETE_ADAPTER: + subsys_delete_adapter(phm, phr); + break; + default: + phr->error = HPI_ERROR_INVALID_FUNC; + break; + } +} + +static void control_message(struct hpi_adapter_obj *pao, + struct hpi_message *phm, struct hpi_response *phr) +{ + + struct hpi_hw_obj *phw = pao->priv; + + switch (phm->function) { + case HPI_CONTROL_GET_STATE: + if (pao->has_control_cache) { + rmb(); /* make sure we see updates DM_aed from DSP */ + if (hpi_check_control_cache(phw->p_cache, phm, phr)) + break; + } + hw_message(pao, phm, phr); + break; + case HPI_CONTROL_GET_INFO: + hw_message(pao, phm, phr); + break; + case HPI_CONTROL_SET_STATE: + hw_message(pao, phm, phr); + if (pao->has_control_cache) + hpi_sync_control_cache(phw->p_cache, phm, phr); + break; + default: + phr->error = HPI_ERROR_INVALID_FUNC; + break; + } +} + +static void adapter_message(struct hpi_adapter_obj *pao, + struct hpi_message *phm, struct hpi_response *phr) +{ + switch (phm->function) { + default: + hw_message(pao, phm, phr); + break; + } +} + +static void outstream_message(struct hpi_adapter_obj *pao, + struct hpi_message *phm, struct hpi_response *phr) +{ + + if (phm->obj_index >= HPI_MAX_STREAMS) { + phr->error = HPI_ERROR_INVALID_STREAM; + HPI_DEBUG_LOG(WARNING, + "message referencing invalid stream %d " + "on adapter index %d\n", phm->obj_index, + phm->adapter_index); + return; + } + + switch (phm->function) { + case HPI_OSTREAM_WRITE: + outstream_write(pao, phm, phr); + break; + case HPI_OSTREAM_GET_INFO: + outstream_get_info(pao, phm, phr); + break; + case HPI_OSTREAM_HOSTBUFFER_ALLOC: + outstream_host_buffer_allocate(pao, phm, phr); + break; + case HPI_OSTREAM_HOSTBUFFER_GET_INFO: + outstream_host_buffer_get_info(pao, phm, phr); + break; + case HPI_OSTREAM_HOSTBUFFER_FREE: + outstream_host_buffer_free(pao, phm, phr); + break; + case HPI_OSTREAM_START: + outstream_start(pao, phm, phr); + break; + case HPI_OSTREAM_OPEN: + outstream_open(pao, phm, phr); + break; + case HPI_OSTREAM_RESET: + outstream_reset(pao, phm, phr); + break; + default: + hw_message(pao, phm, phr); + break; + } +} + +static void instream_message(struct hpi_adapter_obj *pao, + struct hpi_message *phm, struct hpi_response *phr) +{ + + if (phm->obj_index >= HPI_MAX_STREAMS) { + phr->error = HPI_ERROR_INVALID_STREAM; + HPI_DEBUG_LOG(WARNING, + "message referencing invalid stream %d " + "on adapter index %d\n", phm->obj_index, + phm->adapter_index); + return; + } + + switch (phm->function) { + case HPI_ISTREAM_READ: + instream_read(pao, phm, phr); + break; + case HPI_ISTREAM_GET_INFO: + instream_get_info(pao, phm, phr); + break; + case HPI_ISTREAM_HOSTBUFFER_ALLOC: + instream_host_buffer_allocate(pao, phm, phr); + break; + case HPI_ISTREAM_HOSTBUFFER_GET_INFO: + instream_host_buffer_get_info(pao, phm, phr); + break; + case HPI_ISTREAM_HOSTBUFFER_FREE: + instream_host_buffer_free(pao, phm, phr); + break; + case HPI_ISTREAM_START: + instream_start(pao, phm, phr); + break; + default: + hw_message(pao, phm, phr); + break; + } +} + +/*****************************************************************************/ +/** Entry point to this HPI backend + * All calls to the HPI start here + */ +void HPI_6205(struct hpi_message *phm, struct hpi_response *phr) +{ + struct hpi_adapter_obj *pao = NULL; + + /* subsytem messages are processed by every HPI. + * All other messages are ignored unless the adapter index matches + * an adapter in the HPI + */ + HPI_DEBUG_LOG(DEBUG, "HPI obj=%d, func=%d\n", phm->object, + phm->function); + + /* if Dsp has crashed then do not communicate with it any more */ + if (phm->object != HPI_OBJ_SUBSYSTEM) { + pao = hpi_find_adapter(phm->adapter_index); + if (!pao) { + HPI_DEBUG_LOG(DEBUG, + " %d,%d refused, for another HPI?\n", + phm->object, phm->function); + return; + } + + if ((pao->dsp_crashed >= 10) + && (phm->function != HPI_ADAPTER_DEBUG_READ)) { + /* allow last resort debug read even after crash */ + hpi_init_response(phr, phm->object, phm->function, + HPI_ERROR_DSP_HARDWARE); + HPI_DEBUG_LOG(WARNING, " %d,%d dsp crashed.\n", + phm->object, phm->function); + return; + } + } + + /* Init default response */ + if (phm->function != HPI_SUBSYS_CREATE_ADAPTER) + hpi_init_response(phr, phm->object, phm->function, + HPI_ERROR_PROCESSING_MESSAGE); + + HPI_DEBUG_LOG(VERBOSE, "start of switch\n"); + switch (phm->type) { + case HPI_TYPE_MESSAGE: + switch (phm->object) { + case HPI_OBJ_SUBSYSTEM: + subsys_message(phm, phr); + break; + + case HPI_OBJ_ADAPTER: + phr->size = + sizeof(struct hpi_response_header) + + sizeof(struct hpi_adapter_res); + adapter_message(pao, phm, phr); + break; + + case HPI_OBJ_CONTROLEX: + case HPI_OBJ_CONTROL: + control_message(pao, phm, phr); + break; + + case HPI_OBJ_OSTREAM: + outstream_message(pao, phm, phr); + break; + + case HPI_OBJ_ISTREAM: + instream_message(pao, phm, phr); + break; + + default: + hw_message(pao, phm, phr); + break; + } + break; + + default: + phr->error = HPI_ERROR_INVALID_TYPE; + break; + } +} + +/*****************************************************************************/ +/* SUBSYSTEM */ + +/** Create an adapter object and initialise it based on resource information + * passed in in the message + * *** NOTE - you cannot use this function AND the FindAdapters function at the + * same time, the application must use only one of them to get the adapters *** + */ +static void subsys_create_adapter(struct hpi_message *phm, + struct hpi_response *phr) +{ + /* create temp adapter obj, because we don't know what index yet */ + struct hpi_adapter_obj ao; + u32 os_error_code; + u16 err; + + HPI_DEBUG_LOG(DEBUG, " subsys_create_adapter\n"); + + memset(&ao, 0, sizeof(ao)); + + /* this HPI only creates adapters for TI/PCI devices */ + if (phm->u.s.resource.bus_type != HPI_BUS_PCI) + return; + if (phm->u.s.resource.r.pci->vendor_id != HPI_PCI_VENDOR_ID_TI) + return; + if (phm->u.s.resource.r.pci->device_id != HPI_PCI_DEV_ID_DSP6205) + return; + + ao.priv = kmalloc(sizeof(struct hpi_hw_obj), GFP_KERNEL); + if (!ao.priv) { + HPI_DEBUG_LOG(ERROR, "cant get mem for adapter object\n"); + phr->error = HPI_ERROR_MEMORY_ALLOC; + return; + } + memset(ao.priv, 0, sizeof(struct hpi_hw_obj)); + + ao.pci = *phm->u.s.resource.r.pci; + err = create_adapter_obj(&ao, &os_error_code); + if (!err) + err = hpi_add_adapter(&ao); + if (err) { + phr->u.s.data = os_error_code; + delete_adapter_obj(&ao); + phr->error = err; + return; + } + + phr->u.s.aw_adapter_list[ao.index] = ao.adapter_type; + phr->u.s.adapter_index = ao.index; + phr->u.s.num_adapters++; + phr->error = 0; +} + +/** delete an adapter - required by WDM driver */ +static void subsys_delete_adapter(struct hpi_message *phm, + struct hpi_response *phr) +{ + struct hpi_adapter_obj *pao; + struct hpi_hw_obj *phw; + + pao = hpi_find_adapter(phm->adapter_index); + if (!pao) { + phr->error = HPI_ERROR_INVALID_OBJ_INDEX; + return; + } + phw = (struct hpi_hw_obj *)pao->priv; + /* reset adapter h/w */ + /* Reset C6713 #1 */ + boot_loader_write_mem32(pao, 0, C6205_BAR0_TIMER1_CTL, 0); + /* reset C6205 */ + iowrite32(C6205_HDCR_WARMRESET, phw->prHDCR); + + delete_adapter_obj(pao); + phr->error = 0; +} + +/** Create adapter object + allocate buffers, bootload DSPs, initialise control cache +*/ +static u16 create_adapter_obj(struct hpi_adapter_obj *pao, + u32 *pos_error_code) +{ + struct hpi_hw_obj *phw = pao->priv; + struct bus_master_interface *interface; + u32 phys_addr; +#ifndef HPI6205_NO_HSR_POLL + u32 time_out = HPI6205_TIMEOUT; + u32 temp1; +#endif + int i; + u16 err; + + /* init error reporting */ + pao->dsp_crashed = 0; + + for (i = 0; i < HPI_MAX_STREAMS; i++) + phw->flag_outstream_just_reset[i] = 1; + + /* The C6205 memory area 1 is 8Mbyte window into DSP registers */ + phw->prHSR = + pao->pci.ap_mem_base[1] + + C6205_BAR1_HSR / sizeof(*pao->pci.ap_mem_base[1]); + phw->prHDCR = + pao->pci.ap_mem_base[1] + + C6205_BAR1_HDCR / sizeof(*pao->pci.ap_mem_base[1]); + phw->prDSPP = + pao->pci.ap_mem_base[1] + + C6205_BAR1_DSPP / sizeof(*pao->pci.ap_mem_base[1]); + + pao->has_control_cache = 0; + + if (hpios_locked_mem_alloc(&phw->h_locked_mem, + sizeof(struct bus_master_interface), + pao->pci.p_os_data)) + phw->p_interface_buffer = NULL; + else if (hpios_locked_mem_get_virt_addr(&phw->h_locked_mem, + (void *)&phw->p_interface_buffer)) + phw->p_interface_buffer = NULL; + + HPI_DEBUG_LOG(DEBUG, "interface buffer address %p\n", + phw->p_interface_buffer); + + if (phw->p_interface_buffer) { + memset((void *)phw->p_interface_buffer, 0, + sizeof(struct bus_master_interface)); + phw->p_interface_buffer->dsp_ack = H620_HIF_UNKNOWN; + } + + err = adapter_boot_load_dsp(pao, pos_error_code); + if (err) + /* no need to clean up as SubSysCreateAdapter */ + /* calls DeleteAdapter on error. */ + return err; + + HPI_DEBUG_LOG(INFO, "load DSP code OK\n"); + + /* allow boot load even if mem alloc wont work */ + if (!phw->p_interface_buffer) + return hpi6205_error(0, HPI_ERROR_MEMORY_ALLOC); + + interface = phw->p_interface_buffer; + +#ifndef HPI6205_NO_HSR_POLL + /* wait for first interrupt indicating the DSP init is done */ + time_out = HPI6205_TIMEOUT * 10; + temp1 = 0; + while (((temp1 & C6205_HSR_INTSRC) == 0) && --time_out) + temp1 = ioread32(phw->prHSR); + + if (temp1 & C6205_HSR_INTSRC) + HPI_DEBUG_LOG(INFO, + "interrupt confirming DSP code running OK\n"); + else { + HPI_DEBUG_LOG(ERROR, + "timed out waiting for interrupt " + "confirming DSP code running\n"); + return hpi6205_error(0, HPI6205_ERROR_6205_NO_IRQ); + } + + /* reset the interrupt */ + iowrite32(C6205_HSR_INTSRC, phw->prHSR); +#endif + + /* make sure the DSP has started ok */ + if (!wait_dsp_ack(phw, H620_HIF_RESET, HPI6205_TIMEOUT * 10)) { + HPI_DEBUG_LOG(ERROR, "timed out waiting reset state \n"); + return hpi6205_error(0, HPI6205_ERROR_6205_INIT_FAILED); + } + /* Note that *pao, *phw are zeroed after allocation, + * so pointers and flags are NULL by default. + * Allocate bus mastering control cache buffer and tell the DSP about it + */ + if (interface->control_cache.number_of_controls) { + void *p_control_cache_virtual; + + err = hpios_locked_mem_alloc(&phw->h_control_cache, + interface->control_cache.size_in_bytes, + pao->pci.p_os_data); + if (!err) + err = hpios_locked_mem_get_virt_addr(&phw-> + h_control_cache, &p_control_cache_virtual); + if (!err) { + memset(p_control_cache_virtual, 0, + interface->control_cache.size_in_bytes); + + phw->p_cache = + hpi_alloc_control_cache(interface-> + control_cache.number_of_controls, + interface->control_cache.size_in_bytes, + (struct hpi_control_cache_info *) + p_control_cache_virtual); + } + if (!err) { + err = hpios_locked_mem_get_phys_addr(&phw-> + h_control_cache, &phys_addr); + interface->control_cache.physical_address32 = + phys_addr; + } + + if (!err) + pao->has_control_cache = 1; + else { + if (hpios_locked_mem_valid(&phw->h_control_cache)) + hpios_locked_mem_free(&phw->h_control_cache); + pao->has_control_cache = 0; + } + } + /* allocate bus mastering async buffer and tell the DSP about it */ + if (interface->async_buffer.b.size) { + err = hpios_locked_mem_alloc(&phw->h_async_event_buffer, + interface->async_buffer.b.size * + sizeof(struct hpi_async_event), pao->pci.p_os_data); + if (!err) + err = hpios_locked_mem_get_virt_addr + (&phw->h_async_event_buffer, (void *) + &phw->p_async_event_buffer); + if (!err) + memset((void *)phw->p_async_event_buffer, 0, + interface->async_buffer.b.size * + sizeof(struct hpi_async_event)); + if (!err) { + err = hpios_locked_mem_get_phys_addr + (&phw->h_async_event_buffer, &phys_addr); + interface->async_buffer.physical_address32 = + phys_addr; + } + if (err) { + if (hpios_locked_mem_valid(&phw-> + h_async_event_buffer)) { + hpios_locked_mem_free + (&phw->h_async_event_buffer); + phw->p_async_event_buffer = NULL; + } + } + } + send_dsp_command(phw, H620_HIF_IDLE); + + { + struct hpi_message hM; + struct hpi_response hR; + u32 max_streams; + + HPI_DEBUG_LOG(VERBOSE, "init ADAPTER_GET_INFO\n"); + memset(&hM, 0, sizeof(hM)); + hM.type = HPI_TYPE_MESSAGE; + hM.size = sizeof(hM); + hM.object = HPI_OBJ_ADAPTER; + hM.function = HPI_ADAPTER_GET_INFO; + hM.adapter_index = 0; + memset(&hR, 0, sizeof(hR)); + hR.size = sizeof(hR); + + err = message_response_sequence(pao, &hM, &hR); + if (err) { + HPI_DEBUG_LOG(ERROR, "message transport error %d\n", + err); + return err; + } + if (hR.error) + return hR.error; + + pao->adapter_type = hR.u.a.adapter_type; + pao->index = hR.u.a.adapter_index; + + max_streams = hR.u.a.num_outstreams + hR.u.a.num_instreams; + + hpios_locked_mem_prepare((max_streams * 6) / 10, max_streams, + 65536, pao->pci.p_os_data); + + HPI_DEBUG_LOG(VERBOSE, + "got adapter info type %x index %d serial %d\n", + hR.u.a.adapter_type, hR.u.a.adapter_index, + hR.u.a.serial_number); + } + + pao->open = 0; /* upon creation the adapter is closed */ + + HPI_DEBUG_LOG(INFO, "bootload DSP OK\n"); + return 0; +} + +/** Free memory areas allocated by adapter + * this routine is called from SubSysDeleteAdapter, + * and SubSysCreateAdapter if duplicate index +*/ +static void delete_adapter_obj(struct hpi_adapter_obj *pao) +{ + struct hpi_hw_obj *phw; + int i; + + phw = pao->priv; + + if (hpios_locked_mem_valid(&phw->h_async_event_buffer)) { + hpios_locked_mem_free(&phw->h_async_event_buffer); + phw->p_async_event_buffer = NULL; + } + + if (hpios_locked_mem_valid(&phw->h_control_cache)) { + hpios_locked_mem_free(&phw->h_control_cache); + hpi_free_control_cache(phw->p_cache); + } + + if (hpios_locked_mem_valid(&phw->h_locked_mem)) { + hpios_locked_mem_free(&phw->h_locked_mem); + phw->p_interface_buffer = NULL; + } + + for (i = 0; i < HPI_MAX_STREAMS; i++) + if (hpios_locked_mem_valid(&phw->instream_host_buffers[i])) { + hpios_locked_mem_free(&phw->instream_host_buffers[i]); + /*?phw->InStreamHostBuffers[i] = NULL; */ + phw->instream_host_buffer_size[i] = 0; + } + + for (i = 0; i < HPI_MAX_STREAMS; i++) + if (hpios_locked_mem_valid(&phw->outstream_host_buffers[i])) { + hpios_locked_mem_free(&phw->outstream_host_buffers + [i]); + phw->outstream_host_buffer_size[i] = 0; + } + + hpios_locked_mem_unprepare(pao->pci.p_os_data); + + hpi_delete_adapter(pao); + kfree(phw); +} + +/*****************************************************************************/ +/* OutStream Host buffer functions */ + +/** Allocate or attach buffer for busmastering +*/ +static void outstream_host_buffer_allocate(struct hpi_adapter_obj *pao, + struct hpi_message *phm, struct hpi_response *phr) +{ + u16 err = 0; + u32 command = phm->u.d.u.buffer.command; + struct hpi_hw_obj *phw = pao->priv; + struct bus_master_interface *interface = phw->p_interface_buffer; + + hpi_init_response(phr, phm->object, phm->function, 0); + + if (command == HPI_BUFFER_CMD_EXTERNAL + || command == HPI_BUFFER_CMD_INTERNAL_ALLOC) { + /* ALLOC phase, allocate a buffer with power of 2 size, + get its bus address for PCI bus mastering + */ + phm->u.d.u.buffer.buffer_size = + roundup_pow_of_two(phm->u.d.u.buffer.buffer_size); + /* return old size and allocated size, + so caller can detect change */ + phr->u.d.u.stream_info.data_available = + phw->outstream_host_buffer_size[phm->obj_index]; + phr->u.d.u.stream_info.buffer_size = + phm->u.d.u.buffer.buffer_size; + + if (phw->outstream_host_buffer_size[phm->obj_index] == + phm->u.d.u.buffer.buffer_size) { + /* Same size, no action required */ + return; + } + + if (hpios_locked_mem_valid(&phw->outstream_host_buffers[phm-> + obj_index])) + hpios_locked_mem_free(&phw->outstream_host_buffers + [phm->obj_index]); + + err = hpios_locked_mem_alloc(&phw->outstream_host_buffers + [phm->obj_index], phm->u.d.u.buffer.buffer_size, + pao->pci.p_os_data); + + if (err) { + phr->error = HPI_ERROR_INVALID_DATASIZE; + phw->outstream_host_buffer_size[phm->obj_index] = 0; + return; + } + + err = hpios_locked_mem_get_phys_addr + (&phw->outstream_host_buffers[phm->obj_index], + &phm->u.d.u.buffer.pci_address); + /* get the phys addr into msg for single call alloc caller + * needs to do this for split alloc (or use the same message) + * return the phy address for split alloc in the respose too + */ + phr->u.d.u.stream_info.auxiliary_data_available = + phm->u.d.u.buffer.pci_address; + + if (err) { + hpios_locked_mem_free(&phw->outstream_host_buffers + [phm->obj_index]); + phw->outstream_host_buffer_size[phm->obj_index] = 0; + phr->error = HPI_ERROR_MEMORY_ALLOC; + return; + } + } + + if (command == HPI_BUFFER_CMD_EXTERNAL + || command == HPI_BUFFER_CMD_INTERNAL_GRANTADAPTER) { + /* GRANT phase. Set up the BBM status, tell the DSP about + the buffer so it can start using BBM. + */ + struct hpi_hostbuffer_status *status; + + if (phm->u.d.u.buffer.buffer_size & (phm->u.d.u.buffer. + buffer_size - 1)) { + HPI_DEBUG_LOG(ERROR, + "buffer size must be 2^N not %d\n", + phm->u.d.u.buffer.buffer_size); + phr->error = HPI_ERROR_INVALID_DATASIZE; + return; + } + phw->outstream_host_buffer_size[phm->obj_index] = + phm->u.d.u.buffer.buffer_size; + status = &interface->outstream_host_buffer_status[phm-> + obj_index]; + status->samples_processed = 0; + status->stream_state = HPI_STATE_STOPPED; + status->dSP_index = 0; + status->host_index = status->dSP_index; + status->size_in_bytes = phm->u.d.u.buffer.buffer_size; + + hw_message(pao, phm, phr); + + if (phr->error + && hpios_locked_mem_valid(&phw-> + outstream_host_buffers[phm->obj_index])) { + hpios_locked_mem_free(&phw->outstream_host_buffers + [phm->obj_index]); + phw->outstream_host_buffer_size[phm->obj_index] = 0; + } + } +} + +static void outstream_host_buffer_get_info(struct hpi_adapter_obj *pao, + struct hpi_message *phm, struct hpi_response *phr) +{ + struct hpi_hw_obj *phw = pao->priv; + struct bus_master_interface *interface = phw->p_interface_buffer; + struct hpi_hostbuffer_status *status; + u8 *p_bbm_data; + + if (hpios_locked_mem_valid(&phw->outstream_host_buffers[phm-> + obj_index])) { + if (hpios_locked_mem_get_virt_addr(&phw-> + outstream_host_buffers[phm->obj_index], + (void *)&p_bbm_data)) { + phr->error = HPI_ERROR_INVALID_OPERATION; + return; + } + status = &interface->outstream_host_buffer_status[phm-> + obj_index]; + hpi_init_response(phr, HPI_OBJ_OSTREAM, + HPI_OSTREAM_HOSTBUFFER_GET_INFO, 0); + phr->u.d.u.hostbuffer_info.p_buffer = p_bbm_data; + phr->u.d.u.hostbuffer_info.p_status = status; + } else { + hpi_init_response(phr, HPI_OBJ_OSTREAM, + HPI_OSTREAM_HOSTBUFFER_GET_INFO, + HPI_ERROR_INVALID_OPERATION); + } +} + +static void outstream_host_buffer_free(struct hpi_adapter_obj *pao, + struct hpi_message *phm, struct hpi_response *phr) +{ + struct hpi_hw_obj *phw = pao->priv; + u32 command = phm->u.d.u.buffer.command; + + if (phw->outstream_host_buffer_size[phm->obj_index]) { + if (command == HPI_BUFFER_CMD_EXTERNAL + || command == HPI_BUFFER_CMD_INTERNAL_REVOKEADAPTER) { + phw->outstream_host_buffer_size[phm->obj_index] = 0; + hw_message(pao, phm, phr); + /* Tell adapter to stop using the host buffer. */ + } + if (command == HPI_BUFFER_CMD_EXTERNAL + || command == HPI_BUFFER_CMD_INTERNAL_FREE) + hpios_locked_mem_free(&phw->outstream_host_buffers + [phm->obj_index]); + } + /* Should HPI_ERROR_INVALID_OPERATION be returned + if no host buffer is allocated? */ + else + hpi_init_response(phr, HPI_OBJ_OSTREAM, + HPI_OSTREAM_HOSTBUFFER_FREE, 0); + +} + +static long outstream_get_space_available(struct hpi_hostbuffer_status + *status) +{ + return status->size_in_bytes - ((long)(status->host_index) - + (long)(status->dSP_index)); +} + +static void outstream_write(struct hpi_adapter_obj *pao, + struct hpi_message *phm, struct hpi_response *phr) +{ + struct hpi_hw_obj *phw = pao->priv; + struct bus_master_interface *interface = phw->p_interface_buffer; + struct hpi_hostbuffer_status *status; + long space_available; + + if (!phw->outstream_host_buffer_size[phm->obj_index]) { + /* there is no BBM buffer, write via message */ + hw_message(pao, phm, phr); + return; + } + + hpi_init_response(phr, phm->object, phm->function, 0); + status = &interface->outstream_host_buffer_status[phm->obj_index]; + + if (phw->flag_outstream_just_reset[phm->obj_index]) { + /* Format can only change after reset. Must tell DSP. */ + u16 function = phm->function; + phw->flag_outstream_just_reset[phm->obj_index] = 0; + phm->function = HPI_OSTREAM_SET_FORMAT; + hw_message(pao, phm, phr); /* send the format to the DSP */ + phm->function = function; + if (phr->error) + return; + } +#if 1 + if (phw->flag_outstream_just_reset[phm->obj_index]) { + /* First OutStremWrite() call following reset will write data to the + adapter's buffers, reducing delay before stream can start + */ + int partial_write = 0; + unsigned int original_size = 0; + + /* Send the first buffer to the DSP the old way. */ + /* Limit size of first transfer - */ + /* expect that this will not usually be triggered. */ + if (phm->u.d.u.data.data_size > HPI6205_SIZEOF_DATA) { + partial_write = 1; + original_size = phm->u.d.u.data.data_size; + phm->u.d.u.data.data_size = HPI6205_SIZEOF_DATA; + } + /* write it */ + phm->function = HPI_OSTREAM_WRITE; + hw_message(pao, phm, phr); + /* update status information that the DSP would typically + * update (and will update next time the DSP + * buffer update task reads data from the host BBM buffer) + */ + status->auxiliary_data_available = phm->u.d.u.data.data_size; + status->host_index += phm->u.d.u.data.data_size; + status->dSP_index += phm->u.d.u.data.data_size; + + /* if we did a full write, we can return from here. */ + if (!partial_write) + return; + + /* tweak buffer parameters and let the rest of the */ + /* buffer land in internal BBM buffer */ + phm->u.d.u.data.data_size = + original_size - HPI6205_SIZEOF_DATA; + phm->u.d.u.data.pb_data += HPI6205_SIZEOF_DATA; + } +#endif + + space_available = outstream_get_space_available(status); + if (space_available < (long)phm->u.d.u.data.data_size) { + phr->error = HPI_ERROR_INVALID_DATASIZE; + return; + } + + /* HostBuffers is used to indicate host buffer is internally allocated. + otherwise, assumed external, data written externally */ + if (phm->u.d.u.data.pb_data + && hpios_locked_mem_valid(&phw->outstream_host_buffers[phm-> + obj_index])) { + u8 *p_bbm_data; + long l_first_write; + u8 *p_app_data = (u8 *)phm->u.d.u.data.pb_data; + + if (hpios_locked_mem_get_virt_addr(&phw-> + outstream_host_buffers[phm->obj_index], + (void *)&p_bbm_data)) { + phr->error = HPI_ERROR_INVALID_OPERATION; + return; + } + + /* either all data, + or enough to fit from current to end of BBM buffer */ + l_first_write = + min(phm->u.d.u.data.data_size, + status->size_in_bytes - + (status->host_index & (status->size_in_bytes - 1))); + + memcpy(p_bbm_data + + (status->host_index & (status->size_in_bytes - 1)), + p_app_data, l_first_write); + /* remaining data if any */ + memcpy(p_bbm_data, p_app_data + l_first_write, + phm->u.d.u.data.data_size - l_first_write); + } + status->host_index += phm->u.d.u.data.data_size; +} + +static void outstream_get_info(struct hpi_adapter_obj *pao, + struct hpi_message *phm, struct hpi_response *phr) +{ + struct hpi_hw_obj *phw = pao->priv; + struct bus_master_interface *interface = phw->p_interface_buffer; + struct hpi_hostbuffer_status *status; + + if (!phw->outstream_host_buffer_size[phm->obj_index]) { + hw_message(pao, phm, phr); + return; + } + + hpi_init_response(phr, phm->object, phm->function, 0); + + status = &interface->outstream_host_buffer_status[phm->obj_index]; + + phr->u.d.u.stream_info.state = (u16)status->stream_state; + phr->u.d.u.stream_info.samples_transferred = + status->samples_processed; + phr->u.d.u.stream_info.buffer_size = status->size_in_bytes; + phr->u.d.u.stream_info.data_available = + status->size_in_bytes - outstream_get_space_available(status); + phr->u.d.u.stream_info.auxiliary_data_available = + status->auxiliary_data_available; +} + +static void outstream_start(struct hpi_adapter_obj *pao, + struct hpi_message *phm, struct hpi_response *phr) +{ + hw_message(pao, phm, phr); +} + +static void outstream_reset(struct hpi_adapter_obj *pao, + struct hpi_message *phm, struct hpi_response *phr) +{ + struct hpi_hw_obj *phw = pao->priv; + phw->flag_outstream_just_reset[phm->obj_index] = 1; + hw_message(pao, phm, phr); +} + +static void outstream_open(struct hpi_adapter_obj *pao, + struct hpi_message *phm, struct hpi_response *phr) +{ + outstream_reset(pao, phm, phr); +} + +/*****************************************************************************/ +/* InStream Host buffer functions */ + +static void instream_host_buffer_allocate(struct hpi_adapter_obj *pao, + struct hpi_message *phm, struct hpi_response *phr) +{ + u16 err = 0; + u32 command = phm->u.d.u.buffer.command; + struct hpi_hw_obj *phw = pao->priv; + struct bus_master_interface *interface = phw->p_interface_buffer; + + hpi_init_response(phr, phm->object, phm->function, 0); + + if (command == HPI_BUFFER_CMD_EXTERNAL + || command == HPI_BUFFER_CMD_INTERNAL_ALLOC) { + + phm->u.d.u.buffer.buffer_size = + roundup_pow_of_two(phm->u.d.u.buffer.buffer_size); + phr->u.d.u.stream_info.data_available = + phw->instream_host_buffer_size[phm->obj_index]; + phr->u.d.u.stream_info.buffer_size = + phm->u.d.u.buffer.buffer_size; + + if (phw->instream_host_buffer_size[phm->obj_index] == + phm->u.d.u.buffer.buffer_size) { + /* Same size, no action required */ + return; + } + + if (hpios_locked_mem_valid(&phw->instream_host_buffers[phm-> + obj_index])) + hpios_locked_mem_free(&phw->instream_host_buffers + [phm->obj_index]); + + err = hpios_locked_mem_alloc(&phw->instream_host_buffers[phm-> + obj_index], phm->u.d.u.buffer.buffer_size, + pao->pci.p_os_data); + + if (err) { + phr->error = HPI_ERROR_INVALID_DATASIZE; + phw->instream_host_buffer_size[phm->obj_index] = 0; + return; + } + + err = hpios_locked_mem_get_phys_addr + (&phw->instream_host_buffers[phm->obj_index], + &phm->u.d.u.buffer.pci_address); + /* get the phys addr into msg for single call alloc. Caller + needs to do this for split alloc so return the phy address */ + phr->u.d.u.stream_info.auxiliary_data_available = + phm->u.d.u.buffer.pci_address; + if (err) { + hpios_locked_mem_free(&phw->instream_host_buffers + [phm->obj_index]); + phw->instream_host_buffer_size[phm->obj_index] = 0; + phr->error = HPI_ERROR_MEMORY_ALLOC; + return; + } + } + + if (command == HPI_BUFFER_CMD_EXTERNAL + || command == HPI_BUFFER_CMD_INTERNAL_GRANTADAPTER) { + struct hpi_hostbuffer_status *status; + + if (phm->u.d.u.buffer.buffer_size & (phm->u.d.u.buffer. + buffer_size - 1)) { + HPI_DEBUG_LOG(ERROR, + "buffer size must be 2^N not %d\n", + phm->u.d.u.buffer.buffer_size); + phr->error = HPI_ERROR_INVALID_DATASIZE; + return; + } + + phw->instream_host_buffer_size[phm->obj_index] = + phm->u.d.u.buffer.buffer_size; + status = &interface->instream_host_buffer_status[phm-> + obj_index]; + status->samples_processed = 0; + status->stream_state = HPI_STATE_STOPPED; + status->dSP_index = 0; + status->host_index = status->dSP_index; + status->size_in_bytes = phm->u.d.u.buffer.buffer_size; + + hw_message(pao, phm, phr); + if (phr->error + && hpios_locked_mem_valid(&phw-> + instream_host_buffers[phm->obj_index])) { + hpios_locked_mem_free(&phw->instream_host_buffers + [phm->obj_index]); + phw->instream_host_buffer_size[phm->obj_index] = 0; + } + } +} + +static void instream_host_buffer_get_info(struct hpi_adapter_obj *pao, + struct hpi_message *phm, struct hpi_response *phr) +{ + struct hpi_hw_obj *phw = pao->priv; + struct bus_master_interface *interface = phw->p_interface_buffer; + struct hpi_hostbuffer_status *status; + u8 *p_bbm_data; + + if (hpios_locked_mem_valid(&phw->instream_host_buffers[phm-> + obj_index])) { + if (hpios_locked_mem_get_virt_addr(&phw-> + instream_host_buffers[phm->obj_index], + (void *)&p_bbm_data)) { + phr->error = HPI_ERROR_INVALID_OPERATION; + return; + } + status = &interface->instream_host_buffer_status[phm-> + obj_index]; + hpi_init_response(phr, HPI_OBJ_ISTREAM, + HPI_ISTREAM_HOSTBUFFER_GET_INFO, 0); + phr->u.d.u.hostbuffer_info.p_buffer = p_bbm_data; + phr->u.d.u.hostbuffer_info.p_status = status; + } else { + hpi_init_response(phr, HPI_OBJ_ISTREAM, + HPI_ISTREAM_HOSTBUFFER_GET_INFO, + HPI_ERROR_INVALID_OPERATION); + } +} + +static void instream_host_buffer_free(struct hpi_adapter_obj *pao, + struct hpi_message *phm, struct hpi_response *phr) +{ + struct hpi_hw_obj *phw = pao->priv; + u32 command = phm->u.d.u.buffer.command; + + if (phw->instream_host_buffer_size[phm->obj_index]) { + if (command == HPI_BUFFER_CMD_EXTERNAL + || command == HPI_BUFFER_CMD_INTERNAL_REVOKEADAPTER) { + phw->instream_host_buffer_size[phm->obj_index] = 0; + hw_message(pao, phm, phr); + } + + if (command == HPI_BUFFER_CMD_EXTERNAL + || command == HPI_BUFFER_CMD_INTERNAL_FREE) + hpios_locked_mem_free(&phw->instream_host_buffers + [phm->obj_index]); + + } else { + /* Should HPI_ERROR_INVALID_OPERATION be returned + if no host buffer is allocated? */ + hpi_init_response(phr, HPI_OBJ_ISTREAM, + HPI_ISTREAM_HOSTBUFFER_FREE, 0); + + } + +} + +static void instream_start(struct hpi_adapter_obj *pao, + struct hpi_message *phm, struct hpi_response *phr) +{ + hw_message(pao, phm, phr); +} + +static long instream_get_bytes_available(struct hpi_hostbuffer_status *status) +{ + return (long)(status->dSP_index) - (long)(status->host_index); +} + +static void instream_read(struct hpi_adapter_obj *pao, + struct hpi_message *phm, struct hpi_response *phr) +{ + struct hpi_hw_obj *phw = pao->priv; + struct bus_master_interface *interface = phw->p_interface_buffer; + struct hpi_hostbuffer_status *status; + long data_available; + u8 *p_bbm_data; + long l_first_read; + u8 *p_app_data = (u8 *)phm->u.d.u.data.pb_data; + + if (!phw->instream_host_buffer_size[phm->obj_index]) { + hw_message(pao, phm, phr); + return; + } + hpi_init_response(phr, phm->object, phm->function, 0); + + status = &interface->instream_host_buffer_status[phm->obj_index]; + data_available = instream_get_bytes_available(status); + if (data_available < (long)phm->u.d.u.data.data_size) { + phr->error = HPI_ERROR_INVALID_DATASIZE; + return; + } + + if (hpios_locked_mem_valid(&phw->instream_host_buffers[phm-> + obj_index])) { + if (hpios_locked_mem_get_virt_addr(&phw-> + instream_host_buffers[phm->obj_index], + (void *)&p_bbm_data)) { + phr->error = HPI_ERROR_INVALID_OPERATION; + return; + } + + /* either all data, + or enough to fit from current to end of BBM buffer */ + l_first_read = + min(phm->u.d.u.data.data_size, + status->size_in_bytes - + (status->host_index & (status->size_in_bytes - 1))); + + memcpy(p_app_data, + p_bbm_data + + (status->host_index & (status->size_in_bytes - 1)), + l_first_read); + /* remaining data if any */ + memcpy(p_app_data + l_first_read, p_bbm_data, + phm->u.d.u.data.data_size - l_first_read); + } + status->host_index += phm->u.d.u.data.data_size; +} + +static void instream_get_info(struct hpi_adapter_obj *pao, + struct hpi_message *phm, struct hpi_response *phr) +{ + struct hpi_hw_obj *phw = pao->priv; + struct bus_master_interface *interface = phw->p_interface_buffer; + struct hpi_hostbuffer_status *status; + if (!phw->instream_host_buffer_size[phm->obj_index]) { + hw_message(pao, phm, phr); + return; + } + + status = &interface->instream_host_buffer_status[phm->obj_index]; + + hpi_init_response(phr, phm->object, phm->function, 0); + + phr->u.d.u.stream_info.state = (u16)status->stream_state; + phr->u.d.u.stream_info.samples_transferred = + status->samples_processed; + phr->u.d.u.stream_info.buffer_size = status->size_in_bytes; + phr->u.d.u.stream_info.data_available = + instream_get_bytes_available(status); + phr->u.d.u.stream_info.auxiliary_data_available = + status->auxiliary_data_available; +} + +/*****************************************************************************/ +/* LOW-LEVEL */ +#define HPI6205_MAX_FILES_TO_LOAD 2 + +static u16 adapter_boot_load_dsp(struct hpi_adapter_obj *pao, + u32 *pos_error_code) +{ + struct hpi_hw_obj *phw = pao->priv; + struct dsp_code dsp_code; + u16 boot_code_id[HPI6205_MAX_FILES_TO_LOAD]; + u16 firmware_id = pao->pci.subsys_device_id; + u32 temp; + int dsp = 0, i = 0; + u16 err = 0; + + boot_code_id[0] = HPI_ADAPTER_ASI(0x6205); + + /* special cases where firmware_id != subsys ID */ + switch (firmware_id) { + case HPI_ADAPTER_FAMILY_ASI(0x5000): + boot_code_id[0] = firmware_id; + firmware_id = 0; + break; + case HPI_ADAPTER_FAMILY_ASI(0x5300): + case HPI_ADAPTER_FAMILY_ASI(0x5400): + case HPI_ADAPTER_FAMILY_ASI(0x6300): + firmware_id = HPI_ADAPTER_FAMILY_ASI(0x6400); + break; + case HPI_ADAPTER_FAMILY_ASI(0x5600): + case HPI_ADAPTER_FAMILY_ASI(0x6500): + firmware_id = HPI_ADAPTER_FAMILY_ASI(0x6600); + break; + } + boot_code_id[1] = firmware_id; + + /* reset DSP by writing a 1 to the WARMRESET bit */ + temp = C6205_HDCR_WARMRESET; + iowrite32(temp, phw->prHDCR); + hpios_delay_micro_seconds(1000); + + /* check that PCI i/f was configured by EEPROM */ + temp = ioread32(phw->prHSR); + if ((temp & (C6205_HSR_CFGERR | C6205_HSR_EEREAD)) != + C6205_HSR_EEREAD) + return hpi6205_error(0, HPI6205_ERROR_6205_EEPROM); + temp |= 0x04; + /* disable PINTA interrupt */ + iowrite32(temp, phw->prHSR); + + /* check control register reports PCI boot mode */ + temp = ioread32(phw->prHDCR); + if (!(temp & C6205_HDCR_PCIBOOT)) + return hpi6205_error(0, HPI6205_ERROR_6205_REG); + + /* try writing a couple of numbers to the DSP page register */ + /* and reading them back. */ + temp = 1; + iowrite32(temp, phw->prDSPP); + if ((temp | C6205_DSPP_MAP1) != ioread32(phw->prDSPP)) + return hpi6205_error(0, HPI6205_ERROR_6205_DSPPAGE); + temp = 2; + iowrite32(temp, phw->prDSPP); + if ((temp | C6205_DSPP_MAP1) != ioread32(phw->prDSPP)) + return hpi6205_error(0, HPI6205_ERROR_6205_DSPPAGE); + temp = 3; + iowrite32(temp, phw->prDSPP); + if ((temp | C6205_DSPP_MAP1) != ioread32(phw->prDSPP)) + return hpi6205_error(0, HPI6205_ERROR_6205_DSPPAGE); + /* reset DSP page to the correct number */ + temp = 0; + iowrite32(temp, phw->prDSPP); + if ((temp | C6205_DSPP_MAP1) != ioread32(phw->prDSPP)) + return hpi6205_error(0, HPI6205_ERROR_6205_DSPPAGE); + phw->dsp_page = 0; + + /* release 6713 from reset before 6205 is bootloaded. + This ensures that the EMIF is inactive, + and the 6713 HPI gets the correct bootmode etc + */ + if (boot_code_id[1] != 0) { + /* DSP 1 is a C6713 */ + /* CLKX0 <- '1' release the C6205 bootmode pulldowns */ + boot_loader_write_mem32(pao, 0, (0x018C0024L), 0x00002202); + hpios_delay_micro_seconds(100); + /* Reset the 6713 #1 - revB */ + boot_loader_write_mem32(pao, 0, C6205_BAR0_TIMER1_CTL, 0); + + /* dummy read every 4 words for 6205 advisory 1.4.4 */ + boot_loader_read_mem32(pao, 0, 0); + + hpios_delay_micro_seconds(100); + /* Release C6713 from reset - revB */ + boot_loader_write_mem32(pao, 0, C6205_BAR0_TIMER1_CTL, 4); + hpios_delay_micro_seconds(100); + } + + for (dsp = 0; dsp < HPI6205_MAX_FILES_TO_LOAD; dsp++) { + /* is there a DSP to load? */ + if (boot_code_id[dsp] == 0) + continue; + + err = boot_loader_config_emif(pao, dsp); + if (err) + return err; + + err = boot_loader_test_internal_memory(pao, dsp); + if (err) + return err; + + err = boot_loader_test_external_memory(pao, dsp); + if (err) + return err; + + err = boot_loader_test_pld(pao, dsp); + if (err) + return err; + + /* write the DSP code down into the DSPs memory */ + dsp_code.ps_dev = pao->pci.p_os_data; + err = hpi_dsp_code_open(boot_code_id[dsp], &dsp_code, + pos_error_code); + if (err) + return err; + + while (1) { + u32 length; + u32 address; + u32 type; + u32 *pcode; + + err = hpi_dsp_code_read_word(&dsp_code, &length); + if (err) + break; + if (length == 0xFFFFFFFF) + break; /* end of code */ + + err = hpi_dsp_code_read_word(&dsp_code, &address); + if (err) + break; + err = hpi_dsp_code_read_word(&dsp_code, &type); + if (err) + break; + err = hpi_dsp_code_read_block(length, &dsp_code, + &pcode); + if (err) + break; + for (i = 0; i < (int)length; i++) { + err = boot_loader_write_mem32(pao, dsp, + address, *pcode); + if (err) + break; + /* dummy read every 4 words */ + /* for 6205 advisory 1.4.4 */ + if (i % 4 == 0) + boot_loader_read_mem32(pao, dsp, + address); + pcode++; + address += 4; + } + + } + if (err) { + hpi_dsp_code_close(&dsp_code); + return err; + } + + /* verify code */ + hpi_dsp_code_rewind(&dsp_code); + while (1) { + u32 length = 0; + u32 address = 0; + u32 type = 0; + u32 *pcode = NULL; + u32 data = 0; + + hpi_dsp_code_read_word(&dsp_code, &length); + if (length == 0xFFFFFFFF) + break; /* end of code */ + + hpi_dsp_code_read_word(&dsp_code, &address); + hpi_dsp_code_read_word(&dsp_code, &type); + hpi_dsp_code_read_block(length, &dsp_code, &pcode); + + for (i = 0; i < (int)length; i++) { + data = boot_loader_read_mem32(pao, dsp, + address); + if (data != *pcode) { + err = 0; + break; + } + pcode++; + address += 4; + } + if (err) + break; + } + hpi_dsp_code_close(&dsp_code); + if (err) + return err; + } + + /* After bootloading all DSPs, start DSP0 running + * The DSP0 code will handle starting and synchronizing with its slaves + */ + if (phw->p_interface_buffer) { + /* we need to tell the card the physical PCI address */ + u32 physicalPC_iaddress; + struct bus_master_interface *interface = + phw->p_interface_buffer; + u32 host_mailbox_address_on_dsp; + u32 physicalPC_iaddress_verify = 0; + int time_out = 10; + /* set ack so we know when DSP is ready to go */ + /* (dwDspAck will be changed to HIF_RESET) */ + interface->dsp_ack = H620_HIF_UNKNOWN; + wmb(); /* ensure ack is written before dsp writes back */ + + err = hpios_locked_mem_get_phys_addr(&phw->h_locked_mem, + &physicalPC_iaddress); + + /* locate the host mailbox on the DSP. */ + host_mailbox_address_on_dsp = 0x80000000; + while ((physicalPC_iaddress != physicalPC_iaddress_verify) + && time_out--) { + err = boot_loader_write_mem32(pao, 0, + host_mailbox_address_on_dsp, + physicalPC_iaddress); + physicalPC_iaddress_verify = + boot_loader_read_mem32(pao, 0, + host_mailbox_address_on_dsp); + } + } + HPI_DEBUG_LOG(DEBUG, "starting DS_ps running\n"); + /* enable interrupts */ + temp = ioread32(phw->prHSR); + temp &= ~(u32)C6205_HSR_INTAM; + iowrite32(temp, phw->prHSR); + + /* start code running... */ + temp = ioread32(phw->prHDCR); + temp |= (u32)C6205_HDCR_DSPINT; + iowrite32(temp, phw->prHDCR); + + /* give the DSP 10ms to start up */ + hpios_delay_micro_seconds(10000); + return err; + +} + +/*****************************************************************************/ +/* Bootloader utility functions */ + +static u32 boot_loader_read_mem32(struct hpi_adapter_obj *pao, int dsp_index, + u32 address) +{ + struct hpi_hw_obj *phw = pao->priv; + u32 data = 0; + __iomem u32 *p_data; + + if (dsp_index == 0) { + /* DSP 0 is always C6205 */ + if ((address >= 0x01800000) & (address < 0x02000000)) { + /* BAR1 register access */ + p_data = pao->pci.ap_mem_base[1] + + (address & 0x007fffff) / + sizeof(*pao->pci.ap_mem_base[1]); + /* HPI_DEBUG_LOG(WARNING, + "BAR1 access %08x\n", dwAddress); */ + } else { + u32 dw4M_page = address >> 22L; + if (dw4M_page != phw->dsp_page) { + phw->dsp_page = dw4M_page; + /* *INDENT OFF* */ + iowrite32(phw->dsp_page, phw->prDSPP); + /* *INDENT-ON* */ + } + address &= 0x3fffff; /* address within 4M page */ + /* BAR0 memory access */ + p_data = pao->pci.ap_mem_base[0] + + address / sizeof(u32); + } + data = ioread32(p_data); + } else if (dsp_index == 1) { + /* DSP 1 is a C6713 */ + u32 lsb; + boot_loader_write_mem32(pao, 0, HPIAL_ADDR, address); + boot_loader_write_mem32(pao, 0, HPIAH_ADDR, address >> 16); + lsb = boot_loader_read_mem32(pao, 0, HPIDL_ADDR); + data = boot_loader_read_mem32(pao, 0, HPIDH_ADDR); + data = (data << 16) | (lsb & 0xFFFF); + } + return data; +} + +static u16 boot_loader_write_mem32(struct hpi_adapter_obj *pao, int dsp_index, + u32 address, u32 data) +{ + struct hpi_hw_obj *phw = pao->priv; + u16 err = 0; + __iomem u32 *p_data; + /* u32 dwVerifyData=0; */ + + if (dsp_index == 0) { + /* DSP 0 is always C6205 */ + if ((address >= 0x01800000) & (address < 0x02000000)) { + /* BAR1 - DSP register access using */ + /* Non-prefetchable PCI access */ + p_data = pao->pci.ap_mem_base[1] + + (address & 0x007fffff) / + sizeof(*pao->pci.ap_mem_base[1]); + } else { + /* BAR0 access - all of DSP memory using */ + /* pre-fetchable PCI access */ + u32 dw4M_page = address >> 22L; + if (dw4M_page != phw->dsp_page) { + phw->dsp_page = dw4M_page; + /* *INDENT-OFF* */ + iowrite32(phw->dsp_page, phw->prDSPP); + /* *INDENT-ON* */ + } + address &= 0x3fffff; /* address within 4M page */ + p_data = pao->pci.ap_mem_base[0] + + address / sizeof(u32); + } + iowrite32(data, p_data); + } else if (dsp_index == 1) { + /* DSP 1 is a C6713 */ + boot_loader_write_mem32(pao, 0, HPIAL_ADDR, address); + boot_loader_write_mem32(pao, 0, HPIAH_ADDR, address >> 16); + + /* dummy read every 4 words for 6205 advisory 1.4.4 */ + boot_loader_read_mem32(pao, 0, 0); + + boot_loader_write_mem32(pao, 0, HPIDL_ADDR, data); + boot_loader_write_mem32(pao, 0, HPIDH_ADDR, data >> 16); + + /* dummy read every 4 words for 6205 advisory 1.4.4 */ + boot_loader_read_mem32(pao, 0, 0); + } else + err = hpi6205_error(dsp_index, HPI6205_ERROR_BAD_DSPINDEX); + return err; +} + +static u16 boot_loader_config_emif(struct hpi_adapter_obj *pao, int dsp_index) +{ + u16 err = 0; + + if (dsp_index == 0) { + u32 setting; + + /* DSP 0 is always C6205 */ + + /* Set the EMIF */ + /* memory map of C6205 */ + /* 00000000-0000FFFF 16Kx32 internal program */ + /* 00400000-00BFFFFF CE0 2Mx32 SDRAM running @ 100MHz */ + + /* EMIF config */ + /*------------ */ + /* Global EMIF control */ + boot_loader_write_mem32(pao, dsp_index, 0x01800000, 0x3779); +#define WS_OFS 28 +#define WST_OFS 22 +#define WH_OFS 20 +#define RS_OFS 16 +#define RST_OFS 8 +#define MTYPE_OFS 4 +#define RH_OFS 0 + + /* EMIF CE0 setup - 2Mx32 Sync DRAM on ASI5000 cards only */ + setting = 0x00000030; + boot_loader_write_mem32(pao, dsp_index, 0x01800008, setting); + if (setting != boot_loader_read_mem32(pao, dsp_index, + 0x01800008)) + return hpi6205_error(dsp_index, + HPI6205_ERROR_DSP_EMIF); + + /* EMIF CE1 setup - 32 bit async. This is 6713 #1 HPI, */ + /* which occupies D15..0. 6713 starts at 27MHz, so need */ + /* plenty of wait states. See dsn8701.rtf, and 6713 errata. */ + /* WST should be 71, but 63 is max possible */ + setting = + (1L << WS_OFS) | (63L << WST_OFS) | (1L << WH_OFS) | + (1L << RS_OFS) | (63L << RST_OFS) | (1L << RH_OFS) | + (2L << MTYPE_OFS); + boot_loader_write_mem32(pao, dsp_index, 0x01800004, setting); + if (setting != boot_loader_read_mem32(pao, dsp_index, + 0x01800004)) + return hpi6205_error(dsp_index, + HPI6205_ERROR_DSP_EMIF); + + /* EMIF CE2 setup - 32 bit async. This is 6713 #2 HPI, */ + /* which occupies D15..0. 6713 starts at 27MHz, so need */ + /* plenty of wait states */ + setting = + (1L << WS_OFS) | (28L << WST_OFS) | (1L << WH_OFS) | + (1L << RS_OFS) | (63L << RST_OFS) | (1L << RH_OFS) | + (2L << MTYPE_OFS); + boot_loader_write_mem32(pao, dsp_index, 0x01800010, setting); + if (setting != boot_loader_read_mem32(pao, dsp_index, + 0x01800010)) + return hpi6205_error(dsp_index, + HPI6205_ERROR_DSP_EMIF); + + /* EMIF CE3 setup - 32 bit async. */ + /* This is the PLD on the ASI5000 cards only */ + setting = + (1L << WS_OFS) | (10L << WST_OFS) | (1L << WH_OFS) | + (1L << RS_OFS) | (10L << RST_OFS) | (1L << RH_OFS) | + (2L << MTYPE_OFS); + boot_loader_write_mem32(pao, dsp_index, 0x01800014, setting); + if (setting != boot_loader_read_mem32(pao, dsp_index, + 0x01800014)) + return hpi6205_error(dsp_index, + HPI6205_ERROR_DSP_EMIF); + + /* set EMIF SDRAM control for 2Mx32 SDRAM (512x32x4 bank) */ + /* need to use this else DSP code crashes? */ + boot_loader_write_mem32(pao, dsp_index, 0x01800018, + 0x07117000); + + /* EMIF SDRAM Refresh Timing */ + /* EMIF SDRAM timing (orig = 0x410, emulator = 0x61a) */ + boot_loader_write_mem32(pao, dsp_index, 0x0180001C, + 0x00000410); + + } else if (dsp_index == 1) { + /* test access to the C6713s HPI registers */ + u32 write_data = 0, read_data = 0, i = 0; + + /* Set up HPIC for little endian, by setiing HPIC:HWOB=1 */ + write_data = 1; + boot_loader_write_mem32(pao, 0, HPICL_ADDR, write_data); + boot_loader_write_mem32(pao, 0, HPICH_ADDR, write_data); + /* C67 HPI is on lower 16bits of 32bit EMIF */ + read_data = + 0xFFF7 & boot_loader_read_mem32(pao, 0, HPICL_ADDR); + if (write_data != read_data) { + err = hpi6205_error(dsp_index, + HPI6205_ERROR_C6713_HPIC); + HPI_DEBUG_LOG(ERROR, "HPICL %x %x\n", write_data, + read_data); + + return err; + } + /* HPIA - walking ones test */ + write_data = 1; + for (i = 0; i < 32; i++) { + boot_loader_write_mem32(pao, 0, HPIAL_ADDR, + write_data); + boot_loader_write_mem32(pao, 0, HPIAH_ADDR, + (write_data >> 16)); + read_data = + 0xFFFF & boot_loader_read_mem32(pao, 0, + HPIAL_ADDR); + read_data = + read_data | ((0xFFFF & + boot_loader_read_mem32(pao, 0, + HPIAH_ADDR)) + << 16); + if (read_data != write_data) { + err = hpi6205_error(dsp_index, + HPI6205_ERROR_C6713_HPIA); + HPI_DEBUG_LOG(ERROR, "HPIA %x %x\n", + write_data, read_data); + return err; + } + write_data = write_data << 1; + } + + /* setup C67x PLL + * ** C6713 datasheet says we cannot program PLL from HPI, + * and indeed if we try to set the PLL multiply from the HPI, + * the PLL does not seem to lock, so we enable the PLL and + * use the default multiply of x 7, which for a 27MHz clock + * gives a DSP speed of 189MHz + */ + /* bypass PLL */ + boot_loader_write_mem32(pao, dsp_index, 0x01B7C100, 0x0000); + hpios_delay_micro_seconds(1000); + /* EMIF = 189/3=63MHz */ + boot_loader_write_mem32(pao, dsp_index, 0x01B7C120, 0x8002); + /* peri = 189/2 */ + boot_loader_write_mem32(pao, dsp_index, 0x01B7C11C, 0x8001); + /* cpu = 189/1 */ + boot_loader_write_mem32(pao, dsp_index, 0x01B7C118, 0x8000); + hpios_delay_micro_seconds(1000); + /* ** SGT test to take GPO3 high when we start the PLL */ + /* and low when the delay is completed */ + /* FSX0 <- '1' (GPO3) */ + boot_loader_write_mem32(pao, 0, (0x018C0024L), 0x00002A0A); + /* PLL not bypassed */ + boot_loader_write_mem32(pao, dsp_index, 0x01B7C100, 0x0001); + hpios_delay_micro_seconds(1000); + /* FSX0 <- '0' (GPO3) */ + boot_loader_write_mem32(pao, 0, (0x018C0024L), 0x00002A02); + + /* 6205 EMIF CE1 resetup - 32 bit async. */ + /* Now 6713 #1 is running at 189MHz can reduce waitstates */ + boot_loader_write_mem32(pao, 0, 0x01800004, /* CE1 */ + (1L << WS_OFS) | (8L << WST_OFS) | (1L << WH_OFS) | + (1L << RS_OFS) | (12L << RST_OFS) | (1L << RH_OFS) | + (2L << MTYPE_OFS)); + + hpios_delay_micro_seconds(1000); + + /* check that we can read one of the PLL registers */ + /* PLL should not be bypassed! */ + if ((boot_loader_read_mem32(pao, dsp_index, 0x01B7C100) & 0xF) + != 0x0001) { + err = hpi6205_error(dsp_index, + HPI6205_ERROR_C6713_PLL); + return err; + } + /* setup C67x EMIF (note this is the only use of + BAR1 via BootLoader_WriteMem32) */ + boot_loader_write_mem32(pao, dsp_index, C6713_EMIF_GCTL, + 0x000034A8); + boot_loader_write_mem32(pao, dsp_index, C6713_EMIF_CE0, + 0x00000030); + boot_loader_write_mem32(pao, dsp_index, C6713_EMIF_SDRAMEXT, + 0x001BDF29); + boot_loader_write_mem32(pao, dsp_index, C6713_EMIF_SDRAMCTL, + 0x47117000); + boot_loader_write_mem32(pao, dsp_index, + C6713_EMIF_SDRAMTIMING, 0x00000410); + + hpios_delay_micro_seconds(1000); + } else if (dsp_index == 2) { + /* DSP 2 is a C6713 */ + + } else + err = hpi6205_error(dsp_index, HPI6205_ERROR_BAD_DSPINDEX); + return err; +} + +static u16 boot_loader_test_memory(struct hpi_adapter_obj *pao, int dsp_index, + u32 start_address, u32 length) +{ + u32 i = 0, j = 0; + u32 test_addr = 0; + u32 test_data = 0, data = 0; + + length = 1000; + + /* for 1st word, test each bit in the 32bit word, */ + /* dwLength specifies number of 32bit words to test */ + /*for(i=0; ipci.subsys_device_id == 0x5000) { + /* DSP 0 is always C6205 */ + dRAM_start_address = 0x00400000; + dRAM_size = 0x200000; + /*dwDRAMinc=1024; */ + } else + return 0; + } else if ((dsp_index == 1) || (dsp_index == 2)) { + /* DSP 1 is a C6713 */ + dRAM_start_address = 0x80000000; + dRAM_size = 0x200000; + /*dwDRAMinc=1024; */ + } else + return hpi6205_error(dsp_index, HPI6205_ERROR_BAD_DSPINDEX); + + if (boot_loader_test_memory(pao, dsp_index, dRAM_start_address, + dRAM_size)) + return hpi6205_error(dsp_index, HPI6205_ERROR_DSP_EXTMEM); + return 0; +} + +static u16 boot_loader_test_pld(struct hpi_adapter_obj *pao, int dsp_index) +{ + u32 data = 0; + if (dsp_index == 0) { + /* only test for DSP0 PLD on ASI5000 card */ + if (pao->pci.subsys_device_id == 0x5000) { + /* PLD is located at CE3=0x03000000 */ + data = boot_loader_read_mem32(pao, dsp_index, + 0x03000008); + if ((data & 0xF) != 0x5) + return hpi6205_error(dsp_index, + HPI6205_ERROR_DSP_PLD); + data = boot_loader_read_mem32(pao, dsp_index, + 0x0300000C); + if ((data & 0xF) != 0xA) + return hpi6205_error(dsp_index, + HPI6205_ERROR_DSP_PLD); + } + } else if (dsp_index == 1) { + /* DSP 1 is a C6713 */ + if (pao->pci.subsys_device_id == 0x8700) { + /* PLD is located at CE1=0x90000000 */ + data = boot_loader_read_mem32(pao, dsp_index, + 0x90000010); + if ((data & 0xFF) != 0xAA) + return hpi6205_error(dsp_index, + HPI6205_ERROR_DSP_PLD); + /* 8713 - LED on */ + boot_loader_write_mem32(pao, dsp_index, 0x90000000, + 0x02); + } + } + return 0; +} + +/** Transfer data to or from DSP + nOperation = H620_H620_HIF_SEND_DATA or H620_HIF_GET_DATA +*/ +static short hpi6205_transfer_data(struct hpi_adapter_obj *pao, u8 *p_data, + u32 data_size, int operation) +{ + struct hpi_hw_obj *phw = pao->priv; + u32 data_transferred = 0; + u16 err = 0; +#ifndef HPI6205_NO_HSR_POLL + u32 time_out; +#endif + u32 temp2; + struct bus_master_interface *interface = phw->p_interface_buffer; + + if (!p_data) + return HPI_ERROR_INVALID_DATA_TRANSFER; + + data_size &= ~3L; /* round data_size down to nearest 4 bytes */ + + /* make sure state is IDLE */ + if (!wait_dsp_ack(phw, H620_HIF_IDLE, HPI6205_TIMEOUT)) + return HPI_ERROR_DSP_HARDWARE; + + while (data_transferred < data_size) { + u32 this_copy = data_size - data_transferred; + + if (this_copy > HPI6205_SIZEOF_DATA) + this_copy = HPI6205_SIZEOF_DATA; + + if (operation == H620_HIF_SEND_DATA) + memcpy((void *)&interface->u.b_data[0], + &p_data[data_transferred], this_copy); + + interface->transfer_size_in_bytes = this_copy; + +#ifdef HPI6205_NO_HSR_POLL + /* DSP must change this back to nOperation */ + interface->dsp_ack = H620_HIF_IDLE; +#endif + + send_dsp_command(phw, operation); + +#ifdef HPI6205_NO_HSR_POLL + temp2 = wait_dsp_ack(phw, operation, HPI6205_TIMEOUT); + HPI_DEBUG_LOG(DEBUG, "spun %d times for data xfer of %d\n", + HPI6205_TIMEOUT - temp2, this_copy); + + if (!temp2) { + /* timed out */ + HPI_DEBUG_LOG(ERROR, + "timed out waiting for " "state %d got %d\n", + operation, interface->dsp_ack); + + break; + } +#else + /* spin waiting on the result */ + time_out = HPI6205_TIMEOUT; + temp2 = 0; + while ((temp2 == 0) && time_out--) { + /* give 16k bus mastering transfer time to happen */ + /*(16k / 132Mbytes/s = 122usec) */ + hpios_delay_micro_seconds(20); + temp2 = ioread32(phw->prHSR); + temp2 &= C6205_HSR_INTSRC; + } + HPI_DEBUG_LOG(DEBUG, "spun %d times for data xfer of %d\n", + HPI6205_TIMEOUT - time_out, this_copy); + if (temp2 == C6205_HSR_INTSRC) { + HPI_DEBUG_LOG(VERBOSE, + "interrupt from HIF OK\n"); + /* + if(interface->dwDspAck != nOperation) { + HPI_DEBUG_LOG(DEBUG("interface->dwDspAck=%d, + expected %d \n", + interface->dwDspAck,nOperation); + } + */ + } +/* need to handle this differently... */ + else { + HPI_DEBUG_LOG(ERROR, + "interrupt from HIF BAD\n"); + err = HPI_ERROR_DSP_HARDWARE; + } + + /* reset the interrupt from the DSP */ + iowrite32(C6205_HSR_INTSRC, phw->prHSR); +#endif + if (operation == H620_HIF_GET_DATA) + memcpy(&p_data[data_transferred], + (void *)&interface->u.b_data[0], this_copy); + + data_transferred += this_copy; + } + if (interface->dsp_ack != operation) + HPI_DEBUG_LOG(DEBUG, "interface->dsp_ack=%d, expected %d\n", + interface->dsp_ack, operation); + /* err=HPI_ERROR_DSP_HARDWARE; */ + + send_dsp_command(phw, H620_HIF_IDLE); + + return err; +} + +/* wait for up to timeout_us microseconds for the DSP + to signal state by DMA into dwDspAck +*/ +static int wait_dsp_ack(struct hpi_hw_obj *phw, int state, int timeout_us) +{ + struct bus_master_interface *interface = phw->p_interface_buffer; + int t = timeout_us / 4; + + rmb(); /* ensure interface->dsp_ack is up to date */ + while ((interface->dsp_ack != state) && --t) { + hpios_delay_micro_seconds(4); + rmb(); /* DSP changes dsp_ack by DMA */ + } + + /*HPI_DEBUG_LOG(VERBOSE, "Spun %d for %d\n", timeout_us/4-t, state); */ + return t * 4; +} + +/* set the busmaster interface to cmd, then interrupt the DSP */ +static void send_dsp_command(struct hpi_hw_obj *phw, int cmd) +{ + struct bus_master_interface *interface = phw->p_interface_buffer; + + u32 r; + + interface->host_cmd = cmd; + wmb(); /* DSP gets state by DMA, make sure it is written to memory */ + /* before we interrupt the DSP */ + r = ioread32(phw->prHDCR); + r |= (u32)C6205_HDCR_DSPINT; + iowrite32(r, phw->prHDCR); + r &= ~(u32)C6205_HDCR_DSPINT; + iowrite32(r, phw->prHDCR); +} + +static unsigned int message_count; + +static u16 message_response_sequence(struct hpi_adapter_obj *pao, + struct hpi_message *phm, struct hpi_response *phr) +{ +#ifndef HPI6205_NO_HSR_POLL + u32 temp2; +#endif + u32 time_out, time_out2; + struct hpi_hw_obj *phw = pao->priv; + struct bus_master_interface *interface = phw->p_interface_buffer; + u16 err = 0; + + message_count++; + /* Assume buffer of type struct bus_master_interface + is allocated "noncacheable" */ + + if (!wait_dsp_ack(phw, H620_HIF_IDLE, HPI6205_TIMEOUT)) { + HPI_DEBUG_LOG(DEBUG, "timeout waiting for idle\n"); + return hpi6205_error(0, HPI6205_ERROR_MSG_RESP_IDLE_TIMEOUT); + } + interface->u.message_buffer = *phm; + /* signal we want a response */ + send_dsp_command(phw, H620_HIF_GET_RESP); + + time_out2 = wait_dsp_ack(phw, H620_HIF_GET_RESP, HPI6205_TIMEOUT); + + if (time_out2 == 0) { + HPI_DEBUG_LOG(ERROR, + "(%u) timed out waiting for " "GET_RESP state [%x]\n", + message_count, interface->dsp_ack); + } else { + HPI_DEBUG_LOG(VERBOSE, + "(%u) transition to GET_RESP after %u\n", + message_count, HPI6205_TIMEOUT - time_out2); + } + /* spin waiting on HIF interrupt flag (end of msg process) */ + time_out = HPI6205_TIMEOUT; + +#ifndef HPI6205_NO_HSR_POLL + temp2 = 0; + while ((temp2 == 0) && --time_out) { + temp2 = ioread32(phw->prHSR); + temp2 &= C6205_HSR_INTSRC; + hpios_delay_micro_seconds(1); + } + if (temp2 == C6205_HSR_INTSRC) { + rmb(); /* ensure we see latest value for dsp_ack */ + if ((interface->dsp_ack != H620_HIF_GET_RESP)) { + HPI_DEBUG_LOG(DEBUG, + "(%u)interface->dsp_ack(0x%x) != " + "H620_HIF_GET_RESP, t=%u\n", message_count, + interface->dsp_ack, + HPI6205_TIMEOUT - time_out); + } else { + HPI_DEBUG_LOG(VERBOSE, + "(%u)int with GET_RESP after %u\n", + message_count, HPI6205_TIMEOUT - time_out); + } + + } else { + /* can we do anything else in response to the error ? */ + HPI_DEBUG_LOG(ERROR, + "interrupt from HIF module BAD (function %x)\n", + phm->function); + } + + /* reset the interrupt from the DSP */ + iowrite32(C6205_HSR_INTSRC, phw->prHSR); +#endif + + /* read the result */ + if (time_out != 0) + *phr = interface->u.response_buffer; + + /* set interface back to idle */ + send_dsp_command(phw, H620_HIF_IDLE); + + if ((time_out == 0) || (time_out2 == 0)) { + HPI_DEBUG_LOG(DEBUG, "something timed out!\n"); + return hpi6205_error(0, HPI6205_ERROR_MSG_RESP_TIMEOUT); + } + /* special case for adapter close - */ + /* wait for the DSP to indicate it is idle */ + if (phm->function == HPI_ADAPTER_CLOSE) { + if (!wait_dsp_ack(phw, H620_HIF_IDLE, HPI6205_TIMEOUT)) { + HPI_DEBUG_LOG(DEBUG, + "timeout waiting for idle " + "(on adapter_close)\n"); + return hpi6205_error(0, + HPI6205_ERROR_MSG_RESP_IDLE_TIMEOUT); + } + } + err = hpi_validate_response(phm, phr); + return err; +} + +static void hw_message(struct hpi_adapter_obj *pao, struct hpi_message *phm, + struct hpi_response *phr) +{ + + u16 err = 0; + + hpios_dsplock_lock(pao); + + err = message_response_sequence(pao, phm, phr); + + /* maybe an error response */ + if (err) { + /* something failed in the HPI/DSP interface */ + phr->error = err; + pao->dsp_crashed++; + + /* just the header of the response is valid */ + phr->size = sizeof(struct hpi_response_header); + goto err; + } else + pao->dsp_crashed = 0; + + if (phr->error != 0) /* something failed in the DSP */ + goto err; + + switch (phm->function) { + case HPI_OSTREAM_WRITE: + case HPI_ISTREAM_ANC_WRITE: + err = hpi6205_transfer_data(pao, phm->u.d.u.data.pb_data, + phm->u.d.u.data.data_size, H620_HIF_SEND_DATA); + break; + + case HPI_ISTREAM_READ: + case HPI_OSTREAM_ANC_READ: + err = hpi6205_transfer_data(pao, phm->u.d.u.data.pb_data, + phm->u.d.u.data.data_size, H620_HIF_GET_DATA); + break; + + case HPI_CONTROL_SET_STATE: + if (phm->object == HPI_OBJ_CONTROLEX + && phm->u.cx.attribute == HPI_COBRANET_SET_DATA) + err = hpi6205_transfer_data(pao, + phm->u.cx.u.cobranet_bigdata.pb_data, + phm->u.cx.u.cobranet_bigdata.byte_count, + H620_HIF_SEND_DATA); + break; + + case HPI_CONTROL_GET_STATE: + if (phm->object == HPI_OBJ_CONTROLEX + && phm->u.cx.attribute == HPI_COBRANET_GET_DATA) + err = hpi6205_transfer_data(pao, + phm->u.cx.u.cobranet_bigdata.pb_data, + phr->u.cx.u.cobranet_data.byte_count, + H620_HIF_GET_DATA); + break; + } + phr->error = err; + +err: + hpios_dsplock_unlock(pao); + + return; +} diff --git a/sound/pci/asihpi/hpi6205.h b/sound/pci/asihpi/hpi6205.h new file mode 100644 index 000000000000..1adae0857cda --- /dev/null +++ b/sound/pci/asihpi/hpi6205.h @@ -0,0 +1,93 @@ +/***************************************************************************** + + AudioScience HPI driver + Copyright (C) 1997-2010 AudioScience Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation; + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Host Interface module for an ASI6205 based +bus mastering PCI adapter. + +Copyright AudioScience, Inc., 2003 +******************************************************************************/ + +#ifndef _HPI6205_H_ +#define _HPI6205_H_ + +/* transitional conditional compile shared between host and DSP */ +/* #define HPI6205_NO_HSR_POLL */ + +#include "hpi_internal.h" + +/*********************************************************** + Defines used for basic messaging +************************************************************/ +#define H620_HIF_RESET 0 +#define H620_HIF_IDLE 1 +#define H620_HIF_GET_RESP 2 +#define H620_HIF_DATA_DONE 3 +#define H620_HIF_DATA_MASK 0x10 +#define H620_HIF_SEND_DATA 0x14 +#define H620_HIF_GET_DATA 0x15 +#define H620_HIF_UNKNOWN 0x0000ffff + +/*********************************************************** + Types used for mixer control caching +************************************************************/ + +#define H620_MAX_ISTREAMS 32 +#define H620_MAX_OSTREAMS 32 +#define HPI_NMIXER_CONTROLS 2048 + +/********************************************************************* +This is used for dynamic control cache allocation +**********************************************************************/ +struct controlcache_6205 { + u32 number_of_controls; + u32 physical_address32; + u32 size_in_bytes; +}; + +/********************************************************************* +This is used for dynamic allocation of async event array +**********************************************************************/ +struct async_event_buffer_6205 { + u32 physical_address32; + u32 spare; + struct hpi_fifo_buffer b; +}; + +/*********************************************************** +The Host located memory buffer that the 6205 will bus master +in and out of. +************************************************************/ +#define HPI6205_SIZEOF_DATA (16*1024) +struct bus_master_interface { + u32 host_cmd; + u32 dsp_ack; + u32 transfer_size_in_bytes; + union { + struct hpi_message message_buffer; + struct hpi_response response_buffer; + u8 b_data[HPI6205_SIZEOF_DATA]; + } u; + struct controlcache_6205 control_cache; + struct async_event_buffer_6205 async_buffer; + struct hpi_hostbuffer_status + instream_host_buffer_status[H620_MAX_ISTREAMS]; + struct hpi_hostbuffer_status + outstream_host_buffer_status[H620_MAX_OSTREAMS]; +}; + +#endif diff --git a/sound/pci/asihpi/hpi_internal.h b/sound/pci/asihpi/hpi_internal.h new file mode 100644 index 000000000000..f1cd6f1a0d44 --- /dev/null +++ b/sound/pci/asihpi/hpi_internal.h @@ -0,0 +1,1641 @@ +/****************************************************************************** + + AudioScience HPI driver + Copyright (C) 1997-2010 AudioScience Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation; + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +HPI internal definitions + +(C) Copyright AudioScience Inc. 1996-2009 +******************************************************************************/ + +#ifndef _HPI_INTERNAL_H_ +#define _HPI_INTERNAL_H_ + +#include "hpi.h" +/** maximum number of memory regions mapped to an adapter */ +#define HPI_MAX_ADAPTER_MEM_SPACES (2) + +/* Each OS needs its own hpios.h, or specific define as above */ +#include "hpios.h" + +/* physical memory allocation */ +void hpios_locked_mem_init(void + ); +void hpios_locked_mem_free_all(void + ); +#define hpios_locked_mem_prepare(a, b, c, d); +#define hpios_locked_mem_unprepare(a) + +/** Allocate and map an area of locked memory for bus master DMA operations. + +On success, *pLockedMemeHandle is a valid handle, and 0 is returned +On error *pLockedMemHandle marked invalid, non-zero returned. + +If this function succeeds, then HpiOs_LockedMem_GetVirtAddr() and +HpiOs_LockedMem_GetPyhsAddr() will always succed on the returned handle. +*/ +u16 hpios_locked_mem_alloc(struct consistent_dma_area *p_locked_mem_handle, + /**< memory handle */ + u32 size, /**< size in bytes to allocate */ + struct pci_dev *p_os_reference + /**< OS specific data required for memory allocation */ + ); + +/** Free mapping and memory represented by LockedMemHandle + +Frees any resources, then invalidates the handle. +Returns 0 on success, 1 if handle is invalid. + +*/ +u16 hpios_locked_mem_free(struct consistent_dma_area *locked_mem_handle); + +/** Get the physical PCI address of memory represented by LockedMemHandle. + +If handle is invalid *pPhysicalAddr is set to zero and return 1 +*/ +u16 hpios_locked_mem_get_phys_addr(struct consistent_dma_area + *locked_mem_handle, u32 *p_physical_addr); + +/** Get the CPU address of of memory represented by LockedMemHandle. + +If handle is NULL *ppvVirtualAddr is set to NULL and return 1 +*/ +u16 hpios_locked_mem_get_virt_addr(struct consistent_dma_area + *locked_mem_handle, void **ppv_virtual_addr); + +/** Check that handle is valid +i.e it represents a valid memory area +*/ +u16 hpios_locked_mem_valid(struct consistent_dma_area *locked_mem_handle); + +/* timing/delay */ +void hpios_delay_micro_seconds(u32 num_micro_sec); + +struct hpi_message; +struct hpi_response; + +typedef void hpi_handler_func(struct hpi_message *, struct hpi_response *); + +/* If the assert fails, compiler complains + something like size of array `msg' is negative. + Unlike linux BUILD_BUG_ON, this works outside function scope. +*/ +#define compile_time_assert(cond, msg) \ + typedef char ASSERT_##msg[(cond) ? 1 : -1] + +/*/////////////////////////////////////////////////////////////////////////// */ +/* Private HPI Entity related definitions */ + +#define STR_SIZE_FIELD_MAX 65535U +#define STR_TYPE_FIELD_MAX 255U +#define STR_ROLE_FIELD_MAX 255U + +struct hpi_entity_str { + uint16_t size; + uint8_t type; + uint8_t role; +}; + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4200) +#endif + +struct hpi_entity { + struct hpi_entity_str header; +#if ! defined(HPI_OS_DSP_C6000) || (defined(HPI_OS_DSP_C6000) && (__TI_COMPILER_VERSION__ > 6000008)) + /* DSP C6000 compiler v6.0.8 and lower + do not support flexible array member */ + uint8_t value[]; +#else + /* NOTE! Using sizeof(struct hpi_entity) will give erroneous results */ +#define HPI_INTERNAL_WARN_ABOUT_ENTITY_VALUE + uint8_t value[1]; +#endif +}; + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +/******************************************* bus types */ +enum HPI_BUSES { + HPI_BUS_ISAPNP = 1, + HPI_BUS_PCI = 2, + HPI_BUS_USB = 3, + HPI_BUS_NET = 4 +}; + +/******************************************* CONTROL ATTRIBUTES ****/ +/* (in order of control type ID */ + + /* This allows for 255 control types, 256 unique attributes each */ +#define HPI_CTL_ATTR(ctl, ai) (HPI_CONTROL_##ctl * 0x100 + ai) + +/* Get the sub-index of the attribute for a control type */ +#define HPI_CTL_ATTR_INDEX(i) (i&0xff) + +/* Generic control attributes. */ + +/** Enable a control. +0=disable, 1=enable +\note generic to all mixer plugins? +*/ +#define HPI_GENERIC_ENABLE HPI_CTL_ATTR(GENERIC, 1) + +/** Enable event generation for a control. +0=disable, 1=enable +\note generic to all controls that can generate events +*/ +#define HPI_GENERIC_EVENT_ENABLE HPI_CTL_ATTR(GENERIC, 2) + +/* Volume Control attributes */ +#define HPI_VOLUME_GAIN HPI_CTL_ATTR(VOLUME, 1) +#define HPI_VOLUME_AUTOFADE HPI_CTL_ATTR(VOLUME, 2) + +/** For HPI_ControlQuery() to get the number of channels of a volume control*/ +#define HPI_VOLUME_NUM_CHANNELS HPI_CTL_ATTR(VOLUME, 6) +#define HPI_VOLUME_RANGE HPI_CTL_ATTR(VOLUME, 10) + +/** Level Control attributes */ +#define HPI_LEVEL_GAIN HPI_CTL_ATTR(LEVEL, 1) +#define HPI_LEVEL_RANGE HPI_CTL_ATTR(LEVEL, 10) + +/* Meter Control attributes */ +/** return RMS signal level */ +#define HPI_METER_RMS HPI_CTL_ATTR(METER, 1) +/** return peak signal level */ +#define HPI_METER_PEAK HPI_CTL_ATTR(METER, 2) +/** ballistics for ALL rms meters on adapter */ +#define HPI_METER_RMS_BALLISTICS HPI_CTL_ATTR(METER, 3) +/** ballistics for ALL peak meters on adapter */ +#define HPI_METER_PEAK_BALLISTICS HPI_CTL_ATTR(METER, 4) + +/** For HPI_ControlQuery() to get the number of channels of a meter control*/ +#define HPI_METER_NUM_CHANNELS HPI_CTL_ATTR(METER, 5) + +/* Multiplexer control attributes */ +#define HPI_MULTIPLEXER_SOURCE HPI_CTL_ATTR(MULTIPLEXER, 1) +#define HPI_MULTIPLEXER_QUERYSOURCE HPI_CTL_ATTR(MULTIPLEXER, 2) + +/** AES/EBU transmitter control attributes */ +/** AESEBU or SPDIF */ +#define HPI_AESEBUTX_FORMAT HPI_CTL_ATTR(AESEBUTX, 1) +#define HPI_AESEBUTX_SAMPLERATE HPI_CTL_ATTR(AESEBUTX, 3) +#define HPI_AESEBUTX_CHANNELSTATUS HPI_CTL_ATTR(AESEBUTX, 4) +#define HPI_AESEBUTX_USERDATA HPI_CTL_ATTR(AESEBUTX, 5) + +/** AES/EBU receiver control attributes */ +#define HPI_AESEBURX_FORMAT HPI_CTL_ATTR(AESEBURX, 1) +#define HPI_AESEBURX_ERRORSTATUS HPI_CTL_ATTR(AESEBURX, 2) +#define HPI_AESEBURX_SAMPLERATE HPI_CTL_ATTR(AESEBURX, 3) +#define HPI_AESEBURX_CHANNELSTATUS HPI_CTL_ATTR(AESEBURX, 4) +#define HPI_AESEBURX_USERDATA HPI_CTL_ATTR(AESEBURX, 5) + +/** \defgroup tuner_defs Tuners +\{ +*/ +/** \defgroup tuner_attrs Tuner control attributes +\{ +*/ +#define HPI_TUNER_BAND HPI_CTL_ATTR(TUNER, 1) +#define HPI_TUNER_FREQ HPI_CTL_ATTR(TUNER, 2) +#define HPI_TUNER_LEVEL HPI_CTL_ATTR(TUNER, 3) +#define HPI_TUNER_AUDIOMUTE HPI_CTL_ATTR(TUNER, 4) +/* use TUNER_STATUS instead */ +#define HPI_TUNER_VIDEO_STATUS HPI_CTL_ATTR(TUNER, 5) +#define HPI_TUNER_GAIN HPI_CTL_ATTR(TUNER, 6) +#define HPI_TUNER_STATUS HPI_CTL_ATTR(TUNER, 7) +#define HPI_TUNER_MODE HPI_CTL_ATTR(TUNER, 8) +/** RDS data. */ +#define HPI_TUNER_RDS HPI_CTL_ATTR(TUNER, 9) +/** Audio pre-emphasis. */ +#define HPI_TUNER_DEEMPHASIS HPI_CTL_ATTR(TUNER, 10) +/** HD Radio tuner program control. */ +#define HPI_TUNER_PROGRAM HPI_CTL_ATTR(TUNER, 11) +/** HD Radio tuner digital signal quality. */ +#define HPI_TUNER_HDRADIO_SIGNAL_QUALITY HPI_CTL_ATTR(TUNER, 12) +/** HD Radio SDK firmware version. */ +#define HPI_TUNER_HDRADIO_SDK_VERSION HPI_CTL_ATTR(TUNER, 13) +/** HD Radio DSP firmware version. */ +#define HPI_TUNER_HDRADIO_DSP_VERSION HPI_CTL_ATTR(TUNER, 14) + +/** \} */ + +/** \defgroup pads_attrs Tuner PADs control attributes +\{ +*/ +/** The text string containing the station/channel combination. */ +#define HPI_PAD_CHANNEL_NAME HPI_CTL_ATTR(PAD, 1) +/** The text string containing the artist. */ +#define HPI_PAD_ARTIST HPI_CTL_ATTR(PAD, 2) +/** The text string containing the title. */ +#define HPI_PAD_TITLE HPI_CTL_ATTR(PAD, 3) +/** The text string containing the comment. */ +#define HPI_PAD_COMMENT HPI_CTL_ATTR(PAD, 4) +/** The integer containing the PTY code. */ +#define HPI_PAD_PROGRAM_TYPE HPI_CTL_ATTR(PAD, 5) +/** The integer containing the program identification. */ +#define HPI_PAD_PROGRAM_ID HPI_CTL_ATTR(PAD, 6) +/** The integer containing whether traffic information is supported. +Contains either 1 or 0. */ +#define HPI_PAD_TA_SUPPORT HPI_CTL_ATTR(PAD, 7) +/** The integer containing whether traffic announcement is in progress. +Contains either 1 or 0. */ +#define HPI_PAD_TA_ACTIVE HPI_CTL_ATTR(PAD, 8) +/** \} */ +/** \} */ + +/* VOX control attributes */ +#define HPI_VOX_THRESHOLD HPI_CTL_ATTR(VOX, 1) + +/*?? channel mode used hpi_multiplexer_source attribute == 1 */ +#define HPI_CHANNEL_MODE_MODE HPI_CTL_ATTR(CHANNEL_MODE, 1) + +/** \defgroup channel_modes Channel Modes +Used for HPI_ChannelModeSet/Get() +\{ +*/ +/** Left channel out = left channel in, Right channel out = right channel in. */ +#define HPI_CHANNEL_MODE_NORMAL 1 +/** Left channel out = right channel in, Right channel out = left channel in. */ +#define HPI_CHANNEL_MODE_SWAP 2 +/** Left channel out = left channel in, Right channel out = left channel in. */ +#define HPI_CHANNEL_MODE_LEFT_TO_STEREO 3 +/** Left channel out = right channel in, Right channel out = right channel in.*/ +#define HPI_CHANNEL_MODE_RIGHT_TO_STEREO 4 +/** Left channel out = (left channel in + right channel in)/2, + Right channel out = mute. */ +#define HPI_CHANNEL_MODE_STEREO_TO_LEFT 5 +/** Left channel out = mute, + Right channel out = (right channel in + left channel in)/2. */ +#define HPI_CHANNEL_MODE_STEREO_TO_RIGHT 6 +#define HPI_CHANNEL_MODE_LAST 6 +/** \} */ + +/* Bitstream control set attributes */ +#define HPI_BITSTREAM_DATA_POLARITY HPI_CTL_ATTR(BITSTREAM, 1) +#define HPI_BITSTREAM_CLOCK_EDGE HPI_CTL_ATTR(BITSTREAM, 2) +#define HPI_BITSTREAM_CLOCK_SOURCE HPI_CTL_ATTR(BITSTREAM, 3) + +#define HPI_POLARITY_POSITIVE 0 +#define HPI_POLARITY_NEGATIVE 1 + +/* Bitstream control get attributes */ +#define HPI_BITSTREAM_ACTIVITY 1 + +/* SampleClock control attributes */ +#define HPI_SAMPLECLOCK_SOURCE HPI_CTL_ATTR(SAMPLECLOCK, 1) +#define HPI_SAMPLECLOCK_SAMPLERATE HPI_CTL_ATTR(SAMPLECLOCK, 2) +#define HPI_SAMPLECLOCK_SOURCE_INDEX HPI_CTL_ATTR(SAMPLECLOCK, 3) +#define HPI_SAMPLECLOCK_LOCAL_SAMPLERATE\ + HPI_CTL_ATTR(SAMPLECLOCK, 4) +#define HPI_SAMPLECLOCK_AUTO HPI_CTL_ATTR(SAMPLECLOCK, 5) +#define HPI_SAMPLECLOCK_LOCAL_LOCK HPI_CTL_ATTR(SAMPLECLOCK, 6) + +/* Microphone control attributes */ +#define HPI_MICROPHONE_PHANTOM_POWER HPI_CTL_ATTR(MICROPHONE, 1) + +/** Equalizer control attributes +*/ +/** Used to get number of filters in an EQ. (Can't set) */ +#define HPI_EQUALIZER_NUM_FILTERS HPI_CTL_ATTR(EQUALIZER, 1) +/** Set/get the filter by type, freq, Q, gain */ +#define HPI_EQUALIZER_FILTER HPI_CTL_ATTR(EQUALIZER, 2) +/** Get the biquad coefficients */ +#define HPI_EQUALIZER_COEFFICIENTS HPI_CTL_ATTR(EQUALIZER, 3) + +#define HPI_COMPANDER_PARAMS HPI_CTL_ATTR(COMPANDER, 1) + +/* Cobranet control attributes. + MUST be distinct from all other control attributes. + This is so that host side processing can easily identify a Cobranet control + and apply additional host side operations (like copying data) as required. +*/ +#define HPI_COBRANET_SET HPI_CTL_ATTR(COBRANET, 1) +#define HPI_COBRANET_GET HPI_CTL_ATTR(COBRANET, 2) +#define HPI_COBRANET_SET_DATA HPI_CTL_ATTR(COBRANET, 3) +#define HPI_COBRANET_GET_DATA HPI_CTL_ATTR(COBRANET, 4) +#define HPI_COBRANET_GET_STATUS HPI_CTL_ATTR(COBRANET, 5) +#define HPI_COBRANET_SEND_PACKET HPI_CTL_ATTR(COBRANET, 6) +#define HPI_COBRANET_GET_PACKET HPI_CTL_ATTR(COBRANET, 7) + +/*------------------------------------------------------------ + Cobranet Chip Bridge - copied from HMI.H +------------------------------------------------------------*/ +#define HPI_COBRANET_HMI_cobra_bridge 0x20000 +#define HPI_COBRANET_HMI_cobra_bridge_tx_pkt_buf \ + (HPI_COBRANET_HMI_cobra_bridge + 0x1000) +#define HPI_COBRANET_HMI_cobra_bridge_rx_pkt_buf \ + (HPI_COBRANET_HMI_cobra_bridge + 0x2000) +#define HPI_COBRANET_HMI_cobra_if_table1 0x110000 +#define HPI_COBRANET_HMI_cobra_if_phy_address \ + (HPI_COBRANET_HMI_cobra_if_table1 + 0xd) +#define HPI_COBRANET_HMI_cobra_protocolIP 0x72000 +#define HPI_COBRANET_HMI_cobra_ip_mon_currentIP \ + (HPI_COBRANET_HMI_cobra_protocolIP + 0x0) +#define HPI_COBRANET_HMI_cobra_ip_mon_staticIP \ + (HPI_COBRANET_HMI_cobra_protocolIP + 0x2) +#define HPI_COBRANET_HMI_cobra_sys 0x100000 +#define HPI_COBRANET_HMI_cobra_sys_desc \ + (HPI_COBRANET_HMI_cobra_sys + 0x0) +#define HPI_COBRANET_HMI_cobra_sys_objectID \ + (HPI_COBRANET_HMI_cobra_sys + 0x100) +#define HPI_COBRANET_HMI_cobra_sys_contact \ + (HPI_COBRANET_HMI_cobra_sys + 0x200) +#define HPI_COBRANET_HMI_cobra_sys_name \ + (HPI_COBRANET_HMI_cobra_sys + 0x300) +#define HPI_COBRANET_HMI_cobra_sys_location \ + (HPI_COBRANET_HMI_cobra_sys + 0x400) + +/*------------------------------------------------------------ + Cobranet Chip Status bits +------------------------------------------------------------*/ +#define HPI_COBRANET_HMI_STATUS_RXPACKET 2 +#define HPI_COBRANET_HMI_STATUS_TXPACKET 3 + +/*------------------------------------------------------------ + Ethernet header size +------------------------------------------------------------*/ +#define HPI_ETHERNET_HEADER_SIZE (16) + +/* These defines are used to fill in protocol information for an Ethernet packet + sent using HMI on CS18102 */ +/** ID supplied by Cirrius for ASI packets. */ +#define HPI_ETHERNET_PACKET_ID 0x85 +/** Simple packet - no special routing required */ +#define HPI_ETHERNET_PACKET_V1 0x01 +/** This packet must make its way to the host across the HPI interface */ +#define HPI_ETHERNET_PACKET_HOSTED_VIA_HMI 0x20 +/** This packet must make its way to the host across the HPI interface */ +#define HPI_ETHERNET_PACKET_HOSTED_VIA_HMI_V1 0x21 +/** This packet must make its way to the host across the HPI interface */ +#define HPI_ETHERNET_PACKET_HOSTED_VIA_HPI 0x40 +/** This packet must make its way to the host across the HPI interface */ +#define HPI_ETHERNET_PACKET_HOSTED_VIA_HPI_V1 0x41 + +#define HPI_ETHERNET_UDP_PORT (44600) /*!< UDP messaging port */ + +/** Base network time out is set to 100 milli-seconds. */ +#define HPI_ETHERNET_TIMEOUT_MS (100) + +/** \defgroup tonedet_attr Tonedetector attributes +\{ +Used by HPI_ToneDetector_Set() and HPI_ToneDetector_Get() +*/ + +/** Set the threshold level of a tonedetector, +Threshold is a -ve number in units of dB/100, +*/ +#define HPI_TONEDETECTOR_THRESHOLD HPI_CTL_ATTR(TONEDETECTOR, 1) + +/** Get the current state of tonedetection +The result is a bitmap of detected tones. pairs of bits represent the left +and right channels, with left channel in LSB. +The lowest frequency detector state is in the LSB +*/ +#define HPI_TONEDETECTOR_STATE HPI_CTL_ATTR(TONEDETECTOR, 2) + +/** Get the frequency of a tonedetector band. +*/ +#define HPI_TONEDETECTOR_FREQUENCY HPI_CTL_ATTR(TONEDETECTOR, 3) + +/**\}*/ + +/** \defgroup silencedet_attr SilenceDetector attributes +\{ +*/ + +/** Get the current state of tonedetection +The result is a bitmap with 1s for silent channels. Left channel is in LSB +*/ +#define HPI_SILENCEDETECTOR_STATE \ + HPI_CTL_ATTR(SILENCEDETECTOR, 2) + +/** Set the threshold level of a SilenceDetector, +Threshold is a -ve number in units of dB/100, +*/ +#define HPI_SILENCEDETECTOR_THRESHOLD \ + HPI_CTL_ATTR(SILENCEDETECTOR, 1) + +/** get/set the silence time before the detector triggers +*/ +#define HPI_SILENCEDETECTOR_DELAY \ + HPI_CTL_ATTR(SILENCEDETECTOR, 3) + +/**\}*/ + +/* Locked memory buffer alloc/free phases */ +/** use one message to allocate or free physical memory */ +#define HPI_BUFFER_CMD_EXTERNAL 0 +/** alloc physical memory */ +#define HPI_BUFFER_CMD_INTERNAL_ALLOC 1 +/** send physical memory address to adapter */ +#define HPI_BUFFER_CMD_INTERNAL_GRANTADAPTER 2 +/** notify adapter to stop using physical buffer */ +#define HPI_BUFFER_CMD_INTERNAL_REVOKEADAPTER 3 +/** free physical buffer */ +#define HPI_BUFFER_CMD_INTERNAL_FREE 4 + +/******************************************* CONTROLX ATTRIBUTES ****/ +/* NOTE: All controlx attributes must be unique, unlike control attributes */ + +/*****************************************************************************/ +/*****************************************************************************/ +/******** HPI LOW LEVEL MESSAGES *******/ +/*****************************************************************************/ +/*****************************************************************************/ +/** Pnp ids */ +/** "ASI" - actual is "ASX" - need to change */ +#define HPI_ID_ISAPNP_AUDIOSCIENCE 0x0669 +/** PCI vendor ID that AudioScience uses */ +#define HPI_PCI_VENDOR_ID_AUDIOSCIENCE 0x175C +/** PCI vendor ID that the DSP56301 has */ +#define HPI_PCI_VENDOR_ID_MOTOROLA 0x1057 +/** PCI vendor ID that TI uses */ +#define HPI_PCI_VENDOR_ID_TI 0x104C + +#define HPI_PCI_DEV_ID_PCI2040 0xAC60 +/** TI's C6205 PCI interface has this ID */ +#define HPI_PCI_DEV_ID_DSP6205 0xA106 + +#define HPI_USB_VENDOR_ID_AUDIOSCIENCE 0x1257 +#define HPI_USB_W2K_TAG 0x57495341 /* "ASIW" */ +#define HPI_USB_LINUX_TAG 0x4C495341 /* "ASIL" */ + +/** First 2 hex digits define the adapter family */ +#define HPI_ADAPTER_FAMILY_MASK 0xff00 + +#define HPI_ADAPTER_FAMILY_ASI(f) (f & HPI_ADAPTER_FAMILY_MASK) +#define HPI_ADAPTER_ASI(f) (f) + +/******************************************* message types */ +#define HPI_TYPE_MESSAGE 1 +#define HPI_TYPE_RESPONSE 2 +#define HPI_TYPE_DATA 3 +#define HPI_TYPE_SSX2BYPASS_MESSAGE 4 + +/******************************************* object types */ +#define HPI_OBJ_SUBSYSTEM 1 +#define HPI_OBJ_ADAPTER 2 +#define HPI_OBJ_OSTREAM 3 +#define HPI_OBJ_ISTREAM 4 +#define HPI_OBJ_MIXER 5 +#define HPI_OBJ_NODE 6 +#define HPI_OBJ_CONTROL 7 +#define HPI_OBJ_NVMEMORY 8 +#define HPI_OBJ_GPIO 9 +#define HPI_OBJ_WATCHDOG 10 +#define HPI_OBJ_CLOCK 11 +#define HPI_OBJ_PROFILE 12 +#define HPI_OBJ_CONTROLEX 13 +#define HPI_OBJ_ASYNCEVENT 14 + +#define HPI_OBJ_MAXINDEX 14 + +/******************************************* methods/functions */ + +#define HPI_OBJ_FUNCTION_SPACING 0x100 +#define HPI_MAKE_INDEX(obj, index) (obj * HPI_OBJ_FUNCTION_SPACING + index) +#define HPI_EXTRACT_INDEX(fn) (fn & 0xff) + +/* SUB-SYSTEM */ +#define HPI_SUBSYS_OPEN HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 1) +#define HPI_SUBSYS_GET_VERSION HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 2) +#define HPI_SUBSYS_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 3) +#define HPI_SUBSYS_FIND_ADAPTERS HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 4) +#define HPI_SUBSYS_CREATE_ADAPTER HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 5) +#define HPI_SUBSYS_CLOSE HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 6) +#define HPI_SUBSYS_DELETE_ADAPTER HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 7) +#define HPI_SUBSYS_DRIVER_LOAD HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 8) +#define HPI_SUBSYS_DRIVER_UNLOAD HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 9) +#define HPI_SUBSYS_READ_PORT_8 HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 10) +#define HPI_SUBSYS_WRITE_PORT_8 HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 11) +#define HPI_SUBSYS_GET_NUM_ADAPTERS HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 12) +#define HPI_SUBSYS_GET_ADAPTER HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 13) +#define HPI_SUBSYS_SET_NETWORK_INTERFACE HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 14) +#define HPI_SUBSYS_FUNCTION_COUNT 14 +/* ADAPTER */ +#define HPI_ADAPTER_OPEN HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 1) +#define HPI_ADAPTER_CLOSE HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 2) +#define HPI_ADAPTER_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 3) +#define HPI_ADAPTER_GET_ASSERT HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 4) +#define HPI_ADAPTER_TEST_ASSERT HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 5) +#define HPI_ADAPTER_SET_MODE HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 6) +#define HPI_ADAPTER_GET_MODE HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 7) +#define HPI_ADAPTER_ENABLE_CAPABILITY HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 8) +#define HPI_ADAPTER_SELFTEST HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 9) +#define HPI_ADAPTER_FIND_OBJECT HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 10) +#define HPI_ADAPTER_QUERY_FLASH HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 11) +#define HPI_ADAPTER_START_FLASH HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 12) +#define HPI_ADAPTER_PROGRAM_FLASH HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 13) +#define HPI_ADAPTER_SET_PROPERTY HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 14) +#define HPI_ADAPTER_GET_PROPERTY HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 15) +#define HPI_ADAPTER_ENUM_PROPERTY HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 16) +#define HPI_ADAPTER_MODULE_INFO HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 17) +#define HPI_ADAPTER_DEBUG_READ HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 18) +#define HPI_ADAPTER_FUNCTION_COUNT 18 +/* OUTPUT STREAM */ +#define HPI_OSTREAM_OPEN HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 1) +#define HPI_OSTREAM_CLOSE HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 2) +#define HPI_OSTREAM_WRITE HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 3) +#define HPI_OSTREAM_START HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 4) +#define HPI_OSTREAM_STOP HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 5) +#define HPI_OSTREAM_RESET HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 6) +#define HPI_OSTREAM_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 7) +#define HPI_OSTREAM_QUERY_FORMAT HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 8) +#define HPI_OSTREAM_DATA HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 9) +#define HPI_OSTREAM_SET_VELOCITY HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 10) +#define HPI_OSTREAM_SET_PUNCHINOUT HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 11) +#define HPI_OSTREAM_SINEGEN HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 12) +#define HPI_OSTREAM_ANC_RESET HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 13) +#define HPI_OSTREAM_ANC_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 14) +#define HPI_OSTREAM_ANC_READ HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 15) +#define HPI_OSTREAM_SET_TIMESCALE HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 16) +#define HPI_OSTREAM_SET_FORMAT HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 17) +#define HPI_OSTREAM_HOSTBUFFER_ALLOC HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 18) +#define HPI_OSTREAM_HOSTBUFFER_FREE HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 19) +#define HPI_OSTREAM_GROUP_ADD HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 20) +#define HPI_OSTREAM_GROUP_GETMAP HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 21) +#define HPI_OSTREAM_GROUP_RESET HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 22) +#define HPI_OSTREAM_HOSTBUFFER_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 23) +#define HPI_OSTREAM_WAIT_START HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 24) +#define HPI_OSTREAM_FUNCTION_COUNT 24 +/* INPUT STREAM */ +#define HPI_ISTREAM_OPEN HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 1) +#define HPI_ISTREAM_CLOSE HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 2) +#define HPI_ISTREAM_SET_FORMAT HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 3) +#define HPI_ISTREAM_READ HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 4) +#define HPI_ISTREAM_START HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 5) +#define HPI_ISTREAM_STOP HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 6) +#define HPI_ISTREAM_RESET HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 7) +#define HPI_ISTREAM_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 8) +#define HPI_ISTREAM_QUERY_FORMAT HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 9) +#define HPI_ISTREAM_ANC_RESET HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 10) +#define HPI_ISTREAM_ANC_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 11) +#define HPI_ISTREAM_ANC_WRITE HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 12) +#define HPI_ISTREAM_HOSTBUFFER_ALLOC HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 13) +#define HPI_ISTREAM_HOSTBUFFER_FREE HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 14) +#define HPI_ISTREAM_GROUP_ADD HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 15) +#define HPI_ISTREAM_GROUP_GETMAP HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 16) +#define HPI_ISTREAM_GROUP_RESET HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 17) +#define HPI_ISTREAM_HOSTBUFFER_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 18) +#define HPI_ISTREAM_WAIT_START HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 19) +#define HPI_ISTREAM_FUNCTION_COUNT 19 +/* MIXER */ +/* NOTE: + GET_NODE_INFO, SET_CONNECTION, GET_CONNECTIONS are not currently used */ +#define HPI_MIXER_OPEN HPI_MAKE_INDEX(HPI_OBJ_MIXER, 1) +#define HPI_MIXER_CLOSE HPI_MAKE_INDEX(HPI_OBJ_MIXER, 2) +#define HPI_MIXER_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_MIXER, 3) +#define HPI_MIXER_GET_NODE_INFO HPI_MAKE_INDEX(HPI_OBJ_MIXER, 4) +#define HPI_MIXER_GET_CONTROL HPI_MAKE_INDEX(HPI_OBJ_MIXER, 5) +#define HPI_MIXER_SET_CONNECTION HPI_MAKE_INDEX(HPI_OBJ_MIXER, 6) +#define HPI_MIXER_GET_CONNECTIONS HPI_MAKE_INDEX(HPI_OBJ_MIXER, 7) +#define HPI_MIXER_GET_CONTROL_BY_INDEX HPI_MAKE_INDEX(HPI_OBJ_MIXER, 8) +#define HPI_MIXER_GET_CONTROL_ARRAY_BY_INDEX HPI_MAKE_INDEX(HPI_OBJ_MIXER, 9) +#define HPI_MIXER_GET_CONTROL_MULTIPLE_VALUES HPI_MAKE_INDEX(HPI_OBJ_MIXER, 10) +#define HPI_MIXER_STORE HPI_MAKE_INDEX(HPI_OBJ_MIXER, 11) +#define HPI_MIXER_FUNCTION_COUNT 11 +/* MIXER CONTROLS */ +#define HPI_CONTROL_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_CONTROL, 1) +#define HPI_CONTROL_GET_STATE HPI_MAKE_INDEX(HPI_OBJ_CONTROL, 2) +#define HPI_CONTROL_SET_STATE HPI_MAKE_INDEX(HPI_OBJ_CONTROL, 3) +#define HPI_CONTROL_FUNCTION_COUNT 3 +/* NONVOL MEMORY */ +#define HPI_NVMEMORY_OPEN HPI_MAKE_INDEX(HPI_OBJ_NVMEMORY, 1) +#define HPI_NVMEMORY_READ_BYTE HPI_MAKE_INDEX(HPI_OBJ_NVMEMORY, 2) +#define HPI_NVMEMORY_WRITE_BYTE HPI_MAKE_INDEX(HPI_OBJ_NVMEMORY, 3) +#define HPI_NVMEMORY_FUNCTION_COUNT 3 +/* GPIO */ +#define HPI_GPIO_OPEN HPI_MAKE_INDEX(HPI_OBJ_GPIO, 1) +#define HPI_GPIO_READ_BIT HPI_MAKE_INDEX(HPI_OBJ_GPIO, 2) +#define HPI_GPIO_WRITE_BIT HPI_MAKE_INDEX(HPI_OBJ_GPIO, 3) +#define HPI_GPIO_READ_ALL HPI_MAKE_INDEX(HPI_OBJ_GPIO, 4) +#define HPI_GPIO_WRITE_STATUS HPI_MAKE_INDEX(HPI_OBJ_GPIO, 5) +#define HPI_GPIO_FUNCTION_COUNT 5 +/* ASYNC EVENT */ +#define HPI_ASYNCEVENT_OPEN HPI_MAKE_INDEX(HPI_OBJ_ASYNCEVENT, 1) +#define HPI_ASYNCEVENT_CLOSE HPI_MAKE_INDEX(HPI_OBJ_ASYNCEVENT, 2) +#define HPI_ASYNCEVENT_WAIT HPI_MAKE_INDEX(HPI_OBJ_ASYNCEVENT, 3) +#define HPI_ASYNCEVENT_GETCOUNT HPI_MAKE_INDEX(HPI_OBJ_ASYNCEVENT, 4) +#define HPI_ASYNCEVENT_GET HPI_MAKE_INDEX(HPI_OBJ_ASYNCEVENT, 5) +#define HPI_ASYNCEVENT_SENDEVENTS HPI_MAKE_INDEX(HPI_OBJ_ASYNCEVENT, 6) +#define HPI_ASYNCEVENT_FUNCTION_COUNT 6 +/* WATCH-DOG */ +#define HPI_WATCHDOG_OPEN HPI_MAKE_INDEX(HPI_OBJ_WATCHDOG, 1) +#define HPI_WATCHDOG_SET_TIME HPI_MAKE_INDEX(HPI_OBJ_WATCHDOG, 2) +#define HPI_WATCHDOG_PING HPI_MAKE_INDEX(HPI_OBJ_WATCHDOG, 3) +/* CLOCK */ +#define HPI_CLOCK_OPEN HPI_MAKE_INDEX(HPI_OBJ_CLOCK, 1) +#define HPI_CLOCK_SET_TIME HPI_MAKE_INDEX(HPI_OBJ_CLOCK, 2) +#define HPI_CLOCK_GET_TIME HPI_MAKE_INDEX(HPI_OBJ_CLOCK, 3) +/* PROFILE */ +#define HPI_PROFILE_OPEN_ALL HPI_MAKE_INDEX(HPI_OBJ_PROFILE, 1) +#define HPI_PROFILE_START_ALL HPI_MAKE_INDEX(HPI_OBJ_PROFILE, 2) +#define HPI_PROFILE_STOP_ALL HPI_MAKE_INDEX(HPI_OBJ_PROFILE, 3) +#define HPI_PROFILE_GET HPI_MAKE_INDEX(HPI_OBJ_PROFILE, 4) +#define HPI_PROFILE_GET_IDLECOUNT HPI_MAKE_INDEX(HPI_OBJ_PROFILE, 5) +#define HPI_PROFILE_GET_NAME HPI_MAKE_INDEX(HPI_OBJ_PROFILE, 6) +#define HPI_PROFILE_GET_UTILIZATION HPI_MAKE_INDEX(HPI_OBJ_PROFILE, 7) +#define HPI_PROFILE_FUNCTION_COUNT 7 +/* ////////////////////////////////////////////////////////////////////// */ +/* PRIVATE ATTRIBUTES */ + +/* ////////////////////////////////////////////////////////////////////// */ +/* STRUCTURES */ +#ifndef DISABLE_PRAGMA_PACK1 +#pragma pack(push, 1) +#endif + +/** PCI bus resource */ +struct hpi_pci { + u32 __iomem *ap_mem_base[HPI_MAX_ADAPTER_MEM_SPACES]; + struct pci_dev *p_os_data; + +#ifndef HPI64BIT /* keep structure size constant */ + u32 padding[HPI_MAX_ADAPTER_MEM_SPACES + 1]; +#endif + u16 vendor_id; + u16 device_id; + u16 subsys_vendor_id; + u16 subsys_device_id; + u16 bus_number; + u16 device_number; + u32 interrupt; +}; + +struct hpi_resource { + union { + const struct hpi_pci *pci; + const char *net_if; + } r; +#ifndef HPI64BIT /* keep structure size constant */ + u32 pad_to64; +#endif + u16 bus_type; /* HPI_BUS_PNPISA, _PCI, _USB etc */ + u16 padding; + +}; + +/** Format info used inside struct hpi_message + Not the same as public API struct hpi_format */ +struct hpi_msg_format { + u32 sample_rate; + /**< 11025, 32000, 44100 ... */ + u32 bit_rate; /**< for MPEG */ + u32 attributes; + /**< Stereo/JointStereo/Mono */ + u16 channels; /**< 1,2..., (or ancillary mode or idle bit */ + u16 format; /**< HPI_FORMAT_PCM16, _MPEG etc. see \ref HPI_FORMATS. */ +}; + +/** Buffer+format structure. + Must be kept 7 * 32 bits to match public struct hpi_datastruct */ +struct hpi_msg_data { + struct hpi_msg_format format; + u8 *pb_data; +#ifndef HPI64BIT + u32 padding; +#endif + u32 data_size; +}; + +/** struct hpi_datastructure used up to 3.04 driver */ +struct hpi_data_legacy32 { + struct hpi_format format; + u32 pb_data; + u32 data_size; +}; + +#ifdef HPI64BIT +/* Compatibility version of struct hpi_data*/ +struct hpi_data_compat32 { + struct hpi_msg_format format; + u32 pb_data; + u32 padding; + u32 data_size; +}; +#endif + +struct hpi_buffer { + /** placehoder for backward compatability (see dwBufferSize) */ + struct hpi_msg_format reserved; + u32 command; /**< HPI_BUFFER_CMD_xxx*/ + u32 pci_address; /**< PCI physical address of buffer for DSP DMA */ + u32 buffer_size; /**< must line up with data_size of HPI_DATA*/ +}; + +/*/////////////////////////////////////////////////////////////////////////// */ +/* This is used for background buffer bus mastering stream buffers. */ +struct hpi_hostbuffer_status { + u32 samples_processed; + u32 auxiliary_data_available; + u32 stream_state; + /* DSP index in to the host bus master buffer. */ + u32 dSP_index; + /* Host index in to the host bus master buffer. */ + u32 host_index; + u32 size_in_bytes; +}; + +struct hpi_streamid { + u16 object_type; + /**< Type of object, HPI_OBJ_OSTREAM or HPI_OBJ_ISTREAM. */ + u16 stream_index; /**< outstream or instream index. */ +}; + +struct hpi_punchinout { + u32 punch_in_sample; + u32 punch_out_sample; +}; + +struct hpi_subsys_msg { + struct hpi_resource resource; +}; + +struct hpi_subsys_res { + u32 version; + u32 data; /* used to return extended version */ + u16 num_adapters; /* number of adapters */ + u16 adapter_index; + u16 aw_adapter_list[HPI_MAX_ADAPTERS]; +}; + +struct hpi_adapter_msg { + u32 adapter_mode; /* adapter mode */ + u16 assert_id; /* assert number for "test assert" call + object_index for find object call + query_or_set for hpi_adapter_set_mode_ex() */ + u16 object_type; /* for adapter find object call */ +}; + +union hpi_adapterx_msg { + struct hpi_adapter_msg adapter; + struct { + u32 offset; + } query_flash; + struct { + u32 offset; + u32 length; + u32 key; + } start_flash; + struct { + u32 checksum; + u16 sequence; + u16 length; + u16 offset; /**< offset from start of msg to data */ + u16 unused; + } program_flash; + struct { + u16 property; + u16 parameter1; + u16 parameter2; + } property_set; + struct { + u16 index; + u16 what; + u16 property_index; + } property_enum; + struct { + u16 index; + } module_info; + struct { + u32 dsp_address; + u32 count_bytes; + } debug_read; +}; + +struct hpi_adapter_res { + u32 serial_number; + u16 adapter_type; + u16 adapter_index; /* is this needed? also used for dsp_index */ + u16 num_instreams; + u16 num_outstreams; + u16 num_mixers; + u16 version; + u8 sz_adapter_assert[HPI_STRING_LEN]; +}; + +union hpi_adapterx_res { + struct hpi_adapter_res adapter; + struct { + u32 checksum; + u32 length; + u32 version; + } query_flash; + struct { + u16 sequence; + } program_flash; + struct { + u16 parameter1; + u16 parameter2; + } property_get; +}; + +struct hpi_stream_msg { + union { + struct hpi_msg_data data; + struct hpi_data_legacy32 data32; + u16 velocity; + struct hpi_punchinout pio; + u32 time_scale; + struct hpi_buffer buffer; + struct hpi_streamid stream; + } u; +}; + +struct hpi_stream_res { + union { + struct { + /* size of hardware buffer */ + u32 buffer_size; + /* OutStream - data to play, + InStream - data recorded */ + u32 data_available; + /* OutStream - samples played, + InStream - samples recorded */ + u32 samples_transferred; + /* Adapter - OutStream - data to play, + InStream - data recorded */ + u32 auxiliary_data_available; + u16 state; /* HPI_STATE_PLAYING, _STATE_STOPPED */ + u16 padding; + } stream_info; + struct { + u32 buffer_size; + u32 data_available; + u32 samples_transfered; + u16 state; + u16 outstream_index; + u16 instream_index; + u16 padding; + u32 auxiliary_data_available; + } legacy_stream_info; + struct { + /* bitmap of grouped OutStreams */ + u32 outstream_group_map; + /* bitmap of grouped InStreams */ + u32 instream_group_map; + } group_info; + struct { + /* pointer to the buffer */ + u8 *p_buffer; + /* pointer to the hostbuffer status */ + struct hpi_hostbuffer_status *p_status; + } hostbuffer_info; + } u; +}; + +struct hpi_mixer_msg { + u16 control_index; + u16 control_type; /* = HPI_CONTROL_METER _VOLUME etc */ + u16 padding1; /* maintain alignment of subsequent fields */ + u16 node_type1; /* = HPI_SOURCENODE_LINEIN etc */ + u16 node_index1; /* = 0..N */ + u16 node_type2; + u16 node_index2; + u16 padding2; /* round to 4 bytes */ +}; + +struct hpi_mixer_res { + u16 src_node_type; /* = HPI_SOURCENODE_LINEIN etc */ + u16 src_node_index; /* = 0..N */ + u16 dst_node_type; + u16 dst_node_index; + /* Also controlType for MixerGetControlByIndex */ + u16 control_index; + /* may indicate which DSP the control is located on */ + u16 dsp_index; +}; + +union hpi_mixerx_msg { + struct { + u16 starting_index; + u16 flags; + u32 length_in_bytes; /* length in bytes of p_data */ + u32 p_data; /* pointer to a data array */ + } gcabi; + struct { + u16 command; + u16 index; + } store; /* for HPI_MIXER_STORE message */ +}; + +union hpi_mixerx_res { + struct { + u32 bytes_returned; /* size of items returned */ + u32 p_data; /* pointer to data array */ + u16 more_to_do; /* indicates if there is more to do */ + } gcabi; +}; + +struct hpi_control_msg { + u16 attribute; /* control attribute or property */ + u16 saved_index; + u32 param1; /* generic parameter 1 */ + u32 param2; /* generic parameter 2 */ + short an_log_value[HPI_MAX_CHANNELS]; +}; + +struct hpi_control_union_msg { + u16 attribute; /* control attribute or property */ + u16 saved_index; /* only used in ctrl save/restore */ + union { + struct { + u32 param1; /* generic parameter 1 */ + u32 param2; /* generic parameter 2 */ + short an_log_value[HPI_MAX_CHANNELS]; + } old; + union { + u32 frequency; + u32 gain; + u32 band; + u32 deemphasis; + u32 program; + struct { + u32 mode; + u32 value; + } mode; + } tuner; + } u; +}; + +struct hpi_control_res { + /* Could make union. dwParam, anLogValue never used in same response */ + u32 param1; + u32 param2; + short an_log_value[HPI_MAX_CHANNELS]; +}; + +union hpi_control_union_res { + struct { + u32 param1; + u32 param2; + short an_log_value[HPI_MAX_CHANNELS]; + } old; + union { + u32 band; + u32 frequency; + u32 gain; + u32 level; + u32 deemphasis; + struct { + u32 data[2]; + u32 bLER; + } rds; + } tuner; + struct { + char sz_data[8]; + u32 remaining_chars; + } chars8; + char c_data12[12]; +}; + +/* HPI_CONTROLX_STRUCTURES */ + +/* Message */ + +/** Used for all HMI variables where max length <= 8 bytes +*/ +struct hpi_controlx_msg_cobranet_data { + u32 hmi_address; + u32 byte_count; + u32 data[2]; +}; + +/** Used for string data, and for packet bridge +*/ +struct hpi_controlx_msg_cobranet_bigdata { + u32 hmi_address; + u32 byte_count; + u8 *pb_data; +#ifndef HPI64BIT + u32 padding; +#endif +}; + +/** Used for PADS control reading of string fields. +*/ +struct hpi_controlx_msg_pad_data { + u32 field; + u32 byte_count; + u8 *pb_data; +#ifndef HPI64BIT + u32 padding; +#endif +}; + +/** Used for generic data +*/ + +struct hpi_controlx_msg_generic { + u32 param1; + u32 param2; +}; + +struct hpi_controlx_msg { + u16 attribute; /* control attribute or property */ + u16 saved_index; + union { + struct hpi_controlx_msg_cobranet_data cobranet_data; + struct hpi_controlx_msg_cobranet_bigdata cobranet_bigdata; + struct hpi_controlx_msg_generic generic; + struct hpi_controlx_msg_pad_data pad_data; + /*struct param_value universal_value; */ + /* nothing extra to send for status read */ + } u; +}; + +/* Response */ +/** +*/ +struct hpi_controlx_res_cobranet_data { + u32 byte_count; + u32 data[2]; +}; + +struct hpi_controlx_res_cobranet_bigdata { + u32 byte_count; +}; + +struct hpi_controlx_res_cobranet_status { + u32 status; + u32 readable_size; + u32 writeable_size; +}; + +struct hpi_controlx_res_generic { + u32 param1; + u32 param2; +}; + +struct hpi_controlx_res { + union { + struct hpi_controlx_res_cobranet_bigdata cobranet_bigdata; + struct hpi_controlx_res_cobranet_data cobranet_data; + struct hpi_controlx_res_cobranet_status cobranet_status; + struct hpi_controlx_res_generic generic; + /*struct param_info universal_info; */ + /*struct param_value universal_value; */ + } u; +}; + +struct hpi_nvmemory_msg { + u16 address; + u16 data; +}; + +struct hpi_nvmemory_res { + u16 size_in_bytes; + u16 data; +}; + +struct hpi_gpio_msg { + u16 bit_index; + u16 bit_data; +}; + +struct hpi_gpio_res { + u16 number_input_bits; + u16 number_output_bits; + u16 bit_data[4]; +}; + +struct hpi_async_msg { + u32 events; + u16 maximum_events; + u16 padding; +}; + +struct hpi_async_res { + union { + struct { + u16 count; + } count; + struct { + u32 events; + u16 number_returned; + u16 padding; + } get; + struct hpi_async_event event; + } u; +}; + +struct hpi_watchdog_msg { + u32 time_ms; +}; + +struct hpi_watchdog_res { + u32 time_ms; +}; + +struct hpi_clock_msg { + u16 hours; + u16 minutes; + u16 seconds; + u16 milli_seconds; +}; + +struct hpi_clock_res { + u16 size_in_bytes; + u16 hours; + u16 minutes; + u16 seconds; + u16 milli_seconds; + u16 padding; +}; + +struct hpi_profile_msg { + u16 bin_index; + u16 padding; +}; + +struct hpi_profile_res_open { + u16 max_profiles; +}; + +struct hpi_profile_res_time { + u32 micro_seconds; + u32 call_count; + u32 max_micro_seconds; + u32 min_micro_seconds; + u16 seconds; +}; + +struct hpi_profile_res_name { + u8 sz_name[32]; +}; + +struct hpi_profile_res { + union { + struct hpi_profile_res_open o; + struct hpi_profile_res_time t; + struct hpi_profile_res_name n; + } u; +}; + +struct hpi_message_header { + u16 size; /* total size in bytes */ + u8 type; /* HPI_TYPE_MESSAGE */ + u8 version; /* message version */ + u16 object; /* HPI_OBJ_* */ + u16 function; /* HPI_SUBSYS_xxx, HPI_ADAPTER_xxx */ + u16 adapter_index; /* the adapter index */ + u16 obj_index; /* */ +}; + +struct hpi_message { + /* following fields must match HPI_MESSAGE_HEADER */ + u16 size; /* total size in bytes */ + u8 type; /* HPI_TYPE_MESSAGE */ + u8 version; /* message version */ + u16 object; /* HPI_OBJ_* */ + u16 function; /* HPI_SUBSYS_xxx, HPI_ADAPTER_xxx */ + u16 adapter_index; /* the adapter index */ + u16 obj_index; /* */ + union { + struct hpi_subsys_msg s; + struct hpi_adapter_msg a; + union hpi_adapterx_msg ax; + struct hpi_stream_msg d; + struct hpi_mixer_msg m; + union hpi_mixerx_msg mx; /* extended mixer; */ + struct hpi_control_msg c; /* mixer control; */ + /* identical to struct hpi_control_msg, + but field naming is improved */ + struct hpi_control_union_msg cu; + struct hpi_controlx_msg cx; /* extended mixer control; */ + struct hpi_nvmemory_msg n; + struct hpi_gpio_msg l; /* digital i/o */ + struct hpi_watchdog_msg w; + struct hpi_clock_msg t; /* dsp time */ + struct hpi_profile_msg p; + struct hpi_async_msg as; + char fixed_size[32]; + } u; +}; + +#define HPI_MESSAGE_SIZE_BY_OBJECT { \ + sizeof(struct hpi_message_header) , /* default, no object type 0 */ \ + sizeof(struct hpi_message_header) + sizeof(struct hpi_subsys_msg),\ + sizeof(struct hpi_message_header) + sizeof(union hpi_adapterx_msg),\ + sizeof(struct hpi_message_header) + sizeof(struct hpi_stream_msg),\ + sizeof(struct hpi_message_header) + sizeof(struct hpi_stream_msg),\ + sizeof(struct hpi_message_header) + sizeof(struct hpi_mixer_msg),\ + sizeof(struct hpi_message_header) , /* no node message */ \ + sizeof(struct hpi_message_header) + sizeof(struct hpi_control_msg),\ + sizeof(struct hpi_message_header) + sizeof(struct hpi_nvmemory_msg),\ + sizeof(struct hpi_message_header) + sizeof(struct hpi_gpio_msg),\ + sizeof(struct hpi_message_header) + sizeof(struct hpi_watchdog_msg),\ + sizeof(struct hpi_message_header) + sizeof(struct hpi_clock_msg),\ + sizeof(struct hpi_message_header) + sizeof(struct hpi_profile_msg),\ + sizeof(struct hpi_message_header) + sizeof(struct hpi_controlx_msg),\ + sizeof(struct hpi_message_header) + sizeof(struct hpi_async_msg) \ +} + +struct hpi_response_header { + u16 size; + u8 type; /* HPI_TYPE_RESPONSE */ + u8 version; /* response version */ + u16 object; /* HPI_OBJ_* */ + u16 function; /* HPI_SUBSYS_xxx, HPI_ADAPTER_xxx */ + u16 error; /* HPI_ERROR_xxx */ + u16 specific_error; /* adapter specific error */ +}; + +struct hpi_response { +/* following fields must match HPI_RESPONSE_HEADER */ + u16 size; + u8 type; /* HPI_TYPE_RESPONSE */ + u8 version; /* response version */ + u16 object; /* HPI_OBJ_* */ + u16 function; /* HPI_SUBSYS_xxx, HPI_ADAPTER_xxx */ + u16 error; /* HPI_ERROR_xxx */ + u16 specific_error; /* adapter specific error */ + union { + struct hpi_subsys_res s; + struct hpi_adapter_res a; + union hpi_adapterx_res ax; + struct hpi_stream_res d; + struct hpi_mixer_res m; + union hpi_mixerx_res mx; /* extended mixer; */ + struct hpi_control_res c; /* mixer control; */ + /* identical to hpi_control_res, but field naming is improved */ + union hpi_control_union_res cu; + struct hpi_controlx_res cx; /* extended mixer control; */ + struct hpi_nvmemory_res n; + struct hpi_gpio_res l; /* digital i/o */ + struct hpi_watchdog_res w; + struct hpi_clock_res t; /* dsp time */ + struct hpi_profile_res p; + struct hpi_async_res as; + u8 bytes[52]; + } u; +}; + +#define HPI_RESPONSE_SIZE_BY_OBJECT { \ + sizeof(struct hpi_response_header) ,/* default, no object type 0 */ \ + sizeof(struct hpi_response_header) + sizeof(struct hpi_subsys_res),\ + sizeof(struct hpi_response_header) + sizeof(union hpi_adapterx_res),\ + sizeof(struct hpi_response_header) + sizeof(struct hpi_stream_res),\ + sizeof(struct hpi_response_header) + sizeof(struct hpi_stream_res),\ + sizeof(struct hpi_response_header) + sizeof(struct hpi_mixer_res),\ + sizeof(struct hpi_response_header) , /* no node response */ \ + sizeof(struct hpi_response_header) + sizeof(struct hpi_control_res),\ + sizeof(struct hpi_response_header) + sizeof(struct hpi_nvmemory_res),\ + sizeof(struct hpi_response_header) + sizeof(struct hpi_gpio_res),\ + sizeof(struct hpi_response_header) + sizeof(struct hpi_watchdog_res),\ + sizeof(struct hpi_response_header) + sizeof(struct hpi_clock_res),\ + sizeof(struct hpi_response_header) + sizeof(struct hpi_profile_res),\ + sizeof(struct hpi_response_header) + sizeof(struct hpi_controlx_res),\ + sizeof(struct hpi_response_header) + sizeof(struct hpi_async_res) \ +} + +/*********************** version 1 message/response *****************************/ +#define HPINET_ETHERNET_DATA_SIZE (1500) +#define HPINET_IP_HDR_SIZE (20) +#define HPINET_IP_DATA_SIZE (HPINET_ETHERNET_DATA_SIZE - HPINET_IP_HDR_SIZE) +#define HPINET_UDP_HDR_SIZE (8) +#define HPINET_UDP_DATA_SIZE (HPINET_IP_DATA_SIZE - HPINET_UDP_HDR_SIZE) +#define HPINET_ASI_HDR_SIZE (2) +#define HPINET_ASI_DATA_SIZE (HPINET_UDP_DATA_SIZE - HPINET_ASI_HDR_SIZE) + +#define HPI_MAX_PAYLOAD_SIZE (HPINET_ASI_DATA_SIZE - 2) + +/* New style message/response, but still V0 compatible */ +struct hpi_msg_adapter_get_info { + struct hpi_message_header h; +}; + +struct hpi_res_adapter_get_info { + struct hpi_response_header h; /*v0 */ + struct hpi_adapter_res p; +}; + +/* padding is so these are same size as v0 hpi_message */ +struct hpi_msg_adapter_query_flash { + struct hpi_message_header h; + u32 offset; + u8 pad_to_version0_size[sizeof(struct hpi_message) - /* V0 res */ + sizeof(struct hpi_message_header) - 1 * sizeof(u32)]; +}; + +/* padding is so these are same size as v0 hpi_response */ +struct hpi_res_adapter_query_flash { + struct hpi_response_header h; + u32 checksum; + u32 length; + u32 version; + u8 pad_to_version0_size[sizeof(struct hpi_response) - /* V0 res */ + sizeof(struct hpi_response_header) - 3 * sizeof(u32)]; +}; + +struct hpi_msg_adapter_start_flash { + struct hpi_message_header h; + u32 offset; + u32 length; + u32 key; + u8 pad_to_version0_size[sizeof(struct hpi_message) - /* V0 res */ + sizeof(struct hpi_message_header) - 3 * sizeof(u32)]; +}; + +struct hpi_res_adapter_start_flash { + struct hpi_response_header h; + u8 pad_to_version0_size[sizeof(struct hpi_response) - /* V0 res */ + sizeof(struct hpi_response_header)]; +}; + +struct hpi_msg_adapter_program_flash_payload { + u32 checksum; + u16 sequence; + u16 length; + u16 offset; /**< offset from start of msg to data */ + u16 unused; + /* ensure sizeof(header + payload) == sizeof(hpi_message_V0) + because old firmware expects data after message of this size */ + u8 pad_to_version0_size[sizeof(struct hpi_message) - /* V0 message */ + sizeof(struct hpi_message_header) - sizeof(u32) - + 4 * sizeof(u16)]; +}; + +struct hpi_msg_adapter_program_flash { + struct hpi_message_header h; + struct hpi_msg_adapter_program_flash_payload p; + u32 data[256]; +}; + +struct hpi_res_adapter_program_flash { + struct hpi_response_header h; + u16 sequence; + u8 pad_to_version0_size[sizeof(struct hpi_response) - /* V0 res */ + sizeof(struct hpi_response_header) - sizeof(u16)]; +}; + +#if 1 +#define hpi_message_header_v1 hpi_message_header +#define hpi_response_header_v1 hpi_response_header +#else +/* V1 headers in Addition to v0 headers */ +struct hpi_message_header_v1 { + struct hpi_message_header h0; +/* struct { +} h1; */ +}; + +struct hpi_response_header_v1 { + struct hpi_response_header h0; + struct { + u16 adapter_index; /* the adapter index */ + u16 obj_index; /* object index */ + } h1; +}; +#endif + +/* STRV HPI Packet */ +struct hpi_msg_strv { + struct hpi_message_header h; + struct hpi_entity strv; +}; + +struct hpi_res_strv { + struct hpi_response_header h; + struct hpi_entity strv; +}; +#define MIN_STRV_PACKET_SIZE sizeof(struct hpi_res_strv) + +struct hpi_msg_payload_v0 { + struct hpi_message_header h; + union { + struct hpi_subsys_msg s; + struct hpi_adapter_msg a; + union hpi_adapterx_msg ax; + struct hpi_stream_msg d; + struct hpi_mixer_msg m; + union hpi_mixerx_msg mx; + struct hpi_control_msg c; + struct hpi_control_union_msg cu; + struct hpi_controlx_msg cx; + struct hpi_nvmemory_msg n; + struct hpi_gpio_msg l; + struct hpi_watchdog_msg w; + struct hpi_clock_msg t; + struct hpi_profile_msg p; + struct hpi_async_msg as; + } u; +}; + +struct hpi_res_payload_v0 { + struct hpi_response_header h; + union { + struct hpi_subsys_res s; + struct hpi_adapter_res a; + union hpi_adapterx_res ax; + struct hpi_stream_res d; + struct hpi_mixer_res m; + union hpi_mixerx_res mx; + struct hpi_control_res c; + union hpi_control_union_res cu; + struct hpi_controlx_res cx; + struct hpi_nvmemory_res n; + struct hpi_gpio_res l; + struct hpi_watchdog_res w; + struct hpi_clock_res t; + struct hpi_profile_res p; + struct hpi_async_res as; + } u; +}; + +union hpi_message_buffer_v1 { + struct hpi_message m0; /* version 0 */ + struct hpi_message_header_v1 h; + unsigned char buf[HPI_MAX_PAYLOAD_SIZE]; +}; + +union hpi_response_buffer_v1 { + struct hpi_response r0; /* version 0 */ + struct hpi_response_header_v1 h; + unsigned char buf[HPI_MAX_PAYLOAD_SIZE]; +}; + +compile_time_assert((sizeof(union hpi_message_buffer_v1) <= + HPI_MAX_PAYLOAD_SIZE), message_buffer_ok); +compile_time_assert((sizeof(union hpi_response_buffer_v1) <= + HPI_MAX_PAYLOAD_SIZE), response_buffer_ok); + +/*////////////////////////////////////////////////////////////////////////// */ +/* declarations for compact control calls */ +struct hpi_control_defn { + u8 type; + u8 channels; + u8 src_node_type; + u8 src_node_index; + u8 dest_node_type; + u8 dest_node_index; +}; + +/*////////////////////////////////////////////////////////////////////////// */ +/* declarations for control caching (internal to HPI<->DSP interaction) */ + +/** A compact representation of (part of) a controls state. +Used for efficient transfer of the control state +between DSP and host or across a network +*/ +struct hpi_control_cache_info { + /** one of HPI_CONTROL_* */ + u8 control_type; + /** The total size of cached information in 32-bit words. */ + u8 size_in32bit_words; + /** The original index of the control on the DSP */ + u16 control_index; +}; + +struct hpi_control_cache_single { + struct hpi_control_cache_info i; + union { + struct { /* volume */ + u16 an_log[2]; + } v; + struct { /* peak meter */ + u16 an_log_peak[2]; + u16 an_logRMS[2]; + } p; + struct { /* channel mode */ + u16 mode; + } m; + struct { /* multiplexer */ + u16 source_node_type; + u16 source_node_index; + } x; + struct { /* level/trim */ + u16 an_log[2]; + } l; + struct { /* tuner - partial caching. + some attributes go to the DSP. */ + u32 freq_ink_hz; + u16 band; + u16 level; + } t; + struct { /* AESEBU rx status */ + u32 error_status; + u32 source; + } aes3rx; + struct { /* AESEBU tx */ + u32 format; + } aes3tx; + struct { /* tone detector */ + u16 state; + } tone; + struct { /* silence detector */ + u32 state; + u32 count; + } silence; + struct { /* sample clock */ + u16 source; + u16 source_index; + u32 sample_rate; + } clk; + struct { /* microphone control */ + u16 state; + } phantom_power; + struct { /* generic control */ + u32 dw1; + u32 dw2; + } g; + } u; +}; + +struct hpi_control_cache_pad { + struct hpi_control_cache_info i; + u32 field_valid_flags; + u8 c_channel[8]; + u8 c_artist[40]; + u8 c_title[40]; + u8 c_comment[200]; + u32 pTY; + u32 pI; + u32 traffic_supported; + u32 traffic_anouncement; +}; + +/*/////////////////////////////////////////////////////////////////////////// */ +/* declarations for 2^N sized FIFO buffer (internal to HPI<->DSP interaction) */ +struct hpi_fifo_buffer { + u32 size; + u32 dSP_index; + u32 host_index; +}; + +#ifndef DISABLE_PRAGMA_PACK1 +#pragma pack(pop) +#endif + +/* skip host side function declarations for DSP + compile and documentation extraction */ + +char hpi_handle_object(const u32 handle); + +void hpi_handle_to_indexes(const u32 handle, u16 *pw_adapter_index, + u16 *pw_object_index); + +u32 hpi_indexes_to_handle(const char c_object, const u16 adapter_index, + const u16 object_index); + +/*////////////////////////////////////////////////////////////////////////// */ + +/* main HPI entry point */ +hpi_handler_func hpi_send_recv; + +/* UDP message */ +void hpi_send_recvUDP(struct hpi_message *phm, struct hpi_response *phr, + const unsigned int timeout); + +/* used in PnP OS/driver */ +u16 hpi_subsys_create_adapter(const struct hpi_hsubsys *ph_subsys, + const struct hpi_resource *p_resource, u16 *pw_adapter_index); + +u16 hpi_subsys_delete_adapter(const struct hpi_hsubsys *ph_subsys, + u16 adapter_index); + +u16 hpi_outstream_host_buffer_get_info(const struct hpi_hsubsys *ph_subsys, + u32 h_outstream, u8 **pp_buffer, + struct hpi_hostbuffer_status **pp_status); + +u16 hpi_instream_host_buffer_get_info(const struct hpi_hsubsys *ph_subsys, + u32 h_instream, u8 **pp_buffer, + struct hpi_hostbuffer_status **pp_status); + +u16 hpi_adapter_restart(u16 adapter_index); + +/* +The following 3 functions were last declared in header files for +driver 3.10. HPI_ControlQuery() used to be the recommended way +of getting a volume range. Declared here for binary asihpi32.dll +compatibility. +*/ + +void hpi_format_to_msg(struct hpi_msg_format *pMF, + const struct hpi_format *pF); +void hpi_stream_response_to_legacy(struct hpi_stream_res *pSR); + +/*////////////////////////////////////////////////////////////////////////// */ +/* declarations for individual HPI entry points */ +hpi_handler_func HPI_1000; +hpi_handler_func HPI_6000; +hpi_handler_func HPI_6205; +hpi_handler_func HPI_COMMON; + +#endif /* _HPI_INTERNAL_H_ */ diff --git a/sound/pci/asihpi/hpicmn.c b/sound/pci/asihpi/hpicmn.c new file mode 100644 index 000000000000..565102cae4f8 --- /dev/null +++ b/sound/pci/asihpi/hpicmn.c @@ -0,0 +1,643 @@ +/****************************************************************************** + + AudioScience HPI driver + Copyright (C) 1997-2010 AudioScience Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation; + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +\file hpicmn.c + + Common functions used by hpixxxx.c modules + +(C) Copyright AudioScience Inc. 1998-2003 +*******************************************************************************/ +#define SOURCEFILE_NAME "hpicmn.c" + +#include "hpi_internal.h" +#include "hpidebug.h" +#include "hpicmn.h" + +struct hpi_adapters_list { + struct hpios_spinlock list_lock; + struct hpi_adapter_obj adapter[HPI_MAX_ADAPTERS]; + u16 gw_num_adapters; +}; + +static struct hpi_adapters_list adapters; + +/** +* Given an HPI Message that was sent out and a response that was received, +* validate that the response has the correct fields filled in, +* i.e ObjectType, Function etc +**/ +u16 hpi_validate_response(struct hpi_message *phm, struct hpi_response *phr) +{ + u16 error = 0; + + if ((phr->type != HPI_TYPE_RESPONSE) + || (phr->object != phm->object) + || (phr->function != phm->function)) + error = HPI_ERROR_INVALID_RESPONSE; + + return error; +} + +u16 hpi_add_adapter(struct hpi_adapter_obj *pao) +{ + u16 retval = 0; + /*HPI_ASSERT(pao->wAdapterType); */ + + hpios_alistlock_lock(&adapters); + + if (pao->index >= HPI_MAX_ADAPTERS) { + retval = HPI_ERROR_BAD_ADAPTER_NUMBER; + goto unlock; + } + + if (adapters.adapter[pao->index].adapter_type) { + { + retval = HPI_DUPLICATE_ADAPTER_NUMBER; + goto unlock; + } + } + adapters.adapter[pao->index] = *pao; + hpios_dsplock_init(&adapters.adapter[pao->index]); + adapters.gw_num_adapters++; + +unlock: + hpios_alistlock_un_lock(&adapters); + return retval; +} + +void hpi_delete_adapter(struct hpi_adapter_obj *pao) +{ + memset(pao, 0, sizeof(struct hpi_adapter_obj)); + + hpios_alistlock_lock(&adapters); + adapters.gw_num_adapters--; /* dec the number of adapters */ + hpios_alistlock_un_lock(&adapters); +} + +/** +* FindAdapter returns a pointer to the struct hpi_adapter_obj with +* index wAdapterIndex in an HPI_ADAPTERS_LIST structure. +* +*/ +struct hpi_adapter_obj *hpi_find_adapter(u16 adapter_index) +{ + struct hpi_adapter_obj *pao = NULL; + + if (adapter_index >= HPI_MAX_ADAPTERS) { + HPI_DEBUG_LOG(VERBOSE, "find_adapter invalid index %d ", + adapter_index); + return NULL; + } + + pao = &adapters.adapter[adapter_index]; + if (pao->adapter_type != 0) { + /* + HPI_DEBUG_LOG(VERBOSE, "Found adapter index %d\n", + wAdapterIndex); + */ + return pao; + } else { + /* + HPI_DEBUG_LOG(VERBOSE, "No adapter index %d\n", + wAdapterIndex); + */ + return NULL; + } +} + +/** +* +* wipe an HPI_ADAPTERS_LIST structure. +* +**/ +static void wipe_adapter_list(void + ) +{ + memset(&adapters, 0, sizeof(adapters)); +} + +/** +* SubSysGetAdapters fills awAdapterList in an struct hpi_response structure +* with all adapters in the given HPI_ADAPTERS_LIST. +* +*/ +static void subsys_get_adapters(struct hpi_response *phr) +{ + /* fill in the response adapter array with the position */ + /* identified by the adapter number/index of the adapters in */ + /* this HPI */ + /* i.e. if we have an A120 with it's jumper set to */ + /* Adapter Number 2 then put an Adapter type A120 in the */ + /* array in position 1 */ + /* NOTE: AdapterNumber is 1..N, Index is 0..N-1 */ + + /* input: NONE */ + /* output: wNumAdapters */ + /* awAdapter[] */ + /* */ + + short i; + struct hpi_adapter_obj *pao = NULL; + + HPI_DEBUG_LOG(VERBOSE, "subsys_get_adapters\n"); + + /* for each adapter, place it's type in the position of the array */ + /* corresponding to it's adapter number */ + for (i = 0; i < adapters.gw_num_adapters; i++) { + pao = &adapters.adapter[i]; + if (phr->u.s.aw_adapter_list[pao->index] != 0) { + phr->error = HPI_DUPLICATE_ADAPTER_NUMBER; + phr->specific_error = pao->index; + return; + } + phr->u.s.aw_adapter_list[pao->index] = pao->adapter_type; + } + + phr->u.s.num_adapters = adapters.gw_num_adapters; + phr->error = 0; /* the function completed OK; */ +} + +static unsigned int control_cache_alloc_check(struct hpi_control_cache *pC) +{ + unsigned int i; + int cached = 0; + if (!pC) + return 0; + if ((!pC->init) && (pC->p_cache != NULL) && (pC->control_count) + && (pC->cache_size_in_bytes) + ) { + u32 *p_master_cache; + pC->init = 1; + + p_master_cache = (u32 *)pC->p_cache; + HPI_DEBUG_LOG(VERBOSE, "check %d controls\n", + pC->control_count); + for (i = 0; i < pC->control_count; i++) { + struct hpi_control_cache_info *info = + (struct hpi_control_cache_info *) + p_master_cache; + + if (info->control_type) { + pC->p_info[i] = info; + cached++; + } else + pC->p_info[i] = NULL; + + if (info->size_in32bit_words) + p_master_cache += info->size_in32bit_words; + else + p_master_cache += + sizeof(struct + hpi_control_cache_single) / + sizeof(u32); + + HPI_DEBUG_LOG(VERBOSE, + "cached %d, pinfo %p index %d type %d\n", + cached, pC->p_info[i], info->control_index, + info->control_type); + } + /* + We didn't find anything to cache, so try again later ! + */ + if (!cached) + pC->init = 0; + } + return pC->init; +} + +/** Find a control. +*/ +static short find_control(struct hpi_message *phm, + struct hpi_control_cache *p_cache, struct hpi_control_cache_info **pI, + u16 *pw_control_index) +{ + *pw_control_index = phm->obj_index; + + if (!control_cache_alloc_check(p_cache)) { + HPI_DEBUG_LOG(VERBOSE, + "control_cache_alloc_check() failed. adap%d ci%d\n", + phm->adapter_index, *pw_control_index); + return 0; + } + + *pI = p_cache->p_info[*pw_control_index]; + if (!*pI) { + HPI_DEBUG_LOG(VERBOSE, "uncached adap %d, control %d\n", + phm->adapter_index, *pw_control_index); + return 0; + } else { + HPI_DEBUG_LOG(VERBOSE, "find_control() type %d\n", + (*pI)->control_type); + } + return 1; +} + +/** Used by the kernel driver to figure out if a buffer needs mapping. + */ +short hpi_check_buffer_mapping(struct hpi_control_cache *p_cache, + struct hpi_message *phm, void **p, unsigned int *pN) +{ + *pN = 0; + *p = NULL; + if ((phm->function == HPI_CONTROL_GET_STATE) + && (phm->object == HPI_OBJ_CONTROLEX) + ) { + u16 control_index; + struct hpi_control_cache_info *pI; + + if (!find_control(phm, p_cache, &pI, &control_index)) + return 0; + } + return 0; +} + +/* allow unified treatment of several string fields within struct */ +#define HPICMN_PAD_OFS_AND_SIZE(m) {\ + offsetof(struct hpi_control_cache_pad, m), \ + sizeof(((struct hpi_control_cache_pad *)(NULL))->m) } + +struct pad_ofs_size { + unsigned int offset; + unsigned int field_size; +}; + +static struct pad_ofs_size pad_desc[] = { + HPICMN_PAD_OFS_AND_SIZE(c_channel), /* HPI_PAD_CHANNEL_NAME */ + HPICMN_PAD_OFS_AND_SIZE(c_artist), /* HPI_PAD_ARTIST */ + HPICMN_PAD_OFS_AND_SIZE(c_title), /* HPI_PAD_TITLE */ + HPICMN_PAD_OFS_AND_SIZE(c_comment), /* HPI_PAD_COMMENT */ +}; + +/** CheckControlCache checks the cache and fills the struct hpi_response + * accordingly. It returns one if a cache hit occurred, zero otherwise. + */ +short hpi_check_control_cache(struct hpi_control_cache *p_cache, + struct hpi_message *phm, struct hpi_response *phr) +{ + short found = 1; + u16 control_index; + struct hpi_control_cache_info *pI; + struct hpi_control_cache_single *pC; + struct hpi_control_cache_pad *p_pad; + + if (!find_control(phm, p_cache, &pI, &control_index)) + return 0; + + phr->error = 0; + + /* pC is the default cached control strucure. May be cast to + something else in the following switch statement. + */ + pC = (struct hpi_control_cache_single *)pI; + p_pad = (struct hpi_control_cache_pad *)pI; + + switch (pI->control_type) { + + case HPI_CONTROL_METER: + if (phm->u.c.attribute == HPI_METER_PEAK) { + phr->u.c.an_log_value[0] = pC->u.p.an_log_peak[0]; + phr->u.c.an_log_value[1] = pC->u.p.an_log_peak[1]; + } else if (phm->u.c.attribute == HPI_METER_RMS) { + phr->u.c.an_log_value[0] = pC->u.p.an_logRMS[0]; + phr->u.c.an_log_value[1] = pC->u.p.an_logRMS[1]; + } else + found = 0; + break; + case HPI_CONTROL_VOLUME: + if (phm->u.c.attribute == HPI_VOLUME_GAIN) { + phr->u.c.an_log_value[0] = pC->u.v.an_log[0]; + phr->u.c.an_log_value[1] = pC->u.v.an_log[1]; + } else + found = 0; + break; + case HPI_CONTROL_MULTIPLEXER: + if (phm->u.c.attribute == HPI_MULTIPLEXER_SOURCE) { + phr->u.c.param1 = pC->u.x.source_node_type; + phr->u.c.param2 = pC->u.x.source_node_index; + } else { + found = 0; + } + break; + case HPI_CONTROL_CHANNEL_MODE: + if (phm->u.c.attribute == HPI_CHANNEL_MODE_MODE) + phr->u.c.param1 = pC->u.m.mode; + else + found = 0; + break; + case HPI_CONTROL_LEVEL: + if (phm->u.c.attribute == HPI_LEVEL_GAIN) { + phr->u.c.an_log_value[0] = pC->u.l.an_log[0]; + phr->u.c.an_log_value[1] = pC->u.l.an_log[1]; + } else + found = 0; + break; + case HPI_CONTROL_TUNER: + { + struct hpi_control_cache_single *pCT = + (struct hpi_control_cache_single *)pI; + if (phm->u.c.attribute == HPI_TUNER_FREQ) + phr->u.c.param1 = pCT->u.t.freq_ink_hz; + else if (phm->u.c.attribute == HPI_TUNER_BAND) + phr->u.c.param1 = pCT->u.t.band; + else if ((phm->u.c.attribute == HPI_TUNER_LEVEL) + && (phm->u.c.param1 == + HPI_TUNER_LEVEL_AVERAGE)) + phr->u.c.param1 = pCT->u.t.level; + else + found = 0; + } + break; + case HPI_CONTROL_AESEBU_RECEIVER: + if (phm->u.c.attribute == HPI_AESEBURX_ERRORSTATUS) + phr->u.c.param1 = pC->u.aes3rx.error_status; + else if (phm->u.c.attribute == HPI_AESEBURX_FORMAT) + phr->u.c.param1 = pC->u.aes3rx.source; + else + found = 0; + break; + case HPI_CONTROL_AESEBU_TRANSMITTER: + if (phm->u.c.attribute == HPI_AESEBUTX_FORMAT) + phr->u.c.param1 = pC->u.aes3tx.format; + else + found = 0; + break; + case HPI_CONTROL_TONEDETECTOR: + if (phm->u.c.attribute == HPI_TONEDETECTOR_STATE) + phr->u.c.param1 = pC->u.tone.state; + else + found = 0; + break; + case HPI_CONTROL_SILENCEDETECTOR: + if (phm->u.c.attribute == HPI_SILENCEDETECTOR_STATE) { + phr->u.c.param1 = pC->u.silence.state; + phr->u.c.param2 = pC->u.silence.count; + } else + found = 0; + break; + case HPI_CONTROL_MICROPHONE: + if (phm->u.c.attribute == HPI_MICROPHONE_PHANTOM_POWER) + phr->u.c.param1 = pC->u.phantom_power.state; + else + found = 0; + break; + case HPI_CONTROL_SAMPLECLOCK: + if (phm->u.c.attribute == HPI_SAMPLECLOCK_SOURCE) + phr->u.c.param1 = pC->u.clk.source; + else if (phm->u.c.attribute == HPI_SAMPLECLOCK_SOURCE_INDEX) { + if (pC->u.clk.source_index == + HPI_ERROR_ILLEGAL_CACHE_VALUE) { + phr->u.c.param1 = 0; + phr->error = HPI_ERROR_INVALID_OPERATION; + } else + phr->u.c.param1 = pC->u.clk.source_index; + } else if (phm->u.c.attribute == HPI_SAMPLECLOCK_SAMPLERATE) + phr->u.c.param1 = pC->u.clk.sample_rate; + else + found = 0; + break; + case HPI_CONTROL_PAD: + + if (!(p_pad->field_valid_flags & (1 << + HPI_CTL_ATTR_INDEX(phm->u.c. + attribute)))) { + phr->error = HPI_ERROR_INVALID_CONTROL_ATTRIBUTE; + break; + } + + if (phm->u.c.attribute == HPI_PAD_PROGRAM_ID) + phr->u.c.param1 = p_pad->pI; + else if (phm->u.c.attribute == HPI_PAD_PROGRAM_TYPE) + phr->u.c.param1 = p_pad->pTY; + else { + unsigned int index = + HPI_CTL_ATTR_INDEX(phm->u.c.attribute) - 1; + unsigned int offset = phm->u.c.param1; + unsigned int pad_string_len, field_size; + char *pad_string; + unsigned int tocopy; + + HPI_DEBUG_LOG(VERBOSE, "PADS HPI_PADS_ %d\n", + phm->u.c.attribute); + + if (index > ARRAY_SIZE(pad_desc) - 1) { + phr->error = + HPI_ERROR_INVALID_CONTROL_ATTRIBUTE; + break; + } + + pad_string = ((char *)p_pad) + pad_desc[index].offset; + field_size = pad_desc[index].field_size; + /* Ensure null terminator */ + pad_string[field_size - 1] = 0; + + pad_string_len = strlen(pad_string) + 1; + + if (offset > pad_string_len) { + phr->error = HPI_ERROR_INVALID_CONTROL_VALUE; + break; + } + + tocopy = pad_string_len - offset; + if (tocopy > sizeof(phr->u.cu.chars8.sz_data)) + tocopy = sizeof(phr->u.cu.chars8.sz_data); + + HPI_DEBUG_LOG(VERBOSE, + "PADS memcpy(%d), offset %d \n", tocopy, + offset); + memcpy(phr->u.cu.chars8.sz_data, &pad_string[offset], + tocopy); + + phr->u.cu.chars8.remaining_chars = + pad_string_len - offset - tocopy; + } + break; + default: + found = 0; + break; + } + + if (found) + HPI_DEBUG_LOG(VERBOSE, + "cached adap %d, ctl %d, type %d, attr %d\n", + phm->adapter_index, pI->control_index, + pI->control_type, phm->u.c.attribute); + else + HPI_DEBUG_LOG(VERBOSE, + "uncached adap %d, ctl %d, ctl type %d\n", + phm->adapter_index, pI->control_index, + pI->control_type); + + if (found) + phr->size = + sizeof(struct hpi_response_header) + + sizeof(struct hpi_control_res); + + return found; +} + +/** Updates the cache with Set values. + +Only update if no error. +Volume and Level return the limited values in the response, so use these +Multiplexer does so use sent values +*/ +void hpi_sync_control_cache(struct hpi_control_cache *p_cache, + struct hpi_message *phm, struct hpi_response *phr) +{ + u16 control_index; + struct hpi_control_cache_single *pC; + struct hpi_control_cache_info *pI; + + if (!find_control(phm, p_cache, &pI, &control_index)) + return; + + /* pC is the default cached control strucure. + May be cast to something else in the following switch statement. + */ + pC = (struct hpi_control_cache_single *)pI; + + switch (pI->control_type) { + case HPI_CONTROL_VOLUME: + if (phm->u.c.attribute == HPI_VOLUME_GAIN) { + pC->u.v.an_log[0] = phr->u.c.an_log_value[0]; + pC->u.v.an_log[1] = phr->u.c.an_log_value[1]; + } + break; + case HPI_CONTROL_MULTIPLEXER: + /* mux does not return its setting on Set command. */ + if (phr->error) + return; + if (phm->u.c.attribute == HPI_MULTIPLEXER_SOURCE) { + pC->u.x.source_node_type = (u16)phm->u.c.param1; + pC->u.x.source_node_index = (u16)phm->u.c.param2; + } + break; + case HPI_CONTROL_CHANNEL_MODE: + /* mode does not return its setting on Set command. */ + if (phr->error) + return; + if (phm->u.c.attribute == HPI_CHANNEL_MODE_MODE) + pC->u.m.mode = (u16)phm->u.c.param1; + break; + case HPI_CONTROL_LEVEL: + if (phm->u.c.attribute == HPI_LEVEL_GAIN) { + pC->u.v.an_log[0] = phr->u.c.an_log_value[0]; + pC->u.v.an_log[1] = phr->u.c.an_log_value[1]; + } + break; + case HPI_CONTROL_MICROPHONE: + if (phm->u.c.attribute == HPI_MICROPHONE_PHANTOM_POWER) + pC->u.phantom_power.state = (u16)phm->u.c.param1; + break; + case HPI_CONTROL_AESEBU_TRANSMITTER: + if (phr->error) + return; + if (phm->u.c.attribute == HPI_AESEBUTX_FORMAT) + pC->u.aes3tx.format = phm->u.c.param1; + break; + case HPI_CONTROL_AESEBU_RECEIVER: + if (phr->error) + return; + if (phm->u.c.attribute == HPI_AESEBURX_FORMAT) + pC->u.aes3rx.source = phm->u.c.param1; + break; + case HPI_CONTROL_SAMPLECLOCK: + if (phr->error) + return; + if (phm->u.c.attribute == HPI_SAMPLECLOCK_SOURCE) + pC->u.clk.source = (u16)phm->u.c.param1; + else if (phm->u.c.attribute == HPI_SAMPLECLOCK_SOURCE_INDEX) + pC->u.clk.source_index = (u16)phm->u.c.param1; + else if (phm->u.c.attribute == HPI_SAMPLECLOCK_SAMPLERATE) + pC->u.clk.sample_rate = phm->u.c.param1; + break; + default: + break; + } +} + +struct hpi_control_cache *hpi_alloc_control_cache(const u32 + number_of_controls, const u32 size_in_bytes, + struct hpi_control_cache_info *pDSP_control_buffer) +{ + struct hpi_control_cache *p_cache = + kmalloc(sizeof(*p_cache), GFP_KERNEL); + p_cache->cache_size_in_bytes = size_in_bytes; + p_cache->control_count = number_of_controls; + p_cache->p_cache = + (struct hpi_control_cache_single *)pDSP_control_buffer; + p_cache->init = 0; + p_cache->p_info = + kmalloc(sizeof(*p_cache->p_info) * p_cache->control_count, + GFP_KERNEL); + return p_cache; +} + +void hpi_free_control_cache(struct hpi_control_cache *p_cache) +{ + if ((p_cache->init) && (p_cache->p_info)) { + kfree(p_cache->p_info); + p_cache->p_info = NULL; + p_cache->init = 0; + kfree(p_cache); + } +} + +static void subsys_message(struct hpi_message *phm, struct hpi_response *phr) +{ + + switch (phm->function) { + case HPI_SUBSYS_OPEN: + case HPI_SUBSYS_CLOSE: + case HPI_SUBSYS_DRIVER_UNLOAD: + phr->error = 0; + break; + case HPI_SUBSYS_DRIVER_LOAD: + wipe_adapter_list(); + hpios_alistlock_init(&adapters); + phr->error = 0; + break; + case HPI_SUBSYS_GET_INFO: + subsys_get_adapters(phr); + break; + case HPI_SUBSYS_CREATE_ADAPTER: + case HPI_SUBSYS_DELETE_ADAPTER: + phr->error = 0; + break; + default: + phr->error = HPI_ERROR_INVALID_FUNC; + break; + } +} + +void HPI_COMMON(struct hpi_message *phm, struct hpi_response *phr) +{ + switch (phm->type) { + case HPI_TYPE_MESSAGE: + switch (phm->object) { + case HPI_OBJ_SUBSYSTEM: + subsys_message(phm, phr); + break; + } + break; + + default: + phr->error = HPI_ERROR_INVALID_TYPE; + break; + } +} diff --git a/sound/pci/asihpi/hpicmn.h b/sound/pci/asihpi/hpicmn.h new file mode 100644 index 000000000000..6229022f56cb --- /dev/null +++ b/sound/pci/asihpi/hpicmn.h @@ -0,0 +1,64 @@ +/** + + AudioScience HPI driver + Copyright (C) 1997-2010 AudioScience Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation; + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +struct hpi_adapter_obj { + struct hpi_pci pci; /* PCI info - bus#,dev#,address etc */ + u16 adapter_type; /* ASI6701 etc */ + u16 index; /* */ + u16 open; /* =1 when adapter open */ + u16 mixer_open; + + struct hpios_spinlock dsp_lock; + + u16 dsp_crashed; + u16 has_control_cache; + void *priv; +}; + +struct hpi_control_cache { + u32 init; /**< indicates whether the + structures are initialized */ + u32 control_count; + u32 cache_size_in_bytes; + struct hpi_control_cache_info + **p_info; /**< pointer to allocated memory of + lookup pointers. */ + struct hpi_control_cache_single + *p_cache; /**< pointer to DSP's control cache. */ +}; + +struct hpi_adapter_obj *hpi_find_adapter(u16 adapter_index); +u16 hpi_add_adapter(struct hpi_adapter_obj *pao); + +void hpi_delete_adapter(struct hpi_adapter_obj *pao); + +short hpi_check_control_cache(struct hpi_control_cache *pC, + struct hpi_message *phm, struct hpi_response *phr); +struct hpi_control_cache *hpi_alloc_control_cache(const u32 + number_of_controls, const u32 size_in_bytes, + struct hpi_control_cache_info + *pDSP_control_buffer); +void hpi_free_control_cache(struct hpi_control_cache *p_cache); + +void hpi_sync_control_cache(struct hpi_control_cache *pC, + struct hpi_message *phm, struct hpi_response *phr); +u16 hpi_validate_response(struct hpi_message *phm, struct hpi_response *phr); +short hpi_check_buffer_mapping(struct hpi_control_cache *p_cache, + struct hpi_message *phm, void **p, unsigned int *pN); diff --git a/sound/pci/asihpi/hpidebug.c b/sound/pci/asihpi/hpidebug.c new file mode 100644 index 000000000000..4cd85a401b34 --- /dev/null +++ b/sound/pci/asihpi/hpidebug.c @@ -0,0 +1,225 @@ +/************************************************************************ + + AudioScience HPI driver + Copyright (C) 1997-2010 AudioScience Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation; + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Debug macro translation. + +************************************************************************/ + +#include "hpi_internal.h" +#include "hpidebug.h" + +/* Debug level; 0 quiet; 1 informative, 2 debug, 3 verbose debug. */ +int hpi_debug_level = HPI_DEBUG_LEVEL_DEFAULT; + +void hpi_debug_init(void) +{ + printk(KERN_INFO "debug start\n"); +} + +int hpi_debug_level_set(int level) +{ + int old_level; + + old_level = hpi_debug_level; + hpi_debug_level = level; + return old_level; +} + +int hpi_debug_level_get(void) +{ + return hpi_debug_level; +} + +#ifdef HPIOS_DEBUG_PRINT +/* implies OS has no printf-like function */ +#include + +void hpi_debug_printf(char *fmt, ...) +{ + va_list arglist; + char buffer[128]; + + va_start(arglist, fmt); + + if (buffer[0]) + HPIOS_DEBUG_PRINT(buffer); + va_end(arglist); +} +#endif + +struct treenode { + void *array; + unsigned int num_elements; +}; + +#define make_treenode_from_array(nodename, array) \ +static void *tmp_strarray_##nodename[] = array; \ +static struct treenode nodename = { \ + &tmp_strarray_##nodename, \ + ARRAY_SIZE(tmp_strarray_##nodename) \ +}; + +#define get_treenode_elem(node_ptr, idx, type) \ + (&(*((type *)(node_ptr)->array)[idx])) + +make_treenode_from_array(hpi_control_type_strings, HPI_CONTROL_TYPE_STRINGS) + + make_treenode_from_array(hpi_subsys_strings, HPI_SUBSYS_STRINGS) + make_treenode_from_array(hpi_adapter_strings, HPI_ADAPTER_STRINGS) + make_treenode_from_array(hpi_istream_strings, HPI_ISTREAM_STRINGS) + make_treenode_from_array(hpi_ostream_strings, HPI_OSTREAM_STRINGS) + make_treenode_from_array(hpi_mixer_strings, HPI_MIXER_STRINGS) + make_treenode_from_array(hpi_node_strings, + { + "NODE is invalid object"}) + + make_treenode_from_array(hpi_control_strings, HPI_CONTROL_STRINGS) + make_treenode_from_array(hpi_nvmemory_strings, HPI_OBJ_STRINGS) + make_treenode_from_array(hpi_digitalio_strings, HPI_DIGITALIO_STRINGS) + make_treenode_from_array(hpi_watchdog_strings, HPI_WATCHDOG_STRINGS) + make_treenode_from_array(hpi_clock_strings, HPI_CLOCK_STRINGS) + make_treenode_from_array(hpi_profile_strings, HPI_PROFILE_STRINGS) + make_treenode_from_array(hpi_asyncevent_strings, HPI_ASYNCEVENT_STRINGS) +#define HPI_FUNCTION_STRINGS \ +{ \ + &hpi_subsys_strings,\ + &hpi_adapter_strings,\ + &hpi_ostream_strings,\ + &hpi_istream_strings,\ + &hpi_mixer_strings,\ + &hpi_node_strings,\ + &hpi_control_strings,\ + &hpi_nvmemory_strings,\ + &hpi_digitalio_strings,\ + &hpi_watchdog_strings,\ + &hpi_clock_strings,\ + &hpi_profile_strings,\ + &hpi_control_strings, \ + &hpi_asyncevent_strings \ +}; + make_treenode_from_array(hpi_function_strings, HPI_FUNCTION_STRINGS) + + compile_time_assert(HPI_OBJ_MAXINDEX == 14, obj_list_doesnt_match); + +static char *hpi_function_string(unsigned int function) +{ + unsigned int object; + struct treenode *tmp; + + object = function / HPI_OBJ_FUNCTION_SPACING; + function = function - object * HPI_OBJ_FUNCTION_SPACING; + + if (object == 0 || object == HPI_OBJ_NODE + || object > hpi_function_strings.num_elements) + return "invalid object"; + + tmp = get_treenode_elem(&hpi_function_strings, object - 1, + struct treenode *); + + if (function == 0 || function > tmp->num_elements) + return "invalid function"; + + return get_treenode_elem(tmp, function - 1, char *); +} + +void hpi_debug_message(struct hpi_message *phm, char *sz_fileline) +{ + if (phm) { + if ((phm->object <= HPI_OBJ_MAXINDEX) && phm->object) { + u16 index = 0; + u16 attrib = 0; + int is_control = 0; + + index = phm->obj_index; + switch (phm->object) { + case HPI_OBJ_ADAPTER: + case HPI_OBJ_PROFILE: + break; + case HPI_OBJ_MIXER: + if (phm->function == + HPI_MIXER_GET_CONTROL_BY_INDEX) + index = phm->u.m.control_index; + break; + case HPI_OBJ_OSTREAM: + case HPI_OBJ_ISTREAM: + break; + + case HPI_OBJ_CONTROLEX: + case HPI_OBJ_CONTROL: + if (phm->version == 1) + attrib = HPI_CTL_ATTR(UNIVERSAL, 1); + else + attrib = phm->u.c.attribute; + is_control = 1; + break; + default: + break; + } + + if (is_control && (attrib & 0xFF00)) { + int control_type = (attrib & 0xFF00) >> 8; + int attr_index = HPI_CTL_ATTR_INDEX(attrib); + /* note the KERN facility level + is in szFileline already */ + printk("%s adapter %d %s " + "ctrl_index x%04x %s %d\n", + sz_fileline, phm->adapter_index, + hpi_function_string(phm->function), + index, + get_treenode_elem + (&hpi_control_type_strings, + control_type, char *), + attr_index); + + } else + printk("%s adapter %d %s " + "idx x%04x attr x%04x \n", + sz_fileline, phm->adapter_index, + hpi_function_string(phm->function), + index, attrib); + } else { + printk("adap=%d, invalid obj=%d, func=0x%x\n", + phm->adapter_index, phm->object, + phm->function); + } + } else + printk(KERN_ERR + "NULL message pointer to hpi_debug_message!\n"); +} + +void hpi_debug_data(u16 *pdata, u32 len) +{ + u32 i; + int j; + int k; + int lines; + int cols = 8; + + lines = (len + cols - 1) / cols; + if (lines > 8) + lines = 8; + + for (i = 0, j = 0; j < lines; j++) { + printk(KERN_DEBUG "%p:", (pdata + i)); + + for (k = 0; k < cols && i < len; i++, k++) + printk("%s%04x", k == 0 ? "" : " ", pdata[i]); + + printk("\n"); + } +} diff --git a/sound/pci/asihpi/hpidebug.h b/sound/pci/asihpi/hpidebug.h new file mode 100644 index 000000000000..44dccadcc25b --- /dev/null +++ b/sound/pci/asihpi/hpidebug.h @@ -0,0 +1,385 @@ +/***************************************************************************** + + AudioScience HPI driver + Copyright (C) 1997-2010 AudioScience Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation; + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Debug macros. + +*****************************************************************************/ + +#ifndef _HPIDEBUG_H +#define _HPIDEBUG_H + +#include "hpi_internal.h" + +/* Define debugging levels. */ +enum { HPI_DEBUG_LEVEL_ERROR = 0, /* always log errors */ + HPI_DEBUG_LEVEL_WARNING = 1, + HPI_DEBUG_LEVEL_NOTICE = 2, + HPI_DEBUG_LEVEL_INFO = 3, + HPI_DEBUG_LEVEL_DEBUG = 4, + HPI_DEBUG_LEVEL_VERBOSE = 5 /* same printk level as DEBUG */ +}; + +#define HPI_DEBUG_LEVEL_DEFAULT HPI_DEBUG_LEVEL_NOTICE + +/* an OS can define an extra flag string that is appended to + the start of each message, eg see hpios_linux.h */ + +#ifdef SOURCEFILE_NAME +#define FILE_LINE SOURCEFILE_NAME ":" __stringify(__LINE__) " " +#else +#define FILE_LINE __FILE__ ":" __stringify(__LINE__) " " +#endif + +#if defined(HPI_DEBUG) && defined(_WINDOWS) +#define HPI_DEBUGBREAK() debug_break() +#else +#define HPI_DEBUGBREAK() +#endif + +#define HPI_DEBUG_ASSERT(expression) \ + do { \ + if (!(expression)) {\ + printk(KERN_ERR FILE_LINE\ + "ASSERT " __stringify(expression));\ + HPI_DEBUGBREAK();\ + } \ + } while (0) + +#define HPI_DEBUG_LOG(level, ...) \ + do { \ + if (hpi_debug_level >= HPI_DEBUG_LEVEL_##level) { \ + printk(HPI_DEBUG_FLAG_##level \ + FILE_LINE __VA_ARGS__); \ + } \ + } while (0) + +void hpi_debug_init(void); +int hpi_debug_level_set(int level); +int hpi_debug_level_get(void); +/* needed by Linux driver for dynamic debug level changes */ +extern int hpi_debug_level; + +void hpi_debug_message(struct hpi_message *phm, char *sz_fileline); + +void hpi_debug_data(u16 *pdata, u32 len); + +#define HPI_DEBUG_DATA(pdata, len) \ + do { \ + if (hpi_debug_level >= HPI_DEBUG_LEVEL_VERBOSE) \ + hpi_debug_data(pdata, len); \ + } while (0) + +#define HPI_DEBUG_MESSAGE(level, phm) \ + do { \ + if (hpi_debug_level >= HPI_DEBUG_LEVEL_##level) { \ + hpi_debug_message(phm,HPI_DEBUG_FLAG_##level \ + FILE_LINE __stringify(level));\ + } \ + } while (0) + +#define HPI_DEBUG_RESPONSE(phr) \ + do { \ + if ((hpi_debug_level >= HPI_DEBUG_LEVEL_DEBUG) && (phr->error))\ + HPI_DEBUG_LOG(ERROR, \ + "HPI response - error# %d\n", \ + phr->error); \ + else if (hpi_debug_level >= HPI_DEBUG_LEVEL_VERBOSE) \ + HPI_DEBUG_LOG(VERBOSE, "HPI response OK\n");\ + } while (0) + +#ifndef compile_time_assert +#define compile_time_assert(cond, msg) \ + typedef char msg[(cond) ? 1 : -1] +#endif + + /* check that size is exactly some number */ +#define function_count_check(sym, size) \ + compile_time_assert((sym##_FUNCTION_COUNT) == (size),\ + strings_match_defs_##sym) + +/* These strings should be generated using a macro which defines + the corresponding symbol values. */ +#define HPI_OBJ_STRINGS \ +{ \ + "HPI_OBJ_SUBSYSTEM", \ + "HPI_OBJ_ADAPTER", \ + "HPI_OBJ_OSTREAM", \ + "HPI_OBJ_ISTREAM", \ + "HPI_OBJ_MIXER", \ + "HPI_OBJ_NODE", \ + "HPI_OBJ_CONTROL", \ + "HPI_OBJ_NVMEMORY", \ + "HPI_OBJ_DIGITALIO", \ + "HPI_OBJ_WATCHDOG", \ + "HPI_OBJ_CLOCK", \ + "HPI_OBJ_PROFILE", \ + "HPI_OBJ_CONTROLEX" \ +} + +#define HPI_SUBSYS_STRINGS \ +{ \ + "HPI_SUBSYS_OPEN", \ + "HPI_SUBSYS_GET_VERSION", \ + "HPI_SUBSYS_GET_INFO", \ + "HPI_SUBSYS_FIND_ADAPTERS", \ + "HPI_SUBSYS_CREATE_ADAPTER",\ + "HPI_SUBSYS_CLOSE", \ + "HPI_SUBSYS_DELETE_ADAPTER", \ + "HPI_SUBSYS_DRIVER_LOAD", \ + "HPI_SUBSYS_DRIVER_UNLOAD", \ + "HPI_SUBSYS_READ_PORT_8", \ + "HPI_SUBSYS_WRITE_PORT_8", \ + "HPI_SUBSYS_GET_NUM_ADAPTERS",\ + "HPI_SUBSYS_GET_ADAPTER", \ + "HPI_SUBSYS_SET_NETWORK_INTERFACE"\ +} +function_count_check(HPI_SUBSYS, 14); + +#define HPI_ADAPTER_STRINGS \ +{ \ + "HPI_ADAPTER_OPEN", \ + "HPI_ADAPTER_CLOSE", \ + "HPI_ADAPTER_GET_INFO", \ + "HPI_ADAPTER_GET_ASSERT", \ + "HPI_ADAPTER_TEST_ASSERT", \ + "HPI_ADAPTER_SET_MODE", \ + "HPI_ADAPTER_GET_MODE", \ + "HPI_ADAPTER_ENABLE_CAPABILITY",\ + "HPI_ADAPTER_SELFTEST", \ + "HPI_ADAPTER_FIND_OBJECT", \ + "HPI_ADAPTER_QUERY_FLASH", \ + "HPI_ADAPTER_START_FLASH", \ + "HPI_ADAPTER_PROGRAM_FLASH", \ + "HPI_ADAPTER_SET_PROPERTY", \ + "HPI_ADAPTER_GET_PROPERTY", \ + "HPI_ADAPTER_ENUM_PROPERTY", \ + "HPI_ADAPTER_MODULE_INFO", \ + "HPI_ADAPTER_DEBUG_READ" \ +} + +function_count_check(HPI_ADAPTER, 18); + +#define HPI_OSTREAM_STRINGS \ +{ \ + "HPI_OSTREAM_OPEN", \ + "HPI_OSTREAM_CLOSE", \ + "HPI_OSTREAM_WRITE", \ + "HPI_OSTREAM_START", \ + "HPI_OSTREAM_STOP", \ + "HPI_OSTREAM_RESET", \ + "HPI_OSTREAM_GET_INFO", \ + "HPI_OSTREAM_QUERY_FORMAT", \ + "HPI_OSTREAM_DATA", \ + "HPI_OSTREAM_SET_VELOCITY", \ + "HPI_OSTREAM_SET_PUNCHINOUT", \ + "HPI_OSTREAM_SINEGEN", \ + "HPI_OSTREAM_ANC_RESET", \ + "HPI_OSTREAM_ANC_GET_INFO", \ + "HPI_OSTREAM_ANC_READ", \ + "HPI_OSTREAM_SET_TIMESCALE",\ + "HPI_OSTREAM_SET_FORMAT", \ + "HPI_OSTREAM_HOSTBUFFER_ALLOC", \ + "HPI_OSTREAM_HOSTBUFFER_FREE", \ + "HPI_OSTREAM_GROUP_ADD",\ + "HPI_OSTREAM_GROUP_GETMAP", \ + "HPI_OSTREAM_GROUP_RESET", \ + "HPI_OSTREAM_HOSTBUFFER_GET_INFO", \ + "HPI_OSTREAM_WAIT_START", \ +} +function_count_check(HPI_OSTREAM, 24); + +#define HPI_ISTREAM_STRINGS \ +{ \ + "HPI_ISTREAM_OPEN", \ + "HPI_ISTREAM_CLOSE", \ + "HPI_ISTREAM_SET_FORMAT", \ + "HPI_ISTREAM_READ", \ + "HPI_ISTREAM_START", \ + "HPI_ISTREAM_STOP", \ + "HPI_ISTREAM_RESET", \ + "HPI_ISTREAM_GET_INFO", \ + "HPI_ISTREAM_QUERY_FORMAT", \ + "HPI_ISTREAM_ANC_RESET", \ + "HPI_ISTREAM_ANC_GET_INFO", \ + "HPI_ISTREAM_ANC_WRITE", \ + "HPI_ISTREAM_HOSTBUFFER_ALLOC",\ + "HPI_ISTREAM_HOSTBUFFER_FREE", \ + "HPI_ISTREAM_GROUP_ADD", \ + "HPI_ISTREAM_GROUP_GETMAP", \ + "HPI_ISTREAM_GROUP_RESET", \ + "HPI_ISTREAM_HOSTBUFFER_GET_INFO", \ + "HPI_ISTREAM_WAIT_START", \ +} +function_count_check(HPI_ISTREAM, 19); + +#define HPI_MIXER_STRINGS \ +{ \ + "HPI_MIXER_OPEN", \ + "HPI_MIXER_CLOSE", \ + "HPI_MIXER_GET_INFO", \ + "HPI_MIXER_GET_NODE_INFO", \ + "HPI_MIXER_GET_CONTROL", \ + "HPI_MIXER_SET_CONNECTION", \ + "HPI_MIXER_GET_CONNECTIONS", \ + "HPI_MIXER_GET_CONTROL_BY_INDEX", \ + "HPI_MIXER_GET_CONTROL_ARRAY_BY_INDEX", \ + "HPI_MIXER_GET_CONTROL_MULTIPLE_VALUES", \ + "HPI_MIXER_STORE", \ +} +function_count_check(HPI_MIXER, 11); + +#define HPI_CONTROL_STRINGS \ +{ \ + "HPI_CONTROL_GET_INFO", \ + "HPI_CONTROL_GET_STATE", \ + "HPI_CONTROL_SET_STATE" \ +} +function_count_check(HPI_CONTROL, 3); + +#define HPI_NVMEMORY_STRINGS \ +{ \ + "HPI_NVMEMORY_OPEN", \ + "HPI_NVMEMORY_READ_BYTE", \ + "HPI_NVMEMORY_WRITE_BYTE" \ +} +function_count_check(HPI_NVMEMORY, 3); + +#define HPI_DIGITALIO_STRINGS \ +{ \ + "HPI_GPIO_OPEN", \ + "HPI_GPIO_READ_BIT", \ + "HPI_GPIO_WRITE_BIT", \ + "HPI_GPIO_READ_ALL", \ + "HPI_GPIO_WRITE_STATUS"\ +} +function_count_check(HPI_GPIO, 5); + +#define HPI_WATCHDOG_STRINGS \ +{ \ + "HPI_WATCHDOG_OPEN", \ + "HPI_WATCHDOG_SET_TIME", \ + "HPI_WATCHDOG_PING" \ +} + +#define HPI_CLOCK_STRINGS \ +{ \ + "HPI_CLOCK_OPEN", \ + "HPI_CLOCK_SET_TIME", \ + "HPI_CLOCK_GET_TIME" \ +} + +#define HPI_PROFILE_STRINGS \ +{ \ + "HPI_PROFILE_OPEN_ALL", \ + "HPI_PROFILE_START_ALL", \ + "HPI_PROFILE_STOP_ALL", \ + "HPI_PROFILE_GET", \ + "HPI_PROFILE_GET_IDLECOUNT", \ + "HPI_PROFILE_GET_NAME", \ + "HPI_PROFILE_GET_UTILIZATION" \ +} +function_count_check(HPI_PROFILE, 7); + +#define HPI_ASYNCEVENT_STRINGS \ +{ \ + "HPI_ASYNCEVENT_OPEN",\ + "HPI_ASYNCEVENT_CLOSE ",\ + "HPI_ASYNCEVENT_WAIT",\ + "HPI_ASYNCEVENT_GETCOUNT",\ + "HPI_ASYNCEVENT_GET",\ + "HPI_ASYNCEVENT_SENDEVENTS"\ +} +function_count_check(HPI_ASYNCEVENT, 6); + +#define HPI_CONTROL_TYPE_STRINGS \ +{ \ + "null control", \ + "HPI_CONTROL_CONNECTION", \ + "HPI_CONTROL_VOLUME", \ + "HPI_CONTROL_METER", \ + "HPI_CONTROL_MUTE", \ + "HPI_CONTROL_MULTIPLEXER", \ + "HPI_CONTROL_AESEBU_TRANSMITTER", \ + "HPI_CONTROL_AESEBU_RECEIVER", \ + "HPI_CONTROL_LEVEL", \ + "HPI_CONTROL_TUNER", \ + "HPI_CONTROL_ONOFFSWITCH", \ + "HPI_CONTROL_VOX", \ + "HPI_CONTROL_AES18_TRANSMITTER", \ + "HPI_CONTROL_AES18_RECEIVER", \ + "HPI_CONTROL_AES18_BLOCKGENERATOR", \ + "HPI_CONTROL_CHANNEL_MODE", \ + "HPI_CONTROL_BITSTREAM", \ + "HPI_CONTROL_SAMPLECLOCK", \ + "HPI_CONTROL_MICROPHONE", \ + "HPI_CONTROL_PARAMETRIC_EQ", \ + "HPI_CONTROL_COMPANDER", \ + "HPI_CONTROL_COBRANET", \ + "HPI_CONTROL_TONE_DETECT", \ + "HPI_CONTROL_SILENCE_DETECT", \ + "HPI_CONTROL_PAD", \ + "HPI_CONTROL_SRC" ,\ + "HPI_CONTROL_UNIVERSAL" \ +} + +compile_time_assert((HPI_CONTROL_LAST_INDEX + 1 == 27), + controltype_strings_match_defs); + +#define HPI_SOURCENODE_STRINGS \ +{ \ + "no source", \ + "HPI_SOURCENODE_OSTREAM", \ + "HPI_SOURCENODE_LINEIN", \ + "HPI_SOURCENODE_AESEBU_IN", \ + "HPI_SOURCENODE_TUNER", \ + "HPI_SOURCENODE_RF", \ + "HPI_SOURCENODE_CLOCK_SOURCE", \ + "HPI_SOURCENODE_RAW_BITSTREAM", \ + "HPI_SOURCENODE_MICROPHONE", \ + "HPI_SOURCENODE_COBRANET", \ + "HPI_SOURCENODE_ANALOG", \ + "HPI_SOURCENODE_ADAPTER" \ +} + +compile_time_assert((HPI_SOURCENODE_LAST_INDEX - HPI_SOURCENODE_BASE + 1) == + (12), sourcenode_strings_match_defs); + +#define HPI_DESTNODE_STRINGS \ +{ \ + "no destination", \ + "HPI_DESTNODE_ISTREAM", \ + "HPI_DESTNODE_LINEOUT", \ + "HPI_DESTNODE_AESEBU_OUT", \ + "HPI_DESTNODE_RF", \ + "HPI_DESTNODE_SPEAKER", \ + "HPI_DESTNODE_COBRANET", \ + "HPI_DESTNODE_ANALOG" \ +} +compile_time_assert((HPI_DESTNODE_LAST_INDEX - HPI_DESTNODE_BASE + 1) == (8), + destnode_strings_match_defs); + +#define HPI_CONTROL_CHANNEL_MODE_STRINGS \ +{ \ + "XXX HPI_CHANNEL_MODE_ERROR XXX", \ + "HPI_CHANNEL_MODE_NORMAL", \ + "HPI_CHANNEL_MODE_SWAP", \ + "HPI_CHANNEL_MODE_LEFT_ONLY", \ + "HPI_CHANNEL_MODE_RIGHT_ONLY" \ +} + +#endif /* _HPIDEBUG_H */ diff --git a/sound/pci/asihpi/hpidspcd.c b/sound/pci/asihpi/hpidspcd.c new file mode 100644 index 000000000000..9b10d9a5c255 --- /dev/null +++ b/sound/pci/asihpi/hpidspcd.c @@ -0,0 +1,172 @@ +/***********************************************************************/ +/*! + + AudioScience HPI driver + Copyright (C) 1997-2010 AudioScience Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation; + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +\file +Functions for reading DSP code to load into DSP + +(Linux only:) If DSPCODE_FIRMWARE_LOADER is defined, code is read using +hotplug firmware loader from individual dsp code files + +If neither of the above is defined, code is read from linked arrays. +DSPCODE_ARRAY is defined. + +HPI_INCLUDE_**** must be defined +and the appropriate hzz?????.c or hex?????.c linked in + + */ +/***********************************************************************/ +#define SOURCEFILE_NAME "hpidspcd.c" +#include "hpidspcd.h" +#include "hpidebug.h" + +/** + Header structure for binary dsp code file (see asidsp.doc) + This structure must match that used in s2bin.c for generation of asidsp.bin + */ + +#ifndef DISABLE_PRAGMA_PACK1 +#pragma pack(push, 1) +#endif + +struct code_header { + u32 size; + char type[4]; + u32 adapter; + u32 version; + u32 crc; +}; + +#ifndef DISABLE_PRAGMA_PACK1 +#pragma pack(pop) +#endif + +#define HPI_VER_DECIMAL ((int)(HPI_VER_MAJOR(HPI_VER) * 10000 + \ + HPI_VER_MINOR(HPI_VER) * 100 + HPI_VER_RELEASE(HPI_VER))) + +/***********************************************************************/ +#include "linux/pci.h" +/*-------------------------------------------------------------------*/ +short hpi_dsp_code_open(u32 adapter, struct dsp_code *ps_dsp_code, + u32 *pos_error_code) +{ + const struct firmware *ps_firmware = ps_dsp_code->ps_firmware; + struct code_header header; + char fw_name[20]; + int err; + + sprintf(fw_name, "asihpi/dsp%04x.bin", adapter); + HPI_DEBUG_LOG(INFO, "requesting firmware for %s\n", fw_name); + + err = request_firmware(&ps_firmware, fw_name, + &ps_dsp_code->ps_dev->dev); + if (err != 0) { + HPI_DEBUG_LOG(ERROR, "%d, request_firmware failed for %s\n", + err, fw_name); + goto error1; + } + if (ps_firmware->size < sizeof(header)) { + HPI_DEBUG_LOG(ERROR, "header size too small %s\n", fw_name); + goto error2; + } + memcpy(&header, ps_firmware->data, sizeof(header)); + if (header.adapter != adapter) { + HPI_DEBUG_LOG(ERROR, "adapter type incorrect %4x != %4x\n", + header.adapter, adapter); + goto error2; + } + if (header.size != ps_firmware->size) { + HPI_DEBUG_LOG(ERROR, "code size wrong %d != %ld\n", + header.size, (unsigned long)ps_firmware->size); + goto error2; + } + + if (header.version / 10000 != HPI_VER_DECIMAL / 10000) { + HPI_DEBUG_LOG(ERROR, + "firmware major version mismatch " + "DSP image %d != driver %d\n", header.version, + HPI_VER_DECIMAL); + goto error2; + } + + if (header.version != HPI_VER_DECIMAL) { + HPI_DEBUG_LOG(WARNING, + "version mismatch DSP image %d != driver %d\n", + header.version, HPI_VER_DECIMAL); + /* goto error2; still allow driver to load */ + } + + HPI_DEBUG_LOG(INFO, "dsp code %s opened\n", fw_name); + ps_dsp_code->ps_firmware = ps_firmware; + ps_dsp_code->block_length = header.size / sizeof(u32); + ps_dsp_code->word_count = sizeof(header) / sizeof(u32); + ps_dsp_code->version = header.version; + ps_dsp_code->crc = header.crc; + return 0; + +error2: + release_firmware(ps_firmware); +error1: + ps_dsp_code->ps_firmware = NULL; + ps_dsp_code->block_length = 0; + return HPI_ERROR_DSP_FILE_NOT_FOUND; +} + +/*-------------------------------------------------------------------*/ +void hpi_dsp_code_close(struct dsp_code *ps_dsp_code) +{ + if (ps_dsp_code->ps_firmware != NULL) { + HPI_DEBUG_LOG(DEBUG, "dsp code closed\n"); + release_firmware(ps_dsp_code->ps_firmware); + ps_dsp_code->ps_firmware = NULL; + } +} + +/*-------------------------------------------------------------------*/ +void hpi_dsp_code_rewind(struct dsp_code *ps_dsp_code) +{ + /* Go back to start of data, after header */ + ps_dsp_code->word_count = sizeof(struct code_header) / sizeof(u32); +} + +/*-------------------------------------------------------------------*/ +short hpi_dsp_code_read_word(struct dsp_code *ps_dsp_code, u32 *pword) +{ + if (ps_dsp_code->word_count + 1 > ps_dsp_code->block_length) + return (HPI_ERROR_DSP_FILE_FORMAT); + + *pword = ((u32 *)(ps_dsp_code->ps_firmware->data))[ps_dsp_code-> + word_count]; + ps_dsp_code->word_count++; + return 0; +} + +/*-------------------------------------------------------------------*/ +short hpi_dsp_code_read_block(size_t words_requested, + struct dsp_code *ps_dsp_code, u32 **ppblock) +{ + if (ps_dsp_code->word_count + words_requested > + ps_dsp_code->block_length) + return HPI_ERROR_DSP_FILE_FORMAT; + + *ppblock = + ((u32 *)(ps_dsp_code->ps_firmware->data)) + + ps_dsp_code->word_count; + ps_dsp_code->word_count += words_requested; + return 0; +} diff --git a/sound/pci/asihpi/hpidspcd.h b/sound/pci/asihpi/hpidspcd.h new file mode 100644 index 000000000000..d7c240398225 --- /dev/null +++ b/sound/pci/asihpi/hpidspcd.h @@ -0,0 +1,104 @@ +/***********************************************************************/ +/** + + AudioScience HPI driver + Copyright (C) 1997-2010 AudioScience Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation; + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +\file +Functions for reading DSP code to load into DSP + + hpi_dspcode_defines HPI DSP code loading method +Define exactly one of these to select how the DSP code is supplied to +the adapter. + +End users writing applications that use the HPI interface do not have to +use any of the below defines; they are only necessary for building drivers + +HPI_DSPCODE_FILE: +DSP code is supplied as a file that is opened and read from by the driver. + +HPI_DSPCODE_FIRMWARE: +DSP code is read using the hotplug firmware loader module. + Only valid when compiling the HPI kernel driver under Linux. +*/ +/***********************************************************************/ +#ifndef _HPIDSPCD_H_ +#define _HPIDSPCD_H_ + +#include "hpi_internal.h" + +#ifndef DISABLE_PRAGMA_PACK1 +#pragma pack(push, 1) +#endif + +/** Descriptor for dspcode from firmware loader */ +struct dsp_code { + /** Firmware descriptor */ + const struct firmware *ps_firmware; + struct pci_dev *ps_dev; + /** Expected number of words in the whole dsp code,INCL header */ + long int block_length; + /** Number of words read so far */ + long int word_count; + /** Version read from dsp code file */ + u32 version; + /** CRC read from dsp code file */ + u32 crc; +}; + +#ifndef DISABLE_PRAGMA_PACK1 +#pragma pack(pop) +#endif + +/** Prepare *psDspCode to refer to the requuested adapter. + Searches the file, or selects the appropriate linked array + +\return 0 for success, or error code if requested code is not available +*/ +short hpi_dsp_code_open( + /** Code identifier, usually adapter family */ + u32 adapter, + /** Pointer to DSP code control structure */ + struct dsp_code *ps_dsp_code, + /** Pointer to dword to receive OS specific error code */ + u32 *pos_error_code); + +/** Close the DSP code file */ +void hpi_dsp_code_close(struct dsp_code *ps_dsp_code); + +/** Rewind to the beginning of the DSP code file (for verify) */ +void hpi_dsp_code_rewind(struct dsp_code *ps_dsp_code); + +/** Read one word from the dsp code file + \return 0 for success, or error code if eof, or block length exceeded +*/ +short hpi_dsp_code_read_word(struct dsp_code *ps_dsp_code, + /**< DSP code descriptor */ + u32 *pword /**< where to store the read word */ + ); + +/** Get a block of dsp code into an internal buffer, and provide a pointer to +that buffer. (If dsp code is already an array in memory, it is referenced, +not copied.) + +\return Error if requested number of words are not available +*/ +short hpi_dsp_code_read_block(size_t words_requested, + struct dsp_code *ps_dsp_code, + /* Pointer to store (Pointer to code buffer) */ + u32 **ppblock); + +#endif diff --git a/sound/pci/asihpi/hpifunc.c b/sound/pci/asihpi/hpifunc.c new file mode 100644 index 000000000000..254c580db639 --- /dev/null +++ b/sound/pci/asihpi/hpifunc.c @@ -0,0 +1,3864 @@ + +#include "hpi_internal.h" +#include "hpimsginit.h" + +#include "hpidebug.h" + +struct hpi_handle { + unsigned int obj_index:12; + unsigned int obj_type:4; + unsigned int adapter_index:14; + unsigned int spare:1; + unsigned int read_only:1; +}; + +union handle_word { + struct hpi_handle h; + u32 w; +}; + +u32 hpi_indexes_to_handle(const char c_object, const u16 adapter_index, + const u16 object_index) +{ + union handle_word handle; + + handle.h.adapter_index = adapter_index; + handle.h.spare = 0; + handle.h.read_only = 0; + handle.h.obj_type = c_object; + handle.h.obj_index = object_index; + return handle.w; +} + +void hpi_handle_to_indexes(const u32 handle, u16 *pw_adapter_index, + u16 *pw_object_index) +{ + union handle_word uhandle; + uhandle.w = handle; + + if (pw_adapter_index) + *pw_adapter_index = (u16)uhandle.h.adapter_index; + if (pw_object_index) + *pw_object_index = (u16)uhandle.h.obj_index; +} + +char hpi_handle_object(const u32 handle) +{ + union handle_word uhandle; + uhandle.w = handle; + return (char)uhandle.h.obj_type; +} + +#define u32TOINDEX(h, i1) \ +do {\ + if (h == 0) \ + return HPI_ERROR_INVALID_OBJ; \ + else \ + hpi_handle_to_indexes(h, i1, NULL); \ +} while (0) + +#define u32TOINDEXES(h, i1, i2) \ +do {\ + if (h == 0) \ + return HPI_ERROR_INVALID_OBJ; \ + else \ + hpi_handle_to_indexes(h, i1, i2);\ +} while (0) + +void hpi_format_to_msg(struct hpi_msg_format *pMF, + const struct hpi_format *pF) +{ + pMF->sample_rate = pF->sample_rate; + pMF->bit_rate = pF->bit_rate; + pMF->attributes = pF->attributes; + pMF->channels = pF->channels; + pMF->format = pF->format; +} + +static void hpi_msg_to_format(struct hpi_format *pF, + struct hpi_msg_format *pMF) +{ + pF->sample_rate = pMF->sample_rate; + pF->bit_rate = pMF->bit_rate; + pF->attributes = pMF->attributes; + pF->channels = pMF->channels; + pF->format = pMF->format; + pF->mode_legacy = 0; + pF->unused = 0; +} + +void hpi_stream_response_to_legacy(struct hpi_stream_res *pSR) +{ + pSR->u.legacy_stream_info.auxiliary_data_available = + pSR->u.stream_info.auxiliary_data_available; + pSR->u.legacy_stream_info.state = pSR->u.stream_info.state; +} + +static struct hpi_hsubsys gh_subsys; + +struct hpi_hsubsys *hpi_subsys_create(void + ) +{ + struct hpi_message hm; + struct hpi_response hr; + + memset(&gh_subsys, 0, sizeof(struct hpi_hsubsys)); + + { + hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM, + HPI_SUBSYS_OPEN); + hpi_send_recv(&hm, &hr); + + if (hr.error == 0) + return &gh_subsys; + + } + return NULL; +} + +void hpi_subsys_free(const struct hpi_hsubsys *ph_subsys) +{ + struct hpi_message hm; + struct hpi_response hr; + + hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM, + HPI_SUBSYS_CLOSE); + hpi_send_recv(&hm, &hr); + +} + +u16 hpi_subsys_get_version(const struct hpi_hsubsys *ph_subsys, u32 *pversion) +{ + struct hpi_message hm; + struct hpi_response hr; + + hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM, + HPI_SUBSYS_GET_VERSION); + hpi_send_recv(&hm, &hr); + *pversion = hr.u.s.version; + return hr.error; +} + +u16 hpi_subsys_get_version_ex(const struct hpi_hsubsys *ph_subsys, + u32 *pversion_ex) +{ + struct hpi_message hm; + struct hpi_response hr; + + hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM, + HPI_SUBSYS_GET_VERSION); + hpi_send_recv(&hm, &hr); + *pversion_ex = hr.u.s.data; + return hr.error; +} + +u16 hpi_subsys_get_info(const struct hpi_hsubsys *ph_subsys, u32 *pversion, + u16 *pw_num_adapters, u16 aw_adapter_list[], u16 list_length) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM, + HPI_SUBSYS_GET_INFO); + + hpi_send_recv(&hm, &hr); + + *pversion = hr.u.s.version; + if (list_length > HPI_MAX_ADAPTERS) + memcpy(aw_adapter_list, &hr.u.s.aw_adapter_list, + HPI_MAX_ADAPTERS); + else + memcpy(aw_adapter_list, &hr.u.s.aw_adapter_list, list_length); + *pw_num_adapters = hr.u.s.num_adapters; + return hr.error; +} + +u16 hpi_subsys_find_adapters(const struct hpi_hsubsys *ph_subsys, + u16 *pw_num_adapters, u16 aw_adapter_list[], u16 list_length) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM, + HPI_SUBSYS_FIND_ADAPTERS); + + hpi_send_recv(&hm, &hr); + + if (list_length > HPI_MAX_ADAPTERS) { + memcpy(aw_adapter_list, &hr.u.s.aw_adapter_list, + HPI_MAX_ADAPTERS * sizeof(u16)); + memset(&aw_adapter_list[HPI_MAX_ADAPTERS], 0, + (list_length - HPI_MAX_ADAPTERS) * sizeof(u16)); + } else + memcpy(aw_adapter_list, &hr.u.s.aw_adapter_list, + list_length * sizeof(u16)); + *pw_num_adapters = hr.u.s.num_adapters; + + return hr.error; +} + +u16 hpi_subsys_create_adapter(const struct hpi_hsubsys *ph_subsys, + const struct hpi_resource *p_resource, u16 *pw_adapter_index) +{ + struct hpi_message hm; + struct hpi_response hr; + + hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM, + HPI_SUBSYS_CREATE_ADAPTER); + hm.u.s.resource = *p_resource; + + hpi_send_recv(&hm, &hr); + + *pw_adapter_index = hr.u.s.adapter_index; + return hr.error; +} + +u16 hpi_subsys_delete_adapter(const struct hpi_hsubsys *ph_subsys, + u16 adapter_index) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM, + HPI_SUBSYS_DELETE_ADAPTER); + hm.adapter_index = adapter_index; + hpi_send_recv(&hm, &hr); + return hr.error; +} + +u16 hpi_subsys_get_num_adapters(const struct hpi_hsubsys *ph_subsys, + int *pn_num_adapters) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM, + HPI_SUBSYS_GET_NUM_ADAPTERS); + hpi_send_recv(&hm, &hr); + *pn_num_adapters = (int)hr.u.s.num_adapters; + return hr.error; +} + +u16 hpi_subsys_get_adapter(const struct hpi_hsubsys *ph_subsys, int iterator, + u32 *padapter_index, u16 *pw_adapter_type) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM, + HPI_SUBSYS_GET_ADAPTER); + hm.adapter_index = (u16)iterator; + hpi_send_recv(&hm, &hr); + *padapter_index = (int)hr.u.s.adapter_index; + *pw_adapter_type = hr.u.s.aw_adapter_list[0]; + return hr.error; +} + +u16 hpi_subsys_set_host_network_interface(const struct hpi_hsubsys *ph_subsys, + const char *sz_interface) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM, + HPI_SUBSYS_SET_NETWORK_INTERFACE); + if (sz_interface == NULL) + return HPI_ERROR_INVALID_RESOURCE; + hm.u.s.resource.r.net_if = sz_interface; + hpi_send_recv(&hm, &hr); + return hr.error; +} + +u16 hpi_adapter_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, + HPI_ADAPTER_OPEN); + hm.adapter_index = adapter_index; + + hpi_send_recv(&hm, &hr); + + return hr.error; + +} + +u16 hpi_adapter_close(const struct hpi_hsubsys *ph_subsys, u16 adapter_index) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, + HPI_ADAPTER_CLOSE); + hm.adapter_index = adapter_index; + + hpi_send_recv(&hm, &hr); + + return hr.error; +} + +u16 hpi_adapter_set_mode(const struct hpi_hsubsys *ph_subsys, + u16 adapter_index, u32 adapter_mode) +{ + return hpi_adapter_set_mode_ex(ph_subsys, adapter_index, adapter_mode, + HPI_ADAPTER_MODE_SET); +} + +u16 hpi_adapter_set_mode_ex(const struct hpi_hsubsys *ph_subsys, + u16 adapter_index, u32 adapter_mode, u16 query_or_set) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, + HPI_ADAPTER_SET_MODE); + hm.adapter_index = adapter_index; + hm.u.a.adapter_mode = adapter_mode; + hm.u.a.assert_id = query_or_set; + hpi_send_recv(&hm, &hr); + return hr.error; +} + +u16 hpi_adapter_get_mode(const struct hpi_hsubsys *ph_subsys, + u16 adapter_index, u32 *padapter_mode) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, + HPI_ADAPTER_GET_MODE); + hm.adapter_index = adapter_index; + hpi_send_recv(&hm, &hr); + if (padapter_mode) + *padapter_mode = hr.u.a.serial_number; + return hr.error; +} + +u16 hpi_adapter_get_info(const struct hpi_hsubsys *ph_subsys, + u16 adapter_index, u16 *pw_num_outstreams, u16 *pw_num_instreams, + u16 *pw_version, u32 *pserial_number, u16 *pw_adapter_type) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, + HPI_ADAPTER_GET_INFO); + hm.adapter_index = adapter_index; + + hpi_send_recv(&hm, &hr); + + *pw_adapter_type = hr.u.a.adapter_type; + *pw_num_outstreams = hr.u.a.num_outstreams; + *pw_num_instreams = hr.u.a.num_instreams; + *pw_version = hr.u.a.version; + *pserial_number = hr.u.a.serial_number; + return hr.error; +} + +u16 hpi_adapter_get_module_by_index(const struct hpi_hsubsys *ph_subsys, + u16 adapter_index, u16 module_index, u16 *pw_num_outputs, + u16 *pw_num_inputs, u16 *pw_version, u32 *pserial_number, + u16 *pw_module_type, u32 *ph_module) +{ + struct hpi_message hm; + struct hpi_response hr; + + hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, + HPI_ADAPTER_MODULE_INFO); + hm.adapter_index = adapter_index; + hm.u.ax.module_info.index = module_index; + + hpi_send_recv(&hm, &hr); + + *pw_module_type = hr.u.a.adapter_type; + *pw_num_outputs = hr.u.a.num_outstreams; + *pw_num_inputs = hr.u.a.num_instreams; + *pw_version = hr.u.a.version; + *pserial_number = hr.u.a.serial_number; + *ph_module = 0; + + return hr.error; +} + +u16 hpi_adapter_get_assert(const struct hpi_hsubsys *ph_subsys, + u16 adapter_index, u16 *assert_present, char *psz_assert, + u16 *pw_line_number) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, + HPI_ADAPTER_GET_ASSERT); + hm.adapter_index = adapter_index; + hpi_send_recv(&hm, &hr); + + *assert_present = 0; + + if (!hr.error) { + + *pw_line_number = (u16)hr.u.a.serial_number; + if (*pw_line_number) { + + int i; + char *src = (char *)hr.u.a.sz_adapter_assert; + char *dst = psz_assert; + + *assert_present = 1; + + for (i = 0; i < HPI_STRING_LEN; i++) { + char c; + c = *src++; + *dst++ = c; + if (c == 0) + break; + } + + } + } + return hr.error; +} + +u16 hpi_adapter_get_assert_ex(const struct hpi_hsubsys *ph_subsys, + u16 adapter_index, u16 *assert_present, char *psz_assert, + u32 *pline_number, u16 *pw_assert_on_dsp) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, + HPI_ADAPTER_GET_ASSERT); + hm.adapter_index = adapter_index; + + hpi_send_recv(&hm, &hr); + + *assert_present = 0; + + if (!hr.error) { + + *pline_number = hr.u.a.serial_number; + + *assert_present = hr.u.a.adapter_type; + + *pw_assert_on_dsp = hr.u.a.adapter_index; + + if (!*assert_present && *pline_number) + + *assert_present = 1; + + if (*assert_present) { + + int i; + char *src = (char *)hr.u.a.sz_adapter_assert; + char *dst = psz_assert; + + for (i = 0; i < HPI_STRING_LEN; i++) { + char c; + c = *src++; + *dst++ = c; + if (c == 0) + break; + } + + } else { + *psz_assert = 0; + } + } + return hr.error; +} + +u16 hpi_adapter_test_assert(const struct hpi_hsubsys *ph_subsys, + u16 adapter_index, u16 assert_id) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, + HPI_ADAPTER_TEST_ASSERT); + hm.adapter_index = adapter_index; + hm.u.a.assert_id = assert_id; + + hpi_send_recv(&hm, &hr); + + return hr.error; +} + +u16 hpi_adapter_enable_capability(const struct hpi_hsubsys *ph_subsys, + u16 adapter_index, u16 capability, u32 key) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, + HPI_ADAPTER_ENABLE_CAPABILITY); + hm.adapter_index = adapter_index; + hm.u.a.assert_id = capability; + hm.u.a.adapter_mode = key; + + hpi_send_recv(&hm, &hr); + + return hr.error; +} + +u16 hpi_adapter_self_test(const struct hpi_hsubsys *ph_subsys, + u16 adapter_index) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, + HPI_ADAPTER_SELFTEST); + hm.adapter_index = adapter_index; + hpi_send_recv(&hm, &hr); + return hr.error; +} + +u16 hpi_adapter_debug_read(const struct hpi_hsubsys *ph_subsys, + u16 adapter_index, u32 dsp_address, char *p_buffer, int *count_bytes) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, + HPI_ADAPTER_DEBUG_READ); + + hr.size = sizeof(hr); + + hm.adapter_index = adapter_index; + hm.u.ax.debug_read.dsp_address = dsp_address; + + if (*count_bytes > sizeof(hr.u.bytes)) + *count_bytes = sizeof(hr.u.bytes); + + hm.u.ax.debug_read.count_bytes = *count_bytes; + + hpi_send_recv(&hm, &hr); + + if (!hr.error) { + *count_bytes = hr.size - 12; + memcpy(p_buffer, &hr.u.bytes, *count_bytes); + } else + *count_bytes = 0; + return hr.error; +} + +u16 hpi_adapter_set_property(const struct hpi_hsubsys *ph_subsys, + u16 adapter_index, u16 property, u16 parameter1, u16 parameter2) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, + HPI_ADAPTER_SET_PROPERTY); + hm.adapter_index = adapter_index; + hm.u.ax.property_set.property = property; + hm.u.ax.property_set.parameter1 = parameter1; + hm.u.ax.property_set.parameter2 = parameter2; + + hpi_send_recv(&hm, &hr); + + return hr.error; +} + +u16 hpi_adapter_get_property(const struct hpi_hsubsys *ph_subsys, + u16 adapter_index, u16 property, u16 *pw_parameter1, + u16 *pw_parameter2) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, + HPI_ADAPTER_GET_PROPERTY); + hm.adapter_index = adapter_index; + hm.u.ax.property_set.property = property; + + hpi_send_recv(&hm, &hr); + if (!hr.error) { + if (pw_parameter1) + *pw_parameter1 = hr.u.ax.property_get.parameter1; + if (pw_parameter2) + *pw_parameter2 = hr.u.ax.property_get.parameter2; + } + + return hr.error; +} + +u16 hpi_adapter_enumerate_property(const struct hpi_hsubsys *ph_subsys, + u16 adapter_index, u16 index, u16 what_to_enumerate, + u16 property_index, u32 *psetting) +{ + return 0; +} + +u16 hpi_format_create(struct hpi_format *p_format, u16 channels, u16 format, + u32 sample_rate, u32 bit_rate, u32 attributes) +{ + u16 error = 0; + struct hpi_msg_format fmt; + + switch (channels) { + case 1: + case 2: + case 4: + case 6: + case 8: + case 16: + break; + default: + error = HPI_ERROR_INVALID_CHANNELS; + return error; + } + fmt.channels = channels; + + switch (format) { + case HPI_FORMAT_PCM16_SIGNED: + case HPI_FORMAT_PCM24_SIGNED: + case HPI_FORMAT_PCM32_SIGNED: + case HPI_FORMAT_PCM32_FLOAT: + case HPI_FORMAT_PCM16_BIGENDIAN: + case HPI_FORMAT_PCM8_UNSIGNED: + case HPI_FORMAT_MPEG_L1: + case HPI_FORMAT_MPEG_L2: + case HPI_FORMAT_MPEG_L3: + case HPI_FORMAT_DOLBY_AC2: + case HPI_FORMAT_AA_TAGIT1_HITS: + case HPI_FORMAT_AA_TAGIT1_INSERTS: + case HPI_FORMAT_RAW_BITSTREAM: + case HPI_FORMAT_AA_TAGIT1_HITS_EX1: + case HPI_FORMAT_OEM1: + case HPI_FORMAT_OEM2: + break; + default: + error = HPI_ERROR_INVALID_FORMAT; + return error; + } + fmt.format = format; + + if (sample_rate < 8000L) { + error = HPI_ERROR_INCOMPATIBLE_SAMPLERATE; + sample_rate = 8000L; + } + if (sample_rate > 200000L) { + error = HPI_ERROR_INCOMPATIBLE_SAMPLERATE; + sample_rate = 200000L; + } + fmt.sample_rate = sample_rate; + + switch (format) { + case HPI_FORMAT_MPEG_L1: + case HPI_FORMAT_MPEG_L2: + case HPI_FORMAT_MPEG_L3: + fmt.bit_rate = bit_rate; + break; + case HPI_FORMAT_PCM16_SIGNED: + case HPI_FORMAT_PCM16_BIGENDIAN: + fmt.bit_rate = channels * sample_rate * 2; + break; + case HPI_FORMAT_PCM32_SIGNED: + case HPI_FORMAT_PCM32_FLOAT: + fmt.bit_rate = channels * sample_rate * 4; + break; + case HPI_FORMAT_PCM8_UNSIGNED: + fmt.bit_rate = channels * sample_rate; + break; + default: + fmt.bit_rate = 0; + } + + switch (format) { + case HPI_FORMAT_MPEG_L2: + if ((channels == 1) + && (attributes != HPI_MPEG_MODE_DEFAULT)) { + attributes = HPI_MPEG_MODE_DEFAULT; + error = HPI_ERROR_INVALID_FORMAT; + } else if (attributes > HPI_MPEG_MODE_DUALCHANNEL) { + attributes = HPI_MPEG_MODE_DEFAULT; + error = HPI_ERROR_INVALID_FORMAT; + } + fmt.attributes = attributes; + break; + default: + fmt.attributes = attributes; + } + + hpi_msg_to_format(p_format, &fmt); + return error; +} + +u16 hpi_stream_estimate_buffer_size(struct hpi_format *p_format, + u32 host_polling_rate_in_milli_seconds, u32 *recommended_buffer_size) +{ + + u32 bytes_per_second; + u32 size; + u16 channels; + struct hpi_format *pF = p_format; + + channels = pF->channels; + + switch (pF->format) { + case HPI_FORMAT_PCM16_BIGENDIAN: + case HPI_FORMAT_PCM16_SIGNED: + bytes_per_second = pF->sample_rate * 2L * channels; + break; + case HPI_FORMAT_PCM24_SIGNED: + bytes_per_second = pF->sample_rate * 3L * channels; + break; + case HPI_FORMAT_PCM32_SIGNED: + case HPI_FORMAT_PCM32_FLOAT: + bytes_per_second = pF->sample_rate * 4L * channels; + break; + case HPI_FORMAT_PCM8_UNSIGNED: + bytes_per_second = pF->sample_rate * 1L * channels; + break; + case HPI_FORMAT_MPEG_L1: + case HPI_FORMAT_MPEG_L2: + case HPI_FORMAT_MPEG_L3: + bytes_per_second = pF->bit_rate / 8L; + break; + case HPI_FORMAT_DOLBY_AC2: + + bytes_per_second = 256000L / 8L; + break; + default: + return HPI_ERROR_INVALID_FORMAT; + } + size = (bytes_per_second * host_polling_rate_in_milli_seconds * 2) / + 1000L; + + *recommended_buffer_size = + roundup_pow_of_two(((size + 4095L) & ~4095L)); + return 0; +} + +u16 hpi_outstream_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index, + u16 outstream_index, u32 *ph_outstream) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, + HPI_OSTREAM_OPEN); + hm.adapter_index = adapter_index; + hm.obj_index = outstream_index; + + hpi_send_recv(&hm, &hr); + + if (hr.error == 0) + *ph_outstream = + hpi_indexes_to_handle(HPI_OBJ_OSTREAM, adapter_index, + outstream_index); + else + *ph_outstream = 0; + return hr.error; +} + +u16 hpi_outstream_close(const struct hpi_hsubsys *ph_subsys, u32 h_outstream) +{ + struct hpi_message hm; + struct hpi_response hr; + + hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, + HPI_OSTREAM_HOSTBUFFER_FREE); + u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index); + hpi_send_recv(&hm, &hr); + + hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, + HPI_OSTREAM_GROUP_RESET); + u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index); + hpi_send_recv(&hm, &hr); + + hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, + HPI_OSTREAM_CLOSE); + u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index); + hpi_send_recv(&hm, &hr); + + return hr.error; +} + +u16 hpi_outstream_get_info_ex(const struct hpi_hsubsys *ph_subsys, + u32 h_outstream, u16 *pw_state, u32 *pbuffer_size, u32 *pdata_to_play, + u32 *psamples_played, u32 *pauxiliary_data_to_play) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, + HPI_OSTREAM_GET_INFO); + u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index); + + hpi_send_recv(&hm, &hr); + + if (pw_state) + *pw_state = hr.u.d.u.stream_info.state; + if (pbuffer_size) + *pbuffer_size = hr.u.d.u.stream_info.buffer_size; + if (pdata_to_play) + *pdata_to_play = hr.u.d.u.stream_info.data_available; + if (psamples_played) + *psamples_played = hr.u.d.u.stream_info.samples_transferred; + if (pauxiliary_data_to_play) + *pauxiliary_data_to_play = + hr.u.d.u.stream_info.auxiliary_data_available; + return hr.error; +} + +u16 hpi_outstream_write_buf(const struct hpi_hsubsys *ph_subsys, + u32 h_outstream, const u8 *pb_data, u32 bytes_to_write, + const struct hpi_format *p_format) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, + HPI_OSTREAM_WRITE); + u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index); + hm.u.d.u.data.pb_data = (u8 *)pb_data; + hm.u.d.u.data.data_size = bytes_to_write; + + hpi_format_to_msg(&hm.u.d.u.data.format, p_format); + + hpi_send_recv(&hm, &hr); + + return hr.error; +} + +u16 hpi_outstream_start(const struct hpi_hsubsys *ph_subsys, u32 h_outstream) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, + HPI_OSTREAM_START); + u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index); + + hpi_send_recv(&hm, &hr); + + return hr.error; +} + +u16 hpi_outstream_wait_start(const struct hpi_hsubsys *ph_subsys, + u32 h_outstream) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, + HPI_OSTREAM_WAIT_START); + u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index); + + hpi_send_recv(&hm, &hr); + + return hr.error; +} + +u16 hpi_outstream_stop(const struct hpi_hsubsys *ph_subsys, u32 h_outstream) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, + HPI_OSTREAM_STOP); + u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index); + + hpi_send_recv(&hm, &hr); + + return hr.error; +} + +u16 hpi_outstream_sinegen(const struct hpi_hsubsys *ph_subsys, + u32 h_outstream) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, + HPI_OSTREAM_SINEGEN); + u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index); + + hpi_send_recv(&hm, &hr); + + return hr.error; +} + +u16 hpi_outstream_reset(const struct hpi_hsubsys *ph_subsys, u32 h_outstream) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, + HPI_OSTREAM_RESET); + u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index); + + hpi_send_recv(&hm, &hr); + + return hr.error; +} + +u16 hpi_outstream_query_format(const struct hpi_hsubsys *ph_subsys, + u32 h_outstream, struct hpi_format *p_format) +{ + struct hpi_message hm; + struct hpi_response hr; + + hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, + HPI_OSTREAM_QUERY_FORMAT); + u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index); + + hpi_format_to_msg(&hm.u.d.u.data.format, p_format); + + hpi_send_recv(&hm, &hr); + + return hr.error; +} + +u16 hpi_outstream_set_format(const struct hpi_hsubsys *ph_subsys, + u32 h_outstream, struct hpi_format *p_format) +{ + struct hpi_message hm; + struct hpi_response hr; + + hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, + HPI_OSTREAM_SET_FORMAT); + u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index); + + hpi_format_to_msg(&hm.u.d.u.data.format, p_format); + + hpi_send_recv(&hm, &hr); + + return hr.error; +} + +u16 hpi_outstream_set_velocity(const struct hpi_hsubsys *ph_subsys, + u32 h_outstream, short velocity) +{ + struct hpi_message hm; + struct hpi_response hr; + + hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, + HPI_OSTREAM_SET_VELOCITY); + u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index); + hm.u.d.u.velocity = velocity; + + hpi_send_recv(&hm, &hr); + + return hr.error; +} + +u16 hpi_outstream_set_punch_in_out(const struct hpi_hsubsys *ph_subsys, + u32 h_outstream, u32 punch_in_sample, u32 punch_out_sample) +{ + struct hpi_message hm; + struct hpi_response hr; + + hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, + HPI_OSTREAM_SET_PUNCHINOUT); + u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index); + + hm.u.d.u.pio.punch_in_sample = punch_in_sample; + hm.u.d.u.pio.punch_out_sample = punch_out_sample; + + hpi_send_recv(&hm, &hr); + + return hr.error; +} + +u16 hpi_outstream_ancillary_reset(const struct hpi_hsubsys *ph_subsys, + u32 h_outstream, u16 mode) +{ + struct hpi_message hm; + struct hpi_response hr; + + hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, + HPI_OSTREAM_ANC_RESET); + u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index); + hm.u.d.u.data.format.channels = mode; + hpi_send_recv(&hm, &hr); + return hr.error; +} + +u16 hpi_outstream_ancillary_get_info(const struct hpi_hsubsys *ph_subsys, + u32 h_outstream, u32 *pframes_available) +{ + struct hpi_message hm; + struct hpi_response hr; + + hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, + HPI_OSTREAM_ANC_GET_INFO); + u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index); + hpi_send_recv(&hm, &hr); + if (hr.error == 0) { + if (pframes_available) + *pframes_available = + hr.u.d.u.stream_info.data_available / + sizeof(struct hpi_anc_frame); + } + return hr.error; +} + +u16 hpi_outstream_ancillary_read(const struct hpi_hsubsys *ph_subsys, + u32 h_outstream, struct hpi_anc_frame *p_anc_frame_buffer, + u32 anc_frame_buffer_size_in_bytes, + u32 number_of_ancillary_frames_to_read) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, + HPI_OSTREAM_ANC_READ); + u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index); + hm.u.d.u.data.pb_data = (u8 *)p_anc_frame_buffer; + hm.u.d.u.data.data_size = + number_of_ancillary_frames_to_read * + sizeof(struct hpi_anc_frame); + if (hm.u.d.u.data.data_size <= anc_frame_buffer_size_in_bytes) + hpi_send_recv(&hm, &hr); + else + hr.error = HPI_ERROR_INVALID_DATA_TRANSFER; + return hr.error; +} + +u16 hpi_outstream_set_time_scale(const struct hpi_hsubsys *ph_subsys, + u32 h_outstream, u32 time_scale) +{ + struct hpi_message hm; + struct hpi_response hr; + + hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, + HPI_OSTREAM_SET_TIMESCALE); + u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index); + + hm.u.d.u.time_scale = time_scale; + + hpi_send_recv(&hm, &hr); + + return hr.error; +} + +u16 hpi_outstream_host_buffer_allocate(const struct hpi_hsubsys *ph_subsys, + u32 h_outstream, u32 size_in_bytes) +{ + struct hpi_message hm; + struct hpi_response hr; + + hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, + HPI_OSTREAM_HOSTBUFFER_ALLOC); + u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index); + hm.u.d.u.data.data_size = size_in_bytes; + hpi_send_recv(&hm, &hr); + return hr.error; +} + +u16 hpi_outstream_host_buffer_get_info(const struct hpi_hsubsys *ph_subsys, + u32 h_outstream, u8 **pp_buffer, + struct hpi_hostbuffer_status **pp_status) +{ + struct hpi_message hm; + struct hpi_response hr; + + hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, + HPI_OSTREAM_HOSTBUFFER_GET_INFO); + u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index); + hpi_send_recv(&hm, &hr); + + if (hr.error == 0) { + if (pp_buffer) + *pp_buffer = hr.u.d.u.hostbuffer_info.p_buffer; + if (pp_status) + *pp_status = hr.u.d.u.hostbuffer_info.p_status; + } + return hr.error; +} + +u16 hpi_outstream_host_buffer_free(const struct hpi_hsubsys *ph_subsys, + u32 h_outstream) +{ + struct hpi_message hm; + struct hpi_response hr; + + hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, + HPI_OSTREAM_HOSTBUFFER_FREE); + u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index); + hpi_send_recv(&hm, &hr); + return hr.error; +} + +u16 hpi_outstream_group_add(const struct hpi_hsubsys *ph_subsys, + u32 h_outstream, u32 h_stream) +{ + struct hpi_message hm; + struct hpi_response hr; + u16 adapter; + char c_obj_type; + + hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, + HPI_OSTREAM_GROUP_ADD); + hr.error = 0; + u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index); + c_obj_type = hpi_handle_object(h_stream); + switch (c_obj_type) { + case HPI_OBJ_OSTREAM: + hm.u.d.u.stream.object_type = HPI_OBJ_OSTREAM; + u32TOINDEXES(h_stream, &adapter, + &hm.u.d.u.stream.stream_index); + break; + case HPI_OBJ_ISTREAM: + hm.u.d.u.stream.object_type = HPI_OBJ_ISTREAM; + u32TOINDEXES(h_stream, &adapter, + &hm.u.d.u.stream.stream_index); + break; + default: + return HPI_ERROR_INVALID_STREAM; + } + if (adapter != hm.adapter_index) + return HPI_ERROR_NO_INTERADAPTER_GROUPS; + + hpi_send_recv(&hm, &hr); + return hr.error; +} + +u16 hpi_outstream_group_get_map(const struct hpi_hsubsys *ph_subsys, + u32 h_outstream, u32 *poutstream_map, u32 *pinstream_map) +{ + struct hpi_message hm; + struct hpi_response hr; + + hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, + HPI_OSTREAM_GROUP_GETMAP); + u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index); + hpi_send_recv(&hm, &hr); + + if (poutstream_map) + *poutstream_map = hr.u.d.u.group_info.outstream_group_map; + if (pinstream_map) + *pinstream_map = hr.u.d.u.group_info.instream_group_map; + + return hr.error; +} + +u16 hpi_outstream_group_reset(const struct hpi_hsubsys *ph_subsys, + u32 h_outstream) +{ + struct hpi_message hm; + struct hpi_response hr; + + hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, + HPI_OSTREAM_GROUP_RESET); + u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index); + hpi_send_recv(&hm, &hr); + return hr.error; +} + +u16 hpi_instream_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index, + u16 instream_index, u32 *ph_instream) +{ + struct hpi_message hm; + struct hpi_response hr; + + hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM, + HPI_ISTREAM_OPEN); + hm.adapter_index = adapter_index; + hm.obj_index = instream_index; + + hpi_send_recv(&hm, &hr); + + if (hr.error == 0) + *ph_instream = + hpi_indexes_to_handle(HPI_OBJ_ISTREAM, adapter_index, + instream_index); + else + *ph_instream = 0; + + return hr.error; +} + +u16 hpi_instream_close(const struct hpi_hsubsys *ph_subsys, u32 h_instream) +{ + struct hpi_message hm; + struct hpi_response hr; + + hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM, + HPI_ISTREAM_HOSTBUFFER_FREE); + u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index); + hpi_send_recv(&hm, &hr); + + hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM, + HPI_ISTREAM_GROUP_RESET); + u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index); + hpi_send_recv(&hm, &hr); + + hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM, + HPI_ISTREAM_CLOSE); + u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index); + hpi_send_recv(&hm, &hr); + + return hr.error; +} + +u16 hpi_instream_query_format(const struct hpi_hsubsys *ph_subsys, + u32 h_instream, const struct hpi_format *p_format) +{ + struct hpi_message hm; + struct hpi_response hr; + + hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM, + HPI_ISTREAM_QUERY_FORMAT); + u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index); + hpi_format_to_msg(&hm.u.d.u.data.format, p_format); + + hpi_send_recv(&hm, &hr); + + return hr.error; +} + +u16 hpi_instream_set_format(const struct hpi_hsubsys *ph_subsys, + u32 h_instream, const struct hpi_format *p_format) +{ + struct hpi_message hm; + struct hpi_response hr; + + hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM, + HPI_ISTREAM_SET_FORMAT); + u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index); + hpi_format_to_msg(&hm.u.d.u.data.format, p_format); + + hpi_send_recv(&hm, &hr); + + return hr.error; +} + +u16 hpi_instream_read_buf(const struct hpi_hsubsys *ph_subsys, u32 h_instream, + u8 *pb_data, u32 bytes_to_read) +{ + struct hpi_message hm; + struct hpi_response hr; + + hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM, + HPI_ISTREAM_READ); + u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index); + hm.u.d.u.data.data_size = bytes_to_read; + hm.u.d.u.data.pb_data = pb_data; + + hpi_send_recv(&hm, &hr); + + return hr.error; +} + +u16 hpi_instream_start(const struct hpi_hsubsys *ph_subsys, u32 h_instream) +{ + struct hpi_message hm; + struct hpi_response hr; + + hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM, + HPI_ISTREAM_START); + u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index); + + hpi_send_recv(&hm, &hr); + + return hr.error; +} + +u16 hpi_instream_wait_start(const struct hpi_hsubsys *ph_subsys, + u32 h_instream) +{ + struct hpi_message hm; + struct hpi_response hr; + + hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM, + HPI_ISTREAM_WAIT_START); + u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index); + + hpi_send_recv(&hm, &hr); + + return hr.error; +} + +u16 hpi_instream_stop(const struct hpi_hsubsys *ph_subsys, u32 h_instream) +{ + struct hpi_message hm; + struct hpi_response hr; + + hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM, + HPI_ISTREAM_STOP); + u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index); + + hpi_send_recv(&hm, &hr); + + return hr.error; +} + +u16 hpi_instream_reset(const struct hpi_hsubsys *ph_subsys, u32 h_instream) +{ + struct hpi_message hm; + struct hpi_response hr; + + hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM, + HPI_ISTREAM_RESET); + u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index); + + hpi_send_recv(&hm, &hr); + + return hr.error; +} + +u16 hpi_instream_get_info_ex(const struct hpi_hsubsys *ph_subsys, + u32 h_instream, u16 *pw_state, u32 *pbuffer_size, u32 *pdata_recorded, + u32 *psamples_recorded, u32 *pauxiliary_data_recorded) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM, + HPI_ISTREAM_GET_INFO); + u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index); + + hpi_send_recv(&hm, &hr); + + if (pw_state) + *pw_state = hr.u.d.u.stream_info.state; + if (pbuffer_size) + *pbuffer_size = hr.u.d.u.stream_info.buffer_size; + if (pdata_recorded) + *pdata_recorded = hr.u.d.u.stream_info.data_available; + if (psamples_recorded) + *psamples_recorded = hr.u.d.u.stream_info.samples_transferred; + if (pauxiliary_data_recorded) + *pauxiliary_data_recorded = + hr.u.d.u.stream_info.auxiliary_data_available; + return hr.error; +} + +u16 hpi_instream_ancillary_reset(const struct hpi_hsubsys *ph_subsys, + u32 h_instream, u16 bytes_per_frame, u16 mode, u16 alignment, + u16 idle_bit) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM, + HPI_ISTREAM_ANC_RESET); + u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index); + hm.u.d.u.data.format.attributes = bytes_per_frame; + hm.u.d.u.data.format.format = (mode << 8) | (alignment & 0xff); + hm.u.d.u.data.format.channels = idle_bit; + hpi_send_recv(&hm, &hr); + return hr.error; +} + +u16 hpi_instream_ancillary_get_info(const struct hpi_hsubsys *ph_subsys, + u32 h_instream, u32 *pframe_space) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM, + HPI_ISTREAM_ANC_GET_INFO); + u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index); + hpi_send_recv(&hm, &hr); + if (pframe_space) + *pframe_space = + (hr.u.d.u.stream_info.buffer_size - + hr.u.d.u.stream_info.data_available) / + sizeof(struct hpi_anc_frame); + return hr.error; +} + +u16 hpi_instream_ancillary_write(const struct hpi_hsubsys *ph_subsys, + u32 h_instream, const struct hpi_anc_frame *p_anc_frame_buffer, + u32 anc_frame_buffer_size_in_bytes, + u32 number_of_ancillary_frames_to_write) +{ + struct hpi_message hm; + struct hpi_response hr; + + hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM, + HPI_ISTREAM_ANC_WRITE); + u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index); + hm.u.d.u.data.pb_data = (u8 *)p_anc_frame_buffer; + hm.u.d.u.data.data_size = + number_of_ancillary_frames_to_write * + sizeof(struct hpi_anc_frame); + if (hm.u.d.u.data.data_size <= anc_frame_buffer_size_in_bytes) + hpi_send_recv(&hm, &hr); + else + hr.error = HPI_ERROR_INVALID_DATA_TRANSFER; + return hr.error; +} + +u16 hpi_instream_host_buffer_allocate(const struct hpi_hsubsys *ph_subsys, + u32 h_instream, u32 size_in_bytes) +{ + + struct hpi_message hm; + struct hpi_response hr; + + hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM, + HPI_ISTREAM_HOSTBUFFER_ALLOC); + u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index); + hm.u.d.u.data.data_size = size_in_bytes; + hpi_send_recv(&hm, &hr); + return hr.error; +} + +u16 hpi_instream_host_buffer_get_info(const struct hpi_hsubsys *ph_subsys, + u32 h_instream, u8 **pp_buffer, + struct hpi_hostbuffer_status **pp_status) +{ + struct hpi_message hm; + struct hpi_response hr; + + hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM, + HPI_ISTREAM_HOSTBUFFER_GET_INFO); + u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index); + hpi_send_recv(&hm, &hr); + + if (hr.error == 0) { + if (pp_buffer) + *pp_buffer = hr.u.d.u.hostbuffer_info.p_buffer; + if (pp_status) + *pp_status = hr.u.d.u.hostbuffer_info.p_status; + } + return hr.error; +} + +u16 hpi_instream_host_buffer_free(const struct hpi_hsubsys *ph_subsys, + u32 h_instream) +{ + + struct hpi_message hm; + struct hpi_response hr; + + hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM, + HPI_ISTREAM_HOSTBUFFER_FREE); + u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index); + hpi_send_recv(&hm, &hr); + return hr.error; +} + +u16 hpi_instream_group_add(const struct hpi_hsubsys *ph_subsys, + u32 h_instream, u32 h_stream) +{ + struct hpi_message hm; + struct hpi_response hr; + u16 adapter; + char c_obj_type; + + hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM, + HPI_ISTREAM_GROUP_ADD); + hr.error = 0; + u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index); + c_obj_type = hpi_handle_object(h_stream); + + switch (c_obj_type) { + case HPI_OBJ_OSTREAM: + hm.u.d.u.stream.object_type = HPI_OBJ_OSTREAM; + u32TOINDEXES(h_stream, &adapter, + &hm.u.d.u.stream.stream_index); + break; + case HPI_OBJ_ISTREAM: + hm.u.d.u.stream.object_type = HPI_OBJ_ISTREAM; + u32TOINDEXES(h_stream, &adapter, + &hm.u.d.u.stream.stream_index); + break; + default: + return HPI_ERROR_INVALID_STREAM; + } + + if (adapter != hm.adapter_index) + return HPI_ERROR_NO_INTERADAPTER_GROUPS; + + hpi_send_recv(&hm, &hr); + return hr.error; +} + +u16 hpi_instream_group_get_map(const struct hpi_hsubsys *ph_subsys, + u32 h_instream, u32 *poutstream_map, u32 *pinstream_map) +{ + struct hpi_message hm; + struct hpi_response hr; + + hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM, + HPI_ISTREAM_HOSTBUFFER_FREE); + u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index); + hpi_send_recv(&hm, &hr); + + if (poutstream_map) + *poutstream_map = hr.u.d.u.group_info.outstream_group_map; + if (pinstream_map) + *pinstream_map = hr.u.d.u.group_info.instream_group_map; + + return hr.error; +} + +u16 hpi_instream_group_reset(const struct hpi_hsubsys *ph_subsys, + u32 h_instream) +{ + struct hpi_message hm; + struct hpi_response hr; + + hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM, + HPI_ISTREAM_GROUP_RESET); + u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index); + hpi_send_recv(&hm, &hr); + return hr.error; +} + +u16 hpi_mixer_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index, + u32 *ph_mixer) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_MIXER, HPI_MIXER_OPEN); + hm.adapter_index = adapter_index; + + hpi_send_recv(&hm, &hr); + + if (hr.error == 0) + *ph_mixer = + hpi_indexes_to_handle(HPI_OBJ_MIXER, adapter_index, + 0); + else + *ph_mixer = 0; + return hr.error; +} + +u16 hpi_mixer_close(const struct hpi_hsubsys *ph_subsys, u32 h_mixer) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_MIXER, HPI_MIXER_CLOSE); + u32TOINDEX(h_mixer, &hm.adapter_index); + hpi_send_recv(&hm, &hr); + return hr.error; +} + +u16 hpi_mixer_get_control(const struct hpi_hsubsys *ph_subsys, u32 h_mixer, + u16 src_node_type, u16 src_node_type_index, u16 dst_node_type, + u16 dst_node_type_index, u16 control_type, u32 *ph_control) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_MIXER, + HPI_MIXER_GET_CONTROL); + u32TOINDEX(h_mixer, &hm.adapter_index); + hm.u.m.node_type1 = src_node_type; + hm.u.m.node_index1 = src_node_type_index; + hm.u.m.node_type2 = dst_node_type; + hm.u.m.node_index2 = dst_node_type_index; + hm.u.m.control_type = control_type; + + hpi_send_recv(&hm, &hr); + + if (hr.error == 0) + *ph_control = + hpi_indexes_to_handle(HPI_OBJ_CONTROL, + hm.adapter_index, hr.u.m.control_index); + else + *ph_control = 0; + return hr.error; +} + +u16 hpi_mixer_get_control_by_index(const struct hpi_hsubsys *ph_subsys, + u32 h_mixer, u16 control_index, u16 *pw_src_node_type, + u16 *pw_src_node_index, u16 *pw_dst_node_type, u16 *pw_dst_node_index, + u16 *pw_control_type, u32 *ph_control) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_MIXER, + HPI_MIXER_GET_CONTROL_BY_INDEX); + u32TOINDEX(h_mixer, &hm.adapter_index); + hm.u.m.control_index = control_index; + hpi_send_recv(&hm, &hr); + + if (pw_src_node_type) { + *pw_src_node_type = + hr.u.m.src_node_type + HPI_SOURCENODE_NONE; + *pw_src_node_index = hr.u.m.src_node_index; + *pw_dst_node_type = hr.u.m.dst_node_type + HPI_DESTNODE_NONE; + *pw_dst_node_index = hr.u.m.dst_node_index; + } + if (pw_control_type) + *pw_control_type = hr.u.m.control_index; + + if (ph_control) { + if (hr.error == 0) + *ph_control = + hpi_indexes_to_handle(HPI_OBJ_CONTROL, + hm.adapter_index, control_index); + else + *ph_control = 0; + } + return hr.error; +} + +u16 hpi_mixer_store(const struct hpi_hsubsys *ph_subsys, u32 h_mixer, + enum HPI_MIXER_STORE_COMMAND command, u16 index) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_MIXER, HPI_MIXER_STORE); + u32TOINDEX(h_mixer, &hm.adapter_index); + hm.u.mx.store.command = command; + hm.u.mx.store.index = index; + hpi_send_recv(&hm, &hr); + return hr.error; +} + +static +u16 hpi_control_param_set(const struct hpi_hsubsys *ph_subsys, + const u32 h_control, const u16 attrib, const u32 param1, + const u32 param2) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, + HPI_CONTROL_SET_STATE); + u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + hm.u.c.attribute = attrib; + hm.u.c.param1 = param1; + hm.u.c.param2 = param2; + hpi_send_recv(&hm, &hr); + return hr.error; +} + +static +u16 hpi_control_param_get(const struct hpi_hsubsys *ph_subsys, + const u32 h_control, const u16 attrib, u32 param1, u32 param2, + u32 *pparam1, u32 *pparam2) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, + HPI_CONTROL_GET_STATE); + u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + hm.u.c.attribute = attrib; + hm.u.c.param1 = param1; + hm.u.c.param2 = param2; + hpi_send_recv(&hm, &hr); + if (pparam1) + *pparam1 = hr.u.c.param1; + if (pparam2) + *pparam2 = hr.u.c.param2; + + return hr.error; +} + +#define hpi_control_param1_get(s, h, a, p1) \ + hpi_control_param_get(s, h, a, 0, 0, p1, NULL) +#define hpi_control_param2_get(s, h, a, p1, p2) \ + hpi_control_param_get(s, h, a, 0, 0, p1, p2) +#define hpi_control_ex_param1_get(s, h, a, p1) \ + hpi_control_ex_param_get(s, h, a, 0, 0, p1, NULL) +#define hpi_control_ex_param2_get(s, h, a, p1, p2) \ + hpi_control_ex_param_get(s, h, a, 0, 0, p1, p2) + +static +u16 hpi_control_query(const struct hpi_hsubsys *ph_subsys, + const u32 h_control, const u16 attrib, const u32 index, + const u32 param, u32 *psetting) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, + HPI_CONTROL_GET_INFO); + u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + + hm.u.c.attribute = attrib; + hm.u.c.param1 = index; + hm.u.c.param2 = param; + + hpi_send_recv(&hm, &hr); + *psetting = hr.u.c.param1; + + return hr.error; +} + +static u16 hpi_control_get_string(const struct hpi_hsubsys *ph_subsys, + const u32 h_control, const u16 attribute, char *psz_string, + const u32 string_length) +{ + unsigned int sub_string_index = 0, j = 0; + char c = 0; + unsigned int n = 0; + u16 hE = 0; + + if ((string_length < 1) || (string_length > 256)) + return HPI_ERROR_INVALID_CONTROL_VALUE; + for (sub_string_index = 0; sub_string_index < string_length; + sub_string_index += 8) { + struct hpi_message hm; + struct hpi_response hr; + + hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, + HPI_CONTROL_GET_STATE); + u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + hm.u.c.attribute = attribute; + hm.u.c.param1 = sub_string_index; + hm.u.c.param2 = 0; + hpi_send_recv(&hm, &hr); + + if (sub_string_index == 0 + && (hr.u.cu.chars8.remaining_chars + 8) > + string_length) + return HPI_ERROR_INVALID_CONTROL_VALUE; + + if (hr.error) { + hE = hr.error; + break; + } + for (j = 0; j < 8; j++) { + c = hr.u.cu.chars8.sz_data[j]; + psz_string[sub_string_index + j] = c; + n++; + if (n >= string_length) { + psz_string[string_length - 1] = 0; + hE = HPI_ERROR_INVALID_CONTROL_VALUE; + break; + } + if (c == 0) + break; + } + + if ((hr.u.cu.chars8.remaining_chars == 0) + && ((sub_string_index + j) < string_length) + && (c != 0)) { + c = 0; + psz_string[sub_string_index + j] = c; + } + if (c == 0) + break; + } + return hE; +} + +u16 HPI_AESEBU__receiver_query_format(const struct hpi_hsubsys *ph_subsys, + const u32 h_aes_rx, const u32 index, u16 *pw_format) +{ + u32 qr; + u16 err; + + err = hpi_control_query(ph_subsys, h_aes_rx, HPI_AESEBURX_FORMAT, + index, 0, &qr); + *pw_format = (u16)qr; + return err; +} + +u16 HPI_AESEBU__receiver_set_format(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 format) +{ + return hpi_control_param_set(ph_subsys, h_control, + HPI_AESEBURX_FORMAT, format, 0); +} + +u16 HPI_AESEBU__receiver_get_format(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 *pw_format) +{ + u16 err; + u32 param; + + err = hpi_control_param1_get(ph_subsys, h_control, + HPI_AESEBURX_FORMAT, ¶m); + if (!err && pw_format) + *pw_format = (u16)param; + + return err; +} + +u16 HPI_AESEBU__receiver_get_sample_rate(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 *psample_rate) +{ + return hpi_control_param1_get(ph_subsys, h_control, + HPI_AESEBURX_SAMPLERATE, psample_rate); +} + +u16 HPI_AESEBU__receiver_get_user_data(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 index, u16 *pw_data) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, + HPI_CONTROL_GET_STATE); + u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + hm.u.c.attribute = HPI_AESEBURX_USERDATA; + hm.u.c.param1 = index; + + hpi_send_recv(&hm, &hr); + + if (pw_data) + *pw_data = (u16)hr.u.c.param2; + return hr.error; +} + +u16 HPI_AESEBU__receiver_get_channel_status(const struct hpi_hsubsys + *ph_subsys, u32 h_control, u16 index, u16 *pw_data) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, + HPI_CONTROL_GET_STATE); + u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + hm.u.c.attribute = HPI_AESEBURX_CHANNELSTATUS; + hm.u.c.param1 = index; + + hpi_send_recv(&hm, &hr); + + if (pw_data) + *pw_data = (u16)hr.u.c.param2; + return hr.error; +} + +u16 HPI_AESEBU__receiver_get_error_status(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 *pw_error_data) +{ + u32 error_data = 0; + u16 error = 0; + + error = hpi_control_param1_get(ph_subsys, h_control, + HPI_AESEBURX_ERRORSTATUS, &error_data); + if (pw_error_data) + *pw_error_data = (u16)error_data; + return error; +} + +u16 HPI_AESEBU__transmitter_set_sample_rate(const struct hpi_hsubsys + *ph_subsys, u32 h_control, u32 sample_rate) +{ + return hpi_control_param_set(ph_subsys, h_control, + HPI_AESEBUTX_SAMPLERATE, sample_rate, 0); +} + +u16 HPI_AESEBU__transmitter_set_user_data(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 index, u16 data) +{ + return hpi_control_param_set(ph_subsys, h_control, + HPI_AESEBUTX_USERDATA, index, data); +} + +u16 HPI_AESEBU__transmitter_set_channel_status(const struct hpi_hsubsys + *ph_subsys, u32 h_control, u16 index, u16 data) +{ + return hpi_control_param_set(ph_subsys, h_control, + HPI_AESEBUTX_CHANNELSTATUS, index, data); +} + +u16 HPI_AESEBU__transmitter_get_channel_status(const struct hpi_hsubsys + *ph_subsys, u32 h_control, u16 index, u16 *pw_data) +{ + return HPI_ERROR_INVALID_OPERATION; +} + +u16 HPI_AESEBU__transmitter_query_format(const struct hpi_hsubsys *ph_subsys, + const u32 h_aes_tx, const u32 index, u16 *pw_format) +{ + u32 qr; + u16 err; + + err = hpi_control_query(ph_subsys, h_aes_tx, HPI_AESEBUTX_FORMAT, + index, 0, &qr); + *pw_format = (u16)qr; + return err; +} + +u16 HPI_AESEBU__transmitter_set_format(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 output_format) +{ + return hpi_control_param_set(ph_subsys, h_control, + HPI_AESEBUTX_FORMAT, output_format, 0); +} + +u16 HPI_AESEBU__transmitter_get_format(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 *pw_output_format) +{ + u16 err; + u32 param; + + err = hpi_control_param1_get(ph_subsys, h_control, + HPI_AESEBUTX_FORMAT, ¶m); + if (!err && pw_output_format) + *pw_output_format = (u16)param; + + return err; +} + +u16 hpi_bitstream_set_clock_edge(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 edge_type) +{ + return hpi_control_param_set(ph_subsys, h_control, + HPI_BITSTREAM_CLOCK_EDGE, edge_type, 0); +} + +u16 hpi_bitstream_set_data_polarity(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 polarity) +{ + return hpi_control_param_set(ph_subsys, h_control, + HPI_BITSTREAM_DATA_POLARITY, polarity, 0); +} + +u16 hpi_bitstream_get_activity(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 *pw_clk_activity, u16 *pw_data_activity) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, + HPI_CONTROL_GET_STATE); + u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + hm.u.c.attribute = HPI_BITSTREAM_ACTIVITY; + hpi_send_recv(&hm, &hr); + if (pw_clk_activity) + *pw_clk_activity = (u16)hr.u.c.param1; + if (pw_data_activity) + *pw_data_activity = (u16)hr.u.c.param2; + return hr.error; +} + +u16 hpi_channel_mode_query_mode(const struct hpi_hsubsys *ph_subsys, + const u32 h_mode, const u32 index, u16 *pw_mode) +{ + u32 qr; + u16 err; + + err = hpi_control_query(ph_subsys, h_mode, HPI_CHANNEL_MODE_MODE, + index, 0, &qr); + *pw_mode = (u16)qr; + return err; +} + +u16 hpi_channel_mode_set(const struct hpi_hsubsys *ph_subsys, u32 h_control, + u16 mode) +{ + return hpi_control_param_set(ph_subsys, h_control, + HPI_CHANNEL_MODE_MODE, mode, 0); +} + +u16 hpi_channel_mode_get(const struct hpi_hsubsys *ph_subsys, u32 h_control, + u16 *mode) +{ + u32 mode32 = 0; + u16 error = hpi_control_param1_get(ph_subsys, h_control, + HPI_CHANNEL_MODE_MODE, &mode32); + if (mode) + *mode = (u16)mode32; + return error; +} + +u16 hpi_cobranet_hmi_write(const struct hpi_hsubsys *ph_subsys, u32 h_control, + u32 hmi_address, u32 byte_count, u8 *pb_data) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROLEX, + HPI_CONTROL_SET_STATE); + u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + + hm.u.cx.u.cobranet_data.byte_count = byte_count; + hm.u.cx.u.cobranet_data.hmi_address = hmi_address; + + if (byte_count <= 8) { + memcpy(hm.u.cx.u.cobranet_data.data, pb_data, byte_count); + hm.u.cx.attribute = HPI_COBRANET_SET; + } else { + hm.u.cx.u.cobranet_bigdata.pb_data = pb_data; + hm.u.cx.attribute = HPI_COBRANET_SET_DATA; + } + + hpi_send_recv(&hm, &hr); + + return hr.error; +} + +u16 hpi_cobranet_hmi_read(const struct hpi_hsubsys *ph_subsys, u32 h_control, + u32 hmi_address, u32 max_byte_count, u32 *pbyte_count, u8 *pb_data) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROLEX, + HPI_CONTROL_GET_STATE); + u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + + hm.u.cx.u.cobranet_data.byte_count = max_byte_count; + hm.u.cx.u.cobranet_data.hmi_address = hmi_address; + + if (max_byte_count <= 8) { + hm.u.cx.attribute = HPI_COBRANET_GET; + } else { + hm.u.cx.u.cobranet_bigdata.pb_data = pb_data; + hm.u.cx.attribute = HPI_COBRANET_GET_DATA; + } + + hpi_send_recv(&hm, &hr); + if (!hr.error && pb_data) { + + *pbyte_count = hr.u.cx.u.cobranet_data.byte_count; + + if (*pbyte_count < max_byte_count) + max_byte_count = *pbyte_count; + + if (hm.u.cx.attribute == HPI_COBRANET_GET) { + memcpy(pb_data, hr.u.cx.u.cobranet_data.data, + max_byte_count); + } else { + + } + + } + return hr.error; +} + +u16 hpi_cobranet_hmi_get_status(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 *pstatus, u32 *preadable_size, + u32 *pwriteable_size) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROLEX, + HPI_CONTROL_GET_STATE); + u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + + hm.u.cx.attribute = HPI_COBRANET_GET_STATUS; + + hpi_send_recv(&hm, &hr); + if (!hr.error) { + if (pstatus) + *pstatus = hr.u.cx.u.cobranet_status.status; + if (preadable_size) + *preadable_size = + hr.u.cx.u.cobranet_status.readable_size; + if (pwriteable_size) + *pwriteable_size = + hr.u.cx.u.cobranet_status.writeable_size; + } + return hr.error; +} + +u16 hpi_cobranet_getI_paddress(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 *pi_paddress) +{ + u32 byte_count; + u32 iP; + u16 error; + error = hpi_cobranet_hmi_read(ph_subsys, h_control, + HPI_COBRANET_HMI_cobra_ip_mon_currentIP, 4, &byte_count, + (u8 *)&iP); + + *pi_paddress = + ((iP & 0xff000000) >> 8) | ((iP & 0x00ff0000) << 8) | ((iP & + 0x0000ff00) >> 8) | ((iP & 0x000000ff) << 8); + + if (error) + *pi_paddress = 0; + + return error; + +} + +u16 hpi_cobranet_setI_paddress(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 i_paddress) +{ + u32 iP; + u16 error; + + iP = ((i_paddress & 0xff000000) >> 8) | ((i_paddress & 0x00ff0000) << + 8) | ((i_paddress & 0x0000ff00) >> 8) | ((i_paddress & + 0x000000ff) << 8); + + error = hpi_cobranet_hmi_write(ph_subsys, h_control, + HPI_COBRANET_HMI_cobra_ip_mon_currentIP, 4, (u8 *)&iP); + + return error; + +} + +u16 hpi_cobranet_get_staticI_paddress(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 *pi_paddress) +{ + u32 byte_count; + u32 iP; + u16 error; + error = hpi_cobranet_hmi_read(ph_subsys, h_control, + HPI_COBRANET_HMI_cobra_ip_mon_staticIP, 4, &byte_count, + (u8 *)&iP); + + *pi_paddress = + ((iP & 0xff000000) >> 8) | ((iP & 0x00ff0000) << 8) | ((iP & + 0x0000ff00) >> 8) | ((iP & 0x000000ff) << 8); + + if (error) + *pi_paddress = 0; + + return error; + +} + +u16 hpi_cobranet_set_staticI_paddress(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 i_paddress) +{ + u32 iP; + u16 error; + + iP = ((i_paddress & 0xff000000) >> 8) | ((i_paddress & 0x00ff0000) << + 8) | ((i_paddress & 0x0000ff00) >> 8) | ((i_paddress & + 0x000000ff) << 8); + + error = hpi_cobranet_hmi_write(ph_subsys, h_control, + HPI_COBRANET_HMI_cobra_ip_mon_staticIP, 4, (u8 *)&iP); + + return error; + +} + +u16 hpi_cobranet_getMA_caddress(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 *pmAC_MS_bs, u32 *pmAC_LS_bs) +{ + u32 byte_count; + u16 error; + u32 mAC; + error = hpi_cobranet_hmi_read(ph_subsys, h_control, + HPI_COBRANET_HMI_cobra_if_phy_address, 4, &byte_count, + (u8 *)&mAC); + *pmAC_MS_bs = + ((mAC & 0xff000000) >> 8) | ((mAC & 0x00ff0000) << 8) | ((mAC + & 0x0000ff00) >> 8) | ((mAC & 0x000000ff) << 8); + error += hpi_cobranet_hmi_read(ph_subsys, h_control, + HPI_COBRANET_HMI_cobra_if_phy_address + 1, 4, &byte_count, + (u8 *)&mAC); + *pmAC_LS_bs = + ((mAC & 0xff000000) >> 8) | ((mAC & 0x00ff0000) << 8) | ((mAC + & 0x0000ff00) >> 8) | ((mAC & 0x000000ff) << 8); + + if (error) { + *pmAC_MS_bs = 0; + *pmAC_LS_bs = 0; + } + + return error; +} + +u16 hpi_compander_set(const struct hpi_hsubsys *ph_subsys, u32 h_control, + u16 attack, u16 decay, short ratio100, short threshold0_01dB, + short makeup_gain0_01dB) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, + HPI_CONTROL_SET_STATE); + u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + + hm.u.c.param1 = attack + ((u32)ratio100 << 16); + hm.u.c.param2 = (decay & 0xFFFFL); + hm.u.c.an_log_value[0] = threshold0_01dB; + hm.u.c.an_log_value[1] = makeup_gain0_01dB; + hm.u.c.attribute = HPI_COMPANDER_PARAMS; + + hpi_send_recv(&hm, &hr); + + return hr.error; +} + +u16 hpi_compander_get(const struct hpi_hsubsys *ph_subsys, u32 h_control, + u16 *pw_attack, u16 *pw_decay, short *pw_ratio100, + short *pn_threshold0_01dB, short *pn_makeup_gain0_01dB) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, + HPI_CONTROL_GET_STATE); + u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + hm.u.c.attribute = HPI_COMPANDER_PARAMS; + + hpi_send_recv(&hm, &hr); + + if (pw_attack) + *pw_attack = (short)(hr.u.c.param1 & 0xFFFF); + if (pw_decay) + *pw_decay = (short)(hr.u.c.param2 & 0xFFFF); + if (pw_ratio100) + *pw_ratio100 = (short)(hr.u.c.param1 >> 16); + + if (pn_threshold0_01dB) + *pn_threshold0_01dB = hr.u.c.an_log_value[0]; + if (pn_makeup_gain0_01dB) + *pn_makeup_gain0_01dB = hr.u.c.an_log_value[1]; + + return hr.error; +} + +u16 hpi_level_query_range(const struct hpi_hsubsys *ph_subsys, u32 h_control, + short *min_gain_01dB, short *max_gain_01dB, short *step_gain_01dB) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, + HPI_CONTROL_GET_STATE); + u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + hm.u.c.attribute = HPI_LEVEL_RANGE; + + hpi_send_recv(&hm, &hr); + if (hr.error) { + hr.u.c.an_log_value[0] = 0; + hr.u.c.an_log_value[1] = 0; + hr.u.c.param1 = 0; + } + if (min_gain_01dB) + *min_gain_01dB = hr.u.c.an_log_value[0]; + if (max_gain_01dB) + *max_gain_01dB = hr.u.c.an_log_value[1]; + if (step_gain_01dB) + *step_gain_01dB = (short)hr.u.c.param1; + return hr.error; +} + +u16 hpi_level_set_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control, + short an_gain0_01dB[HPI_MAX_CHANNELS] + ) +{ + struct hpi_message hm; + struct hpi_response hr; + + hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, + HPI_CONTROL_SET_STATE); + u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + memcpy(hm.u.c.an_log_value, an_gain0_01dB, + sizeof(short) * HPI_MAX_CHANNELS); + hm.u.c.attribute = HPI_LEVEL_GAIN; + + hpi_send_recv(&hm, &hr); + + return hr.error; +} + +u16 hpi_level_get_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control, + short an_gain0_01dB[HPI_MAX_CHANNELS] + ) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, + HPI_CONTROL_GET_STATE); + u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + hm.u.c.attribute = HPI_LEVEL_GAIN; + + hpi_send_recv(&hm, &hr); + + memcpy(an_gain0_01dB, hr.u.c.an_log_value, + sizeof(short) * HPI_MAX_CHANNELS); + return hr.error; +} + +u16 hpi_meter_query_channels(const struct hpi_hsubsys *ph_subsys, + const u32 h_meter, u32 *p_channels) +{ + return hpi_control_query(ph_subsys, h_meter, HPI_METER_NUM_CHANNELS, + 0, 0, p_channels); +} + +u16 hpi_meter_get_peak(const struct hpi_hsubsys *ph_subsys, u32 h_control, + short an_peakdB[HPI_MAX_CHANNELS] + ) +{ + short i = 0; + + struct hpi_message hm; + struct hpi_response hr; + + hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, + HPI_CONTROL_GET_STATE); + u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + hm.obj_index = hm.obj_index; + hm.u.c.attribute = HPI_METER_PEAK; + + hpi_send_recv(&hm, &hr); + + if (!hr.error) + memcpy(an_peakdB, hr.u.c.an_log_value, + sizeof(short) * HPI_MAX_CHANNELS); + else + for (i = 0; i < HPI_MAX_CHANNELS; i++) + an_peakdB[i] = HPI_METER_MINIMUM; + return hr.error; +} + +u16 hpi_meter_get_rms(const struct hpi_hsubsys *ph_subsys, u32 h_control, + short an_rmsdB[HPI_MAX_CHANNELS] + ) +{ + short i = 0; + + struct hpi_message hm; + struct hpi_response hr; + + hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, + HPI_CONTROL_GET_STATE); + u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + hm.u.c.attribute = HPI_METER_RMS; + + hpi_send_recv(&hm, &hr); + + if (!hr.error) + memcpy(an_rmsdB, hr.u.c.an_log_value, + sizeof(short) * HPI_MAX_CHANNELS); + else + for (i = 0; i < HPI_MAX_CHANNELS; i++) + an_rmsdB[i] = HPI_METER_MINIMUM; + + return hr.error; +} + +u16 hpi_meter_set_rms_ballistics(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 attack, u16 decay) +{ + return hpi_control_param_set(ph_subsys, h_control, + HPI_METER_RMS_BALLISTICS, attack, decay); +} + +u16 hpi_meter_get_rms_ballistics(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 *pn_attack, u16 *pn_decay) +{ + u32 attack; + u32 decay; + u16 error; + + error = hpi_control_param2_get(ph_subsys, h_control, + HPI_METER_RMS_BALLISTICS, &attack, &decay); + + if (pn_attack) + *pn_attack = (unsigned short)attack; + if (pn_decay) + *pn_decay = (unsigned short)decay; + + return error; +} + +u16 hpi_meter_set_peak_ballistics(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 attack, u16 decay) +{ + return hpi_control_param_set(ph_subsys, h_control, + HPI_METER_PEAK_BALLISTICS, attack, decay); +} + +u16 hpi_meter_get_peak_ballistics(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 *pn_attack, u16 *pn_decay) +{ + u32 attack; + u32 decay; + u16 error; + + error = hpi_control_param2_get(ph_subsys, h_control, + HPI_METER_PEAK_BALLISTICS, &attack, &decay); + + if (pn_attack) + *pn_attack = (short)attack; + if (pn_decay) + *pn_decay = (short)decay; + + return error; +} + +u16 hpi_microphone_set_phantom_power(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 on_off) +{ + return hpi_control_param_set(ph_subsys, h_control, + HPI_MICROPHONE_PHANTOM_POWER, (u32)on_off, 0); +} + +u16 hpi_microphone_get_phantom_power(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 *pw_on_off) +{ + u16 error = 0; + u32 on_off = 0; + error = hpi_control_param1_get(ph_subsys, h_control, + HPI_MICROPHONE_PHANTOM_POWER, &on_off); + if (pw_on_off) + *pw_on_off = (u16)on_off; + return error; +} + +u16 hpi_multiplexer_set_source(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 source_node_type, u16 source_node_index) +{ + return hpi_control_param_set(ph_subsys, h_control, + HPI_MULTIPLEXER_SOURCE, source_node_type, source_node_index); +} + +u16 hpi_multiplexer_get_source(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 *source_node_type, u16 *source_node_index) +{ + u32 node, index; + u16 error = hpi_control_param2_get(ph_subsys, h_control, + HPI_MULTIPLEXER_SOURCE, &node, + &index); + if (source_node_type) + *source_node_type = (u16)node; + if (source_node_index) + *source_node_index = (u16)index; + return error; +} + +u16 hpi_multiplexer_query_source(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 index, u16 *source_node_type, + u16 *source_node_index) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, + HPI_CONTROL_GET_STATE); + u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + hm.u.c.attribute = HPI_MULTIPLEXER_QUERYSOURCE; + hm.u.c.param1 = index; + + hpi_send_recv(&hm, &hr); + + if (source_node_type) + *source_node_type = (u16)hr.u.c.param1; + if (source_node_index) + *source_node_index = (u16)hr.u.c.param2; + return hr.error; +} + +u16 hpi_parametricEQ__get_info(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 *pw_number_of_bands, u16 *pw_on_off) +{ + u32 oB = 0; + u32 oO = 0; + u16 error = 0; + + error = hpi_control_param2_get(ph_subsys, h_control, + HPI_EQUALIZER_NUM_FILTERS, &oO, &oB); + if (pw_number_of_bands) + *pw_number_of_bands = (u16)oB; + if (pw_on_off) + *pw_on_off = (u16)oO; + return error; +} + +u16 hpi_parametricEQ__set_state(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 on_off) +{ + return hpi_control_param_set(ph_subsys, h_control, + HPI_EQUALIZER_NUM_FILTERS, on_off, 0); +} + +u16 hpi_parametricEQ__get_band(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 index, u16 *pn_type, u32 *pfrequency_hz, + short *pnQ100, short *pn_gain0_01dB) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, + HPI_CONTROL_GET_STATE); + u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + hm.u.c.attribute = HPI_EQUALIZER_FILTER; + hm.u.c.param2 = index; + + hpi_send_recv(&hm, &hr); + + if (pfrequency_hz) + *pfrequency_hz = hr.u.c.param1; + if (pn_type) + *pn_type = (u16)(hr.u.c.param2 >> 16); + if (pnQ100) + *pnQ100 = hr.u.c.an_log_value[1]; + if (pn_gain0_01dB) + *pn_gain0_01dB = hr.u.c.an_log_value[0]; + + return hr.error; +} + +u16 hpi_parametricEQ__set_band(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 index, u16 type, u32 frequency_hz, short q100, + short gain0_01dB) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, + HPI_CONTROL_SET_STATE); + u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + + hm.u.c.param1 = frequency_hz; + hm.u.c.param2 = (index & 0xFFFFL) + ((u32)type << 16); + hm.u.c.an_log_value[0] = gain0_01dB; + hm.u.c.an_log_value[1] = q100; + hm.u.c.attribute = HPI_EQUALIZER_FILTER; + + hpi_send_recv(&hm, &hr); + + return hr.error; +} + +u16 hpi_parametricEQ__get_coeffs(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 index, short coeffs[5] + ) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, + HPI_CONTROL_GET_STATE); + u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + hm.u.c.attribute = HPI_EQUALIZER_COEFFICIENTS; + hm.u.c.param2 = index; + + hpi_send_recv(&hm, &hr); + + coeffs[0] = (short)hr.u.c.an_log_value[0]; + coeffs[1] = (short)hr.u.c.an_log_value[1]; + coeffs[2] = (short)hr.u.c.param1; + coeffs[3] = (short)(hr.u.c.param1 >> 16); + coeffs[4] = (short)hr.u.c.param2; + + return hr.error; +} + +u16 hpi_sample_clock_query_source(const struct hpi_hsubsys *ph_subsys, + const u32 h_clock, const u32 index, u16 *pw_source) +{ + u32 qr; + u16 err; + + err = hpi_control_query(ph_subsys, h_clock, HPI_SAMPLECLOCK_SOURCE, + index, 0, &qr); + *pw_source = (u16)qr; + return err; +} + +u16 hpi_sample_clock_set_source(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 source) +{ + return hpi_control_param_set(ph_subsys, h_control, + HPI_SAMPLECLOCK_SOURCE, source, 0); +} + +u16 hpi_sample_clock_get_source(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 *pw_source) +{ + u16 error = 0; + u32 source = 0; + error = hpi_control_param1_get(ph_subsys, h_control, + HPI_SAMPLECLOCK_SOURCE, &source); + if (!error) + if (pw_source) + *pw_source = (u16)source; + return error; +} + +u16 hpi_sample_clock_query_source_index(const struct hpi_hsubsys *ph_subsys, + const u32 h_clock, const u32 index, const u32 source, + u16 *pw_source_index) +{ + u32 qr; + u16 err; + + err = hpi_control_query(ph_subsys, h_clock, + HPI_SAMPLECLOCK_SOURCE_INDEX, index, source, &qr); + *pw_source_index = (u16)qr; + return err; +} + +u16 hpi_sample_clock_set_source_index(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 source_index) +{ + return hpi_control_param_set(ph_subsys, h_control, + HPI_SAMPLECLOCK_SOURCE_INDEX, source_index, 0); +} + +u16 hpi_sample_clock_get_source_index(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u16 *pw_source_index) +{ + u16 error = 0; + u32 source_index = 0; + error = hpi_control_param1_get(ph_subsys, h_control, + HPI_SAMPLECLOCK_SOURCE_INDEX, &source_index); + if (!error) + if (pw_source_index) + *pw_source_index = (u16)source_index; + return error; +} + +u16 hpi_sample_clock_query_local_rate(const struct hpi_hsubsys *ph_subsys, + const u32 h_clock, const u32 index, u32 *prate) +{ + u16 err; + err = hpi_control_query(ph_subsys, h_clock, + HPI_SAMPLECLOCK_LOCAL_SAMPLERATE, index, 0, prate); + + return err; +} + +u16 hpi_sample_clock_set_local_rate(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 sample_rate) +{ + return hpi_control_param_set(ph_subsys, h_control, + HPI_SAMPLECLOCK_LOCAL_SAMPLERATE, sample_rate, 0); +} + +u16 hpi_sample_clock_get_local_rate(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 *psample_rate) +{ + u16 error = 0; + u32 sample_rate = 0; + error = hpi_control_param1_get(ph_subsys, h_control, + HPI_SAMPLECLOCK_LOCAL_SAMPLERATE, &sample_rate); + if (!error) + if (psample_rate) + *psample_rate = sample_rate; + return error; +} + +u16 hpi_sample_clock_get_sample_rate(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 *psample_rate) +{ + u16 error = 0; + u32 sample_rate = 0; + error = hpi_control_param1_get(ph_subsys, h_control, + HPI_SAMPLECLOCK_SAMPLERATE, &sample_rate); + if (!error) + if (psample_rate) + *psample_rate = sample_rate; + return error; +} + +u16 hpi_sample_clock_set_auto(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 enable) +{ + return hpi_control_param_set(ph_subsys, h_control, + HPI_SAMPLECLOCK_AUTO, enable, 0); +} + +u16 hpi_sample_clock_get_auto(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 *penable) +{ + return hpi_control_param1_get(ph_subsys, h_control, + HPI_SAMPLECLOCK_AUTO, penable); +} + +u16 hpi_sample_clock_set_local_rate_lock(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 lock) +{ + return hpi_control_param_set(ph_subsys, h_control, + HPI_SAMPLECLOCK_LOCAL_LOCK, lock, 0); +} + +u16 hpi_sample_clock_get_local_rate_lock(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 *plock) +{ + return hpi_control_param1_get(ph_subsys, h_control, + HPI_SAMPLECLOCK_LOCAL_LOCK, plock); +} + +u16 hpi_tone_detector_get_frequency(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 index, u32 *frequency) +{ + return hpi_control_param_get(ph_subsys, h_control, + HPI_TONEDETECTOR_FREQUENCY, index, 0, frequency, NULL); +} + +u16 hpi_tone_detector_get_state(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 *state) +{ + return hpi_control_param_get(ph_subsys, h_control, + HPI_TONEDETECTOR_STATE, 0, 0, (u32 *)state, NULL); +} + +u16 hpi_tone_detector_set_enable(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 enable) +{ + return hpi_control_param_set(ph_subsys, h_control, HPI_GENERIC_ENABLE, + (u32)enable, 0); +} + +u16 hpi_tone_detector_get_enable(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 *enable) +{ + return hpi_control_param_get(ph_subsys, h_control, HPI_GENERIC_ENABLE, + 0, 0, (u32 *)enable, NULL); +} + +u16 hpi_tone_detector_set_event_enable(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 event_enable) +{ + return hpi_control_param_set(ph_subsys, h_control, + HPI_GENERIC_EVENT_ENABLE, (u32)event_enable, 0); +} + +u16 hpi_tone_detector_get_event_enable(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 *event_enable) +{ + return hpi_control_param_get(ph_subsys, h_control, + HPI_GENERIC_EVENT_ENABLE, 0, 0, (u32 *)event_enable, NULL); +} + +u16 hpi_tone_detector_set_threshold(const struct hpi_hsubsys *ph_subsys, + u32 h_control, int threshold) +{ + return hpi_control_param_set(ph_subsys, h_control, + HPI_TONEDETECTOR_THRESHOLD, (u32)threshold, 0); +} + +u16 hpi_tone_detector_get_threshold(const struct hpi_hsubsys *ph_subsys, + u32 h_control, int *threshold) +{ + return hpi_control_param_get(ph_subsys, h_control, + HPI_TONEDETECTOR_THRESHOLD, 0, 0, (u32 *)threshold, NULL); +} + +u16 hpi_silence_detector_get_state(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 *state) +{ + return hpi_control_param_get(ph_subsys, h_control, + HPI_SILENCEDETECTOR_STATE, 0, 0, (u32 *)state, NULL); +} + +u16 hpi_silence_detector_set_enable(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 enable) +{ + return hpi_control_param_set(ph_subsys, h_control, HPI_GENERIC_ENABLE, + (u32)enable, 0); +} + +u16 hpi_silence_detector_get_enable(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 *enable) +{ + return hpi_control_param_get(ph_subsys, h_control, HPI_GENERIC_ENABLE, + 0, 0, (u32 *)enable, NULL); +} + +u16 hpi_silence_detector_set_event_enable(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 event_enable) +{ + return hpi_control_param_set(ph_subsys, h_control, + HPI_GENERIC_EVENT_ENABLE, (u32)event_enable, 0); +} + +u16 hpi_silence_detector_get_event_enable(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 *event_enable) +{ + return hpi_control_param_get(ph_subsys, h_control, + HPI_GENERIC_EVENT_ENABLE, 0, 0, (u32 *)event_enable, NULL); +} + +u16 hpi_silence_detector_set_delay(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 delay) +{ + return hpi_control_param_set(ph_subsys, h_control, + HPI_SILENCEDETECTOR_DELAY, (u32)delay, 0); +} + +u16 hpi_silence_detector_get_delay(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 *delay) +{ + return hpi_control_param_get(ph_subsys, h_control, + HPI_SILENCEDETECTOR_DELAY, 0, 0, (u32 *)delay, NULL); +} + +u16 hpi_silence_detector_set_threshold(const struct hpi_hsubsys *ph_subsys, + u32 h_control, int threshold) +{ + return hpi_control_param_set(ph_subsys, h_control, + HPI_SILENCEDETECTOR_THRESHOLD, (u32)threshold, 0); +} + +u16 hpi_silence_detector_get_threshold(const struct hpi_hsubsys *ph_subsys, + u32 h_control, int *threshold) +{ + return hpi_control_param_get(ph_subsys, h_control, + HPI_SILENCEDETECTOR_THRESHOLD, 0, 0, (u32 *)threshold, NULL); +} + +u16 hpi_tuner_query_band(const struct hpi_hsubsys *ph_subsys, + const u32 h_tuner, const u32 index, u16 *pw_band) +{ + u32 qr; + u16 err; + + err = hpi_control_query(ph_subsys, h_tuner, HPI_TUNER_BAND, index, 0, + &qr); + *pw_band = (u16)qr; + return err; +} + +u16 hpi_tuner_set_band(const struct hpi_hsubsys *ph_subsys, u32 h_control, + u16 band) +{ + return hpi_control_param_set(ph_subsys, h_control, HPI_TUNER_BAND, + band, 0); +} + +u16 hpi_tuner_get_band(const struct hpi_hsubsys *ph_subsys, u32 h_control, + u16 *pw_band) +{ + u32 band = 0; + u16 error = 0; + + error = hpi_control_param1_get(ph_subsys, h_control, HPI_TUNER_BAND, + &band); + if (pw_band) + *pw_band = (u16)band; + return error; +} + +u16 hpi_tuner_query_frequency(const struct hpi_hsubsys *ph_subsys, + const u32 h_tuner, const u32 index, const u16 band, u32 *pfreq) +{ + return hpi_control_query(ph_subsys, h_tuner, HPI_TUNER_FREQ, index, + band, pfreq); +} + +u16 hpi_tuner_set_frequency(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 freq_ink_hz) +{ + return hpi_control_param_set(ph_subsys, h_control, HPI_TUNER_FREQ, + freq_ink_hz, 0); +} + +u16 hpi_tuner_get_frequency(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 *pw_freq_ink_hz) +{ + return hpi_control_param1_get(ph_subsys, h_control, HPI_TUNER_FREQ, + pw_freq_ink_hz); +} + +u16 hpi_tuner_query_gain(const struct hpi_hsubsys *ph_subsys, + const u32 h_tuner, const u32 index, u16 *pw_gain) +{ + u32 qr; + u16 err; + + err = hpi_control_query(ph_subsys, h_tuner, HPI_TUNER_BAND, index, 0, + &qr); + *pw_gain = (u16)qr; + return err; +} + +u16 hpi_tuner_set_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control, + short gain) +{ + return hpi_control_param_set(ph_subsys, h_control, HPI_TUNER_GAIN, + gain, 0); +} + +u16 hpi_tuner_get_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control, + short *pn_gain) +{ + u32 gain = 0; + u16 error = 0; + + error = hpi_control_param1_get(ph_subsys, h_control, HPI_TUNER_GAIN, + &gain); + if (pn_gain) + *pn_gain = (u16)gain; + return error; +} + +u16 hpi_tuner_getRF_level(const struct hpi_hsubsys *ph_subsys, u32 h_control, + short *pw_level) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, + HPI_CONTROL_GET_STATE); + u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + hm.u.c.attribute = HPI_TUNER_LEVEL; + hm.u.c.param1 = HPI_TUNER_LEVEL_AVERAGE; + hpi_send_recv(&hm, &hr); + if (pw_level) + *pw_level = (short)hr.u.c.param1; + return hr.error; +} + +u16 hpi_tuner_get_rawRF_level(const struct hpi_hsubsys *ph_subsys, + u32 h_control, short *pw_level) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, + HPI_CONTROL_GET_STATE); + u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + hm.u.c.attribute = HPI_TUNER_LEVEL; + hm.u.c.param1 = HPI_TUNER_LEVEL_RAW; + hpi_send_recv(&hm, &hr); + if (pw_level) + *pw_level = (short)hr.u.c.param1; + return hr.error; +} + +u16 hpi_tuner_query_deemphasis(const struct hpi_hsubsys *ph_subsys, + const u32 h_tuner, const u32 index, const u16 band, u32 *pdeemphasis) +{ + return hpi_control_query(ph_subsys, h_tuner, HPI_TUNER_DEEMPHASIS, + index, band, pdeemphasis); +} + +u16 hpi_tuner_set_deemphasis(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 deemphasis) +{ + return hpi_control_param_set(ph_subsys, h_control, + HPI_TUNER_DEEMPHASIS, deemphasis, 0); +} + +u16 hpi_tuner_get_deemphasis(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 *pdeemphasis) +{ + return hpi_control_param1_get(ph_subsys, h_control, + HPI_TUNER_DEEMPHASIS, pdeemphasis); +} + +u16 hpi_tuner_query_program(const struct hpi_hsubsys *ph_subsys, + const u32 h_tuner, u32 *pbitmap_program) +{ + return hpi_control_query(ph_subsys, h_tuner, HPI_TUNER_PROGRAM, 0, 0, + pbitmap_program); +} + +u16 hpi_tuner_set_program(const struct hpi_hsubsys *ph_subsys, u32 h_control, + u32 program) +{ + return hpi_control_param_set(ph_subsys, h_control, HPI_TUNER_PROGRAM, + program, 0); +} + +u16 hpi_tuner_get_program(const struct hpi_hsubsys *ph_subsys, u32 h_control, + u32 *pprogram) +{ + return hpi_control_param1_get(ph_subsys, h_control, HPI_TUNER_PROGRAM, + pprogram); +} + +u16 hpi_tuner_get_hd_radio_dsp_version(const struct hpi_hsubsys *ph_subsys, + u32 h_control, char *psz_dsp_version, const u32 string_size) +{ + return hpi_control_get_string(ph_subsys, h_control, + HPI_TUNER_HDRADIO_DSP_VERSION, psz_dsp_version, string_size); +} + +u16 hpi_tuner_get_hd_radio_sdk_version(const struct hpi_hsubsys *ph_subsys, + u32 h_control, char *psz_sdk_version, const u32 string_size) +{ + return hpi_control_get_string(ph_subsys, h_control, + HPI_TUNER_HDRADIO_SDK_VERSION, psz_sdk_version, string_size); +} + +u16 hpi_tuner_get_status(const struct hpi_hsubsys *ph_subsys, u32 h_control, + u16 *pw_status_mask, u16 *pw_status) +{ + u32 status = 0; + u16 error = 0; + + error = hpi_control_param1_get(ph_subsys, h_control, HPI_TUNER_STATUS, + &status); + if (pw_status) { + if (!error) { + *pw_status_mask = (u16)(status >> 16); + *pw_status = (u16)(status & 0xFFFF); + } else { + *pw_status_mask = 0; + *pw_status = 0; + } + } + return error; +} + +u16 hpi_tuner_set_mode(const struct hpi_hsubsys *ph_subsys, u32 h_control, + u32 mode, u32 value) +{ + return hpi_control_param_set(ph_subsys, h_control, HPI_TUNER_MODE, + mode, value); +} + +u16 hpi_tuner_get_mode(const struct hpi_hsubsys *ph_subsys, u32 h_control, + u32 mode, u32 *pn_value) +{ + return hpi_control_param_get(ph_subsys, h_control, HPI_TUNER_MODE, + mode, 0, pn_value, NULL); +} + +u16 hpi_tuner_get_hd_radio_signal_quality(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 *pquality) +{ + return hpi_control_param_get(ph_subsys, h_control, + HPI_TUNER_HDRADIO_SIGNAL_QUALITY, 0, 0, pquality, NULL); +} + +u16 hpi_tuner_getRDS(const struct hpi_hsubsys *ph_subsys, u32 h_control, + char *p_data) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, + HPI_CONTROL_GET_STATE); + u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + hm.u.c.attribute = HPI_TUNER_RDS; + hpi_send_recv(&hm, &hr); + if (p_data) { + *(u32 *)&p_data[0] = hr.u.cu.tuner.rds.data[0]; + *(u32 *)&p_data[4] = hr.u.cu.tuner.rds.data[1]; + *(u32 *)&p_data[8] = hr.u.cu.tuner.rds.bLER; + } + return hr.error; +} + +u16 HPI_PAD__get_channel_name(const struct hpi_hsubsys *ph_subsys, + u32 h_control, char *psz_string, const u32 data_length) +{ + return hpi_control_get_string(ph_subsys, h_control, + HPI_PAD_CHANNEL_NAME, psz_string, data_length); +} + +u16 HPI_PAD__get_artist(const struct hpi_hsubsys *ph_subsys, u32 h_control, + char *psz_string, const u32 data_length) +{ + return hpi_control_get_string(ph_subsys, h_control, HPI_PAD_ARTIST, + psz_string, data_length); +} + +u16 HPI_PAD__get_title(const struct hpi_hsubsys *ph_subsys, u32 h_control, + char *psz_string, const u32 data_length) +{ + return hpi_control_get_string(ph_subsys, h_control, HPI_PAD_TITLE, + psz_string, data_length); +} + +u16 HPI_PAD__get_comment(const struct hpi_hsubsys *ph_subsys, u32 h_control, + char *psz_string, const u32 data_length) +{ + return hpi_control_get_string(ph_subsys, h_control, HPI_PAD_COMMENT, + psz_string, data_length); +} + +u16 HPI_PAD__get_program_type(const struct hpi_hsubsys *ph_subsys, + u32 h_control, u32 *ppTY) +{ + return hpi_control_param_get(ph_subsys, h_control, + HPI_PAD_PROGRAM_TYPE, 0, 0, ppTY, NULL); +} + +u16 HPI_PAD__get_rdsPI(const struct hpi_hsubsys *ph_subsys, u32 h_control, + u32 *ppI) +{ + return hpi_control_param_get(ph_subsys, h_control, HPI_PAD_PROGRAM_ID, + 0, 0, ppI, NULL); +} + +u16 hpi_volume_query_channels(const struct hpi_hsubsys *ph_subsys, + const u32 h_volume, u32 *p_channels) +{ + return hpi_control_query(ph_subsys, h_volume, HPI_VOLUME_NUM_CHANNELS, + 0, 0, p_channels); +} + +u16 hpi_volume_set_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control, + short an_log_gain[HPI_MAX_CHANNELS] + ) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, + HPI_CONTROL_SET_STATE); + u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + memcpy(hm.u.c.an_log_value, an_log_gain, + sizeof(short) * HPI_MAX_CHANNELS); + hm.u.c.attribute = HPI_VOLUME_GAIN; + + hpi_send_recv(&hm, &hr); + + return hr.error; +} + +u16 hpi_volume_get_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control, + short an_log_gain[HPI_MAX_CHANNELS] + ) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, + HPI_CONTROL_GET_STATE); + u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + hm.u.c.attribute = HPI_VOLUME_GAIN; + + hpi_send_recv(&hm, &hr); + + memcpy(an_log_gain, hr.u.c.an_log_value, + sizeof(short) * HPI_MAX_CHANNELS); + return hr.error; +} + +u16 hpi_volume_query_range(const struct hpi_hsubsys *ph_subsys, u32 h_control, + short *min_gain_01dB, short *max_gain_01dB, short *step_gain_01dB) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, + HPI_CONTROL_GET_STATE); + u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + hm.u.c.attribute = HPI_VOLUME_RANGE; + + hpi_send_recv(&hm, &hr); + if (hr.error) { + hr.u.c.an_log_value[0] = 0; + hr.u.c.an_log_value[1] = 0; + hr.u.c.param1 = 0; + } + if (min_gain_01dB) + *min_gain_01dB = hr.u.c.an_log_value[0]; + if (max_gain_01dB) + *max_gain_01dB = hr.u.c.an_log_value[1]; + if (step_gain_01dB) + *step_gain_01dB = (short)hr.u.c.param1; + return hr.error; +} + +u16 hpi_volume_auto_fade_profile(const struct hpi_hsubsys *ph_subsys, + u32 h_control, short an_stop_gain0_01dB[HPI_MAX_CHANNELS], + u32 duration_ms, u16 profile) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, + HPI_CONTROL_SET_STATE); + u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + + memcpy(hm.u.c.an_log_value, an_stop_gain0_01dB, + sizeof(short) * HPI_MAX_CHANNELS); + + hm.u.c.attribute = HPI_VOLUME_AUTOFADE; + hm.u.c.param1 = duration_ms; + hm.u.c.param2 = profile; + + hpi_send_recv(&hm, &hr); + + return hr.error; +} + +u16 hpi_volume_auto_fade(const struct hpi_hsubsys *ph_subsys, u32 h_control, + short an_stop_gain0_01dB[HPI_MAX_CHANNELS], u32 duration_ms) +{ + return hpi_volume_auto_fade_profile(ph_subsys, h_control, + an_stop_gain0_01dB, duration_ms, HPI_VOLUME_AUTOFADE_LOG); +} + +u16 hpi_vox_set_threshold(const struct hpi_hsubsys *ph_subsys, u32 h_control, + short an_gain0_01dB) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, + HPI_CONTROL_SET_STATE); + u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + hm.u.c.attribute = HPI_VOX_THRESHOLD; + + hm.u.c.an_log_value[0] = an_gain0_01dB; + + hpi_send_recv(&hm, &hr); + + return hr.error; +} + +u16 hpi_vox_get_threshold(const struct hpi_hsubsys *ph_subsys, u32 h_control, + short *an_gain0_01dB) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, + HPI_CONTROL_GET_STATE); + u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + hm.u.c.attribute = HPI_VOX_THRESHOLD; + + hpi_send_recv(&hm, &hr); + + *an_gain0_01dB = hr.u.c.an_log_value[0]; + + return hr.error; +} + +static size_t strv_packet_size = MIN_STRV_PACKET_SIZE; + +static size_t entity_type_to_size[LAST_ENTITY_TYPE] = { + 0, + sizeof(struct hpi_entity), + sizeof(void *), + + sizeof(int), + sizeof(float), + sizeof(double), + + sizeof(char), + sizeof(char), + + 4 * sizeof(char), + 16 * sizeof(char), + 6 * sizeof(char), +}; + +inline size_t hpi_entity_size(struct hpi_entity *entity_ptr) +{ + return entity_ptr->header.size; +} + +inline size_t hpi_entity_header_size(struct hpi_entity *entity_ptr) +{ + return sizeof(entity_ptr->header); +} + +inline size_t hpi_entity_value_size(struct hpi_entity *entity_ptr) +{ + return hpi_entity_size(entity_ptr) - + hpi_entity_header_size(entity_ptr); +} + +inline size_t hpi_entity_item_count(struct hpi_entity *entity_ptr) +{ + return hpi_entity_value_size(entity_ptr) / + entity_type_to_size[entity_ptr->header.type]; +} + +inline struct hpi_entity *hpi_entity_ptr_to_next(struct hpi_entity + *entity_ptr) +{ + return (void *)(((uint8_t *) entity_ptr) + + hpi_entity_size(entity_ptr)); +} + +inline u16 hpi_entity_check_type(const enum e_entity_type t) +{ + if (t >= 0 && t < STR_TYPE_FIELD_MAX) + return 0; + return HPI_ERROR_ENTITY_TYPE_INVALID; +} + +inline u16 hpi_entity_check_role(const enum e_entity_role r) +{ + if (r >= 0 && r < STR_ROLE_FIELD_MAX) + return 0; + return HPI_ERROR_ENTITY_ROLE_INVALID; +} + +static u16 hpi_entity_get_next(struct hpi_entity *entity, int recursive_flag, + void *guard_p, struct hpi_entity **next) +{ + HPI_DEBUG_ASSERT(entity != NULL); + HPI_DEBUG_ASSERT(next != NULL); + HPI_DEBUG_ASSERT(hpi_entity_size(entity) != 0); + + if (guard_p <= (void *)entity) { + *next = NULL; + return 0; + } + + if (recursive_flag && entity->header.type == entity_type_sequence) + *next = (struct hpi_entity *)entity->value; + else + *next = (struct hpi_entity *)hpi_entity_ptr_to_next(entity); + + if (guard_p <= (void *)*next) { + *next = NULL; + return 0; + } + + HPI_DEBUG_ASSERT(guard_p >= (void *)hpi_entity_ptr_to_next(*next)); + return 0; +} + +u16 hpi_entity_find_next(struct hpi_entity *container_entity, + enum e_entity_type type, enum e_entity_role role, int recursive_flag, + struct hpi_entity **current_match) +{ + struct hpi_entity *tmp = NULL; + void *guard_p = NULL; + + HPI_DEBUG_ASSERT(container_entity != NULL); + guard_p = hpi_entity_ptr_to_next(container_entity); + + if (*current_match != NULL) + hpi_entity_get_next(*current_match, recursive_flag, guard_p, + &tmp); + else + hpi_entity_get_next(container_entity, 1, guard_p, &tmp); + + while (tmp) { + u16 err; + + HPI_DEBUG_ASSERT((void *)tmp >= (void *)container_entity); + + if ((!type || tmp->header.type == type) && (!role + || tmp->header.role == role)) { + *current_match = tmp; + return 0; + } + + err = hpi_entity_get_next(tmp, recursive_flag, guard_p, + current_match); + if (err) + return err; + + tmp = *current_match; + } + + *current_match = NULL; + return 0; +} + +void hpi_entity_free(struct hpi_entity *entity) +{ + if (entity != NULL) + kfree(entity); +} + +static u16 hpi_entity_alloc_and_copy(struct hpi_entity *src, + struct hpi_entity **dst) +{ + size_t buf_size; + HPI_DEBUG_ASSERT(dst != NULL); + HPI_DEBUG_ASSERT(src != NULL); + + buf_size = hpi_entity_size(src); + *dst = kmalloc(buf_size, GFP_KERNEL); + if (dst == NULL) + return HPI_ERROR_MEMORY_ALLOC; + memcpy(*dst, src, buf_size); + return 0; +} + +u16 hpi_universal_info(const struct hpi_hsubsys *ph_subsys, u32 hC, + struct hpi_entity **info) +{ + struct hpi_msg_strv hm; + struct hpi_res_strv *phr; + u16 hpi_err; + int remaining_attempts = 2; + size_t resp_packet_size = 1024; + + *info = NULL; + + while (remaining_attempts--) { + phr = kmalloc(resp_packet_size, GFP_KERNEL); + HPI_DEBUG_ASSERT(phr != NULL); + + hpi_init_message_responseV1(&hm.h, (u16)sizeof(hm), &phr->h, + (u16)resp_packet_size, HPI_OBJ_CONTROL, + HPI_CONTROL_GET_INFO); + u32TOINDEXES(hC, &hm.h.adapter_index, &hm.h.obj_index); + + hm.strv.header.size = sizeof(hm.strv); + phr->strv.header.size = resp_packet_size - sizeof(phr->h); + + hpi_send_recv((struct hpi_message *)&hm.h, + (struct hpi_response *)&phr->h); + if (phr->h.error == HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL) { + + HPI_DEBUG_ASSERT(phr->h.specific_error > + MIN_STRV_PACKET_SIZE + && phr->h.specific_error < 1500); + resp_packet_size = phr->h.specific_error; + } else { + remaining_attempts = 0; + if (!phr->h.error) + hpi_entity_alloc_and_copy(&phr->strv, info); + } + + hpi_err = phr->h.error; + kfree(phr); + } + + return hpi_err; +} + +u16 hpi_universal_get(const struct hpi_hsubsys *ph_subsys, u32 hC, + struct hpi_entity **value) +{ + struct hpi_msg_strv hm; + struct hpi_res_strv *phr; + u16 hpi_err; + int remaining_attempts = 2; + + *value = NULL; + + while (remaining_attempts--) { + phr = kmalloc(strv_packet_size, GFP_KERNEL); + if (!phr) + return HPI_ERROR_MEMORY_ALLOC; + + hpi_init_message_responseV1(&hm.h, (u16)sizeof(hm), &phr->h, + (u16)strv_packet_size, HPI_OBJ_CONTROL, + HPI_CONTROL_GET_STATE); + u32TOINDEXES(hC, &hm.h.adapter_index, &hm.h.obj_index); + + hm.strv.header.size = sizeof(hm.strv); + phr->strv.header.size = strv_packet_size - sizeof(phr->h); + + hpi_send_recv((struct hpi_message *)&hm.h, + (struct hpi_response *)&phr->h); + if (phr->h.error == HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL) { + + HPI_DEBUG_ASSERT(phr->h.specific_error > + MIN_STRV_PACKET_SIZE + && phr->h.specific_error < 1000); + strv_packet_size = phr->h.specific_error; + } else { + remaining_attempts = 0; + if (!phr->h.error) + hpi_entity_alloc_and_copy(&phr->strv, value); + } + + hpi_err = phr->h.error; + kfree(phr); + } + + return hpi_err; +} + +u16 hpi_universal_set(const struct hpi_hsubsys *ph_subsys, u32 hC, + struct hpi_entity *value) +{ + struct hpi_msg_strv *phm; + struct hpi_res_strv hr; + + phm = kmalloc(sizeof(phm->h) + value->header.size, GFP_KERNEL); + HPI_DEBUG_ASSERT(phm != NULL); + + hpi_init_message_responseV1(&phm->h, + sizeof(phm->h) + value->header.size, &hr.h, sizeof(hr), + HPI_OBJ_CONTROL, HPI_CONTROL_SET_STATE); + u32TOINDEXES(hC, &phm->h.adapter_index, &phm->h.obj_index); + hr.strv.header.size = sizeof(hr.strv); + + memcpy(&phm->strv, value, value->header.size); + hpi_send_recv((struct hpi_message *)&phm->h, + (struct hpi_response *)&hr.h); + + return hr.h.error; +} + +u16 hpi_entity_alloc_and_pack(const enum e_entity_type type, + const size_t item_count, const enum e_entity_role role, void *value, + struct hpi_entity **entity) +{ + size_t bytes_to_copy, total_size; + u16 hE = 0; + *entity = NULL; + + hE = hpi_entity_check_type(type); + if (hE) + return hE; + + HPI_DEBUG_ASSERT(role > entity_role_null && type < LAST_ENTITY_ROLE); + + bytes_to_copy = entity_type_to_size[type] * item_count; + total_size = hpi_entity_header_size(*entity) + bytes_to_copy; + + HPI_DEBUG_ASSERT(total_size >= hpi_entity_header_size(*entity) + && total_size < STR_SIZE_FIELD_MAX); + + *entity = kmalloc(total_size, GFP_KERNEL); + if (*entity == NULL) + return HPI_ERROR_MEMORY_ALLOC; + memcpy((*entity)->value, value, bytes_to_copy); + (*entity)->header.size = + hpi_entity_header_size(*entity) + bytes_to_copy; + (*entity)->header.type = type; + (*entity)->header.role = role; + return 0; +} + +u16 hpi_entity_copy_value_from(struct hpi_entity *entity, + enum e_entity_type type, size_t item_count, void *value_dst_p) +{ + size_t bytes_to_copy; + + if (entity->header.type != type) + return HPI_ERROR_ENTITY_TYPE_MISMATCH; + + if (hpi_entity_item_count(entity) != item_count) + return HPI_ERROR_ENTITY_ITEM_COUNT; + + bytes_to_copy = entity_type_to_size[type] * item_count; + memcpy(value_dst_p, entity->value, bytes_to_copy); + return 0; +} + +u16 hpi_entity_unpack(struct hpi_entity *entity, enum e_entity_type *type, + size_t *item_count, enum e_entity_role *role, void **value) +{ + u16 err = 0; + HPI_DEBUG_ASSERT(entity != NULL); + + if (type) + *type = entity->header.type; + + if (role) + *role = entity->header.role; + + if (value) + *value = entity->value; + + if (item_count != NULL) { + if (entity->header.type == entity_type_sequence) { + void *guard_p = hpi_entity_ptr_to_next(entity); + struct hpi_entity *next = NULL; + void *contents = entity->value; + + *item_count = 0; + while (contents < guard_p) { + (*item_count)++; + err = hpi_entity_get_next(contents, 0, + guard_p, &next); + if (next == NULL || err) + break; + contents = next; + } + } else { + *item_count = hpi_entity_item_count(entity); + } + } + return err; +} + +u16 hpi_gpio_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index, + u32 *ph_gpio, u16 *pw_number_input_bits, u16 *pw_number_output_bits) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_GPIO, HPI_GPIO_OPEN); + hm.adapter_index = adapter_index; + + hpi_send_recv(&hm, &hr); + + if (hr.error == 0) { + *ph_gpio = + hpi_indexes_to_handle(HPI_OBJ_GPIO, adapter_index, 0); + if (pw_number_input_bits) + *pw_number_input_bits = hr.u.l.number_input_bits; + if (pw_number_output_bits) + *pw_number_output_bits = hr.u.l.number_output_bits; + } else + *ph_gpio = 0; + return hr.error; +} + +u16 hpi_gpio_read_bit(const struct hpi_hsubsys *ph_subsys, u32 h_gpio, + u16 bit_index, u16 *pw_bit_data) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_GPIO, HPI_GPIO_READ_BIT); + u32TOINDEX(h_gpio, &hm.adapter_index); + hm.u.l.bit_index = bit_index; + + hpi_send_recv(&hm, &hr); + + *pw_bit_data = hr.u.l.bit_data[0]; + return hr.error; +} + +u16 hpi_gpio_read_all_bits(const struct hpi_hsubsys *ph_subsys, u32 h_gpio, + u16 aw_all_bit_data[4] + ) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_GPIO, HPI_GPIO_READ_ALL); + u32TOINDEX(h_gpio, &hm.adapter_index); + + hpi_send_recv(&hm, &hr); + + if (aw_all_bit_data) { + aw_all_bit_data[0] = hr.u.l.bit_data[0]; + aw_all_bit_data[1] = hr.u.l.bit_data[1]; + aw_all_bit_data[2] = hr.u.l.bit_data[2]; + aw_all_bit_data[3] = hr.u.l.bit_data[3]; + } + return hr.error; +} + +u16 hpi_gpio_write_bit(const struct hpi_hsubsys *ph_subsys, u32 h_gpio, + u16 bit_index, u16 bit_data) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_GPIO, HPI_GPIO_WRITE_BIT); + u32TOINDEX(h_gpio, &hm.adapter_index); + hm.u.l.bit_index = bit_index; + hm.u.l.bit_data = bit_data; + + hpi_send_recv(&hm, &hr); + + return hr.error; +} + +u16 hpi_gpio_write_status(const struct hpi_hsubsys *ph_subsys, u32 h_gpio, + u16 aw_all_bit_data[4] + ) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_GPIO, + HPI_GPIO_WRITE_STATUS); + u32TOINDEX(h_gpio, &hm.adapter_index); + + hpi_send_recv(&hm, &hr); + + if (aw_all_bit_data) { + aw_all_bit_data[0] = hr.u.l.bit_data[0]; + aw_all_bit_data[1] = hr.u.l.bit_data[1]; + aw_all_bit_data[2] = hr.u.l.bit_data[2]; + aw_all_bit_data[3] = hr.u.l.bit_data[3]; + } + return hr.error; +} + +u16 hpi_async_event_open(const struct hpi_hsubsys *ph_subsys, + u16 adapter_index, u32 *ph_async) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_ASYNCEVENT, + HPI_ASYNCEVENT_OPEN); + hm.adapter_index = adapter_index; + + hpi_send_recv(&hm, &hr); + + if (hr.error == 0) + + *ph_async = + hpi_indexes_to_handle(HPI_OBJ_ASYNCEVENT, + adapter_index, 0); + else + *ph_async = 0; + return hr.error; + +} + +u16 hpi_async_event_close(const struct hpi_hsubsys *ph_subsys, u32 h_async) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_ASYNCEVENT, + HPI_ASYNCEVENT_OPEN); + u32TOINDEX(h_async, &hm.adapter_index); + + hpi_send_recv(&hm, &hr); + + return hr.error; +} + +u16 hpi_async_event_wait(const struct hpi_hsubsys *ph_subsys, u32 h_async, + u16 maximum_events, struct hpi_async_event *p_events, + u16 *pw_number_returned) +{ + return 0; +} + +u16 hpi_async_event_get_count(const struct hpi_hsubsys *ph_subsys, + u32 h_async, u16 *pw_count) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_ASYNCEVENT, + HPI_ASYNCEVENT_GETCOUNT); + u32TOINDEX(h_async, &hm.adapter_index); + + hpi_send_recv(&hm, &hr); + + if (hr.error == 0) + if (pw_count) + *pw_count = hr.u.as.u.count.count; + + return hr.error; +} + +u16 hpi_async_event_get(const struct hpi_hsubsys *ph_subsys, u32 h_async, + u16 maximum_events, struct hpi_async_event *p_events, + u16 *pw_number_returned) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_ASYNCEVENT, + HPI_ASYNCEVENT_GET); + u32TOINDEX(h_async, &hm.adapter_index); + + hpi_send_recv(&hm, &hr); + if (!hr.error) { + memcpy(p_events, &hr.u.as.u.event, + sizeof(struct hpi_async_event)); + *pw_number_returned = 1; + } + + return hr.error; +} + +u16 hpi_nv_memory_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index, + u32 *ph_nv_memory, u16 *pw_size_in_bytes) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_NVMEMORY, + HPI_NVMEMORY_OPEN); + hm.adapter_index = adapter_index; + + hpi_send_recv(&hm, &hr); + + if (hr.error == 0) { + *ph_nv_memory = + hpi_indexes_to_handle(HPI_OBJ_NVMEMORY, adapter_index, + 0); + if (pw_size_in_bytes) + *pw_size_in_bytes = hr.u.n.size_in_bytes; + } else + *ph_nv_memory = 0; + return hr.error; +} + +u16 hpi_nv_memory_read_byte(const struct hpi_hsubsys *ph_subsys, + u32 h_nv_memory, u16 index, u16 *pw_data) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_NVMEMORY, + HPI_NVMEMORY_READ_BYTE); + u32TOINDEX(h_nv_memory, &hm.adapter_index); + hm.u.n.address = index; + + hpi_send_recv(&hm, &hr); + + *pw_data = hr.u.n.data; + return hr.error; +} + +u16 hpi_nv_memory_write_byte(const struct hpi_hsubsys *ph_subsys, + u32 h_nv_memory, u16 index, u16 data) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_NVMEMORY, + HPI_NVMEMORY_WRITE_BYTE); + u32TOINDEX(h_nv_memory, &hm.adapter_index); + hm.u.n.address = index; + hm.u.n.data = data; + + hpi_send_recv(&hm, &hr); + + return hr.error; +} + +u16 hpi_profile_open_all(const struct hpi_hsubsys *ph_subsys, + u16 adapter_index, u16 profile_index, u32 *ph_profile, + u16 *pw_max_profiles) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_PROFILE, + HPI_PROFILE_OPEN_ALL); + hm.adapter_index = adapter_index; + hm.obj_index = profile_index; + hpi_send_recv(&hm, &hr); + + *pw_max_profiles = hr.u.p.u.o.max_profiles; + if (hr.error == 0) + *ph_profile = + hpi_indexes_to_handle(HPI_OBJ_PROFILE, adapter_index, + profile_index); + else + *ph_profile = 0; + return hr.error; +} + +u16 hpi_profile_get(const struct hpi_hsubsys *ph_subsys, u32 h_profile, + u16 bin_index, u16 *pw_seconds, u32 *pmicro_seconds, u32 *pcall_count, + u32 *pmax_micro_seconds, u32 *pmin_micro_seconds) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_PROFILE, HPI_PROFILE_GET); + u32TOINDEXES(h_profile, &hm.adapter_index, &hm.obj_index); + hm.u.p.bin_index = bin_index; + hpi_send_recv(&hm, &hr); + if (pw_seconds) + *pw_seconds = hr.u.p.u.t.seconds; + if (pmicro_seconds) + *pmicro_seconds = hr.u.p.u.t.micro_seconds; + if (pcall_count) + *pcall_count = hr.u.p.u.t.call_count; + if (pmax_micro_seconds) + *pmax_micro_seconds = hr.u.p.u.t.max_micro_seconds; + if (pmin_micro_seconds) + *pmin_micro_seconds = hr.u.p.u.t.min_micro_seconds; + return hr.error; +} + +u16 hpi_profile_get_utilization(const struct hpi_hsubsys *ph_subsys, + u32 h_profile, u32 *putilization) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_PROFILE, + HPI_PROFILE_GET_UTILIZATION); + u32TOINDEXES(h_profile, &hm.adapter_index, &hm.obj_index); + hpi_send_recv(&hm, &hr); + if (hr.error) { + if (putilization) + *putilization = 0; + } else { + if (putilization) + *putilization = hr.u.p.u.t.call_count; + } + return hr.error; +} + +u16 hpi_profile_get_name(const struct hpi_hsubsys *ph_subsys, u32 h_profile, + u16 bin_index, char *sz_name, u16 name_length) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_PROFILE, + HPI_PROFILE_GET_NAME); + u32TOINDEXES(h_profile, &hm.adapter_index, &hm.obj_index); + hm.u.p.bin_index = bin_index; + hpi_send_recv(&hm, &hr); + if (hr.error) { + if (sz_name) + strcpy(sz_name, "??"); + } else { + if (sz_name) + memcpy(sz_name, (char *)hr.u.p.u.n.sz_name, + name_length); + } + return hr.error; +} + +u16 hpi_profile_start_all(const struct hpi_hsubsys *ph_subsys, u32 h_profile) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_PROFILE, + HPI_PROFILE_START_ALL); + u32TOINDEXES(h_profile, &hm.adapter_index, &hm.obj_index); + hpi_send_recv(&hm, &hr); + + return hr.error; +} + +u16 hpi_profile_stop_all(const struct hpi_hsubsys *ph_subsys, u32 h_profile) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_PROFILE, + HPI_PROFILE_STOP_ALL); + u32TOINDEXES(h_profile, &hm.adapter_index, &hm.obj_index); + hpi_send_recv(&hm, &hr); + + return hr.error; +} + +u16 hpi_watchdog_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index, + u32 *ph_watchdog) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_WATCHDOG, + HPI_WATCHDOG_OPEN); + hm.adapter_index = adapter_index; + + hpi_send_recv(&hm, &hr); + + if (hr.error == 0) + *ph_watchdog = + hpi_indexes_to_handle(HPI_OBJ_WATCHDOG, adapter_index, + 0); + else + *ph_watchdog = 0; + return hr.error; +} + +u16 hpi_watchdog_set_time(const struct hpi_hsubsys *ph_subsys, u32 h_watchdog, + u32 time_millisec) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_WATCHDOG, + HPI_WATCHDOG_SET_TIME); + u32TOINDEX(h_watchdog, &hm.adapter_index); + hm.u.w.time_ms = time_millisec; + + hpi_send_recv(&hm, &hr); + + return hr.error; +} + +u16 hpi_watchdog_ping(const struct hpi_hsubsys *ph_subsys, u32 h_watchdog) +{ + struct hpi_message hm; + struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_WATCHDOG, + HPI_WATCHDOG_PING); + u32TOINDEX(h_watchdog, &hm.adapter_index); + + hpi_send_recv(&hm, &hr); + + return hr.error; +} diff --git a/sound/pci/asihpi/hpimsginit.c b/sound/pci/asihpi/hpimsginit.c new file mode 100644 index 000000000000..8e1d099ed7e4 --- /dev/null +++ b/sound/pci/asihpi/hpimsginit.c @@ -0,0 +1,130 @@ +/****************************************************************************** + + AudioScience HPI driver + Copyright (C) 1997-2010 AudioScience Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation; + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Hardware Programming Interface (HPI) Utility functions. + + (C) Copyright AudioScience Inc. 2007 +*******************************************************************************/ + +#include "hpi_internal.h" +#include "hpimsginit.h" + +/* The actual message size for each object type */ +static u16 msg_size[HPI_OBJ_MAXINDEX + 1] = HPI_MESSAGE_SIZE_BY_OBJECT; +/* The actual response size for each object type */ +static u16 res_size[HPI_OBJ_MAXINDEX + 1] = HPI_RESPONSE_SIZE_BY_OBJECT; +/* Flag to enable alternate message type for SSX2 bypass. */ +static u16 gwSSX2_bypass; + +/** \internal + * Used by ASIO driver to disable SSX2 for a single process + * \param phSubSys Pointer to HPI subsystem handle. + * \param wBypass New bypass setting 0 = off, nonzero = on + * \return Previous bypass setting. + */ +u16 hpi_subsys_ssx2_bypass(const struct hpi_hsubsys *ph_subsys, u16 bypass) +{ + u16 old_value = gwSSX2_bypass; + + gwSSX2_bypass = bypass; + + return old_value; +} + +/** \internal + * initialize the HPI message structure + */ +static void hpi_init_message(struct hpi_message *phm, u16 object, + u16 function) +{ + memset(phm, 0, sizeof(*phm)); + if ((object > 0) && (object <= HPI_OBJ_MAXINDEX)) + phm->size = msg_size[object]; + else + phm->size = sizeof(*phm); + + if (gwSSX2_bypass) + phm->type = HPI_TYPE_SSX2BYPASS_MESSAGE; + else + phm->type = HPI_TYPE_MESSAGE; + phm->object = object; + phm->function = function; + phm->version = 0; + /* Expect adapter index to be set by caller */ +} + +/** \internal + * initialize the HPI response structure + */ +void hpi_init_response(struct hpi_response *phr, u16 object, u16 function, + u16 error) +{ + memset(phr, 0, sizeof(*phr)); + phr->type = HPI_TYPE_RESPONSE; + if ((object > 0) && (object <= HPI_OBJ_MAXINDEX)) + phr->size = res_size[object]; + else + phr->size = sizeof(*phr); + phr->object = object; + phr->function = function; + phr->error = error; + phr->specific_error = 0; + phr->version = 0; +} + +void hpi_init_message_response(struct hpi_message *phm, + struct hpi_response *phr, u16 object, u16 function) +{ + hpi_init_message(phm, object, function); + /* default error return if the response is + not filled in by the callee */ + hpi_init_response(phr, object, function, + HPI_ERROR_PROCESSING_MESSAGE); +} + +static void hpi_init_messageV1(struct hpi_message_header *phm, u16 size, + u16 object, u16 function) +{ + memset(phm, 0, sizeof(*phm)); + if ((object > 0) && (object <= HPI_OBJ_MAXINDEX)) { + phm->size = size; + phm->type = HPI_TYPE_MESSAGE; + phm->object = object; + phm->function = function; + phm->version = 1; + /* Expect adapter index to be set by caller */ + } +} + +void hpi_init_responseV1(struct hpi_response_header *phr, u16 size, + u16 object, u16 function) +{ + memset(phr, 0, sizeof(*phr)); + phr->size = size; + phr->version = 1; + phr->type = HPI_TYPE_RESPONSE; + phr->error = HPI_ERROR_PROCESSING_MESSAGE; +} + +void hpi_init_message_responseV1(struct hpi_message_header *phm, u16 msg_size, + struct hpi_response_header *phr, u16 res_size, u16 object, + u16 function) +{ + hpi_init_messageV1(phm, msg_size, object, function); + hpi_init_responseV1(phr, res_size, object, function); +} diff --git a/sound/pci/asihpi/hpimsginit.h b/sound/pci/asihpi/hpimsginit.h new file mode 100644 index 000000000000..864ad020c9b3 --- /dev/null +++ b/sound/pci/asihpi/hpimsginit.h @@ -0,0 +1,40 @@ +/****************************************************************************** + + AudioScience HPI driver + Copyright (C) 1997-2010 AudioScience Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation; + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Hardware Programming Interface (HPI) Utility functions + + (C) Copyright AudioScience Inc. 2007 +*******************************************************************************/ +/* Initialise response headers, or msg/response pairs. +Note that it is valid to just init a response e.g. when a lower level is preparing +a response to a message. +However, when sending a message, a matching response buffer always must be prepared +*/ + +void hpi_init_response(struct hpi_response *phr, u16 object, u16 function, + u16 error); + +void hpi_init_message_response(struct hpi_message *phm, + struct hpi_response *phr, u16 object, u16 function); + +void hpi_init_responseV1(struct hpi_response_header *phr, u16 size, + u16 object, u16 function); + +void hpi_init_message_responseV1(struct hpi_message_header *phm, u16 msg_size, + struct hpi_response_header *phr, u16 res_size, u16 object, + u16 function); diff --git a/sound/pci/asihpi/hpimsgx.c b/sound/pci/asihpi/hpimsgx.c new file mode 100644 index 000000000000..2ee90dc3d897 --- /dev/null +++ b/sound/pci/asihpi/hpimsgx.c @@ -0,0 +1,907 @@ +/****************************************************************************** + + AudioScience HPI driver + Copyright (C) 1997-2010 AudioScience Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation; + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Extended Message Function With Response Cacheing + +(C) Copyright AudioScience Inc. 2002 +*****************************************************************************/ +#define SOURCEFILE_NAME "hpimsgx.c" +#include "hpi_internal.h" +#include "hpimsginit.h" +#include "hpimsgx.h" +#include "hpidebug.h" + +static struct pci_device_id asihpi_pci_tbl[] = { +#include "hpipcida.h" +}; + +static struct hpios_spinlock msgx_lock; + +static hpi_handler_func *hpi_entry_points[HPI_MAX_ADAPTERS]; + +static hpi_handler_func *hpi_lookup_entry_point_function(const struct hpi_pci + *pci_info) +{ + + int i; + + for (i = 0; asihpi_pci_tbl[i].vendor != 0; i++) { + if (asihpi_pci_tbl[i].vendor != PCI_ANY_ID + && asihpi_pci_tbl[i].vendor != pci_info->vendor_id) + continue; + if (asihpi_pci_tbl[i].device != PCI_ANY_ID + && asihpi_pci_tbl[i].device != pci_info->device_id) + continue; + if (asihpi_pci_tbl[i].subvendor != PCI_ANY_ID + && asihpi_pci_tbl[i].subvendor != + pci_info->subsys_vendor_id) + continue; + if (asihpi_pci_tbl[i].subdevice != PCI_ANY_ID + && asihpi_pci_tbl[i].subdevice != + pci_info->subsys_device_id) + continue; + + HPI_DEBUG_LOG(DEBUG, " %x,%lu\n", i, + asihpi_pci_tbl[i].driver_data); + return (hpi_handler_func *) asihpi_pci_tbl[i].driver_data; + } + + return NULL; +} + +static inline void hw_entry_point(struct hpi_message *phm, + struct hpi_response *phr) +{ + + hpi_handler_func *ep; + + if (phm->adapter_index < HPI_MAX_ADAPTERS) { + ep = (hpi_handler_func *) hpi_entry_points[phm-> + adapter_index]; + if (ep) { + HPI_DEBUG_MESSAGE(DEBUG, phm); + ep(phm, phr); + HPI_DEBUG_RESPONSE(phr); + return; + } + } + hpi_init_response(phr, phm->object, phm->function, + HPI_ERROR_PROCESSING_MESSAGE); +} + +static void adapter_open(struct hpi_message *phm, struct hpi_response *phr); +static void adapter_close(struct hpi_message *phm, struct hpi_response *phr); + +static void mixer_open(struct hpi_message *phm, struct hpi_response *phr); +static void mixer_close(struct hpi_message *phm, struct hpi_response *phr); + +static void outstream_open(struct hpi_message *phm, struct hpi_response *phr, + void *h_owner); +static void outstream_close(struct hpi_message *phm, struct hpi_response *phr, + void *h_owner); +static void instream_open(struct hpi_message *phm, struct hpi_response *phr, + void *h_owner); +static void instream_close(struct hpi_message *phm, struct hpi_response *phr, + void *h_owner); + +static void HPIMSGX__reset(u16 adapter_index); +static u16 HPIMSGX__init(struct hpi_message *phm, struct hpi_response *phr); +static void HPIMSGX__cleanup(u16 adapter_index, void *h_owner); + +#ifndef DISABLE_PRAGMA_PACK1 +#pragma pack(push, 1) +#endif + +struct hpi_subsys_response { + struct hpi_response_header h; + struct hpi_subsys_res s; +}; + +struct hpi_adapter_response { + struct hpi_response_header h; + struct hpi_adapter_res a; +}; + +struct hpi_mixer_response { + struct hpi_response_header h; + struct hpi_mixer_res m; +}; + +struct hpi_stream_response { + struct hpi_response_header h; + struct hpi_stream_res d; +}; + +struct adapter_info { + u16 type; + u16 num_instreams; + u16 num_outstreams; +}; + +struct asi_open_state { + int open_flag; + void *h_owner; +}; + +#ifndef DISABLE_PRAGMA_PACK1 +#pragma pack(pop) +#endif + +/* Globals */ +static struct hpi_adapter_response rESP_HPI_ADAPTER_OPEN[HPI_MAX_ADAPTERS]; + +static struct hpi_stream_response + rESP_HPI_OSTREAM_OPEN[HPI_MAX_ADAPTERS][HPI_MAX_STREAMS]; + +static struct hpi_stream_response + rESP_HPI_ISTREAM_OPEN[HPI_MAX_ADAPTERS][HPI_MAX_STREAMS]; + +static struct hpi_mixer_response rESP_HPI_MIXER_OPEN[HPI_MAX_ADAPTERS]; + +static struct hpi_subsys_response gRESP_HPI_SUBSYS_FIND_ADAPTERS; + +static struct adapter_info aDAPTER_INFO[HPI_MAX_ADAPTERS]; + +/* use these to keep track of opens from user mode apps/DLLs */ +static struct asi_open_state + outstream_user_open[HPI_MAX_ADAPTERS][HPI_MAX_STREAMS]; + +static struct asi_open_state + instream_user_open[HPI_MAX_ADAPTERS][HPI_MAX_STREAMS]; + +static void subsys_message(struct hpi_message *phm, struct hpi_response *phr, + void *h_owner) +{ + switch (phm->function) { + case HPI_SUBSYS_GET_VERSION: + hpi_init_response(phr, HPI_OBJ_SUBSYSTEM, + HPI_SUBSYS_GET_VERSION, 0); + phr->u.s.version = HPI_VER >> 8; /* return major.minor */ + phr->u.s.data = HPI_VER; /* return major.minor.release */ + break; + case HPI_SUBSYS_OPEN: + /*do not propagate the message down the chain */ + hpi_init_response(phr, HPI_OBJ_SUBSYSTEM, HPI_SUBSYS_OPEN, 0); + break; + case HPI_SUBSYS_CLOSE: + /*do not propagate the message down the chain */ + hpi_init_response(phr, HPI_OBJ_SUBSYSTEM, HPI_SUBSYS_CLOSE, + 0); + HPIMSGX__cleanup(HPIMSGX_ALLADAPTERS, h_owner); + break; + case HPI_SUBSYS_DRIVER_LOAD: + /* Initialize this module's internal state */ + hpios_msgxlock_init(&msgx_lock); + memset(&hpi_entry_points, 0, sizeof(hpi_entry_points)); + hpios_locked_mem_init(); + /* Init subsys_findadapters response to no-adapters */ + HPIMSGX__reset(HPIMSGX_ALLADAPTERS); + hpi_init_response(phr, HPI_OBJ_SUBSYSTEM, + HPI_SUBSYS_DRIVER_LOAD, 0); + /* individual HPIs dont implement driver load */ + HPI_COMMON(phm, phr); + break; + case HPI_SUBSYS_DRIVER_UNLOAD: + HPI_COMMON(phm, phr); + HPIMSGX__cleanup(HPIMSGX_ALLADAPTERS, h_owner); + hpios_locked_mem_free_all(); + hpi_init_response(phr, HPI_OBJ_SUBSYSTEM, + HPI_SUBSYS_DRIVER_UNLOAD, 0); + return; + + case HPI_SUBSYS_GET_INFO: + HPI_COMMON(phm, phr); + break; + + case HPI_SUBSYS_FIND_ADAPTERS: + memcpy(phr, &gRESP_HPI_SUBSYS_FIND_ADAPTERS, + sizeof(gRESP_HPI_SUBSYS_FIND_ADAPTERS)); + break; + case HPI_SUBSYS_GET_NUM_ADAPTERS: + memcpy(phr, &gRESP_HPI_SUBSYS_FIND_ADAPTERS, + sizeof(gRESP_HPI_SUBSYS_FIND_ADAPTERS)); + phr->function = HPI_SUBSYS_GET_NUM_ADAPTERS; + break; + case HPI_SUBSYS_GET_ADAPTER: + { + int count = phm->adapter_index; + int index = 0; + hpi_init_response(phr, HPI_OBJ_SUBSYSTEM, + HPI_SUBSYS_GET_ADAPTER, 0); + + /* This is complicated by the fact that we want to + * "skip" 0's in the adapter list. + * First, make sure we are pointing to a + * non-zero adapter type. + */ + while (gRESP_HPI_SUBSYS_FIND_ADAPTERS. + s.aw_adapter_list[index] == 0) { + index++; + if (index >= HPI_MAX_ADAPTERS) + break; + } + while (count) { + /* move on to the next adapter */ + index++; + if (index >= HPI_MAX_ADAPTERS) + break; + while (gRESP_HPI_SUBSYS_FIND_ADAPTERS. + s.aw_adapter_list[index] == 0) { + index++; + if (index >= HPI_MAX_ADAPTERS) + break; + } + count--; + } + + if (index < HPI_MAX_ADAPTERS) { + phr->u.s.adapter_index = (u16)index; + phr->u.s.aw_adapter_list[0] = + gRESP_HPI_SUBSYS_FIND_ADAPTERS. + s.aw_adapter_list[index]; + } else { + phr->u.s.adapter_index = 0; + phr->u.s.aw_adapter_list[0] = 0; + phr->error = HPI_ERROR_BAD_ADAPTER_NUMBER; + } + break; + } + case HPI_SUBSYS_CREATE_ADAPTER: + HPIMSGX__init(phm, phr); + break; + case HPI_SUBSYS_DELETE_ADAPTER: + HPIMSGX__cleanup(phm->adapter_index, h_owner); + { + struct hpi_message hm; + struct hpi_response hr; + /* call to HPI_ADAPTER_CLOSE */ + hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, + HPI_ADAPTER_CLOSE); + hm.adapter_index = phm->adapter_index; + hw_entry_point(&hm, &hr); + } + hw_entry_point(phm, phr); + gRESP_HPI_SUBSYS_FIND_ADAPTERS.s. + aw_adapter_list[phm->adapter_index] + = 0; + hpi_entry_points[phm->adapter_index] = NULL; + break; + default: + hw_entry_point(phm, phr); + break; + } +} + +static void adapter_message(struct hpi_message *phm, struct hpi_response *phr, + void *h_owner) +{ + switch (phm->function) { + case HPI_ADAPTER_OPEN: + adapter_open(phm, phr); + break; + case HPI_ADAPTER_CLOSE: + adapter_close(phm, phr); + break; + default: + hw_entry_point(phm, phr); + break; + } +} + +static void mixer_message(struct hpi_message *phm, struct hpi_response *phr) +{ + switch (phm->function) { + case HPI_MIXER_OPEN: + mixer_open(phm, phr); + break; + case HPI_MIXER_CLOSE: + mixer_close(phm, phr); + break; + default: + hw_entry_point(phm, phr); + break; + } +} + +static void outstream_message(struct hpi_message *phm, + struct hpi_response *phr, void *h_owner) +{ + if (phm->obj_index >= aDAPTER_INFO[phm->adapter_index].num_outstreams) { + hpi_init_response(phr, HPI_OBJ_OSTREAM, phm->function, + HPI_ERROR_INVALID_OBJ_INDEX); + return; + } + + switch (phm->function) { + case HPI_OSTREAM_OPEN: + outstream_open(phm, phr, h_owner); + break; + case HPI_OSTREAM_CLOSE: + outstream_close(phm, phr, h_owner); + break; + default: + hw_entry_point(phm, phr); + break; + } +} + +static void instream_message(struct hpi_message *phm, + struct hpi_response *phr, void *h_owner) +{ + if (phm->obj_index >= aDAPTER_INFO[phm->adapter_index].num_instreams) { + hpi_init_response(phr, HPI_OBJ_ISTREAM, phm->function, + HPI_ERROR_INVALID_OBJ_INDEX); + return; + } + + switch (phm->function) { + case HPI_ISTREAM_OPEN: + instream_open(phm, phr, h_owner); + break; + case HPI_ISTREAM_CLOSE: + instream_close(phm, phr, h_owner); + break; + default: + hw_entry_point(phm, phr); + break; + } +} + +/* NOTE: HPI_Message() must be defined in the driver as a wrapper for + * HPI_MessageEx so that functions in hpifunc.c compile. + */ +void hpi_send_recv_ex(struct hpi_message *phm, struct hpi_response *phr, + void *h_owner) +{ + HPI_DEBUG_MESSAGE(DEBUG, phm); + + if (phm->type != HPI_TYPE_MESSAGE) { + hpi_init_response(phr, phm->object, phm->function, + HPI_ERROR_INVALID_TYPE); + return; + } + + if (phm->adapter_index >= HPI_MAX_ADAPTERS + && phm->adapter_index != HPIMSGX_ALLADAPTERS) { + hpi_init_response(phr, phm->object, phm->function, + HPI_ERROR_BAD_ADAPTER_NUMBER); + return; + } + + switch (phm->object) { + case HPI_OBJ_SUBSYSTEM: + subsys_message(phm, phr, h_owner); + break; + + case HPI_OBJ_ADAPTER: + adapter_message(phm, phr, h_owner); + break; + + case HPI_OBJ_MIXER: + mixer_message(phm, phr); + break; + + case HPI_OBJ_OSTREAM: + outstream_message(phm, phr, h_owner); + break; + + case HPI_OBJ_ISTREAM: + instream_message(phm, phr, h_owner); + break; + + default: + hw_entry_point(phm, phr); + break; + } + HPI_DEBUG_RESPONSE(phr); +#if 1 + if (phr->error >= HPI_ERROR_BACKEND_BASE) { + void *ep = NULL; + char *ep_name; + + HPI_DEBUG_MESSAGE(ERROR, phm); + + if (phm->adapter_index < HPI_MAX_ADAPTERS) + ep = hpi_entry_points[phm->adapter_index]; + + /* Don't need this? Have adapter index in debug info + Know at driver load time index->backend mapping */ + if (ep == HPI_6000) + ep_name = "HPI_6000"; + else if (ep == HPI_6205) + ep_name = "HPI_6205"; + else + ep_name = "unknown"; + + HPI_DEBUG_LOG(ERROR, "HPI %s response - error# %d\n", ep_name, + phr->error); + + if (hpi_debug_level >= HPI_DEBUG_LEVEL_VERBOSE) + hpi_debug_data((u16 *)phm, + sizeof(*phm) / sizeof(u16)); + } +#endif +} + +static void adapter_open(struct hpi_message *phm, struct hpi_response *phr) +{ + HPI_DEBUG_LOG(VERBOSE, "adapter_open\n"); + memcpy(phr, &rESP_HPI_ADAPTER_OPEN[phm->adapter_index], + sizeof(rESP_HPI_ADAPTER_OPEN[0])); +} + +static void adapter_close(struct hpi_message *phm, struct hpi_response *phr) +{ + HPI_DEBUG_LOG(VERBOSE, "adapter_close\n"); + hpi_init_response(phr, HPI_OBJ_ADAPTER, HPI_ADAPTER_CLOSE, 0); +} + +static void mixer_open(struct hpi_message *phm, struct hpi_response *phr) +{ + memcpy(phr, &rESP_HPI_MIXER_OPEN[phm->adapter_index], + sizeof(rESP_HPI_MIXER_OPEN[0])); +} + +static void mixer_close(struct hpi_message *phm, struct hpi_response *phr) +{ + hpi_init_response(phr, HPI_OBJ_MIXER, HPI_MIXER_CLOSE, 0); +} + +static void instream_open(struct hpi_message *phm, struct hpi_response *phr, + void *h_owner) +{ + + struct hpi_message hm; + struct hpi_response hr; + + hpi_init_response(phr, HPI_OBJ_ISTREAM, HPI_ISTREAM_OPEN, 0); + + hpios_msgxlock_lock(&msgx_lock); + + if (instream_user_open[phm->adapter_index][phm->obj_index].open_flag) + phr->error = HPI_ERROR_OBJ_ALREADY_OPEN; + else if (rESP_HPI_ISTREAM_OPEN[phm->adapter_index] + [phm->obj_index].h.error) + memcpy(phr, + &rESP_HPI_ISTREAM_OPEN[phm->adapter_index][phm-> + obj_index], + sizeof(rESP_HPI_ISTREAM_OPEN[0][0])); + else { + instream_user_open[phm->adapter_index][phm-> + obj_index].open_flag = 1; + hpios_msgxlock_un_lock(&msgx_lock); + + /* issue a reset */ + hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM, + HPI_ISTREAM_RESET); + hm.adapter_index = phm->adapter_index; + hm.obj_index = phm->obj_index; + hw_entry_point(&hm, &hr); + + hpios_msgxlock_lock(&msgx_lock); + if (hr.error) { + instream_user_open[phm->adapter_index][phm-> + obj_index].open_flag = 0; + phr->error = hr.error; + } else { + instream_user_open[phm->adapter_index][phm-> + obj_index].open_flag = 1; + instream_user_open[phm->adapter_index][phm-> + obj_index].h_owner = h_owner; + memcpy(phr, + &rESP_HPI_ISTREAM_OPEN[phm->adapter_index] + [phm->obj_index], + sizeof(rESP_HPI_ISTREAM_OPEN[0][0])); + } + } + hpios_msgxlock_un_lock(&msgx_lock); +} + +static void instream_close(struct hpi_message *phm, struct hpi_response *phr, + void *h_owner) +{ + + struct hpi_message hm; + struct hpi_response hr; + + hpi_init_response(phr, HPI_OBJ_ISTREAM, HPI_ISTREAM_CLOSE, 0); + + hpios_msgxlock_lock(&msgx_lock); + if (h_owner == + instream_user_open[phm->adapter_index][phm-> + obj_index].h_owner) { + /* HPI_DEBUG_LOG(INFO,"closing adapter %d " + "instream %d owned by %p\n", + phm->wAdapterIndex, phm->wObjIndex, hOwner); */ + instream_user_open[phm->adapter_index][phm-> + obj_index].h_owner = NULL; + hpios_msgxlock_un_lock(&msgx_lock); + /* issue a reset */ + hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM, + HPI_ISTREAM_RESET); + hm.adapter_index = phm->adapter_index; + hm.obj_index = phm->obj_index; + hw_entry_point(&hm, &hr); + hpios_msgxlock_lock(&msgx_lock); + if (hr.error) { + instream_user_open[phm->adapter_index][phm-> + obj_index].h_owner = h_owner; + phr->error = hr.error; + } else { + instream_user_open[phm->adapter_index][phm-> + obj_index].open_flag = 0; + instream_user_open[phm->adapter_index][phm-> + obj_index].h_owner = NULL; + } + } else { + HPI_DEBUG_LOG(WARNING, + "%p trying to close %d instream %d owned by %p\n", + h_owner, phm->adapter_index, phm->obj_index, + instream_user_open[phm->adapter_index][phm-> + obj_index].h_owner); + phr->error = HPI_ERROR_OBJ_NOT_OPEN; + } + hpios_msgxlock_un_lock(&msgx_lock); +} + +static void outstream_open(struct hpi_message *phm, struct hpi_response *phr, + void *h_owner) +{ + + struct hpi_message hm; + struct hpi_response hr; + + hpi_init_response(phr, HPI_OBJ_OSTREAM, HPI_OSTREAM_OPEN, 0); + + hpios_msgxlock_lock(&msgx_lock); + + if (outstream_user_open[phm->adapter_index][phm->obj_index].open_flag) + phr->error = HPI_ERROR_OBJ_ALREADY_OPEN; + else if (rESP_HPI_OSTREAM_OPEN[phm->adapter_index] + [phm->obj_index].h.error) + memcpy(phr, + &rESP_HPI_OSTREAM_OPEN[phm->adapter_index][phm-> + obj_index], + sizeof(rESP_HPI_OSTREAM_OPEN[0][0])); + else { + outstream_user_open[phm->adapter_index][phm-> + obj_index].open_flag = 1; + hpios_msgxlock_un_lock(&msgx_lock); + + /* issue a reset */ + hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, + HPI_OSTREAM_RESET); + hm.adapter_index = phm->adapter_index; + hm.obj_index = phm->obj_index; + hw_entry_point(&hm, &hr); + + hpios_msgxlock_lock(&msgx_lock); + if (hr.error) { + outstream_user_open[phm->adapter_index][phm-> + obj_index].open_flag = 0; + phr->error = hr.error; + } else { + outstream_user_open[phm->adapter_index][phm-> + obj_index].open_flag = 1; + outstream_user_open[phm->adapter_index][phm-> + obj_index].h_owner = h_owner; + memcpy(phr, + &rESP_HPI_OSTREAM_OPEN[phm->adapter_index] + [phm->obj_index], + sizeof(rESP_HPI_OSTREAM_OPEN[0][0])); + } + } + hpios_msgxlock_un_lock(&msgx_lock); +} + +static void outstream_close(struct hpi_message *phm, struct hpi_response *phr, + void *h_owner) +{ + + struct hpi_message hm; + struct hpi_response hr; + + hpi_init_response(phr, HPI_OBJ_OSTREAM, HPI_OSTREAM_CLOSE, 0); + + hpios_msgxlock_lock(&msgx_lock); + + if (h_owner == + outstream_user_open[phm->adapter_index][phm-> + obj_index].h_owner) { + /* HPI_DEBUG_LOG(INFO,"closing adapter %d " + "outstream %d owned by %p\n", + phm->wAdapterIndex, phm->wObjIndex, hOwner); */ + outstream_user_open[phm->adapter_index][phm-> + obj_index].h_owner = NULL; + hpios_msgxlock_un_lock(&msgx_lock); + /* issue a reset */ + hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, + HPI_OSTREAM_RESET); + hm.adapter_index = phm->adapter_index; + hm.obj_index = phm->obj_index; + hw_entry_point(&hm, &hr); + hpios_msgxlock_lock(&msgx_lock); + if (hr.error) { + outstream_user_open[phm->adapter_index][phm-> + obj_index].h_owner = h_owner; + phr->error = hr.error; + } else { + outstream_user_open[phm->adapter_index][phm-> + obj_index].open_flag = 0; + outstream_user_open[phm->adapter_index][phm-> + obj_index].h_owner = NULL; + } + } else { + HPI_DEBUG_LOG(WARNING, + "%p trying to close %d outstream %d owned by %p\n", + h_owner, phm->adapter_index, phm->obj_index, + outstream_user_open[phm->adapter_index][phm-> + obj_index].h_owner); + phr->error = HPI_ERROR_OBJ_NOT_OPEN; + } + hpios_msgxlock_un_lock(&msgx_lock); +} + +static u16 adapter_prepare(u16 adapter) +{ + struct hpi_message hm; + struct hpi_response hr; + + /* Open the adapter and streams */ + u16 i; + + /* call to HPI_ADAPTER_OPEN */ + hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, + HPI_ADAPTER_OPEN); + hm.adapter_index = adapter; + hw_entry_point(&hm, &hr); + memcpy(&rESP_HPI_ADAPTER_OPEN[adapter], &hr, + sizeof(rESP_HPI_ADAPTER_OPEN[0])); + if (hr.error) + return hr.error; + + /* call to HPI_ADAPTER_GET_INFO */ + hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, + HPI_ADAPTER_GET_INFO); + hm.adapter_index = adapter; + hw_entry_point(&hm, &hr); + if (hr.error) + return hr.error; + + aDAPTER_INFO[adapter].num_outstreams = hr.u.a.num_outstreams; + aDAPTER_INFO[adapter].num_instreams = hr.u.a.num_instreams; + aDAPTER_INFO[adapter].type = hr.u.a.adapter_type; + + gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.aw_adapter_list[adapter] = + hr.u.a.adapter_type; + gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.num_adapters++; + if (gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.num_adapters > HPI_MAX_ADAPTERS) + gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.num_adapters = + HPI_MAX_ADAPTERS; + + /* call to HPI_OSTREAM_OPEN */ + for (i = 0; i < aDAPTER_INFO[adapter].num_outstreams; i++) { + hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, + HPI_OSTREAM_OPEN); + hm.adapter_index = adapter; + hm.obj_index = i; + hw_entry_point(&hm, &hr); + memcpy(&rESP_HPI_OSTREAM_OPEN[adapter][i], &hr, + sizeof(rESP_HPI_OSTREAM_OPEN[0][0])); + outstream_user_open[adapter][i].open_flag = 0; + outstream_user_open[adapter][i].h_owner = NULL; + } + + /* call to HPI_ISTREAM_OPEN */ + for (i = 0; i < aDAPTER_INFO[adapter].num_instreams; i++) { + hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM, + HPI_ISTREAM_OPEN); + hm.adapter_index = adapter; + hm.obj_index = i; + hw_entry_point(&hm, &hr); + memcpy(&rESP_HPI_ISTREAM_OPEN[adapter][i], &hr, + sizeof(rESP_HPI_ISTREAM_OPEN[0][0])); + instream_user_open[adapter][i].open_flag = 0; + instream_user_open[adapter][i].h_owner = NULL; + } + + /* call to HPI_MIXER_OPEN */ + hpi_init_message_response(&hm, &hr, HPI_OBJ_MIXER, HPI_MIXER_OPEN); + hm.adapter_index = adapter; + hw_entry_point(&hm, &hr); + memcpy(&rESP_HPI_MIXER_OPEN[adapter], &hr, + sizeof(rESP_HPI_MIXER_OPEN[0])); + + return gRESP_HPI_SUBSYS_FIND_ADAPTERS.h.error; +} + +static void HPIMSGX__reset(u16 adapter_index) +{ + int i; + u16 adapter; + struct hpi_response hr; + + if (adapter_index == HPIMSGX_ALLADAPTERS) { + /* reset all responses to contain errors */ + hpi_init_response(&hr, HPI_OBJ_SUBSYSTEM, + HPI_SUBSYS_FIND_ADAPTERS, 0); + memcpy(&gRESP_HPI_SUBSYS_FIND_ADAPTERS, &hr, + sizeof(&gRESP_HPI_SUBSYS_FIND_ADAPTERS)); + + for (adapter = 0; adapter < HPI_MAX_ADAPTERS; adapter++) { + + hpi_init_response(&hr, HPI_OBJ_ADAPTER, + HPI_ADAPTER_OPEN, HPI_ERROR_BAD_ADAPTER); + memcpy(&rESP_HPI_ADAPTER_OPEN[adapter], &hr, + sizeof(rESP_HPI_ADAPTER_OPEN[adapter])); + + hpi_init_response(&hr, HPI_OBJ_MIXER, HPI_MIXER_OPEN, + HPI_ERROR_INVALID_OBJ); + memcpy(&rESP_HPI_MIXER_OPEN[adapter], &hr, + sizeof(rESP_HPI_MIXER_OPEN[adapter])); + + for (i = 0; i < HPI_MAX_STREAMS; i++) { + hpi_init_response(&hr, HPI_OBJ_OSTREAM, + HPI_OSTREAM_OPEN, + HPI_ERROR_INVALID_OBJ); + memcpy(&rESP_HPI_OSTREAM_OPEN[adapter][i], + &hr, + sizeof(rESP_HPI_OSTREAM_OPEN[adapter] + [i])); + hpi_init_response(&hr, HPI_OBJ_ISTREAM, + HPI_ISTREAM_OPEN, + HPI_ERROR_INVALID_OBJ); + memcpy(&rESP_HPI_ISTREAM_OPEN[adapter][i], + &hr, + sizeof(rESP_HPI_ISTREAM_OPEN[adapter] + [i])); + } + } + } else if (adapter_index < HPI_MAX_ADAPTERS) { + rESP_HPI_ADAPTER_OPEN[adapter_index].h.error = + HPI_ERROR_BAD_ADAPTER; + rESP_HPI_MIXER_OPEN[adapter_index].h.error = + HPI_ERROR_INVALID_OBJ; + for (i = 0; i < HPI_MAX_STREAMS; i++) { + rESP_HPI_OSTREAM_OPEN[adapter_index][i].h.error = + HPI_ERROR_INVALID_OBJ; + rESP_HPI_ISTREAM_OPEN[adapter_index][i].h.error = + HPI_ERROR_INVALID_OBJ; + } + if (gRESP_HPI_SUBSYS_FIND_ADAPTERS. + s.aw_adapter_list[adapter_index]) { + gRESP_HPI_SUBSYS_FIND_ADAPTERS. + s.aw_adapter_list[adapter_index] = 0; + gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.num_adapters--; + } + } +} + +static u16 HPIMSGX__init(struct hpi_message *phm, + /* HPI_SUBSYS_CREATE_ADAPTER structure with */ + /* resource list or NULL=find all */ + struct hpi_response *phr + /* response from HPI_ADAPTER_GET_INFO */ + ) +{ + hpi_handler_func *entry_point_func; + struct hpi_response hr; + + if (gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.num_adapters >= HPI_MAX_ADAPTERS) + return HPI_ERROR_BAD_ADAPTER_NUMBER; + + /* Init response here so we can pass in previous adapter list */ + hpi_init_response(&hr, phm->object, phm->function, + HPI_ERROR_INVALID_OBJ); + memcpy(hr.u.s.aw_adapter_list, + gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.aw_adapter_list, + sizeof(gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.aw_adapter_list)); + + entry_point_func = + hpi_lookup_entry_point_function(phm->u.s.resource.r.pci); + + if (entry_point_func) { + HPI_DEBUG_MESSAGE(DEBUG, phm); + entry_point_func(phm, &hr); + } else { + phr->error = HPI_ERROR_PROCESSING_MESSAGE; + return phr->error; + } + if (hr.error == 0) { + /* the adapter was created succesfully + save the mapping for future use */ + hpi_entry_points[hr.u.s.adapter_index] = entry_point_func; + /* prepare adapter (pre-open streams etc.) */ + HPI_DEBUG_LOG(DEBUG, + "HPI_SUBSYS_CREATE_ADAPTER successful," + " preparing adapter\n"); + adapter_prepare(hr.u.s.adapter_index); + } + memcpy(phr, &hr, hr.size); + return phr->error; +} + +static void HPIMSGX__cleanup(u16 adapter_index, void *h_owner) +{ + int i, adapter, adapter_limit; + + if (!h_owner) + return; + + if (adapter_index == HPIMSGX_ALLADAPTERS) { + adapter = 0; + adapter_limit = HPI_MAX_ADAPTERS; + } else { + adapter = adapter_index; + adapter_limit = adapter + 1; + } + + for (; adapter < adapter_limit; adapter++) { + /* printk(KERN_INFO "Cleanup adapter #%d\n",wAdapter); */ + for (i = 0; i < HPI_MAX_STREAMS; i++) { + if (h_owner == + outstream_user_open[adapter][i].h_owner) { + struct hpi_message hm; + struct hpi_response hr; + + HPI_DEBUG_LOG(DEBUG, + "close adapter %d ostream %d\n", + adapter, i); + + hpi_init_message_response(&hm, &hr, + HPI_OBJ_OSTREAM, HPI_OSTREAM_RESET); + hm.adapter_index = (u16)adapter; + hm.obj_index = (u16)i; + hw_entry_point(&hm, &hr); + + hm.function = HPI_OSTREAM_HOSTBUFFER_FREE; + hw_entry_point(&hm, &hr); + + hm.function = HPI_OSTREAM_GROUP_RESET; + hw_entry_point(&hm, &hr); + + outstream_user_open[adapter][i].open_flag = 0; + outstream_user_open[adapter][i].h_owner = + NULL; + } + if (h_owner == instream_user_open[adapter][i].h_owner) { + struct hpi_message hm; + struct hpi_response hr; + + HPI_DEBUG_LOG(DEBUG, + "close adapter %d istream %d\n", + adapter, i); + + hpi_init_message_response(&hm, &hr, + HPI_OBJ_ISTREAM, HPI_ISTREAM_RESET); + hm.adapter_index = (u16)adapter; + hm.obj_index = (u16)i; + hw_entry_point(&hm, &hr); + + hm.function = HPI_ISTREAM_HOSTBUFFER_FREE; + hw_entry_point(&hm, &hr); + + hm.function = HPI_ISTREAM_GROUP_RESET; + hw_entry_point(&hm, &hr); + + instream_user_open[adapter][i].open_flag = 0; + instream_user_open[adapter][i].h_owner = NULL; + } + } + } +} diff --git a/sound/pci/asihpi/hpimsgx.h b/sound/pci/asihpi/hpimsgx.h new file mode 100644 index 000000000000..fd49e7542a88 --- /dev/null +++ b/sound/pci/asihpi/hpimsgx.h @@ -0,0 +1,36 @@ +/****************************************************************************** + + AudioScience HPI driver + Copyright (C) 1997-2010 AudioScience Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation; + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + HPI Extended Message Handler Functions + +(C) Copyright AudioScience Inc. 1997-2003 +******************************************************************************/ + +#ifndef _HPIMSGX_H_ +#define _HPIMSGX_H_ + +#include "hpi_internal.h" + +#define HPIMSGX_ALLADAPTERS (0xFFFF) + +void hpi_send_recv_ex(struct hpi_message *phm, struct hpi_response *phr, + void *h_owner); + +#define HPI_MESSAGE_LOWER_LAYER hpi_send_recv_ex + +#endif /* _HPIMSGX_H_ */ diff --git a/sound/pci/asihpi/hpioctl.c b/sound/pci/asihpi/hpioctl.c new file mode 100644 index 000000000000..7396ac54e99f --- /dev/null +++ b/sound/pci/asihpi/hpioctl.c @@ -0,0 +1,484 @@ +/******************************************************************************* + + AudioScience HPI driver + Copyright (C) 1997-2010 AudioScience Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation; + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Common Linux HPI ioctl and module probe/remove functions +*******************************************************************************/ +#define SOURCEFILE_NAME "hpioctl.c" + +#include "hpi_internal.h" +#include "hpimsginit.h" +#include "hpidebug.h" +#include "hpimsgx.h" +#include "hpioctl.h" + +#include +#include +#include +#include +#include + +#ifdef MODULE_FIRMWARE +MODULE_FIRMWARE("asihpi/dsp5000.bin"); +MODULE_FIRMWARE("asihpi/dsp6200.bin"); +MODULE_FIRMWARE("asihpi/dsp6205.bin"); +MODULE_FIRMWARE("asihpi/dsp6400.bin"); +MODULE_FIRMWARE("asihpi/dsp6600.bin"); +MODULE_FIRMWARE("asihpi/dsp8700.bin"); +MODULE_FIRMWARE("asihpi/dsp8900.bin"); +#endif + +static int prealloc_stream_buf; +module_param(prealloc_stream_buf, int, S_IRUGO); +MODULE_PARM_DESC(prealloc_stream_buf, + "preallocate size for per-adapter stream buffer"); + +/* Allow the debug level to be changed after module load. + E.g. echo 2 > /sys/module/asihpi/parameters/hpiDebugLevel +*/ +module_param(hpi_debug_level, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(hpi_debug_level, "debug verbosity 0..5"); + +/* List of adapters found */ +static struct hpi_adapter adapters[HPI_MAX_ADAPTERS]; + +/* Wrapper function to HPI_Message to enable dumping of the + message and response types. +*/ +static void hpi_send_recv_f(struct hpi_message *phm, struct hpi_response *phr, + struct file *file) +{ + int adapter = phm->adapter_index; + + if ((adapter >= HPI_MAX_ADAPTERS || adapter < 0) + && (phm->object != HPI_OBJ_SUBSYSTEM)) + phr->error = HPI_ERROR_INVALID_OBJ_INDEX; + else + hpi_send_recv_ex(phm, phr, file); +} + +/* This is called from hpifunc.c functions, called by ALSA + * (or other kernel process) In this case there is no file descriptor + * available for the message cache code + */ +void hpi_send_recv(struct hpi_message *phm, struct hpi_response *phr) +{ + hpi_send_recv_f(phm, phr, HOWNER_KERNEL); +} + +EXPORT_SYMBOL(hpi_send_recv); +/* for radio-asihpi */ + +int asihpi_hpi_release(struct file *file) +{ + struct hpi_message hm; + struct hpi_response hr; + +/* HPI_DEBUG_LOG(INFO,"hpi_release file %p, pid %d\n", file, current->pid); */ + /* close the subsystem just in case the application forgot to. */ + hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM, + HPI_SUBSYS_CLOSE); + hpi_send_recv_ex(&hm, &hr, file); + return 0; +} + +long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct hpi_ioctl_linux __user *phpi_ioctl_data; + void __user *puhm; + void __user *puhr; + union hpi_message_buffer_v1 *hm; + union hpi_response_buffer_v1 *hr; + u16 res_max_size; + u32 uncopied_bytes; + struct hpi_adapter *pa = NULL; + int err = 0; + + if (cmd != HPI_IOCTL_LINUX) + return -EINVAL; + + hm = kmalloc(sizeof(*hm), GFP_KERNEL); + hr = kmalloc(sizeof(*hr), GFP_KERNEL); + if (!hm || !hr) { + err = -ENOMEM; + goto out; + } + + phpi_ioctl_data = (struct hpi_ioctl_linux __user *)arg; + + /* Read the message and response pointers from user space. */ + get_user(puhm, &phpi_ioctl_data->phm); + get_user(puhr, &phpi_ioctl_data->phr); + + /* Now read the message size and data from user space. */ + get_user(hm->h.size, (u16 __user *)puhm); + if (hm->h.size > sizeof(*hm)) + hm->h.size = sizeof(*hm); + + /*printk(KERN_INFO "message size %d\n", hm->h.wSize); */ + + uncopied_bytes = copy_from_user(hm, puhm, hm->h.size); + if (uncopied_bytes) { + HPI_DEBUG_LOG(ERROR, "uncopied bytes %d\n", uncopied_bytes); + err = -EFAULT; + goto out; + } + + get_user(res_max_size, (u16 __user *)puhr); + /* printk(KERN_INFO "user response size %d\n", res_max_size); */ + if (res_max_size < sizeof(struct hpi_response_header)) { + HPI_DEBUG_LOG(WARNING, "small res size %d\n", res_max_size); + err = -EFAULT; + goto out; + } + + pa = &adapters[hm->h.adapter_index]; + hr->h.size = 0; + if (hm->h.object == HPI_OBJ_SUBSYSTEM) { + switch (hm->h.function) { + case HPI_SUBSYS_CREATE_ADAPTER: + case HPI_SUBSYS_DELETE_ADAPTER: + /* Application must not use these functions! */ + hr->h.size = sizeof(hr->h); + hr->h.error = HPI_ERROR_INVALID_OPERATION; + hr->h.function = hm->h.function; + uncopied_bytes = copy_to_user(puhr, hr, hr->h.size); + if (uncopied_bytes) + err = -EFAULT; + else + err = 0; + goto out; + + default: + hpi_send_recv_f(&hm->m0, &hr->r0, file); + } + } else { + u16 __user *ptr = NULL; + u32 size = 0; + + /* -1=no data 0=read from user mem, 1=write to user mem */ + int wrflag = -1; + u32 adapter = hm->h.adapter_index; + + if ((hm->h.adapter_index > HPI_MAX_ADAPTERS) || (!pa->type)) { + hpi_init_response(&hr->r0, HPI_OBJ_ADAPTER, + HPI_ADAPTER_OPEN, + HPI_ERROR_BAD_ADAPTER_NUMBER); + + uncopied_bytes = + copy_to_user(puhr, hr, sizeof(hr->h)); + if (uncopied_bytes) + err = -EFAULT; + else + err = 0; + goto out; + } + + if (mutex_lock_interruptible(&adapters[adapter].mutex)) { + err = -EINTR; + goto out; + } + + /* Dig out any pointers embedded in the message. */ + switch (hm->h.function) { + case HPI_OSTREAM_WRITE: + case HPI_ISTREAM_READ:{ + /* Yes, sparse, this is correct. */ + ptr = (u16 __user *)hm->m0.u.d.u.data.pb_data; + size = hm->m0.u.d.u.data.data_size; + + /* Allocate buffer according to application request. + ?Is it better to alloc/free for the duration + of the transaction? + */ + if (pa->buffer_size < size) { + HPI_DEBUG_LOG(DEBUG, + "realloc adapter %d stream " + "buffer from %zd to %d\n", + hm->h.adapter_index, + pa->buffer_size, size); + if (pa->p_buffer) { + pa->buffer_size = 0; + vfree(pa->p_buffer); + } + pa->p_buffer = vmalloc(size); + if (pa->p_buffer) + pa->buffer_size = size; + else { + HPI_DEBUG_LOG(ERROR, + "HPI could not allocate " + "stream buffer size %d\n", + size); + + mutex_unlock(&adapters + [adapter].mutex); + err = -EINVAL; + goto out; + } + } + + hm->m0.u.d.u.data.pb_data = pa->p_buffer; + if (hm->h.function == HPI_ISTREAM_READ) + /* from card, WRITE to user mem */ + wrflag = 1; + else + wrflag = 0; + break; + } + + default: + size = 0; + break; + } + + if (size && (wrflag == 0)) { + uncopied_bytes = + copy_from_user(pa->p_buffer, ptr, size); + if (uncopied_bytes) + HPI_DEBUG_LOG(WARNING, + "missed %d of %d " + "bytes from user\n", uncopied_bytes, + size); + } + + hpi_send_recv_f(&hm->m0, &hr->r0, file); + + if (size && (wrflag == 1)) { + uncopied_bytes = + copy_to_user(ptr, pa->p_buffer, size); + if (uncopied_bytes) + HPI_DEBUG_LOG(WARNING, + "missed %d of %d " "bytes to user\n", + uncopied_bytes, size); + } + + mutex_unlock(&adapters[adapter].mutex); + } + + /* on return response size must be set */ + /*printk(KERN_INFO "response size %d\n", hr->h.wSize); */ + + if (!hr->h.size) { + HPI_DEBUG_LOG(ERROR, "response zero size\n"); + err = -EFAULT; + goto out; + } + + if (hr->h.size > res_max_size) { + HPI_DEBUG_LOG(ERROR, "response too big %d %d\n", hr->h.size, + res_max_size); + /*HPI_DEBUG_MESSAGE(ERROR, hm); */ + err = -EFAULT; + goto out; + } + + uncopied_bytes = copy_to_user(puhr, hr, hr->h.size); + if (uncopied_bytes) { + HPI_DEBUG_LOG(ERROR, "uncopied bytes %d\n", uncopied_bytes); + err = -EFAULT; + goto out; + } + +out: + kfree(hm); + kfree(hr); + return err; +} + +int __devinit asihpi_adapter_probe(struct pci_dev *pci_dev, + const struct pci_device_id *pci_id) +{ + int err, idx, nm; + unsigned int memlen; + struct hpi_message hm; + struct hpi_response hr; + struct hpi_adapter adapter; + struct hpi_pci pci; + + memset(&adapter, 0, sizeof(adapter)); + + printk(KERN_DEBUG "probe PCI device (%04x:%04x,%04x:%04x,%04x)\n", + pci_dev->vendor, pci_dev->device, pci_dev->subsystem_vendor, + pci_dev->subsystem_device, pci_dev->devfn); + + hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM, + HPI_SUBSYS_CREATE_ADAPTER); + hpi_init_response(&hr, HPI_OBJ_SUBSYSTEM, HPI_SUBSYS_CREATE_ADAPTER, + HPI_ERROR_PROCESSING_MESSAGE); + + hm.adapter_index = -1; /* an invalid index */ + + /* fill in HPI_PCI information from kernel provided information */ + adapter.pci = pci_dev; + + nm = HPI_MAX_ADAPTER_MEM_SPACES; + + for (idx = 0; idx < nm; idx++) { + HPI_DEBUG_LOG(INFO, "resource %d %s %08llx-%08llx %04llx\n", + idx, pci_dev->resource[idx].name, + (unsigned long long)pci_resource_start(pci_dev, idx), + (unsigned long long)pci_resource_end(pci_dev, idx), + (unsigned long long)pci_resource_flags(pci_dev, idx)); + + if (pci_resource_flags(pci_dev, idx) & IORESOURCE_MEM) { + memlen = pci_resource_len(pci_dev, idx); + adapter.ap_remapped_mem_base[idx] = + ioremap(pci_resource_start(pci_dev, idx), + memlen); + if (!adapter.ap_remapped_mem_base[idx]) { + HPI_DEBUG_LOG(ERROR, + "ioremap failed, aborting\n"); + /* unmap previously mapped pci mem space */ + goto err; + } + } + + pci.ap_mem_base[idx] = adapter.ap_remapped_mem_base[idx]; + } + + /* could replace Pci with direct pointer to pci_dev for linux + Instead wrap accessor functions for IDs etc. + Would it work for windows? + */ + pci.bus_number = pci_dev->bus->number; + pci.vendor_id = (u16)pci_dev->vendor; + pci.device_id = (u16)pci_dev->device; + pci.subsys_vendor_id = (u16)(pci_dev->subsystem_vendor & 0xffff); + pci.subsys_device_id = (u16)(pci_dev->subsystem_device & 0xffff); + pci.device_number = pci_dev->devfn; + pci.interrupt = pci_dev->irq; + pci.p_os_data = pci_dev; + + hm.u.s.resource.bus_type = HPI_BUS_PCI; + hm.u.s.resource.r.pci = &pci; + + /* call CreateAdapterObject on the relevant hpi module */ + hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL); + if (hr.error) + goto err; + + if (prealloc_stream_buf) { + adapter.p_buffer = vmalloc(prealloc_stream_buf); + if (!adapter.p_buffer) { + HPI_DEBUG_LOG(ERROR, + "HPI could not allocate " + "kernel buffer size %d\n", + prealloc_stream_buf); + goto err; + } + } + + adapter.index = hr.u.s.adapter_index; + adapter.type = hr.u.s.aw_adapter_list[adapter.index]; + hm.adapter_index = adapter.index; + + err = hpi_adapter_open(NULL, adapter.index); + if (err) + goto err; + + adapter.snd_card_asihpi = NULL; + /* WARNING can't init mutex in 'adapter' + * and then copy it to adapters[] ?!?! + */ + adapters[hr.u.s.adapter_index] = adapter; + mutex_init(&adapters[adapter.index].mutex); + pci_set_drvdata(pci_dev, &adapters[adapter.index]); + + printk(KERN_INFO "probe found adapter ASI%04X HPI index #%d.\n", + adapter.type, adapter.index); + + return 0; + +err: + for (idx = 0; idx < HPI_MAX_ADAPTER_MEM_SPACES; idx++) { + if (adapter.ap_remapped_mem_base[idx]) { + iounmap(adapter.ap_remapped_mem_base[idx]); + adapter.ap_remapped_mem_base[idx] = NULL; + } + } + + if (adapter.p_buffer) { + adapter.buffer_size = 0; + vfree(adapter.p_buffer); + } + + HPI_DEBUG_LOG(ERROR, "adapter_probe failed\n"); + return -ENODEV; +} + +void __devexit asihpi_adapter_remove(struct pci_dev *pci_dev) +{ + int idx; + struct hpi_message hm; + struct hpi_response hr; + struct hpi_adapter *pa; + pa = (struct hpi_adapter *)pci_get_drvdata(pci_dev); + + hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM, + HPI_SUBSYS_DELETE_ADAPTER); + hm.adapter_index = pa->index; + hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL); + + /* unmap PCI memory space, mapped during device init. */ + for (idx = 0; idx < HPI_MAX_ADAPTER_MEM_SPACES; idx++) { + if (pa->ap_remapped_mem_base[idx]) { + iounmap(pa->ap_remapped_mem_base[idx]); + pa->ap_remapped_mem_base[idx] = NULL; + } + } + + if (pa->p_buffer) { + pa->buffer_size = 0; + vfree(pa->p_buffer); + } + + pci_set_drvdata(pci_dev, NULL); + /* + printk(KERN_INFO "PCI device (%04x:%04x,%04x:%04x,%04x)," + " HPI index # %d, removed.\n", + pci_dev->vendor, pci_dev->device, + pci_dev->subsystem_vendor, + pci_dev->subsystem_device, pci_dev->devfn, + pa->index); + */ +} + +void __init asihpi_init(void) +{ + struct hpi_message hm; + struct hpi_response hr; + + memset(adapters, 0, sizeof(adapters)); + + printk(KERN_INFO "ASIHPI driver %d.%02d.%02d\n", + HPI_VER_MAJOR(HPI_VER), HPI_VER_MINOR(HPI_VER), + HPI_VER_RELEASE(HPI_VER)); + + hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM, + HPI_SUBSYS_DRIVER_LOAD); + hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL); +} + +void asihpi_exit(void) +{ + struct hpi_message hm; + struct hpi_response hr; + + hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM, + HPI_SUBSYS_DRIVER_UNLOAD); + hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL); +} diff --git a/sound/pci/asihpi/hpioctl.h b/sound/pci/asihpi/hpioctl.h new file mode 100644 index 000000000000..847f72f03fe1 --- /dev/null +++ b/sound/pci/asihpi/hpioctl.h @@ -0,0 +1,38 @@ +/******************************************************************************* + + AudioScience HPI driver + Copyright (C) 1997-2010 AudioScience Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation; + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Linux HPI ioctl, and shared module init functions +*******************************************************************************/ + +int __devinit asihpi_adapter_probe(struct pci_dev *pci_dev, + const struct pci_device_id *pci_id); +void __devexit asihpi_adapter_remove(struct pci_dev *pci_dev); +void __init asihpi_init(void); +void __exit asihpi_exit(void); + +int asihpi_hpi_release(struct file *file); + +long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg); + +/* This is called from hpifunc.c functions, called by ALSA + * (or other kernel process) In this case there is no file descriptor + * available for the message cache code + */ +void hpi_send_recv(struct hpi_message *phm, struct hpi_response *phr); + +#define HOWNER_KERNEL ((void *)-1) diff --git a/sound/pci/asihpi/hpios.c b/sound/pci/asihpi/hpios.c new file mode 100644 index 000000000000..de615cfdb950 --- /dev/null +++ b/sound/pci/asihpi/hpios.c @@ -0,0 +1,114 @@ +/****************************************************************************** + + AudioScience HPI driver + Copyright (C) 1997-2010 AudioScience Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation; + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +HPI Operating System function implementation for Linux + +(C) Copyright AudioScience Inc. 1997-2003 +******************************************************************************/ +#define SOURCEFILE_NAME "hpios.c" +#include "hpi_internal.h" +#include "hpidebug.h" +#include +#include + +void hpios_delay_micro_seconds(u32 num_micro_sec) +{ + if ((usecs_to_jiffies(num_micro_sec) > 1) && !in_interrupt()) { + /* MUST NOT SCHEDULE IN INTERRUPT CONTEXT! */ + schedule_timeout_uninterruptible(usecs_to_jiffies + (num_micro_sec)); + } else if (num_micro_sec <= 2000) + udelay(num_micro_sec); + else + mdelay(num_micro_sec / 1000); + +} + +void hpios_locked_mem_init(void) +{ +} + +/** Allocated an area of locked memory for bus master DMA operations. + +On error, return -ENOMEM, and *pMemArea.size = 0 +*/ +u16 hpios_locked_mem_alloc(struct consistent_dma_area *p_mem_area, u32 size, + struct pci_dev *pdev) +{ + /*?? any benefit in using managed dmam_alloc_coherent? */ + p_mem_area->vaddr = + dma_alloc_coherent(&pdev->dev, size, &p_mem_area->dma_handle, + GFP_DMA32 | GFP_KERNEL); + + if (p_mem_area->vaddr) { + HPI_DEBUG_LOG(DEBUG, "allocated %d bytes, dma 0x%x vma %p\n", + size, (unsigned int)p_mem_area->dma_handle, + p_mem_area->vaddr); + p_mem_area->pdev = &pdev->dev; + p_mem_area->size = size; + return 0; + } else { + HPI_DEBUG_LOG(WARNING, + "failed to allocate %d bytes locked memory\n", size); + p_mem_area->size = 0; + return -ENOMEM; + } +} + +u16 hpios_locked_mem_free(struct consistent_dma_area *p_mem_area) +{ + if (p_mem_area->size) { + dma_free_coherent(p_mem_area->pdev, p_mem_area->size, + p_mem_area->vaddr, p_mem_area->dma_handle); + HPI_DEBUG_LOG(DEBUG, "freed %lu bytes, dma 0x%x vma %p\n", + (unsigned long)p_mem_area->size, + (unsigned int)p_mem_area->dma_handle, + p_mem_area->vaddr); + p_mem_area->size = 0; + return 0; + } else { + return 1; + } +} + +void hpios_locked_mem_free_all(void) +{ +} + +void __iomem *hpios_map_io(struct pci_dev *pci_dev, int idx, + unsigned int length) +{ + HPI_DEBUG_LOG(DEBUG, "mapping %d %s %08llx-%08llx %04llx len 0x%x\n", + idx, pci_dev->resource[idx].name, + (unsigned long long)pci_resource_start(pci_dev, idx), + (unsigned long long)pci_resource_end(pci_dev, idx), + (unsigned long long)pci_resource_flags(pci_dev, idx), length); + + if (!(pci_resource_flags(pci_dev, idx) & IORESOURCE_MEM)) { + HPI_DEBUG_LOG(ERROR, "not an io memory resource\n"); + return NULL; + } + + if (length > pci_resource_len(pci_dev, idx)) { + HPI_DEBUG_LOG(ERROR, "resource too small for requested %d \n", + length); + return NULL; + } + + return ioremap(pci_resource_start(pci_dev, idx), length); +} diff --git a/sound/pci/asihpi/hpios.h b/sound/pci/asihpi/hpios.h new file mode 100644 index 000000000000..a62c3f1e5f09 --- /dev/null +++ b/sound/pci/asihpi/hpios.h @@ -0,0 +1,178 @@ +/****************************************************************************** + + AudioScience HPI driver + Copyright (C) 1997-2010 AudioScience Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation; + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +HPI Operating System Specific macros for Linux Kernel driver + +(C) Copyright AudioScience Inc. 1997-2003 +******************************************************************************/ +#ifndef _HPIOS_H_ +#define _HPIOS_H_ + +#undef HPI_OS_LINUX_KERNEL +#define HPI_OS_LINUX_KERNEL + +#define HPI_OS_DEFINED +#define HPI_KERNEL_MODE + +#define HPI_REASSIGN_DUPLICATE_ADAPTER_IDX + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define HPI_NO_OS_FILE_OPS + +#ifdef CONFIG_64BIT +#define HPI64BIT +#endif + +/** Details of a memory area allocated with pci_alloc_consistent +Need all info for parameters to pci_free_consistent +*/ +struct consistent_dma_area { + struct device *pdev; + /* looks like dma-mapping dma_devres ?! */ + size_t size; + void *vaddr; + dma_addr_t dma_handle; +}; + +static inline u16 hpios_locked_mem_get_phys_addr(struct consistent_dma_area + *locked_mem_handle, u32 *p_physical_addr) +{ + *p_physical_addr = locked_mem_handle->dma_handle; + return 0; +} + +static inline u16 hpios_locked_mem_get_virt_addr(struct consistent_dma_area + *locked_mem_handle, void **pp_virtual_addr) +{ + *pp_virtual_addr = locked_mem_handle->vaddr; + return 0; +} + +static inline u16 hpios_locked_mem_valid(struct consistent_dma_area + *locked_mem_handle) +{ + return locked_mem_handle->size != 0; +} + +struct hpi_ioctl_linux { + void __user *phm; + void __user *phr; +}; + +/* Conflict?: H is already used by a number of drivers hid, bluetooth hci, + and some sound drivers sb16, hdsp, emu10k. AFAIK 0xFC is ununsed command +*/ +#define HPI_IOCTL_LINUX _IOWR('H', 0xFC, struct hpi_ioctl_linux) + +#define HPI_DEBUG_FLAG_ERROR KERN_ERR +#define HPI_DEBUG_FLAG_WARNING KERN_WARNING +#define HPI_DEBUG_FLAG_NOTICE KERN_NOTICE +#define HPI_DEBUG_FLAG_INFO KERN_INFO +#define HPI_DEBUG_FLAG_DEBUG KERN_DEBUG +#define HPI_DEBUG_FLAG_VERBOSE KERN_DEBUG /* kernel has no verbose */ + +#include + +#define HPI_LOCKING + +struct hpios_spinlock { + spinlock_t lock; /* SEE hpios_spinlock */ + int lock_context; +}; + +/* The reason for all this evilness is that ALSA calls some of a drivers + * operators in atomic context, and some not. But all our functions channel + * through the HPI_Message conduit, so we can't handle the different context + * per function + */ +#define IN_LOCK_BH 1 +#define IN_LOCK_IRQ 0 +static inline void cond_lock(struct hpios_spinlock *l) +{ + if (irqs_disabled()) { + /* NO bh or isr can execute on this processor, + so ordinary lock will do + */ + spin_lock(&((l)->lock)); + l->lock_context = IN_LOCK_IRQ; + } else { + spin_lock_bh(&((l)->lock)); + l->lock_context = IN_LOCK_BH; + } +} + +static inline void cond_unlock(struct hpios_spinlock *l) +{ + if (l->lock_context == IN_LOCK_BH) + spin_unlock_bh(&((l)->lock)); + else + spin_unlock(&((l)->lock)); +} + +#define hpios_msgxlock_init(obj) spin_lock_init(&(obj)->lock) +#define hpios_msgxlock_lock(obj) cond_lock(obj) +#define hpios_msgxlock_un_lock(obj) cond_unlock(obj) + +#define hpios_dsplock_init(obj) spin_lock_init(&(obj)->dsp_lock.lock) +#define hpios_dsplock_lock(obj) cond_lock(&(obj)->dsp_lock) +#define hpios_dsplock_unlock(obj) cond_unlock(&(obj)->dsp_lock) + +#ifdef CONFIG_SND_DEBUG +#define HPI_DEBUG +#endif + +#define HPI_ALIST_LOCKING +#define hpios_alistlock_init(obj) spin_lock_init(&((obj)->list_lock.lock)) +#define hpios_alistlock_lock(obj) spin_lock(&((obj)->list_lock.lock)) +#define hpios_alistlock_un_lock(obj) spin_unlock(&((obj)->list_lock.lock)) + +struct hpi_adapter { + /* mutex prevents contention for one card + between multiple user programs (via ioctl) */ + struct mutex mutex; + u16 index; + u16 type; + + /* ALSA card structure */ + void *snd_card_asihpi; + + char *p_buffer; + size_t buffer_size; + struct pci_dev *pci; + void __iomem *ap_remapped_mem_base[HPI_MAX_ADAPTER_MEM_SPACES]; +}; + +static inline void hpios_unmap_io(void __iomem *addr, + unsigned long size) +{ + iounmap(addr); +} + +void __iomem *hpios_map_io(struct pci_dev *pci_dev, int idx, + unsigned int length); + +#endif diff --git a/sound/pci/asihpi/hpipcida.h b/sound/pci/asihpi/hpipcida.h new file mode 100644 index 000000000000..bb30868ce1a3 --- /dev/null +++ b/sound/pci/asihpi/hpipcida.h @@ -0,0 +1,37 @@ +/****************************************************************************** + + AudioScience HPI driver + Copyright (C) 1997-2010 AudioScience Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation; + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Array initializer for PCI card IDs + +(C) Copyright AudioScience Inc. 1998-2003 +*******************************************************************************/ + +/*NOTE: when adding new lines to this header file + they MUST be grouped by HPI entry point. +*/ + +{ +HPI_PCI_VENDOR_ID_TI, HPI_PCI_DEV_ID_DSP6205, + HPI_PCI_VENDOR_ID_AUDIOSCIENCE, PCI_ANY_ID, 0, 0, + (kernel_ulong_t) HPI_6205} +, { +HPI_PCI_VENDOR_ID_TI, HPI_PCI_DEV_ID_PCI2040, + HPI_PCI_VENDOR_ID_AUDIOSCIENCE, PCI_ANY_ID, 0, 0, + (kernel_ulong_t) HPI_6000} +, { +0} -- cgit v1.2.3 From 20133d4cd329af7a02ee5af36bba1796d5ff7b1d Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 21 Apr 2010 11:04:07 -0400 Subject: ALSA: snd-meastro3: Document hardware volume control a bit While working on a fix for the volume being muted on the allegro in my Compaq EVO N600C after suspend, I've learned a few things about the hardware volume control worth documenting. The actual fix for the suspend / resume issue is in the next patch in this set. Signed-off-by: Hans de Goede Signed-off-by: Takashi Iwai --- sound/pci/maestro3.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c index b56e33676780..53d2a5d61baf 100644 --- a/sound/pci/maestro3.c +++ b/sound/pci/maestro3.c @@ -1598,6 +1598,10 @@ static void snd_m3_update_ptr(struct snd_m3 *chip, struct m3_dma *s) } } +/* The m3's hardware volume works by incrementing / decrementing 2 counters + (without wrap around) in response to volume button presses and then + generating an interrupt. The pair of counters is stored in bits 1-3 and 5-7 + of a byte wide register. The meaning of bits 0 and 4 is unknown. */ static void snd_m3_update_hw_volume(unsigned long private_data) { struct snd_m3 *chip = (struct snd_m3 *) private_data; @@ -1609,7 +1613,15 @@ static void snd_m3_update_hw_volume(unsigned long private_data) values. */ x = inb(chip->iobase + SHADOW_MIX_REG_VOICE) & 0xee; - /* Reset the volume control registers. */ + /* Reset the volume counters to 4. Tests on the allegro integrated + into a Compaq N600C laptop, have revealed that: + 1) Writing any value will result in the 2 counters being reset to + 4 so writing 0x88 is not strictly necessary + 2) Writing to any of the 4 involved registers will reset all 4 + of them (and reading them always returns the same value for all + of them) + It could be that a maestro deviates from this, so leave the code + as is. */ outb(0x88, chip->iobase + SHADOW_MIX_REG_VOICE); outb(0x88, chip->iobase + HW_VOL_COUNTER_VOICE); outb(0x88, chip->iobase + SHADOW_MIX_REG_MASTER); @@ -1629,7 +1641,9 @@ static void snd_m3_update_hw_volume(unsigned long private_data) val = chip->ac97->regs[AC97_MASTER_VOL]; switch (x) { case 0x88: - /* mute */ + /* The counters have not changed, yet we've received a HV + interrupt. According to tests run by various people this + happens when pressing the mute button. */ val ^= 0x8000; chip->ac97->regs[AC97_MASTER_VOL] = val; outw(val, chip->iobase + CODEC_DATA); @@ -1638,7 +1652,7 @@ static void snd_m3_update_hw_volume(unsigned long private_data) &chip->master_switch->id); break; case 0xaa: - /* volume up */ + /* counters increased by 1 -> volume up */ if ((val & 0x7f) > 0) val--; if ((val & 0x7f00) > 0) @@ -1650,7 +1664,7 @@ static void snd_m3_update_hw_volume(unsigned long private_data) &chip->master_volume->id); break; case 0x66: - /* volume down */ + /* counters decreased by 1 -> volume down */ if ((val & 0x7f) < 0x1f) val++; if ((val & 0x7f00) < 0x1f00) -- cgit v1.2.3 From eb581adf25fe9e42197e591926de85459e68b9fd Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 23 Apr 2010 05:26:42 -0400 Subject: ALSA: snd-maestro3: Make hardware volume buttons an input device (rev2) While working on the sound suspend / resume problems with my laptop I noticed that the hardware volume handling code in essence just detects key presses, and then does some hardcoded modification of the master volume based on which key is pressed. This made me think that clearly the right thing to do here is just report these keypresses to userspace and let userspace decide what to with them. This patch adds a Kconfig option which when enabled reports the volume buttons as keypresses using an input device. When enabled this option also gets rid of the ugly direct ac97 writes from the tasklet, the ac97lock and the need for using a tasklet in general. As an added bonus the keys now work identical to volume keys on a (usb) keyboard with multimedia keys, providing visual feedback of the volume level change, and a better range of the volume control (with a properly configured desktop environment). Signed-off-by: Hans de Goede Signed-off-by: Takashi Iwai --- sound/pci/Kconfig | 10 +++++ sound/pci/maestro3.c | 116 ++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 124 insertions(+), 2 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index 517ae65ffc8e..ebcf065ad2c2 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -667,6 +667,16 @@ config SND_MAESTRO3 To compile this driver as a module, choose M here: the module will be called snd-maestro3. +config SND_MAESTRO3_INPUT + bool "Enable input device for maestro3 volume buttons" + depends on SND_MAESTRO3 + depends on INPUT=y || INPUT=SND_MAESTRO3 + help + If you say Y here, you will get an input device which reports + keypresses for the volume buttons connected to the maestro3 chip. + If you say N the buttons will directly control the master volume. + It is recommended to say Y. + config SND_MIXART tristate "Digigram miXart" select SND_HWDEP diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c index 53d2a5d61baf..217a4dcb259e 100644 --- a/sound/pci/maestro3.c +++ b/sound/pci/maestro3.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -844,11 +845,17 @@ struct snd_m3 { struct m3_dma *substreams; spinlock_t reg_lock; - spinlock_t ac97_lock; +#ifdef CONFIG_SND_MAESTRO3_INPUT + struct input_dev *input_dev; + char phys[64]; /* physical device path */ +#else + spinlock_t ac97_lock; struct snd_kcontrol *master_switch; struct snd_kcontrol *master_volume; struct tasklet_struct hwvol_tq; +#endif + unsigned int in_suspend; #ifdef CONFIG_PM @@ -1606,7 +1613,9 @@ static void snd_m3_update_hw_volume(unsigned long private_data) { struct snd_m3 *chip = (struct snd_m3 *) private_data; int x, val; +#ifndef CONFIG_SND_MAESTRO3_INPUT unsigned long flags; +#endif /* Figure out which volume control button was pushed, based on differences from the default register @@ -1632,6 +1641,7 @@ static void snd_m3_update_hw_volume(unsigned long private_data) if (chip->in_suspend) return; +#ifndef CONFIG_SND_MAESTRO3_INPUT if (!chip->master_switch || !chip->master_volume) return; @@ -1677,6 +1687,35 @@ static void snd_m3_update_hw_volume(unsigned long private_data) break; } spin_unlock_irqrestore(&chip->ac97_lock, flags); +#else + if (!chip->input_dev) + return; + + val = 0; + switch (x) { + case 0x88: + /* The counters have not changed, yet we've received a HV + interrupt. According to tests run by various people this + happens when pressing the mute button. */ + val = KEY_MUTE; + break; + case 0xaa: + /* counters increased by 1 -> volume up */ + val = KEY_VOLUMEUP; + break; + case 0x66: + /* counters decreased by 1 -> volume down */ + val = KEY_VOLUMEDOWN; + break; + } + + if (val) { + input_report_key(chip->input_dev, val, 1); + input_sync(chip->input_dev); + input_report_key(chip->input_dev, val, 0); + input_sync(chip->input_dev); + } +#endif } static irqreturn_t snd_m3_interrupt(int irq, void *dev_id) @@ -1691,7 +1730,11 @@ static irqreturn_t snd_m3_interrupt(int irq, void *dev_id) return IRQ_NONE; if (status & HV_INT_PENDING) +#ifdef CONFIG_SND_MAESTRO3_INPUT + snd_m3_update_hw_volume((unsigned long)chip); +#else tasklet_schedule(&chip->hwvol_tq); +#endif /* * ack an assp int if its running @@ -1957,18 +2000,24 @@ static unsigned short snd_m3_ac97_read(struct snd_ac97 *ac97, unsigned short reg) { struct snd_m3 *chip = ac97->private_data; +#ifndef CONFIG_SND_MAESTRO3_INPUT unsigned long flags; +#endif unsigned short data = 0xffff; if (snd_m3_ac97_wait(chip)) goto fail; +#ifndef CONFIG_SND_MAESTRO3_INPUT spin_lock_irqsave(&chip->ac97_lock, flags); +#endif snd_m3_outb(chip, 0x80 | (reg & 0x7f), CODEC_COMMAND); if (snd_m3_ac97_wait(chip)) goto fail_unlock; data = snd_m3_inw(chip, CODEC_DATA); fail_unlock: +#ifndef CONFIG_SND_MAESTRO3_INPUT spin_unlock_irqrestore(&chip->ac97_lock, flags); +#endif fail: return data; } @@ -1977,14 +2026,20 @@ static void snd_m3_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val) { struct snd_m3 *chip = ac97->private_data; +#ifndef CONFIG_SND_MAESTRO3_INPUT unsigned long flags; +#endif if (snd_m3_ac97_wait(chip)) return; +#ifndef CONFIG_SND_MAESTRO3_INPUT spin_lock_irqsave(&chip->ac97_lock, flags); +#endif snd_m3_outw(chip, val, CODEC_DATA); snd_m3_outb(chip, reg & 0x7f, CODEC_COMMAND); +#ifndef CONFIG_SND_MAESTRO3_INPUT spin_unlock_irqrestore(&chip->ac97_lock, flags); +#endif } @@ -2091,7 +2146,9 @@ static int __devinit snd_m3_mixer(struct snd_m3 *chip) { struct snd_ac97_bus *pbus; struct snd_ac97_template ac97; +#ifndef CONFIG_SND_MAESTRO3_INPUT struct snd_ctl_elem_id elem_id; +#endif int err; static struct snd_ac97_bus_ops ops = { .write = snd_m3_ac97_write, @@ -2111,6 +2168,7 @@ static int __devinit snd_m3_mixer(struct snd_m3 *chip) schedule_timeout_uninterruptible(msecs_to_jiffies(100)); snd_ac97_write(chip->ac97, AC97_PCM, 0); +#ifndef CONFIG_SND_MAESTRO3_INPUT memset(&elem_id, 0, sizeof(elem_id)); elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; strcpy(elem_id.name, "Master Playback Switch"); @@ -2119,6 +2177,7 @@ static int __devinit snd_m3_mixer(struct snd_m3 *chip) elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; strcpy(elem_id.name, "Master Playback Volume"); chip->master_volume = snd_ctl_find_id(chip->card, &elem_id); +#endif return 0; } @@ -2398,6 +2457,11 @@ static int snd_m3_free(struct snd_m3 *chip) struct m3_dma *s; int i; +#ifdef CONFIG_SND_MAESTRO3_INPUT + if (chip->input_dev) + input_unregister_device(chip->input_dev); +#endif + if (chip->substreams) { spin_lock_irq(&chip->reg_lock); for (i = 0; i < chip->num_substreams; i++) { @@ -2524,6 +2588,41 @@ static int m3_resume(struct pci_dev *pci) } #endif /* CONFIG_PM */ +#ifdef CONFIG_SND_MAESTRO3_INPUT +static int __devinit snd_m3_input_register(struct snd_m3 *chip) +{ + struct input_dev *input_dev; + int err; + + input_dev = input_allocate_device(); + if (!input_dev) + return -ENOMEM; + + snprintf(chip->phys, sizeof(chip->phys), "pci-%s/input0", + pci_name(chip->pci)); + + input_dev->name = chip->card->driver; + input_dev->phys = chip->phys; + input_dev->id.bustype = BUS_PCI; + input_dev->id.vendor = chip->pci->vendor; + input_dev->id.product = chip->pci->device; + input_dev->dev.parent = &chip->pci->dev; + + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(KEY_MUTE, input_dev->keybit); + __set_bit(KEY_VOLUMEDOWN, input_dev->keybit); + __set_bit(KEY_VOLUMEUP, input_dev->keybit); + + err = input_register_device(input_dev); + if (err) { + input_free_device(input_dev); + return err; + } + + chip->input_dev = input_dev; + return 0; +} +#endif /* CONFIG_INPUT */ /* */ @@ -2567,7 +2666,9 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci, } spin_lock_init(&chip->reg_lock); +#ifndef CONFIG_SND_MAESTRO3_INPUT spin_lock_init(&chip->ac97_lock); +#endif switch (pci->device) { case PCI_DEVICE_ID_ESS_ALLEGRO: @@ -2650,7 +2751,9 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci, snd_m3_hv_init(chip); +#ifndef CONFIG_SND_MAESTRO3_INPUT tasklet_init(&chip->hwvol_tq, snd_m3_update_hw_volume, (unsigned long)chip); +#endif if (request_irq(pci->irq, snd_m3_interrupt, IRQF_SHARED, card->driver, chip)) { @@ -2682,7 +2785,16 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci, if ((err = snd_m3_pcm(chip, 0)) < 0) return err; - + +#ifdef CONFIG_SND_MAESTRO3_INPUT + if (chip->hv_config & HV_CTRL_ENABLE) { + err = snd_m3_input_register(chip); + if (err) + snd_printk(KERN_WARNING "Input device registration " + "failed with error %i", err); + } +#endif + snd_m3_enable_ints(chip); snd_m3_assp_continue(chip); -- cgit v1.2.3 From 5a5e02e5095ed89a0a1f4031e7440078c209442b Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 23 Apr 2010 05:26:43 -0400 Subject: ALSA: snd-es1968: Make hardware volume buttons an input device (rev2) The hardware volume handling code in essence just detects key presses, and then does some hardcoded modification of the master volume based on which key is pressed. Clearly the right thing to do here is just report these keypresses to userspace and let userspace decide what to with them. This patch adds a Kconfig option which when enabled reports the volume buttons as keypresses using an input device. When enabled this option also gets rid of the ugly direct ac97 writes from the tasklet, the ac97lock and the need for using a tasklet in general. Signed-off-by: Hans de Goede Signed-off-by: Takashi Iwai --- sound/pci/Kconfig | 10 +++++ sound/pci/es1968.c | 128 ++++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 128 insertions(+), 10 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index ebcf065ad2c2..e7a8cd058efb 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -513,6 +513,16 @@ config SND_ES1968 To compile this driver as a module, choose M here: the module will be called snd-es1968. +config SND_ES1968_INPUT + bool "Enable input device for es1968 volume buttons" + depends on SND_ES1968 + depends on INPUT=y || INPUT=SND_ES1968 + help + If you say Y here, you will get an input device which reports + keypresses for the volume buttons connected to the es1968 chip. + If you say N the buttons will directly control the master volume. + It is recommended to say Y. + config SND_FM801 tristate "ForteMedia FM801" select SND_OPL3_LIB diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c index ecaea9fb48ec..aa973cee8155 100644 --- a/sound/pci/es1968.c +++ b/sound/pci/es1968.c @@ -104,6 +104,7 @@ #include #include #include +#include #include #include @@ -517,14 +518,9 @@ struct es1968 { /* ALSA Stuff */ struct snd_ac97 *ac97; - struct snd_kcontrol *master_switch; /* for h/w volume control */ - struct snd_kcontrol *master_volume; - struct snd_rawmidi *rmidi; spinlock_t reg_lock; - spinlock_t ac97_lock; - struct tasklet_struct hwvol_tq; unsigned int in_suspend; /* Maestro Stuff */ @@ -547,6 +543,16 @@ struct es1968 { #ifdef SUPPORT_JOYSTICK struct gameport *gameport; #endif + +#ifdef CONFIG_SND_ES1968_INPUT + struct input_dev *input_dev; + char phys[64]; /* physical device path */ +#else + struct snd_kcontrol *master_switch; /* for h/w volume control */ + struct snd_kcontrol *master_volume; + spinlock_t ac97_lock; + struct tasklet_struct hwvol_tq; +#endif }; static irqreturn_t snd_es1968_interrupt(int irq, void *dev_id); @@ -632,28 +638,38 @@ static int snd_es1968_ac97_wait_poll(struct es1968 *chip) static void snd_es1968_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val) { struct es1968 *chip = ac97->private_data; +#ifndef CONFIG_SND_ES1968_INPUT unsigned long flags; +#endif snd_es1968_ac97_wait(chip); /* Write the bus */ +#ifndef CONFIG_SND_ES1968_INPUT spin_lock_irqsave(&chip->ac97_lock, flags); +#endif outw(val, chip->io_port + ESM_AC97_DATA); /*msleep(1);*/ outb(reg, chip->io_port + ESM_AC97_INDEX); /*msleep(1);*/ +#ifndef CONFIG_SND_ES1968_INPUT spin_unlock_irqrestore(&chip->ac97_lock, flags); +#endif } static unsigned short snd_es1968_ac97_read(struct snd_ac97 *ac97, unsigned short reg) { u16 data = 0; struct es1968 *chip = ac97->private_data; +#ifndef CONFIG_SND_ES1968_INPUT unsigned long flags; +#endif snd_es1968_ac97_wait(chip); +#ifndef CONFIG_SND_ES1968_INPUT spin_lock_irqsave(&chip->ac97_lock, flags); +#endif outb(reg | 0x80, chip->io_port + ESM_AC97_INDEX); /*msleep(1);*/ @@ -661,7 +677,9 @@ static unsigned short snd_es1968_ac97_read(struct snd_ac97 *ac97, unsigned short data = inw(chip->io_port + ESM_AC97_DATA); /*msleep(1);*/ } +#ifndef CONFIG_SND_ES1968_INPUT spin_unlock_irqrestore(&chip->ac97_lock, flags); +#endif return data; } @@ -1874,13 +1892,17 @@ static void snd_es1968_update_pcm(struct es1968 *chip, struct esschan *es) } } -/* - */ +/* The hardware volume works by incrementing / decrementing 2 counters + (without wrap around) in response to volume button presses and then + generating an interrupt. The pair of counters is stored in bits 1-3 and 5-7 + of a byte wide register. The meaning of bits 0 and 4 is unknown. */ static void es1968_update_hw_volume(unsigned long private_data) { struct es1968 *chip = (struct es1968 *) private_data; int x, val; +#ifndef CONFIG_SND_ES1968_INPUT unsigned long flags; +#endif /* Figure out which volume control button was pushed, based on differences from the default register @@ -1895,6 +1917,7 @@ static void es1968_update_hw_volume(unsigned long private_data) if (chip->in_suspend) return; +#ifndef CONFIG_SND_ES1968_INPUT if (! chip->master_switch || ! chip->master_volume) return; @@ -1937,6 +1960,35 @@ static void es1968_update_hw_volume(unsigned long private_data) break; } spin_unlock_irqrestore(&chip->ac97_lock, flags); +#else + if (!chip->input_dev) + return; + + val = 0; + switch (x) { + case 0x88: + /* The counters have not changed, yet we've received a HV + interrupt. According to tests run by various people this + happens when pressing the mute button. */ + val = KEY_MUTE; + break; + case 0xaa: + /* counters increased by 1 -> volume up */ + val = KEY_VOLUMEUP; + break; + case 0x66: + /* counters decreased by 1 -> volume down */ + val = KEY_VOLUMEDOWN; + break; + } + + if (val) { + input_report_key(chip->input_dev, val, 1); + input_sync(chip->input_dev); + input_report_key(chip->input_dev, val, 0); + input_sync(chip->input_dev); + } +#endif } /* @@ -1953,7 +2005,11 @@ static irqreturn_t snd_es1968_interrupt(int irq, void *dev_id) outw(inw(chip->io_port + 4) & 1, chip->io_port + 4); if (event & ESM_HWVOL_IRQ) +#ifdef CONFIG_SND_ES1968_INPUT + es1968_update_hw_volume((unsigned long)chip); +#else tasklet_schedule(&chip->hwvol_tq); /* we'll do this later */ +#endif /* else ack 'em all, i imagine */ outb(0xFF, chip->io_port + 0x1A); @@ -1993,7 +2049,9 @@ snd_es1968_mixer(struct es1968 *chip) { struct snd_ac97_bus *pbus; struct snd_ac97_template ac97; +#ifndef CONFIG_SND_ES1968_INPUT struct snd_ctl_elem_id elem_id; +#endif int err; static struct snd_ac97_bus_ops ops = { .write = snd_es1968_ac97_write, @@ -2009,6 +2067,7 @@ snd_es1968_mixer(struct es1968 *chip) if ((err = snd_ac97_mixer(pbus, &ac97, &chip->ac97)) < 0) return err; +#ifndef CONFIG_SND_ES1968_INPUT /* attach master switch / volumes for h/w volume control */ memset(&elem_id, 0, sizeof(elem_id)); elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; @@ -2018,6 +2077,7 @@ snd_es1968_mixer(struct es1968 *chip) elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; strcpy(elem_id.name, "Master Playback Volume"); chip->master_volume = snd_ctl_find_id(chip->card, &elem_id); +#endif return 0; } @@ -2474,8 +2534,49 @@ static inline int snd_es1968_create_gameport(struct es1968 *chip, int dev) { ret static inline void snd_es1968_free_gameport(struct es1968 *chip) { } #endif +#ifdef CONFIG_SND_ES1968_INPUT +static int __devinit snd_es1968_input_register(struct es1968 *chip) +{ + struct input_dev *input_dev; + int err; + + input_dev = input_allocate_device(); + if (!input_dev) + return -ENOMEM; + + snprintf(chip->phys, sizeof(chip->phys), "pci-%s/input0", + pci_name(chip->pci)); + + input_dev->name = chip->card->driver; + input_dev->phys = chip->phys; + input_dev->id.bustype = BUS_PCI; + input_dev->id.vendor = chip->pci->vendor; + input_dev->id.product = chip->pci->device; + input_dev->dev.parent = &chip->pci->dev; + + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(KEY_MUTE, input_dev->keybit); + __set_bit(KEY_VOLUMEDOWN, input_dev->keybit); + __set_bit(KEY_VOLUMEUP, input_dev->keybit); + + err = input_register_device(input_dev); + if (err) { + input_free_device(input_dev); + return err; + } + + chip->input_dev = input_dev; + return 0; +} +#endif /* CONFIG_SND_ES1968_INPUT */ + static int snd_es1968_free(struct es1968 *chip) { +#ifdef CONFIG_SND_ES1968_INPUT + if (chip->input_dev) + input_unregister_device(chip->input_dev); +#endif + if (chip->io_port) { if (chip->irq >= 0) synchronize_irq(chip->irq); @@ -2486,8 +2587,6 @@ static int snd_es1968_free(struct es1968 *chip) if (chip->irq >= 0) free_irq(chip->irq, chip); snd_es1968_free_gameport(chip); - chip->master_switch = NULL; - chip->master_volume = NULL; pci_release_regions(chip->pci); pci_disable_device(chip->pci); kfree(chip); @@ -2558,9 +2657,11 @@ static int __devinit snd_es1968_create(struct snd_card *card, spin_lock_init(&chip->substream_lock); INIT_LIST_HEAD(&chip->buf_list); INIT_LIST_HEAD(&chip->substream_list); - spin_lock_init(&chip->ac97_lock); mutex_init(&chip->memory_mutex); +#ifndef CONFIG_SND_ES1968_INPUT + spin_lock_init(&chip->ac97_lock); tasklet_init(&chip->hwvol_tq, es1968_update_hw_volume, (unsigned long)chip); +#endif chip->card = card; chip->pci = pci; chip->irq = -1; @@ -2713,6 +2814,13 @@ static int __devinit snd_es1968_probe(struct pci_dev *pci, snd_es1968_create_gameport(chip, dev); +#ifdef CONFIG_SND_ES1968_INPUT + err = snd_es1968_input_register(chip); + if (err) + snd_printk(KERN_WARNING "Input device registration " + "failed with error %i", err); +#endif + snd_es1968_start_irq(chip); chip->clock = clock[dev]; -- cgit v1.2.3 From ef5dbbccbbfa7d2211fa8efcc095a9f4a7912dda Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 May 2010 08:40:25 +0200 Subject: ALSA: hda - Remove superfluous external amp setup for ALC888 We had a fixed external amp setup enabled for ALC888, but this seems unnecessary. The amps are controlled rather by GPIOs. Let's remove it now. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 2078a1ac0f10..2b0131fb026d 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -10580,9 +10580,6 @@ static int patch_alc882(struct hda_codec *codec) spec->stream_digital_playback = &alc882_pcm_digital_playback; spec->stream_digital_capture = &alc882_pcm_digital_capture; - if (codec->vendor_id == 0x10ec0888) - spec->init_amp = ALC_INIT_DEFAULT; /* always initialize */ - if (!spec->adc_nids && spec->input_mux) { int i, j; spec->num_adc_nids = 0; -- cgit v1.2.3 From 6895b5262ea80c99e526d40d39e0ca48014bdb95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Fri, 7 May 2010 00:12:12 +0300 Subject: ALSA: es1968: Clear interrupts before enabling them MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid spurious interrupts when initializing the device. Signed-off-by: Ville Syrjälä Signed-off-by: Takashi Iwai --- sound/pci/es1968.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci') diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c index aa973cee8155..23a58f0d6cb9 100644 --- a/sound/pci/es1968.c +++ b/sound/pci/es1968.c @@ -2401,6 +2401,7 @@ static void snd_es1968_start_irq(struct es1968 *chip) w = ESM_HIRQ_DSIE | ESM_HIRQ_HW_VOLUME; if (chip->rmidi) w |= ESM_HIRQ_MPU401; + outb(w, chip->io_port + 0x1A); outw(w, chip->io_port + ESM_PORT_HOST_IRQ); } -- cgit v1.2.3 From 1bde78bc25a8e244006f63adb851bfb34407d48c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Fri, 7 May 2010 00:12:13 +0300 Subject: ALSA: maestro3: Clear interrupts before enabling them MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid spurious interrupts when initializing the device. Signed-off-by: Ville Syrjälä Signed-off-by: Takashi Iwai --- sound/pci/maestro3.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci') diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c index 217a4dcb259e..3c40d726b46e 100644 --- a/sound/pci/maestro3.c +++ b/sound/pci/maestro3.c @@ -2443,6 +2443,7 @@ snd_m3_enable_ints(struct snd_m3 *chip) val = ASSP_INT_ENABLE /*| MPU401_INT_ENABLE*/; if (chip->hv_config & HV_CTRL_ENABLE) val |= HV_INT_ENABLE; + outb(val, chip->iobase + HOST_INT_STATUS); outw(val, io + HOST_INT_CTRL); outb(inb(io + ASSP_CONTROL_C) | ASSP_HOST_INT_ENABLE, io + ASSP_CONTROL_C); -- cgit v1.2.3 From 1965c441ecf1f9cd723dd590cbb6c191cfc13fb2 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 6 May 2010 16:37:03 -0500 Subject: ALSA: hda: enable SPDIF output for Conexant 5051/Lenovo docking stations This patch enables the SPDIF output pin by default. It also enables it for quirks related to Levono docking stations (x200 and 25041, identified with the same 17aa:20f2 ID). Even though not all Lenovo docking stations have SPDIF connectors, enabling the pin by default shouldn't be a problem for anyone. Other quirks remain unmodified. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_conexant.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 114af8e19d80..0aa122e26dc8 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -1784,6 +1784,7 @@ static struct hda_verb cxt5051_init_verbs[] = { {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44}, {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44}, /* SPDIF route: PCM */ + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, {0x1c, AC_VERB_SET_CONNECT_SEL, 0x0}, /* EAPD */ {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ @@ -1840,6 +1841,7 @@ static struct hda_verb cxt5051_lenovo_x200_init_verbs[] = { {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44}, {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44}, /* SPDIF route: PCM */ + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* needed for W500 Advanced Mini Dock 250410 */ {0x1c, AC_VERB_SET_CONNECT_SEL, 0x0}, /* EAPD */ {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ @@ -1911,7 +1913,7 @@ enum { CXT5051_LAPTOP, /* Laptops w/ EAPD support */ CXT5051_HP, /* no docking */ CXT5051_HP_DV6736, /* HP without mic switch */ - CXT5051_LENOVO_X200, /* Lenovo X200 laptop */ + CXT5051_LENOVO_X200, /* Lenovo X200 laptop, also used for Advanced Mini Dock 250410 */ CXT5051_F700, /* HP Compaq Presario F700 */ CXT5051_TOSHIBA, /* Toshiba M300 & co */ CXT5051_MODELS -- cgit v1.2.3 From beaffc399397eb9427225f37c1e56c099571b2df Mon Sep 17 00:00:00 2001 From: Shahin Ghazinouri Date: Tue, 11 May 2010 08:19:55 +0200 Subject: ALSA: hda - Fixes distorted recording on US15W chipset The HDA controller in US15W (Poulsbo) reports inaccurate position values for capture streams when using the LPIB read method, resulting in distorted recordings. However, using the position buffer is broken for playback streams, resulting in a fallback to the LPIB method with the current driver. This patch works around the issue by independently detecting the read position method for capture and playback streams. The patch will not have any effect if the position fix method is explicitly set. [Code simplified by tiwai] Signed-off-by: Shahin Ghazinouri Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 236b4caa142f..cad9b70c27a0 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -425,7 +425,7 @@ struct azx { struct snd_dma_buffer posbuf; /* flags */ - int position_fix; + int position_fix[2]; /* for both playback/capture streams */ int poll_count; unsigned int running :1; unsigned int initialized :1; @@ -1306,8 +1306,10 @@ static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev) azx_sd_writel(azx_dev, SD_BDLPU, upper_32_bits(azx_dev->bdl.addr)); /* enable the position buffer */ - if (chip->position_fix == POS_FIX_POSBUF || - chip->position_fix == POS_FIX_AUTO || + if (chip->position_fix[0] == POS_FIX_POSBUF || + chip->position_fix[0] == POS_FIX_AUTO || + chip->position_fix[1] == POS_FIX_POSBUF || + chip->position_fix[1] == POS_FIX_AUTO || chip->via_dmapos_patch) { if (!(azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE)) azx_writel(chip, DPLBASE, @@ -1847,13 +1849,16 @@ static unsigned int azx_get_position(struct azx *chip, if (chip->via_dmapos_patch) pos = azx_via_get_position(chip, azx_dev); - else if (chip->position_fix == POS_FIX_POSBUF || - chip->position_fix == POS_FIX_AUTO) { - /* use the position buffer */ - pos = le32_to_cpu(*azx_dev->posbuf); - } else { - /* read LPIB */ - pos = azx_sd_readl(azx_dev, SD_LPIB); + else { + int stream = azx_dev->substream->stream; + if (chip->position_fix[stream] == POS_FIX_POSBUF || + chip->position_fix[stream] == POS_FIX_AUTO) { + /* use the position buffer */ + pos = le32_to_cpu(*azx_dev->posbuf); + } else { + /* read LPIB */ + pos = azx_sd_readl(azx_dev, SD_LPIB); + } } if (pos >= azx_dev->bufsize) pos = 0; @@ -1881,22 +1886,24 @@ static snd_pcm_uframes_t azx_pcm_pointer(struct snd_pcm_substream *substream) static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev) { unsigned int pos; + int stream; if (azx_dev->start_flag && time_before_eq(jiffies, azx_dev->start_jiffies)) return -1; /* bogus (too early) interrupt */ azx_dev->start_flag = 0; + stream = azx_dev->substream->stream; pos = azx_get_position(chip, azx_dev); - if (chip->position_fix == POS_FIX_AUTO) { + if (chip->position_fix[stream] == POS_FIX_AUTO) { if (!pos) { printk(KERN_WARNING "hda-intel: Invalid position buffer, " "using LPIB read method instead.\n"); - chip->position_fix = POS_FIX_LPIB; + chip->position_fix[stream] = POS_FIX_LPIB; pos = azx_get_position(chip, azx_dev); } else - chip->position_fix = POS_FIX_POSBUF; + chip->position_fix[stream] = POS_FIX_POSBUF; } if (!bdl_pos_adj[chip->dev_index]) @@ -2435,7 +2442,8 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, chip->dev_index = dev; INIT_WORK(&chip->irq_pending_work, azx_irq_pending_work); - chip->position_fix = check_position_fix(chip, position_fix[dev]); + chip->position_fix[0] = chip->position_fix[1] = + check_position_fix(chip, position_fix[dev]); check_probe_mask(chip, dev); chip->single_cmd = single_cmd; -- cgit v1.2.3 From e54637205b00837bf00de916b0ae361c6aa0b139 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 11 May 2010 10:21:46 +0200 Subject: [ALSA] snd-hda-intel: use WALLCLK register to check for early irqs Use 24Mhz WALLCLK register to ignore too early interrupts and wrong interrupt status. The bad timing confuses the higher ALSA layer and causes audio skipping. More information about behaviour and debugging can be found in kernel bz#15912. https://bugzilla.kernel.org/show_bug.cgi?id=15912 Signed-off-by: Jaroslav Kysela --- sound/pci/hda/hda_intel.c | 42 +++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 19 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index cad9b70c27a0..0a6c55bb7d6b 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -174,7 +174,7 @@ MODULE_DESCRIPTION("Intel HDA driver"); #define ICH6_GSTS_FSTS (1 << 1) /* flush status */ #define ICH6_REG_INTCTL 0x20 #define ICH6_REG_INTSTS 0x24 -#define ICH6_REG_WALCLK 0x30 +#define ICH6_REG_WALLCLK 0x30 /* 24Mhz source */ #define ICH6_REG_SYNC 0x34 #define ICH6_REG_CORBLBASE 0x40 #define ICH6_REG_CORBUBASE 0x44 @@ -340,8 +340,8 @@ struct azx_dev { unsigned int period_bytes; /* size of the period in bytes */ unsigned int frags; /* number for period in the play buffer */ unsigned int fifo_size; /* FIFO size */ - unsigned long start_jiffies; /* start + minimum jiffies */ - unsigned long min_jiffies; /* minimum jiffies before position is valid */ + unsigned long start_wallclk; /* start + minimum wallclk */ + unsigned long period_wallclk; /* wallclk for period */ void __iomem *sd_addr; /* stream descriptor pointer */ @@ -361,7 +361,6 @@ struct azx_dev { unsigned int opened :1; unsigned int running :1; unsigned int irq_pending :1; - unsigned int start_flag: 1; /* stream full start flag */ /* * For VIA: * A flag to ensure DMA position is 0 @@ -1676,8 +1675,9 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream) return err; } - azx_dev->min_jiffies = (runtime->period_size * HZ) / - (runtime->rate * 2); + /* wallclk has 24Mhz clock source */ + azx_dev->period_wallclk = (((runtime->period_size * 24000) / + runtime->rate) * 1000); azx_setup_controller(chip, azx_dev); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) azx_dev->fifo_size = azx_sd_readw(azx_dev, SD_FIFOSIZE) + 1; @@ -1731,14 +1731,15 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) if (s->pcm->card != substream->pcm->card) continue; azx_dev = get_azx_dev(s); - if (rstart) { - azx_dev->start_flag = 1; - azx_dev->start_jiffies = jiffies + azx_dev->min_jiffies; - } - if (start) + if (start) { + azx_dev->start_wallclk = azx_readl(chip, WALLCLK); + if (!rstart) + azx_dev->start_wallclk -= + azx_dev->period_wallclk; azx_stream_start(chip, azx_dev); - else + } else { azx_stream_stop(chip, azx_dev); + } azx_dev->running = start; } spin_unlock(&chip->reg_lock); @@ -1885,13 +1886,14 @@ static snd_pcm_uframes_t azx_pcm_pointer(struct snd_pcm_substream *substream) */ static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev) { + u32 wallclk; unsigned int pos; int stream; - if (azx_dev->start_flag && - time_before_eq(jiffies, azx_dev->start_jiffies)) + wallclk = azx_readl(chip, WALLCLK); + if ((wallclk - azx_dev->start_wallclk) < + (azx_dev->period_wallclk * 2) / 3) return -1; /* bogus (too early) interrupt */ - azx_dev->start_flag = 0; stream = azx_dev->substream->stream; pos = azx_get_position(chip, azx_dev); @@ -1906,13 +1908,12 @@ static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev) chip->position_fix[stream] = POS_FIX_POSBUF; } - if (!bdl_pos_adj[chip->dev_index]) - return 1; /* no delayed ack */ if (WARN_ONCE(!azx_dev->period_bytes, "hda-intel: zero azx_dev->period_bytes")) return 0; /* this shouldn't happen! */ if (pos % azx_dev->period_bytes > azx_dev->period_bytes / 2) return 0; /* NG - it's below the period boundary */ + azx_dev->start_wallclk = wallclk; return 1; /* OK, it's fine */ } @@ -1922,7 +1923,7 @@ static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev) static void azx_irq_pending_work(struct work_struct *work) { struct azx *chip = container_of(work, struct azx, irq_pending_work); - int i, pending; + int i, pending, ok; if (!chip->irq_pending_warned) { printk(KERN_WARNING @@ -1941,11 +1942,14 @@ static void azx_irq_pending_work(struct work_struct *work) !azx_dev->substream || !azx_dev->running) continue; - if (azx_position_ok(chip, azx_dev)) { + ok = azx_position_ok(chip, azx_dev); + if (ok > 0) { azx_dev->irq_pending = 0; spin_unlock(&chip->reg_lock); snd_pcm_period_elapsed(azx_dev->substream); spin_lock(&chip->reg_lock); + } else if (ok < 0) { + pending = 0; /* too early */ } else pending++; } -- cgit v1.2.3 From f48f606d9f85a8b25c716082c73c36e9ff6f0cde Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 11 May 2010 12:10:47 +0200 Subject: [ALSA] snd-hda-intel: Improve azx_position_ok() Add back the zero return value (activate workqueue) when bdl_pos_adj is nonzero for position check. Do the position related check only for first next period using wallclk counter. Return -1 value (ignore interrupt) when period_bytes variable is zero. Signed-off-by: Jaroslav Kysela --- sound/pci/hda/hda_intel.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 0a6c55bb7d6b..170610e1d7da 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -1890,9 +1890,8 @@ static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev) unsigned int pos; int stream; - wallclk = azx_readl(chip, WALLCLK); - if ((wallclk - azx_dev->start_wallclk) < - (azx_dev->period_wallclk * 2) / 3) + wallclk = azx_readl(chip, WALLCLK) - azx_dev->start_wallclk; + if (wallclk < (azx_dev->period_wallclk * 2) / 3) return -1; /* bogus (too early) interrupt */ stream = azx_dev->substream->stream; @@ -1910,9 +1909,11 @@ static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev) if (WARN_ONCE(!azx_dev->period_bytes, "hda-intel: zero azx_dev->period_bytes")) - return 0; /* this shouldn't happen! */ - if (pos % azx_dev->period_bytes > azx_dev->period_bytes / 2) - return 0; /* NG - it's below the period boundary */ + return -1; /* this shouldn't happen! */ + if (wallclk <= azx_dev->period_wallclk && + pos % azx_dev->period_bytes > azx_dev->period_bytes / 2) + /* NG - it's below the first next period boundary */ + return bdl_pos_adj[chip->dev_index] ? 0 : -1; azx_dev->start_wallclk = wallclk; return 1; /* OK, it's fine */ } -- cgit v1.2.3 From 2448b14715bc83d75ed779452fe89285df4d772a Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 14 May 2010 16:49:20 +0200 Subject: ALSA: asihpi: testing the wrong variable There is a typo here. We want to test "*dst" not "dst". Signed-off-by: Dan Carpenter Signed-off-by: Takashi Iwai --- sound/pci/asihpi/hpifunc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/asihpi/hpifunc.c b/sound/pci/asihpi/hpifunc.c index 254c580db639..15f0c7ea0e52 100644 --- a/sound/pci/asihpi/hpifunc.c +++ b/sound/pci/asihpi/hpifunc.c @@ -3279,7 +3279,7 @@ static u16 hpi_entity_alloc_and_copy(struct hpi_entity *src, buf_size = hpi_entity_size(src); *dst = kmalloc(buf_size, GFP_KERNEL); - if (dst == NULL) + if (*dst == NULL) return HPI_ERROR_MEMORY_ALLOC; memcpy(*dst, src, buf_size); return 0; -- cgit v1.2.3 From 1be1d76b8a1f622fd3a64778dfe117053f2af1f2 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 14 May 2010 16:50:05 +0200 Subject: ALSA: asihpi: incorrect range check The entity_type_to_size[] array has LAST_ENTITY_TYPE (11) number of elements, not LAST_ENTITY_ROLE (17). This only affects the debug output. Signed-off-by: Dan Carpenter Signed-off-by: Takashi Iwai --- sound/pci/asihpi/hpifunc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/asihpi/hpifunc.c b/sound/pci/asihpi/hpifunc.c index 15f0c7ea0e52..eda26b312324 100644 --- a/sound/pci/asihpi/hpifunc.c +++ b/sound/pci/asihpi/hpifunc.c @@ -3407,7 +3407,7 @@ u16 hpi_entity_alloc_and_pack(const enum e_entity_type type, if (hE) return hE; - HPI_DEBUG_ASSERT(role > entity_role_null && type < LAST_ENTITY_ROLE); + HPI_DEBUG_ASSERT(role > entity_role_null && type < LAST_ENTITY_TYPE); bytes_to_copy = entity_type_to_size[type] * item_count; total_size = hpi_entity_header_size(*entity) + bytes_to_copy; -- cgit v1.2.3 From e9abf85fe1fb0cd1da19ec231f2d6873e4af9fbd Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Fri, 14 May 2010 16:36:12 +0800 Subject: ALSA: intelhdmi - add dependency on SND_DYNAMIC_MINORS This is necessary to support >=3 HDMI playback devices starting from the CougarPoint codec. Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai --- sound/pci/hda/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/pci') diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 567348b05b5a..9194c3c1d04a 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -145,6 +145,7 @@ config SND_HDA_CODEC_NVHDMI config SND_HDA_CODEC_INTELHDMI bool "Build INTEL HDMI HD-audio codec support" + select SND_DYNAMIC_MINORS default y help Say Y here to include INTEL HDMI HD-audio codec support in -- cgit v1.2.3 From 41da2e0a01bf13986a561d8a16929d009eb9099c Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Fri, 14 May 2010 16:36:13 +0800 Subject: ALSA: intelhdmi - user friendly codec name Use the full chipset codename as codec name. They are more user friendly than the spec abbrs. Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_intelhdmi.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c index 88d035104cc5..433224d28b99 100644 --- a/sound/pci/hda/patch_intelhdmi.c +++ b/sound/pci/hda/patch_intelhdmi.c @@ -185,14 +185,14 @@ static int patch_intel_hdmi(struct hda_codec *codec) } static struct hda_codec_preset snd_hda_preset_intelhdmi[] = { - { .id = 0x808629fb, .name = "G45 DEVCL", .patch = patch_intel_hdmi }, - { .id = 0x80862801, .name = "G45 DEVBLC", .patch = patch_intel_hdmi }, - { .id = 0x80862802, .name = "G45 DEVCTG", .patch = patch_intel_hdmi }, - { .id = 0x80862803, .name = "G45 DEVELK", .patch = patch_intel_hdmi }, - { .id = 0x80862804, .name = "G45 DEVIBX", .patch = patch_intel_hdmi }, - { .id = 0x80860054, .name = "Q57 DEVIBX", .patch = patch_intel_hdmi }, - { .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_intel_hdmi }, - {} /* terminator */ +{ .id = 0x808629fb, .name = "Crestline HDMI", .patch = patch_intel_hdmi }, +{ .id = 0x80862801, .name = "Bearlake HDMI", .patch = patch_intel_hdmi }, +{ .id = 0x80862802, .name = "Cantiga HDMI", .patch = patch_intel_hdmi }, +{ .id = 0x80862803, .name = "Eaglelake HDMI", .patch = patch_intel_hdmi }, +{ .id = 0x80862804, .name = "IbexPeak HDMI", .patch = patch_intel_hdmi }, +{ .id = 0x80860054, .name = "IbexPeak HDMI", .patch = patch_intel_hdmi }, +{ .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_intel_hdmi }, +{} /* terminator */ }; MODULE_ALIAS("snd-hda-codec-id:808629fb"); -- cgit v1.2.3 From e48b00870f269541c28bec8d457904a04545d5e5 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Fri, 14 May 2010 16:36:14 +0800 Subject: ALSA: intelhdmi - add id for the CougarPoint chipset Add id for Intel CougarPoint HDMI audio codec. CougarPoint provides 3 Audio Converters. Increase MAX_HDMI_CVTS/MAX_HDMI_PINS accordingly. Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_intelhdmi.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c index 433224d28b99..b81d23e42ace 100644 --- a/sound/pci/hda/patch_intelhdmi.c +++ b/sound/pci/hda/patch_intelhdmi.c @@ -40,7 +40,7 @@ * * The HDA correspondence of pipes/ports are converter/pin nodes. */ -#define MAX_HDMI_CVTS 2 +#define MAX_HDMI_CVTS 3 #define MAX_HDMI_PINS 3 #include "patch_hdmi.c" @@ -48,6 +48,7 @@ static char *intel_hdmi_pcm_names[MAX_HDMI_CVTS] = { "INTEL HDMI 0", "INTEL HDMI 1", + "INTEL HDMI 2", }; /* @@ -191,6 +192,7 @@ static struct hda_codec_preset snd_hda_preset_intelhdmi[] = { { .id = 0x80862803, .name = "Eaglelake HDMI", .patch = patch_intel_hdmi }, { .id = 0x80862804, .name = "IbexPeak HDMI", .patch = patch_intel_hdmi }, { .id = 0x80860054, .name = "IbexPeak HDMI", .patch = patch_intel_hdmi }, +{ .id = 0x80862805, .name = "CougarPoint HDMI", .patch = patch_intel_hdmi }, { .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_intel_hdmi }, {} /* terminator */ }; @@ -200,6 +202,7 @@ MODULE_ALIAS("snd-hda-codec-id:80862801"); MODULE_ALIAS("snd-hda-codec-id:80862802"); MODULE_ALIAS("snd-hda-codec-id:80862803"); MODULE_ALIAS("snd-hda-codec-id:80862804"); +MODULE_ALIAS("snd-hda-codec-id:80862805"); MODULE_ALIAS("snd-hda-codec-id:80860054"); MODULE_ALIAS("snd-hda-codec-id:10951392"); -- cgit v1.2.3 From 3eaead579ebc09d4f4c050c9b6ce6d67337bcb20 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Fri, 14 May 2010 16:36:15 +0800 Subject: ALSA: hdmi - dont fail on extra nodes The number of HDMI nodes is expected to go up in future. So don't fail hard on seeing extra converter/pin nodes. We can still operate safely on the nodes within MAX_HDMI_CVTS/MAX_HDMI_PINS. Signed-off-by: Wu Fengguang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_hdmi.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 2c2bafbf0258..86067ee78632 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -766,7 +766,7 @@ static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid) if (spec->num_pins >= MAX_HDMI_PINS) { snd_printk(KERN_WARNING "HDMI: no space for pin %d\n", pin_nid); - return -EINVAL; + return -E2BIG; } hdmi_present_sense(codec, pin_nid, &spec->sink_eld[spec->num_pins]); @@ -788,7 +788,7 @@ static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t nid) if (spec->num_cvts >= MAX_HDMI_CVTS) { snd_printk(KERN_WARNING "HDMI: no space for converter %d\n", nid); - return -EINVAL; + return -E2BIG; } spec->cvt[spec->num_cvts] = nid; @@ -820,15 +820,13 @@ static int hdmi_parse_codec(struct hda_codec *codec) switch (type) { case AC_WID_AUD_OUT: - if (hdmi_add_cvt(codec, nid) < 0) - return -EINVAL; + hdmi_add_cvt(codec, nid); break; case AC_WID_PIN: caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP); if (!(caps & (AC_PINCAP_HDMI | AC_PINCAP_DP))) continue; - if (hdmi_add_pin(codec, nid) < 0) - return -EINVAL; + hdmi_add_pin(codec, nid); break; } } -- cgit v1.2.3 From 550a8b691ca67761bbf382d98fbd81d215f1d7f0 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Thu, 13 May 2010 21:58:37 +0200 Subject: ALSA: sound/pci/asihpi: Use kzalloc Use kzalloc rather than the combination of kmalloc and memset. The semantic patch that makes this change is as follows: (http://coccinelle.lip6.fr/) // @@ expression x,size,flags; statement S; @@ -x = kmalloc(size,flags); +x = kzalloc(size,flags); if (x == NULL) S -memset(x, 0, size); // Signed-off-by: Julia Lawall Signed-off-by: Takashi Iwai --- sound/pci/asihpi/hpi6000.c | 3 +-- sound/pci/asihpi/hpi6205.c | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'sound/pci') diff --git a/sound/pci/asihpi/hpi6000.c b/sound/pci/asihpi/hpi6000.c index 9c50931731a5..839ecb2e4b64 100644 --- a/sound/pci/asihpi/hpi6000.c +++ b/sound/pci/asihpi/hpi6000.c @@ -448,14 +448,13 @@ static void subsys_create_adapter(struct hpi_message *phm, if (phm->u.s.resource.r.pci->device_id != HPI_PCI_DEV_ID_PCI2040) return; - ao.priv = kmalloc(sizeof(struct hpi_hw_obj), GFP_KERNEL); + ao.priv = kzalloc(sizeof(struct hpi_hw_obj), GFP_KERNEL); if (!ao.priv) { HPI_DEBUG_LOG(ERROR, "cant get mem for adapter object\n"); phr->error = HPI_ERROR_MEMORY_ALLOC; return; } - memset(ao.priv, 0, sizeof(struct hpi_hw_obj)); /* create the adapter object based on the resource information */ /*? memcpy(&ao.Pci,&phm->u.s.Resource.r.Pci,sizeof(ao.Pci)); */ ao.pci = *phm->u.s.resource.r.pci; diff --git a/sound/pci/asihpi/hpi6205.c b/sound/pci/asihpi/hpi6205.c index 8df2ff76d944..5e88c1fc2b9e 100644 --- a/sound/pci/asihpi/hpi6205.c +++ b/sound/pci/asihpi/hpi6205.c @@ -482,13 +482,12 @@ static void subsys_create_adapter(struct hpi_message *phm, if (phm->u.s.resource.r.pci->device_id != HPI_PCI_DEV_ID_DSP6205) return; - ao.priv = kmalloc(sizeof(struct hpi_hw_obj), GFP_KERNEL); + ao.priv = kzalloc(sizeof(struct hpi_hw_obj), GFP_KERNEL); if (!ao.priv) { HPI_DEBUG_LOG(ERROR, "cant get mem for adapter object\n"); phr->error = HPI_ERROR_MEMORY_ALLOC; return; } - memset(ao.priv, 0, sizeof(struct hpi_hw_obj)); ao.pci = *phm->u.s.resource.r.pci; err = create_adapter_obj(&ao, &os_error_code); -- cgit v1.2.3 From fbc256692eac29e04cf87e45736d7ff149180a52 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Thu, 20 May 2010 10:40:55 +0200 Subject: ALSA: hda: Storage class should be before const qualifier The C99 specification states in section 6.11.5: The placement of a storage-class specifier other than at the beginning of the declaration specifiers in a declaration is an obsolescent feature. Signed-off-by: Tobias Klauser Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 80a183f99eb3..53538b0f9991 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -14207,7 +14207,7 @@ enum { ALC269_FIXUP_SONY_VAIO, }; -const static struct hda_verb alc269_sony_vaio_fixup_verbs[] = { +static const struct hda_verb alc269_sony_vaio_fixup_verbs[] = { {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREFGRD}, {} }; -- cgit v1.2.3