diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /sound/pcmcia/vx |
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'sound/pcmcia/vx')
-rw-r--r-- | sound/pcmcia/vx/Makefile | 11 | ||||
-rw-r--r-- | sound/pcmcia/vx/vx_entry.c | 384 | ||||
-rw-r--r-- | sound/pcmcia/vx/vxp440.c | 14 | ||||
-rw-r--r-- | sound/pcmcia/vx/vxp_mixer.c | 148 | ||||
-rw-r--r-- | sound/pcmcia/vx/vxp_ops.c | 614 | ||||
-rw-r--r-- | sound/pcmcia/vx/vxpocket.c | 164 | ||||
-rw-r--r-- | sound/pcmcia/vx/vxpocket.h | 118 |
7 files changed, 1453 insertions, 0 deletions
diff --git a/sound/pcmcia/vx/Makefile b/sound/pcmcia/vx/Makefile new file mode 100644 index 000000000000..f35dfa1af094 --- /dev/null +++ b/sound/pcmcia/vx/Makefile @@ -0,0 +1,11 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz> +# + +snd-vx-cs-objs := vx_entry.o vxp_ops.o vxp_mixer.o +snd-vxpocket-objs := vxpocket.o +snd-vxp440-objs := vxp440.o + +obj-$(CONFIG_SND_VXPOCKET) += snd-vxpocket.o snd-vx-cs.o +obj-$(CONFIG_SND_VXP440) += snd-vxp440.o snd-vx-cs.o diff --git a/sound/pcmcia/vx/vx_entry.c b/sound/pcmcia/vx/vx_entry.c new file mode 100644 index 000000000000..53d8172c52ae --- /dev/null +++ b/sound/pcmcia/vx/vx_entry.c @@ -0,0 +1,384 @@ +/* + * Driver for Digigram VXpocket soundcards + * + * PCMCIA entry part + * + * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 + */ + +#include <sound/driver.h> +#include <sound/core.h> +#include "vxpocket.h" +#include <pcmcia/ciscode.h> +#include <pcmcia/cisreg.h> + + +MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>"); +MODULE_DESCRIPTION("Common routines for Digigram PCMCIA VX drivers"); +MODULE_LICENSE("GPL"); + +/* + * prototypes + */ +static void vxpocket_config(dev_link_t *link); +static int vxpocket_event(event_t event, int priority, event_callback_args_t *args); + + +static void vxpocket_release(dev_link_t *link) +{ + if (link->state & DEV_CONFIG) { + /* release cs resources */ + pcmcia_release_configuration(link->handle); + pcmcia_release_io(link->handle, &link->io); + pcmcia_release_irq(link->handle, &link->irq); + link->state &= ~DEV_CONFIG; + } +} + +/* + * destructor + */ +static int snd_vxpocket_free(vx_core_t *chip) +{ + struct snd_vxpocket *vxp = (struct snd_vxpocket *)chip; + struct snd_vxp_entry *hw; + dev_link_t *link = &vxp->link; + + vxpocket_release(link); + + /* Break the link with Card Services */ + if (link->handle) + pcmcia_deregister_client(link->handle); + + hw = vxp->hw_entry; + if (hw) + hw->card_list[vxp->index] = NULL; + chip->card = NULL; + if (chip->dev) + kfree(chip->dev); + + snd_vx_free_firmware(chip); + kfree(chip); + return 0; +} + +static int snd_vxpocket_dev_free(snd_device_t *device) +{ + vx_core_t *chip = device->device_data; + return snd_vxpocket_free(chip); +} + +/* + * snd_vxpocket_attach - attach callback for cs + * @hw: the hardware information + */ +dev_link_t *snd_vxpocket_attach(struct snd_vxp_entry *hw) +{ + client_reg_t client_reg; /* Register with cardmgr */ + dev_link_t *link; /* Info for cardmgr */ + int i, ret; + vx_core_t *chip; + struct snd_vxpocket *vxp; + snd_card_t *card; + static snd_device_ops_t ops = { + .dev_free = snd_vxpocket_dev_free, + }; + + snd_printdd(KERN_DEBUG "vxpocket_attach called\n"); + /* find an empty slot from the card list */ + for (i = 0; i < SNDRV_CARDS; i++) { + if (! hw->card_list[i]) + break; + } + if (i >= SNDRV_CARDS) { + snd_printk(KERN_ERR "vxpocket: too many cards found\n"); + return NULL; + } + if (! hw->enable_table[i]) + return NULL; /* disabled explicitly */ + + /* ok, create a card instance */ + card = snd_card_new(hw->index_table[i], hw->id_table[i], THIS_MODULE, 0); + if (card == NULL) { + snd_printk(KERN_ERR "vxpocket: cannot create a card instance\n"); + return NULL; + } + + chip = snd_vx_create(card, hw->hardware, hw->ops, + sizeof(struct snd_vxpocket) - sizeof(vx_core_t)); + if (! chip) + return NULL; + +#ifdef SND_VX_FW_LOADER + /* fake a device here since pcmcia doesn't give a valid device... */ + chip->dev = kcalloc(1, sizeof(*chip->dev), GFP_KERNEL); + if (! chip->dev) { + snd_printk(KERN_ERR "vxp: can't malloc chip->dev\n"); + kfree(chip); + snd_card_free(card); + return NULL; + } + device_initialize(chip->dev); + sprintf(chip->dev->bus_id, "vxpocket%d", i); +#endif + + if (snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops) < 0) { + kfree(chip); + snd_card_free(card); + return NULL; + } + + vxp = (struct snd_vxpocket *)chip; + vxp->index = i; + vxp->hw_entry = hw; + chip->ibl.size = hw->ibl[i]; + hw->card_list[i] = chip; + + link = &vxp->link; + link->priv = chip; + + link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; + link->io.NumPorts1 = 16; + + link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT; + // link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED; + + link->irq.IRQInfo1 = IRQ_LEVEL_ID; + link->irq.Handler = &snd_vx_irq_handler; + link->irq.Instance = chip; + + link->conf.Attributes = CONF_ENABLE_IRQ; + link->conf.Vcc = 50; + link->conf.IntType = INT_MEMORY_AND_IO; + link->conf.ConfigIndex = 1; + link->conf.Present = PRESENT_OPTION; + + /* Register with Card Services */ + memset(&client_reg, 0, sizeof(client_reg)); + client_reg.dev_info = hw->dev_info; + client_reg.EventMask = + CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL +#ifdef CONFIG_PM + | CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET + | CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME +#endif + ; + client_reg.event_handler = &vxpocket_event; + client_reg.Version = 0x0210; + client_reg.event_callback_args.client_data = link; + + ret = pcmcia_register_client(&link->handle, &client_reg); + if (ret != CS_SUCCESS) { + cs_error(link->handle, RegisterClient, ret); + snd_card_free(card); + return NULL; + } + + /* Chain drivers */ + link->next = hw->dev_list; + hw->dev_list = link; + + /* snd_card_set_pm_callback(card, snd_vxpocket_suspend, snd_vxpocket_resume, chip); */ + + return link; +} + + +/** + * snd_vxpocket_assign_resources - initialize the hardware and card instance. + * @port: i/o port for the card + * @irq: irq number for the card + * + * this function assigns the specified port and irq, boot the card, + * create pcm and control instances, and initialize the rest hardware. + * + * returns 0 if successful, or a negative error code. + */ +static int snd_vxpocket_assign_resources(vx_core_t *chip, int port, int irq) +{ + int err; + snd_card_t *card = chip->card; + struct snd_vxpocket *vxp = (struct snd_vxpocket *)chip; + + snd_printdd(KERN_DEBUG "vxpocket assign resources: port = 0x%x, irq = %d\n", port, irq); + vxp->port = port; + + sprintf(card->shortname, "Digigram %s", card->driver); + sprintf(card->longname, "%s at 0x%x, irq %i", + card->shortname, port, irq); + + chip->irq = irq; + + if ((err = snd_vx_setup_firmware(chip)) < 0) + return err; + + return 0; +} + + +/* + * snd_vxpocket_detach - detach callback for cs + * @hw: the hardware information + */ +void snd_vxpocket_detach(struct snd_vxp_entry *hw, dev_link_t *link) +{ + vx_core_t *chip; + + if (! link) + return; + + chip = link->priv; + + snd_printdd(KERN_DEBUG "vxpocket_detach called\n"); + /* Remove the interface data from the linked list */ + if (hw) { + dev_link_t **linkp; + /* Locate device structure */ + for (linkp = &hw->dev_list; *linkp; linkp = &(*linkp)->next) + if (*linkp == link) { + *linkp = link->next; + break; + } + } + chip->chip_status |= VX_STAT_IS_STALE; /* to be sure */ + snd_card_disconnect(chip->card); + snd_card_free_in_thread(chip->card); +} + +/* + * configuration callback + */ + +#define CS_CHECK(fn, ret) \ +do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) + +static void vxpocket_config(dev_link_t *link) +{ + client_handle_t handle = link->handle; + vx_core_t *chip = link->priv; + struct snd_vxpocket *vxp = (struct snd_vxpocket *)chip; + tuple_t tuple; + cisparse_t *parse = NULL; + u_short buf[32]; + int last_fn, last_ret; + + snd_printdd(KERN_DEBUG "vxpocket_config called\n"); + parse = kmalloc(sizeof(*parse), GFP_KERNEL); + if (! parse) { + snd_printk(KERN_ERR "vx: cannot allocate\n"); + return; + } + tuple.Attributes = 0; + tuple.TupleData = (cisdata_t *)buf; + tuple.TupleDataMax = sizeof(buf); + tuple.TupleOffset = 0; + tuple.DesiredTuple = CISTPL_CONFIG; + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); + CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); + CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, parse)); + link->conf.ConfigBase = parse->config.base; + link->conf.Present = parse->config.rmask[0]; + + /* Configure card */ + link->state |= DEV_CONFIG; + + CS_CHECK(RequestIO, pcmcia_request_io(handle, &link->io)); + CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq)); + CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf)); + + if (snd_vxpocket_assign_resources(chip, link->io.BasePort1, link->irq.AssignedIRQ) < 0) + goto failed; + + link->dev = &vxp->node; + link->state &= ~DEV_CONFIG_PENDING; + kfree(parse); + return; + +cs_failed: + cs_error(link->handle, last_fn, last_ret); +failed: + pcmcia_release_configuration(link->handle); + pcmcia_release_io(link->handle, &link->io); + pcmcia_release_irq(link->handle, &link->irq); + link->state &= ~DEV_CONFIG; + kfree(parse); +} + + +/* + * event callback + */ +static int vxpocket_event(event_t event, int priority, event_callback_args_t *args) +{ + dev_link_t *link = args->client_data; + vx_core_t *chip = link->priv; + + switch (event) { + case CS_EVENT_CARD_REMOVAL: + snd_printdd(KERN_DEBUG "CARD_REMOVAL..\n"); + link->state &= ~DEV_PRESENT; + if (link->state & DEV_CONFIG) { + chip->chip_status |= VX_STAT_IS_STALE; + } + break; + case CS_EVENT_CARD_INSERTION: + snd_printdd(KERN_DEBUG "CARD_INSERTION..\n"); + link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; + vxpocket_config(link); + break; +#ifdef CONFIG_PM + case CS_EVENT_PM_SUSPEND: + snd_printdd(KERN_DEBUG "SUSPEND\n"); + link->state |= DEV_SUSPEND; + if (chip && chip->card->pm_suspend) { + snd_printdd(KERN_DEBUG "snd_vx_suspend calling\n"); + chip->card->pm_suspend(chip->card, PMSG_SUSPEND); + } + /* Fall through... */ + case CS_EVENT_RESET_PHYSICAL: + snd_printdd(KERN_DEBUG "RESET_PHYSICAL\n"); + if (link->state & DEV_CONFIG) + pcmcia_release_configuration(link->handle); + break; + case CS_EVENT_PM_RESUME: + snd_printdd(KERN_DEBUG "RESUME\n"); + link->state &= ~DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_CARD_RESET: + snd_printdd(KERN_DEBUG "CARD_RESET\n"); + if (DEV_OK(link)) { + //struct snd_vxpocket *vxp = (struct snd_vxpocket *)chip; + snd_printdd(KERN_DEBUG "requestconfig...\n"); + pcmcia_request_configuration(link->handle, &link->conf); + if (chip && chip->card->pm_resume) { + snd_printdd(KERN_DEBUG "calling snd_vx_resume\n"); + chip->card->pm_resume(chip->card); + } + } + snd_printdd(KERN_DEBUG "resume done!\n"); + break; +#endif + } + return 0; +} + +/* + * exported stuffs + */ +EXPORT_SYMBOL(snd_vxpocket_ops); +EXPORT_SYMBOL(snd_vxpocket_attach); +EXPORT_SYMBOL(snd_vxpocket_detach); diff --git a/sound/pcmcia/vx/vxp440.c b/sound/pcmcia/vx/vxp440.c new file mode 100644 index 000000000000..59190a833001 --- /dev/null +++ b/sound/pcmcia/vx/vxp440.c @@ -0,0 +1,14 @@ +#define COMPILE_VXP440 + +/* + add the following as /etc/pcmcia/vxp440.conf: + + device "snd-vxp440" + class "audio" module "snd-vxp440" + + card "Digigram VX-POCKET440" + manfid 0x01f1, 0x0100 + bind "snd-vxp440" +*/ + +#include "vxpocket.c" diff --git a/sound/pcmcia/vx/vxp_mixer.c b/sound/pcmcia/vx/vxp_mixer.c new file mode 100644 index 000000000000..aeaef3d81801 --- /dev/null +++ b/sound/pcmcia/vx/vxp_mixer.c @@ -0,0 +1,148 @@ +/* + * Driver for Digigram VXpocket soundcards + * + * VX-pocket mixer + * + * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 + */ + +#include <sound/driver.h> +#include <sound/core.h> +#include <sound/control.h> +#include "vxpocket.h" + +#define MIC_LEVEL_MIN 0 +#define MIC_LEVEL_MAX 8 + +/* + * mic level control (for VXPocket) + */ +static int vx_mic_level_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = MIC_LEVEL_MAX; + return 0; +} + +static int vx_mic_level_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + vx_core_t *_chip = snd_kcontrol_chip(kcontrol); + struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; + ucontrol->value.integer.value[0] = chip->mic_level; + return 0; +} + +static int vx_mic_level_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + vx_core_t *_chip = snd_kcontrol_chip(kcontrol); + struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; + down(&_chip->mixer_mutex); + if (chip->mic_level != ucontrol->value.integer.value[0]) { + vx_set_mic_level(_chip, ucontrol->value.integer.value[0]); + chip->mic_level = ucontrol->value.integer.value[0]; + up(&_chip->mixer_mutex); + return 1; + } + up(&_chip->mixer_mutex); + return 0; +} + +static snd_kcontrol_new_t vx_control_mic_level = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Mic Capture Volume", + .info = vx_mic_level_info, + .get = vx_mic_level_get, + .put = vx_mic_level_put, +}; + +/* + * mic boost level control (for VXP440) + */ +static int vx_mic_boost_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int vx_mic_boost_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + vx_core_t *_chip = snd_kcontrol_chip(kcontrol); + struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; + ucontrol->value.integer.value[0] = chip->mic_level; + return 0; +} + +static int vx_mic_boost_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + vx_core_t *_chip = snd_kcontrol_chip(kcontrol); + struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; + down(&_chip->mixer_mutex); + if (chip->mic_level != ucontrol->value.integer.value[0]) { + vx_set_mic_boost(_chip, ucontrol->value.integer.value[0]); + chip->mic_level = ucontrol->value.integer.value[0]; + up(&_chip->mixer_mutex); + return 1; + } + up(&_chip->mixer_mutex); + return 0; +} + +static snd_kcontrol_new_t vx_control_mic_boost = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Mic Boost", + .info = vx_mic_boost_info, + .get = vx_mic_boost_get, + .put = vx_mic_boost_put, +}; + + +int vxp_add_mic_controls(vx_core_t *_chip) +{ + struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; + int err; + + /* mute input levels */ + chip->mic_level = 0; + switch (_chip->type) { + case VX_TYPE_VXPOCKET: + vx_set_mic_level(_chip, 0); + break; + case VX_TYPE_VXP440: + vx_set_mic_boost(_chip, 0); + break; + } + + /* mic level */ + switch (_chip->type) { + case VX_TYPE_VXPOCKET: + if ((err = snd_ctl_add(_chip->card, snd_ctl_new1(&vx_control_mic_level, chip))) < 0) + return err; + break; + case VX_TYPE_VXP440: + if ((err = snd_ctl_add(_chip->card, snd_ctl_new1(&vx_control_mic_boost, chip))) < 0) + return err; + break; + } + + return 0; +} + diff --git a/sound/pcmcia/vx/vxp_ops.c b/sound/pcmcia/vx/vxp_ops.c new file mode 100644 index 000000000000..ef6734271607 --- /dev/null +++ b/sound/pcmcia/vx/vxp_ops.c @@ -0,0 +1,614 @@ +/* + * Driver for Digigram VXpocket soundcards + * + * lowlevel routines for VXpocket soundcards + * + * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 + */ + +#include <sound/driver.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/firmware.h> +#include <sound/core.h> +#include <asm/io.h> +#include "vxpocket.h" + + +static int vxp_reg_offset[VX_REG_MAX] = { + [VX_ICR] = 0x00, // ICR + [VX_CVR] = 0x01, // CVR + [VX_ISR] = 0x02, // ISR + [VX_IVR] = 0x03, // IVR + [VX_RXH] = 0x05, // RXH + [VX_RXM] = 0x06, // RXM + [VX_RXL] = 0x07, // RXL + [VX_DMA] = 0x04, // DMA + [VX_CDSP] = 0x08, // CDSP + [VX_LOFREQ] = 0x09, // LFREQ + [VX_HIFREQ] = 0x0a, // HFREQ + [VX_DATA] = 0x0b, // DATA + [VX_MICRO] = 0x0c, // MICRO + [VX_DIALOG] = 0x0d, // DIALOG + [VX_CSUER] = 0x0e, // CSUER + [VX_RUER] = 0x0f, // RUER +}; + + +inline static unsigned long vxp_reg_addr(vx_core_t *_chip, int reg) +{ + struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; + return chip->port + vxp_reg_offset[reg]; +} + +/* + * snd_vx_inb - read a byte from the register + * @offset: register offset + */ +static unsigned char vxp_inb(vx_core_t *chip, int offset) +{ + return inb(vxp_reg_addr(chip, offset)); +} + +/* + * snd_vx_outb - write a byte on the register + * @offset: the register offset + * @val: the value to write + */ +static void vxp_outb(vx_core_t *chip, int offset, unsigned char val) +{ + outb(val, vxp_reg_addr(chip, offset)); +} + +/* + * redefine macros to call directly + */ +#undef vx_inb +#define vx_inb(chip,reg) vxp_inb((vx_core_t*)(chip), VX_##reg) +#undef vx_outb +#define vx_outb(chip,reg,val) vxp_outb((vx_core_t*)(chip), VX_##reg,val) + + +/* + * vx_check_magic - check the magic word on xilinx + * + * returns zero if a magic word is detected, or a negative error code. + */ +static int vx_check_magic(vx_core_t *chip) +{ + unsigned long end_time = jiffies + HZ / 5; + int c; + do { + c = vx_inb(chip, CDSP); + if (c == CDSP_MAGIC) + return 0; + snd_vx_delay(chip, 10); + } while (time_after_eq(end_time, jiffies)); + snd_printk(KERN_ERR "cannot find xilinx magic word (%x)\n", c); + return -EIO; +} + + +/* + * vx_reset_dsp - reset the DSP + */ + +#define XX_DSP_RESET_WAIT_TIME 2 /* ms */ + +static void vxp_reset_dsp(vx_core_t *_chip) +{ + struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; + + /* set the reset dsp bit to 1 */ + vx_outb(chip, CDSP, chip->regCDSP | VXP_CDSP_DSP_RESET_MASK); + vx_inb(chip, CDSP); + mdelay(XX_DSP_RESET_WAIT_TIME); + /* reset the bit */ + chip->regCDSP &= ~VXP_CDSP_DSP_RESET_MASK; + vx_outb(chip, CDSP, chip->regCDSP); + vx_inb(chip, CDSP); + mdelay(XX_DSP_RESET_WAIT_TIME); +} + +/* + * reset codec bit + */ +static void vxp_reset_codec(vx_core_t *_chip) +{ + struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; + + /* Set the reset CODEC bit to 1. */ + vx_outb(chip, CDSP, chip->regCDSP | VXP_CDSP_CODEC_RESET_MASK); + vx_inb(chip, CDSP); + snd_vx_delay(_chip, 10); + /* Set the reset CODEC bit to 0. */ + chip->regCDSP &= ~VXP_CDSP_CODEC_RESET_MASK; + vx_outb(chip, CDSP, chip->regCDSP); + vx_inb(chip, CDSP); + snd_vx_delay(_chip, 1); +} + +/* + * vx_load_xilinx_binary - load the xilinx binary image + * the binary image is the binary array converted from the bitstream file. + */ +static int vxp_load_xilinx_binary(vx_core_t *_chip, const struct firmware *fw) +{ + struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; + unsigned int i; + int c; + int regCSUER, regRUER; + unsigned char *image; + unsigned char data; + + /* Switch to programmation mode */ + chip->regDIALOG |= VXP_DLG_XILINX_REPROG_MASK; + vx_outb(chip, DIALOG, chip->regDIALOG); + + /* Save register CSUER and RUER */ + regCSUER = vx_inb(chip, CSUER); + regRUER = vx_inb(chip, RUER); + + /* reset HF0 and HF1 */ + vx_outb(chip, ICR, 0); + + /* Wait for answer HF2 equal to 1 */ + snd_printdd(KERN_DEBUG "check ISR_HF2\n"); + if (vx_check_isr(_chip, ISR_HF2, ISR_HF2, 20) < 0) + goto _error; + + /* set HF1 for loading xilinx binary */ + vx_outb(chip, ICR, ICR_HF1); + image = fw->data; + for (i = 0; i < fw->size; i++, image++) { + data = *image; + if (vx_wait_isr_bit(_chip, ISR_TX_EMPTY) < 0) + goto _error; + vx_outb(chip, TXL, data); + /* wait for reading */ + if (vx_wait_for_rx_full(_chip) < 0) + goto _error; + c = vx_inb(chip, RXL); + if (c != (int)data) + snd_printk(KERN_ERR "vxpocket: load xilinx mismatch at %d: 0x%x != 0x%x\n", i, c, (int)data); + } + + /* reset HF1 */ + vx_outb(chip, ICR, 0); + + /* wait for HF3 */ + if (vx_check_isr(_chip, ISR_HF3, ISR_HF3, 20) < 0) + goto _error; + + /* read the number of bytes received */ + if (vx_wait_for_rx_full(_chip) < 0) + goto _error; + + c = (int)vx_inb(chip, RXH) << 16; + c |= (int)vx_inb(chip, RXM) << 8; + c |= vx_inb(chip, RXL); + + snd_printdd(KERN_DEBUG "xilinx: dsp size received 0x%x, orig 0x%x\n", c, fw->size); + + vx_outb(chip, ICR, ICR_HF0); + + /* TEMPO 250ms : wait until Xilinx is downloaded */ + snd_vx_delay(_chip, 300); + + /* test magical word */ + if (vx_check_magic(_chip) < 0) + goto _error; + + /* Restore register 0x0E and 0x0F (thus replacing COR and FCSR) */ + vx_outb(chip, CSUER, regCSUER); + vx_outb(chip, RUER, regRUER); + + /* Reset the Xilinx's signal enabling IO access */ + chip->regDIALOG |= VXP_DLG_XILINX_REPROG_MASK; + vx_outb(chip, DIALOG, chip->regDIALOG); + vx_inb(chip, DIALOG); + snd_vx_delay(_chip, 10); + chip->regDIALOG &= ~VXP_DLG_XILINX_REPROG_MASK; + vx_outb(chip, DIALOG, chip->regDIALOG); + vx_inb(chip, DIALOG); + + /* Reset of the Codec */ + vxp_reset_codec(_chip); + vx_reset_dsp(_chip); + + return 0; + + _error: + vx_outb(chip, CSUER, regCSUER); + vx_outb(chip, RUER, regRUER); + chip->regDIALOG &= ~VXP_DLG_XILINX_REPROG_MASK; + vx_outb(chip, DIALOG, chip->regDIALOG); + return -EIO; +} + + +/* + * vxp_load_dsp - load_dsp callback + */ +static int vxp_load_dsp(vx_core_t *vx, int index, const struct firmware *fw) +{ + int err; + + switch (index) { + case 0: + /* xilinx boot */ + if ((err = vx_check_magic(vx)) < 0) + return err; + if ((err = snd_vx_load_boot_image(vx, fw)) < 0) + return err; + return 0; + case 1: + /* xilinx image */ + return vxp_load_xilinx_binary(vx, fw); + case 2: + /* DSP boot */ + return snd_vx_dsp_boot(vx, fw); + case 3: + /* DSP image */ + return snd_vx_dsp_load(vx, fw); + default: + snd_BUG(); + return -EINVAL; + } +} + + +/* + * vx_test_and_ack - test and acknowledge interrupt + * + * called from irq hander, too + * + * spinlock held! + */ +static int vxp_test_and_ack(vx_core_t *_chip) +{ + struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; + + /* not booted yet? */ + if (! (_chip->chip_status & VX_STAT_XILINX_LOADED)) + return -ENXIO; + + if (! (vx_inb(chip, DIALOG) & VXP_DLG_MEMIRQ_MASK)) + return -EIO; + + /* ok, interrupts generated, now ack it */ + /* set ACQUIT bit up and down */ + vx_outb(chip, DIALOG, chip->regDIALOG | VXP_DLG_ACK_MEMIRQ_MASK); + /* useless read just to spend some time and maintain + * the ACQUIT signal up for a while ( a bus cycle ) + */ + vx_inb(chip, DIALOG); + vx_outb(chip, DIALOG, chip->regDIALOG & ~VXP_DLG_ACK_MEMIRQ_MASK); + + return 0; +} + + +/* + * vx_validate_irq - enable/disable IRQ + */ +static void vxp_validate_irq(vx_core_t *_chip, int enable) +{ + struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; + + /* Set the interrupt enable bit to 1 in CDSP register */ + if (enable) + chip->regCDSP |= VXP_CDSP_VALID_IRQ_MASK; + else + chip->regCDSP &= ~VXP_CDSP_VALID_IRQ_MASK; + vx_outb(chip, CDSP, chip->regCDSP); +} + +/* + * vx_setup_pseudo_dma - set up the pseudo dma read/write mode. + * @do_write: 0 = read, 1 = set up for DMA write + */ +static void vx_setup_pseudo_dma(vx_core_t *_chip, int do_write) +{ + struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; + + /* Interrupt mode and HREQ pin enabled for host transmit / receive data transfers */ + vx_outb(chip, ICR, do_write ? ICR_TREQ : ICR_RREQ); + /* Reset the pseudo-dma register */ + vx_inb(chip, ISR); + vx_outb(chip, ISR, 0); + + /* Select DMA in read/write transfer mode and in 16-bit accesses */ + chip->regDIALOG |= VXP_DLG_DMA16_SEL_MASK; + chip->regDIALOG |= do_write ? VXP_DLG_DMAWRITE_SEL_MASK : VXP_DLG_DMAREAD_SEL_MASK; + vx_outb(chip, DIALOG, chip->regDIALOG); + +} + +/* + * vx_release_pseudo_dma - disable the pseudo-DMA mode + */ +static void vx_release_pseudo_dma(vx_core_t *_chip) +{ + struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; + + /* Disable DMA and 16-bit accesses */ + chip->regDIALOG &= ~(VXP_DLG_DMAWRITE_SEL_MASK| + VXP_DLG_DMAREAD_SEL_MASK| + VXP_DLG_DMA16_SEL_MASK); + vx_outb(chip, DIALOG, chip->regDIALOG); + /* HREQ pin disabled. */ + vx_outb(chip, ICR, 0); +} + +/* + * vx_pseudo_dma_write - write bulk data on pseudo-DMA mode + * @count: data length to transfer in bytes + * + * data size must be aligned to 6 bytes to ensure the 24bit alignment on DSP. + * NB: call with a certain lock! + */ +static void vxp_dma_write(vx_core_t *chip, snd_pcm_runtime_t *runtime, + vx_pipe_t *pipe, int count) +{ + long port = vxp_reg_addr(chip, VX_DMA); + int offset = pipe->hw_ptr; + unsigned short *addr = (unsigned short *)(runtime->dma_area + offset); + + vx_setup_pseudo_dma(chip, 1); + if (offset + count > pipe->buffer_bytes) { + int length = pipe->buffer_bytes - offset; + count -= length; + length >>= 1; /* in 16bit words */ + /* Transfer using pseudo-dma. */ + while (length-- > 0) { + outw(cpu_to_le16(*addr), port); + addr++; + } + addr = (unsigned short *)runtime->dma_area; + pipe->hw_ptr = 0; + } + pipe->hw_ptr += count; + count >>= 1; /* in 16bit words */ + /* Transfer using pseudo-dma. */ + while (count-- > 0) { + outw(cpu_to_le16(*addr), port); + addr++; + } + vx_release_pseudo_dma(chip); +} + + +/* + * vx_pseudo_dma_read - read bulk data on pseudo DMA mode + * @offset: buffer offset in bytes + * @count: data length to transfer in bytes + * + * the read length must be aligned to 6 bytes, as well as write. + * NB: call with a certain lock! + */ +static void vxp_dma_read(vx_core_t *chip, snd_pcm_runtime_t *runtime, + vx_pipe_t *pipe, int count) +{ + struct snd_vxpocket *pchip = (struct snd_vxpocket *)chip; + long port = vxp_reg_addr(chip, VX_DMA); + int offset = pipe->hw_ptr; + unsigned short *addr = (unsigned short *)(runtime->dma_area + offset); + + snd_assert(count % 2 == 0, return); + vx_setup_pseudo_dma(chip, 0); + if (offset + count > pipe->buffer_bytes) { + int length = pipe->buffer_bytes - offset; + count -= length; + length >>= 1; /* in 16bit words */ + /* Transfer using pseudo-dma. */ + while (length-- > 0) + *addr++ = le16_to_cpu(inw(port)); + addr = (unsigned short *)runtime->dma_area; + pipe->hw_ptr = 0; + } + pipe->hw_ptr += count; + count >>= 1; /* in 16bit words */ + /* Transfer using pseudo-dma. */ + while (count-- > 1) + *addr++ = le16_to_cpu(inw(port)); + /* Disable DMA */ + pchip->regDIALOG &= ~VXP_DLG_DMAREAD_SEL_MASK; + vx_outb(chip, DIALOG, pchip->regDIALOG); + /* Read the last word (16 bits) */ + *addr = le16_to_cpu(inw(port)); + /* Disable 16-bit accesses */ + pchip->regDIALOG &= ~VXP_DLG_DMA16_SEL_MASK; + vx_outb(chip, DIALOG, pchip->regDIALOG); + /* HREQ pin disabled. */ + vx_outb(chip, ICR, 0); +} + + +/* + * write a codec data (24bit) + */ +static void vxp_write_codec_reg(vx_core_t *chip, int codec, unsigned int data) +{ + int i; + + /* Activate access to the corresponding codec register */ + if (! codec) + vx_inb(chip, LOFREQ); + else + vx_inb(chip, CODEC2); + + /* We have to send 24 bits (3 x 8 bits). Start with most signif. Bit */ + for (i = 0; i < 24; i++, data <<= 1) + vx_outb(chip, DATA, ((data & 0x800000) ? VX_DATA_CODEC_MASK : 0)); + + /* Terminate access to codec registers */ + vx_inb(chip, HIFREQ); +} + + +/* + * vx_set_mic_boost - set mic boost level (on vxp440 only) + * @boost: 0 = 20dB, 1 = +38dB + */ +void vx_set_mic_boost(vx_core_t *chip, int boost) +{ + struct snd_vxpocket *pchip = (struct snd_vxpocket *)chip; + unsigned long flags; + + if (chip->chip_status & VX_STAT_IS_STALE) + return; + + spin_lock_irqsave(&chip->lock, flags); + if (pchip->regCDSP & P24_CDSP_MICS_SEL_MASK) { + if (boost) { + /* boost: 38 dB */ + pchip->regCDSP &= ~P24_CDSP_MIC20_SEL_MASK; + pchip->regCDSP |= P24_CDSP_MIC38_SEL_MASK; + } else { + /* minimum value: 20 dB */ + pchip->regCDSP |= P24_CDSP_MIC20_SEL_MASK; + pchip->regCDSP &= ~P24_CDSP_MIC38_SEL_MASK; + } + vx_outb(chip, CDSP, pchip->regCDSP); + } + spin_unlock_irqrestore(&chip->lock, flags); +} + +/* + * remap the linear value (0-8) to the actual value (0-15) + */ +static int vx_compute_mic_level(int level) +{ + switch (level) { + case 5: level = 6 ; break; + case 6: level = 8 ; break; + case 7: level = 11; break; + case 8: level = 15; break; + default: break ; + } + return level; +} + +/* + * vx_set_mic_level - set mic level (on vxpocket only) + * @level: the mic level = 0 - 8 (max) + */ +void vx_set_mic_level(vx_core_t *chip, int level) +{ + struct snd_vxpocket *pchip = (struct snd_vxpocket *)chip; + unsigned long flags; + + if (chip->chip_status & VX_STAT_IS_STALE) + return; + + spin_lock_irqsave(&chip->lock, flags); + if (pchip->regCDSP & VXP_CDSP_MIC_SEL_MASK) { + level = vx_compute_mic_level(level); + vx_outb(chip, MICRO, level); + } + spin_unlock_irqrestore(&chip->lock, flags); +} + + +/* + * change the input audio source + */ +static void vxp_change_audio_source(vx_core_t *_chip, int src) +{ + struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; + |