/*
 *   ALSA lowlevel driver for RME Digi9652 Hammerfall
 *
 *      Copyright (c) 1999 Paul Barton-Davis
 *	Portions of this file are: Copyright (c) 1999 IEM - Winfried Ritsch
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */      

#define __SND_OSS_COMPAT__
#define SND_MAIN_OBJECT_FILE

#define DEBUG 1

#include "../../include/driver.h"
#include "../../include/pcm.h"
#include "../../include/control.h"
#include "../../include/info.h"
#include "../../include/rme9652.h"

#include <asm/uaccess.h>
#include <asm/hardirq.h>

static void rme9652_stop_card (rme9652_t *);
static void rme9652_start_card (rme9652_t *, int from_pause);
static int  rme9652_set_thru (rme9652_t *, int, int);
static int  rme9652_adat_sample_rate (rme9652_t *);
static int  rme9652_spdif_sample_rate (rme9652_t *);

static int  snd_rme9652_create_switches (snd_card_t *, rme9652_t *);
static int  snd_rme9652_create_pcm (snd_card_t *, int, rme9652_t *);
static void snd_rme9652_proc_init (rme9652_t *);
static void snd_rme9652_proc_done (rme9652_t * rme9652);
static int  snd_rme9652_playback_open (void *, snd_pcm_subchn_t *);
static int  snd_rme9652_capture_open (void *, snd_pcm_subchn_t *);
static int  snd_rme9652_playback_release (void *, snd_pcm_subchn_t *);
static int  snd_rme9652_capture_release (void *, snd_pcm_subchn_t *);
#if 0
static long snd_rme9652_capture_read (void *private_data,
				      snd_pcm_subchn_t *subchn,
				      const char *buf,
				      long count);
static long snd_rme9652_playback_write (void *private_data,
					snd_pcm_subchn_t *subchn,
					const char *buf,
					long count);
#endif
static int snd_rme9652_playback_ioctl (void *,
				       snd_pcm_subchn_t * ,
				       unsigned int,
				       unsigned long *);
static int snd_rme9652_playback_trigger (void *,
					 snd_pcm_subchn_t *, 
					 int);
static int snd_rme9652_playback_prepare (void *,
					 snd_pcm_subchn_t *);

static unsigned int snd_rme9652_playback_pointer (void *private_data,
						  snd_pcm_subchn_t * subchn);

static int snd_rme9652_capture_ioctl (void *,
				      snd_pcm_subchn_t * ,
				      unsigned int,
				      unsigned long *);
static int snd_rme9652_capture_trigger (void *,
					snd_pcm_subchn_t *, 
					int);
static int snd_rme9652_capture_prepare (void *,
					snd_pcm_subchn_t *);

static unsigned int snd_rme9652_capture_pointer (void *private_data,
						  snd_pcm_subchn_t * subchn);


#define REJECT_IF_BUSY(s) if (rme9652_running((s))) { return -EBUSY; }


/*
 *  Playback support subchannel description. Note that all subchannels
 *  are MONO.
 */

static snd_pcm_hardware_t snd_rme9652_playback_subchannel_info =
{
	SND_PCM_CHNINFO_STREAM |
	SND_PCM_CHNINFO_BLOCK |
	SND_PCM_CHNINFO_MMAP |
	SND_PCM_CHNINFO_MMAP_VALID |
	SND_PCM_CHNINFO_PAUSE |
	SND_PCM_CHNINFO_NONINTERLEAVE, /* modes */

	SND_PCM_FMT_S32_LE,     /* formats */

	SND_PCM_RATE_KNOT | 	/* 64kHz */
	SND_PCM_RATE_44100 | 
	SND_PCM_RATE_48000 | 
	SND_PCM_RATE_88200 | 
	SND_PCM_RATE_96000,     /* rates */ 

	44100,			/* min. rate (can go lower, but not
				   via s/w control)
				*/
	96000,			/* max. rate */
	1,			/* min. voices */
	1,                      /* max. voices */
	64,			/* min. fragment size */
	32768,			/* max. fragment size */
	0,			/* fragment align - not used */
	0,			/* there is no FIFO */
	64,			/* transfer block size (bytes) */
	snd_rme9652_playback_ioctl,
	snd_rme9652_playback_prepare,
	snd_rme9652_playback_trigger,
	snd_rme9652_playback_pointer
};

/*
 *  Capture support subchannel description. Note that all subchannels
 *  are MONO
 */

static snd_pcm_hardware_t snd_rme9652_capture_subchannel_info =
{
	SND_PCM_CHNINFO_STREAM |
	SND_PCM_CHNINFO_BLOCK |
	SND_PCM_CHNINFO_MMAP |
	SND_PCM_CHNINFO_MMAP_VALID |
	SND_PCM_CHNINFO_PAUSE |
	SND_PCM_CHNINFO_NONINTERLEAVE, /* modes */

	SND_PCM_FMT_S32_LE,	/* formats */

	SND_PCM_RATE_KNOT |	/* 64kHz */
	SND_PCM_RATE_44100 | 
	SND_PCM_RATE_48000 | 
	SND_PCM_RATE_88200 | 
	SND_PCM_RATE_96000,	/* rates */

	44100,			/* min. rate (can go lower, but not
				   via s/w control)
				*/
	96000,			/* max. rate */
	1,			/* min. voices */
	1,                      /* max. voices */
	64,			/* min. fragment size */
	32768,			/* max. fragment size */
	0,			/* fragment align - safe value */
	0,			/* there is no FIFO */
	64,			/* transfer block size (bytes) */
	snd_rme9652_capture_ioctl,
	snd_rme9652_capture_prepare,
	snd_rme9652_capture_trigger,
	snd_rme9652_capture_pointer
};

inline static int rme9652_adat_sample_rate (rme9652_t *rme9652)

{
	if (readl(rme9652->iobase + RME9652_status_register) & RME9652_DS_rd) {
		return (readl(rme9652->iobase + RME9652_status_register)&
			 RME9652_fs48) ? 96000 : 88200;
	} else {
		return (readl(rme9652->iobase + RME9652_status_register)&
			 RME9652_fs48) ? 48000 : 44100;
	}
}

inline static int rme9652_spdif_sample_rate (rme9652_t *s)

{
	unsigned int rate_bits;

	if (readl (s->iobase + RME9652_status_register) & RME9652_ERF) {
		return 1;  /* error condition */
	} 

	rate_bits = (unsigned int) readl (s->iobase + RME9652_status_register) & RME9652_F;

	switch (rme9652_decode_spdif_rate (rate_bits)) {
	case 0x7:
		return 32000;
		break;

	case 0x6:
		return 44100;
		break;

	case 0x5:
		return 48000;
		break;

	case 0x4:
		return 88200;
		break;

	case 0x3:
		return 96000;
		break;

	case 0x0:
		return 64000;
		break;

	default:
		printk (KERN_WARNING "%s: unknown S/PDIF input rate (bits = 0x%x)\n",
			s->card_name,
			rate_bits);
		return 0;
		break;
	}
}

inline static void  rme9652_compute_fragment_size (rme9652_t *s)
{
	unsigned int i;
	unsigned int fragshift;

	/* The 9652 always runs with just 2 fragments (RME calls them
	   "buffers"). The fragment size is specified by setting the
	   latency. If the latency is N samples, the fragment size is
	   N*4 bytes.

	   While the board runs, the hardware pointer will circulate
	   through both fragments. However, the value of the pointer
	   that can be read from the status register (AND-ed with
	   RME9652_bus_pos) varies from 0 to 32K; the real value needs
	   to be masked not with RME9652_buf_pos, but with a mask
	   computed from the fragment size.

	   So, here we compute the fragment size and the mask.  
	*/

	i = s->control_register & RME9652_latency;
	fragshift = rme9652_decode_latency (i) + 8;

	s->fragment_bytes = 1<<(fragshift);
	s->hw_offsetmask = 0;

	for (i = 6; i < fragshift; i++) {
		s->hw_offsetmask |= 1<<i;
	}
}

inline static void rme9652_update_hw_offset (rme9652_t *s)
{
	int offset;

	/* the status register tells us which 64 byte chunk of the
	   current fragment the hardware pointer is within. 
	*/

	offset = readl (s->iobase + RME9652_status_register);

	offset &= s->hw_offsetmask;

	/* The value is always current, so the blocks currently being
	   written&read to/from have (in almost every case) not yet
	   been transferred across the PCI bus. This transfer occurs in
	   64 byte chunks. 

	   We therefore do not expose the "real" position to the rest
	   of the driver, but compute an "effective" position based
	   on the 64 byte transfer size.

	   If the pointer is in the first 64 bytes of the buffer, then
	   the effective position is actually toward the end of the
	   buffer.  
	*/

	if (offset < 64) {
		s->hw_offset = s->fragment_bytes - (64 - offset);
	} else {
		s->hw_offset = offset - 64;
	}
} 

static void snd_rme9652_free_buffers (rme9652_t *rme9652)

