| 
									
										
										
										
											2011-05-03 16:14:46 +02:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  *  Support for Digigram Lola PCI-e boards | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *  Copyright (c) 2011 Takashi Iwai <tiwai@suse.de> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *  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., 59 | 
					
						
							|  |  |  |  *  Temple Place - Suite 330, Boston, MA  02111-1307, USA. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/kernel.h>
 | 
					
						
							|  |  |  | #include <linux/init.h>
 | 
					
						
							|  |  |  | #include <linux/dma-mapping.h>
 | 
					
						
							|  |  |  | #include <linux/pci.h>
 | 
					
						
							| 
									
										
										
										
											2011-05-03 18:21:01 +02:00
										 |  |  | #include <linux/delay.h>
 | 
					
						
							| 
									
										
										
										
											2011-05-03 16:14:46 +02:00
										 |  |  | #include <sound/core.h>
 | 
					
						
							|  |  |  | #include <sound/pcm.h>
 | 
					
						
							|  |  |  | #include "lola.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-05-03 16:41:02 +02:00
										 |  |  | #define LOLA_MAX_BDL_ENTRIES	8
 | 
					
						
							| 
									
										
										
										
											2011-05-03 16:14:46 +02:00
										 |  |  | #define LOLA_MAX_BUF_SIZE	(1024*1024*1024)
 | 
					
						
							| 
									
										
										
										
											2011-05-03 16:41:02 +02:00
										 |  |  | #define LOLA_BDL_ENTRY_SIZE	(16 * 16)
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-05-03 16:14:46 +02:00
										 |  |  | static struct lola_pcm *lola_get_pcm(struct snd_pcm_substream *substream) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct lola *chip = snd_pcm_substream_chip(substream); | 
					
						
							|  |  |  | 	return &chip->pcm[substream->stream]; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct lola_stream *lola_get_stream(struct snd_pcm_substream *substream) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct lola_pcm *pcm = lola_get_pcm(substream); | 
					
						
							|  |  |  | 	unsigned int idx = substream->number; | 
					
						
							|  |  |  | 	return &pcm->streams[idx]; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static unsigned int lola_get_lrc(struct lola *chip) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return lola_readl(chip, BAR1, LRC); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static unsigned int lola_get_tstamp(struct lola *chip, bool quick_no_sync) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	unsigned int tstamp = lola_get_lrc(chip) >> 8; | 
					
						
							|  |  |  | 	if (chip->granularity) { | 
					
						
							|  |  |  | 		unsigned int wait_banks = quick_no_sync ? 0 : 8; | 
					
						
							|  |  |  | 		tstamp += (wait_banks + 1) * chip->granularity - 1; | 
					
						
							|  |  |  | 		tstamp -= tstamp % chip->granularity; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return tstamp << 8; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* clear any pending interrupt status */ | 
					
						
							|  |  |  | static void lola_stream_clear_pending_irq(struct lola *chip, | 
					
						
							|  |  |  | 					  struct lola_stream *str) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	unsigned int val = lola_dsd_read(chip, str->dsd, STS); | 
					
						
							|  |  |  | 	val &= LOLA_DSD_STS_DESE | LOLA_DSD_STS_BCIS; | 
					
						
							|  |  |  | 	if (val) | 
					
						
							|  |  |  | 		lola_dsd_write(chip, str->dsd, STS, val); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void lola_stream_start(struct lola *chip, struct lola_stream *str, | 
					
						
							|  |  |  | 			      unsigned int tstamp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	lola_stream_clear_pending_irq(chip, str); | 
					
						
							|  |  |  | 	lola_dsd_write(chip, str->dsd, CTL, | 
					
						
							|  |  |  | 		       LOLA_DSD_CTL_SRUN | | 
					
						
							|  |  |  | 		       LOLA_DSD_CTL_IOCE | | 
					
						
							|  |  |  | 		       LOLA_DSD_CTL_DEIE | | 
					
						
							|  |  |  | 		       LOLA_DSD_CTL_VLRCV | | 
					
						
							|  |  |  | 		       tstamp); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void lola_stream_stop(struct lola *chip, struct lola_stream *str, | 
					
						
							|  |  |  | 			     unsigned int tstamp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	lola_dsd_write(chip, str->dsd, CTL, | 
					
						
							|  |  |  | 		       LOLA_DSD_CTL_IOCE | | 
					
						
							|  |  |  | 		       LOLA_DSD_CTL_DEIE | | 
					
						
							|  |  |  | 		       LOLA_DSD_CTL_VLRCV | | 
					
						
							|  |  |  | 		       tstamp); | 
					
						
							|  |  |  | 	lola_stream_clear_pending_irq(chip, str); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void wait_for_srst_clear(struct lola *chip, struct lola_stream *str) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2011-05-03 16:41:02 +02:00
										 |  |  | 	unsigned long end_time = jiffies + msecs_to_jiffies(200); | 
					
						
							| 
									
										
										
										
											2011-05-03 16:14:46 +02:00
										 |  |  | 	while (time_before(jiffies, end_time)) { | 
					
						
							|  |  |  | 		unsigned int val; | 
					
						
							|  |  |  | 		val = lola_dsd_read(chip, str->dsd, CTL); | 
					
						
							|  |  |  | 		if (!(val & LOLA_DSD_CTL_SRST)) | 
					
						
							|  |  |  | 			return; | 
					
						
							|  |  |  | 		msleep(1); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	printk(KERN_WARNING SFX "SRST not clear (stream %d)\n", str->dsd); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-05-03 16:47:03 +02:00
										 |  |  | static int lola_stream_wait_for_fifo(struct lola *chip, | 
					
						
							|  |  |  | 				     struct lola_stream *str, | 
					
						
							|  |  |  | 				     bool ready) | 
					
						
							| 
									
										
										
										
											2011-05-03 16:14:46 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2011-05-03 16:47:03 +02:00
										 |  |  | 	unsigned int val = ready ? LOLA_DSD_STS_FIFORDY : 0; | 
					
						
							|  |  |  | 	unsigned long end_time = jiffies + msecs_to_jiffies(200); | 
					
						
							| 
									
										
										
										
											2011-05-03 16:14:46 +02:00
										 |  |  | 	while (time_before(jiffies, end_time)) { | 
					
						
							| 
									
										
										
										
											2011-05-03 16:47:03 +02:00
										 |  |  | 		unsigned int reg = lola_dsd_read(chip, str->dsd, STS); | 
					
						
							|  |  |  | 		if ((reg & LOLA_DSD_STS_FIFORDY) == val) | 
					
						
							| 
									
										
										
										
											2011-05-03 16:14:46 +02:00
										 |  |  | 			return 0; | 
					
						
							|  |  |  | 		msleep(1); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	printk(KERN_WARNING SFX "FIFO not ready (stream %d)\n", str->dsd); | 
					
						
							|  |  |  | 	return -EIO; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-05-03 17:02:35 +02:00
										 |  |  | /* sync for FIFO ready/empty for all linked streams;
 | 
					
						
							|  |  |  |  * clear paused flag when FIFO gets ready again | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int lola_sync_wait_for_fifo(struct lola *chip, | 
					
						
							|  |  |  | 				   struct snd_pcm_substream *substream, | 
					
						
							|  |  |  | 				   bool ready) | 
					
						
							| 
									
										
										
										
											2011-05-03 16:47:03 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2011-05-03 17:02:35 +02:00
										 |  |  | 	unsigned int val = ready ? LOLA_DSD_STS_FIFORDY : 0; | 
					
						
							|  |  |  | 	unsigned long end_time = jiffies + msecs_to_jiffies(200); | 
					
						
							|  |  |  | 	struct snd_pcm_substream *s; | 
					
						
							|  |  |  | 	int pending = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	while (time_before(jiffies, end_time)) { | 
					
						
							|  |  |  | 		pending = 0; | 
					
						
							|  |  |  | 		snd_pcm_group_for_each_entry(s, substream) { | 
					
						
							|  |  |  | 			struct lola_stream *str; | 
					
						
							|  |  |  | 			if (s->pcm->card != substream->pcm->card) | 
					
						
							|  |  |  | 				continue; | 
					
						
							|  |  |  | 			str = lola_get_stream(s); | 
					
						
							|  |  |  | 			if (str->prepared && str->paused) { | 
					
						
							|  |  |  | 				unsigned int reg; | 
					
						
							|  |  |  | 				reg = lola_dsd_read(chip, str->dsd, STS); | 
					
						
							|  |  |  | 				if ((reg & LOLA_DSD_STS_FIFORDY) != val) { | 
					
						
							|  |  |  | 					pending = str->dsd + 1; | 
					
						
							|  |  |  | 					break; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if (ready) | 
					
						
							|  |  |  | 					str->paused = 0; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (!pending) | 
					
						
							|  |  |  | 			return 0; | 
					
						
							|  |  |  | 		msleep(1); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	printk(KERN_WARNING SFX "FIFO not ready (pending %d)\n", pending - 1); | 
					
						
							|  |  |  | 	return -EIO; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* finish pause - prepare for a new resume */ | 
					
						
							|  |  |  | static void lola_sync_pause(struct lola *chip, | 
					
						
							|  |  |  | 			    struct snd_pcm_substream *substream) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct snd_pcm_substream *s; | 
					
						
							| 
									
										
										
										
											2011-05-03 16:47:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-05-03 17:02:35 +02:00
										 |  |  | 	lola_sync_wait_for_fifo(chip, substream, false); | 
					
						
							|  |  |  | 	snd_pcm_group_for_each_entry(s, substream) { | 
					
						
							|  |  |  | 		struct lola_stream *str; | 
					
						
							|  |  |  | 		if (s->pcm->card != substream->pcm->card) | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 		str = lola_get_stream(s); | 
					
						
							|  |  |  | 		if (str->paused && str->prepared) | 
					
						
							| 
									
										
										
										
											2011-05-03 16:59:27 +02:00
										 |  |  | 			lola_dsd_write(chip, str->dsd, CTL, LOLA_DSD_CTL_SRUN | | 
					
						
							|  |  |  | 				       LOLA_DSD_CTL_IOCE | LOLA_DSD_CTL_DEIE); | 
					
						
							| 
									
										
										
										
											2011-05-03 17:02:35 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	lola_sync_wait_for_fifo(chip, substream, true); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void lola_stream_reset(struct lola *chip, struct lola_stream *str) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (str->prepared) { | 
					
						
							|  |  |  | 		if (str->paused) | 
					
						
							|  |  |  | 			lola_sync_pause(chip, str->substream); | 
					
						
							|  |  |  | 		str->prepared = 0; | 
					
						
							| 
									
										
										
										
											2011-05-03 16:47:03 +02:00
										 |  |  | 		lola_dsd_write(chip, str->dsd, CTL, | 
					
						
							|  |  |  | 			       LOLA_DSD_CTL_IOCE | LOLA_DSD_CTL_DEIE); | 
					
						
							|  |  |  | 		lola_stream_wait_for_fifo(chip, str, false); | 
					
						
							|  |  |  | 		lola_stream_clear_pending_irq(chip, str); | 
					
						
							|  |  |  | 		lola_dsd_write(chip, str->dsd, CTL, LOLA_DSD_CTL_SRST); | 
					
						
							|  |  |  | 		lola_dsd_write(chip, str->dsd, LVI, 0); | 
					
						
							|  |  |  | 		lola_dsd_write(chip, str->dsd, BDPU, 0); | 
					
						
							|  |  |  | 		lola_dsd_write(chip, str->dsd, BDPL, 0); | 
					
						
							|  |  |  | 		wait_for_srst_clear(chip, str); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-05-03 16:14:46 +02:00
										 |  |  | static struct snd_pcm_hardware lola_pcm_hw = { | 
					
						
							|  |  |  | 	.info =			(SNDRV_PCM_INFO_MMAP | | 
					
						
							|  |  |  | 				 SNDRV_PCM_INFO_INTERLEAVED | | 
					
						
							|  |  |  | 				 SNDRV_PCM_INFO_BLOCK_TRANSFER | | 
					
						
							|  |  |  | 				 SNDRV_PCM_INFO_MMAP_VALID | | 
					
						
							|  |  |  | 				 SNDRV_PCM_INFO_PAUSE), | 
					
						
							|  |  |  | 	.formats =		(SNDRV_PCM_FMTBIT_S16_LE | | 
					
						
							|  |  |  | 				 SNDRV_PCM_FMTBIT_S24_LE | | 
					
						
							|  |  |  | 				 SNDRV_PCM_FMTBIT_S32_LE | | 
					
						
							|  |  |  | 				 SNDRV_PCM_FMTBIT_FLOAT_LE), | 
					
						
							| 
									
										
										
										
											2011-05-03 16:47:03 +02:00
										 |  |  | 	.rates =		SNDRV_PCM_RATE_8000_192000, | 
					
						
							|  |  |  | 	.rate_min =		8000, | 
					
						
							|  |  |  | 	.rate_max =		192000, | 
					
						
							| 
									
										
										
										
											2011-05-03 16:14:46 +02:00
										 |  |  | 	.channels_min =		1, | 
					
						
							|  |  |  | 	.channels_max =		2, | 
					
						
							|  |  |  | 	.buffer_bytes_max =	LOLA_MAX_BUF_SIZE, | 
					
						
							|  |  |  | 	.period_bytes_min =	128, | 
					
						
							|  |  |  | 	.period_bytes_max =	LOLA_MAX_BUF_SIZE / 2, | 
					
						
							|  |  |  | 	.periods_min =		2, | 
					
						
							| 
									
										
										
										
											2011-05-03 16:47:03 +02:00
										 |  |  | 	.periods_max =		LOLA_MAX_BDL_ENTRIES, | 
					
						
							| 
									
										
										
										
											2011-05-03 16:14:46 +02:00
										 |  |  | 	.fifo_size =		0, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int lola_pcm_open(struct snd_pcm_substream *substream) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct lola *chip = snd_pcm_substream_chip(substream); | 
					
						
							|  |  |  | 	struct lola_pcm *pcm = lola_get_pcm(substream); | 
					
						
							|  |  |  | 	struct lola_stream *str = lola_get_stream(substream); | 
					
						
							|  |  |  | 	struct snd_pcm_runtime *runtime = substream->runtime; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mutex_lock(&chip->open_mutex); | 
					
						
							|  |  |  | 	if (str->opened) { | 
					
						
							|  |  |  | 		mutex_unlock(&chip->open_mutex); | 
					
						
							|  |  |  | 		return -EBUSY; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	str->substream = substream; | 
					
						
							|  |  |  | 	str->master = NULL; | 
					
						
							|  |  |  | 	str->opened = 1; | 
					
						
							|  |  |  | 	runtime->hw = lola_pcm_hw; | 
					
						
							|  |  |  | 	runtime->hw.channels_max = pcm->num_streams - str->index; | 
					
						
							| 
									
										
										
										
											2011-05-03 16:59:27 +02:00
										 |  |  | 	if (chip->sample_rate) { | 
					
						
							|  |  |  | 		/* sample rate is locked */ | 
					
						
							|  |  |  | 		runtime->hw.rate_min = chip->sample_rate; | 
					
						
							|  |  |  | 		runtime->hw.rate_max = chip->sample_rate; | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		runtime->hw.rate_min = chip->sample_rate_min; | 
					
						
							|  |  |  | 		runtime->hw.rate_max = chip->sample_rate_max; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	chip->ref_count_rate++; | 
					
						
							| 
									
										
										
										
											2011-05-03 16:14:46 +02:00
										 |  |  | 	snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); | 
					
						
							| 
									
										
										
										
											2011-05-03 16:59:27 +02:00
										 |  |  | 	/* period size = multiple of chip->granularity (8, 16 or 32 frames)*/ | 
					
						
							| 
									
										
										
										
											2011-05-03 16:47:03 +02:00
										 |  |  | 	snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, | 
					
						
							| 
									
										
										
										
											2011-05-03 16:51:56 +02:00
										 |  |  | 				   chip->granularity); | 
					
						
							| 
									
										
										
										
											2011-05-03 16:47:03 +02:00
										 |  |  | 	snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, | 
					
						
							| 
									
										
										
										
											2011-05-03 16:51:56 +02:00
										 |  |  | 				   chip->granularity); | 
					
						
							| 
									
										
										
										
											2011-05-03 16:14:46 +02:00
										 |  |  | 	mutex_unlock(&chip->open_mutex); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void lola_cleanup_slave_streams(struct lola_pcm *pcm, | 
					
						
							|  |  |  | 				       struct lola_stream *str) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 	for (i = str->index + 1; i < pcm->num_streams; i++) { | 
					
						
							|  |  |  | 		struct lola_stream *s = &pcm->streams[i]; | 
					
						
							|  |  |  | 		if (s->master != str) | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		s->master = NULL; | 
					
						
							|  |  |  | 		s->opened = 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int lola_pcm_close(struct snd_pcm_substream *substream) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct lola *chip = snd_pcm_substream_chip(substream); | 
					
						
							|  |  |  | 	struct lola_stream *str = lola_get_stream(substream); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mutex_lock(&chip->open_mutex); | 
					
						
							|  |  |  | 	if (str->substream == substream) { | 
					
						
							|  |  |  | 		str->substream = NULL; | 
					
						
							|  |  |  | 		str->opened = 0; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2011-05-03 16:59:27 +02:00
										 |  |  | 	if (--chip->ref_count_rate == 0) { | 
					
						
							|  |  |  | 		/* release sample rate */ | 
					
						
							|  |  |  | 		chip->sample_rate = 0; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2011-05-03 16:14:46 +02:00
										 |  |  | 	mutex_unlock(&chip->open_mutex); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int lola_pcm_hw_params(struct snd_pcm_substream *substream, | 
					
						
							|  |  |  | 			      struct snd_pcm_hw_params *hw_params) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct lola_stream *str = lola_get_stream(substream); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	str->bufsize = 0; | 
					
						
							|  |  |  | 	str->period_bytes = 0; | 
					
						
							|  |  |  | 	str->format_verb = 0; | 
					
						
							|  |  |  | 	return snd_pcm_lib_malloc_pages(substream, | 
					
						
							|  |  |  | 					params_buffer_bytes(hw_params)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int lola_pcm_hw_free(struct snd_pcm_substream *substream) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct lola *chip = snd_pcm_substream_chip(substream); | 
					
						
							|  |  |  | 	struct lola_pcm *pcm = lola_get_pcm(substream); | 
					
						
							|  |  |  | 	struct lola_stream *str = lola_get_stream(substream); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mutex_lock(&chip->open_mutex); | 
					
						
							|  |  |  | 	lola_stream_reset(chip, str); | 
					
						
							|  |  |  | 	lola_cleanup_slave_streams(pcm, str); | 
					
						
							|  |  |  | 	mutex_unlock(&chip->open_mutex); | 
					
						
							|  |  |  | 	return snd_pcm_lib_free_pages(substream); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * set up a BDL entry | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int setup_bdle(struct snd_pcm_substream *substream, | 
					
						
							|  |  |  | 		      struct lola_stream *str, u32 **bdlp, | 
					
						
							|  |  |  | 		      int ofs, int size) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u32 *bdl = *bdlp; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	while (size > 0) { | 
					
						
							|  |  |  | 		dma_addr_t addr; | 
					
						
							|  |  |  | 		int chunk; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (str->frags >= LOLA_MAX_BDL_ENTRIES) | 
					
						
							|  |  |  | 			return -EINVAL; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-05-03 16:50:51 +02:00
										 |  |  | 		addr = snd_pcm_sgbuf_get_addr(substream, ofs); | 
					
						
							| 
									
										
										
										
											2011-05-03 16:14:46 +02:00
										 |  |  | 		/* program the address field of the BDL entry */ | 
					
						
							|  |  |  | 		bdl[0] = cpu_to_le32((u32)addr); | 
					
						
							|  |  |  | 		bdl[1] = cpu_to_le32(upper_32_bits(addr)); | 
					
						
							|  |  |  | 		/* program the size field of the BDL entry */ | 
					
						
							| 
									
										
										
										
											2011-05-03 16:50:51 +02:00
										 |  |  | 		chunk = snd_pcm_sgbuf_get_chunk_size(substream, ofs, size); | 
					
						
							| 
									
										
										
										
											2011-05-03 16:14:46 +02:00
										 |  |  | 		bdl[2] = cpu_to_le32(chunk); | 
					
						
							|  |  |  | 		/* program the IOC to enable interrupt
 | 
					
						
							|  |  |  | 		 * only when the whole fragment is processed | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		size -= chunk; | 
					
						
							|  |  |  | 		bdl[3] = size ? 0 : cpu_to_le32(0x01); | 
					
						
							|  |  |  | 		bdl += 4; | 
					
						
							|  |  |  | 		str->frags++; | 
					
						
							|  |  |  | 		ofs += chunk; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	*bdlp = bdl; | 
					
						
							|  |  |  | 	return ofs; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * set up BDL entries | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2011-05-03 16:41:02 +02:00
										 |  |  | static int lola_setup_periods(struct lola *chip, struct lola_pcm *pcm, | 
					
						
							| 
									
										
										
										
											2011-05-03 16:14:46 +02:00
										 |  |  | 			      struct snd_pcm_substream *substream, | 
					
						
							|  |  |  | 			      struct lola_stream *str) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u32 *bdl; | 
					
						
							|  |  |  | 	int i, ofs, periods, period_bytes; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	period_bytes = str->period_bytes; | 
					
						
							|  |  |  | 	periods = str->bufsize / period_bytes; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* program the initial BDL entries */ | 
					
						
							| 
									
										
										
										
											2011-05-03 16:41:02 +02:00
										 |  |  | 	bdl = (u32 *)(pcm->bdl.area + LOLA_BDL_ENTRY_SIZE * str->index); | 
					
						
							| 
									
										
										
										
											2011-05-03 16:14:46 +02:00
										 |  |  | 	ofs = 0; | 
					
						
							|  |  |  | 	str->frags = 0; | 
					
						
							|  |  |  | 	for (i = 0; i < periods; i++) { | 
					
						
							|  |  |  | 		ofs = setup_bdle(substream, str, &bdl, ofs, period_bytes); | 
					
						
							|  |  |  | 		if (ofs < 0) | 
					
						
							|  |  |  | 			goto error; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |  error: | 
					
						
							|  |  |  | 	snd_printk(KERN_ERR SFX "Too many BDL entries: buffer=%d, period=%d\n", | 
					
						
							|  |  |  | 		   str->bufsize, period_bytes); | 
					
						
							|  |  |  | 	return -EINVAL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static unsigned int lola_get_format_verb(struct snd_pcm_substream *substream) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	unsigned int verb; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (substream->runtime->format) { | 
					
						
							|  |  |  | 	case SNDRV_PCM_FORMAT_S16_LE: | 
					
						
							|  |  |  | 		verb = 0x00000000; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case SNDRV_PCM_FORMAT_S24_LE: | 
					
						
							|  |  |  | 		verb = 0x00000200; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case SNDRV_PCM_FORMAT_S32_LE: | 
					
						
							|  |  |  | 		verb = 0x00000300; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case SNDRV_PCM_FORMAT_FLOAT_LE: | 
					
						
							|  |  |  | 		verb = 0x00001300; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	verb |= substream->runtime->channels; | 
					
						
							|  |  |  | 	return verb; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int lola_set_stream_config(struct lola *chip, | 
					
						
							|  |  |  | 				  struct lola_stream *str, | 
					
						
							|  |  |  | 				  int channels) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int i, err; | 
					
						
							|  |  |  | 	unsigned int verb, val; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* set format info for all channels
 | 
					
						
							|  |  |  | 	 * (with only one command for the first channel) | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	err = lola_codec_read(chip, str->nid, LOLA_VERB_SET_STREAM_FORMAT, | 
					
						
							|  |  |  | 			      str->format_verb, 0, &val, NULL); | 
					
						
							|  |  |  | 	if (err < 0) { | 
					
						
							|  |  |  | 		printk(KERN_ERR SFX "Cannot set stream format 0x%x\n", | 
					
						
							|  |  |  | 		       str->format_verb); | 
					
						
							|  |  |  | 		return err; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* update stream - channel config */ | 
					
						
							|  |  |  | 	for (i = 0; i < channels; i++) { | 
					
						
							|  |  |  | 		verb = (str->index << 6) | i; | 
					
						
							|  |  |  | 		err = lola_codec_read(chip, str[i].nid, | 
					
						
							|  |  |  | 				      LOLA_VERB_SET_CHANNEL_STREAMID, 0, verb, | 
					
						
							|  |  |  | 				      &val, NULL); | 
					
						
							|  |  |  | 		if (err < 0) { | 
					
						
							|  |  |  | 			printk(KERN_ERR SFX "Cannot set stream channel %d\n", i); | 
					
						
							|  |  |  | 			return err; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * set up the SD for streaming | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2011-05-03 16:41:02 +02:00
										 |  |  | static int lola_setup_controller(struct lola *chip, struct lola_pcm *pcm, | 
					
						
							|  |  |  | 				 struct lola_stream *str) | 
					
						
							| 
									
										
										
										
											2011-05-03 16:14:46 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2011-05-03 16:41:02 +02:00
										 |  |  | 	dma_addr_t bdl; | 
					
						
							| 
									
										
										
										
											2011-05-03 16:47:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (str->prepared) | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-05-03 16:14:46 +02:00
										 |  |  | 	/* set up BDL */ | 
					
						
							| 
									
										
										
										
											2011-05-03 16:41:02 +02:00
										 |  |  | 	bdl = pcm->bdl.addr + LOLA_BDL_ENTRY_SIZE * str->index; | 
					
						
							|  |  |  | 	lola_dsd_write(chip, str->dsd, BDPL, (u32)bdl); | 
					
						
							|  |  |  | 	lola_dsd_write(chip, str->dsd, BDPU, upper_32_bits(bdl)); | 
					
						
							| 
									
										
										
										
											2011-05-03 16:14:46 +02:00
										 |  |  | 	/* program the stream LVI (last valid index) of the BDL */ | 
					
						
							|  |  |  | 	lola_dsd_write(chip, str->dsd, LVI, str->frags - 1); | 
					
						
							| 
									
										
										
										
											2011-05-03 16:47:03 +02:00
										 |  |  | 	lola_stream_clear_pending_irq(chip, str); | 
					
						
							| 
									
										
										
										
											2011-05-03 16:14:46 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-05-03 16:47:03 +02:00
										 |  |  |  	lola_dsd_write(chip, str->dsd, CTL, | 
					
						
							|  |  |  | 		       LOLA_DSD_CTL_IOCE | LOLA_DSD_CTL_DEIE | LOLA_DSD_CTL_SRUN); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	str->prepared = 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return lola_stream_wait_for_fifo(chip, str, true); | 
					
						
							| 
									
										
										
										
											2011-05-03 16:14:46 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int lola_pcm_prepare(struct snd_pcm_substream *substream) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct lola *chip = snd_pcm_substream_chip(substream); | 
					
						
							|  |  |  | 	struct lola_pcm *pcm = lola_get_pcm(substream); | 
					
						
							|  |  |  | 	struct lola_stream *str = lola_get_stream(substream); | 
					
						
							|  |  |  | 	struct snd_pcm_runtime *runtime = substream->runtime; | 
					
						
							|  |  |  | 	unsigned int bufsize, period_bytes, format_verb; | 
					
						
							|  |  |  | 	int i, err; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mutex_lock(&chip->open_mutex); | 
					
						
							|  |  |  | 	lola_stream_reset(chip, str); | 
					
						
							|  |  |  | 	lola_cleanup_slave_streams(pcm, str); | 
					
						
							| 
									
										
										
										
											2011-05-03 17:05:08 +02:00
										 |  |  | 	if (str->index + runtime->channels > pcm->num_streams) { | 
					
						
							| 
									
										
										
										
											2011-05-03 16:14:46 +02:00
										 |  |  | 		mutex_unlock(&chip->open_mutex); | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for (i = 1; i < runtime->channels; i++) { | 
					
						
							|  |  |  | 		str[i].master = str; | 
					
						
							|  |  |  | 		str[i].opened = 1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	mutex_unlock(&chip->open_mutex); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	bufsize = snd_pcm_lib_buffer_bytes(substream); | 
					
						
							|  |  |  | 	period_bytes = snd_pcm_lib_period_bytes(substream); | 
					
						
							|  |  |  | 	format_verb = lola_get_format_verb(substream); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-05-03 16:47:03 +02:00
										 |  |  | 	str->bufsize = bufsize; | 
					
						
							|  |  |  | 	str->period_bytes = period_bytes; | 
					
						
							|  |  |  | 	str->format_verb = format_verb; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	err = lola_setup_periods(chip, pcm, substream, str); | 
					
						
							|  |  |  | 	if (err < 0) | 
					
						
							|  |  |  | 		return err; | 
					
						
							| 
									
										
										
										
											2011-05-03 16:14:46 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-05-03 16:59:27 +02:00
										 |  |  | 	err = lola_set_sample_rate(chip, runtime->rate); | 
					
						
							|  |  |  | 	if (err < 0) | 
					
						
							|  |  |  | 		return err; | 
					
						
							|  |  |  | 	chip->sample_rate = runtime->rate;	/* sample rate gets locked */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-05-03 16:14:46 +02:00
										 |  |  | 	err = lola_set_stream_config(chip, str, runtime->channels); | 
					
						
							|  |  |  | 	if (err < 0) | 
					
						
							|  |  |  | 		return err; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-05-03 16:47:03 +02:00
										 |  |  | 	err = lola_setup_controller(chip, pcm, str); | 
					
						
							|  |  |  | 	if (err < 0) { | 
					
						
							|  |  |  | 		lola_stream_reset(chip, str); | 
					
						
							|  |  |  | 		return err; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							| 
									
										
										
										
											2011-05-03 16:14:46 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int lola_pcm_trigger(struct snd_pcm_substream *substream, int cmd) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct lola *chip = snd_pcm_substream_chip(substream); | 
					
						
							|  |  |  | 	struct lola_stream *str; | 
					
						
							|  |  |  | 	struct snd_pcm_substream *s; | 
					
						
							|  |  |  | 	unsigned int start; | 
					
						
							|  |  |  | 	unsigned int tstamp; | 
					
						
							| 
									
										
										
										
											2011-05-03 16:59:27 +02:00
										 |  |  | 	bool sync_streams; | 
					
						
							| 
									
										
										
										
											2011-05-03 16:14:46 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	switch (cmd) { | 
					
						
							|  |  |  | 	case SNDRV_PCM_TRIGGER_START: | 
					
						
							|  |  |  | 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | 
					
						
							|  |  |  | 	case SNDRV_PCM_TRIGGER_RESUME: | 
					
						
							|  |  |  | 		start = 1; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | 
					
						
							|  |  |  | 	case SNDRV_PCM_TRIGGER_SUSPEND: | 
					
						
							|  |  |  | 	case SNDRV_PCM_TRIGGER_STOP: | 
					
						
							|  |  |  | 		start = 0; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-05-03 16:59:27 +02:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * sample correct synchronization is only needed starting several | 
					
						
							| 
									
										
										
										
											2011-05-03 17:05:08 +02:00
										 |  |  | 	 * streams. On stop or if only one stream do as quick as possible | 
					
						
							| 
									
										
										
										
											2011-05-03 16:59:27 +02:00
										 |  |  | 	 */ | 
					
						
							|  |  |  | 	sync_streams = (start && snd_pcm_stream_linked(substream)); | 
					
						
							|  |  |  | 	tstamp = lola_get_tstamp(chip, !sync_streams); | 
					
						
							| 
									
										
										
										
											2011-05-03 16:14:46 +02:00
										 |  |  | 	spin_lock(&chip->reg_lock); | 
					
						
							|  |  |  | 	snd_pcm_group_for_each_entry(s, substream) { | 
					
						
							|  |  |  | 		if (s->pcm->card != substream->pcm->card) | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 		str = lola_get_stream(s); | 
					
						
							|  |  |  | 		if (start) | 
					
						
							|  |  |  | 			lola_stream_start(chip, str, tstamp); | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			lola_stream_stop(chip, str, tstamp); | 
					
						
							|  |  |  | 		str->running = start; | 
					
						
							| 
									
										
										
										
											2011-05-03 16:59:27 +02:00
										 |  |  | 		str->paused = !start; | 
					
						
							| 
									
										
										
										
											2011-05-03 16:14:46 +02:00
										 |  |  | 		snd_pcm_trigger_done(s, substream); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	spin_unlock(&chip->reg_lock); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static snd_pcm_uframes_t lola_pcm_pointer(struct snd_pcm_substream *substream) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct lola *chip = snd_pcm_substream_chip(substream); | 
					
						
							|  |  |  | 	struct lola_stream *str = lola_get_stream(substream); | 
					
						
							|  |  |  | 	unsigned int pos = lola_dsd_read(chip, str->dsd, LPIB); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (pos >= str->bufsize) | 
					
						
							|  |  |  | 		pos = 0; | 
					
						
							|  |  |  | 	return bytes_to_frames(substream->runtime, pos); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void lola_pcm_update(struct lola *chip, struct lola_pcm *pcm, unsigned int bits) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; bits && i < pcm->num_streams; i++) { | 
					
						
							|  |  |  | 		if (bits & (1 << i)) { | 
					
						
							|  |  |  | 			struct lola_stream *str = &pcm->streams[i]; | 
					
						
							|  |  |  | 			if (str->substream && str->running) | 
					
						
							|  |  |  | 				snd_pcm_period_elapsed(str->substream); | 
					
						
							|  |  |  | 			bits &= ~(1 << i); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct snd_pcm_ops lola_pcm_ops = { | 
					
						
							|  |  |  | 	.open = lola_pcm_open, | 
					
						
							|  |  |  | 	.close = lola_pcm_close, | 
					
						
							|  |  |  | 	.ioctl = snd_pcm_lib_ioctl, | 
					
						
							|  |  |  | 	.hw_params = lola_pcm_hw_params, | 
					
						
							|  |  |  | 	.hw_free = lola_pcm_hw_free, | 
					
						
							|  |  |  | 	.prepare = lola_pcm_prepare, | 
					
						
							|  |  |  | 	.trigger = lola_pcm_trigger, | 
					
						
							|  |  |  | 	.pointer = lola_pcm_pointer, | 
					
						
							| 
									
										
										
										
											2011-05-03 16:50:51 +02:00
										 |  |  | 	.page = snd_pcm_sgbuf_ops_page, | 
					
						
							| 
									
										
										
										
											2011-05-03 16:14:46 +02:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int __devinit lola_create_pcm(struct lola *chip) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct snd_pcm *pcm; | 
					
						
							|  |  |  | 	int i, err; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-05-03 16:41:02 +02:00
										 |  |  | 	for (i = 0; i < 2; i++) { | 
					
						
							|  |  |  | 		err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, | 
					
						
							|  |  |  | 					  snd_dma_pci_data(chip->pci), | 
					
						
							|  |  |  | 					  PAGE_SIZE, &chip->pcm[i].bdl); | 
					
						
							|  |  |  | 		if (err < 0) | 
					
						
							|  |  |  | 			return err; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-05-03 16:14:46 +02:00
										 |  |  | 	err = snd_pcm_new(chip->card, "Digigram Lola", 0, | 
					
						
							|  |  |  | 			  chip->pcm[SNDRV_PCM_STREAM_PLAYBACK].num_streams, | 
					
						
							|  |  |  | 			  chip->pcm[SNDRV_PCM_STREAM_CAPTURE].num_streams, | 
					
						
							|  |  |  | 			  &pcm); | 
					
						
							|  |  |  | 	if (err < 0) | 
					
						
							|  |  |  | 		return err; | 
					
						
							|  |  |  | 	strlcpy(pcm->name, "Digigram Lola", sizeof(pcm->name)); | 
					
						
							|  |  |  | 	pcm->private_data = chip; | 
					
						
							|  |  |  | 	for (i = 0; i < 2; i++) { | 
					
						
							|  |  |  | 		if (chip->pcm[i].num_streams) | 
					
						
							|  |  |  | 			snd_pcm_set_ops(pcm, i, &lola_pcm_ops); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	/* buffer pre-allocation */ | 
					
						
							| 
									
										
										
										
											2011-05-03 16:50:51 +02:00
										 |  |  | 	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, | 
					
						
							| 
									
										
										
										
											2011-05-03 16:14:46 +02:00
										 |  |  | 					      snd_dma_pci_data(chip->pci), | 
					
						
							|  |  |  | 					      1024 * 64, 32 * 1024 * 1024); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void lola_free_pcm(struct lola *chip) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2011-05-03 16:41:02 +02:00
										 |  |  | 	snd_dma_free_pages(&chip->pcm[0].bdl); | 
					
						
							|  |  |  | 	snd_dma_free_pages(&chip->pcm[1].bdl); | 
					
						
							| 
									
										
										
										
											2011-05-03 16:14:46 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int lola_init_stream(struct lola *chip, struct lola_stream *str, | 
					
						
							|  |  |  | 			    int idx, int nid, int dir) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	unsigned int val; | 
					
						
							|  |  |  | 	int err; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	str->nid = nid; | 
					
						
							|  |  |  | 	str->index = idx; | 
					
						
							|  |  |  | 	str->dsd = idx; | 
					
						
							|  |  |  | 	if (dir == PLAY) | 
					
						
							|  |  |  | 		str->dsd += MAX_STREAM_IN_COUNT; | 
					
						
							|  |  |  | 	err = lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val); | 
					
						
							|  |  |  | 	if (err < 0) { | 
					
						
							|  |  |  | 		printk(KERN_ERR SFX "Can't read wcaps for 0x%x\n", nid); | 
					
						
							|  |  |  | 		return err; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (dir == PLAY) { | 
					
						
							|  |  |  | 		/* test TYPE and bits 0..11 (no test bit9 : Digital = 0/1) */ | 
					
						
							|  |  |  | 		if ((val & 0x00f00dff) != 0x00000010) { | 
					
						
							|  |  |  | 			printk(KERN_ERR SFX "Invalid wcaps 0x%x for 0x%x\n", | 
					
						
							|  |  |  | 			       val, nid); | 
					
						
							|  |  |  | 			return -EINVAL; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		/* test TYPE and bits 0..11 (no test bit9 : Digital = 0/1)
 | 
					
						
							|  |  |  | 		 * (bug : ignore bit8: Conn list = 0/1) | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		if ((val & 0x00f00cff) != 0x00100010) { | 
					
						
							|  |  |  | 			printk(KERN_ERR SFX "Invalid wcaps 0x%x for 0x%x\n", | 
					
						
							|  |  |  | 			       val, nid); | 
					
						
							|  |  |  | 			return -EINVAL; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		/* test bit9:DIGITAL and bit12:SRC_PRESENT*/ | 
					
						
							|  |  |  | 		if ((val & 0x00001200) == 0x00001200) | 
					
						
							|  |  |  | 			chip->input_src_caps_mask |= (1 << idx); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	err = lola_read_param(chip, nid, LOLA_PAR_STREAM_FORMATS, &val); | 
					
						
							|  |  |  | 	if (err < 0) { | 
					
						
							|  |  |  | 		printk(KERN_ERR SFX "Can't read FORMATS 0x%x\n", nid); | 
					
						
							|  |  |  | 		return err; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	val &= 3; | 
					
						
							|  |  |  | 	if (val == 3) | 
					
						
							|  |  |  | 		str->can_float = true; | 
					
						
							|  |  |  | 	if (!(val & 1)) { | 
					
						
							|  |  |  | 		printk(KERN_ERR SFX "Invalid formats 0x%x for 0x%x", val, nid); | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int __devinit lola_init_pcm(struct lola *chip, int dir, int *nidp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct lola_pcm *pcm = &chip->pcm[dir]; | 
					
						
							|  |  |  | 	int i, nid, err; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	nid = *nidp; | 
					
						
							|  |  |  | 	for (i = 0; i < pcm->num_streams; i++, nid++) { | 
					
						
							|  |  |  | 		err = lola_init_stream(chip, &pcm->streams[i], i, nid, dir); | 
					
						
							|  |  |  | 		if (err < 0) | 
					
						
							|  |  |  | 			return err; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	*nidp = nid; | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } |