Commit 14b87c6e authored by Jaroslav Kysela's avatar Jaroslav Kysela

[ALSA] Hotplug firmware loader support

Digigram VX core,MIXART driver,Digigram VX222 driver
Digigram VX Pocket driver
The hogplut fw loader is supported by vxpocket, vxp440, vx2222 and mixart
drivers.  The old ALSA fw loader is still supported for built-in kernels.
To use the hotplug, the new firmware data must be installed beforehand
from the latest alsa-tools package.

The experimental suspend/resume for vxpocket, vxp440 and vx222 are added,
too.
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent d137aed3
......@@ -27,6 +27,15 @@
#include <sound/hwdep.h>
#include <linux/interrupt.h>
#if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE)
#if !defined(CONFIG_USE_VXLOADER) && !defined(CONFIG_SND_VX_LIB) /* built-in kernel */
#define SND_VX_FW_LOADER /* use the standard firmware loader */
#endif
#endif
struct firmware;
struct device;
typedef struct snd_vx_core vx_core_t;
typedef struct vx_pipe vx_pipe_t;
......@@ -100,7 +109,7 @@ struct snd_vx_ops {
void (*change_audio_source)(vx_core_t *chip, int src);
void (*set_clock_source)(vx_core_t *chp, int src);
/* chip init */
int (*load_dsp)(vx_core_t *chip, const snd_hwdep_dsp_image_t *dsp);
int (*load_dsp)(vx_core_t *chip, int idx, const struct firmware *fw);
void (*reset_dsp)(vx_core_t *chip);
void (*reset_board)(vx_core_t *chip, int cold_reset);
int (*add_controls)(vx_core_t *chip);
......@@ -169,6 +178,7 @@ struct snd_vx_core {
unsigned int chip_status;
unsigned int pcm_running;
struct device *dev;
snd_hwdep_t *hwdep;
struct vx_rmh irq_rmh; /* RMH used in interrupts */
......@@ -198,6 +208,8 @@ struct snd_vx_core {
unsigned char audio_monitor_active[4]; /* playback hw-monitor mute/unmute */
struct semaphore mixer_mutex;
const struct firmware *firmware[4]; /* loaded firmware data */
};
......@@ -206,25 +218,18 @@ struct snd_vx_core {
*/
vx_core_t *snd_vx_create(snd_card_t *card, struct snd_vx_hardware *hw,
struct snd_vx_ops *ops, int extra_size);
int snd_vx_hwdep_new(vx_core_t *chip);
int snd_vx_load_boot_image(vx_core_t *chip, const snd_hwdep_dsp_image_t *boot);
int snd_vx_dsp_boot(vx_core_t *chip, const snd_hwdep_dsp_image_t *boot);
int snd_vx_dsp_load(vx_core_t *chip, const snd_hwdep_dsp_image_t *dsp);
int snd_vx_setup_firmware(vx_core_t *chip);
int snd_vx_load_boot_image(vx_core_t *chip, const struct firmware *dsp);
int snd_vx_dsp_boot(vx_core_t *chip, const struct firmware *dsp);
int snd_vx_dsp_load(vx_core_t *chip, const struct firmware *dsp);
void snd_vx_free_firmware(vx_core_t *chip);
/*
* interrupt handler; exported for pcmcia
*/
irqreturn_t snd_vx_irq_handler(int irq, void *dev, struct pt_regs *regs);
/*
* power-management routines
*/
#ifdef CONFIG_PM
void snd_vx_suspend(vx_core_t *chip);
void snd_vx_resume(vx_core_t *chip);
#endif
/*
* lowlevel functions
*/
......
......@@ -25,6 +25,7 @@
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/firmware.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/asoundef.h>
......@@ -430,20 +431,19 @@ int vx_send_rih(vx_core_t *chip, int cmd)
* snd_vx_boot_xilinx - boot up the xilinx interface
* @boot: the boot record to load
*/
int snd_vx_load_boot_image(vx_core_t *chip, const snd_hwdep_dsp_image_t *boot)
int snd_vx_load_boot_image(vx_core_t *chip, const struct firmware *boot)
{
unsigned int i;
int no_fillup = vx_has_new_dsp(chip);
/* check the length of boot image */
snd_assert(boot->length > 0, return -EINVAL);
snd_assert(boot->length % 3 == 0, return -EINVAL);
snd_assert(boot->image, return -EINVAL);
snd_assert(boot->size > 0, return -EINVAL);
snd_assert(boot->size % 3 == 0, return -EINVAL);
#if 0
{
/* more strict check */
unsigned int c = ((u32)boot->image[0] << 16) | ((u32)boot->image[1] << 8) | boot->image[2];
snd_assert(boot->length == (c + 2) * 3, return -EINVAL);
unsigned int c = ((u32)boot->data[0] << 16) | ((u32)boot->data[1] << 8) | boot->data[2];
snd_assert(boot->size == (c + 2) * 3, return -EINVAL);
}
#endif
......@@ -454,7 +454,7 @@ int snd_vx_load_boot_image(vx_core_t *chip, const snd_hwdep_dsp_image_t *boot)
/* download boot strap */
for (i = 0; i < 0x600; i += 3) {
if (i >= boot->length) {
if (i >= boot->size) {
if (no_fillup)
break;
if (vx_wait_isr_bit(chip, ISR_TX_EMPTY) < 0) {
......@@ -465,9 +465,7 @@ int snd_vx_load_boot_image(vx_core_t *chip, const snd_hwdep_dsp_image_t *boot)
vx_outb(chip, TXM, 0);
vx_outb(chip, TXL, 0);
} else {
unsigned char image[3];
if (copy_from_user(image, boot->image + i, 3))
return -EFAULT;
unsigned char *image = boot->data + i;
if (vx_wait_isr_bit(chip, ISR_TX_EMPTY) < 0) {
snd_printk(KERN_ERR "dsp boot failed at %d\n", i);
return -EIO;
......@@ -653,7 +651,7 @@ static void vx_proc_init(vx_core_t *chip)
/**
* snd_vx_dsp_boot - load the DSP boot
*/
int snd_vx_dsp_boot(vx_core_t *chip, const snd_hwdep_dsp_image_t *boot)
int snd_vx_dsp_boot(vx_core_t *chip, const struct firmware *boot)
{
int err;
int cold_reset = !(chip->chip_status & VX_STAT_DEVICE_INIT);
......@@ -671,21 +669,20 @@ int snd_vx_dsp_boot(vx_core_t *chip, const snd_hwdep_dsp_image_t *boot)
/**
* snd_vx_dsp_load - load the DSP image
*/
int snd_vx_dsp_load(vx_core_t *chip, const snd_hwdep_dsp_image_t *dsp)
int snd_vx_dsp_load(vx_core_t *chip, const struct firmware *dsp)
{
unsigned int i;
int err;
unsigned int csum = 0;
unsigned char image[3], *cptr;
unsigned char *image, *cptr;
snd_assert(dsp->length % 3 == 0, return -EINVAL);
snd_assert(dsp->size % 3 == 0, return -EINVAL);
vx_toggle_dac_mute(chip, 1);
/* Transfert data buffer from PC to DSP */
for (i = 0; i < dsp->length; i += 3) {
if (copy_from_user(image, dsp->image + i, 3))
return -EFAULT;
for (i = 0; i < dsp->size; i += 3) {
image = dsp->data + i;
/* Wait DSP ready for a new read */
if ((err = vx_wait_isr_bit(chip, ISR_TX_EMPTY)) < 0) {
printk("dsp loading error at position %d\n", i);
......@@ -717,6 +714,54 @@ int snd_vx_dsp_load(vx_core_t *chip, const snd_hwdep_dsp_image_t *dsp)
return 0;
}
#ifdef CONFIG_PM
/*
* suspend
*/
static int snd_vx_suspend(snd_card_t *card, unsigned int state)
{
vx_core_t *chip = card->pm_private_data;
unsigned int i;
snd_assert(chip, return -EINVAL);
chip->chip_status |= VX_STAT_IN_SUSPEND;
for (i = 0; i < chip->hw->num_codecs; i++)
snd_pcm_suspend_all(chip->pcm[i]);
return 0;
}
/*
* resume
*/
static int snd_vx_resume(snd_card_t *card, unsigned int state)
{
vx_core_t *chip = card->pm_private_data;
int i, err;
snd_assert(chip, return -EINVAL);
chip->chip_status &= ~VX_STAT_CHIP_INIT;
for (i = 0; i < 4; i++) {
if (! chip->firmware[i])
continue;
err = chip->ops->load_dsp(chip, i, chip->firmware[i]);
if (err < 0) {
snd_printk(KERN_ERR "vx: firmware resume error at DSP %d\n", i);
return -EIO;
}
}
chip->chip_status |= VX_STAT_CHIP_INIT;
chip->chip_status &= ~VX_STAT_IN_SUSPEND;
return 0;
}
#endif
/**
* snd_vx_create - constructor for vx_core_t
* @hw: hardware specific record
......@@ -753,37 +798,13 @@ vx_core_t *snd_vx_create(snd_card_t *card, struct snd_vx_hardware *hw,
strcpy(card->driver, hw->name);
sprintf(card->shortname, "Digigram %s", hw->name);
snd_card_set_pm_callback(card, snd_vx_suspend, snd_vx_resume, chip);
vx_proc_init(chip);
return chip;
}
#ifdef CONFIG_PM
/*
* suspend
*/
void snd_vx_suspend(vx_core_t *chip)
{
unsigned int i;
chip->chip_status |= VX_STAT_IN_SUSPEND;
for (i = 0; i < chip->hw->num_codecs; i++)
snd_pcm_suspend_all(chip->pcm[i]);
if (chip->hwdep)
chip->hwdep->dsp_loaded = 0;
}
/*
* resume
*/
void snd_vx_resume(vx_core_t *chip)
{
/* clear all stuff... */
chip->chip_status &= ~(VX_STAT_IN_SUSPEND|VX_STAT_CHIP_INIT);
}
#endif
/*
* module entries
*/
......@@ -804,13 +825,10 @@ module_exit(alsa_vx_core_exit)
*/
EXPORT_SYMBOL(snd_vx_check_reg_bit);
EXPORT_SYMBOL(snd_vx_create);
EXPORT_SYMBOL(snd_vx_hwdep_new);
EXPORT_SYMBOL(snd_vx_setup_firmware);
EXPORT_SYMBOL(snd_vx_free_firmware);
EXPORT_SYMBOL(snd_vx_irq_handler);
EXPORT_SYMBOL(snd_vx_delay);
EXPORT_SYMBOL(snd_vx_dsp_boot);
EXPORT_SYMBOL(snd_vx_dsp_load);
EXPORT_SYMBOL(snd_vx_load_boot_image);
#ifdef CONFIG_PM
EXPORT_SYMBOL(snd_vx_suspend);
EXPORT_SYMBOL(snd_vx_resume);
#endif
/*
* Driver for Digigram VX soundcards
*
* hwdep device manager
* DSP firmware management
*
* Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>
*
......@@ -21,10 +21,89 @@
*/
#include <sound/driver.h>
#include <linux/firmware.h>
#include <sound/core.h>
#include <sound/hwdep.h>
#include <sound/vx_core.h>
#ifdef SND_VX_FW_LOADER
int snd_vx_setup_firmware(vx_core_t *chip)
{
static char *fw_files[VX_TYPE_NUMS][4] = {
[VX_TYPE_BOARD] = {
NULL, "x1_1_vx2.xlx", "bd56002.boot", "l_1_vx2.d56",
},
[VX_TYPE_V2] = {
NULL, "x1_2_v22.xlx", "bd563v2.boot", "l_1_v22.d56",
},
[VX_TYPE_MIC] = {
NULL, "x1_2_v22.xlx", "bd563v2.boot", "l_1_v22.d56",
},
[VX_TYPE_VXPOCKET] = {
"bx_1_vxp.b56", "x1_1_vxp.xlx", "bd563s3.boot", "l_1_vxp.d56"
},
[VX_TYPE_VXP440] = {
"bx_1_vp4.b56", "x1_1_vp4.xlx", "bd563s3.boot", "l_1_vp4.d56"
},
};
int i, err;
for (i = 0; i < 4; i++) {
char path[32];
const struct firmware *fw;
if (! fw_files[chip->type][i])
continue;
sprintf(path, "vx/%s", fw_files[chip->type][i]);
if (request_firmware(&fw, path, chip->dev)) {
snd_printk(KERN_ERR "vx: can't load firmware %s\n", path);
return -ENOENT;
}
err = chip->ops->load_dsp(chip, i, fw);
if (err < 0) {
release_firmware(fw);
return err;
}
if (i == 1)
chip->chip_status |= VX_STAT_XILINX_LOADED;
#ifdef CONFIG_PM
chip->firmware[i] = fw;
#else
release_firmware(fw);
#endif
}
/* ok, we reached to the last one */
/* create the devices if not built yet */
if ((err = snd_vx_pcm_new(chip)) < 0)
return err;
if ((err = snd_vx_mixer_new(chip)) < 0)
return err;
if (chip->ops->add_controls)
if ((err = chip->ops->add_controls(chip)) < 0)
return err;
chip->chip_status |= VX_STAT_DEVICE_INIT;
chip->chip_status |= VX_STAT_CHIP_INIT;
return snd_card_register(chip->card);
}
/* exported */
void snd_vx_free_firmware(vx_core_t *chip)
{
#ifdef CONFIG_PM
int i;
for (i = 0; i < 4; i++)
release_firmware(chip->firmware[i]);
#endif
}
#else /* old style firmware loading */
static int vx_hwdep_open(snd_hwdep_t *hw, struct file *file)
{
return 0;
......@@ -58,19 +137,54 @@ static int vx_hwdep_dsp_status(snd_hwdep_t *hw, snd_hwdep_dsp_status_t *info)
return 0;
}
static void free_fw(struct firmware *fw)
{
if (fw) {
vfree(fw->data);
kfree(fw);
}
}
static int vx_hwdep_dsp_load(snd_hwdep_t *hw, snd_hwdep_dsp_image_t *dsp)
{
vx_core_t *vx = hw->private_data;
int index, err;
struct firmware *fw;
snd_assert(vx->ops->load_dsp, return -ENXIO);
err = vx->ops->load_dsp(vx, dsp);
if (err < 0)
return err;
fw = kmalloc(sizeof(*fw), GFP_KERNEL);
if (! fw) {
snd_printk(KERN_ERR "cannot allocate firmware\n");
return -ENOMEM;
}
fw->size = dsp->length;
fw->data = vmalloc(fw->size);
if (! fw->data) {
snd_printk(KERN_ERR "cannot allocate firmware image (length=%d)\n",
(int)fw->size);
kfree(fw);
return -ENOMEM;
}
if (copy_from_user(fw->data, dsp->image, dsp->length)) {
free_fw(fw);
return -EFAULT;
}
index = dsp->index;
if (! vx_is_pcmcia(vx))
index++;
err = vx->ops->load_dsp(vx, index, fw);
if (err < 0) {
free_fw(fw);
return err;
}
#ifdef CONFIG_PM
chip->firmware[index] = fw;
#else
free_fw(fw);
#endif
if (index == 1)
vx->chip_status |= VX_STAT_XILINX_LOADED;
if (index < 3)
......@@ -100,7 +214,7 @@ static int vx_hwdep_dsp_load(snd_hwdep_t *hw, snd_hwdep_dsp_image_t *dsp)
/* exported */
int snd_vx_hwdep_new(vx_core_t *chip)
int snd_vx_setup_firmware(vx_core_t *chip)
{
int err;
snd_hwdep_t *hw;
......@@ -118,5 +232,17 @@ int snd_vx_hwdep_new(vx_core_t *chip)
sprintf(hw->name, "VX Loader (%s)", chip->card->driver);
chip->hwdep = hw;
return 0;
return snd_card_register(chip->card);
}
/* exported */
void snd_vx_free_firmware(vx_core_t *chip)
{
#ifdef CONFIG_PM
int i;
for (i = 0; i < 4; i++)
free_fw(chip->firmware[i]);
#endif
}
#endif /* SND_VX_FW_LOADER */
......@@ -549,7 +549,8 @@ static int vx_stop_stream(vx_core_t *chip, vx_pipe_t *pipe)
static snd_pcm_hardware_t vx_pcm_playback_hw = {
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_MMAP_VALID),
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_RESUME),
.formats = /*SNDRV_PCM_FMTBIT_U8 |*/ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_3LE,
.rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
.rate_min = 5000,
......@@ -802,6 +803,7 @@ static int vx_pcm_trigger(snd_pcm_substream_t *subs, int cmd)
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
if (! pipe->is_capture)
vx_pcm_playback_transfer(chip, subs, pipe, 2);
/* FIXME:
......@@ -813,6 +815,7 @@ static int vx_pcm_trigger(snd_pcm_substream_t *subs, int cmd)
pipe->running = 1;
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
vx_toggle_pipe(chip, pipe, 0);
vx_stop_pipe(chip, pipe);
vx_stop_stream(chip, pipe);
......@@ -946,7 +949,8 @@ static snd_pcm_ops_t vx_pcm_playback_ops = {
static snd_pcm_hardware_t vx_pcm_capture_hw = {
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_MMAP_VALID),
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_RESUME),
.formats = /*SNDRV_PCM_FMTBIT_U8 |*/ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_3LE,
.rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
.rate_min = 5000,
......
......@@ -1019,13 +1019,6 @@ static int __devinit snd_mixart_create(mixart_mgr_t *mgr, snd_card_t *card, int
return err;
}
if (idx == 0) {
/* create a DSP loader only on first cardX*/
err = snd_mixart_hwdep_new(mgr);
if (err < 0)
return err;
}
snd_card_set_dev(card, &mgr->pci->dev);
return 0;
......@@ -1411,6 +1404,13 @@ static int __devinit snd_mixart_probe(struct pci_dev *pci,
/* init bufferinfo_array */
memset(mgr->bufferinfo.area, 0, size);
/* set up firmware */
err = snd_mixart_setup_firmware(mgr);
if (err < 0) {
snd_mixart_free(mgr);
return err;
}
pci_set_drvdata(pci, mgr);
dev++;
return 0;
......
/*
* Driver for Digigram miXart soundcards
*
* hwdep device manager
* DSP firmware management
*
* Copyright (c) 2003 by Digigram <alsa@digigram.com>
*
......@@ -22,6 +22,8 @@
#include <sound/driver.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/firmware.h>
#include <asm/io.h>
#include <sound/core.h>
#include "mixart.h"
......@@ -30,19 +32,6 @@
#include "mixart_hwdep.h"
/* miXart hwdep interface id string */
#define SND_MIXART_HWDEP_ID "miXart Loader"
static int mixart_hwdep_open(snd_hwdep_t *hw, struct file *file)
{
return 0;
}
static int mixart_hwdep_release(snd_hwdep_t *hw, struct file *file)
{
return 0;
}
/**
* wait for a value on a peudo register, exit with a timeout
*
......@@ -109,34 +98,30 @@ struct snd_mixart_elf32_phdr {
u32 p_align;
};
static int mixart_load_elf(mixart_mgr_t *mgr, snd_hwdep_dsp_image_t *dsp )
static int mixart_load_elf(mixart_mgr_t *mgr, const struct firmware *dsp )
{
char elf32_magic_number[4] = {0x7f,'E','L','F'};
snd_mixart_elf32_ehdr_t elf_header;
snd_mixart_elf32_ehdr_t *elf_header;
int i;
if ( copy_from_user(&elf_header, dsp->image , sizeof(snd_mixart_elf32_ehdr_t)) )
return -EFAULT;
elf_header = (snd_mixart_elf32_ehdr_t *)dsp->data;
for( i=0; i<4; i++ )
if ( elf32_magic_number[i] != elf_header.e_ident[i] )
if ( elf32_magic_number[i] != elf_header->e_ident[i] )
return -EINVAL;
if( elf_header.e_phoff != 0 ) {
if( elf_header->e_phoff != 0 ) {
snd_mixart_elf32_phdr_t elf_programheader;
for( i=0; i < be16_to_cpu(elf_header.e_phnum); i++ ) {
u32 pos = be32_to_cpu(elf_header.e_phoff) + (u32)(i * be16_to_cpu(elf_header.e_phentsize));
for( i=0; i < be16_to_cpu(elf_header->e_phnum); i++ ) {
u32 pos = be32_to_cpu(elf_header->e_phoff) + (u32)(i * be16_to_cpu(elf_header->e_phentsize));
if( copy_from_user( &elf_programheader, dsp->image + pos, sizeof(elf_programheader) ) )
return -EFAULT;
memcpy( &elf_programheader, dsp->data + pos, sizeof(elf_programheader) );
if(elf_programheader.p_type != 0) {
if( elf_programheader.p_filesz != 0 ) {
if(copy_from_user_toio( MIXART_MEM( mgr, be32_to_cpu(elf_programheader.p_vaddr)),
dsp->image + be32_to_cpu( elf_programheader.p_offset ),
be32_to_cpu( elf_programheader.p_filesz )))
return -EFAULT;
memcpy_toio( MIXART_MEM( mgr, be32_to_cpu(elf_programheader.p_vaddr)),
dsp->data + be32_to_cpu( elf_programheader.p_offset ),
be32_to_cpu( elf_programheader.p_filesz ));
}
}
}
......@@ -144,20 +129,6 @@ static int mixart_load_elf(mixart_mgr_t *mgr, snd_hwdep_dsp_image_t *dsp )
return 0;
}
static int mixart_hwdep_dsp_status(snd_hwdep_t *hw, snd_hwdep_dsp_status_t *info)
{
mixart_mgr_t *mgr = hw->private_data;
strcpy(info->id, "miXart");
info->num_dsps = MIXART_HARDW_FILES_MAX_INDEX;
if (mgr->hwdep->dsp_loaded & (1 << MIXART_MOTHERBOARD_ELF_INDEX))
info->chip_ready = 1;
info->version = MIXART_DRIVER_VERSION;
return 0;
}
/*
* get basic information and init miXart
*/
......@@ -344,9 +315,8 @@ static int mixart_first_init(mixart_mgr_t *mgr)
/* firmware base addresses (when hard coded) */
#define MIXART_MOTHERBOARD_XLX_BASE_ADDRESS 0x00600000
static int mixart_hwdep_dsp_load(snd_hwdep_t *hw, snd_hwdep_dsp_image_t *dsp)
static int mixart_dsp_load(mixart_mgr_t* mgr, int index, const struct firmware *dsp)
{
mixart_mgr_t* mgr = hw->private_data;
int err, card_index;
u32 status_xilinx, status_elf, status_daught;
u32 val;
......@@ -364,7 +334,7 @@ static int mixart_hwdep_dsp_load(snd_hwdep_t *hw, snd_hwdep_dsp_image_t *dsp)
return -EAGAIN; /* try again later */
}
switch (dsp->index) {
switch (index) {
case MIXART_MOTHERBOARD_XLX_INDEX:
/* xilinx already loaded ? */
......@@ -379,8 +349,8 @@ static int mixart_hwdep_dsp_load(snd_hwdep_t *hw, snd_hwdep_dsp_image_t *dsp)
}
/* check xilinx validity */
snd_assert(((u32*)(dsp->image))[0]==0xFFFFFFFF, return -EINVAL);
snd_assert(dsp->length % 4 == 0, return -EINVAL);
snd_assert(((u32*)(dsp->data))[0]==0xFFFFFFFF, return -EINVAL);
snd_assert(dsp->size % 4 == 0, return -EINVAL);
/* set xilinx status to copying */
writel_be( 1, MIXART_MEM( mgr, MIXART_PSEUDOREG_MXLX_STATUS_OFFSET ));
......@@ -388,11 +358,10 @@ static int mixart_hwdep_dsp_load(snd_hwdep_t *hw, snd_hwdep_dsp_image_t *dsp)
/* setup xilinx base address */
writel_be( MIXART_MOTHERBOARD_XLX_BASE_ADDRESS, MIXART_MEM( mgr,MIXART_PSEUDOREG_MXLX_BASE_ADDR_OFFSET ));
/* setup code size for xilinx file */
writel_be( dsp->length, MIXART_MEM( mgr, MIXART_PSEUDOREG_MXLX_SIZE_OFFSET ));
writel_be( dsp->size, MIXART_MEM( mgr, MIXART_PSEUDOREG_MXLX_SIZE_OFFSET ));
/* copy xilinx code */
if (copy_from_user_toio( MIXART_MEM( mgr, MIXART_MOTHERBOARD_XLX_BASE_ADDRESS), dsp->image, dsp->length))
return -EFAULT;
memcpy_toio( MIXART_MEM( mgr, MIXART_MOTHERBOARD_XLX_BASE_ADDRESS), dsp->data, dsp->size);
/* set xilinx status to copy finished */
writel_be( 2, MIXART_MEM( mgr, MIXART_PSEUDOREG_MXLX_STATUS_OFFSET ));
......@@ -428,7 +397,7 @@ static int mixart_hwdep_dsp_load(snd_hwdep_t *hw, snd_hwdep_dsp_image_t *dsp)
writel_be( 1, MIXART_MEM( mgr, MIXART_PSEUDOREG_ELF_STATUS_OFFSET ));
/* process the copying of the elf packets */
err = mixart_load_elf( mgr, dsp);
err = mixart_load_elf( mgr, dsp );
if (err < 0) return err;
/* set elf status to copy finished */
......@@ -479,11 +448,11 @@ static int mixart_hwdep_dsp_load(snd_hwdep_t *hw, snd_hwdep_dsp_image_t *dsp)
}
/* check daughterboard xilinx validity */
snd_assert(((u32*)(dsp->image))[0]==0xFFFFFFFF, return -EINVAL);
snd_assert(dsp->length % 4 == 0, return -EINVAL);
snd_assert(((u32*)(dsp->data))[0]==0xFFFFFFFF, return -EINVAL);
snd_assert(dsp->size % 4 == 0, return -EINVAL);
/* inform mixart about the size of the file */
writel_be( dsp->length, MIXART_MEM( mgr, MIXART_PSEUDOREG_DXLX_SIZE_OFFSET ));
writel_be( dsp->size, MIXART_MEM( mgr, MIXART_PSEUDOREG_DXLX_SIZE_OFFSET ));
/* set daughterboard status to 1 */
writel_be( 1, MIXART_MEM( mgr, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET ));
......@@ -500,8 +469,7 @@ static int mixart_hwdep_dsp_load(snd_hwdep_t *hw, snd_hwdep_dsp_image_t *dsp)
snd_assert(val != 0, return -EINVAL);
/* copy daughterboard xilinx code */
if (copy_from_user_toio( MIXART_MEM( mgr, val), dsp->image, dsp->length))
return -EFAULT;
memcpy_toio( MIXART_MEM( mgr, val), dsp->data, dsp->size);
/* set daughterboard status to 4 */
writel_be( 4, MIXART_MEM( mgr, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET ));
......@@ -549,7 +517,94 @@ static int mixart_hwdep_dsp_load(snd_hwdep_t *hw, snd_hwdep_dsp_image_t *dsp)
}
int snd_mixart_hwdep_new(mixart_mgr_t *mgr)
#if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE)
#if !defined(CONFIG_USE_MIXARTLOADER) && !defined(CONFIG_SND_MIXART) /* built-in kernel */
#define SND_MIXART_FW_LOADER /* use the standard firmware loader */
#endif
#endif
#ifdef SND_MIXART_FW_LOADER
#include <linux/firmware.h>
int snd_mixart_setup_firmware(mixart_mgr_t *mgr)
{
static char *fw_files[3] = {
"miXart8.xlx", "miXart8.elf", "miXart8AES.xlx"
};
char path[32];
const struct firmware *fw_entry;
int i, err;
for (i = 0; i < 3; i++) {
sprintf(path, "mixart/%s", fw_files[i]);
if (request_firmware(&fw_entry, path, &mgr->pci->dev)) {
snd_printk(KERN_ERR "miXart: can't load firmware %s\n", path);
return -ENOENT;
}
/* fake hwdep dsp record */
err = mixart_dsp_load(mgr, i, fw_entry);
release_firmware(fw_entry);
if (err < 0)
return err;
}
return 0;
}
#else /* old style firmware loading */
/* miXart hwdep interface id string */
#define SND_MIXART_HWDEP_ID "miXart Loader"
static int mixart_hwdep_open(snd_hwdep_t *hw, struct file *file)
{
return 0;
}
static int mixart_hwdep_release(snd_hwdep_t *hw, struct file *file)
{
return 0;
}
static int mixart_hwdep_dsp_status(snd_hwdep_t *hw, snd_hwdep_dsp_status_t *info)
{
mixart_mgr_t *mgr = hw->private_data;
strcpy(info->id, "miXart");
info->num_dsps = MIXART_HARDW_FILES_MAX_INDEX;
if (mgr->hwdep->dsp_loaded & (1 << MIXART_MOTHERBOARD_ELF_INDEX))
info->chip_ready = 1;
info->version = MIXART_DRIVER_VERSION;
return 0;
}
static int mixart_hwdep_dsp_load(snd_hwdep_t *hw, snd_hwdep_dsp_image_t *dsp)
{
mixart_mgr_t* mgr = hw->private_data;
struct firmware fw;
int err;
fw->size = dsp->length;
fw->data = vmalloc(dsp->length);
if (! fw->data) {
snd_printk(KERN_ERR "miXart: cannot allocate image size %d\n",
(int)dsp->length);
return -ENOMEM;
}
if (copy_from_user(fw->data, dsp->image, dsp->length)) {
vfree(fw->data);
return -EFAULT;
}
err = mixart_dsp_load(mgr, dsp->index, &fw);
vfree(fw->data);
return err;
}
int snd_mixart_setup_firmware(mixart_mgr_t *mgr)
{
int err;
snd_hwdep_t *hw;
......@@ -568,5 +623,8 @@ int snd_mixart_hwdep_new(mixart_mgr_t *mgr)
sprintf(hw->name, SND_MIXART_HWDEP_ID);
mgr->hwdep = hw;
mgr->hwdep->dsp_loaded = 0;
return 0;
return snd_card_register(mgr->chip[0]->card);
}
#endif /* SND_MIXART_FW_LOADER */
......@@ -140,7 +140,6 @@
#define MIXART_OIDI 0x008 /* 0000 0000 1000 */
/* exported */
int snd_mixart_hwdep_new(mixart_mgr_t *mgr);
int snd_mixart_setup_firmware(mixart_mgr_t *mgr);
#endif /* __SOUND_MIXART_HWDEP_H */
......@@ -225,7 +225,11 @@ static int __devinit snd_vx222_probe(struct pci_dev *pci,
snd_printdd("%s at 0x%lx & 0x%lx, irq %i\n",
card->shortname, vx->port[0], vx->port[1], vx->core.irq);
if ((err = snd_vx_hwdep_new(&vx->core)) < 0) {
#ifdef SND_VX_FW_LOADER
vx->core.dev = &pci->dev;
#endif
if ((err = snd_vx_setup_firmware(&vx->core)) < 0) {
snd_card_free(card);
return err;
}
......@@ -251,6 +255,7 @@ static struct pci_driver driver = {
.id_table = snd_vx222_ids,
.probe = snd_vx222_probe,
.remove = __devexit_p(snd_vx222_remove),
SND_PCI_PM_CALLBACKS
};
static int __init alsa_card_vx222_init(void)
......
......@@ -22,6 +22,7 @@
#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/firmware.h>
#include <sound/core.h>
#include <sound/control.h>
#include <asm/io.h>
......@@ -351,12 +352,11 @@ static int put_xilinx_data(vx_core_t *chip, unsigned int port, unsigned int coun
/*
* load the xilinx image
*/
static int vx2_load_xilinx_binary(vx_core_t *chip, const snd_hwdep_dsp_image_t *xilinx)
static int vx2_load_xilinx_binary(vx_core_t *chip, const struct firmware *xilinx)
{
unsigned int i;
unsigned int port;
unsigned char data;
unsigned char __user *image;
unsigned char *image;
/* XILINX reset (wait at least 1 milisecond between reset on and off). */
vx_outl(chip, CNTRL, VX_CNTRL_REGISTER_VALUE | VX_XILINX_RESET_MASK);
......@@ -371,10 +371,9 @@ static int vx2_load_xilinx_binary(vx_core_t *chip, const snd_hwdep_dsp_image_t *
else
port = VX_GPIOC; /* VX222 V2 and VX222_MIC_BOARD with new PLX9030 use this register */
image = xilinx->image;
for (i = 0; i < xilinx->length; i++, image++) {
__get_user(data, image);
if (put_xilinx_data(chip, port, 8, data) < 0)
image = xilinx->data;
for (i = 0; i < xilinx->size; i++, image++) {
if (put_xilinx_data(chip, port, 8, *image) < 0)
return -EINVAL;
/* don't take too much time in this loop... */
cond_resched();
......@@ -400,25 +399,22 @@ static int vx2_load_xilinx_binary(vx_core_t *chip, const snd_hwdep_dsp_image_t *
/*
* load the boot/dsp images
*/
static int vx2_load_dsp(vx_core_t *vx, const snd_hwdep_dsp_image_t *dsp)
static int vx2_load_dsp(vx_core_t *vx, int index, const struct firmware *dsp)
{
int err;
if (*dsp->name)
snd_printdd("loading dsp [%d] %s, size = %Zd\n",
dsp->index, dsp->name, dsp->length);
switch (dsp->index) {
case 0:
switch (index) {
case 1:
/* xilinx image */
if ((err = vx2_load_xilinx_binary(vx, dsp)) < 0)
return err;
if ((err = vx2_test_xilinx(vx)) < 0)
return err;
return 0;
case 1:
case 2:
/* DSP boot */
return snd_vx_dsp_boot(vx, dsp);
case 2:
case 3:
/* DSP image */
return snd_vx_dsp_load(vx, dsp);
default:
......
......@@ -68,7 +68,10 @@ static int snd_vxpocket_free(vx_core_t *chip)
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;
}
......@@ -120,6 +123,19 @@ dev_link_t *snd_vxpocket_attach(struct snd_vxp_entry *hw)
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);
......@@ -156,11 +172,8 @@ dev_link_t *snd_vxpocket_attach(struct snd_vxp_entry *hw)
link->conf.ConfigIndex = 1;
link->conf.Present = PRESENT_OPTION;
/* Chain drivers */
link->next = hw->dev_list;
hw->dev_list = link;
/* Register with Card Services */
memset(&client_reg, 0, sizeof(client_reg));
client_reg.dev_info = hw->dev_info;
client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
client_reg.EventMask =
......@@ -177,10 +190,16 @@ dev_link_t *snd_vxpocket_attach(struct snd_vxp_entry *hw)
ret = pcmcia_register_client(&link->handle, &client_reg);
if (ret != CS_SUCCESS) {
cs_error(link->handle, RegisterClient, ret);
snd_vxpocket_detach(hw, link);
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;
}
......@@ -208,12 +227,9 @@ static int snd_vxpocket_assign_resources(vx_core_t *chip, int port, int irq)
sprintf(card->longname, "%s at 0x%x, irq %i",
card->shortname, port, irq);
if ((err = snd_vx_hwdep_new(chip)) < 0)
return err;
chip->irq = irq;
if ((err = snd_card_register(chip->card)) < 0)
if ((err = snd_vx_setup_firmware(chip)) < 0)
return err;
return 0;
......@@ -226,7 +242,12 @@ static int snd_vxpocket_assign_resources(vx_core_t *chip, int port, int irq)
*/
void snd_vxpocket_detach(struct snd_vxp_entry *hw, dev_link_t *link)
{
vx_core_t *chip = link->priv;
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 */
......@@ -234,10 +255,10 @@ void snd_vxpocket_detach(struct snd_vxp_entry *hw, dev_link_t *link)
dev_link_t **linkp;
/* Locate device structure */
for (linkp = &hw->dev_list; *linkp; linkp = &(*linkp)->next)
if (*linkp == link)
break;
if (*linkp)
if (*linkp == link) {
*linkp = link->next;
break;
}
}
chip->chip_status |= VX_STAT_IS_STALE; /* to be sure */
snd_card_disconnect(chip->card);
......@@ -266,13 +287,16 @@ static void vxpocket_config(dev_link_t *link)
vx_core_t *chip = link->priv;
struct snd_vxpocket *vxp = (struct snd_vxpocket *)chip;
tuple_t tuple;
cisparse_t parse;
config_info_t conf;
cisparse_t *parse = NULL;
u_short buf[32];
int last_fn, last_ret;
snd_printdd(KERN_DEBUG "vxpocket_config called\n");
tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
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);
......@@ -280,12 +304,9 @@ static void vxpocket_config(dev_link_t *link)
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.ConfigIndex = 1;
CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(handle, &conf));
link->conf.Vcc = conf.Vcc;
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;
......@@ -299,6 +320,7 @@ static void vxpocket_config(dev_link_t *link)
link->dev = &vxp->node;
link->state &= ~DEV_CONFIG_PENDING;
kfree(parse);
return;
cs_failed:
......@@ -307,6 +329,8 @@ static void vxpocket_config(dev_link_t *link)
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);
}
......@@ -328,16 +352,16 @@ static int vxpocket_event(event_t event, int priority, event_callback_args_t *ar
break;
case CS_EVENT_CARD_INSERTION:
snd_printdd(KERN_DEBUG "CARD_INSERTION..\n");
link->state |= DEV_PRESENT;
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) {
if (chip && chip->card->pm_suspend) {
snd_printdd(KERN_DEBUG "snd_vx_suspend calling\n");
snd_vx_suspend(chip);
chip->card->pm_suspend(chip->card, 0);
}
/* Fall through... */
case CS_EVENT_RESET_PHYSICAL:
......@@ -355,9 +379,9 @@ static int vxpocket_event(event_t event, int priority, event_callback_args_t *ar
//struct snd_vxpocket *vxp = (struct snd_vxpocket *)chip;
snd_printdd(KERN_DEBUG "requestconfig...\n");
pcmcia_request_configuration(link->handle, &link->conf);
if (chip) {
if (chip && chip->card->pm_resume) {
snd_printdd(KERN_DEBUG "calling snd_vx_resume\n");
snd_vx_resume(chip);
chip->card->pm_resume(chip->card, 0);
}
}
snd_printdd(KERN_DEBUG "resume done!\n");
......
......@@ -22,6 +22,7 @@
#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/firmware.h>
#include <sound/core.h>
#include <asm/io.h>
#include "vxpocket.h"
......@@ -144,13 +145,13 @@ static void vxp_reset_codec(vx_core_t *_chip)
* 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 snd_hwdep_dsp_image_t *xilinx)
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 __user *image;
unsigned char *image;
unsigned char data;
/* Switch to programmation mode */
......@@ -171,9 +172,9 @@ static int vxp_load_xilinx_binary(vx_core_t *_chip, const snd_hwdep_dsp_image_t
/* set HF1 for loading xilinx binary */
vx_outb(chip, ICR, ICR_HF1);
image = xilinx->image;
for (i = 0; i < xilinx->length; i++, image++) {
__get_user(data, image);
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);
......@@ -200,7 +201,7 @@ static int vxp_load_xilinx_binary(vx_core_t *_chip, const snd_hwdep_dsp_image_t
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, xilinx->length);
snd_printdd(KERN_DEBUG "xilinx: dsp size received 0x%x, orig 0x%x\n", c, fw->size);
vx_outb(chip, ICR, ICR_HF0);
......@@ -242,30 +243,27 @@ static int vxp_load_xilinx_binary(vx_core_t *_chip, const snd_hwdep_dsp_image_t
/*
* vxp_load_dsp - load_dsp callback
*/
static int vxp_load_dsp(vx_core_t *vx, const snd_hwdep_dsp_image_t *dsp)
static int vxp_load_dsp(vx_core_t *vx, int index, const struct firmware *fw)
{
int err;
if (*dsp->name)
snd_printdd("loading dsp [%d] %s, size = %d\n", dsp->index, dsp->name, dsp->length);
switch (dsp->index) {
switch (index) {
case 0:
/* xilinx boot */
if ((err = vx_check_magic(vx)) < 0)
return err;
if ((err = snd_vx_load_boot_image(vx, dsp)) < 0)
if ((err = snd_vx_load_boot_image(vx, fw)) < 0)
return err;
return 0;
case 1:
/* xilinx image */
return vxp_load_xilinx_binary(vx, dsp);
return vxp_load_xilinx_binary(vx, fw);
case 2:
/* DSP boot */
return snd_vx_dsp_boot(vx, dsp);
return snd_vx_dsp_boot(vx, fw);
case 3:
/* DSP image */
return snd_vx_dsp_load(vx, dsp);
return snd_vx_dsp_load(vx, fw);
default:
snd_BUG();
return -EINVAL;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment