Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6bcdbd55 authored by Takashi Iwai's avatar Takashi Iwai
Browse files

Merge branch 'topic/ca0106-resume' into topic/ca0106

parents 6a843641 72077aa3
Loading
Loading
Loading
Loading
+14 −1
Original line number Diff line number Diff line
@@ -690,7 +690,7 @@ struct snd_ca0106 {
	spinlock_t emu_lock;

	struct snd_ac97 *ac97;
	struct snd_pcm *pcm;
	struct snd_pcm *pcm[4];

	struct snd_ca0106_channel playback_channels[4];
	struct snd_ca0106_channel capture_channels[4];
@@ -707,6 +707,11 @@ struct snd_ca0106 {
	struct snd_ca_midi midi2;

	u16 spi_dac_reg[16];

#ifdef CONFIG_PM
#define NUM_SAVED_VOLUMES	9
	unsigned int saved_vol[NUM_SAVED_VOLUMES];
#endif
};

int snd_ca0106_mixer(struct snd_ca0106 *emu);
@@ -725,3 +730,11 @@ int snd_ca0106_i2c_write(struct snd_ca0106 *emu, u32 reg, u32 value);

int snd_ca0106_spi_write(struct snd_ca0106 * emu,
				   unsigned int data);

#ifdef CONFIG_PM
void snd_ca0106_mixer_suspend(struct snd_ca0106 *chip);
void snd_ca0106_mixer_resume(struct snd_ca0106 *chip);
#else
#define snd_ca0106_mixer_suspend(chip)	do { } while (0)
#define snd_ca0106_mixer_resume(chip)	do { } while (0)
#endif
+312 −216
Original line number Diff line number Diff line
@@ -853,13 +853,16 @@ static int snd_ca0106_pcm_trigger_playback(struct snd_pcm_substream *substream,
        struct snd_pcm_substream *s;
	u32 basic = 0;
	u32 extended = 0;
	u32 bits;
	int running = 0;

	switch (cmd) {
	case SNDRV_PCM_TRIGGER_START:
	case SNDRV_PCM_TRIGGER_RESUME:
		running = 1;
		break;
	case SNDRV_PCM_TRIGGER_STOP:
	case SNDRV_PCM_TRIGGER_SUSPEND:
	default:
		running = 0;
		break;
@@ -871,22 +874,32 @@ static int snd_ca0106_pcm_trigger_playback(struct snd_pcm_substream *substream,
		runtime = s->runtime;
		epcm = runtime->private_data;
		channel = epcm->channel_id;
		//snd_printk("channel=%d\n",channel);
		/* snd_printk("channel=%d\n",channel); */
		epcm->running = running;
		basic |= (0x1 << channel);
		extended |= (0x10 << channel);
                snd_pcm_trigger_done(s, substream);
        }
	//snd_printk("basic=0x%x, extended=0x%x\n",basic, extended);
	/* snd_printk("basic=0x%x, extended=0x%x\n",basic, extended); */

	switch (cmd) {
	case SNDRV_PCM_TRIGGER_START:
		snd_ca0106_ptr_write(emu, EXTENDED_INT_MASK, 0, snd_ca0106_ptr_read(emu, EXTENDED_INT_MASK, 0) | (extended));
		snd_ca0106_ptr_write(emu, BASIC_INTERRUPT, 0, snd_ca0106_ptr_read(emu, BASIC_INTERRUPT, 0)|(basic));
	case SNDRV_PCM_TRIGGER_RESUME:
		bits = snd_ca0106_ptr_read(emu, EXTENDED_INT_MASK, 0);
		bits |= extended;
		snd_ca0106_ptr_write(emu, EXTENDED_INT_MASK, 0, bits);
		bits = snd_ca0106_ptr_read(emu, BASIC_INTERRUPT, 0);
		bits |= basic;
		snd_ca0106_ptr_write(emu, BASIC_INTERRUPT, 0, bits);
		break;
	case SNDRV_PCM_TRIGGER_STOP:
		snd_ca0106_ptr_write(emu, BASIC_INTERRUPT, 0, snd_ca0106_ptr_read(emu, BASIC_INTERRUPT, 0) & ~(basic));
		snd_ca0106_ptr_write(emu, EXTENDED_INT_MASK, 0, snd_ca0106_ptr_read(emu, EXTENDED_INT_MASK, 0) & ~(extended));
	case SNDRV_PCM_TRIGGER_SUSPEND:
		bits = snd_ca0106_ptr_read(emu, BASIC_INTERRUPT, 0);
		bits &= ~basic;
		snd_ca0106_ptr_write(emu, BASIC_INTERRUPT, 0, bits);
		bits = snd_ca0106_ptr_read(emu, EXTENDED_INT_MASK, 0);
		bits &= ~extended;
		snd_ca0106_ptr_write(emu, EXTENDED_INT_MASK, 0, bits);
		break;
	default:
		result = -EINVAL;
@@ -1109,21 +1122,13 @@ static int snd_ca0106_ac97(struct snd_ca0106 *chip)
	return snd_ac97_mixer(pbus, &ac97, &chip->ac97);
}

static void ca0106_stop_chip(struct snd_ca0106 *chip);

static int snd_ca0106_free(struct snd_ca0106 *chip)
{
	if (chip->res_port != NULL) {    /* avoid access to already used hardware */
		// disable interrupts
		snd_ca0106_ptr_write(chip, BASIC_INTERRUPT, 0, 0);
		outl(0, chip->port + INTE);
		snd_ca0106_ptr_write(chip, EXTENDED_INT_MASK, 0, 0);
		udelay(1000);
		// disable audio
		//outl(HCFG_LOCKSOUNDCACHE, chip->port + HCFG);
		outl(0, chip->port + HCFG);
		/* FIXME: We need to stop and DMA transfers here.
		 *        But as I am not sure how yet, we cannot from the dma pages.
		 * So we can fix: snd-malloc: Memory leak?  pages not freed = 8
		 */
	if (chip->res_port != NULL) {
		/* avoid access to already used hardware */
		ca0106_stop_chip(chip);
	}
	if (chip->irq >= 0)
		free_irq(chip->irq, chip);
@@ -1209,15 +1214,14 @@ static irqreturn_t snd_ca0106_interrupt(int irq, void *dev_id)
	return IRQ_HANDLED;
}

static int __devinit snd_ca0106_pcm(struct snd_ca0106 *emu, int device, struct snd_pcm **rpcm)
static int __devinit snd_ca0106_pcm(struct snd_ca0106 *emu, int device)
{
	struct snd_pcm *pcm;
	struct snd_pcm_substream *substream;
	int err;
  
	if (rpcm)
		*rpcm = NULL;
	if ((err = snd_pcm_new(emu->card, "ca0106", device, 1, 1, &pcm)) < 0)
	err = snd_pcm_new(emu->card, "ca0106", device, 1, 1, &pcm);
	if (err < 0)
		return err;
  
	pcm->private_data = emu;
@@ -1244,7 +1248,6 @@ static int __devinit snd_ca0106_pcm(struct snd_ca0106 *emu, int device, struct s
	pcm->info_flags = 0;
	pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
	strcpy(pcm->name, "CA0106");
	emu->pcm = pcm;

	for(substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; 
	    substream; 
@@ -1266,8 +1269,7 @@ static int __devinit snd_ca0106_pcm(struct snd_ca0106 *emu, int device, struct s
			return err;
	}
  
	if (rpcm)
		*rpcm = pcm;
	emu->pcm[device] = pcm;
  
	return 0;
}
@@ -1307,89 +1309,10 @@ static unsigned int i2c_adc_init[][2] = {
	{ 0x15, ADC_MUX_LINEIN },  /* ADC Mixer control */
};

static int __devinit snd_ca0106_create(int dev, struct snd_card *card,
					 struct pci_dev *pci,
					 struct snd_ca0106 **rchip)
static void ca0106_init_chip(struct snd_ca0106 *chip, int resume)
{
	struct snd_ca0106 *chip;
	struct snd_ca0106_details *c;
	int err;
	int ch;
	static struct snd_device_ops ops = {
		.dev_free = snd_ca0106_dev_free,
	};
  
	*rchip = NULL;
  
	if ((err = pci_enable_device(pci)) < 0)
		return err;
	if (pci_set_dma_mask(pci, DMA_32BIT_MASK) < 0 ||
	    pci_set_consistent_dma_mask(pci, DMA_32BIT_MASK) < 0) {
		printk(KERN_ERR "error to set 32bit mask DMA\n");
		pci_disable_device(pci);
		return -ENXIO;
	}
  
	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
	if (chip == NULL) {
		pci_disable_device(pci);
		return -ENOMEM;
	}
  
	chip->card = card;
	chip->pci = pci;
	chip->irq = -1;

	spin_lock_init(&chip->emu_lock);
  
	chip->port = pci_resource_start(pci, 0);
	if ((chip->res_port = request_region(chip->port, 0x20,
					     "snd_ca0106")) == NULL) { 
		snd_ca0106_free(chip);
		printk(KERN_ERR "cannot allocate the port\n");
		return -EBUSY;
	}

	if (request_irq(pci->irq, snd_ca0106_interrupt,
			IRQF_SHARED, "snd_ca0106", chip)) {
		snd_ca0106_free(chip);
		printk(KERN_ERR "cannot grab irq\n");
		return -EBUSY;
	}
	chip->irq = pci->irq;
  
 	/* This stores the periods table. */ 
	if(snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci), 1024, &chip->buffer) < 0) {
		snd_ca0106_free(chip);
		return -ENOMEM;
	}

	pci_set_master(pci);
	/* read serial */
	pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &chip->serial);
	pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &chip->model);
#if 1
	printk(KERN_INFO "snd-ca0106: Model %04x Rev %08x Serial %08x\n", chip->model,
	       pci->revision, chip->serial);
#endif
	strcpy(card->driver, "CA0106");
	strcpy(card->shortname, "CA0106");

	for (c = ca0106_chip_details; c->serial; c++) {
		if (subsystem[dev]) {
			if (c->serial == subsystem[dev])
				break;
		} else if (c->serial == chip->serial)
			break;
	}
	chip->details = c;
	if (subsystem[dev]) {
		printk(KERN_INFO "snd-ca0106: Sound card name=%s, subsystem=0x%x. Forced to subsystem=0x%x\n",
                        c->name, chip->serial, subsystem[dev]);
	}

	sprintf(card->longname, "%s at 0x%lx irq %i",
		c->name, chip->port, chip->irq);
	unsigned int def_bits;

	outl(0, chip->port + INTE);

@@ -1407,31 +1330,22 @@ static int __devinit snd_ca0106_create(int dev, struct snd_card *card,
	 *  AN                = 0     (Audio data)
	 *  P                 = 0     (Consumer)
	 */
	snd_ca0106_ptr_write(chip, SPCS0, 0,
				chip->spdif_bits[0] =
	def_bits =
		SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
		SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
		SPCS_GENERATIONSTATUS | 0x00001200 |
				0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
		0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT;
	if (!resume) {
		chip->spdif_bits[0] = def_bits;
		chip->spdif_bits[1] = def_bits;
		chip->spdif_bits[2] = def_bits;
		chip->spdif_bits[3] = def_bits;
	}
	/* Only SPCS1 has been tested */
	snd_ca0106_ptr_write(chip, SPCS1, 0,
				chip->spdif_bits[1] =
				SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
				SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
				SPCS_GENERATIONSTATUS | 0x00001200 |
				0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
	snd_ca0106_ptr_write(chip, SPCS2, 0,
				chip->spdif_bits[2] =
				SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
				SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
				SPCS_GENERATIONSTATUS | 0x00001200 |
				0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
	snd_ca0106_ptr_write(chip, SPCS3, 0,
				chip->spdif_bits[3] =
				SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
				SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
				SPCS_GENERATIONSTATUS | 0x00001200 |
				0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
	snd_ca0106_ptr_write(chip, SPCS1, 0, chip->spdif_bits[1]);
	snd_ca0106_ptr_write(chip, SPCS0, 0, chip->spdif_bits[0]);
	snd_ca0106_ptr_write(chip, SPCS2, 0, chip->spdif_bits[2]);
	snd_ca0106_ptr_write(chip, SPCS3, 0, chip->spdif_bits[3]);

        snd_ca0106_ptr_write(chip, PLAYBACK_MUTE, 0, 0x00fc0000);
        snd_ca0106_ptr_write(chip, CAPTURE_MUTE, 0, 0x00fc0000);
@@ -1439,92 +1353,124 @@ static int __devinit snd_ca0106_create(int dev, struct snd_card *card,
        /* Write 0x8000 to AC97_REC_GAIN to mute it. */
        outb(AC97_REC_GAIN, chip->port + AC97ADDRESS);
        outw(0x8000, chip->port + AC97DATA);
#if 0
#if 0 /* FIXME: what are these? */
	snd_ca0106_ptr_write(chip, SPCS0, 0, 0x2108006);
	snd_ca0106_ptr_write(chip, 0x42, 0, 0x2108006);
	snd_ca0106_ptr_write(chip, 0x43, 0, 0x2108006);
	snd_ca0106_ptr_write(chip, 0x44, 0, 0x2108006);
#endif

	//snd_ca0106_ptr_write(chip, SPDIF_SELECT2, 0, 0xf0f003f); /* OSS drivers set this. */
	/* OSS drivers set this. */
	/* snd_ca0106_ptr_write(chip, SPDIF_SELECT2, 0, 0xf0f003f); */

	/* Analog or Digital output */
	snd_ca0106_ptr_write(chip, SPDIF_SELECT1, 0, 0xf);
	snd_ca0106_ptr_write(chip, SPDIF_SELECT2, 0, 0x000f0000); /* 0x0b000000 for digital, 0x000b0000 for analog, from win2000 drivers. Use 0x000f0000 for surround71 */
	/* 0x0b000000 for digital, 0x000b0000 for analog, from win2000 drivers.
	 * Use 0x000f0000 for surround71
	 */
	snd_ca0106_ptr_write(chip, SPDIF_SELECT2, 0, 0x000f0000);

	chip->spdif_enable = 0; /* Set digital SPDIF output off */
	//snd_ca0106_ptr_write(chip, 0x45, 0, 0); /* Analogue out */
	//snd_ca0106_ptr_write(chip, 0x45, 0, 0xf00); /* Digital out */
	/*snd_ca0106_ptr_write(chip, 0x45, 0, 0);*/ /* Analogue out */
	/*snd_ca0106_ptr_write(chip, 0x45, 0, 0xf00);*/ /* Digital out */

	/* goes to 0x40c80000 when doing SPDIF IN/OUT */
	snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 0, 0x40c81000);
	/* (Mute) CAPTURE feedback into PLAYBACK volume.
	 * Only lower 16 bits matter.
	 */
	snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 1, 0xffffffff);
	/* SPDIF IN Volume */
	snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 2, 0x30300000);
	/* SPDIF IN Volume, 0x70 = (vol & 0x3f) | 0x40 */
	snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 3, 0x00700000);

	snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 0, 0x40c81000); /* goes to 0x40c80000 when doing SPDIF IN/OUT */
	snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 1, 0xffffffff); /* (Mute) CAPTURE feedback into PLAYBACK volume. Only lower 16 bits matter. */
	snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 2, 0x30300000); /* SPDIF IN Volume */
	snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 3, 0x00700000); /* SPDIF IN Volume, 0x70 = (vol & 0x3f) | 0x40 */
	snd_ca0106_ptr_write(chip, PLAYBACK_ROUTING1, 0, 0x32765410);
	snd_ca0106_ptr_write(chip, PLAYBACK_ROUTING2, 0, 0x76767676);
	snd_ca0106_ptr_write(chip, CAPTURE_ROUTING1, 0, 0x32765410);
	snd_ca0106_ptr_write(chip, CAPTURE_ROUTING2, 0, 0x76767676);

	for (ch = 0; ch < 4; ch++) {
		snd_ca0106_ptr_write(chip, CAPTURE_VOLUME1, ch, 0x30303030); /* Only high 16 bits matter */
		/* Only high 16 bits matter */
		snd_ca0106_ptr_write(chip, CAPTURE_VOLUME1, ch, 0x30303030);
		snd_ca0106_ptr_write(chip, CAPTURE_VOLUME2, ch, 0x30303030);
		//snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME1, ch, 0x40404040); /* Mute */
		//snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME2, ch, 0x40404040); /* Mute */
		snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME1, ch, 0xffffffff); /* Mute */
		snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME2, ch, 0xffffffff); /* Mute */
#if 0 /* Mute */
		snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME1, ch, 0x40404040);
		snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME2, ch, 0x40404040);
		snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME1, ch, 0xffffffff);
		snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME2, ch, 0xffffffff);
#endif
	}
	if (chip->details->i2c_adc == 1) {
	        /* Select MIC, Line in, TAD in, AUX in */
	        snd_ca0106_ptr_write(chip, CAPTURE_SOURCE, 0x0, 0x333300e4);
		/* Default to CAPTURE_SOURCE to i2s in */
		if (!resume)
			chip->capture_source = 3;
	} else if (chip->details->ac97 == 1) {
	        /* Default to AC97 in */
	        snd_ca0106_ptr_write(chip, CAPTURE_SOURCE, 0x0, 0x444400e4);
		/* Default to CAPTURE_SOURCE to AC97 in */
		if (!resume)
			chip->capture_source = 4;
	} else {
	        /* Select MIC, Line in, TAD in, AUX in */
	        snd_ca0106_ptr_write(chip, CAPTURE_SOURCE, 0x0, 0x333300e4);
		/* Default to Set CAPTURE_SOURCE to i2s in */
		if (!resume)
			chip->capture_source = 3;
	}

        if (chip->details->gpio_type == 2) { /* The SB0438 use GPIO differently. */
		/* FIXME: Still need to find out what the other GPIO bits do. E.g. For digital spdif out. */
	if (chip->details->gpio_type == 2) {
		/* The SB0438 use GPIO differently. */
		/* FIXME: Still need to find out what the other GPIO bits do.
		 * E.g. For digital spdif out.
		 */
		outl(0x0, chip->port+GPIO);
		//outl(0x00f0e000, chip->port+GPIO); /* Analog */
		/* outl(0x00f0e000, chip->port+GPIO); */ /* Analog */
		outl(0x005f5301, chip->port+GPIO); /* Analog */
	} else if (chip->details->gpio_type == 1) { /* The SB0410 and SB0413 use GPIO differently. */
		/* FIXME: Still need to find out what the other GPIO bits do. E.g. For digital spdif out. */
	} else if (chip->details->gpio_type == 1) {
		/* The SB0410 and SB0413 use GPIO differently. */
		/* FIXME: Still need to find out what the other GPIO bits do.
		 * E.g. For digital spdif out.
		 */
		outl(0x0, chip->port+GPIO);
		//outl(0x00f0e000, chip->port+GPIO); /* Analog */
		/* outl(0x00f0e000, chip->port+GPIO); */ /* Analog */
		outl(0x005f5301, chip->port+GPIO); /* Analog */
	} else {
		outl(0x0, chip->port+GPIO);
		outl(0x005f03a3, chip->port+GPIO); /* Analog */
		//outl(0x005f02a2, chip->port+GPIO);   /* SPDIF */
		/* outl(0x005f02a2, chip->port+GPIO); */ /* SPDIF */
	}
	snd_ca0106_intr_enable(chip, 0x105); /* Win2000 uses 0x1e0 */

	//outl(HCFG_LOCKSOUNDCACHE|HCFG_AUDIOENABLE, chip->port+HCFG);
	//outl(0x00001409, chip->port+HCFG); /* 0x1000 causes AC3 to fails. Maybe it effects 24 bit output. */
	//outl(0x00000009, chip->port+HCFG);
	outl(HCFG_AC97 | HCFG_AUDIOENABLE, chip->port+HCFG); /* AC97 2.0, Enable outputs. */
	/* outl(HCFG_LOCKSOUNDCACHE|HCFG_AUDIOENABLE, chip->port+HCFG); */
	/* 0x1000 causes AC3 to fails. Maybe it effects 24 bit output. */
	/* outl(0x00001409, chip->port+HCFG); */
	/* outl(0x00000009, chip->port+HCFG); */
	/* AC97 2.0, Enable outputs. */
	outl(HCFG_AC97 | HCFG_AUDIOENABLE, chip->port+HCFG);

        if (chip->details->i2c_adc == 1) { /* The SB0410 and SB0413 use I2C to control ADC. */
	if (chip->details->i2c_adc == 1) {
		/* The SB0410 and SB0413 use I2C to control ADC. */
		int size, n;

		size = ARRAY_SIZE(i2c_adc_init);
                //snd_printk("I2C:array size=0x%x\n", size);
		for (n=0; n < size; n++) {
			snd_ca0106_i2c_write(chip, i2c_adc_init[n][0], i2c_adc_init[n][1]);
		}
		/* snd_printk("I2C:array size=0x%x\n", size); */
		for (n = 0; n < size; n++)
			snd_ca0106_i2c_write(chip, i2c_adc_init[n][0],
					     i2c_adc_init[n][1]);
		for (n = 0; n < 4; n++) {
			chip->i2c_capture_volume[n][0] = 0xcf;
			chip->i2c_capture_volume[n][1] = 0xcf;
		}
		chip->i2c_capture_source = 2; /* Line in */
	        //snd_ca0106_i2c_write(chip, ADC_MUX, ADC_MUX_LINEIN); /* Enable Line-in capture. MIC in currently untested. */
		/* Enable Line-in capture. MIC in currently untested. */
		/* snd_ca0106_i2c_write(chip, ADC_MUX, ADC_MUX_LINEIN); */
	}
        if (chip->details->spi_dac == 1) { /* The SB0570 use SPI to control DAC. */

	if (chip->details->spi_dac == 1) {
		/* The SB0570 use SPI to control DAC. */
		int size, n;

		size = ARRAY_SIZE(spi_dac_init);
@@ -1536,9 +1482,112 @@ static int __devinit snd_ca0106_create(int dev, struct snd_card *card,
				chip->spi_dac_reg[reg] = spi_dac_init[n];
		}
	}
}

	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL,
				  chip, &ops)) < 0) {
static void ca0106_stop_chip(struct snd_ca0106 *chip)
{
	/* disable interrupts */
	snd_ca0106_ptr_write(chip, BASIC_INTERRUPT, 0, 0);
	outl(0, chip->port + INTE);
	snd_ca0106_ptr_write(chip, EXTENDED_INT_MASK, 0, 0);
	udelay(1000);
	/* disable audio */
	/* outl(HCFG_LOCKSOUNDCACHE, chip->port + HCFG); */
	outl(0, chip->port + HCFG);
	/* FIXME: We need to stop and DMA transfers here.
	 *        But as I am not sure how yet, we cannot from the dma pages.
	 * So we can fix: snd-malloc: Memory leak?  pages not freed = 8
	 */
}

static int __devinit snd_ca0106_create(int dev, struct snd_card *card,
					 struct pci_dev *pci,
					 struct snd_ca0106 **rchip)
{
	struct snd_ca0106 *chip;
	struct snd_ca0106_details *c;
	int err;
	static struct snd_device_ops ops = {
		.dev_free = snd_ca0106_dev_free,
	};

	*rchip = NULL;

	err = pci_enable_device(pci);
	if (err < 0)
		return err;
	if (pci_set_dma_mask(pci, DMA_32BIT_MASK) < 0 ||
	    pci_set_consistent_dma_mask(pci, DMA_32BIT_MASK) < 0) {
		printk(KERN_ERR "error to set 32bit mask DMA\n");
		pci_disable_device(pci);
		return -ENXIO;
	}

	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
	if (chip == NULL) {
		pci_disable_device(pci);
		return -ENOMEM;
	}

	chip->card = card;
	chip->pci = pci;
	chip->irq = -1;

	spin_lock_init(&chip->emu_lock);

	chip->port = pci_resource_start(pci, 0);
	chip->res_port = request_region(chip->port, 0x20, "snd_ca0106");
	if (!chip->res_port) {
		snd_ca0106_free(chip);
		printk(KERN_ERR "cannot allocate the port\n");
		return -EBUSY;
	}

	if (request_irq(pci->irq, snd_ca0106_interrupt,
			IRQF_SHARED, "snd_ca0106", chip)) {
		snd_ca0106_free(chip);
		printk(KERN_ERR "cannot grab irq\n");
		return -EBUSY;
	}
	chip->irq = pci->irq;

	/* This stores the periods table. */
	if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
				1024, &chip->buffer) < 0) {
		snd_ca0106_free(chip);
		return -ENOMEM;
	}

	pci_set_master(pci);
	/* read serial */
	pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &chip->serial);
	pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &chip->model);
	printk(KERN_INFO "snd-ca0106: Model %04x Rev %08x Serial %08x\n",
	       chip->model, pci->revision, chip->serial);
	strcpy(card->driver, "CA0106");
	strcpy(card->shortname, "CA0106");

	for (c = ca0106_chip_details; c->serial; c++) {
		if (subsystem[dev]) {
			if (c->serial == subsystem[dev])
				break;
		} else if (c->serial == chip->serial)
			break;
	}
	chip->details = c;
	if (subsystem[dev]) {
		printk(KERN_INFO "snd-ca0106: Sound card name=%s, "
		       "subsystem=0x%x. Forced to subsystem=0x%x\n",
		       c->name, chip->serial, subsystem[dev]);
	}

	sprintf(card->longname, "%s at 0x%lx irq %i",
		c->name, chip->port, chip->irq);

	ca0106_init_chip(chip, 0);

	err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
	if (err < 0) {
		snd_ca0106_free(chip);
		return err;
	}