{
	if (rme9652->buffer_address[0]) {
#ifndef RME9652_PREALLOCATE_MEMORY
		snd_free_pages (rme9652->buffer_address[0], 
				RME9652_DMA_AREA_BYTES(rme9652));
#else 
		snd_rme9652_free_buffer (rme9652->buffer_address[0]);
#endif 		
	}

	if (rme9652->buffer_address[1]) {
#ifndef RME9652_PREALLOCATE_MEMORY
		snd_free_pages (rme9652->buffer_address[1],
				RME9652_DMA_AREA_BYTES(rme9652));
#else
		snd_rme9652_free_buffer (rme9652->buffer_address[1]);
#endif
	}
}

int snd_rme9652_free (rme9652_t * rme9652)
{
	int i;

	rme9652_stop_card (rme9652);
	snd_rme9652_proc_done (rme9652);

	for (i = 0; i < rme9652->nchannels; i++) {
		snd_kfree (rme9652->playback_dma_areas[i]);
		snd_kfree (rme9652->capture_dma_areas[i]);
	}

	snd_rme9652_free_buffers (rme9652);

	if (rme9652->iobase) {
		iounmap (rme9652->iobase);
	}

	snd_card_unregister (rme9652->card);
	snd_kfree (rme9652);

	return 0;
}

static int __init snd_rme9652_initialize_memory (rme9652_t * rme9652)

{
	u_long pb;
	u_long cb;

	/* Here the hard time: need a 1664kB physical continuous
	   memory for record and play buffers ???? 
	*/

#ifdef RME9652_PREALLOCATE_MEMORY
	cb = (u_long) snd_rme9652_get_buffer ();
	pb = (u_long) snd_rme9652_get_buffer ();
#else
	cb = snd_malloc_pages (RME9652_DMA_AREA_BYTES(rme9652), &pg, 1);
	pb = snd_malloc_pages (RME9652_DMA_AREA_BYTES(rme9652), &pg, 1);
#endif	

	if (cb == 0 || pb == 0) {
		if (cb) {
			snd_free_pages ((void *) cb, 
					RME9652_DMA_AREA_BYTES(rme9652));
		} 
		if (pb) {
			snd_free_pages ((void *) pb,
					RME9652_DMA_AREA_BYTES(rme9652));
		}

		snd_printk ("%s: no buffers available\n",
			    rme9652->card_name);
		return -ENOMEM;
	}
	
	/* save raw addresses for use when freeing memory later */

	rme9652->buffer_address[0] = (void *) cb;
	rme9652->buffer_address[1] = (void *) pb;

	/* Align to bus-space 64K boundary */

	cb  = virt_to_bus ((unsigned char *) cb);
	cb  = (cb +0xFFFF) & ~0xFFFFl;

	pb  = virt_to_bus ((unsigned char *) pb);
	pb = (pb+0xFFFF) & ~0xFFFFl;

	/* Tell the card where it is */

	writel(cb, rme9652->iobase + RME9652_rec_buffer);
  	writel(pb, rme9652->iobase + RME9652_play_buffer);

	rme9652->capture_buffer = (unsigned char *) bus_to_virt (cb);
	rme9652->playback_buffer = (unsigned char *) bus_to_virt (pb);

	/* XXX need to zero buffers here */

	return 0;
}

int __init snd_rme9652_create (snd_card_t * card, 
			       int device, 
			       rme9652_t * rme9652)

{
	int err;
	unsigned short cmdw;

	rme9652->card = card;
	spin_lock_init(&rme9652->lock);

	/* XXX whats the return on this if it fails ? */

	rme9652->iobase = ioremap (rme9652->port, RME9652_IO_EXTENT);

	pci_read_config_word(rme9652->pci, PCI_COMMAND, &cmdw);

	if(!(cmdw & PCI_COMMAND_MASTER)){
		printk (KERN_ERR 
			"%s: setting card at 0x%p to master mode.\n",
			rme9652->card_name,
			(void *) rme9652->iobase);
		pci_set_master (rme9652->pci);
	} 

	if ((err = snd_rme9652_initialize_memory (rme9652)) < 0) {
		return err;
	}

	if ((err = snd_rme9652_create_pcm (card, device, rme9652)) < 0) {
		return err;
	}

	if ((err = snd_rme9652_create_switches (card, rme9652)) < 0) {
		return err;
	}

	snd_rme9652_proc_init (rme9652);

	/* set defaults:
	     SPDIF Input via Coax 
	     Master clock mode
	     maximum latency (7 = 8192 samples, 64Kbyte buffer,
	         which implies 2 (4096 sample, 32Kbyte) fragments).
	*/
	
	rme9652->control_register = 
		RME9652_inp_0 | 
		RME9652_Master | 
		rme9652_encode_latency (7);

	writel(rme9652->control_register,
	       rme9652->iobase + RME9652_control_register);
	
	rme9652_compute_fragment_size (rme9652);
	rme9652_update_hw_offset (rme9652);

	/* default: thru off for all channels */

	rme9652_set_thru (rme9652, -1, 0);

	return 0;
}

static unsigned char * rme9652_buffer_location (rme9652_t * rme9652, 
						int chn, int subchn) 

{
	if (chn == SND_PCM_CHANNEL_CAPTURE) {
		return rme9652->capture_buffer + 
			(subchn * RME9652_CHANNEL_BUFFER_BYTES);
	} else {
		return rme9652->playback_buffer + 
			(subchn * RME9652_CHANNEL_BUFFER_BYTES);
	}
}

static int __init  snd_rme9652_create_pcm (snd_card_t * card, 
					   int device,
					   rme9652_t * rme9652)

{
	snd_pcm_t *pcm;
	int err;
	int i;

	if ((err = snd_pcm_new (card, 
				rme9652->card_name,
				device, 
				rme9652->nchannels,
				rme9652->nchannels,
				&pcm)) < 0) {
		return err;
	}

	rme9652->pcm = pcm;
	pcm->private_data = rme9652;
	strcpy(pcm->name, rme9652->card_name);

	for (i = 0; i < rme9652->nchannels; i++) {
		if ((rme9652->playback_dma_areas[i] = (snd_dma_area_t *) 
		     snd_kcalloc (sizeof (snd_dma_area_t), 
				  GFP_KERNEL)) == NULL) {
			return -ENOMEM;
		}

		if ((rme9652->capture_dma_areas[i] = (snd_dma_area_t *) 
		     snd_kcalloc (sizeof (snd_dma_area_t), 
				  GFP_KERNEL)) == NULL) {
			return -ENOMEM;
		}

		rme9652->playback_dma_areas[i]->size = 
			RME9652_CHANNEL_BUFFER_BYTES;
	
		rme9652->capture_dma_areas[i]->size = 
			RME9652_CHANNEL_BUFFER_BYTES;
	}

	pcm->chn[SND_PCM_CHANNEL_PLAYBACK].private_data = rme9652;
	pcm->chn[SND_PCM_CHANNEL_PLAYBACK].open = snd_rme9652_playback_open;
	pcm->chn[SND_PCM_CHANNEL_PLAYBACK].close = snd_rme9652_playback_release;

	pcm->chn[SND_PCM_CHANNEL_CAPTURE].private_data = rme9652;
	pcm->chn[SND_PCM_CHANNEL_CAPTURE].open = snd_rme9652_capture_open;
	pcm->chn[SND_PCM_CHANNEL_CAPTURE].close = snd_rme9652_capture_release;

	pcm->info_flags = SND_PCM_INFO_PLAYBACK | SND_PCM_INFO_CAPTURE |
			  SND_PCM_INFO_DUPLEX;

	return 0;
}

static void rme9652_prepare_card (rme9652_t *s) 
{
	int i;

	if (rme9652_running (s)) {
		rme9652_stop_card (s);
		udelay(10);
	}
	
	/* initialize all necessary registers */
	
	for (i = 0; i < RME9652_num_of_init_regs; i++){
		writel (0, s->iobase + i);
		udelay (10);
	}

	/* zero playback/capture memory */
	
	memset (s->playback_buffer, 0, RME9652_DMA_AREA_BYTES(s));
	memset (s->capture_buffer, 0, RME9652_DMA_AREA_BYTES(s));
}

static void rme9652_start_card (rme9652_t *s, int from_pause)
{
	if (!from_pause) {
		rme9652_prepare_card (s);
	}

	s->control_register |= (RME9652_IE|RME9652_start_bit);
	writel(s->control_register, s->iobase + RME9652_control_register);
}

static void rme9652_stop_card(rme9652_t *s)
{
	s->control_register &= ~(RME9652_start_bit|RME9652_IE);
	writel (s->control_register, s->iobase + RME9652_control_register);
}

#define rme9652_capture_is_open(s,c) \
     (((s)->capture_open_mask & (1<<(c)->number)) == (1<<(c)->number))

#define rme9652_playback_is_open(s,c) \
     (((s)->playback_open_mask & (1<<(c)->number)) == (1<<(c)->number))

void  snd_rme9652_interrupt (int irq, void *dev_id, struct pt_regs *regs)
{
	rme9652_t *rme9652 = (rme9652_t *) dev_id;	
	snd_pcm_subchn_t *subchn;
	int status;

	/* fastpath out, to ease interrupt sharing */

	if (!(readl(rme9652->iobase + RME9652_status_register) & RME9652_IRQ)) {
		return;
	}

	spin_lock_irq (&rme9652->lock);

	/* XXX is this really the right place to do this ? */

	writel (0, rme9652->iobase+RME9652_irq_clear);

	rme9652_update_hw_offset (rme9652);
	status = readl (rme9652->iobase + RME9652_status_register);

	/* XXX should we hold be blocking interrupts while we do this ? */

	for (subchn = rme9652->pcm->chn[SND_PCM_CHANNEL_CAPTURE].subchn;
	     subchn;
	     subchn = subchn->next) {
		if (rme9652_capture_is_open (rme9652, subchn)) {
			snd_pcm_transfer_done (subchn);
		}
	} 

	for (subchn = rme9652->pcm->chn[SND_PCM_CHANNEL_PLAYBACK].subchn;
	     subchn;
	     subchn = subchn->next) {
		if (rme9652_playback_is_open (rme9652, subchn)) {
			snd_pcm_transfer_done (subchn);
		}
	} 

	spin_unlock_irq (&rme9652->lock);
}


/*-----------------------------------------------------------------------------
  Control Switch Interface
  ----------------------------------------------------------------------------*/

static int snd_rme9652_get_rate (snd_card_t * card,
				 snd_kswitch_t * kswitch,
				 snd_switch_t * uswitch)
	
{
	unsigned long flags;
	rme9652_t *rme9652 = (rme9652_t *) kswitch->private_data;

	uswitch->type = SND_SW_TYPE_DWORD;
	uswitch->low = 44100;
	uswitch->high = 96000;
	spin_lock_irqsave(&rme9652->lock, flags);
	uswitch->value.data32[0] = rme9652_adat_sample_rate (rme9652);
	spin_unlock_irqrestore(&rme9652->lock, flags);

	return 0;
}

static int rme9652_set_rate (rme9652_t * rme9652, int rate)

{
	unsigned long flags;
	int restart;
	int reject_if_open = 0;

	/* Changing from a "single speed" to a "double speed" rate is
	   not allowed if any subchannels are open. This is because
	   such a change causes a shift in the location of 
	   the DMA buffers and a reduction in the number of available
	   buffers. 

	   Note that a similar but essentially insoluble problem
	   exists for externally-driven rate changes. All we can do
	   is to flag rate changes in the read/write routines.
	*/

	switch (rate) {
	case 44100: 
		if (rme9652_adat_sample_rate (rme9652) > 48000) {
			reject_if_open = 1;
		}
		rate = 0;
		break;
	case 48000: 
		if (rme9652_adat_sample_rate (rme9652) > 48000) {
			reject_if_open = 1;
		}
		rate = RME9652_freq; 
		break;
	case 88200: 
		if (rme9652_adat_sample_rate (rme9652) < 48000) {
			reject_if_open = 1;
		}
		rate = RME9652_DS; 
		break;
	case 96000: 
		if (rme9652_adat_sample_rate (rme9652) < 48000) {
			reject_if_open = 1;
		}
		rate = RME9652_DS | RME9652_freq; 
		break;
	default:
		return -EINVAL;
	}

	if (reject_if_open) {
		if (rme9652->playback_open_mask || rme9652->capture_open_mask) { 
			return -EBUSY; 
		}
	}
	    
	spin_lock_irqsave (&rme9652->lock, flags);

	restart = rme9652_running (rme9652);

	rme9652_stop_card (rme9652);
	rme9652->control_register &= ~(RME9652_freq|RME9652_DS);
	rme9652->control_register |= rate;
	writel (rme9652->control_register,
	        rme9652->iobase + RME9652_control_register);

	spin_unlock_irqrestore(&rme9652->lock, flags);

	if (restart) {
		rme9652_start_card (rme9652, 0);
	}

	return 0;
}

static int snd_rme9652_set_rate (snd_card_t * card,
				 snd_kswitch_t * kswitch,
				 snd_switch_t * uswitch)

{
	rme9652_t *rme9652 = (rme9652_t *) kswitch->private_data;
	int err;

	REJECT_IF_BUSY (rme9652);

	if (uswitch->type != SND_SW_TYPE_DWORD) {
		return -EINVAL;
	}

	if (uswitch->value.data32[0] == rme9652_adat_sample_rate (rme9652)) {
		return 0; 
	}

	if ((err = rme9652_set_rate (rme9652, uswitch->value.data32[0])) < 0) {
		return err;
	} 
	
	return 1;
}

static snd_kswitch_t snd_rme9652_rate_switch = {
	name:	"sample rate",
	get:	(snd_get_switch_t *) snd_rme9652_get_rate,
	set:	(snd_set_switch_t *) snd_rme9652_set_rate,
};

static int snd_rme9652_get_spdif_rate (snd_card_t * card,
				       snd_kswitch_t * kswitch,
				       snd_switch_t * uswitch)
	
{
	unsigned long flags;
	rme9652_t *rme9652 = (rme9652_t *) kswitch->private_data;

	uswitch->type = SND_SW_TYPE_DWORD;
	uswitch->low = 0;
	uswitch->high = 96000;
	spin_lock_irqsave(&rme9652->lock, flags);
	uswitch->value.data32[0] = rme9652_spdif_sample_rate (rme9652);
	spin_unlock_irqrestore(&rme9652->lock, flags);

	return 0;
}

static int rme9652_set_spdif_rate (rme9652_t *rme9652, int rate)
	
{
	unsigned long flags;
	unsigned int bits;

	switch (rate){
	case 0:
	case 1:
		/* pre-existing error conditions being "reset" */
		return 0;
	case 44100: 
		bits = 0; 
		break;
	case 48000: 
		bits = RME9652_freq; 
		break;
	case 88200: 
		bits = RME9652_DS; 
		break;
	case 96000: 
		bits = RME9652_DS | RME9652_freq; 
		break;
	default:
		return -EINVAL;
	}

	spin_lock_irqsave(&rme9652->lock, flags);
	
	if (rme9652_running (rme9652)) {
		rme9652_stop_card (rme9652);
		rme9652->control_register &= 
			~(RME9652_freq|RME9652_DS);
		rme9652->control_register |= bits;
		writel (rme9652->control_register,
			rme9652->iobase+ RME9652_control_register);
		spin_unlock_irqrestore(&rme9652->lock, flags);
		rme9652_start_card (rme9652, 0);
	} else {
		spin_unlock_irqrestore(&rme9652->lock, flags);
	}

	return 0;
}

static int snd_rme9652_set_spdif_rate (snd_card_t * card,
				       snd_kswitch_t * kswitch,
				       snd_switch_t * uswitch)

{
	rme9652_t *rme9652 = (rme9652_t *) kswitch->private_data;
	int err;

	REJECT_IF_BUSY (rme9652);

	if (uswitch->type != SND_SW_TYPE_DWORD) {
		return -EINVAL;
	}

	if (uswitch->value.data32[0] == rme9652_spdif_sample_rate (rme9652)) {
		return 0;
	}

	if ((err = rme9652_set_spdif_rate (rme9652,
					   uswitch->value.data32[0])) < 0) {
		return err;
	} 

	return 1;
}

static snd_kswitch_t snd_rme9652_spdif_rate_switch = {
	name:	"spdif sample rate",
	get:	(snd_get_switch_t *) snd_rme9652_get_spdif_rate,
	set:	(snd_set_switch_t *) snd_rme9652_set_spdif_rate,
};

static int rme9652_spdif_in (rme9652_t * rme9652)

{
	return rme9652_decode_spdif_in (rme9652->control_register & RME9652_inp); 
}

static int snd_rme9652_get_spdif_in (snd_card_t * card,
				     snd_kswitch_t * kswitch,
				     snd_switch_t * uswitch)
	
{
	rme9652_t *rme9652 = (rme9652_t *) kswitch->private_data;
	unsigned long flags;
	
	uswitch->type = SND_SW_TYPE_BYTE;
	uswitch->low = 0;
	uswitch->high = 2;

	spin_lock_irqsave(&rme9652->lock, flags);
	uswitch->value.data8[0] = rme9652_spdif_in (rme9652);
	spin_unlock_irqrestore(&rme9652->lock, flags);
	return 0;
}

static int rme9652_set_spdif_input (rme9652_t *rme9652, int in)

{
	unsigned long flags;
	int restart = 0;

	spin_lock_irqsave(&rme9652->lock, flags);
	
	rme9652->control_register |= rme9652_encode_spdif_in (in);

	restart = rme9652_running (rme9652);

	rme9652_stop_card (rme9652);

	writel (rme9652->control_register,
		rme9652->iobase + RME9652_control_register);

	spin_unlock_irqrestore(&rme9652->lock, flags);

	if (restart) {
		rme9652_start_card (rme9652, 0);
	}

	return 0;
}

static int snd_rme9652_set_spdif_in (snd_card_t * card,
				     snd_kswitch_t * kswitch,
				     snd_switch_t * uswitch)

{
	rme9652_t *rme9652 = (rme9652_t *) kswitch->private_data;
	int err;

	REJECT_IF_BUSY (rme9652);

	if (uswitch->type != SND_SW_TYPE_BYTE) {
		return -EINVAL;
	}

	if (uswitch->value.data8[0] > 2) {
		return -EINVAL;
	}

	if (uswitch->value.data8[0] == rme9652_spdif_in (rme9652)) {
		return 0;
	}

	if ((err = rme9652_set_spdif_input (rme9652,
					    uswitch->value.data8[0])) < 0) {
		return err;
	} 
	
	return 1;
}

