1b285192aSMauro Carvalho Chehab /* 2b285192aSMauro Carvalho Chehab * SAA713x ALSA support for V4L 3b285192aSMauro Carvalho Chehab * 4b285192aSMauro Carvalho Chehab * This program is free software; you can redistribute it and/or modify 5b285192aSMauro Carvalho Chehab * it under the terms of the GNU General Public License as published by 6b285192aSMauro Carvalho Chehab * the Free Software Foundation, version 2 7b285192aSMauro Carvalho Chehab * 8b285192aSMauro Carvalho Chehab * This program is distributed in the hope that it will be useful, 9b285192aSMauro Carvalho Chehab * but WITHOUT ANY WARRANTY; without even the implied warranty of 10b285192aSMauro Carvalho Chehab * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11b285192aSMauro Carvalho Chehab * GNU General Public License for more details. 12b285192aSMauro Carvalho Chehab * 13b285192aSMauro Carvalho Chehab */ 14b285192aSMauro Carvalho Chehab 159a12ccfcSMauro Carvalho Chehab #include "saa7134.h" 169a12ccfcSMauro Carvalho Chehab #include "saa7134-reg.h" 179a12ccfcSMauro Carvalho Chehab 18b285192aSMauro Carvalho Chehab #include <linux/init.h> 19b285192aSMauro Carvalho Chehab #include <linux/slab.h> 20b285192aSMauro Carvalho Chehab #include <linux/time.h> 21b285192aSMauro Carvalho Chehab #include <linux/wait.h> 22b285192aSMauro Carvalho Chehab #include <linux/module.h> 23b285192aSMauro Carvalho Chehab #include <sound/core.h> 24b285192aSMauro Carvalho Chehab #include <sound/control.h> 25b285192aSMauro Carvalho Chehab #include <sound/pcm.h> 26b285192aSMauro Carvalho Chehab #include <sound/pcm_params.h> 27b285192aSMauro Carvalho Chehab #include <sound/initval.h> 28b285192aSMauro Carvalho Chehab #include <linux/interrupt.h> 29ba0d342eSMauro Carvalho Chehab #include <linux/vmalloc.h> 30b285192aSMauro Carvalho Chehab 31b285192aSMauro Carvalho Chehab /* 32b285192aSMauro Carvalho Chehab * Configuration macros 33b285192aSMauro Carvalho Chehab */ 34b285192aSMauro Carvalho Chehab 35b285192aSMauro Carvalho Chehab /* defaults */ 36b285192aSMauro Carvalho Chehab #define MIXER_ADDR_UNSELECTED -1 37b285192aSMauro Carvalho Chehab #define MIXER_ADDR_TVTUNER 0 38b285192aSMauro Carvalho Chehab #define MIXER_ADDR_LINE1 1 39b285192aSMauro Carvalho Chehab #define MIXER_ADDR_LINE2 2 40b285192aSMauro Carvalho Chehab #define MIXER_ADDR_LAST 2 41b285192aSMauro Carvalho Chehab 42b285192aSMauro Carvalho Chehab 43b285192aSMauro Carvalho Chehab static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ 44b285192aSMauro Carvalho Chehab static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ 45b285192aSMauro Carvalho Chehab static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 1}; 46b285192aSMauro Carvalho Chehab 47b285192aSMauro Carvalho Chehab module_param_array(index, int, NULL, 0444); 48b285192aSMauro Carvalho Chehab module_param_array(enable, int, NULL, 0444); 49b285192aSMauro Carvalho Chehab MODULE_PARM_DESC(index, "Index value for SAA7134 capture interface(s)."); 50b285192aSMauro Carvalho Chehab MODULE_PARM_DESC(enable, "Enable (or not) the SAA7134 capture interface(s)."); 51b285192aSMauro Carvalho Chehab 52b285192aSMauro Carvalho Chehab /* 53b285192aSMauro Carvalho Chehab * Main chip structure 54b285192aSMauro Carvalho Chehab */ 55b285192aSMauro Carvalho Chehab 56b285192aSMauro Carvalho Chehab typedef struct snd_card_saa7134 { 57b285192aSMauro Carvalho Chehab struct snd_card *card; 58b285192aSMauro Carvalho Chehab spinlock_t mixer_lock; 59b285192aSMauro Carvalho Chehab int mixer_volume[MIXER_ADDR_LAST+1][2]; 60b285192aSMauro Carvalho Chehab int capture_source_addr; 61b285192aSMauro Carvalho Chehab int capture_source[2]; 62b285192aSMauro Carvalho Chehab struct snd_kcontrol *capture_ctl[MIXER_ADDR_LAST+1]; 63b285192aSMauro Carvalho Chehab struct pci_dev *pci; 64b285192aSMauro Carvalho Chehab struct saa7134_dev *dev; 65b285192aSMauro Carvalho Chehab 66b285192aSMauro Carvalho Chehab unsigned long iobase; 67b285192aSMauro Carvalho Chehab s16 irq; 68b285192aSMauro Carvalho Chehab u16 mute_was_on; 69b285192aSMauro Carvalho Chehab 70b285192aSMauro Carvalho Chehab spinlock_t lock; 71b285192aSMauro Carvalho Chehab } snd_card_saa7134_t; 72b285192aSMauro Carvalho Chehab 73b285192aSMauro Carvalho Chehab 74b285192aSMauro Carvalho Chehab /* 75b285192aSMauro Carvalho Chehab * PCM structure 76b285192aSMauro Carvalho Chehab */ 77b285192aSMauro Carvalho Chehab 78b285192aSMauro Carvalho Chehab typedef struct snd_card_saa7134_pcm { 79b285192aSMauro Carvalho Chehab struct saa7134_dev *dev; 80b285192aSMauro Carvalho Chehab 81b285192aSMauro Carvalho Chehab spinlock_t lock; 82b285192aSMauro Carvalho Chehab 83b285192aSMauro Carvalho Chehab struct snd_pcm_substream *substream; 84b285192aSMauro Carvalho Chehab } snd_card_saa7134_pcm_t; 85b285192aSMauro Carvalho Chehab 86b285192aSMauro Carvalho Chehab static struct snd_card *snd_saa7134_cards[SNDRV_CARDS]; 87b285192aSMauro Carvalho Chehab 88b285192aSMauro Carvalho Chehab 89b285192aSMauro Carvalho Chehab /* 90b285192aSMauro Carvalho Chehab * saa7134 DMA audio stop 91b285192aSMauro Carvalho Chehab * 92b285192aSMauro Carvalho Chehab * Called when the capture device is released or the buffer overflows 93b285192aSMauro Carvalho Chehab * 94b285192aSMauro Carvalho Chehab * - Copied verbatim from saa7134-oss's dsp_dma_stop. 95b285192aSMauro Carvalho Chehab * 96b285192aSMauro Carvalho Chehab */ 97b285192aSMauro Carvalho Chehab 98b285192aSMauro Carvalho Chehab static void saa7134_dma_stop(struct saa7134_dev *dev) 99b285192aSMauro Carvalho Chehab { 100b285192aSMauro Carvalho Chehab dev->dmasound.dma_blk = -1; 101b285192aSMauro Carvalho Chehab dev->dmasound.dma_running = 0; 102b285192aSMauro Carvalho Chehab saa7134_set_dmabits(dev); 103b285192aSMauro Carvalho Chehab } 104b285192aSMauro Carvalho Chehab 105b285192aSMauro Carvalho Chehab /* 106b285192aSMauro Carvalho Chehab * saa7134 DMA audio start 107b285192aSMauro Carvalho Chehab * 108b285192aSMauro Carvalho Chehab * Called when preparing the capture device for use 109b285192aSMauro Carvalho Chehab * 110b285192aSMauro Carvalho Chehab * - Copied verbatim from saa7134-oss's dsp_dma_start. 111b285192aSMauro Carvalho Chehab * 112b285192aSMauro Carvalho Chehab */ 113b285192aSMauro Carvalho Chehab 114b285192aSMauro Carvalho Chehab static void saa7134_dma_start(struct saa7134_dev *dev) 115b285192aSMauro Carvalho Chehab { 116b285192aSMauro Carvalho Chehab dev->dmasound.dma_blk = 0; 117b285192aSMauro Carvalho Chehab dev->dmasound.dma_running = 1; 118b285192aSMauro Carvalho Chehab saa7134_set_dmabits(dev); 119b285192aSMauro Carvalho Chehab } 120b285192aSMauro Carvalho Chehab 121b285192aSMauro Carvalho Chehab /* 122b285192aSMauro Carvalho Chehab * saa7134 audio DMA IRQ handler 123b285192aSMauro Carvalho Chehab * 124b285192aSMauro Carvalho Chehab * Called whenever we get an SAA7134_IRQ_REPORT_DONE_RA3 interrupt 125b285192aSMauro Carvalho Chehab * Handles shifting between the 2 buffers, manages the read counters, 126b285192aSMauro Carvalho Chehab * and notifies ALSA when periods elapse 127b285192aSMauro Carvalho Chehab * 128b285192aSMauro Carvalho Chehab * - Mostly copied from saa7134-oss's saa7134_irq_oss_done. 129b285192aSMauro Carvalho Chehab * 130b285192aSMauro Carvalho Chehab */ 131b285192aSMauro Carvalho Chehab 132b285192aSMauro Carvalho Chehab static void saa7134_irq_alsa_done(struct saa7134_dev *dev, 133b285192aSMauro Carvalho Chehab unsigned long status) 134b285192aSMauro Carvalho Chehab { 135b285192aSMauro Carvalho Chehab int next_blk, reg = 0; 136b285192aSMauro Carvalho Chehab 137b285192aSMauro Carvalho Chehab spin_lock(&dev->slock); 138b285192aSMauro Carvalho Chehab if (UNSET == dev->dmasound.dma_blk) { 1397592eefcSMauro Carvalho Chehab pr_debug("irq: recording stopped\n"); 140b285192aSMauro Carvalho Chehab goto done; 141b285192aSMauro Carvalho Chehab } 142b285192aSMauro Carvalho Chehab if (0 != (status & 0x0f000000)) 1437592eefcSMauro Carvalho Chehab pr_debug("irq: lost %ld\n", (status >> 24) & 0x0f); 144b285192aSMauro Carvalho Chehab if (0 == (status & 0x10000000)) { 145b285192aSMauro Carvalho Chehab /* odd */ 146b285192aSMauro Carvalho Chehab if (0 == (dev->dmasound.dma_blk & 0x01)) 147b285192aSMauro Carvalho Chehab reg = SAA7134_RS_BA1(6); 148b285192aSMauro Carvalho Chehab } else { 149b285192aSMauro Carvalho Chehab /* even */ 150b285192aSMauro Carvalho Chehab if (1 == (dev->dmasound.dma_blk & 0x01)) 151b285192aSMauro Carvalho Chehab reg = SAA7134_RS_BA2(6); 152b285192aSMauro Carvalho Chehab } 153b285192aSMauro Carvalho Chehab if (0 == reg) { 1547592eefcSMauro Carvalho Chehab pr_debug("irq: field oops [%s]\n", 155b285192aSMauro Carvalho Chehab (status & 0x10000000) ? "even" : "odd"); 156b285192aSMauro Carvalho Chehab goto done; 157b285192aSMauro Carvalho Chehab } 158b285192aSMauro Carvalho Chehab 159b285192aSMauro Carvalho Chehab if (dev->dmasound.read_count >= dev->dmasound.blksize * (dev->dmasound.blocks-2)) { 1606139ebc6SMauro Carvalho Chehab pr_debug("irq: overrun [full=%d/%d] - Blocks in %d\n", 1616139ebc6SMauro Carvalho Chehab dev->dmasound.read_count, 162b285192aSMauro Carvalho Chehab dev->dmasound.bufsize, dev->dmasound.blocks); 163b285192aSMauro Carvalho Chehab spin_unlock(&dev->slock); 1641fb8510cSTakashi Iwai snd_pcm_stop_xrun(dev->dmasound.substream); 165b285192aSMauro Carvalho Chehab return; 166b285192aSMauro Carvalho Chehab } 167b285192aSMauro Carvalho Chehab 168b285192aSMauro Carvalho Chehab /* next block addr */ 169b285192aSMauro Carvalho Chehab next_blk = (dev->dmasound.dma_blk + 2) % dev->dmasound.blocks; 170b285192aSMauro Carvalho Chehab saa_writel(reg,next_blk * dev->dmasound.blksize); 1717592eefcSMauro Carvalho Chehab pr_debug("irq: ok, %s, next_blk=%d, addr=%x, blocks=%u, size=%u, read=%u\n", 172b285192aSMauro Carvalho Chehab (status & 0x10000000) ? "even" : "odd ", next_blk, 1736139ebc6SMauro Carvalho Chehab next_blk * dev->dmasound.blksize, dev->dmasound.blocks, 1746139ebc6SMauro Carvalho Chehab dev->dmasound.blksize, dev->dmasound.read_count); 175b285192aSMauro Carvalho Chehab 176b285192aSMauro Carvalho Chehab /* update status & wake waiting readers */ 177b285192aSMauro Carvalho Chehab dev->dmasound.dma_blk = (dev->dmasound.dma_blk + 1) % dev->dmasound.blocks; 178b285192aSMauro Carvalho Chehab dev->dmasound.read_count += dev->dmasound.blksize; 179b285192aSMauro Carvalho Chehab 180b285192aSMauro Carvalho Chehab dev->dmasound.recording_on = reg; 181b285192aSMauro Carvalho Chehab 182b285192aSMauro Carvalho Chehab if (dev->dmasound.read_count >= snd_pcm_lib_period_bytes(dev->dmasound.substream)) { 183b285192aSMauro Carvalho Chehab spin_unlock(&dev->slock); 184b285192aSMauro Carvalho Chehab snd_pcm_period_elapsed(dev->dmasound.substream); 185b285192aSMauro Carvalho Chehab spin_lock(&dev->slock); 186b285192aSMauro Carvalho Chehab } 187b285192aSMauro Carvalho Chehab 188b285192aSMauro Carvalho Chehab done: 189b285192aSMauro Carvalho Chehab spin_unlock(&dev->slock); 190b285192aSMauro Carvalho Chehab 191b285192aSMauro Carvalho Chehab } 192b285192aSMauro Carvalho Chehab 193b285192aSMauro Carvalho Chehab /* 194b285192aSMauro Carvalho Chehab * IRQ request handler 195b285192aSMauro Carvalho Chehab * 196b285192aSMauro Carvalho Chehab * Runs along with saa7134's IRQ handler, discards anything that isn't 197b285192aSMauro Carvalho Chehab * DMA sound 198b285192aSMauro Carvalho Chehab * 199b285192aSMauro Carvalho Chehab */ 200b285192aSMauro Carvalho Chehab 201b285192aSMauro Carvalho Chehab static irqreturn_t saa7134_alsa_irq(int irq, void *dev_id) 202b285192aSMauro Carvalho Chehab { 203b285192aSMauro Carvalho Chehab struct saa7134_dmasound *dmasound = dev_id; 204b285192aSMauro Carvalho Chehab struct saa7134_dev *dev = dmasound->priv_data; 205b285192aSMauro Carvalho Chehab 206b285192aSMauro Carvalho Chehab unsigned long report, status; 207b285192aSMauro Carvalho Chehab int loop, handled = 0; 208b285192aSMauro Carvalho Chehab 209b285192aSMauro Carvalho Chehab for (loop = 0; loop < 10; loop++) { 210b285192aSMauro Carvalho Chehab report = saa_readl(SAA7134_IRQ_REPORT); 211b285192aSMauro Carvalho Chehab status = saa_readl(SAA7134_IRQ_STATUS); 212b285192aSMauro Carvalho Chehab 213b285192aSMauro Carvalho Chehab if (report & SAA7134_IRQ_REPORT_DONE_RA3) { 214b285192aSMauro Carvalho Chehab handled = 1; 215b285192aSMauro Carvalho Chehab saa_writel(SAA7134_IRQ_REPORT, 216b285192aSMauro Carvalho Chehab SAA7134_IRQ_REPORT_DONE_RA3); 217b285192aSMauro Carvalho Chehab saa7134_irq_alsa_done(dev, status); 218b285192aSMauro Carvalho Chehab } else { 219b285192aSMauro Carvalho Chehab goto out; 220b285192aSMauro Carvalho Chehab } 221b285192aSMauro Carvalho Chehab } 222b285192aSMauro Carvalho Chehab 223b285192aSMauro Carvalho Chehab if (loop == 10) { 2247592eefcSMauro Carvalho Chehab pr_debug("error! looping IRQ!"); 225b285192aSMauro Carvalho Chehab } 226b285192aSMauro Carvalho Chehab 227b285192aSMauro Carvalho Chehab out: 228b285192aSMauro Carvalho Chehab return IRQ_RETVAL(handled); 229b285192aSMauro Carvalho Chehab } 230b285192aSMauro Carvalho Chehab 231b285192aSMauro Carvalho Chehab /* 232b285192aSMauro Carvalho Chehab * ALSA capture trigger 233b285192aSMauro Carvalho Chehab * 234b285192aSMauro Carvalho Chehab * - One of the ALSA capture callbacks. 235b285192aSMauro Carvalho Chehab * 236b285192aSMauro Carvalho Chehab * Called whenever a capture is started or stopped. Must be defined, 237b285192aSMauro Carvalho Chehab * but there's nothing we want to do here 238b285192aSMauro Carvalho Chehab * 239b285192aSMauro Carvalho Chehab */ 240b285192aSMauro Carvalho Chehab 241b285192aSMauro Carvalho Chehab static int snd_card_saa7134_capture_trigger(struct snd_pcm_substream * substream, 242b285192aSMauro Carvalho Chehab int cmd) 243b285192aSMauro Carvalho Chehab { 244b285192aSMauro Carvalho Chehab struct snd_pcm_runtime *runtime = substream->runtime; 245b285192aSMauro Carvalho Chehab snd_card_saa7134_pcm_t *pcm = runtime->private_data; 246b285192aSMauro Carvalho Chehab struct saa7134_dev *dev=pcm->dev; 247b285192aSMauro Carvalho Chehab int err = 0; 248b285192aSMauro Carvalho Chehab 249b285192aSMauro Carvalho Chehab spin_lock(&dev->slock); 250b285192aSMauro Carvalho Chehab if (cmd == SNDRV_PCM_TRIGGER_START) { 251b285192aSMauro Carvalho Chehab /* start dma */ 252b285192aSMauro Carvalho Chehab saa7134_dma_start(dev); 253b285192aSMauro Carvalho Chehab } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { 254b285192aSMauro Carvalho Chehab /* stop dma */ 255b285192aSMauro Carvalho Chehab saa7134_dma_stop(dev); 256b285192aSMauro Carvalho Chehab } else { 257b285192aSMauro Carvalho Chehab err = -EINVAL; 258b285192aSMauro Carvalho Chehab } 259b285192aSMauro Carvalho Chehab spin_unlock(&dev->slock); 260b285192aSMauro Carvalho Chehab 261b285192aSMauro Carvalho Chehab return err; 262b285192aSMauro Carvalho Chehab } 263b285192aSMauro Carvalho Chehab 26415e64f0dSHans Verkuil static int saa7134_alsa_dma_init(struct saa7134_dev *dev, int nr_pages) 26515e64f0dSHans Verkuil { 26615e64f0dSHans Verkuil struct saa7134_dmasound *dma = &dev->dmasound; 26715e64f0dSHans Verkuil struct page *pg; 26815e64f0dSHans Verkuil int i; 26915e64f0dSHans Verkuil 27015e64f0dSHans Verkuil dma->vaddr = vmalloc_32(nr_pages << PAGE_SHIFT); 27115e64f0dSHans Verkuil if (NULL == dma->vaddr) { 2727592eefcSMauro Carvalho Chehab pr_debug("vmalloc_32(%d pages) failed\n", nr_pages); 27315e64f0dSHans Verkuil return -ENOMEM; 27415e64f0dSHans Verkuil } 27515e64f0dSHans Verkuil 276*6062ba61SMauro Carvalho Chehab pr_debug("vmalloc is at addr %p, size=%d\n", 277*6062ba61SMauro Carvalho Chehab dma->vaddr, nr_pages << PAGE_SHIFT); 27815e64f0dSHans Verkuil 27915e64f0dSHans Verkuil memset(dma->vaddr, 0, nr_pages << PAGE_SHIFT); 28015e64f0dSHans Verkuil dma->nr_pages = nr_pages; 28115e64f0dSHans Verkuil 28215e64f0dSHans Verkuil dma->sglist = vzalloc(dma->nr_pages * sizeof(*dma->sglist)); 28315e64f0dSHans Verkuil if (NULL == dma->sglist) 28415e64f0dSHans Verkuil goto vzalloc_err; 28515e64f0dSHans Verkuil 28615e64f0dSHans Verkuil sg_init_table(dma->sglist, dma->nr_pages); 28715e64f0dSHans Verkuil for (i = 0; i < dma->nr_pages; i++) { 28815e64f0dSHans Verkuil pg = vmalloc_to_page(dma->vaddr + i * PAGE_SIZE); 28915e64f0dSHans Verkuil if (NULL == pg) 29015e64f0dSHans Verkuil goto vmalloc_to_page_err; 29115e64f0dSHans Verkuil sg_set_page(&dma->sglist[i], pg, PAGE_SIZE, 0); 29215e64f0dSHans Verkuil } 29315e64f0dSHans Verkuil return 0; 29415e64f0dSHans Verkuil 29515e64f0dSHans Verkuil vmalloc_to_page_err: 29615e64f0dSHans Verkuil vfree(dma->sglist); 29715e64f0dSHans Verkuil dma->sglist = NULL; 29815e64f0dSHans Verkuil vzalloc_err: 29915e64f0dSHans Verkuil vfree(dma->vaddr); 30015e64f0dSHans Verkuil dma->vaddr = NULL; 30115e64f0dSHans Verkuil return -ENOMEM; 30215e64f0dSHans Verkuil } 30315e64f0dSHans Verkuil 30415e64f0dSHans Verkuil static int saa7134_alsa_dma_map(struct saa7134_dev *dev) 30515e64f0dSHans Verkuil { 30615e64f0dSHans Verkuil struct saa7134_dmasound *dma = &dev->dmasound; 30715e64f0dSHans Verkuil 30815e64f0dSHans Verkuil dma->sglen = dma_map_sg(&dev->pci->dev, dma->sglist, 30915e64f0dSHans Verkuil dma->nr_pages, PCI_DMA_FROMDEVICE); 31015e64f0dSHans Verkuil 31115e64f0dSHans Verkuil if (0 == dma->sglen) { 31215e64f0dSHans Verkuil pr_warn("%s: saa7134_alsa_map_sg failed\n", __func__); 31315e64f0dSHans Verkuil return -ENOMEM; 31415e64f0dSHans Verkuil } 31515e64f0dSHans Verkuil return 0; 31615e64f0dSHans Verkuil } 31715e64f0dSHans Verkuil 31815e64f0dSHans Verkuil static int saa7134_alsa_dma_unmap(struct saa7134_dev *dev) 31915e64f0dSHans Verkuil { 32015e64f0dSHans Verkuil struct saa7134_dmasound *dma = &dev->dmasound; 32115e64f0dSHans Verkuil 32215e64f0dSHans Verkuil if (!dma->sglen) 32315e64f0dSHans Verkuil return 0; 32415e64f0dSHans Verkuil 32515e64f0dSHans Verkuil dma_unmap_sg(&dev->pci->dev, dma->sglist, dma->sglen, PCI_DMA_FROMDEVICE); 32615e64f0dSHans Verkuil dma->sglen = 0; 32715e64f0dSHans Verkuil return 0; 32815e64f0dSHans Verkuil } 32915e64f0dSHans Verkuil 33015e64f0dSHans Verkuil static int saa7134_alsa_dma_free(struct saa7134_dmasound *dma) 33115e64f0dSHans Verkuil { 33215e64f0dSHans Verkuil vfree(dma->sglist); 33315e64f0dSHans Verkuil dma->sglist = NULL; 33415e64f0dSHans Verkuil vfree(dma->vaddr); 33515e64f0dSHans Verkuil dma->vaddr = NULL; 33615e64f0dSHans Verkuil return 0; 33715e64f0dSHans Verkuil } 33815e64f0dSHans Verkuil 339b285192aSMauro Carvalho Chehab /* 340b285192aSMauro Carvalho Chehab * DMA buffer initialization 341b285192aSMauro Carvalho Chehab * 342b285192aSMauro Carvalho Chehab * Uses V4L functions to initialize the DMA. Shouldn't be necessary in 343b285192aSMauro Carvalho Chehab * ALSA, but I was unable to use ALSA's own DMA, and had to force the 344b285192aSMauro Carvalho Chehab * usage of V4L's 345b285192aSMauro Carvalho Chehab * 346b285192aSMauro Carvalho Chehab * - Copied verbatim from saa7134-oss. 347b285192aSMauro Carvalho Chehab * 348b285192aSMauro Carvalho Chehab */ 349b285192aSMauro Carvalho Chehab 350b285192aSMauro Carvalho Chehab static int dsp_buffer_init(struct saa7134_dev *dev) 351b285192aSMauro Carvalho Chehab { 352b285192aSMauro Carvalho Chehab int err; 353b285192aSMauro Carvalho Chehab 354b285192aSMauro Carvalho Chehab BUG_ON(!dev->dmasound.bufsize); 355b285192aSMauro Carvalho Chehab 35615e64f0dSHans Verkuil err = saa7134_alsa_dma_init(dev, 357b285192aSMauro Carvalho Chehab (dev->dmasound.bufsize + PAGE_SIZE) >> PAGE_SHIFT); 358b285192aSMauro Carvalho Chehab if (0 != err) 359b285192aSMauro Carvalho Chehab return err; 360b285192aSMauro Carvalho Chehab return 0; 361b285192aSMauro Carvalho Chehab } 362b285192aSMauro Carvalho Chehab 363b285192aSMauro Carvalho Chehab /* 364b285192aSMauro Carvalho Chehab * DMA buffer release 365b285192aSMauro Carvalho Chehab * 366b285192aSMauro Carvalho Chehab * Called after closing the device, during snd_card_saa7134_capture_close 367b285192aSMauro Carvalho Chehab * 368b285192aSMauro Carvalho Chehab */ 369b285192aSMauro Carvalho Chehab 370b285192aSMauro Carvalho Chehab static int dsp_buffer_free(struct saa7134_dev *dev) 371b285192aSMauro Carvalho Chehab { 372b285192aSMauro Carvalho Chehab BUG_ON(!dev->dmasound.blksize); 373b285192aSMauro Carvalho Chehab 37415e64f0dSHans Verkuil saa7134_alsa_dma_free(&dev->dmasound); 375b285192aSMauro Carvalho Chehab 376b285192aSMauro Carvalho Chehab dev->dmasound.blocks = 0; 377b285192aSMauro Carvalho Chehab dev->dmasound.blksize = 0; 378b285192aSMauro Carvalho Chehab dev->dmasound.bufsize = 0; 379b285192aSMauro Carvalho Chehab 380b285192aSMauro Carvalho Chehab return 0; 381b285192aSMauro Carvalho Chehab } 382b285192aSMauro Carvalho Chehab 383b285192aSMauro Carvalho Chehab /* 384b285192aSMauro Carvalho Chehab * Setting the capture source and updating the ALSA controls 385b285192aSMauro Carvalho Chehab */ 386b285192aSMauro Carvalho Chehab static int snd_saa7134_capsrc_set(struct snd_kcontrol *kcontrol, 387b285192aSMauro Carvalho Chehab int left, int right, bool force_notify) 388b285192aSMauro Carvalho Chehab { 389b285192aSMauro Carvalho Chehab snd_card_saa7134_t *chip = snd_kcontrol_chip(kcontrol); 390b285192aSMauro Carvalho Chehab int change = 0, addr = kcontrol->private_value; 391b285192aSMauro Carvalho Chehab int active, old_addr; 392b285192aSMauro Carvalho Chehab u32 anabar, xbarin; 393b285192aSMauro Carvalho Chehab int analog_io, rate; 394b285192aSMauro Carvalho Chehab struct saa7134_dev *dev; 395b285192aSMauro Carvalho Chehab 396b285192aSMauro Carvalho Chehab dev = chip->dev; 397b285192aSMauro Carvalho Chehab 398b285192aSMauro Carvalho Chehab spin_lock_irq(&chip->mixer_lock); 399b285192aSMauro Carvalho Chehab 400b285192aSMauro Carvalho Chehab active = left != 0 || right != 0; 401b285192aSMauro Carvalho Chehab old_addr = chip->capture_source_addr; 402b285192aSMauro Carvalho Chehab 403b285192aSMauro Carvalho Chehab /* The active capture source cannot be deactivated */ 404b285192aSMauro Carvalho Chehab if (active) { 405b285192aSMauro Carvalho Chehab change = old_addr != addr || 406b285192aSMauro Carvalho Chehab chip->capture_source[0] != left || 407b285192aSMauro Carvalho Chehab chip->capture_source[1] != right; 408b285192aSMauro Carvalho Chehab 409b285192aSMauro Carvalho Chehab chip->capture_source[0] = left; 410b285192aSMauro Carvalho Chehab chip->capture_source[1] = right; 411b285192aSMauro Carvalho Chehab chip->capture_source_addr = addr; 412b285192aSMauro Carvalho Chehab dev->dmasound.input = addr; 413b285192aSMauro Carvalho Chehab } 414b285192aSMauro Carvalho Chehab spin_unlock_irq(&chip->mixer_lock); 415b285192aSMauro Carvalho Chehab 416b285192aSMauro Carvalho Chehab if (change) { 417b285192aSMauro Carvalho Chehab switch (dev->pci->device) { 418b285192aSMauro Carvalho Chehab 419b285192aSMauro Carvalho Chehab case PCI_DEVICE_ID_PHILIPS_SAA7134: 420b285192aSMauro Carvalho Chehab switch (addr) { 421b285192aSMauro Carvalho Chehab case MIXER_ADDR_TVTUNER: 422b285192aSMauro Carvalho Chehab saa_andorb(SAA7134_AUDIO_FORMAT_CTRL, 423b285192aSMauro Carvalho Chehab 0xc0, 0xc0); 424b285192aSMauro Carvalho Chehab saa_andorb(SAA7134_SIF_SAMPLE_FREQ, 425b285192aSMauro Carvalho Chehab 0x03, 0x00); 426b285192aSMauro Carvalho Chehab break; 427b285192aSMauro Carvalho Chehab case MIXER_ADDR_LINE1: 428b285192aSMauro Carvalho Chehab case MIXER_ADDR_LINE2: 429b285192aSMauro Carvalho Chehab analog_io = (MIXER_ADDR_LINE1 == addr) ? 430b285192aSMauro Carvalho Chehab 0x00 : 0x08; 431b285192aSMauro Carvalho Chehab rate = (32000 == dev->dmasound.rate) ? 432b285192aSMauro Carvalho Chehab 0x01 : 0x03; 433b285192aSMauro Carvalho Chehab saa_andorb(SAA7134_ANALOG_IO_SELECT, 434b285192aSMauro Carvalho Chehab 0x08, analog_io); 435b285192aSMauro Carvalho Chehab saa_andorb(SAA7134_AUDIO_FORMAT_CTRL, 436b285192aSMauro Carvalho Chehab 0xc0, 0x80); 437b285192aSMauro Carvalho Chehab saa_andorb(SAA7134_SIF_SAMPLE_FREQ, 438b285192aSMauro Carvalho Chehab 0x03, rate); 439b285192aSMauro Carvalho Chehab break; 440b285192aSMauro Carvalho Chehab } 441b285192aSMauro Carvalho Chehab 442b285192aSMauro Carvalho Chehab break; 443b285192aSMauro Carvalho Chehab case PCI_DEVICE_ID_PHILIPS_SAA7133: 444b285192aSMauro Carvalho Chehab case PCI_DEVICE_ID_PHILIPS_SAA7135: 445b285192aSMauro Carvalho Chehab xbarin = 0x03; /* adc */ 446b285192aSMauro Carvalho Chehab anabar = 0; 447b285192aSMauro Carvalho Chehab switch (addr) { 448b285192aSMauro Carvalho Chehab case MIXER_ADDR_TVTUNER: 449b285192aSMauro Carvalho Chehab xbarin = 0; /* Demodulator */ 450b285192aSMauro Carvalho Chehab anabar = 2; /* DACs */ 451b285192aSMauro Carvalho Chehab break; 452b285192aSMauro Carvalho Chehab case MIXER_ADDR_LINE1: 453b285192aSMauro Carvalho Chehab anabar = 0; /* aux1, aux1 */ 454b285192aSMauro Carvalho Chehab break; 455b285192aSMauro Carvalho Chehab case MIXER_ADDR_LINE2: 456b285192aSMauro Carvalho Chehab anabar = 9; /* aux2, aux2 */ 457b285192aSMauro Carvalho Chehab break; 458b285192aSMauro Carvalho Chehab } 459b285192aSMauro Carvalho Chehab 460b285192aSMauro Carvalho Chehab /* output xbar always main channel */ 461b285192aSMauro Carvalho Chehab saa_dsp_writel(dev, SAA7133_DIGITAL_OUTPUT_SEL1, 462b285192aSMauro Carvalho Chehab 0xbbbb10); 463b285192aSMauro Carvalho Chehab 464b285192aSMauro Carvalho Chehab if (left || right) { 465b285192aSMauro Carvalho Chehab /* We've got data, turn the input on */ 466b285192aSMauro Carvalho Chehab saa_dsp_writel(dev, SAA7133_DIGITAL_INPUT_XBAR1, 467b285192aSMauro Carvalho Chehab xbarin); 468b285192aSMauro Carvalho Chehab saa_writel(SAA7133_ANALOG_IO_SELECT, anabar); 469b285192aSMauro Carvalho Chehab } else { 470b285192aSMauro Carvalho Chehab saa_dsp_writel(dev, SAA7133_DIGITAL_INPUT_XBAR1, 471b285192aSMauro Carvalho Chehab 0); 472b285192aSMauro Carvalho Chehab saa_writel(SAA7133_ANALOG_IO_SELECT, 0); 473b285192aSMauro Carvalho Chehab } 474b285192aSMauro Carvalho Chehab break; 475b285192aSMauro Carvalho Chehab } 476b285192aSMauro Carvalho Chehab } 477b285192aSMauro Carvalho Chehab 478b285192aSMauro Carvalho Chehab if (change) { 479b285192aSMauro Carvalho Chehab if (force_notify) 480b285192aSMauro Carvalho Chehab snd_ctl_notify(chip->card, 481b285192aSMauro Carvalho Chehab SNDRV_CTL_EVENT_MASK_VALUE, 482b285192aSMauro Carvalho Chehab &chip->capture_ctl[addr]->id); 483b285192aSMauro Carvalho Chehab 484b285192aSMauro Carvalho Chehab if (old_addr != MIXER_ADDR_UNSELECTED && old_addr != addr) 485b285192aSMauro Carvalho Chehab snd_ctl_notify(chip->card, 486b285192aSMauro Carvalho Chehab SNDRV_CTL_EVENT_MASK_VALUE, 487b285192aSMauro Carvalho Chehab &chip->capture_ctl[old_addr]->id); 488b285192aSMauro Carvalho Chehab } 489b285192aSMauro Carvalho Chehab 490b285192aSMauro Carvalho Chehab return change; 491b285192aSMauro Carvalho Chehab } 492b285192aSMauro Carvalho Chehab 493b285192aSMauro Carvalho Chehab /* 494b285192aSMauro Carvalho Chehab * ALSA PCM preparation 495b285192aSMauro Carvalho Chehab * 496b285192aSMauro Carvalho Chehab * - One of the ALSA capture callbacks. 497b285192aSMauro Carvalho Chehab * 498b285192aSMauro Carvalho Chehab * Called right after the capture device is opened, this function configures 499b285192aSMauro Carvalho Chehab * the buffer using the previously defined functions, allocates the memory, 500b285192aSMauro Carvalho Chehab * sets up the hardware registers, and then starts the DMA. When this function 501b285192aSMauro Carvalho Chehab * returns, the audio should be flowing. 502b285192aSMauro Carvalho Chehab * 503b285192aSMauro Carvalho Chehab */ 504b285192aSMauro Carvalho Chehab 505b285192aSMauro Carvalho Chehab static int snd_card_saa7134_capture_prepare(struct snd_pcm_substream * substream) 506b285192aSMauro Carvalho Chehab { 507b285192aSMauro Carvalho Chehab struct snd_pcm_runtime *runtime = substream->runtime; 508b285192aSMauro Carvalho Chehab int bswap, sign; 509b285192aSMauro Carvalho Chehab u32 fmt, control; 510b285192aSMauro Carvalho Chehab snd_card_saa7134_t *saa7134 = snd_pcm_substream_chip(substream); 511b285192aSMauro Carvalho Chehab struct saa7134_dev *dev; 512b285192aSMauro Carvalho Chehab snd_card_saa7134_pcm_t *pcm = runtime->private_data; 513b285192aSMauro Carvalho Chehab 514b285192aSMauro Carvalho Chehab pcm->dev->dmasound.substream = substream; 515b285192aSMauro Carvalho Chehab 516b285192aSMauro Carvalho Chehab dev = saa7134->dev; 517b285192aSMauro Carvalho Chehab 518b285192aSMauro Carvalho Chehab if (snd_pcm_format_width(runtime->format) == 8) 519b285192aSMauro Carvalho Chehab fmt = 0x00; 520b285192aSMauro Carvalho Chehab else 521b285192aSMauro Carvalho Chehab fmt = 0x01; 522b285192aSMauro Carvalho Chehab 523b285192aSMauro Carvalho Chehab if (snd_pcm_format_signed(runtime->format)) 524b285192aSMauro Carvalho Chehab sign = 1; 525b285192aSMauro Carvalho Chehab else 526b285192aSMauro Carvalho Chehab sign = 0; 527b285192aSMauro Carvalho Chehab 528b285192aSMauro Carvalho Chehab if (snd_pcm_format_big_endian(runtime->format)) 529b285192aSMauro Carvalho Chehab bswap = 1; 530b285192aSMauro Carvalho Chehab else 531b285192aSMauro Carvalho Chehab bswap = 0; 532b285192aSMauro Carvalho Chehab 533b285192aSMauro Carvalho Chehab switch (dev->pci->device) { 534b285192aSMauro Carvalho Chehab case PCI_DEVICE_ID_PHILIPS_SAA7134: 535b285192aSMauro Carvalho Chehab if (1 == runtime->channels) 536b285192aSMauro Carvalho Chehab fmt |= (1 << 3); 537b285192aSMauro Carvalho Chehab if (2 == runtime->channels) 538b285192aSMauro Carvalho Chehab fmt |= (3 << 3); 539b285192aSMauro Carvalho Chehab if (sign) 540b285192aSMauro Carvalho Chehab fmt |= 0x04; 541b285192aSMauro Carvalho Chehab 542b285192aSMauro Carvalho Chehab fmt |= (MIXER_ADDR_TVTUNER == dev->dmasound.input) ? 0xc0 : 0x80; 543b285192aSMauro Carvalho Chehab saa_writeb(SAA7134_NUM_SAMPLES0, ((dev->dmasound.blksize - 1) & 0x0000ff)); 544b285192aSMauro Carvalho Chehab saa_writeb(SAA7134_NUM_SAMPLES1, ((dev->dmasound.blksize - 1) & 0x00ff00) >> 8); 545b285192aSMauro Carvalho Chehab saa_writeb(SAA7134_NUM_SAMPLES2, ((dev->dmasound.blksize - 1) & 0xff0000) >> 16); 546b285192aSMauro Carvalho Chehab saa_writeb(SAA7134_AUDIO_FORMAT_CTRL, fmt); 547b285192aSMauro Carvalho Chehab 548b285192aSMauro Carvalho Chehab break; 549b285192aSMauro Carvalho Chehab case PCI_DEVICE_ID_PHILIPS_SAA7133: 550b285192aSMauro Carvalho Chehab case PCI_DEVICE_ID_PHILIPS_SAA7135: 551b285192aSMauro Carvalho Chehab if (1 == runtime->channels) 552b285192aSMauro Carvalho Chehab fmt |= (1 << 4); 553b285192aSMauro Carvalho Chehab if (2 == runtime->channels) 554b285192aSMauro Carvalho Chehab fmt |= (2 << 4); 555b285192aSMauro Carvalho Chehab if (!sign) 556b285192aSMauro Carvalho Chehab fmt |= 0x04; 557b285192aSMauro Carvalho Chehab saa_writel(SAA7133_NUM_SAMPLES, dev->dmasound.blksize -1); 558b285192aSMauro Carvalho Chehab saa_writel(SAA7133_AUDIO_CHANNEL, 0x543210 | (fmt << 24)); 559b285192aSMauro Carvalho Chehab break; 560b285192aSMauro Carvalho Chehab } 561b285192aSMauro Carvalho Chehab 5627592eefcSMauro Carvalho Chehab pr_debug("rec_start: afmt=%d ch=%d => fmt=0x%x swap=%c\n", 563b285192aSMauro Carvalho Chehab runtime->format, runtime->channels, fmt, 564b285192aSMauro Carvalho Chehab bswap ? 'b' : '-'); 565b285192aSMauro Carvalho Chehab /* dma: setup channel 6 (= AUDIO) */ 566b285192aSMauro Carvalho Chehab control = SAA7134_RS_CONTROL_BURST_16 | 567b285192aSMauro Carvalho Chehab SAA7134_RS_CONTROL_ME | 568b285192aSMauro Carvalho Chehab (dev->dmasound.pt.dma >> 12); 569b285192aSMauro Carvalho Chehab if (bswap) 570b285192aSMauro Carvalho Chehab control |= SAA7134_RS_CONTROL_BSWAP; 571b285192aSMauro Carvalho Chehab 572b285192aSMauro Carvalho Chehab saa_writel(SAA7134_RS_BA1(6),0); 573b285192aSMauro Carvalho Chehab saa_writel(SAA7134_RS_BA2(6),dev->dmasound.blksize); 574b285192aSMauro Carvalho Chehab saa_writel(SAA7134_RS_PITCH(6),0); 575b285192aSMauro Carvalho Chehab saa_writel(SAA7134_RS_CONTROL(6),control); 576b285192aSMauro Carvalho Chehab 577b285192aSMauro Carvalho Chehab dev->dmasound.rate = runtime->rate; 578b285192aSMauro Carvalho Chehab 579b285192aSMauro Carvalho Chehab /* Setup and update the card/ALSA controls */ 580b285192aSMauro Carvalho Chehab snd_saa7134_capsrc_set(saa7134->capture_ctl[dev->dmasound.input], 1, 1, 581b285192aSMauro Carvalho Chehab true); 582b285192aSMauro Carvalho Chehab 583b285192aSMauro Carvalho Chehab return 0; 584b285192aSMauro Carvalho Chehab 585b285192aSMauro Carvalho Chehab } 586b285192aSMauro Carvalho Chehab 587b285192aSMauro Carvalho Chehab /* 588b285192aSMauro Carvalho Chehab * ALSA pointer fetching 589b285192aSMauro Carvalho Chehab * 590b285192aSMauro Carvalho Chehab * - One of the ALSA capture callbacks. 591b285192aSMauro Carvalho Chehab * 592b285192aSMauro Carvalho Chehab * Called whenever a period elapses, it must return the current hardware 593b285192aSMauro Carvalho Chehab * position of the buffer. 594b285192aSMauro Carvalho Chehab * Also resets the read counter used to prevent overruns 595b285192aSMauro Carvalho Chehab * 596b285192aSMauro Carvalho Chehab */ 597b285192aSMauro Carvalho Chehab 598b285192aSMauro Carvalho Chehab static snd_pcm_uframes_t 599b285192aSMauro Carvalho Chehab snd_card_saa7134_capture_pointer(struct snd_pcm_substream * substream) 600b285192aSMauro Carvalho Chehab { 601b285192aSMauro Carvalho Chehab struct snd_pcm_runtime *runtime = substream->runtime; 602b285192aSMauro Carvalho Chehab snd_card_saa7134_pcm_t *pcm = runtime->private_data; 603b285192aSMauro Carvalho Chehab struct saa7134_dev *dev=pcm->dev; 604b285192aSMauro Carvalho Chehab 605b285192aSMauro Carvalho Chehab if (dev->dmasound.read_count) { 606b285192aSMauro Carvalho Chehab dev->dmasound.read_count -= snd_pcm_lib_period_bytes(substream); 607b285192aSMauro Carvalho Chehab dev->dmasound.read_offset += snd_pcm_lib_period_bytes(substream); 608b285192aSMauro Carvalho Chehab if (dev->dmasound.read_offset == dev->dmasound.bufsize) 609b285192aSMauro Carvalho Chehab dev->dmasound.read_offset = 0; 610b285192aSMauro Carvalho Chehab } 611b285192aSMauro Carvalho Chehab 612b285192aSMauro Carvalho Chehab return bytes_to_frames(runtime, dev->dmasound.read_offset); 613b285192aSMauro Carvalho Chehab } 614b285192aSMauro Carvalho Chehab 615b285192aSMauro Carvalho Chehab /* 616b285192aSMauro Carvalho Chehab * ALSA hardware capabilities definition 617b285192aSMauro Carvalho Chehab * 618b285192aSMauro Carvalho Chehab * Report only 32kHz for ALSA: 619b285192aSMauro Carvalho Chehab * 620b285192aSMauro Carvalho Chehab * - SAA7133/35 uses DDEP (DemDec Easy Programming mode), which works in 32kHz 621b285192aSMauro Carvalho Chehab * only 622b285192aSMauro Carvalho Chehab * - SAA7134 for TV mode uses DemDec mode (32kHz) 623b285192aSMauro Carvalho Chehab * - Radio works in 32kHz only 624b285192aSMauro Carvalho Chehab * - When recording 48kHz from Line1/Line2, switching of capture source to TV 625b285192aSMauro Carvalho Chehab * means 626b285192aSMauro Carvalho Chehab * switching to 32kHz without any frequency translation 627b285192aSMauro Carvalho Chehab */ 628b285192aSMauro Carvalho Chehab 62947f14314SBhumika Goyal static const struct snd_pcm_hardware snd_card_saa7134_capture = 630b285192aSMauro Carvalho Chehab { 631b285192aSMauro Carvalho Chehab .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 632b285192aSMauro Carvalho Chehab SNDRV_PCM_INFO_BLOCK_TRANSFER | 633b285192aSMauro Carvalho Chehab SNDRV_PCM_INFO_MMAP_VALID), 634b285192aSMauro Carvalho Chehab .formats = SNDRV_PCM_FMTBIT_S16_LE | \ 635b285192aSMauro Carvalho Chehab SNDRV_PCM_FMTBIT_S16_BE | \ 636b285192aSMauro Carvalho Chehab SNDRV_PCM_FMTBIT_S8 | \ 637b285192aSMauro Carvalho Chehab SNDRV_PCM_FMTBIT_U8 | \ 638b285192aSMauro Carvalho Chehab SNDRV_PCM_FMTBIT_U16_LE | \ 639b285192aSMauro Carvalho Chehab SNDRV_PCM_FMTBIT_U16_BE, 640b285192aSMauro Carvalho Chehab .rates = SNDRV_PCM_RATE_32000, 641b285192aSMauro Carvalho Chehab .rate_min = 32000, 642b285192aSMauro Carvalho Chehab .rate_max = 32000, 643b285192aSMauro Carvalho Chehab .channels_min = 1, 644b285192aSMauro Carvalho Chehab .channels_max = 2, 645b285192aSMauro Carvalho Chehab .buffer_bytes_max = (256*1024), 646b285192aSMauro Carvalho Chehab .period_bytes_min = 64, 647b285192aSMauro Carvalho Chehab .period_bytes_max = (256*1024), 648b285192aSMauro Carvalho Chehab .periods_min = 4, 649b285192aSMauro Carvalho Chehab .periods_max = 1024, 650b285192aSMauro Carvalho Chehab }; 651b285192aSMauro Carvalho Chehab 652b285192aSMauro Carvalho Chehab static void snd_card_saa7134_runtime_free(struct snd_pcm_runtime *runtime) 653b285192aSMauro Carvalho Chehab { 654b285192aSMauro Carvalho Chehab snd_card_saa7134_pcm_t *pcm = runtime->private_data; 655b285192aSMauro Carvalho Chehab 656b285192aSMauro Carvalho Chehab kfree(pcm); 657b285192aSMauro Carvalho Chehab } 658b285192aSMauro Carvalho Chehab 659b285192aSMauro Carvalho Chehab 660b285192aSMauro Carvalho Chehab /* 661b285192aSMauro Carvalho Chehab * ALSA hardware params 662b285192aSMauro Carvalho Chehab * 663b285192aSMauro Carvalho Chehab * - One of the ALSA capture callbacks. 664b285192aSMauro Carvalho Chehab * 665b285192aSMauro Carvalho Chehab * Called on initialization, right before the PCM preparation 666b285192aSMauro Carvalho Chehab * 667b285192aSMauro Carvalho Chehab */ 668b285192aSMauro Carvalho Chehab 669b285192aSMauro Carvalho Chehab static int snd_card_saa7134_hw_params(struct snd_pcm_substream * substream, 670b285192aSMauro Carvalho Chehab struct snd_pcm_hw_params * hw_params) 671b285192aSMauro Carvalho Chehab { 672b285192aSMauro Carvalho Chehab snd_card_saa7134_t *saa7134 = snd_pcm_substream_chip(substream); 673b285192aSMauro Carvalho Chehab struct saa7134_dev *dev; 674b285192aSMauro Carvalho Chehab unsigned int period_size, periods; 675b285192aSMauro Carvalho Chehab int err; 676b285192aSMauro Carvalho Chehab 677b285192aSMauro Carvalho Chehab period_size = params_period_bytes(hw_params); 678b285192aSMauro Carvalho Chehab periods = params_periods(hw_params); 679b285192aSMauro Carvalho Chehab 680b285192aSMauro Carvalho Chehab if (period_size < 0x100 || period_size > 0x10000) 681b285192aSMauro Carvalho Chehab return -EINVAL; 682b285192aSMauro Carvalho Chehab if (periods < 4) 683b285192aSMauro Carvalho Chehab return -EINVAL; 684b285192aSMauro Carvalho Chehab if (period_size * periods > 1024 * 1024) 685b285192aSMauro Carvalho Chehab return -EINVAL; 686b285192aSMauro Carvalho Chehab 687b285192aSMauro Carvalho Chehab dev = saa7134->dev; 688b285192aSMauro Carvalho Chehab 689b285192aSMauro Carvalho Chehab if (dev->dmasound.blocks == periods && 690b285192aSMauro Carvalho Chehab dev->dmasound.blksize == period_size) 691b285192aSMauro Carvalho Chehab return 0; 692b285192aSMauro Carvalho Chehab 693b285192aSMauro Carvalho Chehab /* release the old buffer */ 694b285192aSMauro Carvalho Chehab if (substream->runtime->dma_area) { 695b285192aSMauro Carvalho Chehab saa7134_pgtable_free(dev->pci, &dev->dmasound.pt); 69615e64f0dSHans Verkuil saa7134_alsa_dma_unmap(dev); 697b285192aSMauro Carvalho Chehab dsp_buffer_free(dev); 698b285192aSMauro Carvalho Chehab substream->runtime->dma_area = NULL; 699b285192aSMauro Carvalho Chehab } 700b285192aSMauro Carvalho Chehab dev->dmasound.blocks = periods; 701b285192aSMauro Carvalho Chehab dev->dmasound.blksize = period_size; 702b285192aSMauro Carvalho Chehab dev->dmasound.bufsize = period_size * periods; 703b285192aSMauro Carvalho Chehab 704b285192aSMauro Carvalho Chehab err = dsp_buffer_init(dev); 705b285192aSMauro Carvalho Chehab if (0 != err) { 706b285192aSMauro Carvalho Chehab dev->dmasound.blocks = 0; 707b285192aSMauro Carvalho Chehab dev->dmasound.blksize = 0; 708b285192aSMauro Carvalho Chehab dev->dmasound.bufsize = 0; 709b285192aSMauro Carvalho Chehab return err; 710b285192aSMauro Carvalho Chehab } 711b285192aSMauro Carvalho Chehab 71215e64f0dSHans Verkuil err = saa7134_alsa_dma_map(dev); 713e72936d2SHans Verkuil if (err) { 714b285192aSMauro Carvalho Chehab dsp_buffer_free(dev); 715b285192aSMauro Carvalho Chehab return err; 716b285192aSMauro Carvalho Chehab } 717e72936d2SHans Verkuil err = saa7134_pgtable_alloc(dev->pci, &dev->dmasound.pt); 718e72936d2SHans Verkuil if (err) { 71915e64f0dSHans Verkuil saa7134_alsa_dma_unmap(dev); 720b285192aSMauro Carvalho Chehab dsp_buffer_free(dev); 721b285192aSMauro Carvalho Chehab return err; 722b285192aSMauro Carvalho Chehab } 723e72936d2SHans Verkuil err = saa7134_pgtable_build(dev->pci, &dev->dmasound.pt, 724e72936d2SHans Verkuil dev->dmasound.sglist, dev->dmasound.sglen, 0); 725e72936d2SHans Verkuil if (err) { 726b285192aSMauro Carvalho Chehab saa7134_pgtable_free(dev->pci, &dev->dmasound.pt); 72715e64f0dSHans Verkuil saa7134_alsa_dma_unmap(dev); 728b285192aSMauro Carvalho Chehab dsp_buffer_free(dev); 729b285192aSMauro Carvalho Chehab return err; 730b285192aSMauro Carvalho Chehab } 731b285192aSMauro Carvalho Chehab 732b285192aSMauro Carvalho Chehab /* I should be able to use runtime->dma_addr in the control 733b285192aSMauro Carvalho Chehab byte, but it doesn't work. So I allocate the DMA using the 734b285192aSMauro Carvalho Chehab V4L functions, and force ALSA to use that as the DMA area */ 735b285192aSMauro Carvalho Chehab 73615e64f0dSHans Verkuil substream->runtime->dma_area = dev->dmasound.vaddr; 737b285192aSMauro Carvalho Chehab substream->runtime->dma_bytes = dev->dmasound.bufsize; 738b285192aSMauro Carvalho Chehab substream->runtime->dma_addr = 0; 739b285192aSMauro Carvalho Chehab 740b285192aSMauro Carvalho Chehab return 0; 741b285192aSMauro Carvalho Chehab 742b285192aSMauro Carvalho Chehab } 743b285192aSMauro Carvalho Chehab 744b285192aSMauro Carvalho Chehab /* 745b285192aSMauro Carvalho Chehab * ALSA hardware release 746b285192aSMauro Carvalho Chehab * 747b285192aSMauro Carvalho Chehab * - One of the ALSA capture callbacks. 748b285192aSMauro Carvalho Chehab * 749b285192aSMauro Carvalho Chehab * Called after closing the device, but before snd_card_saa7134_capture_close 750b285192aSMauro Carvalho Chehab * It stops the DMA audio and releases the buffers. 751b285192aSMauro Carvalho Chehab * 752b285192aSMauro Carvalho Chehab */ 753b285192aSMauro Carvalho Chehab 754b285192aSMauro Carvalho Chehab static int snd_card_saa7134_hw_free(struct snd_pcm_substream * substream) 755b285192aSMauro Carvalho Chehab { 756b285192aSMauro Carvalho Chehab snd_card_saa7134_t *saa7134 = snd_pcm_substream_chip(substream); 757b285192aSMauro Carvalho Chehab struct saa7134_dev *dev; 758b285192aSMauro Carvalho Chehab 759b285192aSMauro Carvalho Chehab dev = saa7134->dev; 760b285192aSMauro Carvalho Chehab 761b285192aSMauro Carvalho Chehab if (substream->runtime->dma_area) { 762b285192aSMauro Carvalho Chehab saa7134_pgtable_free(dev->pci, &dev->dmasound.pt); 76315e64f0dSHans Verkuil saa7134_alsa_dma_unmap(dev); 764b285192aSMauro Carvalho Chehab dsp_buffer_free(dev); 765b285192aSMauro Carvalho Chehab substream->runtime->dma_area = NULL; 766b285192aSMauro Carvalho Chehab } 767b285192aSMauro Carvalho Chehab 768b285192aSMauro Carvalho Chehab return 0; 769b285192aSMauro Carvalho Chehab } 770b285192aSMauro Carvalho Chehab 771b285192aSMauro Carvalho Chehab /* 772b285192aSMauro Carvalho Chehab * ALSA capture finish 773b285192aSMauro Carvalho Chehab * 774b285192aSMauro Carvalho Chehab * - One of the ALSA capture callbacks. 775b285192aSMauro Carvalho Chehab * 776b285192aSMauro Carvalho Chehab * Called after closing the device. 777b285192aSMauro Carvalho Chehab * 778b285192aSMauro Carvalho Chehab */ 779b285192aSMauro Carvalho Chehab 780b285192aSMauro Carvalho Chehab static int snd_card_saa7134_capture_close(struct snd_pcm_substream * substream) 781b285192aSMauro Carvalho Chehab { 782b285192aSMauro Carvalho Chehab snd_card_saa7134_t *saa7134 = snd_pcm_substream_chip(substream); 783b285192aSMauro Carvalho Chehab struct saa7134_dev *dev = saa7134->dev; 784b285192aSMauro Carvalho Chehab 785b285192aSMauro Carvalho Chehab if (saa7134->mute_was_on) { 786b285192aSMauro Carvalho Chehab dev->ctl_mute = 1; 787b285192aSMauro Carvalho Chehab saa7134_tvaudio_setmute(dev); 788b285192aSMauro Carvalho Chehab } 789b285192aSMauro Carvalho Chehab return 0; 790b285192aSMauro Carvalho Chehab } 791b285192aSMauro Carvalho Chehab 792b285192aSMauro Carvalho Chehab /* 793b285192aSMauro Carvalho Chehab * ALSA capture start 794b285192aSMauro Carvalho Chehab * 795b285192aSMauro Carvalho Chehab * - One of the ALSA capture callbacks. 796b285192aSMauro Carvalho Chehab * 797b285192aSMauro Carvalho Chehab * Called when opening the device. It creates and populates the PCM 798b285192aSMauro Carvalho Chehab * structure 799b285192aSMauro Carvalho Chehab * 800b285192aSMauro Carvalho Chehab */ 801b285192aSMauro Carvalho Chehab 802b285192aSMauro Carvalho Chehab static int snd_card_saa7134_capture_open(struct snd_pcm_substream * substream) 803b285192aSMauro Carvalho Chehab { 804b285192aSMauro Carvalho Chehab struct snd_pcm_runtime *runtime = substream->runtime; 805b285192aSMauro Carvalho Chehab snd_card_saa7134_pcm_t *pcm; 806b285192aSMauro Carvalho Chehab snd_card_saa7134_t *saa7134 = snd_pcm_substream_chip(substream); 807b285192aSMauro Carvalho Chehab struct saa7134_dev *dev; 808b285192aSMauro Carvalho Chehab int amux, err; 809b285192aSMauro Carvalho Chehab 810b285192aSMauro Carvalho Chehab if (!saa7134) { 811395eff95SMauro Carvalho Chehab pr_err("BUG: saa7134 can't find device struct. Can't proceed with open\n"); 812b285192aSMauro Carvalho Chehab return -ENODEV; 813b285192aSMauro Carvalho Chehab } 814b285192aSMauro Carvalho Chehab dev = saa7134->dev; 815b285192aSMauro Carvalho Chehab mutex_lock(&dev->dmasound.lock); 816b285192aSMauro Carvalho Chehab 817b285192aSMauro Carvalho Chehab dev->dmasound.read_count = 0; 818b285192aSMauro Carvalho Chehab dev->dmasound.read_offset = 0; 819b285192aSMauro Carvalho Chehab 820b285192aSMauro Carvalho Chehab amux = dev->input->amux; 821b285192aSMauro Carvalho Chehab if ((amux < 1) || (amux > 3)) 822b285192aSMauro Carvalho Chehab amux = 1; 823b285192aSMauro Carvalho Chehab dev->dmasound.input = amux - 1; 824b285192aSMauro Carvalho Chehab 825b285192aSMauro Carvalho Chehab mutex_unlock(&dev->dmasound.lock); 826b285192aSMauro Carvalho Chehab 827b285192aSMauro Carvalho Chehab pcm = kzalloc(sizeof(*pcm), GFP_KERNEL); 828b285192aSMauro Carvalho Chehab if (pcm == NULL) 829b285192aSMauro Carvalho Chehab return -ENOMEM; 830b285192aSMauro Carvalho Chehab 831b285192aSMauro Carvalho Chehab pcm->dev=saa7134->dev; 832b285192aSMauro Carvalho Chehab 833b285192aSMauro Carvalho Chehab spin_lock_init(&pcm->lock); 834b285192aSMauro Carvalho Chehab 835b285192aSMauro Carvalho Chehab pcm->substream = substream; 836b285192aSMauro Carvalho Chehab runtime->private_data = pcm; 837b285192aSMauro Carvalho Chehab runtime->private_free = snd_card_saa7134_runtime_free; 838b285192aSMauro Carvalho Chehab runtime->hw = snd_card_saa7134_capture; 839b285192aSMauro Carvalho Chehab 840b285192aSMauro Carvalho Chehab if (dev->ctl_mute != 0) { 841b285192aSMauro Carvalho Chehab saa7134->mute_was_on = 1; 842b285192aSMauro Carvalho Chehab dev->ctl_mute = 0; 843b285192aSMauro Carvalho Chehab saa7134_tvaudio_setmute(dev); 844b285192aSMauro Carvalho Chehab } 845b285192aSMauro Carvalho Chehab 846b285192aSMauro Carvalho Chehab err = snd_pcm_hw_constraint_integer(runtime, 847b285192aSMauro Carvalho Chehab SNDRV_PCM_HW_PARAM_PERIODS); 848b285192aSMauro Carvalho Chehab if (err < 0) 849b285192aSMauro Carvalho Chehab return err; 850b285192aSMauro Carvalho Chehab 851b285192aSMauro Carvalho Chehab err = snd_pcm_hw_constraint_step(runtime, 0, 852b285192aSMauro Carvalho Chehab SNDRV_PCM_HW_PARAM_PERIODS, 2); 853b285192aSMauro Carvalho Chehab if (err < 0) 854b285192aSMauro Carvalho Chehab return err; 855b285192aSMauro Carvalho Chehab 856b285192aSMauro Carvalho Chehab return 0; 857b285192aSMauro Carvalho Chehab } 858b285192aSMauro Carvalho Chehab 859b285192aSMauro Carvalho Chehab /* 860b285192aSMauro Carvalho Chehab * page callback (needed for mmap) 861b285192aSMauro Carvalho Chehab */ 862b285192aSMauro Carvalho Chehab 863b285192aSMauro Carvalho Chehab static struct page *snd_card_saa7134_page(struct snd_pcm_substream *substream, 864b285192aSMauro Carvalho Chehab unsigned long offset) 865b285192aSMauro Carvalho Chehab { 866b285192aSMauro Carvalho Chehab void *pageptr = substream->runtime->dma_area + offset; 867b285192aSMauro Carvalho Chehab return vmalloc_to_page(pageptr); 868b285192aSMauro Carvalho Chehab } 869b285192aSMauro Carvalho Chehab 870b285192aSMauro Carvalho Chehab /* 871b285192aSMauro Carvalho Chehab * ALSA capture callbacks definition 872b285192aSMauro Carvalho Chehab */ 873b285192aSMauro Carvalho Chehab 8745c8d8c01SJulia Lawall static const struct snd_pcm_ops snd_card_saa7134_capture_ops = { 875b285192aSMauro Carvalho Chehab .open = snd_card_saa7134_capture_open, 876b285192aSMauro Carvalho Chehab .close = snd_card_saa7134_capture_close, 877b285192aSMauro Carvalho Chehab .ioctl = snd_pcm_lib_ioctl, 878b285192aSMauro Carvalho Chehab .hw_params = snd_card_saa7134_hw_params, 879b285192aSMauro Carvalho Chehab .hw_free = snd_card_saa7134_hw_free, 880b285192aSMauro Carvalho Chehab .prepare = snd_card_saa7134_capture_prepare, 881b285192aSMauro Carvalho Chehab .trigger = snd_card_saa7134_capture_trigger, 882b285192aSMauro Carvalho Chehab .pointer = snd_card_saa7134_capture_pointer, 883b285192aSMauro Carvalho Chehab .page = snd_card_saa7134_page, 884b285192aSMauro Carvalho Chehab }; 885b285192aSMauro Carvalho Chehab 886b285192aSMauro Carvalho Chehab /* 887b285192aSMauro Carvalho Chehab * ALSA PCM setup 888b285192aSMauro Carvalho Chehab * 889b285192aSMauro Carvalho Chehab * Called when initializing the board. Sets up the name and hooks up 890b285192aSMauro Carvalho Chehab * the callbacks 891b285192aSMauro Carvalho Chehab * 892b285192aSMauro Carvalho Chehab */ 893b285192aSMauro Carvalho Chehab 894b285192aSMauro Carvalho Chehab static int snd_card_saa7134_pcm(snd_card_saa7134_t *saa7134, int device) 895b285192aSMauro Carvalho Chehab { 896b285192aSMauro Carvalho Chehab struct snd_pcm *pcm; 897b285192aSMauro Carvalho Chehab int err; 898b285192aSMauro Carvalho Chehab 899b285192aSMauro Carvalho Chehab if ((err = snd_pcm_new(saa7134->card, "SAA7134 PCM", device, 0, 1, &pcm)) < 0) 900b285192aSMauro Carvalho Chehab return err; 901b285192aSMauro Carvalho Chehab snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_card_saa7134_capture_ops); 902b285192aSMauro Carvalho Chehab pcm->private_data = saa7134; 903b285192aSMauro Carvalho Chehab pcm->info_flags = 0; 904b285192aSMauro Carvalho Chehab strcpy(pcm->name, "SAA7134 PCM"); 905b285192aSMauro Carvalho Chehab return 0; 906b285192aSMauro Carvalho Chehab } 907b285192aSMauro Carvalho Chehab 908b285192aSMauro Carvalho Chehab #define SAA713x_VOLUME(xname, xindex, addr) \ 909b285192aSMauro Carvalho Chehab { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ 910b285192aSMauro Carvalho Chehab .info = snd_saa7134_volume_info, \ 911b285192aSMauro Carvalho Chehab .get = snd_saa7134_volume_get, .put = snd_saa7134_volume_put, \ 912b285192aSMauro Carvalho Chehab .private_value = addr } 913b285192aSMauro Carvalho Chehab 914b285192aSMauro Carvalho Chehab static int snd_saa7134_volume_info(struct snd_kcontrol * kcontrol, 915b285192aSMauro Carvalho Chehab struct snd_ctl_elem_info * uinfo) 916b285192aSMauro Carvalho Chehab { 917b285192aSMauro Carvalho Chehab uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 918b285192aSMauro Carvalho Chehab uinfo->count = 2; 919b285192aSMauro Carvalho Chehab uinfo->value.integer.min = 0; 920b285192aSMauro Carvalho Chehab uinfo->value.integer.max = 20; 921b285192aSMauro Carvalho Chehab return 0; 922b285192aSMauro Carvalho Chehab } 923b285192aSMauro Carvalho Chehab 924b285192aSMauro Carvalho Chehab static int snd_saa7134_volume_get(struct snd_kcontrol * kcontrol, 925b285192aSMauro Carvalho Chehab struct snd_ctl_elem_value * ucontrol) 926b285192aSMauro Carvalho Chehab { 927b285192aSMauro Carvalho Chehab snd_card_saa7134_t *chip = snd_kcontrol_chip(kcontrol); 928b285192aSMauro Carvalho Chehab int addr = kcontrol->private_value; 929b285192aSMauro Carvalho Chehab 930b285192aSMauro Carvalho Chehab ucontrol->value.integer.value[0] = chip->mixer_volume[addr][0]; 931b285192aSMauro Carvalho Chehab ucontrol->value.integer.value[1] = chip->mixer_volume[addr][1]; 932b285192aSMauro Carvalho Chehab return 0; 933b285192aSMauro Carvalho Chehab } 934b285192aSMauro Carvalho Chehab 935b285192aSMauro Carvalho Chehab static int snd_saa7134_volume_put(struct snd_kcontrol * kcontrol, 936b285192aSMauro Carvalho Chehab struct snd_ctl_elem_value * ucontrol) 937b285192aSMauro Carvalho Chehab { 938b285192aSMauro Carvalho Chehab snd_card_saa7134_t *chip = snd_kcontrol_chip(kcontrol); 939b285192aSMauro Carvalho Chehab struct saa7134_dev *dev = chip->dev; 940b285192aSMauro Carvalho Chehab 941b285192aSMauro Carvalho Chehab int change, addr = kcontrol->private_value; 942b285192aSMauro Carvalho Chehab int left, right; 943b285192aSMauro Carvalho Chehab 944b285192aSMauro Carvalho Chehab left = ucontrol->value.integer.value[0]; 945b285192aSMauro Carvalho Chehab if (left < 0) 946b285192aSMauro Carvalho Chehab left = 0; 947b285192aSMauro Carvalho Chehab if (left > 20) 948b285192aSMauro Carvalho Chehab left = 20; 949b285192aSMauro Carvalho Chehab right = ucontrol->value.integer.value[1]; 950b285192aSMauro Carvalho Chehab if (right < 0) 951b285192aSMauro Carvalho Chehab right = 0; 952b285192aSMauro Carvalho Chehab if (right > 20) 953b285192aSMauro Carvalho Chehab right = 20; 954b285192aSMauro Carvalho Chehab spin_lock_irq(&chip->mixer_lock); 955b285192aSMauro Carvalho Chehab change = 0; 956b285192aSMauro Carvalho Chehab if (chip->mixer_volume[addr][0] != left) { 957b285192aSMauro Carvalho Chehab change = 1; 958b285192aSMauro Carvalho Chehab right = left; 959b285192aSMauro Carvalho Chehab } 960b285192aSMauro Carvalho Chehab if (chip->mixer_volume[addr][1] != right) { 961b285192aSMauro Carvalho Chehab change = 1; 962b285192aSMauro Carvalho Chehab left = right; 963b285192aSMauro Carvalho Chehab } 964b285192aSMauro Carvalho Chehab if (change) { 965b285192aSMauro Carvalho Chehab switch (dev->pci->device) { 966b285192aSMauro Carvalho Chehab case PCI_DEVICE_ID_PHILIPS_SAA7134: 967b285192aSMauro Carvalho Chehab switch (addr) { 968b285192aSMauro Carvalho Chehab case MIXER_ADDR_TVTUNER: 969b285192aSMauro Carvalho Chehab left = 20; 970b285192aSMauro Carvalho Chehab break; 971b285192aSMauro Carvalho Chehab case MIXER_ADDR_LINE1: 972b285192aSMauro Carvalho Chehab saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x10, 973b285192aSMauro Carvalho Chehab (left > 10) ? 0x00 : 0x10); 974b285192aSMauro Carvalho Chehab break; 975b285192aSMauro Carvalho Chehab case MIXER_ADDR_LINE2: 976b285192aSMauro Carvalho Chehab saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x20, 977b285192aSMauro Carvalho Chehab (left > 10) ? 0x00 : 0x20); 978b285192aSMauro Carvalho Chehab break; 979b285192aSMauro Carvalho Chehab } 980b285192aSMauro Carvalho Chehab break; 981b285192aSMauro Carvalho Chehab case PCI_DEVICE_ID_PHILIPS_SAA7133: 982b285192aSMauro Carvalho Chehab case PCI_DEVICE_ID_PHILIPS_SAA7135: 983b285192aSMauro Carvalho Chehab switch (addr) { 984b285192aSMauro Carvalho Chehab case MIXER_ADDR_TVTUNER: 985b285192aSMauro Carvalho Chehab left = 20; 986b285192aSMauro Carvalho Chehab break; 987b285192aSMauro Carvalho Chehab case MIXER_ADDR_LINE1: 988b285192aSMauro Carvalho Chehab saa_andorb(0x0594, 0x10, 989b285192aSMauro Carvalho Chehab (left > 10) ? 0x00 : 0x10); 990b285192aSMauro Carvalho Chehab break; 991b285192aSMauro Carvalho Chehab case MIXER_ADDR_LINE2: 992b285192aSMauro Carvalho Chehab saa_andorb(0x0594, 0x20, 993b285192aSMauro Carvalho Chehab (left > 10) ? 0x00 : 0x20); 994b285192aSMauro Carvalho Chehab break; 995b285192aSMauro Carvalho Chehab } 996b285192aSMauro Carvalho Chehab break; 997b285192aSMauro Carvalho Chehab } 998b285192aSMauro Carvalho Chehab chip->mixer_volume[addr][0] = left; 999b285192aSMauro Carvalho Chehab chip->mixer_volume[addr][1] = right; 1000b285192aSMauro Carvalho Chehab } 1001b285192aSMauro Carvalho Chehab spin_unlock_irq(&chip->mixer_lock); 1002b285192aSMauro Carvalho Chehab return change; 1003b285192aSMauro Carvalho Chehab } 1004b285192aSMauro Carvalho Chehab 1005b285192aSMauro Carvalho Chehab #define SAA713x_CAPSRC(xname, xindex, addr) \ 1006b285192aSMauro Carvalho Chehab { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ 1007b285192aSMauro Carvalho Chehab .info = snd_saa7134_capsrc_info, \ 1008b285192aSMauro Carvalho Chehab .get = snd_saa7134_capsrc_get, .put = snd_saa7134_capsrc_put, \ 1009b285192aSMauro Carvalho Chehab .private_value = addr } 1010b285192aSMauro Carvalho Chehab 1011b285192aSMauro Carvalho Chehab static int snd_saa7134_capsrc_info(struct snd_kcontrol * kcontrol, 1012b285192aSMauro Carvalho Chehab struct snd_ctl_elem_info * uinfo) 1013b285192aSMauro Carvalho Chehab { 1014b285192aSMauro Carvalho Chehab uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 1015b285192aSMauro Carvalho Chehab uinfo->count = 2; 1016b285192aSMauro Carvalho Chehab uinfo->value.integer.min = 0; 1017b285192aSMauro Carvalho Chehab uinfo->value.integer.max = 1; 1018b285192aSMauro Carvalho Chehab return 0; 1019b285192aSMauro Carvalho Chehab } 1020b285192aSMauro Carvalho Chehab 1021b285192aSMauro Carvalho Chehab static int snd_saa7134_capsrc_get(struct snd_kcontrol * kcontrol, 1022b285192aSMauro Carvalho Chehab struct snd_ctl_elem_value * ucontrol) 1023b285192aSMauro Carvalho Chehab { 1024b285192aSMauro Carvalho Chehab snd_card_saa7134_t *chip = snd_kcontrol_chip(kcontrol); 1025b285192aSMauro Carvalho Chehab int addr = kcontrol->private_value; 1026b285192aSMauro Carvalho Chehab 1027b285192aSMauro Carvalho Chehab spin_lock_irq(&chip->mixer_lock); 1028b285192aSMauro Carvalho Chehab if (chip->capture_source_addr == addr) { 1029b285192aSMauro Carvalho Chehab ucontrol->value.integer.value[0] = chip->capture_source[0]; 1030b285192aSMauro Carvalho Chehab ucontrol->value.integer.value[1] = chip->capture_source[1]; 1031b285192aSMauro Carvalho Chehab } else { 1032b285192aSMauro Carvalho Chehab ucontrol->value.integer.value[0] = 0; 1033b285192aSMauro Carvalho Chehab ucontrol->value.integer.value[1] = 0; 1034b285192aSMauro Carvalho Chehab } 1035b285192aSMauro Carvalho Chehab spin_unlock_irq(&chip->mixer_lock); 1036b285192aSMauro Carvalho Chehab 1037b285192aSMauro Carvalho Chehab return 0; 1038b285192aSMauro Carvalho Chehab } 1039b285192aSMauro Carvalho Chehab 1040b285192aSMauro Carvalho Chehab static int snd_saa7134_capsrc_put(struct snd_kcontrol * kcontrol, 1041b285192aSMauro Carvalho Chehab struct snd_ctl_elem_value * ucontrol) 1042b285192aSMauro Carvalho Chehab { 1043b285192aSMauro Carvalho Chehab int left, right; 1044b285192aSMauro Carvalho Chehab left = ucontrol->value.integer.value[0] & 1; 1045b285192aSMauro Carvalho Chehab right = ucontrol->value.integer.value[1] & 1; 1046b285192aSMauro Carvalho Chehab 1047b285192aSMauro Carvalho Chehab return snd_saa7134_capsrc_set(kcontrol, left, right, false); 1048b285192aSMauro Carvalho Chehab } 1049b285192aSMauro Carvalho Chehab 1050b285192aSMauro Carvalho Chehab static struct snd_kcontrol_new snd_saa7134_volume_controls[] = { 1051b285192aSMauro Carvalho Chehab SAA713x_VOLUME("Video Volume", 0, MIXER_ADDR_TVTUNER), 1052b285192aSMauro Carvalho Chehab SAA713x_VOLUME("Line Volume", 1, MIXER_ADDR_LINE1), 1053b285192aSMauro Carvalho Chehab SAA713x_VOLUME("Line Volume", 2, MIXER_ADDR_LINE2), 1054b285192aSMauro Carvalho Chehab }; 1055b285192aSMauro Carvalho Chehab 1056b285192aSMauro Carvalho Chehab static struct snd_kcontrol_new snd_saa7134_capture_controls[] = { 1057b285192aSMauro Carvalho Chehab SAA713x_CAPSRC("Video Capture Switch", 0, MIXER_ADDR_TVTUNER), 1058b285192aSMauro Carvalho Chehab SAA713x_CAPSRC("Line Capture Switch", 1, MIXER_ADDR_LINE1), 1059b285192aSMauro Carvalho Chehab SAA713x_CAPSRC("Line Capture Switch", 2, MIXER_ADDR_LINE2), 1060b285192aSMauro Carvalho Chehab }; 1061b285192aSMauro Carvalho Chehab 1062b285192aSMauro Carvalho Chehab /* 1063b285192aSMauro Carvalho Chehab * ALSA mixer setup 1064b285192aSMauro Carvalho Chehab * 1065b285192aSMauro Carvalho Chehab * Called when initializing the board. Sets up the name and hooks up 1066b285192aSMauro Carvalho Chehab * the callbacks 1067b285192aSMauro Carvalho Chehab * 1068b285192aSMauro Carvalho Chehab */ 1069b285192aSMauro Carvalho Chehab 1070b285192aSMauro Carvalho Chehab static int snd_card_saa7134_new_mixer(snd_card_saa7134_t * chip) 1071b285192aSMauro Carvalho Chehab { 1072b285192aSMauro Carvalho Chehab struct snd_card *card = chip->card; 1073b285192aSMauro Carvalho Chehab struct snd_kcontrol *kcontrol; 1074b285192aSMauro Carvalho Chehab unsigned int idx; 1075b285192aSMauro Carvalho Chehab int err, addr; 1076b285192aSMauro Carvalho Chehab 1077b285192aSMauro Carvalho Chehab strcpy(card->mixername, "SAA7134 Mixer"); 1078b285192aSMauro Carvalho Chehab 1079b285192aSMauro Carvalho Chehab for (idx = 0; idx < ARRAY_SIZE(snd_saa7134_volume_controls); idx++) { 1080b285192aSMauro Carvalho Chehab kcontrol = snd_ctl_new1(&snd_saa7134_volume_controls[idx], 1081b285192aSMauro Carvalho Chehab chip); 1082b285192aSMauro Carvalho Chehab err = snd_ctl_add(card, kcontrol); 1083b285192aSMauro Carvalho Chehab if (err < 0) 1084b285192aSMauro Carvalho Chehab return err; 1085b285192aSMauro Carvalho Chehab } 1086b285192aSMauro Carvalho Chehab 1087b285192aSMauro Carvalho Chehab for (idx = 0; idx < ARRAY_SIZE(snd_saa7134_capture_controls); idx++) { 1088b285192aSMauro Carvalho Chehab kcontrol = snd_ctl_new1(&snd_saa7134_capture_controls[idx], 1089b285192aSMauro Carvalho Chehab chip); 1090b285192aSMauro Carvalho Chehab addr = snd_saa7134_capture_controls[idx].private_value; 1091b285192aSMauro Carvalho Chehab chip->capture_ctl[addr] = kcontrol; 1092b285192aSMauro Carvalho Chehab err = snd_ctl_add(card, kcontrol); 1093b285192aSMauro Carvalho Chehab if (err < 0) 1094b285192aSMauro Carvalho Chehab return err; 1095b285192aSMauro Carvalho Chehab } 1096b285192aSMauro Carvalho Chehab 1097b285192aSMauro Carvalho Chehab chip->capture_source_addr = MIXER_ADDR_UNSELECTED; 1098b285192aSMauro Carvalho Chehab return 0; 1099b285192aSMauro Carvalho Chehab } 1100b285192aSMauro Carvalho Chehab 1101b285192aSMauro Carvalho Chehab static void snd_saa7134_free(struct snd_card * card) 1102b285192aSMauro Carvalho Chehab { 1103b285192aSMauro Carvalho Chehab snd_card_saa7134_t *chip = card->private_data; 1104b285192aSMauro Carvalho Chehab 1105b285192aSMauro Carvalho Chehab if (chip->dev->dmasound.priv_data == NULL) 1106b285192aSMauro Carvalho Chehab return; 1107b285192aSMauro Carvalho Chehab 1108b285192aSMauro Carvalho Chehab if (chip->irq >= 0) 1109b285192aSMauro Carvalho Chehab free_irq(chip->irq, &chip->dev->dmasound); 1110b285192aSMauro Carvalho Chehab 1111b285192aSMauro Carvalho Chehab chip->dev->dmasound.priv_data = NULL; 1112b285192aSMauro Carvalho Chehab 1113b285192aSMauro Carvalho Chehab } 1114b285192aSMauro Carvalho Chehab 1115b285192aSMauro Carvalho Chehab /* 1116b285192aSMauro Carvalho Chehab * ALSA initialization 1117b285192aSMauro Carvalho Chehab * 1118b285192aSMauro Carvalho Chehab * Called by the init routine, once for each saa7134 device present, 1119b285192aSMauro Carvalho Chehab * it creates the basic structures and registers the ALSA devices 1120b285192aSMauro Carvalho Chehab * 1121b285192aSMauro Carvalho Chehab */ 1122b285192aSMauro Carvalho Chehab 1123b285192aSMauro Carvalho Chehab static int alsa_card_saa7134_create(struct saa7134_dev *dev, int devnum) 1124b285192aSMauro Carvalho Chehab { 1125b285192aSMauro Carvalho Chehab 1126b285192aSMauro Carvalho Chehab struct snd_card *card; 1127b285192aSMauro Carvalho Chehab snd_card_saa7134_t *chip; 1128b285192aSMauro Carvalho Chehab int err; 1129b285192aSMauro Carvalho Chehab 1130b285192aSMauro Carvalho Chehab 1131b285192aSMauro Carvalho Chehab if (devnum >= SNDRV_CARDS) 1132b285192aSMauro Carvalho Chehab return -ENODEV; 1133b285192aSMauro Carvalho Chehab if (!enable[devnum]) 1134b285192aSMauro Carvalho Chehab return -ENODEV; 1135b285192aSMauro Carvalho Chehab 1136e7356888STakashi Iwai err = snd_card_new(&dev->pci->dev, index[devnum], id[devnum], 1137e7356888STakashi Iwai THIS_MODULE, sizeof(snd_card_saa7134_t), &card); 1138b285192aSMauro Carvalho Chehab if (err < 0) 1139b285192aSMauro Carvalho Chehab return err; 1140b285192aSMauro Carvalho Chehab 1141b285192aSMauro Carvalho Chehab strcpy(card->driver, "SAA7134"); 1142b285192aSMauro Carvalho Chehab 1143b285192aSMauro Carvalho Chehab /* Card "creation" */ 1144b285192aSMauro Carvalho Chehab 1145b285192aSMauro Carvalho Chehab card->private_free = snd_saa7134_free; 1146b285192aSMauro Carvalho Chehab chip = card->private_data; 1147b285192aSMauro Carvalho Chehab 1148b285192aSMauro Carvalho Chehab spin_lock_init(&chip->lock); 1149b285192aSMauro Carvalho Chehab spin_lock_init(&chip->mixer_lock); 1150b285192aSMauro Carvalho Chehab 1151b285192aSMauro Carvalho Chehab chip->dev = dev; 1152b285192aSMauro Carvalho Chehab 1153b285192aSMauro Carvalho Chehab chip->card = card; 1154b285192aSMauro Carvalho Chehab 1155b285192aSMauro Carvalho Chehab chip->pci = dev->pci; 1156b285192aSMauro Carvalho Chehab chip->iobase = pci_resource_start(dev->pci, 0); 1157b285192aSMauro Carvalho Chehab 1158b285192aSMauro Carvalho Chehab 1159b285192aSMauro Carvalho Chehab err = request_irq(dev->pci->irq, saa7134_alsa_irq, 11603e018fe4SMichael Opdenacker IRQF_SHARED, dev->name, 1161b285192aSMauro Carvalho Chehab (void*) &dev->dmasound); 1162b285192aSMauro Carvalho Chehab 1163b285192aSMauro Carvalho Chehab if (err < 0) { 116483582009SMauro Carvalho Chehab pr_err("%s: can't get IRQ %d for ALSA\n", 1165b285192aSMauro Carvalho Chehab dev->name, dev->pci->irq); 1166b285192aSMauro Carvalho Chehab goto __nodev; 1167b285192aSMauro Carvalho Chehab } 1168b285192aSMauro Carvalho Chehab 1169b285192aSMauro Carvalho Chehab chip->irq = dev->pci->irq; 1170b285192aSMauro Carvalho Chehab 1171b285192aSMauro Carvalho Chehab mutex_init(&dev->dmasound.lock); 1172b285192aSMauro Carvalho Chehab 1173b285192aSMauro Carvalho Chehab if ((err = snd_card_saa7134_new_mixer(chip)) < 0) 1174b285192aSMauro Carvalho Chehab goto __nodev; 1175b285192aSMauro Carvalho Chehab 1176b285192aSMauro Carvalho Chehab if ((err = snd_card_saa7134_pcm(chip, 0)) < 0) 1177b285192aSMauro Carvalho Chehab goto __nodev; 1178b285192aSMauro Carvalho Chehab 1179b285192aSMauro Carvalho Chehab /* End of "creation" */ 1180b285192aSMauro Carvalho Chehab 1181b285192aSMauro Carvalho Chehab strcpy(card->shortname, "SAA7134"); 1182b285192aSMauro Carvalho Chehab sprintf(card->longname, "%s at 0x%lx irq %d", 1183b285192aSMauro Carvalho Chehab chip->dev->name, chip->iobase, chip->irq); 1184b285192aSMauro Carvalho Chehab 11856139ebc6SMauro Carvalho Chehab pr_info("%s/alsa: %s registered as card %d\n", 11866139ebc6SMauro Carvalho Chehab dev->name, card->longname, index[devnum]); 1187b285192aSMauro Carvalho Chehab 1188b285192aSMauro Carvalho Chehab if ((err = snd_card_register(card)) == 0) { 1189b285192aSMauro Carvalho Chehab snd_saa7134_cards[devnum] = card; 1190b285192aSMauro Carvalho Chehab return 0; 1191b285192aSMauro Carvalho Chehab } 1192b285192aSMauro Carvalho Chehab 1193b285192aSMauro Carvalho Chehab __nodev: 1194b285192aSMauro Carvalho Chehab snd_card_free(card); 1195b285192aSMauro Carvalho Chehab return err; 1196b285192aSMauro Carvalho Chehab } 1197b285192aSMauro Carvalho Chehab 1198b285192aSMauro Carvalho Chehab 1199b285192aSMauro Carvalho Chehab static int alsa_device_init(struct saa7134_dev *dev) 1200b285192aSMauro Carvalho Chehab { 1201b285192aSMauro Carvalho Chehab dev->dmasound.priv_data = dev; 1202b285192aSMauro Carvalho Chehab alsa_card_saa7134_create(dev,dev->nr); 1203b285192aSMauro Carvalho Chehab return 1; 1204b285192aSMauro Carvalho Chehab } 1205b285192aSMauro Carvalho Chehab 1206b285192aSMauro Carvalho Chehab static int alsa_device_exit(struct saa7134_dev *dev) 1207b285192aSMauro Carvalho Chehab { 1208ac75fe5dSMauro Carvalho Chehab if (!snd_saa7134_cards[dev->nr]) 1209ac75fe5dSMauro Carvalho Chehab return 1; 1210b285192aSMauro Carvalho Chehab 1211b285192aSMauro Carvalho Chehab snd_card_free(snd_saa7134_cards[dev->nr]); 1212b285192aSMauro Carvalho Chehab snd_saa7134_cards[dev->nr] = NULL; 1213b285192aSMauro Carvalho Chehab return 1; 1214b285192aSMauro Carvalho Chehab } 1215b285192aSMauro Carvalho Chehab 1216b285192aSMauro Carvalho Chehab /* 1217b285192aSMauro Carvalho Chehab * Module initializer 1218b285192aSMauro Carvalho Chehab * 1219b285192aSMauro Carvalho Chehab * Loops through present saa7134 cards, and assigns an ALSA device 1220b285192aSMauro Carvalho Chehab * to each one 1221b285192aSMauro Carvalho Chehab * 1222b285192aSMauro Carvalho Chehab */ 1223b285192aSMauro Carvalho Chehab 1224b285192aSMauro Carvalho Chehab static int saa7134_alsa_init(void) 1225b285192aSMauro Carvalho Chehab { 1226b285192aSMauro Carvalho Chehab struct saa7134_dev *dev = NULL; 1227b285192aSMauro Carvalho Chehab struct list_head *list; 1228b285192aSMauro Carvalho Chehab 1229b285192aSMauro Carvalho Chehab saa7134_dmasound_init = alsa_device_init; 1230b285192aSMauro Carvalho Chehab saa7134_dmasound_exit = alsa_device_exit; 1231b285192aSMauro Carvalho Chehab 123283582009SMauro Carvalho Chehab pr_info("saa7134 ALSA driver for DMA sound loaded\n"); 1233b285192aSMauro Carvalho Chehab 1234b285192aSMauro Carvalho Chehab list_for_each(list,&saa7134_devlist) { 1235b285192aSMauro Carvalho Chehab dev = list_entry(list, struct saa7134_dev, devlist); 1236b285192aSMauro Carvalho Chehab if (dev->pci->device == PCI_DEVICE_ID_PHILIPS_SAA7130) 123783582009SMauro Carvalho Chehab pr_info("%s/alsa: %s doesn't support digital audio\n", 1238b285192aSMauro Carvalho Chehab dev->name, saa7134_boards[dev->board].name); 1239b285192aSMauro Carvalho Chehab else 1240b285192aSMauro Carvalho Chehab alsa_device_init(dev); 1241b285192aSMauro Carvalho Chehab } 1242b285192aSMauro Carvalho Chehab 1243b285192aSMauro Carvalho Chehab if (dev == NULL) 124483582009SMauro Carvalho Chehab pr_info("saa7134 ALSA: no saa7134 cards found\n"); 1245b285192aSMauro Carvalho Chehab 1246b285192aSMauro Carvalho Chehab return 0; 1247b285192aSMauro Carvalho Chehab 1248b285192aSMauro Carvalho Chehab } 1249b285192aSMauro Carvalho Chehab 1250b285192aSMauro Carvalho Chehab /* 1251b285192aSMauro Carvalho Chehab * Module destructor 1252b285192aSMauro Carvalho Chehab */ 1253b285192aSMauro Carvalho Chehab 1254b285192aSMauro Carvalho Chehab static void saa7134_alsa_exit(void) 1255b285192aSMauro Carvalho Chehab { 1256b285192aSMauro Carvalho Chehab int idx; 1257b285192aSMauro Carvalho Chehab 1258b285192aSMauro Carvalho Chehab for (idx = 0; idx < SNDRV_CARDS; idx++) { 1259ac75fe5dSMauro Carvalho Chehab if (snd_saa7134_cards[idx]) 1260b285192aSMauro Carvalho Chehab snd_card_free(snd_saa7134_cards[idx]); 1261b285192aSMauro Carvalho Chehab } 1262b285192aSMauro Carvalho Chehab 1263b285192aSMauro Carvalho Chehab saa7134_dmasound_init = NULL; 1264b285192aSMauro Carvalho Chehab saa7134_dmasound_exit = NULL; 126583582009SMauro Carvalho Chehab pr_info("saa7134 ALSA driver for DMA sound unloaded\n"); 1266b285192aSMauro Carvalho Chehab 1267b285192aSMauro Carvalho Chehab return; 1268b285192aSMauro Carvalho Chehab } 1269b285192aSMauro Carvalho Chehab 1270b285192aSMauro Carvalho Chehab /* We initialize this late, to make sure the sound system is up and running */ 1271b285192aSMauro Carvalho Chehab late_initcall(saa7134_alsa_init); 1272b285192aSMauro Carvalho Chehab module_exit(saa7134_alsa_exit); 1273b285192aSMauro Carvalho Chehab MODULE_LICENSE("GPL"); 1274b285192aSMauro Carvalho Chehab MODULE_AUTHOR("Ricardo Cerqueira"); 1275