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