static snd_kswitch_t snd_rme9652_spdif_input_switch = {
	name:	"S/PDIF Input Connector",
	get:	(snd_get_switch_t *) snd_rme9652_get_spdif_in,
	set:	(snd_set_switch_t *) snd_rme9652_set_spdif_in,
};

static int rme9652_spdif_out (rme9652_t * rme9652)
       
{
	return (rme9652->control_register & RME9652_opt_out) ? 1 : 0;
}

static int snd_rme9652_get_spdif_out (snd_card_t * card,
				      snd_kswitch_t * kswitch,
				      snd_switch_t * uswitch)
	
{
	unsigned long flags;
	rme9652_t *rme9652 = (rme9652_t *) kswitch->private_data;

	/* this one is almost a boolean, but the semantics are
	   different 
	*/

	uswitch->type = SND_SW_TYPE_BYTE;
	uswitch->low = 0;
	uswitch->high = 1;

	spin_lock_irqsave(&rme9652->lock, flags);
	uswitch->value.data8[0] = rme9652_spdif_out (rme9652);
	spin_unlock_irqrestore(&rme9652->lock, flags);
	return 0;
}

static int rme9652_set_spdif_output (rme9652_t *rme9652, int out)

{
	unsigned long flags;
	int restart = 0;

	spin_lock_irqsave (&rme9652->lock, flags);
	
	if (out) {
		rme9652->control_register |= RME9652_opt_out;
	} else {
		rme9652->control_register &= ~RME9652_opt_out;
	}

	restart = rme9652_running (rme9652);

	rme9652_stop_card (rme9652);
	writel (rme9652->control_register, 
		rme9652->iobase + RME9652_control_register);
	spin_unlock_irqrestore(&rme9652->lock, flags);

	if (restart) {
		rme9652_start_card (rme9652, 0);
	}

	return 0;
}

static int snd_rme9652_set_spdif_out (snd_card_t * card,
				      snd_kswitch_t * kswitch,
				      snd_switch_t * uswitch)

{
	rme9652_t *rme9652 = (rme9652_t *) kswitch->private_data;
	int err;

	REJECT_IF_BUSY (rme9652);

	if (uswitch->type != SND_SW_TYPE_BYTE) {
		return -EINVAL;
	}

	if (uswitch->value.data8[0] > 1) {
		return -EINVAL;
	}

	if (uswitch->value.data8[0] == rme9652_spdif_out (rme9652)) {
		return 0;
	}

	if ((err = rme9652_set_spdif_output (rme9652,
					     uswitch->value.data8[0])) < 0) {
		return err;
	}

	return 1;
}

static snd_kswitch_t snd_rme9652_spdif_output_switch = {
	name:	"S/PDIF Output Connector",
	get:	(snd_get_switch_t *) snd_rme9652_get_spdif_out,
	set:	(snd_set_switch_t *) snd_rme9652_set_spdif_out,
};

static int rme9652_sync_mode (rme9652_t * rme9652)

{
	if (rme9652->control_register & RME9652_wsel) {
		return 2;
	} else if (rme9652->control_register & RME9652_Master) {
		return 1;
	} else {
		return 0;
	}
}

static int snd_rme9652_get_sync_mode (snd_card_t * card,
				      snd_kswitch_t * kswitch,
				      snd_switch_t * uswitch)
	
{
	unsigned long flags;
	rme9652_t *rme9652 = (rme9652_t *) kswitch->private_data;

	uswitch->type = SND_SW_TYPE_BYTE;
	uswitch->low = 0;
	uswitch->high = 2;

	spin_lock_irqsave(&rme9652->lock, flags);
	uswitch->value.data8[0] = rme9652_sync_mode (rme9652);
	spin_unlock_irqrestore(&rme9652->lock, flags);
	return 0;
}

static int rme9652_set_sync_mode (rme9652_t *rme9652, int mode)

{
	unsigned long flags;
	int restart = 0;

	spin_lock_irqsave(&rme9652->lock, flags);

	switch (mode) {
	case 0:
		rme9652->control_register &= ~(RME9652_Master|RME9652_wsel); 
		break;
	case 1:
		rme9652->control_register =
			(rme9652->control_register & ~RME9652_wsel)|RME9652_Master;
		break;
	case 2:
		rme9652->control_register |= (RME9652_Master|RME9652_wsel);
		break;
	}

	restart = rme9652_running (rme9652);
	
	rme9652_stop_card (rme9652);
	writel(rme9652->control_register,
	       rme9652->iobase + RME9652_control_register);

	spin_unlock_irqrestore(&rme9652->lock, flags);

	if (restart) {
		rme9652_start_card (rme9652, 0);
	}
	
	return 0;
}

static int snd_rme9652_set_sync_mode (snd_card_t * card,
				      snd_kswitch_t * kswitch,
				      snd_switch_t * uswitch)

{
	rme9652_t *rme9652 = (rme9652_t *) kswitch->private_data;
	int err;

	REJECT_IF_BUSY (rme9652);

	if (uswitch->type != SND_SW_TYPE_BYTE) {
		return -EINVAL;
	}

	if (uswitch->value.data8[0] > 2) {
		return -EINVAL;
	}

	if (uswitch->value.data8[0] == rme9652_sync_mode (rme9652)) {
		return 0;
	}

	if ((err = rme9652_set_sync_mode (rme9652,
					  uswitch->value.data8[0])) < 0) {
		return err;
	} 

	return 1;
}

static snd_kswitch_t snd_rme9652_sync_mode_switch = {
	name:	"Sync Mode",
	get:	(snd_get_switch_t *) snd_rme9652_get_sync_mode,
	set:	(snd_set_switch_t *) snd_rme9652_set_sync_mode,
};

static int rme9652_sync_pref (rme9652_t * rme9652)

{
	return rme9652_decode_sync_src (rme9652->control_register & 
					RME9652_SyncRef); 
}

static int
snd_rme9652_get_sync_pref (snd_card_t * card,
			   snd_kswitch_t * kswitch,
			   snd_switch_t * uswitch)
	
{
	unsigned long flags;
	rme9652_t *rme9652 = (rme9652_t *) kswitch->private_data;

	uswitch->type = SND_SW_TYPE_BYTE;
	spin_lock_irqsave(&rme9652->lock, flags);
	uswitch->value.data8[0] = rme9652_sync_pref (rme9652);
	spin_unlock_irqrestore(&rme9652->lock, flags);
	return 0;
}

static int rme9652_set_sync_pref (rme9652_t *rme9652, int pref)

{
	unsigned long flags;
	int restart;

	spin_lock_irqsave(&rme9652->lock, flags);

	switch (pref) {
	case RME9652_SYNC_FROM_ADAT1:
		rme9652->control_register &= ~RME9652_SyncRef0; 
		break;
	case RME9652_SYNC_FROM_ADAT2:
		rme9652->control_register =
			(rme9652->control_register & ~RME9652_SyncRef1)|RME9652_SyncRef0;
		break;
	case RME9652_SYNC_FROM_ADAT3:
		rme9652->control_register =
			(rme9652->control_register & ~RME9652_SyncRef0)|RME9652_SyncRef1;
		break;
	case RME9652_SYNC_FROM_SPDIF:
		rme9652->control_register |= (RME9652_SyncRef0|RME9652_SyncRef1);
		break;
	}

	restart = rme9652_running (rme9652);

	rme9652_stop_card (rme9652);
	writel (rme9652->control_register, 
		rme9652->iobase + RME9652_control_register);

	spin_unlock_irqrestore(&rme9652->lock, flags);

	if (restart) {
		rme9652_start_card (rme9652, 0);
	} 
	
	return 0;
}

static int
snd_rme9652_set_sync_pref (snd_card_t * card,
			   snd_kswitch_t * kswitch,
			   snd_switch_t * uswitch)

{
	rme9652_t *rme9652 = (rme9652_t *) kswitch->private_data;
	int err;

	REJECT_IF_BUSY (rme9652);

	if (uswitch->type != SND_SW_TYPE_BYTE) {
		return -EINVAL;
	}

	if (uswitch->value.data8[0] > 3) {
		return -EINVAL;
	}

	if (uswitch->value.data8[0] == rme9652_sync_pref (rme9652)) {
		return 0;
	}

	if ((err = rme9652_set_sync_pref (rme9652,
					  uswitch->value.data8[0])) < 0) {
		return err;
	} 

	return 1;
}

static snd_kswitch_t snd_rme9652_preferred_sync_switch = {
	name:	"Preferred Sync Source",
	get:	(snd_get_switch_t *) snd_rme9652_get_sync_pref,
	set:	(snd_set_switch_t *) snd_rme9652_set_sync_pref,
};

static unsigned int rme9652_thru_status (rme9652_t * rme9652, int chn)

{
	if (chn < 0) {
		return rme9652->thru_bits;
	}

	return (rme9652->thru_bits & (1<<chn)) ? 1 : 0;
}

static int snd_rme9652_get_thru (snd_card_t * card,
				 snd_kswitch_t * kswitch,
				 snd_switch_t * uswitch)

{
	unsigned long flags;
	rme9652_t *rme9652 = (rme9652_t *) kswitch->private_data;
	unsigned int which;

	uswitch->type = SND_SW_TYPE_BOOLEAN;
	which = kswitch->private_value;
	spin_lock_irqsave(&rme9652->lock, flags);
	uswitch->value.enable = rme9652_thru_status (rme9652, which);
	spin_unlock_irqrestore(&rme9652->lock, flags);
	return 0;
}

static int rme9652_set_thru (rme9652_t *rme9652, int channel, int enable)

{
	unsigned long flags;
	int i;

	spin_lock_irqsave(&rme9652->lock, flags);

	if (channel < 0) {

		/* set thru for all channels */
		
		if (enable) {
			for (i = 0; i < rme9652->nchannels; i++) {
				rme9652->thru_bits |= (1<<i);
				writel (1,
					rme9652->iobase + RME9652_thru_base + i);

			} 

		} else {

			for (i = 0; i < rme9652->nchannels; i++) {
				rme9652->thru_bits &= ~(1<<i);
				writel (0,
					rme9652->iobase + RME9652_thru_base + i);
				
			}
		}

	} else {

		/* set thru for just one channel */

		if (enable) {
			rme9652->thru_bits |= (1<<channel);
		} else {
			rme9652->thru_bits &= ~(1<<channel);
		}
		
		writel (enable ? 1 : 0,
			rme9652->iobase + RME9652_thru_base + channel);
	}

	spin_unlock_irqrestore(&rme9652->lock, flags);

	return 0;
}

static int snd_rme9652_set_thru (snd_card_t * card,
				 snd_kswitch_t * kswitch,
				 snd_switch_t * uswitch)
{
	rme9652_t *rme9652 = (rme9652_t *) kswitch->private_data;
	unsigned int chn;
	int err;

	/* Important: do not reject if busy. That would stop
	   key functionality from being used in an application.
	*/

	if (uswitch->type != SND_SW_TYPE_BOOLEAN) {
		return -EINVAL;
	}

	chn = kswitch->private_value;

	if (uswitch->value.enable == rme9652_thru_status (rme9652, chn)) {
		return 0;
	}

	if ((err = rme9652_set_thru (rme9652, chn,
				     uswitch->value.enable)) < 0) {
		return err;
	} 

	return 1;
}
	
static snd_kswitch_t snd_rme9652_thru_switch = {
	name:	"This Should Be OverWritten",
	get:	(snd_get_switch_t *) snd_rme9652_get_thru,
	set:	(snd_set_switch_t *) snd_rme9652_set_thru,
};

static int snd_rme9652_get_all_thru (snd_card_t * card,
				     snd_kswitch_t * kswitch,
				     snd_switch_t * uswitch)

{
	unsigned long flags;
	rme9652_t *rme9652 = (rme9652_t *) kswitch->private_data;

	uswitch->type = SND_SW_TYPE_DWORD;
	uswitch->low = 0;
	uswitch->high = 0x3ffffff;  /* least-significant 26 bits = 1 */
	spin_lock_irqsave(&rme9652->lock, flags);
	uswitch->value.data32[0] = rme9652_thru_status (rme9652, -1);
	spin_unlock_irqrestore(&rme9652->lock, flags);
	return 0;
}

static int snd_rme9652_set_all_thru (snd_card_t * card,
				     snd_kswitch_t * kswitch,
				     snd_switch_t * uswitch)
{
	rme9652_t *rme9652 = (rme9652_t *) kswitch->private_data;
	int err;

	/* Important: do not reject if busy. That would stop
	   key functionality from being used in an application -
	   changing thru status while the card is in use.

	   Note: all global thru changes result in a switch change
	   message 
	*/

	if (uswitch->type != SND_SW_TYPE_DWORD) {
		return -EINVAL;
	}

	if (uswitch->value.data32[0] == rme9652_thru_status (rme9652, -1)) {
		return 0;
	}

	if ((err = rme9652_set_thru (rme9652, -1,
				     uswitch->value.data32[0])) < 0) {
		return err;
	}

	return 1;
}

static snd_kswitch_t snd_rme9652_all_thru_switch = {
	name:	"all channels thru",
	get:	(snd_get_switch_t *) snd_rme9652_get_all_thru,
	set:	(snd_set_switch_t *) snd_rme9652_set_all_thru,
};


static int rme9652_latency_samples (rme9652_t *rme9652)

{
	return 1 << (rme9652_decode_latency 
		     (rme9652->control_register & RME9652_latency) + 6);
}

static int snd_rme9652_get_latency (snd_card_t * card,
				    snd_kswitch_t * kswitch,
				    snd_switch_t * uswitch)

{
	unsigned long flags;
	rme9652_t *rme9652 = (rme9652_t *) kswitch->private_data;

	uswitch->type = SND_SW_TYPE_WORD;
	uswitch->low = 64;
	uswitch->high = 8192;
	spin_lock_irqsave(&rme9652->lock, flags);
	uswitch->value.data16[0] = rme9652_latency_samples (rme9652);
	spin_unlock_irqrestore(&rme9652->lock, flags);

	return 0;
}

static int rme9652_set_latency (rme9652_t *s, unsigned int samples)

{
	int restart = 0;
	int i;

	/* IMPORTANT NOTE: 

	   This function is only invoked through the switch
	   interface, never through the PARAMS ioctl. So, when this is
	   called, we don't know what the current (tied) subchannel
	   parameters are, but we also don't care. The switch
	   interface does not reset the parameters for any running
	   subchannels; instead, they pick up the current parameters
	   when they are started.

	   However, the PCM layer uses the size of a subchannel's
	   snd_dma_area_t to do computations on fragments, queues
	   etc. Since the Hammerfall effectively changes its h/w
	   buffer size when we reset the latency, we do need to 
	   go through all the subchannels and reset their dma_areas'
	   size values.
	*/

	if ((restart = rme9652_running (s))) {
		rme9652_stop_card (s);
	}

	if (samples <= 64) {
		s->control_register = 
			s->control_register&~RME9652_latency;
	} else if (samples <= 128) {
		s->control_register =
			(s->control_register&~RME9652_latency)
			| RME9652_latency0;
	} else if (samples <= 256) {
		s->control_register =
			(s->control_register&~RME9652_latency)
			| RME9652_latency1;
	} else if (samples <= 512) {
		s->control_register =
			(s->control_register&~RME9652_latency)
			| RME9652_latency0 | RME9652_latency1;
	} else if (samples <= 1024) {
		s->control_register =
			(s->control_register&~RME9652_latency)
			| RME9652_latency2;
	} else if (samples <= 2048) {
		s->control_register =
			(s->control_register&~RME9652_latency)
			| RME9652_latency2 | RME9652_latency0;
	} else if (samples <= 4096) {
		s->control_register =
			(s->control_register&~RME9652_latency)
			| RME9652_latency2 | RME9652_latency1;
	} else { /* assume 8192 */
		s->control_register = s->control_register
			| RME9652_latency2 | RME9652_latency1
			| RME9652_latency0;
	}
	
	writel (s->control_register, s->iobase + RME9652_control_register);

	rme9652_compute_fragment_size (s);

	/* Recall: there are 2 fragments, so the total DMA area is
	   twice the size of the fragments.
	*/

	for (i = 0; i < s->nchannels; i++) {
		s->playback_dma_areas[i]->size = 2 * s->fragment_bytes;
		s->capture_dma_areas[i]->size = 2 * s->fragment_bytes;

	}

	if (restart) {
		rme9652_start_card (s, 0);
	}

	return 0;
}		

static int snd_rme9652_set_latency (snd_card_t * card,
				    snd_kswitch_t * kswitch,
				    snd_switch_t * uswitch)
{
	rme9652_t *rme9652 = (rme9652_t *) kswitch->private_data;
	int err;

	REJECT_IF_BUSY(rme9652);

	if (uswitch->type != SND_SW_TYPE_WORD) {
		return -EINVAL;
	}

	if (uswitch->value.data16[0] == rme9652_latency_samples (rme9652)) {
		return 0;
	}

 	if ((err = rme9652_set_latency (rme9652,
					uswitch->value.data16[0])) < 0) {
		return err;
	} 

	return 1;
}

static snd_kswitch_t snd_rme9652_latency_switch = {
	name:	"latency",
	get:	(snd_get_switch_t *) snd_rme9652_get_latency,
	set:	(snd_set_switch_t *) snd_rme9652_set_latency,
};

/* Read-only switches */

static int snd_rme9652_rdonly_switch_set (void *private_data,
					  snd_kswitch_t * kswitch,
					  snd_switch_t * uswitch)
{
	uswitch->type = SND_SW_TYPE_NONE;
	return -EPERM;
}

