Re: new driver: 30mS interval problem

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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

[Index of Archives]     [ALSA User]     [Linux Audio Users]     [Kernel Archive]     [Asterisk PBX]     [Photo Sharing]     [Linux Sound]     [Video 4 Linux]     [Gimp]     [Yosemite News]

  Powered by Linux