19b4ffa48SJaya Kumar /*
29b4ffa48SJaya Kumar  * Driver for audio on multifunction CS5535 companion device
39b4ffa48SJaya Kumar  * Copyright (C) Jaya Kumar
49b4ffa48SJaya Kumar  *
59b4ffa48SJaya Kumar  * Based on Jaroslav Kysela and Takashi Iwai's examples.
69b4ffa48SJaya Kumar  * This work was sponsored by CIS(M) Sdn Bhd.
79b4ffa48SJaya Kumar  *
89b4ffa48SJaya Kumar  * This program is free software; you can redistribute it and/or modify
99b4ffa48SJaya Kumar  * it under the terms of the GNU General Public License as published by
109b4ffa48SJaya Kumar  * the Free Software Foundation; either version 2 of the License, or
119b4ffa48SJaya Kumar  * (at your option) any later version.
129b4ffa48SJaya Kumar  *
139b4ffa48SJaya Kumar  * This program is distributed in the hope that it will be useful,
149b4ffa48SJaya Kumar  * but WITHOUT ANY WARRANTY; without even the implied warranty of
159b4ffa48SJaya Kumar  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
169b4ffa48SJaya Kumar  * GNU General Public License for more details.
179b4ffa48SJaya Kumar  *
189b4ffa48SJaya Kumar  * You should have received a copy of the GNU General Public License
199b4ffa48SJaya Kumar  * along with this program; if not, write to the Free Software
209b4ffa48SJaya Kumar  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
219b4ffa48SJaya Kumar  *
229b4ffa48SJaya Kumar  * todo: add be fmt support, spdif, pm
239b4ffa48SJaya Kumar  */
249b4ffa48SJaya Kumar 
259b4ffa48SJaya Kumar #include <linux/init.h>
269b4ffa48SJaya Kumar #include <linux/slab.h>
279b4ffa48SJaya Kumar #include <linux/pci.h>
289b4ffa48SJaya Kumar #include <sound/driver.h>
299b4ffa48SJaya Kumar #include <sound/core.h>
309b4ffa48SJaya Kumar #include <sound/control.h>
319b4ffa48SJaya Kumar #include <sound/initval.h>
329b4ffa48SJaya Kumar #include <sound/asoundef.h>
339b4ffa48SJaya Kumar #include <sound/pcm.h>
349b4ffa48SJaya Kumar #include <sound/pcm_params.h>
359b4ffa48SJaya Kumar #include <sound/ac97_codec.h>
369b4ffa48SJaya Kumar #include "cs5535audio.h"
379b4ffa48SJaya Kumar 
3866f8df6bSTakashi Iwai static struct snd_pcm_hardware snd_cs5535audio_playback =
399b4ffa48SJaya Kumar {
409b4ffa48SJaya Kumar 	.info =			(
419b4ffa48SJaya Kumar 				SNDRV_PCM_INFO_MMAP |
429b4ffa48SJaya Kumar 				SNDRV_PCM_INFO_INTERLEAVED |
439b4ffa48SJaya Kumar 		 		SNDRV_PCM_INFO_BLOCK_TRANSFER |
449b4ffa48SJaya Kumar 		 		SNDRV_PCM_INFO_MMAP_VALID |
459b4ffa48SJaya Kumar 		 		SNDRV_PCM_INFO_PAUSE |
469ac25594SJaya Kumar 				SNDRV_PCM_INFO_RESUME
479b4ffa48SJaya Kumar 				),
489b4ffa48SJaya Kumar 	.formats =		(
499b4ffa48SJaya Kumar 				SNDRV_PCM_FMTBIT_S16_LE
509b4ffa48SJaya Kumar 				),
519b4ffa48SJaya Kumar 	.rates =		(
529b4ffa48SJaya Kumar 				SNDRV_PCM_RATE_CONTINUOUS |
539b4ffa48SJaya Kumar 				SNDRV_PCM_RATE_8000_48000
549b4ffa48SJaya Kumar 				),
559b4ffa48SJaya Kumar 	.rate_min =		4000,
569b4ffa48SJaya Kumar 	.rate_max =		48000,
579b4ffa48SJaya Kumar 	.channels_min =		2,
589b4ffa48SJaya Kumar 	.channels_max =		2,
599b4ffa48SJaya Kumar 	.buffer_bytes_max =	(128*1024),
609b4ffa48SJaya Kumar 	.period_bytes_min =	64,
619b4ffa48SJaya Kumar 	.period_bytes_max =	(64*1024 - 16),
629b4ffa48SJaya Kumar 	.periods_min =		1,
639b4ffa48SJaya Kumar 	.periods_max =		CS5535AUDIO_MAX_DESCRIPTORS,
649b4ffa48SJaya Kumar 	.fifo_size =		0,
659b4ffa48SJaya Kumar };
669b4ffa48SJaya Kumar 
6766f8df6bSTakashi Iwai static struct snd_pcm_hardware snd_cs5535audio_capture =
689b4ffa48SJaya Kumar {
699b4ffa48SJaya Kumar 	.info =			(
709b4ffa48SJaya Kumar 				SNDRV_PCM_INFO_MMAP |
719b4ffa48SJaya Kumar 				SNDRV_PCM_INFO_INTERLEAVED |
729b4ffa48SJaya Kumar 		 		SNDRV_PCM_INFO_BLOCK_TRANSFER |
73b83f346bSClemens Ladisch 		 		SNDRV_PCM_INFO_MMAP_VALID
749b4ffa48SJaya Kumar 				),
759b4ffa48SJaya Kumar 	.formats =		(
769b4ffa48SJaya Kumar 				SNDRV_PCM_FMTBIT_S16_LE
779b4ffa48SJaya Kumar 				),
789b4ffa48SJaya Kumar 	.rates =		(
799b4ffa48SJaya Kumar 				SNDRV_PCM_RATE_CONTINUOUS |
809b4ffa48SJaya Kumar 				SNDRV_PCM_RATE_8000_48000
819b4ffa48SJaya Kumar 				),
829b4ffa48SJaya Kumar 	.rate_min =		4000,
839b4ffa48SJaya Kumar 	.rate_max =		48000,
849b4ffa48SJaya Kumar 	.channels_min =		2,
859b4ffa48SJaya Kumar 	.channels_max =		2,
869b4ffa48SJaya Kumar 	.buffer_bytes_max =	(128*1024),
879b4ffa48SJaya Kumar 	.period_bytes_min =	64,
889b4ffa48SJaya Kumar 	.period_bytes_max =	(64*1024 - 16),
899b4ffa48SJaya Kumar 	.periods_min =		1,
909b4ffa48SJaya Kumar 	.periods_max =		CS5535AUDIO_MAX_DESCRIPTORS,
919b4ffa48SJaya Kumar 	.fifo_size =		0,
929b4ffa48SJaya Kumar };
939b4ffa48SJaya Kumar 
9466f8df6bSTakashi Iwai static int snd_cs5535audio_playback_open(struct snd_pcm_substream *substream)
959b4ffa48SJaya Kumar {
969b4ffa48SJaya Kumar 	int err;
9766f8df6bSTakashi Iwai 	struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
9866f8df6bSTakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
999b4ffa48SJaya Kumar 
1009b4ffa48SJaya Kumar 	runtime->hw = snd_cs5535audio_playback;
1019b4ffa48SJaya Kumar 	cs5535au->playback_substream = substream;
1029b4ffa48SJaya Kumar 	runtime->private_data = &(cs5535au->dmas[CS5535AUDIO_DMA_PLAYBACK]);
1039b4ffa48SJaya Kumar 	if ((err = snd_pcm_hw_constraint_integer(runtime,
1049b4ffa48SJaya Kumar 				SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
1059b4ffa48SJaya Kumar 		return err;
1069b4ffa48SJaya Kumar 
1079b4ffa48SJaya Kumar 	return 0;
1089b4ffa48SJaya Kumar }
1099b4ffa48SJaya Kumar 
11066f8df6bSTakashi Iwai static int snd_cs5535audio_playback_close(struct snd_pcm_substream *substream)
1119b4ffa48SJaya Kumar {
1129b4ffa48SJaya Kumar 	return 0;
1139b4ffa48SJaya Kumar }
1149b4ffa48SJaya Kumar 
1159b4ffa48SJaya Kumar #define CS5535AUDIO_DESC_LIST_SIZE \
11666f8df6bSTakashi Iwai 	PAGE_ALIGN(CS5535AUDIO_MAX_DESCRIPTORS * sizeof(struct cs5535audio_dma_desc))
1179b4ffa48SJaya Kumar 
11866f8df6bSTakashi Iwai static int cs5535audio_build_dma_packets(struct cs5535audio *cs5535au,
11966f8df6bSTakashi Iwai 					 struct cs5535audio_dma *dma,
12066f8df6bSTakashi Iwai 					 struct snd_pcm_substream *substream,
1219b4ffa48SJaya Kumar 					 unsigned int periods,
1229b4ffa48SJaya Kumar 					 unsigned int period_bytes)
1239b4ffa48SJaya Kumar {
1249b4ffa48SJaya Kumar 	unsigned int i;
1259b4ffa48SJaya Kumar 	u32 addr, desc_addr, jmpprd_addr;
12666f8df6bSTakashi Iwai 	struct cs5535audio_dma_desc *lastdesc;
1279b4ffa48SJaya Kumar 
1289b4ffa48SJaya Kumar 	if (periods > CS5535AUDIO_MAX_DESCRIPTORS)
1299b4ffa48SJaya Kumar 		return -ENOMEM;
1309b4ffa48SJaya Kumar 
1319b4ffa48SJaya Kumar 	if (dma->desc_buf.area == NULL) {
1329b4ffa48SJaya Kumar 		if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
1339b4ffa48SJaya Kumar 					snd_dma_pci_data(cs5535au->pci),
1349b4ffa48SJaya Kumar 					CS5535AUDIO_DESC_LIST_SIZE+1,
1359b4ffa48SJaya Kumar 					&dma->desc_buf) < 0)
1369b4ffa48SJaya Kumar 			return -ENOMEM;
1379b4ffa48SJaya Kumar 		dma->period_bytes = dma->periods = 0;
1389b4ffa48SJaya Kumar 	}
1399b4ffa48SJaya Kumar 
1409b4ffa48SJaya Kumar 	if (dma->periods == periods && dma->period_bytes == period_bytes)
1419b4ffa48SJaya Kumar 		return 0;
1429b4ffa48SJaya Kumar 
143d6e05edcSAndreas Mohr 	/* the u32 cast is okay because in snd*create we successfully told
1449b4ffa48SJaya Kumar    	   pci alloc that we're only 32 bit capable so the uppper will be 0 */
1459b4ffa48SJaya Kumar 	addr = (u32) substream->runtime->dma_addr;
1469b4ffa48SJaya Kumar 	desc_addr = (u32) dma->desc_buf.addr;
1479b4ffa48SJaya Kumar 	for (i = 0; i < periods; i++) {
14866f8df6bSTakashi Iwai 		struct cs5535audio_dma_desc *desc =
14966f8df6bSTakashi Iwai 			&((struct cs5535audio_dma_desc *) dma->desc_buf.area)[i];
1509b4ffa48SJaya Kumar 		desc->addr = cpu_to_le32(addr);
1513e873174STakashi Iwai 		desc->size = cpu_to_le32(period_bytes);
1523e873174STakashi Iwai 		desc->ctlreserved = cpu_to_le32(PRD_EOP);
15366f8df6bSTakashi Iwai 		desc_addr += sizeof(struct cs5535audio_dma_desc);
1549b4ffa48SJaya Kumar 		addr += period_bytes;
1559b4ffa48SJaya Kumar 	}
1569b4ffa48SJaya Kumar 	/* we reserved one dummy descriptor at the end to do the PRD jump */
15766f8df6bSTakashi Iwai 	lastdesc = &((struct cs5535audio_dma_desc *) dma->desc_buf.area)[periods];
1589b4ffa48SJaya Kumar 	lastdesc->addr = cpu_to_le32((u32) dma->desc_buf.addr);
1599b4ffa48SJaya Kumar 	lastdesc->size = 0;
1603e873174STakashi Iwai 	lastdesc->ctlreserved = cpu_to_le32(PRD_JMP);
1619b4ffa48SJaya Kumar 	jmpprd_addr = cpu_to_le32(lastdesc->addr +
16266f8df6bSTakashi Iwai 				  (sizeof(struct cs5535audio_dma_desc)*periods));
1639b4ffa48SJaya Kumar 
1647abcacb0SAndres Salomon 	dma->substream = substream;
1659b4ffa48SJaya Kumar 	dma->period_bytes = period_bytes;
1669b4ffa48SJaya Kumar 	dma->periods = periods;
1679b4ffa48SJaya Kumar 	spin_lock_irq(&cs5535au->reg_lock);
1689b4ffa48SJaya Kumar 	dma->ops->disable_dma(cs5535au);
1699b4ffa48SJaya Kumar 	dma->ops->setup_prd(cs5535au, jmpprd_addr);
1709b4ffa48SJaya Kumar 	spin_unlock_irq(&cs5535au->reg_lock);
1719b4ffa48SJaya Kumar 	return 0;
1729b4ffa48SJaya Kumar }
1739b4ffa48SJaya Kumar 
17466f8df6bSTakashi Iwai static void cs5535audio_playback_enable_dma(struct cs5535audio *cs5535au)
1759b4ffa48SJaya Kumar {
1769b4ffa48SJaya Kumar 	cs_writeb(cs5535au, ACC_BM0_CMD, BM_CTL_EN);
1779b4ffa48SJaya Kumar }
1789b4ffa48SJaya Kumar 
17966f8df6bSTakashi Iwai static void cs5535audio_playback_disable_dma(struct cs5535audio *cs5535au)
1809b4ffa48SJaya Kumar {
1819b4ffa48SJaya Kumar 	cs_writeb(cs5535au, ACC_BM0_CMD, 0);
1829b4ffa48SJaya Kumar }
1839b4ffa48SJaya Kumar 
18466f8df6bSTakashi Iwai static void cs5535audio_playback_pause_dma(struct cs5535audio *cs5535au)
1859b4ffa48SJaya Kumar {
1869b4ffa48SJaya Kumar 	cs_writeb(cs5535au, ACC_BM0_CMD, BM_CTL_PAUSE);
1879b4ffa48SJaya Kumar }
1889b4ffa48SJaya Kumar 
18966f8df6bSTakashi Iwai static void cs5535audio_playback_setup_prd(struct cs5535audio *cs5535au,
1909b4ffa48SJaya Kumar 					   u32 prd_addr)
1919b4ffa48SJaya Kumar {
1929b4ffa48SJaya Kumar 	cs_writel(cs5535au, ACC_BM0_PRD, prd_addr);
1939b4ffa48SJaya Kumar }
1949b4ffa48SJaya Kumar 
1959ac25594SJaya Kumar static u32 cs5535audio_playback_read_prd(struct cs5535audio *cs5535au)
1969ac25594SJaya Kumar {
1979ac25594SJaya Kumar 	return cs_readl(cs5535au, ACC_BM0_PRD);
1989ac25594SJaya Kumar }
1999ac25594SJaya Kumar 
20066f8df6bSTakashi Iwai static u32 cs5535audio_playback_read_dma_pntr(struct cs5535audio *cs5535au)
2019b4ffa48SJaya Kumar {
2029b4ffa48SJaya Kumar 	return cs_readl(cs5535au, ACC_BM0_PNTR);
2039b4ffa48SJaya Kumar }
2049b4ffa48SJaya Kumar 
20566f8df6bSTakashi Iwai static void cs5535audio_capture_enable_dma(struct cs5535audio *cs5535au)
2069b4ffa48SJaya Kumar {
2079b4ffa48SJaya Kumar 	cs_writeb(cs5535au, ACC_BM1_CMD, BM_CTL_EN);
2089b4ffa48SJaya Kumar }
2099b4ffa48SJaya Kumar 
21066f8df6bSTakashi Iwai static void cs5535audio_capture_disable_dma(struct cs5535audio *cs5535au)
2119b4ffa48SJaya Kumar {
2129b4ffa48SJaya Kumar 	cs_writeb(cs5535au, ACC_BM1_CMD, 0);
2139b4ffa48SJaya Kumar }
2149b4ffa48SJaya Kumar 
21566f8df6bSTakashi Iwai static void cs5535audio_capture_pause_dma(struct cs5535audio *cs5535au)
2169b4ffa48SJaya Kumar {
2179b4ffa48SJaya Kumar 	cs_writeb(cs5535au, ACC_BM1_CMD, BM_CTL_PAUSE);
2189b4ffa48SJaya Kumar }
2199b4ffa48SJaya Kumar 
22066f8df6bSTakashi Iwai static void cs5535audio_capture_setup_prd(struct cs5535audio *cs5535au,
2219b4ffa48SJaya Kumar 					  u32 prd_addr)
2229b4ffa48SJaya Kumar {
2239b4ffa48SJaya Kumar 	cs_writel(cs5535au, ACC_BM1_PRD, prd_addr);
2249b4ffa48SJaya Kumar }
2259b4ffa48SJaya Kumar 
2269ac25594SJaya Kumar static u32 cs5535audio_capture_read_prd(struct cs5535audio *cs5535au)
2279ac25594SJaya Kumar {
2289ac25594SJaya Kumar 	return cs_readl(cs5535au, ACC_BM1_PRD);
2299ac25594SJaya Kumar }
2309ac25594SJaya Kumar 
23166f8df6bSTakashi Iwai static u32 cs5535audio_capture_read_dma_pntr(struct cs5535audio *cs5535au)
2329b4ffa48SJaya Kumar {
2339b4ffa48SJaya Kumar 	return cs_readl(cs5535au, ACC_BM1_PNTR);
2349b4ffa48SJaya Kumar }
2359b4ffa48SJaya Kumar 
23666f8df6bSTakashi Iwai static void cs5535audio_clear_dma_packets(struct cs5535audio *cs5535au,
23766f8df6bSTakashi Iwai 					  struct cs5535audio_dma *dma,
23866f8df6bSTakashi Iwai 					  struct snd_pcm_substream *substream)
2399b4ffa48SJaya Kumar {
2409b4ffa48SJaya Kumar 	snd_dma_free_pages(&dma->desc_buf);
2419b4ffa48SJaya Kumar 	dma->desc_buf.area = NULL;
2427abcacb0SAndres Salomon 	dma->substream = NULL;
2439b4ffa48SJaya Kumar }
2449b4ffa48SJaya Kumar 
24566f8df6bSTakashi Iwai static int snd_cs5535audio_hw_params(struct snd_pcm_substream *substream,
24666f8df6bSTakashi Iwai 				     struct snd_pcm_hw_params *hw_params)
2479b4ffa48SJaya Kumar {
24866f8df6bSTakashi Iwai 	struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
24966f8df6bSTakashi Iwai 	struct cs5535audio_dma *dma = substream->runtime->private_data;
2509b4ffa48SJaya Kumar 	int err;
2519b4ffa48SJaya Kumar 
2529b4ffa48SJaya Kumar 	err = snd_pcm_lib_malloc_pages(substream,
2539b4ffa48SJaya Kumar 					params_buffer_bytes(hw_params));
2549b4ffa48SJaya Kumar 	if (err < 0)
2559b4ffa48SJaya Kumar 		return err;
2569b4ffa48SJaya Kumar 	dma->buf_addr = substream->runtime->dma_addr;
2579b4ffa48SJaya Kumar 	dma->buf_bytes = params_buffer_bytes(hw_params);
2589b4ffa48SJaya Kumar 
2599b4ffa48SJaya Kumar 	err = cs5535audio_build_dma_packets(cs5535au, dma, substream,
2609b4ffa48SJaya Kumar 					    params_periods(hw_params),
2619b4ffa48SJaya Kumar 					    params_period_bytes(hw_params));
2629b4ffa48SJaya Kumar 	return err;
2639b4ffa48SJaya Kumar }
2649b4ffa48SJaya Kumar 
26566f8df6bSTakashi Iwai static int snd_cs5535audio_hw_free(struct snd_pcm_substream *substream)
2669b4ffa48SJaya Kumar {
26766f8df6bSTakashi Iwai 	struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
26866f8df6bSTakashi Iwai 	struct cs5535audio_dma *dma = substream->runtime->private_data;
2699b4ffa48SJaya Kumar 
2709b4ffa48SJaya Kumar 	cs5535audio_clear_dma_packets(cs5535au, dma, substream);
2719b4ffa48SJaya Kumar 	return snd_pcm_lib_free_pages(substream);
2729b4ffa48SJaya Kumar }
2739b4ffa48SJaya Kumar 
27466f8df6bSTakashi Iwai static int snd_cs5535audio_playback_prepare(struct snd_pcm_substream *substream)
2759b4ffa48SJaya Kumar {
27666f8df6bSTakashi Iwai 	struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
2779b4ffa48SJaya Kumar 	return snd_ac97_set_rate(cs5535au->ac97, AC97_PCM_FRONT_DAC_RATE,
2789b4ffa48SJaya Kumar 				 substream->runtime->rate);
2799b4ffa48SJaya Kumar }
2809b4ffa48SJaya Kumar 
28166f8df6bSTakashi Iwai static int snd_cs5535audio_trigger(struct snd_pcm_substream *substream, int cmd)
2829b4ffa48SJaya Kumar {
28366f8df6bSTakashi Iwai 	struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
28466f8df6bSTakashi Iwai 	struct cs5535audio_dma *dma = substream->runtime->private_data;
2853e873174STakashi Iwai 	int err = 0;
2869b4ffa48SJaya Kumar 
2873e873174STakashi Iwai 	spin_lock(&cs5535au->reg_lock);
2889b4ffa48SJaya Kumar 	switch (cmd) {
2899b4ffa48SJaya Kumar 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
2909b4ffa48SJaya Kumar 		dma->ops->pause_dma(cs5535au);
2919b4ffa48SJaya Kumar 		break;
2929b4ffa48SJaya Kumar 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
2939b4ffa48SJaya Kumar 		dma->ops->enable_dma(cs5535au);
2949b4ffa48SJaya Kumar 		break;
2959b4ffa48SJaya Kumar 	case SNDRV_PCM_TRIGGER_START:
2969b4ffa48SJaya Kumar 		dma->ops->enable_dma(cs5535au);
2979b4ffa48SJaya Kumar 		break;
2989ac25594SJaya Kumar 	case SNDRV_PCM_TRIGGER_RESUME:
2999ac25594SJaya Kumar 		dma->ops->enable_dma(cs5535au);
3009ac25594SJaya Kumar 		dma->suspended = 0;
3019ac25594SJaya Kumar 		break;
3029b4ffa48SJaya Kumar 	case SNDRV_PCM_TRIGGER_STOP:
3039b4ffa48SJaya Kumar 		dma->ops->disable_dma(cs5535au);
3049b4ffa48SJaya Kumar 		break;
3059ac25594SJaya Kumar 	case SNDRV_PCM_TRIGGER_SUSPEND:
3069ac25594SJaya Kumar 		dma->ops->disable_dma(cs5535au);
3079ac25594SJaya Kumar 		dma->suspended = 1;
3089ac25594SJaya Kumar 		break;
3099b4ffa48SJaya Kumar 	default:
3109b4ffa48SJaya Kumar 		snd_printk(KERN_ERR "unhandled trigger\n");
3113e873174STakashi Iwai 		err = -EINVAL;
3129b4ffa48SJaya Kumar 		break;
3139b4ffa48SJaya Kumar 	}
3143e873174STakashi Iwai 	spin_unlock(&cs5535au->reg_lock);
3153e873174STakashi Iwai 	return err;
3169b4ffa48SJaya Kumar }
3179b4ffa48SJaya Kumar 
31866f8df6bSTakashi Iwai static snd_pcm_uframes_t snd_cs5535audio_pcm_pointer(struct snd_pcm_substream
3199b4ffa48SJaya Kumar 							*substream)
3209b4ffa48SJaya Kumar {
32166f8df6bSTakashi Iwai 	struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
3229b4ffa48SJaya Kumar 	u32 curdma;
32366f8df6bSTakashi Iwai 	struct cs5535audio_dma *dma;
3249b4ffa48SJaya Kumar 
3259b4ffa48SJaya Kumar 	dma = substream->runtime->private_data;
3269b4ffa48SJaya Kumar 	curdma = dma->ops->read_dma_pntr(cs5535au);
3279b4ffa48SJaya Kumar 	if (curdma < dma->buf_addr) {
3289b4ffa48SJaya Kumar 		snd_printk(KERN_ERR "curdma=%x < %x bufaddr.\n",
3299b4ffa48SJaya Kumar 					curdma, dma->buf_addr);
3309b4ffa48SJaya Kumar 		return 0;
3319b4ffa48SJaya Kumar 	}
3329b4ffa48SJaya Kumar 	curdma -= dma->buf_addr;
3339b4ffa48SJaya Kumar 	if (curdma >= dma->buf_bytes) {
3349b4ffa48SJaya Kumar 		snd_printk(KERN_ERR "diff=%x >= %x buf_bytes.\n",
3359b4ffa48SJaya Kumar 					curdma, dma->buf_bytes);
3369b4ffa48SJaya Kumar 		return 0;
3379b4ffa48SJaya Kumar 	}
3389b4ffa48SJaya Kumar 	return bytes_to_frames(substream->runtime, curdma);
3399b4ffa48SJaya Kumar }
3409b4ffa48SJaya Kumar 
34166f8df6bSTakashi Iwai static int snd_cs5535audio_capture_open(struct snd_pcm_substream *substream)
3429b4ffa48SJaya Kumar {
3439b4ffa48SJaya Kumar 	int err;
34466f8df6bSTakashi Iwai 	struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
34566f8df6bSTakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
3469b4ffa48SJaya Kumar 
3479b4ffa48SJaya Kumar 	runtime->hw = snd_cs5535audio_capture;
3489b4ffa48SJaya Kumar 	cs5535au->capture_substream = substream;
3499b4ffa48SJaya Kumar 	runtime->private_data = &(cs5535au->dmas[CS5535AUDIO_DMA_CAPTURE]);
3509b4ffa48SJaya Kumar 	if ((err = snd_pcm_hw_constraint_integer(runtime,
3519b4ffa48SJaya Kumar 					 SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
3529b4ffa48SJaya Kumar 		return err;
3539b4ffa48SJaya Kumar 	return 0;
3549b4ffa48SJaya Kumar }
3559b4ffa48SJaya Kumar 
35666f8df6bSTakashi Iwai static int snd_cs5535audio_capture_close(struct snd_pcm_substream *substream)
3579b4ffa48SJaya Kumar {
3589b4ffa48SJaya Kumar 	return 0;
3599b4ffa48SJaya Kumar }
3609b4ffa48SJaya Kumar 
36166f8df6bSTakashi Iwai static int snd_cs5535audio_capture_prepare(struct snd_pcm_substream *substream)
3629b4ffa48SJaya Kumar {
36366f8df6bSTakashi Iwai 	struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
3649b4ffa48SJaya Kumar 	return snd_ac97_set_rate(cs5535au->ac97, AC97_PCM_LR_ADC_RATE,
3659b4ffa48SJaya Kumar 				 substream->runtime->rate);
3669b4ffa48SJaya Kumar }
3679b4ffa48SJaya Kumar 
36866f8df6bSTakashi Iwai static struct snd_pcm_ops snd_cs5535audio_playback_ops = {
3699b4ffa48SJaya Kumar 	.open =		snd_cs5535audio_playback_open,
3709b4ffa48SJaya Kumar 	.close =	snd_cs5535audio_playback_close,
3719b4ffa48SJaya Kumar 	.ioctl =	snd_pcm_lib_ioctl,
3729b4ffa48SJaya Kumar 	.hw_params =	snd_cs5535audio_hw_params,
3739b4ffa48SJaya Kumar 	.hw_free =	snd_cs5535audio_hw_free,
3749b4ffa48SJaya Kumar 	.prepare =	snd_cs5535audio_playback_prepare,
3759b4ffa48SJaya Kumar 	.trigger =	snd_cs5535audio_trigger,
3769b4ffa48SJaya Kumar 	.pointer =	snd_cs5535audio_pcm_pointer,
3779b4ffa48SJaya Kumar };
3789b4ffa48SJaya Kumar 
37966f8df6bSTakashi Iwai static struct snd_pcm_ops snd_cs5535audio_capture_ops = {
3809b4ffa48SJaya Kumar 	.open =		snd_cs5535audio_capture_open,
3819b4ffa48SJaya Kumar 	.close =	snd_cs5535audio_capture_close,
3829b4ffa48SJaya Kumar 	.ioctl =	snd_pcm_lib_ioctl,
3839b4ffa48SJaya Kumar 	.hw_params =	snd_cs5535audio_hw_params,
3849b4ffa48SJaya Kumar 	.hw_free =	snd_cs5535audio_hw_free,
3859b4ffa48SJaya Kumar 	.prepare =	snd_cs5535audio_capture_prepare,
3869b4ffa48SJaya Kumar 	.trigger =	snd_cs5535audio_trigger,
3879b4ffa48SJaya Kumar 	.pointer =	snd_cs5535audio_pcm_pointer,
3889b4ffa48SJaya Kumar };
3899b4ffa48SJaya Kumar 
39066f8df6bSTakashi Iwai static struct cs5535audio_dma_ops snd_cs5535audio_playback_dma_ops = {
3919b4ffa48SJaya Kumar         .type = CS5535AUDIO_DMA_PLAYBACK,
3929b4ffa48SJaya Kumar         .enable_dma = cs5535audio_playback_enable_dma,
3939b4ffa48SJaya Kumar         .disable_dma = cs5535audio_playback_disable_dma,
3949b4ffa48SJaya Kumar         .setup_prd = cs5535audio_playback_setup_prd,
3959ac25594SJaya Kumar         .read_prd = cs5535audio_playback_read_prd,
3969b4ffa48SJaya Kumar         .pause_dma = cs5535audio_playback_pause_dma,
3979b4ffa48SJaya Kumar         .read_dma_pntr = cs5535audio_playback_read_dma_pntr,
3989b4ffa48SJaya Kumar };
3999b4ffa48SJaya Kumar 
40066f8df6bSTakashi Iwai static struct cs5535audio_dma_ops snd_cs5535audio_capture_dma_ops = {
4019b4ffa48SJaya Kumar         .type = CS5535AUDIO_DMA_CAPTURE,
4029b4ffa48SJaya Kumar         .enable_dma = cs5535audio_capture_enable_dma,
4039b4ffa48SJaya Kumar         .disable_dma = cs5535audio_capture_disable_dma,
4049b4ffa48SJaya Kumar         .setup_prd = cs5535audio_capture_setup_prd,
4059ac25594SJaya Kumar         .read_prd = cs5535audio_capture_read_prd,
4069b4ffa48SJaya Kumar         .pause_dma = cs5535audio_capture_pause_dma,
4079b4ffa48SJaya Kumar         .read_dma_pntr = cs5535audio_capture_read_dma_pntr,
4089b4ffa48SJaya Kumar };
4099b4ffa48SJaya Kumar 
41066f8df6bSTakashi Iwai int __devinit snd_cs5535audio_pcm(struct cs5535audio *cs5535au)
4119b4ffa48SJaya Kumar {
41266f8df6bSTakashi Iwai 	struct snd_pcm *pcm;
4139b4ffa48SJaya Kumar 	int err;
4149b4ffa48SJaya Kumar 
4159b4ffa48SJaya Kumar 	err = snd_pcm_new(cs5535au->card, "CS5535 Audio", 0, 1, 1, &pcm);
4169b4ffa48SJaya Kumar 	if (err < 0)
4179b4ffa48SJaya Kumar 		return err;
4189b4ffa48SJaya Kumar 
4199b4ffa48SJaya Kumar 	cs5535au->dmas[CS5535AUDIO_DMA_PLAYBACK].ops =
4209b4ffa48SJaya Kumar 					&snd_cs5535audio_playback_dma_ops;
4219b4ffa48SJaya Kumar 	cs5535au->dmas[CS5535AUDIO_DMA_CAPTURE].ops =
4229b4ffa48SJaya Kumar 					&snd_cs5535audio_capture_dma_ops;
4239b4ffa48SJaya Kumar 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
4249b4ffa48SJaya Kumar 					&snd_cs5535audio_playback_ops);
4259b4ffa48SJaya Kumar 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
4269b4ffa48SJaya Kumar 					&snd_cs5535audio_capture_ops);
4279b4ffa48SJaya Kumar 
4289b4ffa48SJaya Kumar 	pcm->private_data = cs5535au;
4299b4ffa48SJaya Kumar 	pcm->info_flags = 0;
4309b4ffa48SJaya Kumar 	strcpy(pcm->name, "CS5535 Audio");
4319b4ffa48SJaya Kumar 
4329b4ffa48SJaya Kumar 	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
4339b4ffa48SJaya Kumar 					snd_dma_pci_data(cs5535au->pci),
4349b4ffa48SJaya Kumar 					64*1024, 128*1024);
4359ac25594SJaya Kumar 	cs5535au->pcm = pcm;
4369b4ffa48SJaya Kumar 
4379b4ffa48SJaya Kumar 	return 0;
4389b4ffa48SJaya Kumar }
4399b4ffa48SJaya Kumar 
440