static int snd_rme9652_get_adat_sync (void * private_data,
				      snd_kswitch_t * kswitch,
				      snd_switch_t * uswitch)
{
	rme9652_t *rme9652 = (rme9652_t *) kswitch->private_data;
	
	uswitch->type = SND_SW_TYPE_BYTE;

	switch (kswitch->private_value) {
	case 0:
		uswitch->value.data8[0] = 
			(readl (rme9652->iobase + RME9652_status_register) & 
			 RME9652_lock_0) ? 1 : 0;
		uswitch->value.data8[0] |= 
			((readl (rme9652->iobase + RME9652_status_register) & 
			 RME9652_sync_0) ? 1 : 0) << 1;
		break;

	case 1:
		uswitch->value.data8[0] = 
			(readl (rme9652->iobase + RME9652_status_register) & 
			 RME9652_lock_1) ? 1 : 0;
		uswitch->value.data8[0] |= 
			((readl (rme9652->iobase + RME9652_status_register) & 
			 RME9652_sync_1) ? 1 : 0) << 1;
		break;

	case 2:
		uswitch->value.data8[0] = 
			(readl (rme9652->iobase + RME9652_status_register) & 
			 RME9652_lock_2) ? 1 : 0;
		uswitch->value.data8[0] |= 
			((readl (rme9652->iobase + RME9652_status_register) & 
			 RME9652_sync_2) ? 1 : 0) << 1;
		break;
	}

	return 0;
}

static snd_kswitch_t snd_rme9652_adat1_sync_check_switch = {
	name:		"adat1 sync check",
	get:		snd_rme9652_get_adat_sync,
	set:		snd_rme9652_rdonly_switch_set,
	private_value:	0,
};

static snd_kswitch_t snd_rme9652_adat2_sync_check_switch = {
	name:		"adat2 sync check",
	get:		snd_rme9652_get_adat_sync,
	set:		snd_rme9652_rdonly_switch_set,
	private_value:	1,
};

static snd_kswitch_t snd_rme9652_adat3_sync_check_switch = {
	name:		"adat3 sync check",
	get:		snd_rme9652_get_adat_sync,
	set:		snd_rme9652_rdonly_switch_set,
	private_value:	2,
};

static int
snd_rme9652_get_tc_valid (void *private_data,
			  snd_kswitch_t * kswitch,
			  snd_switch_t * uswitch)

{
	rme9652_t *rme9652 = (rme9652_t *) kswitch->private_data;

	uswitch->type = SND_SW_TYPE_BOOLEAN;
	uswitch->value.enable = (unsigned int) 
		(readl (rme9652->iobase + RME9652_status_register) & 
		 RME9652_tc_valid) ? 1 : 0;
	return 0;
}

static snd_kswitch_t snd_rme9652_tc_valid_switch = {
	name:	"timecode valid",
	get:	snd_rme9652_get_tc_valid,
	set:	snd_rme9652_rdonly_switch_set,
};
	
#if ALSA_HAS_STANDARD_WAY_OF_RETURNING_TIMECODE

static int snd_rme9652_get_tc_value (void *private_data,
				     snd_kswitch_t * kswitch,
				     snd_switch_t * uswitch)

{
	rme9652_t *s = (rme9652_t *) private_data;
	u32 value;
	int i;

	uswitch->type = SND_SW_TYPE_DWORD;

	if ((readl (s->iobase + RME9652_status_register) & 
	     RME9652_tc_valid) == 0) {
		uswitch->value.data32[0] = 0;
		return 0;
	}
	
	/* timecode request */
	
	writel (0, s->iobase + RME9652_time_code);
	
	/* XXX bug alert: loop-based timing !!!! */
	
	for (i = 0; i < 50; i++) {
		if (!(readl(s->iobase +i)&RME9652_tc_busy))
			break;
	}
	
	if (!(readl(s->iobase +i)&RME9652_tc_busy)) {
		return -EIO;
	}

	value = 0;
	
	for (i = 0; i < 32; i++) {
		value >>= 1;
		
		if (readl (s->iobase+i) & RME9652_tc_out)
			value |= 0x80000000; 
	}
	
	if (value > 2*60*48000) {
		value -= 2*60*48000;
	} else {
		value = 0;
	}

	uswitch->value.data32[0] = value;

	return 0;
}

#endif /* ALSA_HAS_STANDARD_WAY_OF_RETURNING_TIMECODE */

int 
snd_rme9652_create_switches (snd_card_t *card, rme9652_t *rme9652)

{
	int i;

	if (snd_control_switch_new (card, &snd_rme9652_rate_switch, 
				    rme9652) == 0) {
		return -1;
	}
	if (snd_control_switch_new (card, &snd_rme9652_spdif_rate_switch, 
				    rme9652) == 0) {
		return -1;
	}

	if (snd_control_switch_new (card, &snd_rme9652_spdif_input_switch, 
				    rme9652) == 0) {
		return -1;
	}

	if (snd_control_switch_new (card, &snd_rme9652_spdif_output_switch, 
				    rme9652) == 0) {
		return -1;
	}

	if (snd_control_switch_new (card, &snd_rme9652_sync_mode_switch, 
				    rme9652) == 0) {
		return -1;
	}

	if (snd_control_switch_new (card, &snd_rme9652_preferred_sync_switch, 
				    rme9652) == 0) {
		return -1;
	}

	if (snd_control_switch_new (card, &snd_rme9652_all_thru_switch, 
				    rme9652) == 0) {
		return -1;
	}

	if (snd_control_switch_new (card, &snd_rme9652_latency_switch, 
				    rme9652) == 0) {
		return -1;
	}

	for (i = 0; i < rme9652->nchannels; i++) {

		sprintf (snd_rme9652_thru_switch.name, 
			 "thru %d", i+1);
		snd_rme9652_thru_switch.private_value = i;

		if (snd_control_switch_new (card,
					    &snd_rme9652_thru_switch, 
					    rme9652) == 0) {
			return -1;
		}
	}

	if (snd_control_switch_new (card, &snd_rme9652_adat1_sync_check_switch,
				    rme9652) == 0) {
		return -1;
	}

	if (snd_control_switch_new (card, &snd_rme9652_adat2_sync_check_switch,
				    rme9652) == 0) {
		return -1;
	}

	if (snd_control_switch_new (card, &snd_rme9652_adat3_sync_check_switch,
				    rme9652) == 0) {
		return -1;
	}

	if (snd_control_switch_new (card, &snd_rme9652_tc_valid_switch,
				    rme9652) == 0) {
		return -1;
	}

	return 0;
}

static unsigned int snd_rme9652_playback_pointer (void *private_data,
						  snd_pcm_subchn_t * subchn)
	
{
	rme9652_t *rme9652 = (rme9652_t *) private_data;
	rme9652_update_hw_offset (rme9652);
	return rme9652->hw_offset;
}

static unsigned int snd_rme9652_capture_pointer (void *private_data,
						 snd_pcm_subchn_t * subchn)
	
{
	rme9652_t *rme9652 = (rme9652_t *) private_data;
	rme9652_update_hw_offset (rme9652);
	return rme9652->hw_offset;
}

static int snd_rme9652_handle_parameter_change (rme9652_t * rme9652, 
						snd_pcm_subchn_t * subchn)
{
	snd_pcm_runtime_t *runtime = subchn->runtime;
	unsigned int size_from_params;

	/* we have to catch fragment size changes and verify that
	   they match the values set through the switch interface,
	   which for a tied-subchannel device is considered the
	   only means of changing them.
	*/

	if (runtime->format.rate != rme9652_adat_sample_rate (rme9652)) {
		printk (KERN_ERR 
			    "%s: sample rate %d doesn't match %d\n",
			rme9652->card_name,
			runtime->format.rate, 
			rme9652_adat_sample_rate (rme9652));			
		return -EINVAL;
	}

	if (runtime->format.voices != 1) {
		printk (KERN_ERR 
			"%s: voices cannot be %d\n", 
			rme9652->card_name,
			runtime->format.voices);
		return -EINVAL;
	}
	
	if (runtime->mode == SND_PCM_MODE_STREAM) {
		size_from_params = runtime->buf.stream.queue_size;

	} else {
		if (runtime->frags != 2) {
			printk (KERN_ERR 
				"%s: must specify 2 fragments\n",
				rme9652->card_name);
			return -EINVAL;
		}

		size_from_params = runtime->buf.block.frag_size;
	}

	if (size_from_params != rme9652->fragment_bytes) {
		printk (KERN_ERR 
			"%s: mismatched fragment/queue size (%d != %d)\n",
			rme9652->card_name,
			size_from_params, 
			rme9652->fragment_bytes);
		return -EINVAL;
	}

	return 0;
}

static int snd_rme9652_ioctl_mmap_size (rme9652_t *rme9652, 
					snd_pcm_subchn_t *subchn, 
					unsigned long *arg)

{
	long *size = (long *) arg;

	/* always map the entire buffer so that if the latency is 
	   changed, there is no need to unmap and remap it.
	*/
	
	*size = RME9652_CHANNEL_BUFFER_BYTES;
	return 0;
}

static int snd_rme9652_playback_ioctl (void *private_data,
				       snd_pcm_subchn_t * subchn,
				       unsigned int cmd,
				       unsigned long *arg)

