18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for the Conexant CX25821 PCIe bridge 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2009 Conexant Systems Inc. 68c2ecf20Sopenharmony_ci * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> 78c2ecf20Sopenharmony_ci * Based on SAA713x ALSA driver and CX88 driver 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/init.h> 148c2ecf20Sopenharmony_ci#include <linux/device.h> 158c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 168c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 178c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 188c2ecf20Sopenharmony_ci#include <linux/pci.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <linux/delay.h> 228c2ecf20Sopenharmony_ci#include <sound/core.h> 238c2ecf20Sopenharmony_ci#include <sound/pcm.h> 248c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 258c2ecf20Sopenharmony_ci#include <sound/control.h> 268c2ecf20Sopenharmony_ci#include <sound/initval.h> 278c2ecf20Sopenharmony_ci#include <sound/tlv.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include "cx25821.h" 308c2ecf20Sopenharmony_ci#include "cx25821-reg.h" 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define AUDIO_SRAM_CHANNEL SRAM_CH08 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define dprintk(level, fmt, arg...) \ 358c2ecf20Sopenharmony_cido { \ 368c2ecf20Sopenharmony_ci if (debug >= level) \ 378c2ecf20Sopenharmony_ci pr_info("%s/1: " fmt, chip->dev->name, ##arg); \ 388c2ecf20Sopenharmony_ci} while (0) 398c2ecf20Sopenharmony_ci#define dprintk_core(level, fmt, arg...) \ 408c2ecf20Sopenharmony_cido { \ 418c2ecf20Sopenharmony_ci if (debug >= level) \ 428c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s/1: " fmt, chip->dev->name, ##arg); \ 438c2ecf20Sopenharmony_ci} while (0) 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/**************************************************************************** 468c2ecf20Sopenharmony_ci Data type declarations - Can be moded to a header file later 478c2ecf20Sopenharmony_ci ****************************************************************************/ 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic int devno; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistruct cx25821_audio_buffer { 528c2ecf20Sopenharmony_ci unsigned int bpl; 538c2ecf20Sopenharmony_ci struct cx25821_riscmem risc; 548c2ecf20Sopenharmony_ci void *vaddr; 558c2ecf20Sopenharmony_ci struct scatterlist *sglist; 568c2ecf20Sopenharmony_ci int sglen; 578c2ecf20Sopenharmony_ci unsigned long nr_pages; 588c2ecf20Sopenharmony_ci}; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistruct cx25821_audio_dev { 618c2ecf20Sopenharmony_ci struct cx25821_dev *dev; 628c2ecf20Sopenharmony_ci struct cx25821_dmaqueue q; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci /* pci i/o */ 658c2ecf20Sopenharmony_ci struct pci_dev *pci; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci /* audio controls */ 688c2ecf20Sopenharmony_ci int irq; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci struct snd_card *card; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci unsigned long iobase; 738c2ecf20Sopenharmony_ci spinlock_t reg_lock; 748c2ecf20Sopenharmony_ci atomic_t count; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci unsigned int dma_size; 778c2ecf20Sopenharmony_ci unsigned int period_size; 788c2ecf20Sopenharmony_ci unsigned int num_periods; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci struct cx25821_audio_buffer *buf; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream; 838c2ecf20Sopenharmony_ci}; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci/**************************************************************************** 878c2ecf20Sopenharmony_ci Module global static vars 888c2ecf20Sopenharmony_ci ****************************************************************************/ 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ 918c2ecf20Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ 928c2ecf20Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444); 958c2ecf20Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable cx25821 soundcard. default enabled."); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cimodule_param_array(index, int, NULL, 0444); 988c2ecf20Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for cx25821 capture interface(s)."); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci/**************************************************************************** 1018c2ecf20Sopenharmony_ci Module macros 1028c2ecf20Sopenharmony_ci ****************************************************************************/ 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ALSA driver module for cx25821 based capture cards"); 1058c2ecf20Sopenharmony_ciMODULE_AUTHOR("Hiep Huynh"); 1068c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1078c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("{{Conexant,25821}"); /* "{{Conexant,23881}," */ 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic unsigned int debug; 1108c2ecf20Sopenharmony_cimodule_param(debug, int, 0644); 1118c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "enable debug messages"); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci/**************************************************************************** 1148c2ecf20Sopenharmony_ci Module specific functions 1158c2ecf20Sopenharmony_ci ****************************************************************************/ 1168c2ecf20Sopenharmony_ci/* Constants taken from cx88-reg.h */ 1178c2ecf20Sopenharmony_ci#define AUD_INT_DN_RISCI1 (1 << 0) 1188c2ecf20Sopenharmony_ci#define AUD_INT_UP_RISCI1 (1 << 1) 1198c2ecf20Sopenharmony_ci#define AUD_INT_RDS_DN_RISCI1 (1 << 2) 1208c2ecf20Sopenharmony_ci#define AUD_INT_DN_RISCI2 (1 << 4) /* yes, 3 is skipped */ 1218c2ecf20Sopenharmony_ci#define AUD_INT_UP_RISCI2 (1 << 5) 1228c2ecf20Sopenharmony_ci#define AUD_INT_RDS_DN_RISCI2 (1 << 6) 1238c2ecf20Sopenharmony_ci#define AUD_INT_DN_SYNC (1 << 12) 1248c2ecf20Sopenharmony_ci#define AUD_INT_UP_SYNC (1 << 13) 1258c2ecf20Sopenharmony_ci#define AUD_INT_RDS_DN_SYNC (1 << 14) 1268c2ecf20Sopenharmony_ci#define AUD_INT_OPC_ERR (1 << 16) 1278c2ecf20Sopenharmony_ci#define AUD_INT_BER_IRQ (1 << 20) 1288c2ecf20Sopenharmony_ci#define AUD_INT_MCHG_IRQ (1 << 21) 1298c2ecf20Sopenharmony_ci#define GP_COUNT_CONTROL_RESET 0x3 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci#define PCI_MSK_AUD_EXT (1 << 4) 1328c2ecf20Sopenharmony_ci#define PCI_MSK_AUD_INT (1 << 3) 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic int cx25821_alsa_dma_init(struct cx25821_audio_dev *chip, 1358c2ecf20Sopenharmony_ci unsigned long nr_pages) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci struct cx25821_audio_buffer *buf = chip->buf; 1388c2ecf20Sopenharmony_ci struct page *pg; 1398c2ecf20Sopenharmony_ci int i; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci buf->vaddr = vmalloc_32(nr_pages << PAGE_SHIFT); 1428c2ecf20Sopenharmony_ci if (NULL == buf->vaddr) { 1438c2ecf20Sopenharmony_ci dprintk(1, "vmalloc_32(%lu pages) failed\n", nr_pages); 1448c2ecf20Sopenharmony_ci return -ENOMEM; 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci dprintk(1, "vmalloc is at addr 0x%p, size=%lu\n", 1488c2ecf20Sopenharmony_ci buf->vaddr, 1498c2ecf20Sopenharmony_ci nr_pages << PAGE_SHIFT); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci memset(buf->vaddr, 0, nr_pages << PAGE_SHIFT); 1528c2ecf20Sopenharmony_ci buf->nr_pages = nr_pages; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci buf->sglist = vzalloc(array_size(sizeof(*buf->sglist), buf->nr_pages)); 1558c2ecf20Sopenharmony_ci if (NULL == buf->sglist) 1568c2ecf20Sopenharmony_ci goto vzalloc_err; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci sg_init_table(buf->sglist, buf->nr_pages); 1598c2ecf20Sopenharmony_ci for (i = 0; i < buf->nr_pages; i++) { 1608c2ecf20Sopenharmony_ci pg = vmalloc_to_page(buf->vaddr + i * PAGE_SIZE); 1618c2ecf20Sopenharmony_ci if (NULL == pg) 1628c2ecf20Sopenharmony_ci goto vmalloc_to_page_err; 1638c2ecf20Sopenharmony_ci sg_set_page(&buf->sglist[i], pg, PAGE_SIZE, 0); 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci return 0; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_civmalloc_to_page_err: 1688c2ecf20Sopenharmony_ci vfree(buf->sglist); 1698c2ecf20Sopenharmony_ci buf->sglist = NULL; 1708c2ecf20Sopenharmony_civzalloc_err: 1718c2ecf20Sopenharmony_ci vfree(buf->vaddr); 1728c2ecf20Sopenharmony_ci buf->vaddr = NULL; 1738c2ecf20Sopenharmony_ci return -ENOMEM; 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic int cx25821_alsa_dma_map(struct cx25821_audio_dev *dev) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci struct cx25821_audio_buffer *buf = dev->buf; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci buf->sglen = dma_map_sg(&dev->pci->dev, buf->sglist, 1818c2ecf20Sopenharmony_ci buf->nr_pages, DMA_FROM_DEVICE); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci if (0 == buf->sglen) { 1848c2ecf20Sopenharmony_ci pr_warn("%s: cx25821_alsa_map_sg failed\n", __func__); 1858c2ecf20Sopenharmony_ci return -ENOMEM; 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci return 0; 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic int cx25821_alsa_dma_unmap(struct cx25821_audio_dev *dev) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci struct cx25821_audio_buffer *buf = dev->buf; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci if (!buf->sglen) 1958c2ecf20Sopenharmony_ci return 0; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci dma_unmap_sg(&dev->pci->dev, buf->sglist, buf->nr_pages, DMA_FROM_DEVICE); 1988c2ecf20Sopenharmony_ci buf->sglen = 0; 1998c2ecf20Sopenharmony_ci return 0; 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic int cx25821_alsa_dma_free(struct cx25821_audio_buffer *buf) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci vfree(buf->sglist); 2058c2ecf20Sopenharmony_ci buf->sglist = NULL; 2068c2ecf20Sopenharmony_ci vfree(buf->vaddr); 2078c2ecf20Sopenharmony_ci buf->vaddr = NULL; 2088c2ecf20Sopenharmony_ci return 0; 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci/* 2128c2ecf20Sopenharmony_ci * BOARD Specific: Sets audio DMA 2138c2ecf20Sopenharmony_ci */ 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic int _cx25821_start_audio_dma(struct cx25821_audio_dev *chip) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci struct cx25821_audio_buffer *buf = chip->buf; 2188c2ecf20Sopenharmony_ci struct cx25821_dev *dev = chip->dev; 2198c2ecf20Sopenharmony_ci const struct sram_channel *audio_ch = 2208c2ecf20Sopenharmony_ci &cx25821_sram_channels[AUDIO_SRAM_CHANNEL]; 2218c2ecf20Sopenharmony_ci u32 tmp = 0; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci /* enable output on the GPIO 0 for the MCLK ADC (Audio) */ 2248c2ecf20Sopenharmony_ci cx25821_set_gpiopin_direction(chip->dev, 0, 0); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci /* Make sure RISC/FIFO are off before changing FIFO/RISC settings */ 2278c2ecf20Sopenharmony_ci cx_clear(AUD_INT_DMA_CTL, 2288c2ecf20Sopenharmony_ci FLD_AUD_DST_A_RISC_EN | FLD_AUD_DST_A_FIFO_EN); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci /* setup fifo + format - out channel */ 2318c2ecf20Sopenharmony_ci cx25821_sram_channel_setup_audio(chip->dev, audio_ch, buf->bpl, 2328c2ecf20Sopenharmony_ci buf->risc.dma); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci /* sets bpl size */ 2358c2ecf20Sopenharmony_ci cx_write(AUD_A_LNGTH, buf->bpl); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci /* reset counter */ 2388c2ecf20Sopenharmony_ci /* GP_COUNT_CONTROL_RESET = 0x3 */ 2398c2ecf20Sopenharmony_ci cx_write(AUD_A_GPCNT_CTL, GP_COUNT_CONTROL_RESET); 2408c2ecf20Sopenharmony_ci atomic_set(&chip->count, 0); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci /* Set the input mode to 16-bit */ 2438c2ecf20Sopenharmony_ci tmp = cx_read(AUD_A_CFG); 2448c2ecf20Sopenharmony_ci cx_write(AUD_A_CFG, tmp | FLD_AUD_DST_PK_MODE | FLD_AUD_DST_ENABLE | 2458c2ecf20Sopenharmony_ci FLD_AUD_CLK_ENABLE); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci /* 2488c2ecf20Sopenharmony_ci pr_info("DEBUG: Start audio DMA, %d B/line, cmds_start(0x%x)= %d lines/FIFO, %d periods, %d byte buffer\n", 2498c2ecf20Sopenharmony_ci buf->bpl, audio_ch->cmds_start, 2508c2ecf20Sopenharmony_ci cx_read(audio_ch->cmds_start + 12)>>1, 2518c2ecf20Sopenharmony_ci chip->num_periods, buf->bpl * chip->num_periods); 2528c2ecf20Sopenharmony_ci */ 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci /* Enables corresponding bits at AUD_INT_STAT */ 2558c2ecf20Sopenharmony_ci cx_write(AUD_A_INT_MSK, FLD_AUD_DST_RISCI1 | FLD_AUD_DST_OF | 2568c2ecf20Sopenharmony_ci FLD_AUD_DST_SYNC | FLD_AUD_DST_OPC_ERR); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci /* Clean any pending interrupt bits already set */ 2598c2ecf20Sopenharmony_ci cx_write(AUD_A_INT_STAT, ~0); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci /* enable audio irqs */ 2628c2ecf20Sopenharmony_ci cx_set(PCI_INT_MSK, chip->dev->pci_irqmask | PCI_MSK_AUD_INT); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci /* Turn on audio downstream fifo and risc enable 0x101 */ 2658c2ecf20Sopenharmony_ci tmp = cx_read(AUD_INT_DMA_CTL); 2668c2ecf20Sopenharmony_ci cx_set(AUD_INT_DMA_CTL, tmp | 2678c2ecf20Sopenharmony_ci (FLD_AUD_DST_A_RISC_EN | FLD_AUD_DST_A_FIFO_EN)); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci mdelay(100); 2708c2ecf20Sopenharmony_ci return 0; 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci/* 2748c2ecf20Sopenharmony_ci * BOARD Specific: Resets audio DMA 2758c2ecf20Sopenharmony_ci */ 2768c2ecf20Sopenharmony_cistatic int _cx25821_stop_audio_dma(struct cx25821_audio_dev *chip) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci struct cx25821_dev *dev = chip->dev; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci /* stop dma */ 2818c2ecf20Sopenharmony_ci cx_clear(AUD_INT_DMA_CTL, 2828c2ecf20Sopenharmony_ci FLD_AUD_DST_A_RISC_EN | FLD_AUD_DST_A_FIFO_EN); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci /* disable irqs */ 2858c2ecf20Sopenharmony_ci cx_clear(PCI_INT_MSK, PCI_MSK_AUD_INT); 2868c2ecf20Sopenharmony_ci cx_clear(AUD_A_INT_MSK, AUD_INT_OPC_ERR | AUD_INT_DN_SYNC | 2878c2ecf20Sopenharmony_ci AUD_INT_DN_RISCI2 | AUD_INT_DN_RISCI1); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci return 0; 2908c2ecf20Sopenharmony_ci} 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci#define MAX_IRQ_LOOP 50 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci/* 2958c2ecf20Sopenharmony_ci * BOARD Specific: IRQ dma bits 2968c2ecf20Sopenharmony_ci */ 2978c2ecf20Sopenharmony_cistatic char *cx25821_aud_irqs[32] = { 2988c2ecf20Sopenharmony_ci "dn_risci1", "up_risci1", "rds_dn_risc1", /* 0-2 */ 2998c2ecf20Sopenharmony_ci NULL, /* reserved */ 3008c2ecf20Sopenharmony_ci "dn_risci2", "up_risci2", "rds_dn_risc2", /* 4-6 */ 3018c2ecf20Sopenharmony_ci NULL, /* reserved */ 3028c2ecf20Sopenharmony_ci "dnf_of", "upf_uf", "rds_dnf_uf", /* 8-10 */ 3038c2ecf20Sopenharmony_ci NULL, /* reserved */ 3048c2ecf20Sopenharmony_ci "dn_sync", "up_sync", "rds_dn_sync", /* 12-14 */ 3058c2ecf20Sopenharmony_ci NULL, /* reserved */ 3068c2ecf20Sopenharmony_ci "opc_err", "par_err", "rip_err", /* 16-18 */ 3078c2ecf20Sopenharmony_ci "pci_abort", "ber_irq", "mchg_irq" /* 19-21 */ 3088c2ecf20Sopenharmony_ci}; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci/* 3118c2ecf20Sopenharmony_ci * BOARD Specific: Threats IRQ audio specific calls 3128c2ecf20Sopenharmony_ci */ 3138c2ecf20Sopenharmony_cistatic void cx25821_aud_irq(struct cx25821_audio_dev *chip, u32 status, 3148c2ecf20Sopenharmony_ci u32 mask) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci struct cx25821_dev *dev = chip->dev; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci if (0 == (status & mask)) 3198c2ecf20Sopenharmony_ci return; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci cx_write(AUD_A_INT_STAT, status); 3228c2ecf20Sopenharmony_ci if (debug > 1 || (status & mask & ~0xff)) 3238c2ecf20Sopenharmony_ci cx25821_print_irqbits(dev->name, "irq aud", cx25821_aud_irqs, 3248c2ecf20Sopenharmony_ci ARRAY_SIZE(cx25821_aud_irqs), status, mask); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci /* risc op code error */ 3278c2ecf20Sopenharmony_ci if (status & AUD_INT_OPC_ERR) { 3288c2ecf20Sopenharmony_ci pr_warn("WARNING %s/1: Audio risc op code error\n", dev->name); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci cx_clear(AUD_INT_DMA_CTL, 3318c2ecf20Sopenharmony_ci FLD_AUD_DST_A_RISC_EN | FLD_AUD_DST_A_FIFO_EN); 3328c2ecf20Sopenharmony_ci cx25821_sram_channel_dump_audio(dev, 3338c2ecf20Sopenharmony_ci &cx25821_sram_channels[AUDIO_SRAM_CHANNEL]); 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci if (status & AUD_INT_DN_SYNC) { 3368c2ecf20Sopenharmony_ci pr_warn("WARNING %s: Downstream sync error!\n", dev->name); 3378c2ecf20Sopenharmony_ci cx_write(AUD_A_GPCNT_CTL, GP_COUNT_CONTROL_RESET); 3388c2ecf20Sopenharmony_ci return; 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci /* risc1 downstream */ 3428c2ecf20Sopenharmony_ci if (status & AUD_INT_DN_RISCI1) { 3438c2ecf20Sopenharmony_ci atomic_set(&chip->count, cx_read(AUD_A_GPCNT)); 3448c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(chip->substream); 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci} 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci/* 3498c2ecf20Sopenharmony_ci * BOARD Specific: Handles IRQ calls 3508c2ecf20Sopenharmony_ci */ 3518c2ecf20Sopenharmony_cistatic irqreturn_t cx25821_irq(int irq, void *dev_id) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci struct cx25821_audio_dev *chip = dev_id; 3548c2ecf20Sopenharmony_ci struct cx25821_dev *dev = chip->dev; 3558c2ecf20Sopenharmony_ci u32 status, pci_status; 3568c2ecf20Sopenharmony_ci u32 audint_status, audint_mask; 3578c2ecf20Sopenharmony_ci int loop, handled = 0; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci audint_status = cx_read(AUD_A_INT_STAT); 3608c2ecf20Sopenharmony_ci audint_mask = cx_read(AUD_A_INT_MSK); 3618c2ecf20Sopenharmony_ci status = cx_read(PCI_INT_STAT); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci for (loop = 0; loop < 1; loop++) { 3648c2ecf20Sopenharmony_ci status = cx_read(PCI_INT_STAT); 3658c2ecf20Sopenharmony_ci if (0 == status) { 3668c2ecf20Sopenharmony_ci status = cx_read(PCI_INT_STAT); 3678c2ecf20Sopenharmony_ci audint_status = cx_read(AUD_A_INT_STAT); 3688c2ecf20Sopenharmony_ci audint_mask = cx_read(AUD_A_INT_MSK); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci if (status) { 3718c2ecf20Sopenharmony_ci handled = 1; 3728c2ecf20Sopenharmony_ci cx_write(PCI_INT_STAT, status); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci cx25821_aud_irq(chip, audint_status, 3758c2ecf20Sopenharmony_ci audint_mask); 3768c2ecf20Sopenharmony_ci break; 3778c2ecf20Sopenharmony_ci } else { 3788c2ecf20Sopenharmony_ci goto out; 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci handled = 1; 3838c2ecf20Sopenharmony_ci cx_write(PCI_INT_STAT, status); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci cx25821_aud_irq(chip, audint_status, audint_mask); 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci pci_status = cx_read(PCI_INT_STAT); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci if (handled) 3918c2ecf20Sopenharmony_ci cx_write(PCI_INT_STAT, pci_status); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ciout: 3948c2ecf20Sopenharmony_ci return IRQ_RETVAL(handled); 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_cistatic int dsp_buffer_free(struct cx25821_audio_dev *chip) 3988c2ecf20Sopenharmony_ci{ 3998c2ecf20Sopenharmony_ci struct cx25821_riscmem *risc = &chip->buf->risc; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci BUG_ON(!chip->dma_size); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci dprintk(2, "Freeing buffer\n"); 4048c2ecf20Sopenharmony_ci cx25821_alsa_dma_unmap(chip); 4058c2ecf20Sopenharmony_ci cx25821_alsa_dma_free(chip->buf); 4068c2ecf20Sopenharmony_ci pci_free_consistent(chip->pci, risc->size, risc->cpu, risc->dma); 4078c2ecf20Sopenharmony_ci kfree(chip->buf); 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci chip->buf = NULL; 4108c2ecf20Sopenharmony_ci chip->dma_size = 0; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci return 0; 4138c2ecf20Sopenharmony_ci} 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci/**************************************************************************** 4168c2ecf20Sopenharmony_ci ALSA PCM Interface 4178c2ecf20Sopenharmony_ci ****************************************************************************/ 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci/* 4208c2ecf20Sopenharmony_ci * Digital hardware definition 4218c2ecf20Sopenharmony_ci */ 4228c2ecf20Sopenharmony_ci#define DEFAULT_FIFO_SIZE 384 4238c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_cx25821_digital_hw = { 4248c2ecf20Sopenharmony_ci .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 4258c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID, 4268c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE, 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_48000, 4298c2ecf20Sopenharmony_ci .rate_min = 48000, 4308c2ecf20Sopenharmony_ci .rate_max = 48000, 4318c2ecf20Sopenharmony_ci .channels_min = 2, 4328c2ecf20Sopenharmony_ci .channels_max = 2, 4338c2ecf20Sopenharmony_ci /* Analog audio output will be full of clicks and pops if there 4348c2ecf20Sopenharmony_ci are not exactly four lines in the SRAM FIFO buffer. */ 4358c2ecf20Sopenharmony_ci .period_bytes_min = DEFAULT_FIFO_SIZE / 3, 4368c2ecf20Sopenharmony_ci .period_bytes_max = DEFAULT_FIFO_SIZE / 3, 4378c2ecf20Sopenharmony_ci .periods_min = 1, 4388c2ecf20Sopenharmony_ci .periods_max = AUDIO_LINE_SIZE, 4398c2ecf20Sopenharmony_ci /* 128 * 128 = 16384 = 1024 * 16 */ 4408c2ecf20Sopenharmony_ci .buffer_bytes_max = (AUDIO_LINE_SIZE * AUDIO_LINE_SIZE), 4418c2ecf20Sopenharmony_ci}; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci/* 4448c2ecf20Sopenharmony_ci * audio pcm capture open callback 4458c2ecf20Sopenharmony_ci */ 4468c2ecf20Sopenharmony_cistatic int snd_cx25821_pcm_open(struct snd_pcm_substream *substream) 4478c2ecf20Sopenharmony_ci{ 4488c2ecf20Sopenharmony_ci struct cx25821_audio_dev *chip = snd_pcm_substream_chip(substream); 4498c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 4508c2ecf20Sopenharmony_ci int err; 4518c2ecf20Sopenharmony_ci unsigned int bpl = 0; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci if (!chip) { 4548c2ecf20Sopenharmony_ci pr_err("DEBUG: cx25821 can't find device struct. Can't proceed with open\n"); 4558c2ecf20Sopenharmony_ci return -ENODEV; 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci err = snd_pcm_hw_constraint_pow2(runtime, 0, 4598c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_PERIODS); 4608c2ecf20Sopenharmony_ci if (err < 0) 4618c2ecf20Sopenharmony_ci goto _error; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci chip->substream = substream; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci runtime->hw = snd_cx25821_digital_hw; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci if (cx25821_sram_channels[AUDIO_SRAM_CHANNEL].fifo_size != 4688c2ecf20Sopenharmony_ci DEFAULT_FIFO_SIZE) { 4698c2ecf20Sopenharmony_ci /* since there are 3 audio Clusters */ 4708c2ecf20Sopenharmony_ci bpl = cx25821_sram_channels[AUDIO_SRAM_CHANNEL].fifo_size / 3; 4718c2ecf20Sopenharmony_ci bpl &= ~7; /* must be multiple of 8 */ 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci if (bpl > AUDIO_LINE_SIZE) 4748c2ecf20Sopenharmony_ci bpl = AUDIO_LINE_SIZE; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci runtime->hw.period_bytes_min = bpl; 4778c2ecf20Sopenharmony_ci runtime->hw.period_bytes_max = bpl; 4788c2ecf20Sopenharmony_ci } 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci return 0; 4818c2ecf20Sopenharmony_ci_error: 4828c2ecf20Sopenharmony_ci dprintk(1, "Error opening PCM!\n"); 4838c2ecf20Sopenharmony_ci return err; 4848c2ecf20Sopenharmony_ci} 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci/* 4878c2ecf20Sopenharmony_ci * audio close callback 4888c2ecf20Sopenharmony_ci */ 4898c2ecf20Sopenharmony_cistatic int snd_cx25821_close(struct snd_pcm_substream *substream) 4908c2ecf20Sopenharmony_ci{ 4918c2ecf20Sopenharmony_ci return 0; 4928c2ecf20Sopenharmony_ci} 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci/* 4958c2ecf20Sopenharmony_ci * hw_params callback 4968c2ecf20Sopenharmony_ci */ 4978c2ecf20Sopenharmony_cistatic int snd_cx25821_hw_params(struct snd_pcm_substream *substream, 4988c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *hw_params) 4998c2ecf20Sopenharmony_ci{ 5008c2ecf20Sopenharmony_ci struct cx25821_audio_dev *chip = snd_pcm_substream_chip(substream); 5018c2ecf20Sopenharmony_ci struct cx25821_audio_buffer *buf; 5028c2ecf20Sopenharmony_ci int ret; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci if (substream->runtime->dma_area) { 5058c2ecf20Sopenharmony_ci dsp_buffer_free(chip); 5068c2ecf20Sopenharmony_ci substream->runtime->dma_area = NULL; 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci chip->period_size = params_period_bytes(hw_params); 5108c2ecf20Sopenharmony_ci chip->num_periods = params_periods(hw_params); 5118c2ecf20Sopenharmony_ci chip->dma_size = chip->period_size * params_periods(hw_params); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci BUG_ON(!chip->dma_size); 5148c2ecf20Sopenharmony_ci BUG_ON(chip->num_periods & (chip->num_periods - 1)); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci buf = kzalloc(sizeof(*buf), GFP_KERNEL); 5178c2ecf20Sopenharmony_ci if (NULL == buf) 5188c2ecf20Sopenharmony_ci return -ENOMEM; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci if (chip->period_size > AUDIO_LINE_SIZE) 5218c2ecf20Sopenharmony_ci chip->period_size = AUDIO_LINE_SIZE; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci buf->bpl = chip->period_size; 5248c2ecf20Sopenharmony_ci chip->buf = buf; 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci ret = cx25821_alsa_dma_init(chip, 5278c2ecf20Sopenharmony_ci (PAGE_ALIGN(chip->dma_size) >> PAGE_SHIFT)); 5288c2ecf20Sopenharmony_ci if (ret < 0) 5298c2ecf20Sopenharmony_ci goto error; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci ret = cx25821_alsa_dma_map(chip); 5328c2ecf20Sopenharmony_ci if (ret < 0) 5338c2ecf20Sopenharmony_ci goto error; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci ret = cx25821_risc_databuffer_audio(chip->pci, &buf->risc, buf->sglist, 5368c2ecf20Sopenharmony_ci chip->period_size, chip->num_periods, 1); 5378c2ecf20Sopenharmony_ci if (ret < 0) { 5388c2ecf20Sopenharmony_ci pr_info("DEBUG: ERROR after cx25821_risc_databuffer_audio()\n"); 5398c2ecf20Sopenharmony_ci goto error; 5408c2ecf20Sopenharmony_ci } 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci /* Loop back to start of program */ 5438c2ecf20Sopenharmony_ci buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); 5448c2ecf20Sopenharmony_ci buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma); 5458c2ecf20Sopenharmony_ci buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci substream->runtime->dma_area = chip->buf->vaddr; 5488c2ecf20Sopenharmony_ci substream->runtime->dma_bytes = chip->dma_size; 5498c2ecf20Sopenharmony_ci substream->runtime->dma_addr = 0; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci return 0; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_cierror: 5548c2ecf20Sopenharmony_ci chip->buf = NULL; 5558c2ecf20Sopenharmony_ci kfree(buf); 5568c2ecf20Sopenharmony_ci return ret; 5578c2ecf20Sopenharmony_ci} 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci/* 5608c2ecf20Sopenharmony_ci * hw free callback 5618c2ecf20Sopenharmony_ci */ 5628c2ecf20Sopenharmony_cistatic int snd_cx25821_hw_free(struct snd_pcm_substream *substream) 5638c2ecf20Sopenharmony_ci{ 5648c2ecf20Sopenharmony_ci struct cx25821_audio_dev *chip = snd_pcm_substream_chip(substream); 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci if (substream->runtime->dma_area) { 5678c2ecf20Sopenharmony_ci dsp_buffer_free(chip); 5688c2ecf20Sopenharmony_ci substream->runtime->dma_area = NULL; 5698c2ecf20Sopenharmony_ci } 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci return 0; 5728c2ecf20Sopenharmony_ci} 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci/* 5758c2ecf20Sopenharmony_ci * prepare callback 5768c2ecf20Sopenharmony_ci */ 5778c2ecf20Sopenharmony_cistatic int snd_cx25821_prepare(struct snd_pcm_substream *substream) 5788c2ecf20Sopenharmony_ci{ 5798c2ecf20Sopenharmony_ci return 0; 5808c2ecf20Sopenharmony_ci} 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci/* 5838c2ecf20Sopenharmony_ci * trigger callback 5848c2ecf20Sopenharmony_ci */ 5858c2ecf20Sopenharmony_cistatic int snd_cx25821_card_trigger(struct snd_pcm_substream *substream, 5868c2ecf20Sopenharmony_ci int cmd) 5878c2ecf20Sopenharmony_ci{ 5888c2ecf20Sopenharmony_ci struct cx25821_audio_dev *chip = snd_pcm_substream_chip(substream); 5898c2ecf20Sopenharmony_ci int err = 0; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci /* Local interrupts are already disabled by ALSA */ 5928c2ecf20Sopenharmony_ci spin_lock(&chip->reg_lock); 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci switch (cmd) { 5958c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 5968c2ecf20Sopenharmony_ci err = _cx25821_start_audio_dma(chip); 5978c2ecf20Sopenharmony_ci break; 5988c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 5998c2ecf20Sopenharmony_ci err = _cx25821_stop_audio_dma(chip); 6008c2ecf20Sopenharmony_ci break; 6018c2ecf20Sopenharmony_ci default: 6028c2ecf20Sopenharmony_ci err = -EINVAL; 6038c2ecf20Sopenharmony_ci break; 6048c2ecf20Sopenharmony_ci } 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci spin_unlock(&chip->reg_lock); 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci return err; 6098c2ecf20Sopenharmony_ci} 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci/* 6128c2ecf20Sopenharmony_ci * pointer callback 6138c2ecf20Sopenharmony_ci */ 6148c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t snd_cx25821_pointer(struct snd_pcm_substream 6158c2ecf20Sopenharmony_ci *substream) 6168c2ecf20Sopenharmony_ci{ 6178c2ecf20Sopenharmony_ci struct cx25821_audio_dev *chip = snd_pcm_substream_chip(substream); 6188c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 6198c2ecf20Sopenharmony_ci u16 count; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci count = atomic_read(&chip->count); 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci return runtime->period_size * (count & (runtime->periods - 1)); 6248c2ecf20Sopenharmony_ci} 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci/* 6278c2ecf20Sopenharmony_ci * page callback (needed for mmap) 6288c2ecf20Sopenharmony_ci */ 6298c2ecf20Sopenharmony_cistatic struct page *snd_cx25821_page(struct snd_pcm_substream *substream, 6308c2ecf20Sopenharmony_ci unsigned long offset) 6318c2ecf20Sopenharmony_ci{ 6328c2ecf20Sopenharmony_ci void *pageptr = substream->runtime->dma_area + offset; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci return vmalloc_to_page(pageptr); 6358c2ecf20Sopenharmony_ci} 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci/* 6388c2ecf20Sopenharmony_ci * operators 6398c2ecf20Sopenharmony_ci */ 6408c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_cx25821_pcm_ops = { 6418c2ecf20Sopenharmony_ci .open = snd_cx25821_pcm_open, 6428c2ecf20Sopenharmony_ci .close = snd_cx25821_close, 6438c2ecf20Sopenharmony_ci .hw_params = snd_cx25821_hw_params, 6448c2ecf20Sopenharmony_ci .hw_free = snd_cx25821_hw_free, 6458c2ecf20Sopenharmony_ci .prepare = snd_cx25821_prepare, 6468c2ecf20Sopenharmony_ci .trigger = snd_cx25821_card_trigger, 6478c2ecf20Sopenharmony_ci .pointer = snd_cx25821_pointer, 6488c2ecf20Sopenharmony_ci .page = snd_cx25821_page, 6498c2ecf20Sopenharmony_ci}; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci/* 6528c2ecf20Sopenharmony_ci * ALSA create a PCM device: Called when initializing the board. 6538c2ecf20Sopenharmony_ci * Sets up the name and hooks up the callbacks 6548c2ecf20Sopenharmony_ci */ 6558c2ecf20Sopenharmony_cistatic int snd_cx25821_pcm(struct cx25821_audio_dev *chip, int device, 6568c2ecf20Sopenharmony_ci char *name) 6578c2ecf20Sopenharmony_ci{ 6588c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 6598c2ecf20Sopenharmony_ci int err; 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci err = snd_pcm_new(chip->card, name, device, 0, 1, &pcm); 6628c2ecf20Sopenharmony_ci if (err < 0) { 6638c2ecf20Sopenharmony_ci pr_info("ERROR: FAILED snd_pcm_new() in %s\n", __func__); 6648c2ecf20Sopenharmony_ci return err; 6658c2ecf20Sopenharmony_ci } 6668c2ecf20Sopenharmony_ci pcm->private_data = chip; 6678c2ecf20Sopenharmony_ci pcm->info_flags = 0; 6688c2ecf20Sopenharmony_ci strscpy(pcm->name, name, sizeof(pcm->name)); 6698c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cx25821_pcm_ops); 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci return 0; 6728c2ecf20Sopenharmony_ci} 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci/**************************************************************************** 6758c2ecf20Sopenharmony_ci Basic Flow for Sound Devices 6768c2ecf20Sopenharmony_ci ****************************************************************************/ 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci/* 6798c2ecf20Sopenharmony_ci * PCI ID Table - 14f1:8801 and 14f1:8811 means function 1: Audio 6808c2ecf20Sopenharmony_ci * Only boards with eeprom and byte 1 at eeprom=1 have it 6818c2ecf20Sopenharmony_ci */ 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_cistatic const struct pci_device_id __maybe_unused cx25821_audio_pci_tbl[] = { 6848c2ecf20Sopenharmony_ci {0x14f1, 0x0920, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, 6858c2ecf20Sopenharmony_ci {0,} 6868c2ecf20Sopenharmony_ci}; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, cx25821_audio_pci_tbl); 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci/* 6918c2ecf20Sopenharmony_ci * Alsa Constructor - Component probe 6928c2ecf20Sopenharmony_ci */ 6938c2ecf20Sopenharmony_cistatic int cx25821_audio_initdev(struct cx25821_dev *dev) 6948c2ecf20Sopenharmony_ci{ 6958c2ecf20Sopenharmony_ci struct snd_card *card; 6968c2ecf20Sopenharmony_ci struct cx25821_audio_dev *chip; 6978c2ecf20Sopenharmony_ci int err; 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci if (devno >= SNDRV_CARDS) { 7008c2ecf20Sopenharmony_ci pr_info("DEBUG ERROR: devno >= SNDRV_CARDS %s\n", __func__); 7018c2ecf20Sopenharmony_ci return -ENODEV; 7028c2ecf20Sopenharmony_ci } 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci if (!enable[devno]) { 7058c2ecf20Sopenharmony_ci ++devno; 7068c2ecf20Sopenharmony_ci pr_info("DEBUG ERROR: !enable[devno] %s\n", __func__); 7078c2ecf20Sopenharmony_ci return -ENOENT; 7088c2ecf20Sopenharmony_ci } 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci err = snd_card_new(&dev->pci->dev, index[devno], id[devno], 7118c2ecf20Sopenharmony_ci THIS_MODULE, 7128c2ecf20Sopenharmony_ci sizeof(struct cx25821_audio_dev), &card); 7138c2ecf20Sopenharmony_ci if (err < 0) { 7148c2ecf20Sopenharmony_ci pr_info("DEBUG ERROR: cannot create snd_card_new in %s\n", 7158c2ecf20Sopenharmony_ci __func__); 7168c2ecf20Sopenharmony_ci return err; 7178c2ecf20Sopenharmony_ci } 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci strscpy(card->driver, "cx25821", sizeof(card->driver)); 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci /* Card "creation" */ 7228c2ecf20Sopenharmony_ci chip = card->private_data; 7238c2ecf20Sopenharmony_ci spin_lock_init(&chip->reg_lock); 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci chip->dev = dev; 7268c2ecf20Sopenharmony_ci chip->card = card; 7278c2ecf20Sopenharmony_ci chip->pci = dev->pci; 7288c2ecf20Sopenharmony_ci chip->iobase = pci_resource_start(dev->pci, 0); 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci chip->irq = dev->pci->irq; 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci err = request_irq(dev->pci->irq, cx25821_irq, 7338c2ecf20Sopenharmony_ci IRQF_SHARED, chip->dev->name, chip); 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci if (err < 0) { 7368c2ecf20Sopenharmony_ci pr_err("ERROR %s: can't get IRQ %d for ALSA\n", chip->dev->name, 7378c2ecf20Sopenharmony_ci dev->pci->irq); 7388c2ecf20Sopenharmony_ci goto error; 7398c2ecf20Sopenharmony_ci } 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci err = snd_cx25821_pcm(chip, 0, "cx25821 Digital"); 7428c2ecf20Sopenharmony_ci if (err < 0) { 7438c2ecf20Sopenharmony_ci pr_info("DEBUG ERROR: cannot create snd_cx25821_pcm %s\n", 7448c2ecf20Sopenharmony_ci __func__); 7458c2ecf20Sopenharmony_ci goto error; 7468c2ecf20Sopenharmony_ci } 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci strscpy(card->shortname, "cx25821", sizeof(card->shortname)); 7498c2ecf20Sopenharmony_ci sprintf(card->longname, "%s at 0x%lx irq %d", chip->dev->name, 7508c2ecf20Sopenharmony_ci chip->iobase, chip->irq); 7518c2ecf20Sopenharmony_ci strscpy(card->mixername, "CX25821", sizeof(card->mixername)); 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci pr_info("%s/%i: ALSA support for cx25821 boards\n", card->driver, 7548c2ecf20Sopenharmony_ci devno); 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci err = snd_card_register(card); 7578c2ecf20Sopenharmony_ci if (err < 0) { 7588c2ecf20Sopenharmony_ci pr_info("DEBUG ERROR: cannot register sound card %s\n", 7598c2ecf20Sopenharmony_ci __func__); 7608c2ecf20Sopenharmony_ci goto error; 7618c2ecf20Sopenharmony_ci } 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci dev->card = card; 7648c2ecf20Sopenharmony_ci devno++; 7658c2ecf20Sopenharmony_ci return 0; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_cierror: 7688c2ecf20Sopenharmony_ci snd_card_free(card); 7698c2ecf20Sopenharmony_ci return err; 7708c2ecf20Sopenharmony_ci} 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci/**************************************************************************** 7738c2ecf20Sopenharmony_ci LINUX MODULE INIT 7748c2ecf20Sopenharmony_ci ****************************************************************************/ 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_cistatic int cx25821_alsa_exit_callback(struct device *dev, void *data) 7778c2ecf20Sopenharmony_ci{ 7788c2ecf20Sopenharmony_ci struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); 7798c2ecf20Sopenharmony_ci struct cx25821_dev *cxdev = get_cx25821(v4l2_dev); 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci snd_card_free(cxdev->card); 7828c2ecf20Sopenharmony_ci return 0; 7838c2ecf20Sopenharmony_ci} 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_cistatic void cx25821_audio_fini(void) 7868c2ecf20Sopenharmony_ci{ 7878c2ecf20Sopenharmony_ci struct device_driver *drv = driver_find("cx25821", &pci_bus_type); 7888c2ecf20Sopenharmony_ci int ret; 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci ret = driver_for_each_device(drv, NULL, NULL, cx25821_alsa_exit_callback); 7918c2ecf20Sopenharmony_ci if (ret) 7928c2ecf20Sopenharmony_ci pr_err("%s failed to find a cx25821 driver.\n", __func__); 7938c2ecf20Sopenharmony_ci} 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_cistatic int cx25821_alsa_init_callback(struct device *dev, void *data) 7968c2ecf20Sopenharmony_ci{ 7978c2ecf20Sopenharmony_ci struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); 7988c2ecf20Sopenharmony_ci struct cx25821_dev *cxdev = get_cx25821(v4l2_dev); 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci cx25821_audio_initdev(cxdev); 8018c2ecf20Sopenharmony_ci return 0; 8028c2ecf20Sopenharmony_ci} 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci/* 8058c2ecf20Sopenharmony_ci * Module initializer 8068c2ecf20Sopenharmony_ci * 8078c2ecf20Sopenharmony_ci * Loops through present saa7134 cards, and assigns an ALSA device 8088c2ecf20Sopenharmony_ci * to each one 8098c2ecf20Sopenharmony_ci * 8108c2ecf20Sopenharmony_ci */ 8118c2ecf20Sopenharmony_cistatic int cx25821_alsa_init(void) 8128c2ecf20Sopenharmony_ci{ 8138c2ecf20Sopenharmony_ci struct device_driver *drv = driver_find("cx25821", &pci_bus_type); 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci return driver_for_each_device(drv, NULL, NULL, cx25821_alsa_init_callback); 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci} 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_cilate_initcall(cx25821_alsa_init); 8208c2ecf20Sopenharmony_cimodule_exit(cx25821_audio_fini); 821