On Tue, Feb 26, 2008 at 2:22 PM, Takashi Iwai <tiwai@xxxxxxx> wrote: > At Tue, 26 Feb 2008 13:56:51 +0100, > > > William Juul wrote: > > > > On Mon, Feb 25, 2008 at 4:43 PM, Takashi Iwai <tiwai@xxxxxxx> wrote: > > > At Mon, 25 Feb 2008 15:46:28 +0100, > > > > > > William Juul wrote: > > > > > > > > Hello > > > > > > > > I am new to ALSA and trying to write a new driver for a DAC connected > > > > with PCM to an AVR32 on a NGW100 reference card provided by Atmel. > > > > > > > > The sampling rate I am currently using is 11047and the DAC is > > > > providing 4 channels of 24bit. The HW interface is using DMA to copy > > > > data to RAM. > > > > By studying the audio data in hexdump or in Audacity I can verify that > > > > the sound looks good in intervals of about 30mS, then all channels are > > > > garbled for 30mS. This pattern repeat itself throughout the audio > > > > capture. > > > > > > > > I am not confident I have configured all ALSA parameters properly. > > > > How can I go about fixing/debugging this 30mS intverval problem? > > > > > > Maybe the period size has to be aligned to some value? > > > > > > > > > > > > > > Below is the command I am using. > > > > > > > > > > > > Best regards > > > > William Juul > > > > > > > > # arecord -r 11047 -c 4 -f S24_LE -s 1 -A 100 -d 5 --buffer-size 16384 > > > > -F 21333 -v > test.wav > > > > > > Try to change the period size as well. > > > > > > > > > Takashi > > > > > I tried changing the period size in both directions but it would never > > change to anything but 490. > > Then your driver has some constraints. Or does it use dmix? > > > > Then I tried changing the number of channels. -c 1 gave me lots of > > "overrun", but -c 2 made everything work. The WAV file would still > > contain data from all 4 channels. I do not understand the correlation > > between the different parameters and why it behaves like this. Thus I > > do not know what to adjust to remedy this. > > > > The command I used to make everything work was: > > arecord -r 11047 -c 2 -f S24_LE -s 100 -A 10000 -d 5 > test.wav > > > > It turned out that --buffer-size and -F did not actually change anything. > > Check with -v option what plugins are being used. > This is the output with -v. The configuration is printed twice since I have compiled a version of arecord that always print it. I do not use dmix, and I have not intentionally made any constraints. The driver source code is attached. best regards William # arecord -r 22786 -c 2 -f S24_LE -s 100 -A 10000 -d 5 -v > temp.wav ALSA /home/williaj/src/ads127x/src/ads127x.c:158: snd_ads127x_pcm_open Recording WAVE 'stdin' : Signed 24 bit Little Endian, Rate 22786 Hz, Stereo pcm->setup: 1 stream : CAPTURE access : RW_INTERLEAVED format : S24_LE subformat : STD channels : 2 rate : 22786 exact rate : 22786 (22786/1) msbits : 32 buffer_size : 4044 period_size : 1011 period_time : 44369 tick_time : 4000 tstamp_mode : NONE period_step : 1 sleep_min : 0 avail_min : 1011 xfer_align : 1011 start_threshold : 1 stop_threshold : 4044 silence_threshold: 0 silence_size : 0 boundary : 2120220672 Plug PCM: Hardware PCM card 0 'AVR32 NGW100 external DAC' device 0 subdevice 0 Its setup is: pcm->setup: 1 stream : CAPTURE access : RW_INTERLEAVED format : S24_LE subformat : STD channels : 2 rate : 22786 exact rate : 22786 (22786/1) msbits : 32 buffer_size : 4044 period_size : 1011 period_time : 44369 tick_time : 4000 tstamp_mode : NONE period_step : 1 sleep_min : 100 avail_min : 227 xfer_align : 1 start_threshold : 1 stop_threshold : 15020 silence_threshold: 0 silence_size : 0 boundary : 2120220672 > > Takashi >
/* * Driver for ADS127X 24 bits 4/8 channel DAC connected to Atmel SSC * * Copyright (C) 2006-2007 Atmel Norway * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published by * the Free Software Foundation. */ #define DEBUG #include <linux/clk.h> #include <linux/err.h> #include <linux/delay.h> #include <linux/device.h> #include <linux/dma-mapping.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/io.h> #include <sound/driver.h> #include <sound/core.h> #include <sound/initval.h> #include <sound/control.h> #include <sound/pcm.h> #include <sound/info.h> #include <linux/atmel-ssc.h> #include <linux/spi/spi.h> #include <linux/spi/at73c213.h> #define NUM_CHAN 4 #define NUM_SUBDEV 1 #define NUM_PCM (NUM_CHAN*NUM_SUBDEV) #define SAMPLING_RATE_MIN 10547 /* Hardware limit */ #define SAMPLING_RATE_MAX 128000 /* Hardware limit. */ #define TARGET_SAMPLING_RATE 22000 /* 22 kHz */ #define MIXER_ADDR_MASTER 0 #define MIXER_ADDR_PCM 1 #define MIXER_ADDR_LAST 1 struct snd_ads127x { struct snd_card *card; struct snd_pcm *pcm; struct snd_pcm_substream *substream; struct at73c213_board_info *board; int irq; int period; unsigned long samplingrate; struct clk *bitclk; struct ssc_device *ssc; struct platform_device *dev; /* Protect registers against concurrent access. */ spinlock_t lock; spinlock_t mixer_lock; int mixer_volume[MIXER_ADDR_LAST+1][2]; int capture_source[MIXER_ADDR_LAST+1][2]; }; #define get_chip(card) ((struct snd_ads127x *)card->private_data) static struct snd_pcm_hardware snd_ads127x_capture_hw = { .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER, .formats = SNDRV_PCM_FMTBIT_S24_LE, .rates = SNDRV_PCM_RATE_CONTINUOUS, .rate_min = SAMPLING_RATE_MIN, /* Replaced by chip->samplingrate later. */ .rate_max = SAMPLING_RATE_MAX, /* Replaced by chip->samplingrate later. */ .channels_min = 1, .channels_max = NUM_CHAN, .buffer_bytes_max = 512 * 1024 - 1, .period_bytes_min = 512, .period_bytes_max = 512 * 1024 - 1, .periods_min = 4, .periods_max = 1024, }; /* * Calculate and set sampling rate and divisions. */ static int snd_ads127x_set_samplingrate(struct snd_ads127x *chip) { unsigned long ssc_rate = clk_get_rate(chip->ssc->clk); unsigned long ssc_div, master_clk, status; /* * We connect two clocks here, picking divisors so the DAC clocks out * data at the same rate the SSC clocks it in ... and as close * as practical to the desired target rate. * * The DAC master clock (MCLK) is programmable, and is as a minimum * either 256 or (not here) 512 times the sampling rate. The rate will * be rounded off to use available clock divisor, so we add approx 20% * to be on the safe side. */ // int i = 0; // unsigned long clk, old_clk = 0; // for (i = 0; i< 25000000; i=i+10) // { // clk = clk_round_rate(chip->board->dac_clk, 5000000 + i); // if (clk != old_clk) // { // printk(KERN_ALERT "clk_round_rate() possible clk-> %lu\n", clk); // old_clk = clk; // // } // } master_clk = clk_round_rate(chip->board->dac_clk, 12000000 ); if (master_clk < 0) { printk(KERN_ALERT "Error calling clk_round_rate()\n"); return master_clk; } printk(KERN_INFO "Will try to set dac_clk to %lu\n target was %d\n minimum %d\n", master_clk, TARGET_SAMPLING_RATE * 256 * 2, TARGET_SAMPLING_RATE * 256); status = clk_set_rate(chip->board->dac_clk, master_clk); if (status < 0) { printk(KERN_ALERT "Error calling clk_set_rate()\n"); return status; } /* * The SSC clock mus be adjusted so that receive clock and frame sync * is matching the desired sampling rate. * * Note that the external DAC is doing one sample on each frame sync */ ssc_div = ssc_rate / (TARGET_SAMPLING_RATE * NUM_PCM * 24); /* Set divider in SSC device. */ ssc_writel(chip->ssc->regs, CMR, ssc_div/2); /* SSC clock / (ssc divider * 24-bit * NUM_PCM). */ chip->samplingrate = ssc_rate / (ssc_div * 24 * NUM_PCM); dev_info(&chip->dev->dev, "supported sampling rate is %lu (%lu divider)\n", chip->samplingrate, ssc_div); return 0; } static int snd_ads127x_pcm_open(struct snd_pcm_substream *substream) { struct snd_ads127x *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; snd_printk(KERN_ALERT "snd_ads127x_pcm_open\n"); snd_ads127x_capture_hw.rate_min = chip->samplingrate; snd_ads127x_capture_hw.rate_max = chip->samplingrate; runtime->hw = snd_ads127x_capture_hw; chip->substream = substream; return 0; } static int snd_ads127x_pcm_close(struct snd_pcm_substream *substream) { struct snd_ads127x *chip = snd_pcm_substream_chip(substream); chip->substream = NULL; snd_printk(KERN_ALERT "snd_ads127x_pcm_close\n"); return 0; } static int snd_ads127x_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); } static int snd_ads127x_pcm_hw_free(struct snd_pcm_substream *substream) { return snd_pcm_lib_free_pages(substream); } static int snd_ads127x_pcm_prepare(struct snd_pcm_substream *substream) { struct snd_ads127x *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; int block_size; block_size = frames_to_bytes(runtime, runtime->period_size); chip->period = 0; ssc_writel(chip->ssc->regs, PDC_RPR, (long)runtime->dma_addr); ssc_writel(chip->ssc->regs, PDC_RCR, runtime->period_size * 2); ssc_writel(chip->ssc->regs, PDC_RNPR, (long)runtime->dma_addr + block_size); ssc_writel(chip->ssc->regs, PDC_RNCR, runtime->period_size * 2); return 0; } static int snd_ads127x_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_ads127x *chip = snd_pcm_substream_chip(substream); int retval = 0; spin_lock(&chip->lock); switch (cmd) { case SNDRV_PCM_TRIGGER_START: ssc_writel(chip->ssc->regs, IER, SSC_BIT(IER_ENDRX)); ssc_writel(chip->ssc->regs, PDC_PTCR, SSC_BIT(PDC_PTCR_RXTEN)); break; case SNDRV_PCM_TRIGGER_STOP: ssc_writel(chip->ssc->regs, PDC_PTCR, SSC_BIT(PDC_PTCR_RXTDIS)); ssc_writel(chip->ssc->regs, IDR, SSC_BIT(IDR_ENDRX)); break; default: dev_dbg(&chip->dev->dev, "spurious command %x\n", cmd); retval = -EINVAL; break; } spin_unlock(&chip->lock); return retval; } static snd_pcm_uframes_t snd_ads127x_pcm_pointer(struct snd_pcm_substream *substream) { struct snd_ads127x *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_uframes_t pos; unsigned long bytes; bytes = ssc_readl(chip->ssc->regs, PDC_RPR) - (unsigned long)runtime->dma_addr; pos = bytes_to_frames(runtime, bytes); if (pos >= runtime->buffer_size) pos -= runtime->buffer_size; return pos; } static struct snd_pcm_ops ads127x_capture_ops = { .open = snd_ads127x_pcm_open, .close = snd_ads127x_pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_ads127x_pcm_hw_params, .hw_free = snd_ads127x_pcm_hw_free, .prepare = snd_ads127x_pcm_prepare, .trigger = snd_ads127x_pcm_trigger, .pointer = snd_ads127x_pcm_pointer, }; static void snd_ads127x_pcm_free(struct snd_pcm *pcm) { struct snd_ads127x *chip = snd_pcm_chip(pcm); snd_printk(KERN_ALERT "snd_ads127x_pcm_free\n"); if (chip->pcm) { snd_pcm_lib_preallocate_free_for_all(chip->pcm); chip->pcm = NULL; } } static int __devinit snd_ads127x_pcm_new(struct snd_ads127x *chip, int device) { struct snd_pcm *pcm; int retval; if ((retval = snd_pcm_new(chip->card, chip->card->driver, device, 0, /*1*/ NUM_SUBDEV, &pcm)) < 0) return retval; pcm->private_data = chip; pcm->private_free = snd_ads127x_pcm_free; pcm->info_flags = SNDRV_PCM_INFO_BLOCK_TRANSFER; strcpy(pcm->name, "ads127x"); chip->pcm = pcm; snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &ads127x_capture_ops); retval = snd_pcm_lib_preallocate_pages_for_all(chip->pcm, SNDRV_DMA_TYPE_DEV, &(chip->ssc->pdev->dev), // SNDRV_DMA_TYPE_CONTINUOUS, snd_dma_continuous_data(GFP_DMA | GFP_KERNEL), 512 * 1024, 512 * 1024); return 0; } static void snd_ads127x_proc_holding_reg(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { u32 reg[32]; int i, trials = 0; struct snd_ads127x *chip = entry->private_data; for (i = 0; i < 32;) { udelay(25); reg[i] = ssc_readl(chip->ssc->regs, RHR); if ((reg[i] != 0) ||(trials > 9)) { i++; trials = 0; } else trials++; } /* snd_iprintf(buffer, "reg RHR \t= 0x%08x\n", reg);*/ snd_iprintf(buffer, "reg RHR\n"); for (i = 0; i < 32; i++) snd_iprintf(buffer, "0x%08x\n", reg[i]); } static void snd_ads127x_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { u32 reg; struct snd_ads127x *chip = entry->private_data; reg = ssc_readl(chip->ssc->regs, CMR); snd_iprintf(buffer, "reg CMR \t= 0x%08x\n", reg); reg = ssc_readl(chip->ssc->regs, RHR); snd_iprintf(buffer, "reg RHR \t= 0x%08x\n", reg); reg = ssc_readl(chip->ssc->regs, SR); snd_iprintf(buffer, "reg SR \t= 0x%08x\n", reg); reg = ssc_readl(chip->ssc->regs, IMR); snd_iprintf(buffer, "reg IMR \t= 0x%08x\n", reg); reg = ssc_readl(chip->ssc->regs, PDC_RPR); snd_iprintf(buffer, "reg PDC_RPR \t= 0x%08x\n", reg); reg = ssc_readl(chip->ssc->regs, PDC_RCR); snd_iprintf(buffer, "reg PDC_RCR \t= 0x%08x\n", reg); reg = ssc_readl(chip->ssc->regs, PDC_RNPR); snd_iprintf(buffer, "reg PDC_RNPR \t= 0x%08x\n", reg); reg = ssc_readl(chip->ssc->regs, PDC_RNCR); snd_iprintf(buffer, "reg PDC_RNCR \t= 0x%08x\n", reg); reg = ssc_readl(chip->ssc->regs, PDC_PTSR); snd_iprintf(buffer, "reg PDC_PTSR \t= 0x%08x\n", reg); } static int snd_ads127x_proc_new(struct snd_ads127x *chip) { int err = 0; struct snd_info_entry *entry; if ((err = snd_card_proc_new(chip->card, "reg-dump", &entry)) >= 0) snd_info_set_text_ops(entry, chip, snd_ads127x_proc_read); if ((err = snd_card_proc_new(chip->card, "reg-rhr", &entry)) >= 0) snd_info_set_text_ops(entry, chip, snd_ads127x_proc_holding_reg); return err; } static irqreturn_t snd_ads127x_interrupt(int irq, void *dev_id) { struct snd_ads127x *chip = dev_id; struct snd_pcm_runtime *runtime = chip->substream->runtime; u32 status; int offset; int block_size; int next_period; int retval = IRQ_NONE; spin_lock(&chip->lock); block_size = frames_to_bytes(runtime, runtime->period_size); status = ssc_readl(chip->ssc->regs, IMR); if (status & SSC_BIT(IMR_ENDRX)) { chip->period++; if (chip->period == runtime->periods) chip->period = 0; next_period = chip->period + 1; if (next_period == runtime->periods) next_period = 0; offset = block_size * next_period; ssc_writel(chip->ssc->regs, PDC_RNPR, (long)runtime->dma_addr + offset); ssc_writel(chip->ssc->regs, PDC_RNCR, runtime->period_size * 2); retval = IRQ_HANDLED; } ssc_readl(chip->ssc->regs, IMR); spin_unlock(&chip->lock); if (status & SSC_BIT(IMR_ENDRX)) snd_pcm_period_elapsed(chip->substream); return retval; } #define ADS127X_VOLUME(xname, xindex, addr) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ .name = xname, \ .index = xindex, \ .info = snd_ads127x_volume_info, \ .get = snd_ads127x_volume_get, \ .put = snd_ads127x_volume_put, \ .private_value = addr } static int snd_ads127x_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 2; uinfo->value.integer.min = -50; uinfo->value.integer.max = 100; return 0; } static int snd_ads127x_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ads127x *chip = snd_kcontrol_chip(kcontrol); int addr = kcontrol->private_value; spin_lock_irq(&chip->mixer_lock); ucontrol->value.integer.value[0] = chip->mixer_volume[addr][0]; ucontrol->value.integer.value[1] = chip->mixer_volume[addr][1]; spin_unlock_irq(&chip->mixer_lock); return 0; } static int snd_ads127x_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ads127x *chip = snd_kcontrol_chip(kcontrol); int change, addr = kcontrol->private_value; int left, right; left = ucontrol->value.integer.value[0]; if (left < -50) left = -50; if (left > 100) left = 100; right = ucontrol->value.integer.value[1]; if (right < -50) right = -50; if (right > 100) right = 100; spin_lock_irq(&chip->mixer_lock); change = chip->mixer_volume[addr][0] != left || chip->mixer_volume[addr][1] != right; chip->mixer_volume[addr][0] = left; chip->mixer_volume[addr][1] = right; spin_unlock_irq(&chip->mixer_lock); return change; } #define ADS127X_CAPSRC(xname, xindex, addr) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, .index = xindex, \ .info = snd_ads127x_capsrc_info, \ .get = snd_ads127x_capsrc_get, \ .put = snd_ads127x_capsrc_put, \ .private_value = addr } static int snd_ads127x_capsrc_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; uinfo->count = 2; uinfo->value.integer.min = 0; uinfo->value.integer.max = 1; return 0; } static int snd_ads127x_capsrc_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ads127x *chip = snd_kcontrol_chip(kcontrol); int addr = kcontrol->private_value; spin_lock_irq(&chip->mixer_lock); ucontrol->value.integer.value[0] = chip->capture_source[addr][0]; ucontrol->value.integer.value[1] = chip->capture_source[addr][1]; spin_unlock_irq(&chip->mixer_lock); return 0; } static int snd_ads127x_capsrc_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ads127x *chip = snd_kcontrol_chip(kcontrol); int change, addr = kcontrol->private_value; int left, right; left = ucontrol->value.integer.value[0] & 1; right = ucontrol->value.integer.value[1] & 1; spin_lock_irq(&chip->mixer_lock); change = chip->capture_source[addr][0] != left && chip->capture_source[addr][1] != right; chip->capture_source[addr][0] = left; chip->capture_source[addr][1] = right; spin_unlock_irq(&chip->mixer_lock); return change; } static struct snd_kcontrol_new snd_ads127x_controls[] __devinitdata = { ADS127X_VOLUME("Master Volume", 0, MIXER_ADDR_MASTER), ADS127X_CAPSRC("Master Capture Switch", 0, MIXER_ADDR_MASTER), ADS127X_VOLUME("PCM Capture Volume", 0, MIXER_ADDR_PCM), ADS127X_CAPSRC("PCM Capture Switch", 0, MIXER_ADDR_PCM) }; static int __devinit snd_ads127x_mixer(struct snd_ads127x *chip) { struct snd_card *card; int errval, idx; if (chip == NULL || chip->pcm == NULL) return -EINVAL; card = chip->card; strcpy(card->mixername, chip->pcm->name); for (idx = 0; idx < ARRAY_SIZE(snd_ads127x_controls); idx++) { errval = snd_ctl_add(card, snd_ctl_new1(&snd_ads127x_controls[idx], chip)); if (errval < 0) { dev_info(&chip->dev->dev, "snd_ctl_add - returning %d for item %d\n", errval, idx); goto cleanup; } } dev_info(&chip->dev->dev, "snd_ads127x_mixer - OK\n"); return 0; cleanup: for (idx = 1; idx < ARRAY_SIZE(snd_ads127x_controls) + 1; idx++) { struct snd_kcontrol *kctl; kctl = snd_ctl_find_numid(card, idx); if (kctl) snd_ctl_remove(card, kctl); } return errval; } /* * Device functions */ static int __devinit snd_ads127x_ssc_init(struct snd_ads127x *chip) { /* * Continuous clock output. * Receive clock inversion (sample on rising edge) * Receive start on rising edge on RF * Delay 0 cycle (0 bit). * Periode is 96 bit (47+1)*2. */ ssc_writel(chip->ssc->regs, RCMR, SSC_BF(RCMR_CKO, 1) | SSC_BIT(RCMR_CKI) | SSC_BF(RCMR_START, 5) | SSC_BF(RCMR_STTDLY, 0) | SSC_BF(RCMR_PERIOD, 47) ); /* * Data length is 8 bit (8 - 1). * Receive MSB first. * Receive 12 bytes (12 -1) each transfer. * Frame sync length is 2 bit (2 - 1). * Frame starts on negative pulse. */ ssc_writel(chip->ssc->regs, RFMR, SSC_BF(RFMR_DATLEN, 24 - 1) // SSC_BF(RFMR_DATLEN, 8 - 1) | SSC_BIT(RFMR_MSBF) | SSC_BF(RFMR_DATNB, 4-1) // | SSC_BF(RFMR_DATNB, 12-1) | SSC_BF(RFMR_FSLEN, 2 - 1) | SSC_BF(RFMR_FSOS, 1)); return 0; } static int __devinit snd_ads127x_chip_init(struct snd_ads127x *chip) { int retval; retval = snd_ads127x_set_samplingrate(chip); if (retval) { dev_info(&chip->dev->dev, "snd_ads127x_set_samplingrate - returning %d\n", retval); goto out; } /* Enable DAC master clock. */ clk_enable(chip->board->dac_clk); /* let ADC stabilize */ msleep(100); /* Enable I2S device, i.e. clock output. */ ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_RXEN)); goto out; out: return retval; } static int snd_ads127x_dev_free(struct snd_device *device) { struct snd_ads127x *chip = device->device_data; ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_RXDIS)); if (chip->irq >= 0) { free_irq(chip->irq, chip); chip->irq = -1; } return 0; } static int __devinit snd_ads127x_dev_init(struct snd_card *card, struct platform_device *dev) { static struct snd_device_ops ops = { .dev_free = snd_ads127x_dev_free, }; struct snd_ads127x *chip = get_chip(card); int irq, retval; irq = chip->ssc->irq; if (irq < 0) return irq; spin_lock_init(&chip->lock); chip->card = card; chip->irq = -1; retval = request_irq(irq, snd_ads127x_interrupt, 0, "ads127x", chip); if (retval) { dev_info(&dev->dev, "unable to request irq %d\n", irq); goto out; } chip->irq = irq; retval = snd_ads127x_ssc_init(chip); if (retval) { dev_info(&dev->dev, "error in snd_ads127x_ssc_init (%d)\n", retval); goto out_irq; } retval = snd_ads127x_chip_init(chip); if (retval) { dev_info(&dev->dev, "error in snd_ads127x_chip_init (%d)\n", retval); goto out_irq; } retval = snd_ads127x_pcm_new(chip, 0); if (retval) { dev_info(&dev->dev, "error in snd_ads127x_pcm_new (%d)\n", retval); goto out_irq; } retval = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); if (retval) { dev_info(&dev->dev, "error in snd_device_new (%d)\n", retval); goto out_irq; } retval = snd_ads127x_proc_new(chip); if (retval) { dev_info(&dev->dev, "error in snd_ads127x_proc_new (%d)\n", retval); goto out_irq; } retval = snd_ads127x_mixer(chip); if (retval) { dev_info(&dev->dev, "error in snd_ads127x_mixer (%d)\n", retval); goto out_snd_dev; } snd_card_set_dev(card, &dev->dev); dev_info(&dev->dev, "snd_ads127x_dev_init - returning OK\n"); goto out; out_snd_dev: snd_device_free(card, chip); out_irq: free_irq(chip->irq, chip); chip->irq = -1; out: return retval; } static int __devinit snd_ads127x_probe(struct platform_device *dev) { struct snd_card *card; struct snd_ads127x *chip; struct at73c213_board_info *board; int retval; char id[16]; printk(KERN_ALERT "dr.wj: snd_ads127x_probe\n"); board = dev->dev.platform_data; if (!board) { dev_info(&dev->dev, "no platform_data\n"); return -ENXIO; } if (!board->dac_clk) { dev_info(&dev->dev, "no DAC clk\n"); return -ENXIO; } if (IS_ERR(board->dac_clk)) { dev_info(&dev->dev, "no DAC clk\n"); return PTR_ERR(board->dac_clk); } retval = -ENOMEM; /* Allocate "card" using some unused identifiers. */ snprintf(id, sizeof id, "ads127x_%d", board->ssc_id); card = snd_card_new(SNDRV_DEFAULT_IDX1, id, THIS_MODULE, sizeof(struct snd_ads127x)); if (!card) { dev_info(&dev->dev, "snd_card_new returning 0\n"); goto out; } chip = card->private_data; chip->dev = dev; chip->board = board; chip->ssc = ssc_request(board->ssc_id); if (IS_ERR(chip->ssc)) { dev_info(&dev->dev, "could not get ssc%d device\n", board->ssc_id); retval = PTR_ERR(chip->ssc); goto out_card; } strcpy(card->driver, "ads127x"); strcpy(card->shortname, board->shortname); sprintf(card->longname, "%s on irq %d", card->shortname, chip->irq); retval = snd_ads127x_dev_init(card, dev); if (retval) { dev_info(&dev->dev, "snd_ads127x_dev_init returning != 0 (%d)\n", retval); goto out_ssc; } retval = snd_card_register(card); if (retval) { dev_info(&dev->dev, "snd_card_register returning != 0 (%d)\n", retval); goto out_ssc; } platform_set_drvdata(dev, card); printk(KERN_ALERT "dr.wj: snd_ads127x_probe OK\n"); goto out; out_ssc: ssc_free(chip->ssc); out_card: snd_card_free(card); out: return retval; } static int __devexit snd_ads127x_remove(struct platform_device *dev) { struct snd_card *card = platform_get_drvdata(dev); struct snd_ads127x *chip = card->private_data; printk(KERN_ALERT "dr.wj: snd_ads127x_remove\n"); /* Stop capture. */ ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_RXDIS)); /* Stop DAC master clock. */ clk_disable(chip->board->dac_clk); ssc_free(chip->ssc); snd_card_free(card); platform_set_drvdata(dev, NULL); return 0; } #define snd_ads127x_suspend NULL #define snd_ads127x_resume NULL #define SND_ADS127x_DRIVER "ads127x" static struct platform_driver ads127x_driver = { .driver = { .name = SND_ADS127x_DRIVER, }, .probe = snd_ads127x_probe, .suspend = snd_ads127x_suspend, .resume = snd_ads127x_resume, .remove = __devexit_p(snd_ads127x_remove), }; static int __init ads127x_init(void) { printk(KERN_ALERT "dr.wj: ads127x_init " __DATE__ " " __TIME__ "\n"); return platform_driver_register(&ads127x_driver); } module_init(ads127x_init); static void __exit ads127x_exit(void) { printk(KERN_ALERT "dr.wj: ads127x_exit\n"); platform_driver_unregister(&ads127x_driver); } module_exit(ads127x_exit); MODULE_AUTHOR("William Juul <william.juul@xxxxxxxxxxxxxx>"); MODULE_DESCRIPTION("Sound driver for ADS127X with Atmel SSC"); MODULE_LICENSE("GPL");
_______________________________________________ Alsa-devel mailing list Alsa-devel@xxxxxxxxxxxxxxxx http://mailman.alsa-project.org/mailman/listinfo/alsa-devel