{
	int result;

	switch (cmd) {
	case SND_PCM_IOCTL1_MMAP_SIZE:
		return snd_rme9652_ioctl_mmap_size 
			((rme9652_t *) private_data, subchn, arg);
	}

	result = snd_pcm_lib_ioctl(private_data, subchn, cmd, arg);

	if (result < 0)
		return result;
	if (cmd == SND_PCM_IOCTL1_PARAMS) {
		return snd_rme9652_handle_parameter_change
			((rme9652_t *) private_data, subchn);
	} else if (cmd == SND_PCM_IOCTL1_SETUP) {
		snd_pcm_channel_setup_t *setup = (snd_pcm_channel_setup_t *)arg;
		setup->msbits_per_sample = 24;
	}
	return 0;
}

static int snd_rme9652_playback_open (void *private_data,
				      snd_pcm_subchn_t * subchn)
{
	rme9652_t *rme9652 = (rme9652_t *) private_data;
	unsigned long flags;

	if (subchn->number % 2) {
		if (rme9652_adat_sample_rate (rme9652) > 48000) {
			return -ENODEV;
		}
	}

	rme9652->playback_dma_areas[subchn->number]->buf = 
		rme9652_buffer_location (rme9652,
					 SND_PCM_CHANNEL_PLAYBACK,
					 subchn->number);

	if (snd_pcm_dma_setup (subchn, 
			       rme9652->playback_dma_areas[subchn->number]) < 0) {
		return -EIO;
	}
	
	snd_pcm_set_sync (subchn);
	
	spin_lock_irqsave (&rme9652->lock, flags);

	if (rme9652->playback_open_mask == 0) {
		rme9652->master_playback_subchn = subchn;
	}

	rme9652->playback_open_mask |= (1<<subchn->number);
	subchn->runtime->hw = &snd_rme9652_playback_subchannel_info;

	spin_unlock_irqrestore (&rme9652->lock, flags);

	return 0;
}

static int snd_rme9652_playback_release (void *private_data,
				       snd_pcm_subchn_t * subchn)

{
	rme9652_t *rme9652 = (rme9652_t *) private_data;
	unsigned long flags;

	spin_lock_irqsave (&rme9652->lock, flags);
	rme9652->playback_open_mask &= ~(1<<subchn->number);

	if (rme9652->master_playback_subchn == subchn) {
		rme9652->master_playback_subchn = NULL;
	}
	spin_unlock_irqrestore (&rme9652->lock, flags);
	return 0;
}

static int snd_rme9652_capture_ioctl (void *private_data,
				       snd_pcm_subchn_t * subchn,
				       unsigned int cmd,
				       unsigned long *arg)

{
	int result;

	switch (cmd) {
	case SND_PCM_IOCTL1_MMAP_SIZE:
		return snd_rme9652_ioctl_mmap_size 
			((rme9652_t *) private_data, subchn, arg);
	}

	result = snd_pcm_lib_ioctl(private_data, subchn, cmd, arg);
	if (result < 0)
		return result;
	if (cmd == SND_PCM_IOCTL1_PARAMS) {
		return snd_rme9652_handle_parameter_change
			((rme9652_t *)private_data, subchn);
	} else if (cmd == SND_PCM_IOCTL1_SETUP) {
		snd_pcm_channel_setup_t *setup = (snd_pcm_channel_setup_t *)arg;
		setup->msbits_per_sample = 24;
	}
	return 0;
}

static int snd_rme9652_capture_open (void *private_data,
				     snd_pcm_subchn_t * subchn)
{
	rme9652_t *rme9652 = (rme9652_t *) private_data;
	unsigned long flags;

	if (subchn->number % 2) {
		if (rme9652_adat_sample_rate (rme9652) > 48000) {
			return -ENODEV;
		}
	}

	rme9652->capture_dma_areas[subchn->number]->buf = 
		rme9652_buffer_location (rme9652,
					 SND_PCM_CHANNEL_CAPTURE,
					 subchn->number);

	if (snd_pcm_dma_setup (subchn, 
			       rme9652->capture_dma_areas[subchn->number]) < 0) {
		return -EIO;
	}

	snd_pcm_set_sync (subchn);

	spin_lock_irqsave (&rme9652->lock, flags);

	if (rme9652->capture_open_mask == 0) {
		rme9652->master_capture_subchn = subchn;
	}

	rme9652->capture_open_mask |= (1<<subchn->number);

	spin_unlock_irqrestore (&rme9652->lock, flags);

	subchn->runtime->hw = &snd_rme9652_capture_subchannel_info;

	return 0;
}

static int snd_rme9652_capture_release (void *private_data,
					snd_pcm_subchn_t * subchn)

{
	rme9652_t *rme9652 = (rme9652_t *) private_data;
	unsigned long flags;

	spin_lock_irqsave (&rme9652->lock, flags);
	rme9652->capture_open_mask &= ~(1<<subchn->number);

	if (rme9652->master_capture_subchn == subchn) {
		rme9652->master_capture_subchn = NULL;
	}

	spin_unlock_irqrestore (&rme9652->lock, flags);
	return 0;
}	


static int snd_rme9652_mark_all_subchns (rme9652_t *rme9652, int status)

{
	snd_pcm_subchn_t * subchn;
	unsigned long flags;

	subchn = rme9652->pcm->chn[SND_PCM_CHANNEL_CAPTURE].subchn;
	while (subchn) {
		if (subchn->runtime) {
			spin_lock_irqsave(&subchn->runtime->lock, flags);
			*subchn->runtime->status = status;
			spin_unlock_irqrestore(&subchn->runtime->lock, flags);
		}
		subchn = subchn->next;
	}

	subchn = rme9652->pcm->chn[SND_PCM_CHANNEL_PLAYBACK].subchn;
	while (subchn) {
		if (subchn->runtime) {
			spin_lock_irqsave(&subchn->runtime->lock, flags);
			*subchn->runtime->status = status;
			spin_unlock_irqrestore(&subchn->runtime->lock, flags);
		}
		subchn = subchn->next;
	}

	return 0;
}

static int 
rme9652_trigger (rme9652_t * rme9652, snd_pcm_subchn_t * subchn, 
		 int chn, int cmd)

{
	switch (cmd) {
	case SND_PCM_TRIGGER_SYNC_GO:
	case SND_PCM_TRIGGER_GO:
		if (!rme9652_running (rme9652)) {
			if (chn == SND_PCM_CHANNEL_CAPTURE) {
				if (subchn != rme9652->master_capture_subchn) {
					return -EBUSY;
				}
			} else {
				if (subchn != rme9652->master_playback_subchn) {
					return -EBUSY;
				}
			}

			rme9652_start_card (rme9652, 0);
		}
		/* else: already running, everything is OK */
		break;

	case SND_PCM_TRIGGER_STOP:
		if (rme9652_running (rme9652)) {
			if (chn == SND_PCM_CHANNEL_CAPTURE) {
				if (subchn != rme9652->master_capture_subchn) {
					return -EBUSY;
				}
			} else {
				if (subchn != rme9652->master_playback_subchn) {
					return -EBUSY;
				}
			}
			rme9652_stop_card (rme9652);
		}
		/* else: not running, everything is OK */
		break;

	case SND_PCM_TRIGGER_PAUSE_PUSH:
		rme9652_stop_card (rme9652);
		snd_rme9652_mark_all_subchns (rme9652, SND_PCM_STATUS_PAUSED);
		break;

	case SND_PCM_TRIGGER_PAUSE_RELEASE:
		rme9652_start_card (rme9652, 1);
		snd_rme9652_mark_all_subchns (rme9652, SND_PCM_STATUS_RUNNING);
		break;
		
	default:
		return -EINVAL;
	}

	return 0;
}

static int snd_rme9652_capture_trigger (void * ptr,
					snd_pcm_subchn_t * subchn, 
					int cmd)

{
	return rme9652_trigger ((rme9652_t *) ptr, subchn, 
				SND_PCM_CHANNEL_CAPTURE,
				cmd);
}

static int snd_rme9652_playback_trigger (void * ptr,
					snd_pcm_subchn_t * subchn, 
					int cmd)

{
	return rme9652_trigger ((rme9652_t *) ptr, subchn, 
				SND_PCM_CHANNEL_PLAYBACK,
				cmd);
}

static int snd_rme9652_playback_prepare (void * private_data,
					 snd_pcm_subchn_t *subchn)
	
{
	rme9652_prepare_card ((rme9652_t *) private_data);
	return 0;
}

static int snd_rme9652_capture_prepare (void * private_data,
					snd_pcm_subchn_t *subchn)

{
	rme9652_prepare_card ((rme9652_t *) private_data);
	return 0;
}

#if 0
static long
snd_rme9652_capture_read (void *private_data,
			  snd_pcm_subchn_t *subchn,
			  const char *buf,
			  long count)

{
	rme9652_t *rme9652 = (rme9652_t *) private_data;
	int sr;

	if (subchn->number == rme9652->nchannels - 1 ||
	    subchn->number == rme9652->nchannels - 2) {

		/* S/PDIF channel */

		sr = rme9652_spdif_sample_rate (rme9652);

		if (rme9652->last_spdif_sample_rate > 0 &&
		    rme9652->last_spdif_sample_rate != sr) {
			rme9652->last_spdif_sample_rate = sr;
			return -EBADFD;
		}

		rme9652->last_spdif_sample_rate = sr;

	} else {

		/* ADAT channel */

		sr = rme9652_adat_sample_rate (rme9652);

		if (sr > 48000) {
			if (subchn->number % 2) {
				
				/* Odd channels are not available in
				   "double speed mode"
				*/

				return -ENODEV;
			}
		}

		if (rme9652->last_adat_sample_rate > 0 &&
		    rme9652->last_adat_sample_rate != sr) {
			rme9652->last_adat_sample_rate = sr;
			return -EBADFD;
		}

		rme9652->last_adat_sample_rate = sr;

	}

	return snd_pcm_lib_read (subchn, buf, count);
}