@@ -1635,7 +1684,7 @@ static int __devinit snd_ca0106_probe(struct pci_dev *pci,
	static int dev;
	struct snd_card *card;
	struct snd_ca0106 *chip;
	int err;
	int i, err;

	if (dev >= SNDRV_CARDS)
		return -ENODEV;
@@ -1648,44 +1697,31 @@ static int __devinit snd_ca0106_probe(struct pci_dev *pci,
	if (card == NULL)
		return -ENOMEM;

	if ((err = snd_ca0106_create(dev, card, pci, &chip)) < 0) {
		snd_card_free(card);
		return err;
	}
	err = snd_ca0106_create(dev, card, pci, &chip);
	if (err < 0)
		goto error;
	card->private_data = chip;

	if ((err = snd_ca0106_pcm(chip, 0, NULL)) < 0) {
		snd_card_free(card);
		return err;
	}
	if ((err = snd_ca0106_pcm(chip, 1, NULL)) < 0) {
		snd_card_free(card);
		return err;
	}
	if ((err = snd_ca0106_pcm(chip, 2, NULL)) < 0) {
		snd_card_free(card);
		return err;
	}
	if ((err = snd_ca0106_pcm(chip, 3, NULL)) < 0) {
		snd_card_free(card);
		return err;
	}
        if (chip->details->ac97 == 1) { /* The SB0410 and SB0413 do not have an AC97 chip. */
		if ((err = snd_ca0106_ac97(chip)) < 0) {
			snd_card_free(card);
			return err;
		}
	for (i = 0; i < 4; i++) {
		err = snd_ca0106_pcm(chip, i);
		if (err < 0)
			goto error;
	}
	if ((err = snd_ca0106_mixer(chip)) < 0) {
		snd_card_free(card);
		return err;

	if (chip->details->ac97 == 1) {
		/* The SB0410 and SB0413 do not have an AC97 chip. */
		err = snd_ca0106_ac97(chip);
		if (err < 0)
			goto error;
	}
	err = snd_ca0106_mixer(chip);
	if (err < 0)
		goto error;

	snd_printdd("ca0106: probe for MIDI channel A ...");
	if ((err = snd_ca0106_midi(chip,CA0106_MIDI_CHAN_A)) < 0) {
		snd_card_free(card);
		snd_printdd(" failed, err=0x%x\n",err);
		return err;
	}
	err = snd_ca0106_midi(chip, CA0106_MIDI_CHAN_A);
	if (err < 0)
		goto error;
	snd_printdd(" done.\n");

#ifdef CONFIG_PROC_FS
@@ -1694,14 +1730,17 @@ static int __devinit snd_ca0106_probe(struct pci_dev *pci,

	snd_card_set_dev(card, &pci->dev);

	if ((err = snd_card_register(card)) < 0) {
		snd_card_free(card);
		return err;
	}
	err = snd_card_register(card);
	if (err < 0)
		goto error;

	pci_set_drvdata(pci, card);
	dev++;
	return 0;

 error:
	snd_card_free(card);
	return err;
}

static void __devexit snd_ca0106_remove(struct pci_dev *pci)
@@ -1710,6 +1749,59 @@ static void __devexit snd_ca0106_remove(struct pci_dev *pci)
	pci_set_drvdata(pci, NULL);
}

#ifdef CONFIG_PM
static int snd_ca0106_suspend(struct pci_dev *pci, pm_message_t state)
{
	struct snd_card *card = pci_get_drvdata(pci);
	struct snd_ca0106 *chip = card->private_data;
	int i;

	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
	for (i = 0; i < 4; i++)
		snd_pcm_suspend_all(chip->pcm[i]);
	if (chip->details->ac97)
		snd_ac97_suspend(chip->ac97);
	snd_ca0106_mixer_suspend(chip);

	ca0106_stop_chip(chip);

	pci_disable_device(pci);
	pci_save_state(pci);
	pci_set_power_state(pci, pci_choose_state(pci, state));
	return 0;
}

static int snd_ca0106_resume(struct pci_dev *pci)
{
	struct snd_card *card = pci_get_drvdata(pci);
	struct snd_ca0106 *chip = card->private_data;
	int i;

	pci_set_power_state(pci, PCI_D0);
	pci_restore_state(pci);

	if (pci_enable_device(pci) < 0) {
		snd_card_disconnect(card);
		return -EIO;
	}

	pci_set_master(pci);

	ca0106_init_chip(chip, 1);

	if (chip->details->ac97)
		snd_ac97_resume(chip->ac97);
	snd_ca0106_mixer_resume(chip);
	if (chip->details->spi_dac) {
		for (i = 0; i < ARRAY_SIZE(chip->spi_dac_reg); i++)
			snd_ca0106_spi_write(chip, chip->spi_dac_reg[i]);
	}

	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
	return 0;
}
#endif

// PCI IDs
static struct pci_device_id snd_ca0106_ids[] = {
	{ 0x1102, 0x0007, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },	/* Audigy LS or Live 24bit */
@@ -1723,6 +1815,10 @@ static struct pci_driver driver = {
	.id_table = snd_ca0106_ids,
	.probe = snd_ca0106_probe,
	.remove = __devexit_p(snd_ca0106_remove),
#ifdef CONFIG_PM
	.suspend = snd_ca0106_suspend,
	.resume = snd_ca0106_resume,
#endif
};

// initialization of the module