static long
snd_rme9652_playback_write (void *private_data,
			    snd_pcm_subchn_t *subchn,
			    const char *buf,
			    long count)

{
	rme9652_t *rme9652 = (rme9652_t *) private_data;

	if (subchn->number < rme9652->nchannels - 2) {

		/* ADAT channel */

		if (rme9652_adat_sample_rate (rme9652) > 48000) {
			if (subchn->number % 2) {
				
				/* Odd channels are not available in
				   "double speed mode"
				*/

				return -ENODEV;
			}
		}
	}

	return snd_pcm_lib_write (subchn, buf, count);
}
#endif

/*------------------------------------------------------------
   /proc interface 
 ------------------------------------------------------------*/

static void 
snd_rme9652_proc_read (snd_info_buffer_t * buffer,
		       void *private_data)
{
	rme9652_t *rme9652 = (rme9652_t *) private_data;
	u32 thru_bits = rme9652->thru_bits;
	int show_auto_sync_source = 0;
	int i;
	unsigned int status;
	int x;

	status = readl (rme9652->iobase + RME9652_status_register);

	if (rme9652->card->type == SND_CARD_TYPE_HAMMERFALL) {
		snd_iprintf(buffer, "RME Digi9652 (Hammerfall)");
	} else {
		snd_iprintf(buffer, "RME Digi9652 (Hammerfall/Light)");
	}

	snd_iprintf (buffer, " #%d\n", rme9652->card->number + 1);
	snd_iprintf (buffer, "Buffers: capture 0x%x playback 0x%x\n",
		     rme9652->capture_buffer,
		     rme9652->playback_buffer);
	snd_iprintf (buffer, "IRQ: %d Registers bus: 0x%x VM: 0x%x\n",
		     rme9652->irqptr->irq,
		     rme9652->port,
		     rme9652->iobase);

	snd_iprintf (buffer, "\n");

	switch (rme9652_decode_latency 
		(rme9652->control_register & RME9652_latency)) {
	case 0: x = 64; break;
	case 1: x = 128; break;
	case 2: x = 256; break;
	case 3: x = 512; break;
	case 4: x = 1024; break;
	case 5: x = 2048; break;
	case 6: x = 2096; break;
	case 7: x = 8192; break;
	default: x = 0; break;
	}

	snd_iprintf (buffer, "Latency: %d samples (2 fragments of %d bytes)\n",
		     x, rme9652->fragment_bytes);
	rme9652_update_hw_offset (rme9652);
	snd_iprintf (buffer, "Hardware pointer: %d\n", rme9652->hw_offset);


	if ((rme9652->control_register & (RME9652_Master|RME9652_wsel)) == 0) {
		snd_iprintf (buffer, "Clock mode: slave/auto\n");
		show_auto_sync_source = 1;
	} else if (rme9652->control_register & RME9652_wsel) {
		snd_iprintf (buffer, "Clock mode: word clock\n");
	} else {
		snd_iprintf (buffer, "Clock mode: master\n");
	}
	
	if (show_auto_sync_source) {
		switch (rme9652_decode_sync_src (rme9652->control_register & RME9652_SyncRef)){
		case 0:
			snd_iprintf (buffer, "Pref. sync source: ADAT1\n");
			break;
		case 1:
			snd_iprintf (buffer, "Pref. sync source: ADAT2\n");
			break;
		case 2:
			snd_iprintf (buffer, "Pref. sync source: ADAT3\n");
			break;
		case 3:
			snd_iprintf (buffer, "Pref. sync source: S/PDIF\n");
			break;
		default:
			snd_iprintf (buffer, "Pref. sync source: ???\n");
		} 
	}

	switch (rme9652_decode_spdif_in (rme9652->control_register & RME9652_inp)) {
	case 0:
		snd_iprintf (buffer, "S/PDIF input: ADAT1\n");
		break;
	case 1:
		snd_iprintf (buffer, "S/PDIF input: Coaxial (Phono)\n");
		break;
	case 2:
		snd_iprintf (buffer, "S/PDIF input: CD\n");
		break;
	default:
		snd_iprintf (buffer, "S/PDIF input: ???\n");
		break;
	}

	if (rme9652->control_register & RME9652_opt_out) {
		snd_iprintf (buffer, "S/PDIF output: Coaxial & ADAT1\n");
	} else {
		snd_iprintf (buffer, "S/PDIF output: Coaxial only\n");
	}

	if (rme9652->control_register & RME9652_PRO) {
		snd_iprintf (buffer, "S/PDIF quality: Professional\n");
	} else {
		snd_iprintf (buffer, "S/PDIF quality: Consumer\n");
	}

	if (rme9652->control_register & RME9652_EMP) {
		snd_iprintf (buffer, "S/PDIF emphasis: on\n");
	} else {
		snd_iprintf (buffer, "S/PDIF emphasis: off\n");
	}

	if (rme9652->control_register & RME9652_Dolby) {
		snd_iprintf (buffer, "S/PDIF Dolby: on\n");
	} else {
		snd_iprintf (buffer, "S/PDIF Dolby: off\n");
	}

	i = rme9652_spdif_sample_rate (rme9652);

	if (i < 0) {
		snd_iprintf (buffer, "S/PDIF sample rate: error flag set\n");
	} else if (i == 0) {
		snd_iprintf (buffer, "S/PDIF sample rate: undetermined\n");
	} else {
		snd_iprintf (buffer, "S/PDIF sample rate: %d\n", i);
	}

	snd_iprintf (buffer, "\n");

	snd_iprintf (buffer, "ADAT Sample rate: %dHz\n",
		     rme9652_adat_sample_rate (rme9652));

	/* Sync Check */

	x = status & RME9652_sync_0;
	if (status & RME9652_lock_0) {
		snd_iprintf (buffer, "ADAT1: %s\n",
			x ? "Sync" : "Lock"); 
	} else {
		snd_iprintf (buffer, "ADAT1: No Lock\n");
	}

	x = status & RME9652_sync_1;
	if (status & RME9652_lock_1) {
		snd_iprintf (buffer, "ADAT2: %s signal\n",
			x ? "Sync" : "Lock"); 
	} else {
		snd_iprintf (buffer, "ADAT2: No Lock\n");
	}

	x = status & RME9652_sync_2;
	if (status & RME9652_lock_2) {
		snd_iprintf (buffer, "ADAT3: %s signal\n",
			x ? "Sync" : "Lock"); 
	} else {
		snd_iprintf (buffer, "ADAT3: No Lock\n");
	}

	switch (rme9652_decode_sync_src 
		(rme9652->control_register & RME9652_SyncRef)) {
	case 0:
		snd_iprintf (buffer, "Preferred sync src: ADAT1\n");
		break;
	case 1:
		snd_iprintf (buffer, "Preferred sync src: ADAT2\n");
		break;
	case 2:
		snd_iprintf (buffer, "Preferred sync src: ADAT3\n");
		break;
	case 3:
		snd_iprintf (buffer, "Preferred sync src: S/PDIF\n");
		break;
	}
	
	snd_iprintf (buffer, "Timecode signal: %s\n",
		     (status & RME9652_tc_valid) ? "yes" : "no");

	/* thru modes */

	snd_iprintf (buffer, "Punch Status:\n\n");

	for (i = 0; i < rme9652->nchannels; i++) {
		if (thru_bits & (1<<i)) {
			snd_iprintf (buffer, "%2d:  on ", i+1);
		} else {
			snd_iprintf (buffer, "%2d: off ", i+1);
		}

		if (((i+1) % 8) == 0) {
			snd_iprintf (buffer, "\n");
		}
	}

	snd_iprintf (buffer, "\n");
}

static void __init 
snd_rme9652_proc_init (rme9652_t * rme9652)
{
	snd_info_entry_t *entry;

	if ((entry = snd_info_create_entry(rme9652->card, "RME9652")) != NULL) {
		entry->private_data = rme9652;
		entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
		entry->t.text.read_size = 256;
		entry->t.text.read = snd_rme9652_proc_read;
		if (snd_info_register(entry) < 0) {
			snd_info_free_entry(entry);
			entry = NULL;
		}
	}
	rme9652->proc_entry = entry;
}

static void snd_rme9652_proc_done(rme9652_t * rme9652)
{
	if (rme9652->proc_entry) {
		snd_info_unregister (rme9652->proc_entry);
		rme9652->proc_entry = NULL;
	}
}

EXPORT_SYMBOL(snd_rme9652_free);
EXPORT_SYMBOL(snd_rme9652_create);
EXPORT_SYMBOL(snd_rme9652_interrupt);

static int __init alsa_rme9652_init(void)
{
	return 0;
}

static void __exit alsa_rme9652_exit(void)
{
}

module_init(alsa_rme9652_init)
module_exit(alsa_rme9652_exit)


