18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for NeoMagic 256AV and 256ZX chipsets. 48c2ecf20Sopenharmony_ci * Copyright (c) 2000 by Takashi Iwai <tiwai@suse.de> 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Based on nm256_audio.c OSS driver in linux kernel. 78c2ecf20Sopenharmony_ci * The original author of OSS nm256 driver wishes to remain anonymous, 88c2ecf20Sopenharmony_ci * so I just put my acknoledgment to him/her here. 98c2ecf20Sopenharmony_ci * The original author's web page is found at 108c2ecf20Sopenharmony_ci * http://www.uglx.org/sony.html 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/io.h> 148c2ecf20Sopenharmony_ci#include <linux/delay.h> 158c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 168c2ecf20Sopenharmony_ci#include <linux/init.h> 178c2ecf20Sopenharmony_ci#include <linux/pci.h> 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci#include <linux/module.h> 208c2ecf20Sopenharmony_ci#include <linux/mutex.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <sound/core.h> 238c2ecf20Sopenharmony_ci#include <sound/info.h> 248c2ecf20Sopenharmony_ci#include <sound/control.h> 258c2ecf20Sopenharmony_ci#include <sound/pcm.h> 268c2ecf20Sopenharmony_ci#include <sound/ac97_codec.h> 278c2ecf20Sopenharmony_ci#include <sound/initval.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define CARD_NAME "NeoMagic 256AV/ZX" 308c2ecf20Sopenharmony_ci#define DRIVER_NAME "NM256" 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ciMODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>"); 338c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("NeoMagic NM256AV/ZX"); 348c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 358c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("{{NeoMagic,NM256AV}," 368c2ecf20Sopenharmony_ci "{NeoMagic,NM256ZX}}"); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/* 398c2ecf20Sopenharmony_ci * some compile conditions. 408c2ecf20Sopenharmony_ci */ 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic int index = SNDRV_DEFAULT_IDX1; /* Index */ 438c2ecf20Sopenharmony_cistatic char *id = SNDRV_DEFAULT_STR1; /* ID for this card */ 448c2ecf20Sopenharmony_cistatic int playback_bufsize = 16; 458c2ecf20Sopenharmony_cistatic int capture_bufsize = 16; 468c2ecf20Sopenharmony_cistatic bool force_ac97; /* disabled as default */ 478c2ecf20Sopenharmony_cistatic int buffer_top; /* not specified */ 488c2ecf20Sopenharmony_cistatic bool use_cache; /* disabled */ 498c2ecf20Sopenharmony_cistatic bool vaio_hack; /* disabled */ 508c2ecf20Sopenharmony_cistatic bool reset_workaround; 518c2ecf20Sopenharmony_cistatic bool reset_workaround_2; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cimodule_param(index, int, 0444); 548c2ecf20Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard."); 558c2ecf20Sopenharmony_cimodule_param(id, charp, 0444); 568c2ecf20Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard."); 578c2ecf20Sopenharmony_cimodule_param(playback_bufsize, int, 0444); 588c2ecf20Sopenharmony_ciMODULE_PARM_DESC(playback_bufsize, "DAC frame size in kB for " CARD_NAME " soundcard."); 598c2ecf20Sopenharmony_cimodule_param(capture_bufsize, int, 0444); 608c2ecf20Sopenharmony_ciMODULE_PARM_DESC(capture_bufsize, "ADC frame size in kB for " CARD_NAME " soundcard."); 618c2ecf20Sopenharmony_cimodule_param(force_ac97, bool, 0444); 628c2ecf20Sopenharmony_ciMODULE_PARM_DESC(force_ac97, "Force to use AC97 codec for " CARD_NAME " soundcard."); 638c2ecf20Sopenharmony_cimodule_param(buffer_top, int, 0444); 648c2ecf20Sopenharmony_ciMODULE_PARM_DESC(buffer_top, "Set the top address of audio buffer for " CARD_NAME " soundcard."); 658c2ecf20Sopenharmony_cimodule_param(use_cache, bool, 0444); 668c2ecf20Sopenharmony_ciMODULE_PARM_DESC(use_cache, "Enable the cache for coefficient table access."); 678c2ecf20Sopenharmony_cimodule_param(vaio_hack, bool, 0444); 688c2ecf20Sopenharmony_ciMODULE_PARM_DESC(vaio_hack, "Enable workaround for Sony VAIO notebooks."); 698c2ecf20Sopenharmony_cimodule_param(reset_workaround, bool, 0444); 708c2ecf20Sopenharmony_ciMODULE_PARM_DESC(reset_workaround, "Enable AC97 RESET workaround for some laptops."); 718c2ecf20Sopenharmony_cimodule_param(reset_workaround_2, bool, 0444); 728c2ecf20Sopenharmony_ciMODULE_PARM_DESC(reset_workaround_2, "Enable extended AC97 RESET workaround for some other laptops."); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci/* just for backward compatibility */ 758c2ecf20Sopenharmony_cistatic bool enable; 768c2ecf20Sopenharmony_cimodule_param(enable, bool, 0444); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci/* 818c2ecf20Sopenharmony_ci * hw definitions 828c2ecf20Sopenharmony_ci */ 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci/* The BIOS signature. */ 858c2ecf20Sopenharmony_ci#define NM_SIGNATURE 0x4e4d0000 868c2ecf20Sopenharmony_ci/* Signature mask. */ 878c2ecf20Sopenharmony_ci#define NM_SIG_MASK 0xffff0000 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci/* Size of the second memory area. */ 908c2ecf20Sopenharmony_ci#define NM_PORT2_SIZE 4096 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci/* The base offset of the mixer in the second memory area. */ 938c2ecf20Sopenharmony_ci#define NM_MIXER_OFFSET 0x600 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci/* The maximum size of a coefficient entry. */ 968c2ecf20Sopenharmony_ci#define NM_MAX_PLAYBACK_COEF_SIZE 0x5000 978c2ecf20Sopenharmony_ci#define NM_MAX_RECORD_COEF_SIZE 0x1260 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci/* The interrupt register. */ 1008c2ecf20Sopenharmony_ci#define NM_INT_REG 0xa04 1018c2ecf20Sopenharmony_ci/* And its bits. */ 1028c2ecf20Sopenharmony_ci#define NM_PLAYBACK_INT 0x40 1038c2ecf20Sopenharmony_ci#define NM_RECORD_INT 0x100 1048c2ecf20Sopenharmony_ci#define NM_MISC_INT_1 0x4000 1058c2ecf20Sopenharmony_ci#define NM_MISC_INT_2 0x1 1068c2ecf20Sopenharmony_ci#define NM_ACK_INT(chip, X) snd_nm256_writew(chip, NM_INT_REG, (X) << 1) 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci/* The AV's "mixer ready" status bit and location. */ 1098c2ecf20Sopenharmony_ci#define NM_MIXER_STATUS_OFFSET 0xa04 1108c2ecf20Sopenharmony_ci#define NM_MIXER_READY_MASK 0x0800 1118c2ecf20Sopenharmony_ci#define NM_MIXER_PRESENCE 0xa06 1128c2ecf20Sopenharmony_ci#define NM_PRESENCE_MASK 0x0050 1138c2ecf20Sopenharmony_ci#define NM_PRESENCE_VALUE 0x0040 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci/* 1168c2ecf20Sopenharmony_ci * For the ZX. It uses the same interrupt register, but it holds 32 1178c2ecf20Sopenharmony_ci * bits instead of 16. 1188c2ecf20Sopenharmony_ci */ 1198c2ecf20Sopenharmony_ci#define NM2_PLAYBACK_INT 0x10000 1208c2ecf20Sopenharmony_ci#define NM2_RECORD_INT 0x80000 1218c2ecf20Sopenharmony_ci#define NM2_MISC_INT_1 0x8 1228c2ecf20Sopenharmony_ci#define NM2_MISC_INT_2 0x2 1238c2ecf20Sopenharmony_ci#define NM2_ACK_INT(chip, X) snd_nm256_writel(chip, NM_INT_REG, (X)) 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci/* The ZX's "mixer ready" status bit and location. */ 1268c2ecf20Sopenharmony_ci#define NM2_MIXER_STATUS_OFFSET 0xa06 1278c2ecf20Sopenharmony_ci#define NM2_MIXER_READY_MASK 0x0800 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci/* The playback registers start from here. */ 1308c2ecf20Sopenharmony_ci#define NM_PLAYBACK_REG_OFFSET 0x0 1318c2ecf20Sopenharmony_ci/* The record registers start from here. */ 1328c2ecf20Sopenharmony_ci#define NM_RECORD_REG_OFFSET 0x200 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci/* The rate register is located 2 bytes from the start of the register area. */ 1358c2ecf20Sopenharmony_ci#define NM_RATE_REG_OFFSET 2 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci/* Mono/stereo flag, number of bits on playback, and rate mask. */ 1388c2ecf20Sopenharmony_ci#define NM_RATE_STEREO 1 1398c2ecf20Sopenharmony_ci#define NM_RATE_BITS_16 2 1408c2ecf20Sopenharmony_ci#define NM_RATE_MASK 0xf0 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci/* Playback enable register. */ 1438c2ecf20Sopenharmony_ci#define NM_PLAYBACK_ENABLE_REG (NM_PLAYBACK_REG_OFFSET + 0x1) 1448c2ecf20Sopenharmony_ci#define NM_PLAYBACK_ENABLE_FLAG 1 1458c2ecf20Sopenharmony_ci#define NM_PLAYBACK_ONESHOT 2 1468c2ecf20Sopenharmony_ci#define NM_PLAYBACK_FREERUN 4 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci/* Mutes the audio output. */ 1498c2ecf20Sopenharmony_ci#define NM_AUDIO_MUTE_REG (NM_PLAYBACK_REG_OFFSET + 0x18) 1508c2ecf20Sopenharmony_ci#define NM_AUDIO_MUTE_LEFT 0x8000 1518c2ecf20Sopenharmony_ci#define NM_AUDIO_MUTE_RIGHT 0x0080 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci/* Recording enable register. */ 1548c2ecf20Sopenharmony_ci#define NM_RECORD_ENABLE_REG (NM_RECORD_REG_OFFSET + 0) 1558c2ecf20Sopenharmony_ci#define NM_RECORD_ENABLE_FLAG 1 1568c2ecf20Sopenharmony_ci#define NM_RECORD_FREERUN 2 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci/* coefficient buffer pointer */ 1598c2ecf20Sopenharmony_ci#define NM_COEFF_START_OFFSET 0x1c 1608c2ecf20Sopenharmony_ci#define NM_COEFF_END_OFFSET 0x20 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci/* DMA buffer offsets */ 1638c2ecf20Sopenharmony_ci#define NM_RBUFFER_START (NM_RECORD_REG_OFFSET + 0x4) 1648c2ecf20Sopenharmony_ci#define NM_RBUFFER_END (NM_RECORD_REG_OFFSET + 0x10) 1658c2ecf20Sopenharmony_ci#define NM_RBUFFER_WMARK (NM_RECORD_REG_OFFSET + 0xc) 1668c2ecf20Sopenharmony_ci#define NM_RBUFFER_CURRP (NM_RECORD_REG_OFFSET + 0x8) 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci#define NM_PBUFFER_START (NM_PLAYBACK_REG_OFFSET + 0x4) 1698c2ecf20Sopenharmony_ci#define NM_PBUFFER_END (NM_PLAYBACK_REG_OFFSET + 0x14) 1708c2ecf20Sopenharmony_ci#define NM_PBUFFER_WMARK (NM_PLAYBACK_REG_OFFSET + 0xc) 1718c2ecf20Sopenharmony_ci#define NM_PBUFFER_CURRP (NM_PLAYBACK_REG_OFFSET + 0x8) 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistruct nm256_stream { 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci struct nm256 *chip; 1768c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream; 1778c2ecf20Sopenharmony_ci int running; 1788c2ecf20Sopenharmony_ci int suspended; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci u32 buf; /* offset from chip->buffer */ 1818c2ecf20Sopenharmony_ci int bufsize; /* buffer size in bytes */ 1828c2ecf20Sopenharmony_ci void __iomem *bufptr; /* mapped pointer */ 1838c2ecf20Sopenharmony_ci unsigned long bufptr_addr; /* physical address of the mapped pointer */ 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci int dma_size; /* buffer size of the substream in bytes */ 1868c2ecf20Sopenharmony_ci int period_size; /* period size in bytes */ 1878c2ecf20Sopenharmony_ci int periods; /* # of periods */ 1888c2ecf20Sopenharmony_ci int shift; /* bit shifts */ 1898c2ecf20Sopenharmony_ci int cur_period; /* current period # */ 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci}; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistruct nm256 { 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci struct snd_card *card; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci void __iomem *cport; /* control port */ 1988c2ecf20Sopenharmony_ci struct resource *res_cport; /* its resource */ 1998c2ecf20Sopenharmony_ci unsigned long cport_addr; /* physical address */ 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci void __iomem *buffer; /* buffer */ 2028c2ecf20Sopenharmony_ci struct resource *res_buffer; /* its resource */ 2038c2ecf20Sopenharmony_ci unsigned long buffer_addr; /* buffer phyiscal address */ 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci u32 buffer_start; /* start offset from pci resource 0 */ 2068c2ecf20Sopenharmony_ci u32 buffer_end; /* end offset */ 2078c2ecf20Sopenharmony_ci u32 buffer_size; /* total buffer size */ 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci u32 all_coeff_buf; /* coefficient buffer */ 2108c2ecf20Sopenharmony_ci u32 coeff_buf[2]; /* coefficient buffer for each stream */ 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci unsigned int coeffs_current: 1; /* coeff. table is loaded? */ 2138c2ecf20Sopenharmony_ci unsigned int use_cache: 1; /* use one big coef. table */ 2148c2ecf20Sopenharmony_ci unsigned int reset_workaround: 1; /* Workaround for some laptops to avoid freeze */ 2158c2ecf20Sopenharmony_ci unsigned int reset_workaround_2: 1; /* Extended workaround for some other laptops to avoid freeze */ 2168c2ecf20Sopenharmony_ci unsigned int in_resume: 1; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci int mixer_base; /* register offset of ac97 mixer */ 2198c2ecf20Sopenharmony_ci int mixer_status_offset; /* offset of mixer status reg. */ 2208c2ecf20Sopenharmony_ci int mixer_status_mask; /* bit mask to test the mixer status */ 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci int irq; 2238c2ecf20Sopenharmony_ci int irq_acks; 2248c2ecf20Sopenharmony_ci irq_handler_t interrupt; 2258c2ecf20Sopenharmony_ci int badintrcount; /* counter to check bogus interrupts */ 2268c2ecf20Sopenharmony_ci struct mutex irq_mutex; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci struct nm256_stream streams[2]; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci struct snd_ac97 *ac97; 2318c2ecf20Sopenharmony_ci unsigned short *ac97_regs; /* register caches, only for valid regs */ 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci struct pci_dev *pci; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci spinlock_t reg_lock; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci}; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci/* 2438c2ecf20Sopenharmony_ci * include coefficient table 2448c2ecf20Sopenharmony_ci */ 2458c2ecf20Sopenharmony_ci#include "nm256_coef.c" 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci/* 2498c2ecf20Sopenharmony_ci * PCI ids 2508c2ecf20Sopenharmony_ci */ 2518c2ecf20Sopenharmony_cistatic const struct pci_device_id snd_nm256_ids[] = { 2528c2ecf20Sopenharmony_ci {PCI_VDEVICE(NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO), 0}, 2538c2ecf20Sopenharmony_ci {PCI_VDEVICE(NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO), 0}, 2548c2ecf20Sopenharmony_ci {PCI_VDEVICE(NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256XL_PLUS_AUDIO), 0}, 2558c2ecf20Sopenharmony_ci {0,}, 2568c2ecf20Sopenharmony_ci}; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, snd_nm256_ids); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci/* 2628c2ecf20Sopenharmony_ci * lowlvel stuffs 2638c2ecf20Sopenharmony_ci */ 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic inline u8 2668c2ecf20Sopenharmony_cisnd_nm256_readb(struct nm256 *chip, int offset) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci return readb(chip->cport + offset); 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic inline u16 2728c2ecf20Sopenharmony_cisnd_nm256_readw(struct nm256 *chip, int offset) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci return readw(chip->cport + offset); 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_cistatic inline u32 2788c2ecf20Sopenharmony_cisnd_nm256_readl(struct nm256 *chip, int offset) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci return readl(chip->cport + offset); 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cistatic inline void 2848c2ecf20Sopenharmony_cisnd_nm256_writeb(struct nm256 *chip, int offset, u8 val) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci writeb(val, chip->cport + offset); 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cistatic inline void 2908c2ecf20Sopenharmony_cisnd_nm256_writew(struct nm256 *chip, int offset, u16 val) 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci writew(val, chip->cport + offset); 2938c2ecf20Sopenharmony_ci} 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_cistatic inline void 2968c2ecf20Sopenharmony_cisnd_nm256_writel(struct nm256 *chip, int offset, u32 val) 2978c2ecf20Sopenharmony_ci{ 2988c2ecf20Sopenharmony_ci writel(val, chip->cport + offset); 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_cistatic inline void 3028c2ecf20Sopenharmony_cisnd_nm256_write_buffer(struct nm256 *chip, const void *src, int offset, int size) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci offset -= chip->buffer_start; 3058c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_DEBUG 3068c2ecf20Sopenharmony_ci if (offset < 0 || offset >= chip->buffer_size) { 3078c2ecf20Sopenharmony_ci dev_err(chip->card->dev, 3088c2ecf20Sopenharmony_ci "write_buffer invalid offset = %d size = %d\n", 3098c2ecf20Sopenharmony_ci offset, size); 3108c2ecf20Sopenharmony_ci return; 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci#endif 3138c2ecf20Sopenharmony_ci memcpy_toio(chip->buffer + offset, src, size); 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci/* 3178c2ecf20Sopenharmony_ci * coefficient handlers -- what a magic! 3188c2ecf20Sopenharmony_ci */ 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_cistatic u16 3218c2ecf20Sopenharmony_cisnd_nm256_get_start_offset(int which) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci u16 offset = 0; 3248c2ecf20Sopenharmony_ci while (which-- > 0) 3258c2ecf20Sopenharmony_ci offset += coefficient_sizes[which]; 3268c2ecf20Sopenharmony_ci return offset; 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_cistatic void 3308c2ecf20Sopenharmony_cisnd_nm256_load_one_coefficient(struct nm256 *chip, int stream, u32 port, int which) 3318c2ecf20Sopenharmony_ci{ 3328c2ecf20Sopenharmony_ci u32 coeff_buf = chip->coeff_buf[stream]; 3338c2ecf20Sopenharmony_ci u16 offset = snd_nm256_get_start_offset(which); 3348c2ecf20Sopenharmony_ci u16 size = coefficient_sizes[which]; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci snd_nm256_write_buffer(chip, coefficients + offset, coeff_buf, size); 3378c2ecf20Sopenharmony_ci snd_nm256_writel(chip, port, coeff_buf); 3388c2ecf20Sopenharmony_ci /* ??? Record seems to behave differently than playback. */ 3398c2ecf20Sopenharmony_ci if (stream == SNDRV_PCM_STREAM_PLAYBACK) 3408c2ecf20Sopenharmony_ci size--; 3418c2ecf20Sopenharmony_ci snd_nm256_writel(chip, port + 4, coeff_buf + size); 3428c2ecf20Sopenharmony_ci} 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_cistatic void 3458c2ecf20Sopenharmony_cisnd_nm256_load_coefficient(struct nm256 *chip, int stream, int number) 3468c2ecf20Sopenharmony_ci{ 3478c2ecf20Sopenharmony_ci /* The enable register for the specified engine. */ 3488c2ecf20Sopenharmony_ci u32 poffset = (stream == SNDRV_PCM_STREAM_CAPTURE ? 3498c2ecf20Sopenharmony_ci NM_RECORD_ENABLE_REG : NM_PLAYBACK_ENABLE_REG); 3508c2ecf20Sopenharmony_ci u32 addr = NM_COEFF_START_OFFSET; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci addr += (stream == SNDRV_PCM_STREAM_CAPTURE ? 3538c2ecf20Sopenharmony_ci NM_RECORD_REG_OFFSET : NM_PLAYBACK_REG_OFFSET); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci if (snd_nm256_readb(chip, poffset) & 1) { 3568c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, 3578c2ecf20Sopenharmony_ci "NM256: Engine was enabled while loading coefficients!\n"); 3588c2ecf20Sopenharmony_ci return; 3598c2ecf20Sopenharmony_ci } 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci /* The recording engine uses coefficient values 8-15. */ 3628c2ecf20Sopenharmony_ci number &= 7; 3638c2ecf20Sopenharmony_ci if (stream == SNDRV_PCM_STREAM_CAPTURE) 3648c2ecf20Sopenharmony_ci number += 8; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci if (! chip->use_cache) { 3678c2ecf20Sopenharmony_ci snd_nm256_load_one_coefficient(chip, stream, addr, number); 3688c2ecf20Sopenharmony_ci return; 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci if (! chip->coeffs_current) { 3718c2ecf20Sopenharmony_ci snd_nm256_write_buffer(chip, coefficients, chip->all_coeff_buf, 3728c2ecf20Sopenharmony_ci NM_TOTAL_COEFF_COUNT * 4); 3738c2ecf20Sopenharmony_ci chip->coeffs_current = 1; 3748c2ecf20Sopenharmony_ci } else { 3758c2ecf20Sopenharmony_ci u32 base = chip->all_coeff_buf; 3768c2ecf20Sopenharmony_ci u32 offset = snd_nm256_get_start_offset(number); 3778c2ecf20Sopenharmony_ci u32 end_offset = offset + coefficient_sizes[number]; 3788c2ecf20Sopenharmony_ci snd_nm256_writel(chip, addr, base + offset); 3798c2ecf20Sopenharmony_ci if (stream == SNDRV_PCM_STREAM_PLAYBACK) 3808c2ecf20Sopenharmony_ci end_offset--; 3818c2ecf20Sopenharmony_ci snd_nm256_writel(chip, addr + 4, base + end_offset); 3828c2ecf20Sopenharmony_ci } 3838c2ecf20Sopenharmony_ci} 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci/* The actual rates supported by the card. */ 3878c2ecf20Sopenharmony_cistatic const unsigned int samplerates[8] = { 3888c2ecf20Sopenharmony_ci 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, 3898c2ecf20Sopenharmony_ci}; 3908c2ecf20Sopenharmony_cistatic const struct snd_pcm_hw_constraint_list constraints_rates = { 3918c2ecf20Sopenharmony_ci .count = ARRAY_SIZE(samplerates), 3928c2ecf20Sopenharmony_ci .list = samplerates, 3938c2ecf20Sopenharmony_ci .mask = 0, 3948c2ecf20Sopenharmony_ci}; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci/* 3978c2ecf20Sopenharmony_ci * return the index of the target rate 3988c2ecf20Sopenharmony_ci */ 3998c2ecf20Sopenharmony_cistatic int 4008c2ecf20Sopenharmony_cisnd_nm256_fixed_rate(unsigned int rate) 4018c2ecf20Sopenharmony_ci{ 4028c2ecf20Sopenharmony_ci unsigned int i; 4038c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(samplerates); i++) { 4048c2ecf20Sopenharmony_ci if (rate == samplerates[i]) 4058c2ecf20Sopenharmony_ci return i; 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci snd_BUG(); 4088c2ecf20Sopenharmony_ci return 0; 4098c2ecf20Sopenharmony_ci} 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci/* 4128c2ecf20Sopenharmony_ci * set sample rate and format 4138c2ecf20Sopenharmony_ci */ 4148c2ecf20Sopenharmony_cistatic void 4158c2ecf20Sopenharmony_cisnd_nm256_set_format(struct nm256 *chip, struct nm256_stream *s, 4168c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 4178c2ecf20Sopenharmony_ci{ 4188c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 4198c2ecf20Sopenharmony_ci int rate_index = snd_nm256_fixed_rate(runtime->rate); 4208c2ecf20Sopenharmony_ci unsigned char ratebits = (rate_index << 4) & NM_RATE_MASK; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci s->shift = 0; 4238c2ecf20Sopenharmony_ci if (snd_pcm_format_width(runtime->format) == 16) { 4248c2ecf20Sopenharmony_ci ratebits |= NM_RATE_BITS_16; 4258c2ecf20Sopenharmony_ci s->shift++; 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci if (runtime->channels > 1) { 4288c2ecf20Sopenharmony_ci ratebits |= NM_RATE_STEREO; 4298c2ecf20Sopenharmony_ci s->shift++; 4308c2ecf20Sopenharmony_ci } 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci runtime->rate = samplerates[rate_index]; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci switch (substream->stream) { 4358c2ecf20Sopenharmony_ci case SNDRV_PCM_STREAM_PLAYBACK: 4368c2ecf20Sopenharmony_ci snd_nm256_load_coefficient(chip, 0, rate_index); /* 0 = playback */ 4378c2ecf20Sopenharmony_ci snd_nm256_writeb(chip, 4388c2ecf20Sopenharmony_ci NM_PLAYBACK_REG_OFFSET + NM_RATE_REG_OFFSET, 4398c2ecf20Sopenharmony_ci ratebits); 4408c2ecf20Sopenharmony_ci break; 4418c2ecf20Sopenharmony_ci case SNDRV_PCM_STREAM_CAPTURE: 4428c2ecf20Sopenharmony_ci snd_nm256_load_coefficient(chip, 1, rate_index); /* 1 = record */ 4438c2ecf20Sopenharmony_ci snd_nm256_writeb(chip, 4448c2ecf20Sopenharmony_ci NM_RECORD_REG_OFFSET + NM_RATE_REG_OFFSET, 4458c2ecf20Sopenharmony_ci ratebits); 4468c2ecf20Sopenharmony_ci break; 4478c2ecf20Sopenharmony_ci } 4488c2ecf20Sopenharmony_ci} 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci/* acquire interrupt */ 4518c2ecf20Sopenharmony_cistatic int snd_nm256_acquire_irq(struct nm256 *chip) 4528c2ecf20Sopenharmony_ci{ 4538c2ecf20Sopenharmony_ci mutex_lock(&chip->irq_mutex); 4548c2ecf20Sopenharmony_ci if (chip->irq < 0) { 4558c2ecf20Sopenharmony_ci if (request_irq(chip->pci->irq, chip->interrupt, IRQF_SHARED, 4568c2ecf20Sopenharmony_ci KBUILD_MODNAME, chip)) { 4578c2ecf20Sopenharmony_ci dev_err(chip->card->dev, 4588c2ecf20Sopenharmony_ci "unable to grab IRQ %d\n", chip->pci->irq); 4598c2ecf20Sopenharmony_ci mutex_unlock(&chip->irq_mutex); 4608c2ecf20Sopenharmony_ci return -EBUSY; 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci chip->irq = chip->pci->irq; 4638c2ecf20Sopenharmony_ci chip->card->sync_irq = chip->irq; 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci chip->irq_acks++; 4668c2ecf20Sopenharmony_ci mutex_unlock(&chip->irq_mutex); 4678c2ecf20Sopenharmony_ci return 0; 4688c2ecf20Sopenharmony_ci} 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci/* release interrupt */ 4718c2ecf20Sopenharmony_cistatic void snd_nm256_release_irq(struct nm256 *chip) 4728c2ecf20Sopenharmony_ci{ 4738c2ecf20Sopenharmony_ci mutex_lock(&chip->irq_mutex); 4748c2ecf20Sopenharmony_ci if (chip->irq_acks > 0) 4758c2ecf20Sopenharmony_ci chip->irq_acks--; 4768c2ecf20Sopenharmony_ci if (chip->irq_acks == 0 && chip->irq >= 0) { 4778c2ecf20Sopenharmony_ci free_irq(chip->irq, chip); 4788c2ecf20Sopenharmony_ci chip->irq = -1; 4798c2ecf20Sopenharmony_ci chip->card->sync_irq = -1; 4808c2ecf20Sopenharmony_ci } 4818c2ecf20Sopenharmony_ci mutex_unlock(&chip->irq_mutex); 4828c2ecf20Sopenharmony_ci} 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci/* 4858c2ecf20Sopenharmony_ci * start / stop 4868c2ecf20Sopenharmony_ci */ 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci/* update the watermark (current period) */ 4898c2ecf20Sopenharmony_cistatic void snd_nm256_pcm_mark(struct nm256 *chip, struct nm256_stream *s, int reg) 4908c2ecf20Sopenharmony_ci{ 4918c2ecf20Sopenharmony_ci s->cur_period++; 4928c2ecf20Sopenharmony_ci s->cur_period %= s->periods; 4938c2ecf20Sopenharmony_ci snd_nm256_writel(chip, reg, s->buf + s->cur_period * s->period_size); 4948c2ecf20Sopenharmony_ci} 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci#define snd_nm256_playback_mark(chip, s) snd_nm256_pcm_mark(chip, s, NM_PBUFFER_WMARK) 4978c2ecf20Sopenharmony_ci#define snd_nm256_capture_mark(chip, s) snd_nm256_pcm_mark(chip, s, NM_RBUFFER_WMARK) 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_cistatic void 5008c2ecf20Sopenharmony_cisnd_nm256_playback_start(struct nm256 *chip, struct nm256_stream *s, 5018c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 5028c2ecf20Sopenharmony_ci{ 5038c2ecf20Sopenharmony_ci /* program buffer pointers */ 5048c2ecf20Sopenharmony_ci snd_nm256_writel(chip, NM_PBUFFER_START, s->buf); 5058c2ecf20Sopenharmony_ci snd_nm256_writel(chip, NM_PBUFFER_END, s->buf + s->dma_size - (1 << s->shift)); 5068c2ecf20Sopenharmony_ci snd_nm256_writel(chip, NM_PBUFFER_CURRP, s->buf); 5078c2ecf20Sopenharmony_ci snd_nm256_playback_mark(chip, s); 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci /* Enable playback engine and interrupts. */ 5108c2ecf20Sopenharmony_ci snd_nm256_writeb(chip, NM_PLAYBACK_ENABLE_REG, 5118c2ecf20Sopenharmony_ci NM_PLAYBACK_ENABLE_FLAG | NM_PLAYBACK_FREERUN); 5128c2ecf20Sopenharmony_ci /* Enable both channels. */ 5138c2ecf20Sopenharmony_ci snd_nm256_writew(chip, NM_AUDIO_MUTE_REG, 0x0); 5148c2ecf20Sopenharmony_ci} 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_cistatic void 5178c2ecf20Sopenharmony_cisnd_nm256_capture_start(struct nm256 *chip, struct nm256_stream *s, 5188c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 5198c2ecf20Sopenharmony_ci{ 5208c2ecf20Sopenharmony_ci /* program buffer pointers */ 5218c2ecf20Sopenharmony_ci snd_nm256_writel(chip, NM_RBUFFER_START, s->buf); 5228c2ecf20Sopenharmony_ci snd_nm256_writel(chip, NM_RBUFFER_END, s->buf + s->dma_size); 5238c2ecf20Sopenharmony_ci snd_nm256_writel(chip, NM_RBUFFER_CURRP, s->buf); 5248c2ecf20Sopenharmony_ci snd_nm256_capture_mark(chip, s); 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci /* Enable playback engine and interrupts. */ 5278c2ecf20Sopenharmony_ci snd_nm256_writeb(chip, NM_RECORD_ENABLE_REG, 5288c2ecf20Sopenharmony_ci NM_RECORD_ENABLE_FLAG | NM_RECORD_FREERUN); 5298c2ecf20Sopenharmony_ci} 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci/* Stop the play engine. */ 5328c2ecf20Sopenharmony_cistatic void 5338c2ecf20Sopenharmony_cisnd_nm256_playback_stop(struct nm256 *chip) 5348c2ecf20Sopenharmony_ci{ 5358c2ecf20Sopenharmony_ci /* Shut off sound from both channels. */ 5368c2ecf20Sopenharmony_ci snd_nm256_writew(chip, NM_AUDIO_MUTE_REG, 5378c2ecf20Sopenharmony_ci NM_AUDIO_MUTE_LEFT | NM_AUDIO_MUTE_RIGHT); 5388c2ecf20Sopenharmony_ci /* Disable play engine. */ 5398c2ecf20Sopenharmony_ci snd_nm256_writeb(chip, NM_PLAYBACK_ENABLE_REG, 0); 5408c2ecf20Sopenharmony_ci} 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_cistatic void 5438c2ecf20Sopenharmony_cisnd_nm256_capture_stop(struct nm256 *chip) 5448c2ecf20Sopenharmony_ci{ 5458c2ecf20Sopenharmony_ci /* Disable recording engine. */ 5468c2ecf20Sopenharmony_ci snd_nm256_writeb(chip, NM_RECORD_ENABLE_REG, 0); 5478c2ecf20Sopenharmony_ci} 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_cistatic int 5508c2ecf20Sopenharmony_cisnd_nm256_playback_trigger(struct snd_pcm_substream *substream, int cmd) 5518c2ecf20Sopenharmony_ci{ 5528c2ecf20Sopenharmony_ci struct nm256 *chip = snd_pcm_substream_chip(substream); 5538c2ecf20Sopenharmony_ci struct nm256_stream *s = substream->runtime->private_data; 5548c2ecf20Sopenharmony_ci int err = 0; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci if (snd_BUG_ON(!s)) 5578c2ecf20Sopenharmony_ci return -ENXIO; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci spin_lock(&chip->reg_lock); 5608c2ecf20Sopenharmony_ci switch (cmd) { 5618c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 5628c2ecf20Sopenharmony_ci s->suspended = 0; 5638c2ecf20Sopenharmony_ci fallthrough; 5648c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 5658c2ecf20Sopenharmony_ci if (! s->running) { 5668c2ecf20Sopenharmony_ci snd_nm256_playback_start(chip, s, substream); 5678c2ecf20Sopenharmony_ci s->running = 1; 5688c2ecf20Sopenharmony_ci } 5698c2ecf20Sopenharmony_ci break; 5708c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 5718c2ecf20Sopenharmony_ci s->suspended = 1; 5728c2ecf20Sopenharmony_ci fallthrough; 5738c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 5748c2ecf20Sopenharmony_ci if (s->running) { 5758c2ecf20Sopenharmony_ci snd_nm256_playback_stop(chip); 5768c2ecf20Sopenharmony_ci s->running = 0; 5778c2ecf20Sopenharmony_ci } 5788c2ecf20Sopenharmony_ci break; 5798c2ecf20Sopenharmony_ci default: 5808c2ecf20Sopenharmony_ci err = -EINVAL; 5818c2ecf20Sopenharmony_ci break; 5828c2ecf20Sopenharmony_ci } 5838c2ecf20Sopenharmony_ci spin_unlock(&chip->reg_lock); 5848c2ecf20Sopenharmony_ci return err; 5858c2ecf20Sopenharmony_ci} 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_cistatic int 5888c2ecf20Sopenharmony_cisnd_nm256_capture_trigger(struct snd_pcm_substream *substream, int cmd) 5898c2ecf20Sopenharmony_ci{ 5908c2ecf20Sopenharmony_ci struct nm256 *chip = snd_pcm_substream_chip(substream); 5918c2ecf20Sopenharmony_ci struct nm256_stream *s = substream->runtime->private_data; 5928c2ecf20Sopenharmony_ci int err = 0; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci if (snd_BUG_ON(!s)) 5958c2ecf20Sopenharmony_ci return -ENXIO; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci spin_lock(&chip->reg_lock); 5988c2ecf20Sopenharmony_ci switch (cmd) { 5998c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 6008c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 6018c2ecf20Sopenharmony_ci if (! s->running) { 6028c2ecf20Sopenharmony_ci snd_nm256_capture_start(chip, s, substream); 6038c2ecf20Sopenharmony_ci s->running = 1; 6048c2ecf20Sopenharmony_ci } 6058c2ecf20Sopenharmony_ci break; 6068c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 6078c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 6088c2ecf20Sopenharmony_ci if (s->running) { 6098c2ecf20Sopenharmony_ci snd_nm256_capture_stop(chip); 6108c2ecf20Sopenharmony_ci s->running = 0; 6118c2ecf20Sopenharmony_ci } 6128c2ecf20Sopenharmony_ci break; 6138c2ecf20Sopenharmony_ci default: 6148c2ecf20Sopenharmony_ci err = -EINVAL; 6158c2ecf20Sopenharmony_ci break; 6168c2ecf20Sopenharmony_ci } 6178c2ecf20Sopenharmony_ci spin_unlock(&chip->reg_lock); 6188c2ecf20Sopenharmony_ci return err; 6198c2ecf20Sopenharmony_ci} 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci/* 6238c2ecf20Sopenharmony_ci * prepare playback/capture channel 6248c2ecf20Sopenharmony_ci */ 6258c2ecf20Sopenharmony_cistatic int snd_nm256_pcm_prepare(struct snd_pcm_substream *substream) 6268c2ecf20Sopenharmony_ci{ 6278c2ecf20Sopenharmony_ci struct nm256 *chip = snd_pcm_substream_chip(substream); 6288c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 6298c2ecf20Sopenharmony_ci struct nm256_stream *s = runtime->private_data; 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci if (snd_BUG_ON(!s)) 6328c2ecf20Sopenharmony_ci return -ENXIO; 6338c2ecf20Sopenharmony_ci s->dma_size = frames_to_bytes(runtime, substream->runtime->buffer_size); 6348c2ecf20Sopenharmony_ci s->period_size = frames_to_bytes(runtime, substream->runtime->period_size); 6358c2ecf20Sopenharmony_ci s->periods = substream->runtime->periods; 6368c2ecf20Sopenharmony_ci s->cur_period = 0; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci spin_lock_irq(&chip->reg_lock); 6398c2ecf20Sopenharmony_ci s->running = 0; 6408c2ecf20Sopenharmony_ci snd_nm256_set_format(chip, s, substream); 6418c2ecf20Sopenharmony_ci spin_unlock_irq(&chip->reg_lock); 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci return 0; 6448c2ecf20Sopenharmony_ci} 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci/* 6488c2ecf20Sopenharmony_ci * get the current pointer 6498c2ecf20Sopenharmony_ci */ 6508c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t 6518c2ecf20Sopenharmony_cisnd_nm256_playback_pointer(struct snd_pcm_substream *substream) 6528c2ecf20Sopenharmony_ci{ 6538c2ecf20Sopenharmony_ci struct nm256 *chip = snd_pcm_substream_chip(substream); 6548c2ecf20Sopenharmony_ci struct nm256_stream *s = substream->runtime->private_data; 6558c2ecf20Sopenharmony_ci unsigned long curp; 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci if (snd_BUG_ON(!s)) 6588c2ecf20Sopenharmony_ci return 0; 6598c2ecf20Sopenharmony_ci curp = snd_nm256_readl(chip, NM_PBUFFER_CURRP) - (unsigned long)s->buf; 6608c2ecf20Sopenharmony_ci curp %= s->dma_size; 6618c2ecf20Sopenharmony_ci return bytes_to_frames(substream->runtime, curp); 6628c2ecf20Sopenharmony_ci} 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t 6658c2ecf20Sopenharmony_cisnd_nm256_capture_pointer(struct snd_pcm_substream *substream) 6668c2ecf20Sopenharmony_ci{ 6678c2ecf20Sopenharmony_ci struct nm256 *chip = snd_pcm_substream_chip(substream); 6688c2ecf20Sopenharmony_ci struct nm256_stream *s = substream->runtime->private_data; 6698c2ecf20Sopenharmony_ci unsigned long curp; 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci if (snd_BUG_ON(!s)) 6728c2ecf20Sopenharmony_ci return 0; 6738c2ecf20Sopenharmony_ci curp = snd_nm256_readl(chip, NM_RBUFFER_CURRP) - (unsigned long)s->buf; 6748c2ecf20Sopenharmony_ci curp %= s->dma_size; 6758c2ecf20Sopenharmony_ci return bytes_to_frames(substream->runtime, curp); 6768c2ecf20Sopenharmony_ci} 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci/* Remapped I/O space can be accessible as pointer on i386 */ 6798c2ecf20Sopenharmony_ci/* This might be changed in the future */ 6808c2ecf20Sopenharmony_ci#ifndef __i386__ 6818c2ecf20Sopenharmony_ci/* 6828c2ecf20Sopenharmony_ci * silence / copy for playback 6838c2ecf20Sopenharmony_ci */ 6848c2ecf20Sopenharmony_cistatic int 6858c2ecf20Sopenharmony_cisnd_nm256_playback_silence(struct snd_pcm_substream *substream, 6868c2ecf20Sopenharmony_ci int channel, unsigned long pos, unsigned long count) 6878c2ecf20Sopenharmony_ci{ 6888c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 6898c2ecf20Sopenharmony_ci struct nm256_stream *s = runtime->private_data; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci memset_io(s->bufptr + pos, 0, count); 6928c2ecf20Sopenharmony_ci return 0; 6938c2ecf20Sopenharmony_ci} 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_cistatic int 6968c2ecf20Sopenharmony_cisnd_nm256_playback_copy(struct snd_pcm_substream *substream, 6978c2ecf20Sopenharmony_ci int channel, unsigned long pos, 6988c2ecf20Sopenharmony_ci void __user *src, unsigned long count) 6998c2ecf20Sopenharmony_ci{ 7008c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 7018c2ecf20Sopenharmony_ci struct nm256_stream *s = runtime->private_data; 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci if (copy_from_user_toio(s->bufptr + pos, src, count)) 7048c2ecf20Sopenharmony_ci return -EFAULT; 7058c2ecf20Sopenharmony_ci return 0; 7068c2ecf20Sopenharmony_ci} 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_cistatic int 7098c2ecf20Sopenharmony_cisnd_nm256_playback_copy_kernel(struct snd_pcm_substream *substream, 7108c2ecf20Sopenharmony_ci int channel, unsigned long pos, 7118c2ecf20Sopenharmony_ci void *src, unsigned long count) 7128c2ecf20Sopenharmony_ci{ 7138c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 7148c2ecf20Sopenharmony_ci struct nm256_stream *s = runtime->private_data; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci memcpy_toio(s->bufptr + pos, src, count); 7178c2ecf20Sopenharmony_ci return 0; 7188c2ecf20Sopenharmony_ci} 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci/* 7218c2ecf20Sopenharmony_ci * copy to user 7228c2ecf20Sopenharmony_ci */ 7238c2ecf20Sopenharmony_cistatic int 7248c2ecf20Sopenharmony_cisnd_nm256_capture_copy(struct snd_pcm_substream *substream, 7258c2ecf20Sopenharmony_ci int channel, unsigned long pos, 7268c2ecf20Sopenharmony_ci void __user *dst, unsigned long count) 7278c2ecf20Sopenharmony_ci{ 7288c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 7298c2ecf20Sopenharmony_ci struct nm256_stream *s = runtime->private_data; 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci if (copy_to_user_fromio(dst, s->bufptr + pos, count)) 7328c2ecf20Sopenharmony_ci return -EFAULT; 7338c2ecf20Sopenharmony_ci return 0; 7348c2ecf20Sopenharmony_ci} 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_cistatic int 7378c2ecf20Sopenharmony_cisnd_nm256_capture_copy_kernel(struct snd_pcm_substream *substream, 7388c2ecf20Sopenharmony_ci int channel, unsigned long pos, 7398c2ecf20Sopenharmony_ci void *dst, unsigned long count) 7408c2ecf20Sopenharmony_ci{ 7418c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 7428c2ecf20Sopenharmony_ci struct nm256_stream *s = runtime->private_data; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci memcpy_fromio(dst, s->bufptr + pos, count); 7458c2ecf20Sopenharmony_ci return 0; 7468c2ecf20Sopenharmony_ci} 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci#endif /* !__i386__ */ 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci/* 7528c2ecf20Sopenharmony_ci * update playback/capture watermarks 7538c2ecf20Sopenharmony_ci */ 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci/* spinlock held! */ 7568c2ecf20Sopenharmony_cistatic void 7578c2ecf20Sopenharmony_cisnd_nm256_playback_update(struct nm256 *chip) 7588c2ecf20Sopenharmony_ci{ 7598c2ecf20Sopenharmony_ci struct nm256_stream *s; 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci s = &chip->streams[SNDRV_PCM_STREAM_PLAYBACK]; 7628c2ecf20Sopenharmony_ci if (s->running && s->substream) { 7638c2ecf20Sopenharmony_ci spin_unlock(&chip->reg_lock); 7648c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(s->substream); 7658c2ecf20Sopenharmony_ci spin_lock(&chip->reg_lock); 7668c2ecf20Sopenharmony_ci snd_nm256_playback_mark(chip, s); 7678c2ecf20Sopenharmony_ci } 7688c2ecf20Sopenharmony_ci} 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci/* spinlock held! */ 7718c2ecf20Sopenharmony_cistatic void 7728c2ecf20Sopenharmony_cisnd_nm256_capture_update(struct nm256 *chip) 7738c2ecf20Sopenharmony_ci{ 7748c2ecf20Sopenharmony_ci struct nm256_stream *s; 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci s = &chip->streams[SNDRV_PCM_STREAM_CAPTURE]; 7778c2ecf20Sopenharmony_ci if (s->running && s->substream) { 7788c2ecf20Sopenharmony_ci spin_unlock(&chip->reg_lock); 7798c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(s->substream); 7808c2ecf20Sopenharmony_ci spin_lock(&chip->reg_lock); 7818c2ecf20Sopenharmony_ci snd_nm256_capture_mark(chip, s); 7828c2ecf20Sopenharmony_ci } 7838c2ecf20Sopenharmony_ci} 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci/* 7868c2ecf20Sopenharmony_ci * hardware info 7878c2ecf20Sopenharmony_ci */ 7888c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_nm256_playback = 7898c2ecf20Sopenharmony_ci{ 7908c2ecf20Sopenharmony_ci .info = SNDRV_PCM_INFO_MMAP_IOMEM |SNDRV_PCM_INFO_MMAP_VALID | 7918c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_INTERLEAVED | 7928c2ecf20Sopenharmony_ci /*SNDRV_PCM_INFO_PAUSE |*/ 7938c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_RESUME, 7948c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, 7958c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_KNOT/*24k*/ | SNDRV_PCM_RATE_8000_48000, 7968c2ecf20Sopenharmony_ci .rate_min = 8000, 7978c2ecf20Sopenharmony_ci .rate_max = 48000, 7988c2ecf20Sopenharmony_ci .channels_min = 1, 7998c2ecf20Sopenharmony_ci .channels_max = 2, 8008c2ecf20Sopenharmony_ci .periods_min = 2, 8018c2ecf20Sopenharmony_ci .periods_max = 1024, 8028c2ecf20Sopenharmony_ci .buffer_bytes_max = 128 * 1024, 8038c2ecf20Sopenharmony_ci .period_bytes_min = 256, 8048c2ecf20Sopenharmony_ci .period_bytes_max = 128 * 1024, 8058c2ecf20Sopenharmony_ci}; 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_nm256_capture = 8088c2ecf20Sopenharmony_ci{ 8098c2ecf20Sopenharmony_ci .info = SNDRV_PCM_INFO_MMAP_IOMEM | SNDRV_PCM_INFO_MMAP_VALID | 8108c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_INTERLEAVED | 8118c2ecf20Sopenharmony_ci /*SNDRV_PCM_INFO_PAUSE |*/ 8128c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_RESUME, 8138c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, 8148c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_KNOT/*24k*/ | SNDRV_PCM_RATE_8000_48000, 8158c2ecf20Sopenharmony_ci .rate_min = 8000, 8168c2ecf20Sopenharmony_ci .rate_max = 48000, 8178c2ecf20Sopenharmony_ci .channels_min = 1, 8188c2ecf20Sopenharmony_ci .channels_max = 2, 8198c2ecf20Sopenharmony_ci .periods_min = 2, 8208c2ecf20Sopenharmony_ci .periods_max = 1024, 8218c2ecf20Sopenharmony_ci .buffer_bytes_max = 128 * 1024, 8228c2ecf20Sopenharmony_ci .period_bytes_min = 256, 8238c2ecf20Sopenharmony_ci .period_bytes_max = 128 * 1024, 8248c2ecf20Sopenharmony_ci}; 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci/* set dma transfer size */ 8288c2ecf20Sopenharmony_cistatic int snd_nm256_pcm_hw_params(struct snd_pcm_substream *substream, 8298c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *hw_params) 8308c2ecf20Sopenharmony_ci{ 8318c2ecf20Sopenharmony_ci /* area and addr are already set and unchanged */ 8328c2ecf20Sopenharmony_ci substream->runtime->dma_bytes = params_buffer_bytes(hw_params); 8338c2ecf20Sopenharmony_ci return 0; 8348c2ecf20Sopenharmony_ci} 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci/* 8378c2ecf20Sopenharmony_ci * open 8388c2ecf20Sopenharmony_ci */ 8398c2ecf20Sopenharmony_cistatic void snd_nm256_setup_stream(struct nm256 *chip, struct nm256_stream *s, 8408c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream, 8418c2ecf20Sopenharmony_ci const struct snd_pcm_hardware *hw_ptr) 8428c2ecf20Sopenharmony_ci{ 8438c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci s->running = 0; 8468c2ecf20Sopenharmony_ci runtime->hw = *hw_ptr; 8478c2ecf20Sopenharmony_ci runtime->hw.buffer_bytes_max = s->bufsize; 8488c2ecf20Sopenharmony_ci runtime->hw.period_bytes_max = s->bufsize / 2; 8498c2ecf20Sopenharmony_ci runtime->dma_area = (void __force *) s->bufptr; 8508c2ecf20Sopenharmony_ci runtime->dma_addr = s->bufptr_addr; 8518c2ecf20Sopenharmony_ci runtime->dma_bytes = s->bufsize; 8528c2ecf20Sopenharmony_ci runtime->private_data = s; 8538c2ecf20Sopenharmony_ci s->substream = substream; 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, 8568c2ecf20Sopenharmony_ci &constraints_rates); 8578c2ecf20Sopenharmony_ci} 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_cistatic int 8608c2ecf20Sopenharmony_cisnd_nm256_playback_open(struct snd_pcm_substream *substream) 8618c2ecf20Sopenharmony_ci{ 8628c2ecf20Sopenharmony_ci struct nm256 *chip = snd_pcm_substream_chip(substream); 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci if (snd_nm256_acquire_irq(chip) < 0) 8658c2ecf20Sopenharmony_ci return -EBUSY; 8668c2ecf20Sopenharmony_ci snd_nm256_setup_stream(chip, &chip->streams[SNDRV_PCM_STREAM_PLAYBACK], 8678c2ecf20Sopenharmony_ci substream, &snd_nm256_playback); 8688c2ecf20Sopenharmony_ci return 0; 8698c2ecf20Sopenharmony_ci} 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_cistatic int 8728c2ecf20Sopenharmony_cisnd_nm256_capture_open(struct snd_pcm_substream *substream) 8738c2ecf20Sopenharmony_ci{ 8748c2ecf20Sopenharmony_ci struct nm256 *chip = snd_pcm_substream_chip(substream); 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci if (snd_nm256_acquire_irq(chip) < 0) 8778c2ecf20Sopenharmony_ci return -EBUSY; 8788c2ecf20Sopenharmony_ci snd_nm256_setup_stream(chip, &chip->streams[SNDRV_PCM_STREAM_CAPTURE], 8798c2ecf20Sopenharmony_ci substream, &snd_nm256_capture); 8808c2ecf20Sopenharmony_ci return 0; 8818c2ecf20Sopenharmony_ci} 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci/* 8848c2ecf20Sopenharmony_ci * close - we don't have to do special.. 8858c2ecf20Sopenharmony_ci */ 8868c2ecf20Sopenharmony_cistatic int 8878c2ecf20Sopenharmony_cisnd_nm256_playback_close(struct snd_pcm_substream *substream) 8888c2ecf20Sopenharmony_ci{ 8898c2ecf20Sopenharmony_ci struct nm256 *chip = snd_pcm_substream_chip(substream); 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci snd_nm256_release_irq(chip); 8928c2ecf20Sopenharmony_ci return 0; 8938c2ecf20Sopenharmony_ci} 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_cistatic int 8978c2ecf20Sopenharmony_cisnd_nm256_capture_close(struct snd_pcm_substream *substream) 8988c2ecf20Sopenharmony_ci{ 8998c2ecf20Sopenharmony_ci struct nm256 *chip = snd_pcm_substream_chip(substream); 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci snd_nm256_release_irq(chip); 9028c2ecf20Sopenharmony_ci return 0; 9038c2ecf20Sopenharmony_ci} 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci/* 9068c2ecf20Sopenharmony_ci * create a pcm instance 9078c2ecf20Sopenharmony_ci */ 9088c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_nm256_playback_ops = { 9098c2ecf20Sopenharmony_ci .open = snd_nm256_playback_open, 9108c2ecf20Sopenharmony_ci .close = snd_nm256_playback_close, 9118c2ecf20Sopenharmony_ci .hw_params = snd_nm256_pcm_hw_params, 9128c2ecf20Sopenharmony_ci .prepare = snd_nm256_pcm_prepare, 9138c2ecf20Sopenharmony_ci .trigger = snd_nm256_playback_trigger, 9148c2ecf20Sopenharmony_ci .pointer = snd_nm256_playback_pointer, 9158c2ecf20Sopenharmony_ci#ifndef __i386__ 9168c2ecf20Sopenharmony_ci .copy_user = snd_nm256_playback_copy, 9178c2ecf20Sopenharmony_ci .copy_kernel = snd_nm256_playback_copy_kernel, 9188c2ecf20Sopenharmony_ci .fill_silence = snd_nm256_playback_silence, 9198c2ecf20Sopenharmony_ci#endif 9208c2ecf20Sopenharmony_ci .mmap = snd_pcm_lib_mmap_iomem, 9218c2ecf20Sopenharmony_ci}; 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_nm256_capture_ops = { 9248c2ecf20Sopenharmony_ci .open = snd_nm256_capture_open, 9258c2ecf20Sopenharmony_ci .close = snd_nm256_capture_close, 9268c2ecf20Sopenharmony_ci .hw_params = snd_nm256_pcm_hw_params, 9278c2ecf20Sopenharmony_ci .prepare = snd_nm256_pcm_prepare, 9288c2ecf20Sopenharmony_ci .trigger = snd_nm256_capture_trigger, 9298c2ecf20Sopenharmony_ci .pointer = snd_nm256_capture_pointer, 9308c2ecf20Sopenharmony_ci#ifndef __i386__ 9318c2ecf20Sopenharmony_ci .copy_user = snd_nm256_capture_copy, 9328c2ecf20Sopenharmony_ci .copy_kernel = snd_nm256_capture_copy_kernel, 9338c2ecf20Sopenharmony_ci#endif 9348c2ecf20Sopenharmony_ci .mmap = snd_pcm_lib_mmap_iomem, 9358c2ecf20Sopenharmony_ci}; 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_cistatic int 9388c2ecf20Sopenharmony_cisnd_nm256_pcm(struct nm256 *chip, int device) 9398c2ecf20Sopenharmony_ci{ 9408c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 9418c2ecf20Sopenharmony_ci int i, err; 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci for (i = 0; i < 2; i++) { 9448c2ecf20Sopenharmony_ci struct nm256_stream *s = &chip->streams[i]; 9458c2ecf20Sopenharmony_ci s->bufptr = chip->buffer + (s->buf - chip->buffer_start); 9468c2ecf20Sopenharmony_ci s->bufptr_addr = chip->buffer_addr + (s->buf - chip->buffer_start); 9478c2ecf20Sopenharmony_ci } 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci err = snd_pcm_new(chip->card, chip->card->driver, device, 9508c2ecf20Sopenharmony_ci 1, 1, &pcm); 9518c2ecf20Sopenharmony_ci if (err < 0) 9528c2ecf20Sopenharmony_ci return err; 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_nm256_playback_ops); 9558c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_nm256_capture_ops); 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci pcm->private_data = chip; 9588c2ecf20Sopenharmony_ci pcm->info_flags = 0; 9598c2ecf20Sopenharmony_ci chip->pcm = pcm; 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci return 0; 9628c2ecf20Sopenharmony_ci} 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci/* 9668c2ecf20Sopenharmony_ci * Initialize the hardware. 9678c2ecf20Sopenharmony_ci */ 9688c2ecf20Sopenharmony_cistatic void 9698c2ecf20Sopenharmony_cisnd_nm256_init_chip(struct nm256 *chip) 9708c2ecf20Sopenharmony_ci{ 9718c2ecf20Sopenharmony_ci /* Reset everything. */ 9728c2ecf20Sopenharmony_ci snd_nm256_writeb(chip, 0x0, 0x11); 9738c2ecf20Sopenharmony_ci snd_nm256_writew(chip, 0x214, 0); 9748c2ecf20Sopenharmony_ci /* stop sounds.. */ 9758c2ecf20Sopenharmony_ci //snd_nm256_playback_stop(chip); 9768c2ecf20Sopenharmony_ci //snd_nm256_capture_stop(chip); 9778c2ecf20Sopenharmony_ci} 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_cistatic irqreturn_t 9818c2ecf20Sopenharmony_cisnd_nm256_intr_check(struct nm256 *chip) 9828c2ecf20Sopenharmony_ci{ 9838c2ecf20Sopenharmony_ci if (chip->badintrcount++ > 1000) { 9848c2ecf20Sopenharmony_ci /* 9858c2ecf20Sopenharmony_ci * I'm not sure if the best thing is to stop the card from 9868c2ecf20Sopenharmony_ci * playing or just release the interrupt (after all, we're in 9878c2ecf20Sopenharmony_ci * a bad situation, so doing fancy stuff may not be such a good 9888c2ecf20Sopenharmony_ci * idea). 9898c2ecf20Sopenharmony_ci * 9908c2ecf20Sopenharmony_ci * I worry about the card engine continuing to play noise 9918c2ecf20Sopenharmony_ci * over and over, however--that could become a very 9928c2ecf20Sopenharmony_ci * obnoxious problem. And we know that when this usually 9938c2ecf20Sopenharmony_ci * happens things are fairly safe, it just means the user's 9948c2ecf20Sopenharmony_ci * inserted a PCMCIA card and someone's spamming us with IRQ 9s. 9958c2ecf20Sopenharmony_ci */ 9968c2ecf20Sopenharmony_ci if (chip->streams[SNDRV_PCM_STREAM_PLAYBACK].running) 9978c2ecf20Sopenharmony_ci snd_nm256_playback_stop(chip); 9988c2ecf20Sopenharmony_ci if (chip->streams[SNDRV_PCM_STREAM_CAPTURE].running) 9998c2ecf20Sopenharmony_ci snd_nm256_capture_stop(chip); 10008c2ecf20Sopenharmony_ci chip->badintrcount = 0; 10018c2ecf20Sopenharmony_ci return IRQ_HANDLED; 10028c2ecf20Sopenharmony_ci } 10038c2ecf20Sopenharmony_ci return IRQ_NONE; 10048c2ecf20Sopenharmony_ci} 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci/* 10078c2ecf20Sopenharmony_ci * Handle a potential interrupt for the device referred to by DEV_ID. 10088c2ecf20Sopenharmony_ci * 10098c2ecf20Sopenharmony_ci * I don't like the cut-n-paste job here either between the two routines, 10108c2ecf20Sopenharmony_ci * but there are sufficient differences between the two interrupt handlers 10118c2ecf20Sopenharmony_ci * that parameterizing it isn't all that great either. (Could use a macro, 10128c2ecf20Sopenharmony_ci * I suppose...yucky bleah.) 10138c2ecf20Sopenharmony_ci */ 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_cistatic irqreturn_t 10168c2ecf20Sopenharmony_cisnd_nm256_interrupt(int irq, void *dev_id) 10178c2ecf20Sopenharmony_ci{ 10188c2ecf20Sopenharmony_ci struct nm256 *chip = dev_id; 10198c2ecf20Sopenharmony_ci u16 status; 10208c2ecf20Sopenharmony_ci u8 cbyte; 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci status = snd_nm256_readw(chip, NM_INT_REG); 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci /* Not ours. */ 10258c2ecf20Sopenharmony_ci if (status == 0) 10268c2ecf20Sopenharmony_ci return snd_nm256_intr_check(chip); 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci chip->badintrcount = 0; 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci /* Rather boring; check for individual interrupts and process them. */ 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci spin_lock(&chip->reg_lock); 10338c2ecf20Sopenharmony_ci if (status & NM_PLAYBACK_INT) { 10348c2ecf20Sopenharmony_ci status &= ~NM_PLAYBACK_INT; 10358c2ecf20Sopenharmony_ci NM_ACK_INT(chip, NM_PLAYBACK_INT); 10368c2ecf20Sopenharmony_ci snd_nm256_playback_update(chip); 10378c2ecf20Sopenharmony_ci } 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci if (status & NM_RECORD_INT) { 10408c2ecf20Sopenharmony_ci status &= ~NM_RECORD_INT; 10418c2ecf20Sopenharmony_ci NM_ACK_INT(chip, NM_RECORD_INT); 10428c2ecf20Sopenharmony_ci snd_nm256_capture_update(chip); 10438c2ecf20Sopenharmony_ci } 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci if (status & NM_MISC_INT_1) { 10468c2ecf20Sopenharmony_ci status &= ~NM_MISC_INT_1; 10478c2ecf20Sopenharmony_ci NM_ACK_INT(chip, NM_MISC_INT_1); 10488c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "NM256: Got misc interrupt #1\n"); 10498c2ecf20Sopenharmony_ci snd_nm256_writew(chip, NM_INT_REG, 0x8000); 10508c2ecf20Sopenharmony_ci cbyte = snd_nm256_readb(chip, 0x400); 10518c2ecf20Sopenharmony_ci snd_nm256_writeb(chip, 0x400, cbyte | 2); 10528c2ecf20Sopenharmony_ci } 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci if (status & NM_MISC_INT_2) { 10558c2ecf20Sopenharmony_ci status &= ~NM_MISC_INT_2; 10568c2ecf20Sopenharmony_ci NM_ACK_INT(chip, NM_MISC_INT_2); 10578c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "NM256: Got misc interrupt #2\n"); 10588c2ecf20Sopenharmony_ci cbyte = snd_nm256_readb(chip, 0x400); 10598c2ecf20Sopenharmony_ci snd_nm256_writeb(chip, 0x400, cbyte & ~2); 10608c2ecf20Sopenharmony_ci } 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci /* Unknown interrupt. */ 10638c2ecf20Sopenharmony_ci if (status) { 10648c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, 10658c2ecf20Sopenharmony_ci "NM256: Fire in the hole! Unknown status 0x%x\n", 10668c2ecf20Sopenharmony_ci status); 10678c2ecf20Sopenharmony_ci /* Pray. */ 10688c2ecf20Sopenharmony_ci NM_ACK_INT(chip, status); 10698c2ecf20Sopenharmony_ci } 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci spin_unlock(&chip->reg_lock); 10728c2ecf20Sopenharmony_ci return IRQ_HANDLED; 10738c2ecf20Sopenharmony_ci} 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci/* 10768c2ecf20Sopenharmony_ci * Handle a potential interrupt for the device referred to by DEV_ID. 10778c2ecf20Sopenharmony_ci * This handler is for the 256ZX, and is very similar to the non-ZX 10788c2ecf20Sopenharmony_ci * routine. 10798c2ecf20Sopenharmony_ci */ 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_cistatic irqreturn_t 10828c2ecf20Sopenharmony_cisnd_nm256_interrupt_zx(int irq, void *dev_id) 10838c2ecf20Sopenharmony_ci{ 10848c2ecf20Sopenharmony_ci struct nm256 *chip = dev_id; 10858c2ecf20Sopenharmony_ci u32 status; 10868c2ecf20Sopenharmony_ci u8 cbyte; 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci status = snd_nm256_readl(chip, NM_INT_REG); 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci /* Not ours. */ 10918c2ecf20Sopenharmony_ci if (status == 0) 10928c2ecf20Sopenharmony_ci return snd_nm256_intr_check(chip); 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci chip->badintrcount = 0; 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci /* Rather boring; check for individual interrupts and process them. */ 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci spin_lock(&chip->reg_lock); 10998c2ecf20Sopenharmony_ci if (status & NM2_PLAYBACK_INT) { 11008c2ecf20Sopenharmony_ci status &= ~NM2_PLAYBACK_INT; 11018c2ecf20Sopenharmony_ci NM2_ACK_INT(chip, NM2_PLAYBACK_INT); 11028c2ecf20Sopenharmony_ci snd_nm256_playback_update(chip); 11038c2ecf20Sopenharmony_ci } 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci if (status & NM2_RECORD_INT) { 11068c2ecf20Sopenharmony_ci status &= ~NM2_RECORD_INT; 11078c2ecf20Sopenharmony_ci NM2_ACK_INT(chip, NM2_RECORD_INT); 11088c2ecf20Sopenharmony_ci snd_nm256_capture_update(chip); 11098c2ecf20Sopenharmony_ci } 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci if (status & NM2_MISC_INT_1) { 11128c2ecf20Sopenharmony_ci status &= ~NM2_MISC_INT_1; 11138c2ecf20Sopenharmony_ci NM2_ACK_INT(chip, NM2_MISC_INT_1); 11148c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "NM256: Got misc interrupt #1\n"); 11158c2ecf20Sopenharmony_ci cbyte = snd_nm256_readb(chip, 0x400); 11168c2ecf20Sopenharmony_ci snd_nm256_writeb(chip, 0x400, cbyte | 2); 11178c2ecf20Sopenharmony_ci } 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci if (status & NM2_MISC_INT_2) { 11208c2ecf20Sopenharmony_ci status &= ~NM2_MISC_INT_2; 11218c2ecf20Sopenharmony_ci NM2_ACK_INT(chip, NM2_MISC_INT_2); 11228c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "NM256: Got misc interrupt #2\n"); 11238c2ecf20Sopenharmony_ci cbyte = snd_nm256_readb(chip, 0x400); 11248c2ecf20Sopenharmony_ci snd_nm256_writeb(chip, 0x400, cbyte & ~2); 11258c2ecf20Sopenharmony_ci } 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci /* Unknown interrupt. */ 11288c2ecf20Sopenharmony_ci if (status) { 11298c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, 11308c2ecf20Sopenharmony_ci "NM256: Fire in the hole! Unknown status 0x%x\n", 11318c2ecf20Sopenharmony_ci status); 11328c2ecf20Sopenharmony_ci /* Pray. */ 11338c2ecf20Sopenharmony_ci NM2_ACK_INT(chip, status); 11348c2ecf20Sopenharmony_ci } 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci spin_unlock(&chip->reg_lock); 11378c2ecf20Sopenharmony_ci return IRQ_HANDLED; 11388c2ecf20Sopenharmony_ci} 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci/* 11418c2ecf20Sopenharmony_ci * AC97 interface 11428c2ecf20Sopenharmony_ci */ 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci/* 11458c2ecf20Sopenharmony_ci * Waits for the mixer to become ready to be written; returns a zero value 11468c2ecf20Sopenharmony_ci * if it timed out. 11478c2ecf20Sopenharmony_ci */ 11488c2ecf20Sopenharmony_cistatic int 11498c2ecf20Sopenharmony_cisnd_nm256_ac97_ready(struct nm256 *chip) 11508c2ecf20Sopenharmony_ci{ 11518c2ecf20Sopenharmony_ci int timeout = 10; 11528c2ecf20Sopenharmony_ci u32 testaddr; 11538c2ecf20Sopenharmony_ci u16 testb; 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci testaddr = chip->mixer_status_offset; 11568c2ecf20Sopenharmony_ci testb = chip->mixer_status_mask; 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci /* 11598c2ecf20Sopenharmony_ci * Loop around waiting for the mixer to become ready. 11608c2ecf20Sopenharmony_ci */ 11618c2ecf20Sopenharmony_ci while (timeout-- > 0) { 11628c2ecf20Sopenharmony_ci if ((snd_nm256_readw(chip, testaddr) & testb) == 0) 11638c2ecf20Sopenharmony_ci return 1; 11648c2ecf20Sopenharmony_ci udelay(100); 11658c2ecf20Sopenharmony_ci } 11668c2ecf20Sopenharmony_ci return 0; 11678c2ecf20Sopenharmony_ci} 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci/* 11708c2ecf20Sopenharmony_ci * Initial register values to be written to the AC97 mixer. 11718c2ecf20Sopenharmony_ci * While most of these are identical to the reset values, we do this 11728c2ecf20Sopenharmony_ci * so that we have most of the register contents cached--this avoids 11738c2ecf20Sopenharmony_ci * reading from the mixer directly (which seems to be problematic, 11748c2ecf20Sopenharmony_ci * probably due to ignorance). 11758c2ecf20Sopenharmony_ci */ 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_cistruct initialValues { 11788c2ecf20Sopenharmony_ci unsigned short reg; 11798c2ecf20Sopenharmony_ci unsigned short value; 11808c2ecf20Sopenharmony_ci}; 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_cistatic const struct initialValues nm256_ac97_init_val[] = 11838c2ecf20Sopenharmony_ci{ 11848c2ecf20Sopenharmony_ci { AC97_MASTER, 0x8000 }, 11858c2ecf20Sopenharmony_ci { AC97_HEADPHONE, 0x8000 }, 11868c2ecf20Sopenharmony_ci { AC97_MASTER_MONO, 0x8000 }, 11878c2ecf20Sopenharmony_ci { AC97_PC_BEEP, 0x8000 }, 11888c2ecf20Sopenharmony_ci { AC97_PHONE, 0x8008 }, 11898c2ecf20Sopenharmony_ci { AC97_MIC, 0x8000 }, 11908c2ecf20Sopenharmony_ci { AC97_LINE, 0x8808 }, 11918c2ecf20Sopenharmony_ci { AC97_CD, 0x8808 }, 11928c2ecf20Sopenharmony_ci { AC97_VIDEO, 0x8808 }, 11938c2ecf20Sopenharmony_ci { AC97_AUX, 0x8808 }, 11948c2ecf20Sopenharmony_ci { AC97_PCM, 0x8808 }, 11958c2ecf20Sopenharmony_ci { AC97_REC_SEL, 0x0000 }, 11968c2ecf20Sopenharmony_ci { AC97_REC_GAIN, 0x0B0B }, 11978c2ecf20Sopenharmony_ci { AC97_GENERAL_PURPOSE, 0x0000 }, 11988c2ecf20Sopenharmony_ci { AC97_3D_CONTROL, 0x8000 }, 11998c2ecf20Sopenharmony_ci { AC97_VENDOR_ID1, 0x8384 }, 12008c2ecf20Sopenharmony_ci { AC97_VENDOR_ID2, 0x7609 }, 12018c2ecf20Sopenharmony_ci}; 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_cistatic int nm256_ac97_idx(unsigned short reg) 12048c2ecf20Sopenharmony_ci{ 12058c2ecf20Sopenharmony_ci int i; 12068c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(nm256_ac97_init_val); i++) 12078c2ecf20Sopenharmony_ci if (nm256_ac97_init_val[i].reg == reg) 12088c2ecf20Sopenharmony_ci return i; 12098c2ecf20Sopenharmony_ci return -1; 12108c2ecf20Sopenharmony_ci} 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci/* 12138c2ecf20Sopenharmony_ci * some nm256 easily crash when reading from mixer registers 12148c2ecf20Sopenharmony_ci * thus we're treating it as a write-only mixer and cache the 12158c2ecf20Sopenharmony_ci * written values 12168c2ecf20Sopenharmony_ci */ 12178c2ecf20Sopenharmony_cistatic unsigned short 12188c2ecf20Sopenharmony_cisnd_nm256_ac97_read(struct snd_ac97 *ac97, unsigned short reg) 12198c2ecf20Sopenharmony_ci{ 12208c2ecf20Sopenharmony_ci struct nm256 *chip = ac97->private_data; 12218c2ecf20Sopenharmony_ci int idx = nm256_ac97_idx(reg); 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci if (idx < 0) 12248c2ecf20Sopenharmony_ci return 0; 12258c2ecf20Sopenharmony_ci return chip->ac97_regs[idx]; 12268c2ecf20Sopenharmony_ci} 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci/* 12298c2ecf20Sopenharmony_ci */ 12308c2ecf20Sopenharmony_cistatic void 12318c2ecf20Sopenharmony_cisnd_nm256_ac97_write(struct snd_ac97 *ac97, 12328c2ecf20Sopenharmony_ci unsigned short reg, unsigned short val) 12338c2ecf20Sopenharmony_ci{ 12348c2ecf20Sopenharmony_ci struct nm256 *chip = ac97->private_data; 12358c2ecf20Sopenharmony_ci int tries = 2; 12368c2ecf20Sopenharmony_ci int idx = nm256_ac97_idx(reg); 12378c2ecf20Sopenharmony_ci u32 base; 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ci if (idx < 0) 12408c2ecf20Sopenharmony_ci return; 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ci base = chip->mixer_base; 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci snd_nm256_ac97_ready(chip); 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci /* Wait for the write to take, too. */ 12478c2ecf20Sopenharmony_ci while (tries-- > 0) { 12488c2ecf20Sopenharmony_ci snd_nm256_writew(chip, base + reg, val); 12498c2ecf20Sopenharmony_ci msleep(1); /* a little delay here seems better.. */ 12508c2ecf20Sopenharmony_ci if (snd_nm256_ac97_ready(chip)) { 12518c2ecf20Sopenharmony_ci /* successful write: set cache */ 12528c2ecf20Sopenharmony_ci chip->ac97_regs[idx] = val; 12538c2ecf20Sopenharmony_ci return; 12548c2ecf20Sopenharmony_ci } 12558c2ecf20Sopenharmony_ci } 12568c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, "nm256: ac97 codec not ready..\n"); 12578c2ecf20Sopenharmony_ci} 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci/* static resolution table */ 12608c2ecf20Sopenharmony_cistatic const struct snd_ac97_res_table nm256_res_table[] = { 12618c2ecf20Sopenharmony_ci { AC97_MASTER, 0x1f1f }, 12628c2ecf20Sopenharmony_ci { AC97_HEADPHONE, 0x1f1f }, 12638c2ecf20Sopenharmony_ci { AC97_MASTER_MONO, 0x001f }, 12648c2ecf20Sopenharmony_ci { AC97_PC_BEEP, 0x001f }, 12658c2ecf20Sopenharmony_ci { AC97_PHONE, 0x001f }, 12668c2ecf20Sopenharmony_ci { AC97_MIC, 0x001f }, 12678c2ecf20Sopenharmony_ci { AC97_LINE, 0x1f1f }, 12688c2ecf20Sopenharmony_ci { AC97_CD, 0x1f1f }, 12698c2ecf20Sopenharmony_ci { AC97_VIDEO, 0x1f1f }, 12708c2ecf20Sopenharmony_ci { AC97_AUX, 0x1f1f }, 12718c2ecf20Sopenharmony_ci { AC97_PCM, 0x1f1f }, 12728c2ecf20Sopenharmony_ci { AC97_REC_GAIN, 0x0f0f }, 12738c2ecf20Sopenharmony_ci { } /* terminator */ 12748c2ecf20Sopenharmony_ci}; 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci/* initialize the ac97 into a known state */ 12778c2ecf20Sopenharmony_cistatic void 12788c2ecf20Sopenharmony_cisnd_nm256_ac97_reset(struct snd_ac97 *ac97) 12798c2ecf20Sopenharmony_ci{ 12808c2ecf20Sopenharmony_ci struct nm256 *chip = ac97->private_data; 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci /* Reset the mixer. 'Tis magic! */ 12838c2ecf20Sopenharmony_ci snd_nm256_writeb(chip, 0x6c0, 1); 12848c2ecf20Sopenharmony_ci if (! chip->reset_workaround) { 12858c2ecf20Sopenharmony_ci /* Dell latitude LS will lock up by this */ 12868c2ecf20Sopenharmony_ci snd_nm256_writeb(chip, 0x6cc, 0x87); 12878c2ecf20Sopenharmony_ci } 12888c2ecf20Sopenharmony_ci if (! chip->reset_workaround_2) { 12898c2ecf20Sopenharmony_ci /* Dell latitude CSx will lock up by this */ 12908c2ecf20Sopenharmony_ci snd_nm256_writeb(chip, 0x6cc, 0x80); 12918c2ecf20Sopenharmony_ci snd_nm256_writeb(chip, 0x6cc, 0x0); 12928c2ecf20Sopenharmony_ci } 12938c2ecf20Sopenharmony_ci if (! chip->in_resume) { 12948c2ecf20Sopenharmony_ci int i; 12958c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(nm256_ac97_init_val); i++) { 12968c2ecf20Sopenharmony_ci /* preload the cache, so as to avoid even a single 12978c2ecf20Sopenharmony_ci * read of the mixer regs 12988c2ecf20Sopenharmony_ci */ 12998c2ecf20Sopenharmony_ci snd_nm256_ac97_write(ac97, nm256_ac97_init_val[i].reg, 13008c2ecf20Sopenharmony_ci nm256_ac97_init_val[i].value); 13018c2ecf20Sopenharmony_ci } 13028c2ecf20Sopenharmony_ci } 13038c2ecf20Sopenharmony_ci} 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci/* create an ac97 mixer interface */ 13068c2ecf20Sopenharmony_cistatic int 13078c2ecf20Sopenharmony_cisnd_nm256_mixer(struct nm256 *chip) 13088c2ecf20Sopenharmony_ci{ 13098c2ecf20Sopenharmony_ci struct snd_ac97_bus *pbus; 13108c2ecf20Sopenharmony_ci struct snd_ac97_template ac97; 13118c2ecf20Sopenharmony_ci int err; 13128c2ecf20Sopenharmony_ci static const struct snd_ac97_bus_ops ops = { 13138c2ecf20Sopenharmony_ci .reset = snd_nm256_ac97_reset, 13148c2ecf20Sopenharmony_ci .write = snd_nm256_ac97_write, 13158c2ecf20Sopenharmony_ci .read = snd_nm256_ac97_read, 13168c2ecf20Sopenharmony_ci }; 13178c2ecf20Sopenharmony_ci 13188c2ecf20Sopenharmony_ci chip->ac97_regs = kcalloc(ARRAY_SIZE(nm256_ac97_init_val), 13198c2ecf20Sopenharmony_ci sizeof(short), GFP_KERNEL); 13208c2ecf20Sopenharmony_ci if (! chip->ac97_regs) 13218c2ecf20Sopenharmony_ci return -ENOMEM; 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci if ((err = snd_ac97_bus(chip->card, 0, &ops, NULL, &pbus)) < 0) 13248c2ecf20Sopenharmony_ci return err; 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_ci memset(&ac97, 0, sizeof(ac97)); 13278c2ecf20Sopenharmony_ci ac97.scaps = AC97_SCAP_AUDIO; /* we support audio! */ 13288c2ecf20Sopenharmony_ci ac97.private_data = chip; 13298c2ecf20Sopenharmony_ci ac97.res_table = nm256_res_table; 13308c2ecf20Sopenharmony_ci pbus->no_vra = 1; 13318c2ecf20Sopenharmony_ci err = snd_ac97_mixer(pbus, &ac97, &chip->ac97); 13328c2ecf20Sopenharmony_ci if (err < 0) 13338c2ecf20Sopenharmony_ci return err; 13348c2ecf20Sopenharmony_ci if (! (chip->ac97->id & (0xf0000000))) { 13358c2ecf20Sopenharmony_ci /* looks like an invalid id */ 13368c2ecf20Sopenharmony_ci sprintf(chip->card->mixername, "%s AC97", chip->card->driver); 13378c2ecf20Sopenharmony_ci } 13388c2ecf20Sopenharmony_ci return 0; 13398c2ecf20Sopenharmony_ci} 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_ci/* 13428c2ecf20Sopenharmony_ci * See if the signature left by the NM256 BIOS is intact; if so, we use 13438c2ecf20Sopenharmony_ci * the associated address as the end of our audio buffer in the video 13448c2ecf20Sopenharmony_ci * RAM. 13458c2ecf20Sopenharmony_ci */ 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_cistatic int 13488c2ecf20Sopenharmony_cisnd_nm256_peek_for_sig(struct nm256 *chip) 13498c2ecf20Sopenharmony_ci{ 13508c2ecf20Sopenharmony_ci /* The signature is located 1K below the end of video RAM. */ 13518c2ecf20Sopenharmony_ci void __iomem *temp; 13528c2ecf20Sopenharmony_ci /* Default buffer end is 5120 bytes below the top of RAM. */ 13538c2ecf20Sopenharmony_ci unsigned long pointer_found = chip->buffer_end - 0x1400; 13548c2ecf20Sopenharmony_ci u32 sig; 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_ci temp = ioremap(chip->buffer_addr + chip->buffer_end - 0x400, 16); 13578c2ecf20Sopenharmony_ci if (temp == NULL) { 13588c2ecf20Sopenharmony_ci dev_err(chip->card->dev, 13598c2ecf20Sopenharmony_ci "Unable to scan for card signature in video RAM\n"); 13608c2ecf20Sopenharmony_ci return -EBUSY; 13618c2ecf20Sopenharmony_ci } 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci sig = readl(temp); 13648c2ecf20Sopenharmony_ci if ((sig & NM_SIG_MASK) == NM_SIGNATURE) { 13658c2ecf20Sopenharmony_ci u32 pointer = readl(temp + 4); 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_ci /* 13688c2ecf20Sopenharmony_ci * If it's obviously invalid, don't use it 13698c2ecf20Sopenharmony_ci */ 13708c2ecf20Sopenharmony_ci if (pointer == 0xffffffff || 13718c2ecf20Sopenharmony_ci pointer < chip->buffer_size || 13728c2ecf20Sopenharmony_ci pointer > chip->buffer_end) { 13738c2ecf20Sopenharmony_ci dev_err(chip->card->dev, 13748c2ecf20Sopenharmony_ci "invalid signature found: 0x%x\n", pointer); 13758c2ecf20Sopenharmony_ci iounmap(temp); 13768c2ecf20Sopenharmony_ci return -ENODEV; 13778c2ecf20Sopenharmony_ci } else { 13788c2ecf20Sopenharmony_ci pointer_found = pointer; 13798c2ecf20Sopenharmony_ci dev_info(chip->card->dev, 13808c2ecf20Sopenharmony_ci "found card signature in video RAM: 0x%x\n", 13818c2ecf20Sopenharmony_ci pointer); 13828c2ecf20Sopenharmony_ci } 13838c2ecf20Sopenharmony_ci } 13848c2ecf20Sopenharmony_ci 13858c2ecf20Sopenharmony_ci iounmap(temp); 13868c2ecf20Sopenharmony_ci chip->buffer_end = pointer_found; 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci return 0; 13898c2ecf20Sopenharmony_ci} 13908c2ecf20Sopenharmony_ci 13918c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 13928c2ecf20Sopenharmony_ci/* 13938c2ecf20Sopenharmony_ci * APM event handler, so the card is properly reinitialized after a power 13948c2ecf20Sopenharmony_ci * event. 13958c2ecf20Sopenharmony_ci */ 13968c2ecf20Sopenharmony_cistatic int nm256_suspend(struct device *dev) 13978c2ecf20Sopenharmony_ci{ 13988c2ecf20Sopenharmony_ci struct snd_card *card = dev_get_drvdata(dev); 13998c2ecf20Sopenharmony_ci struct nm256 *chip = card->private_data; 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_ci snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); 14028c2ecf20Sopenharmony_ci snd_ac97_suspend(chip->ac97); 14038c2ecf20Sopenharmony_ci chip->coeffs_current = 0; 14048c2ecf20Sopenharmony_ci return 0; 14058c2ecf20Sopenharmony_ci} 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_cistatic int nm256_resume(struct device *dev) 14088c2ecf20Sopenharmony_ci{ 14098c2ecf20Sopenharmony_ci struct snd_card *card = dev_get_drvdata(dev); 14108c2ecf20Sopenharmony_ci struct nm256 *chip = card->private_data; 14118c2ecf20Sopenharmony_ci int i; 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_ci /* Perform a full reset on the hardware */ 14148c2ecf20Sopenharmony_ci chip->in_resume = 1; 14158c2ecf20Sopenharmony_ci 14168c2ecf20Sopenharmony_ci snd_nm256_init_chip(chip); 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_ci /* restore ac97 */ 14198c2ecf20Sopenharmony_ci snd_ac97_resume(chip->ac97); 14208c2ecf20Sopenharmony_ci 14218c2ecf20Sopenharmony_ci for (i = 0; i < 2; i++) { 14228c2ecf20Sopenharmony_ci struct nm256_stream *s = &chip->streams[i]; 14238c2ecf20Sopenharmony_ci if (s->substream && s->suspended) { 14248c2ecf20Sopenharmony_ci spin_lock_irq(&chip->reg_lock); 14258c2ecf20Sopenharmony_ci snd_nm256_set_format(chip, s, s->substream); 14268c2ecf20Sopenharmony_ci spin_unlock_irq(&chip->reg_lock); 14278c2ecf20Sopenharmony_ci } 14288c2ecf20Sopenharmony_ci } 14298c2ecf20Sopenharmony_ci 14308c2ecf20Sopenharmony_ci snd_power_change_state(card, SNDRV_CTL_POWER_D0); 14318c2ecf20Sopenharmony_ci chip->in_resume = 0; 14328c2ecf20Sopenharmony_ci return 0; 14338c2ecf20Sopenharmony_ci} 14348c2ecf20Sopenharmony_ci 14358c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(nm256_pm, nm256_suspend, nm256_resume); 14368c2ecf20Sopenharmony_ci#define NM256_PM_OPS &nm256_pm 14378c2ecf20Sopenharmony_ci#else 14388c2ecf20Sopenharmony_ci#define NM256_PM_OPS NULL 14398c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_cistatic int snd_nm256_free(struct nm256 *chip) 14428c2ecf20Sopenharmony_ci{ 14438c2ecf20Sopenharmony_ci if (chip->streams[SNDRV_PCM_STREAM_PLAYBACK].running) 14448c2ecf20Sopenharmony_ci snd_nm256_playback_stop(chip); 14458c2ecf20Sopenharmony_ci if (chip->streams[SNDRV_PCM_STREAM_CAPTURE].running) 14468c2ecf20Sopenharmony_ci snd_nm256_capture_stop(chip); 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci if (chip->irq >= 0) 14498c2ecf20Sopenharmony_ci free_irq(chip->irq, chip); 14508c2ecf20Sopenharmony_ci 14518c2ecf20Sopenharmony_ci iounmap(chip->cport); 14528c2ecf20Sopenharmony_ci iounmap(chip->buffer); 14538c2ecf20Sopenharmony_ci release_and_free_resource(chip->res_cport); 14548c2ecf20Sopenharmony_ci release_and_free_resource(chip->res_buffer); 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_ci pci_disable_device(chip->pci); 14578c2ecf20Sopenharmony_ci kfree(chip->ac97_regs); 14588c2ecf20Sopenharmony_ci kfree(chip); 14598c2ecf20Sopenharmony_ci return 0; 14608c2ecf20Sopenharmony_ci} 14618c2ecf20Sopenharmony_ci 14628c2ecf20Sopenharmony_cistatic int snd_nm256_dev_free(struct snd_device *device) 14638c2ecf20Sopenharmony_ci{ 14648c2ecf20Sopenharmony_ci struct nm256 *chip = device->device_data; 14658c2ecf20Sopenharmony_ci return snd_nm256_free(chip); 14668c2ecf20Sopenharmony_ci} 14678c2ecf20Sopenharmony_ci 14688c2ecf20Sopenharmony_cistatic int 14698c2ecf20Sopenharmony_cisnd_nm256_create(struct snd_card *card, struct pci_dev *pci, 14708c2ecf20Sopenharmony_ci struct nm256 **chip_ret) 14718c2ecf20Sopenharmony_ci{ 14728c2ecf20Sopenharmony_ci struct nm256 *chip; 14738c2ecf20Sopenharmony_ci int err, pval; 14748c2ecf20Sopenharmony_ci static const struct snd_device_ops ops = { 14758c2ecf20Sopenharmony_ci .dev_free = snd_nm256_dev_free, 14768c2ecf20Sopenharmony_ci }; 14778c2ecf20Sopenharmony_ci u32 addr; 14788c2ecf20Sopenharmony_ci 14798c2ecf20Sopenharmony_ci *chip_ret = NULL; 14808c2ecf20Sopenharmony_ci 14818c2ecf20Sopenharmony_ci if ((err = pci_enable_device(pci)) < 0) 14828c2ecf20Sopenharmony_ci return err; 14838c2ecf20Sopenharmony_ci 14848c2ecf20Sopenharmony_ci chip = kzalloc(sizeof(*chip), GFP_KERNEL); 14858c2ecf20Sopenharmony_ci if (chip == NULL) { 14868c2ecf20Sopenharmony_ci pci_disable_device(pci); 14878c2ecf20Sopenharmony_ci return -ENOMEM; 14888c2ecf20Sopenharmony_ci } 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_ci chip->card = card; 14918c2ecf20Sopenharmony_ci chip->pci = pci; 14928c2ecf20Sopenharmony_ci chip->use_cache = use_cache; 14938c2ecf20Sopenharmony_ci spin_lock_init(&chip->reg_lock); 14948c2ecf20Sopenharmony_ci chip->irq = -1; 14958c2ecf20Sopenharmony_ci mutex_init(&chip->irq_mutex); 14968c2ecf20Sopenharmony_ci 14978c2ecf20Sopenharmony_ci /* store buffer sizes in bytes */ 14988c2ecf20Sopenharmony_ci chip->streams[SNDRV_PCM_STREAM_PLAYBACK].bufsize = playback_bufsize * 1024; 14998c2ecf20Sopenharmony_ci chip->streams[SNDRV_PCM_STREAM_CAPTURE].bufsize = capture_bufsize * 1024; 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci /* 15028c2ecf20Sopenharmony_ci * The NM256 has two memory ports. The first port is nothing 15038c2ecf20Sopenharmony_ci * more than a chunk of video RAM, which is used as the I/O ring 15048c2ecf20Sopenharmony_ci * buffer. The second port has the actual juicy stuff (like the 15058c2ecf20Sopenharmony_ci * mixer and the playback engine control registers). 15068c2ecf20Sopenharmony_ci */ 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_ci chip->buffer_addr = pci_resource_start(pci, 0); 15098c2ecf20Sopenharmony_ci chip->cport_addr = pci_resource_start(pci, 1); 15108c2ecf20Sopenharmony_ci 15118c2ecf20Sopenharmony_ci /* Init the memory port info. */ 15128c2ecf20Sopenharmony_ci /* remap control port (#2) */ 15138c2ecf20Sopenharmony_ci chip->res_cport = request_mem_region(chip->cport_addr, NM_PORT2_SIZE, 15148c2ecf20Sopenharmony_ci card->driver); 15158c2ecf20Sopenharmony_ci if (chip->res_cport == NULL) { 15168c2ecf20Sopenharmony_ci dev_err(card->dev, "memory region 0x%lx (size 0x%x) busy\n", 15178c2ecf20Sopenharmony_ci chip->cport_addr, NM_PORT2_SIZE); 15188c2ecf20Sopenharmony_ci err = -EBUSY; 15198c2ecf20Sopenharmony_ci goto __error; 15208c2ecf20Sopenharmony_ci } 15218c2ecf20Sopenharmony_ci chip->cport = ioremap(chip->cport_addr, NM_PORT2_SIZE); 15228c2ecf20Sopenharmony_ci if (chip->cport == NULL) { 15238c2ecf20Sopenharmony_ci dev_err(card->dev, "unable to map control port %lx\n", 15248c2ecf20Sopenharmony_ci chip->cport_addr); 15258c2ecf20Sopenharmony_ci err = -ENOMEM; 15268c2ecf20Sopenharmony_ci goto __error; 15278c2ecf20Sopenharmony_ci } 15288c2ecf20Sopenharmony_ci 15298c2ecf20Sopenharmony_ci if (!strcmp(card->driver, "NM256AV")) { 15308c2ecf20Sopenharmony_ci /* Ok, try to see if this is a non-AC97 version of the hardware. */ 15318c2ecf20Sopenharmony_ci pval = snd_nm256_readw(chip, NM_MIXER_PRESENCE); 15328c2ecf20Sopenharmony_ci if ((pval & NM_PRESENCE_MASK) != NM_PRESENCE_VALUE) { 15338c2ecf20Sopenharmony_ci if (! force_ac97) { 15348c2ecf20Sopenharmony_ci dev_err(card->dev, 15358c2ecf20Sopenharmony_ci "no ac97 is found!\n"); 15368c2ecf20Sopenharmony_ci dev_err(card->dev, 15378c2ecf20Sopenharmony_ci "force the driver to load by passing in the module parameter\n"); 15388c2ecf20Sopenharmony_ci dev_err(card->dev, 15398c2ecf20Sopenharmony_ci " force_ac97=1\n"); 15408c2ecf20Sopenharmony_ci dev_err(card->dev, 15418c2ecf20Sopenharmony_ci "or try sb16, opl3sa2, or cs423x drivers instead.\n"); 15428c2ecf20Sopenharmony_ci err = -ENXIO; 15438c2ecf20Sopenharmony_ci goto __error; 15448c2ecf20Sopenharmony_ci } 15458c2ecf20Sopenharmony_ci } 15468c2ecf20Sopenharmony_ci chip->buffer_end = 2560 * 1024; 15478c2ecf20Sopenharmony_ci chip->interrupt = snd_nm256_interrupt; 15488c2ecf20Sopenharmony_ci chip->mixer_status_offset = NM_MIXER_STATUS_OFFSET; 15498c2ecf20Sopenharmony_ci chip->mixer_status_mask = NM_MIXER_READY_MASK; 15508c2ecf20Sopenharmony_ci } else { 15518c2ecf20Sopenharmony_ci /* Not sure if there is any relevant detect for the ZX or not. */ 15528c2ecf20Sopenharmony_ci if (snd_nm256_readb(chip, 0xa0b) != 0) 15538c2ecf20Sopenharmony_ci chip->buffer_end = 6144 * 1024; 15548c2ecf20Sopenharmony_ci else 15558c2ecf20Sopenharmony_ci chip->buffer_end = 4096 * 1024; 15568c2ecf20Sopenharmony_ci 15578c2ecf20Sopenharmony_ci chip->interrupt = snd_nm256_interrupt_zx; 15588c2ecf20Sopenharmony_ci chip->mixer_status_offset = NM2_MIXER_STATUS_OFFSET; 15598c2ecf20Sopenharmony_ci chip->mixer_status_mask = NM2_MIXER_READY_MASK; 15608c2ecf20Sopenharmony_ci } 15618c2ecf20Sopenharmony_ci 15628c2ecf20Sopenharmony_ci chip->buffer_size = chip->streams[SNDRV_PCM_STREAM_PLAYBACK].bufsize + 15638c2ecf20Sopenharmony_ci chip->streams[SNDRV_PCM_STREAM_CAPTURE].bufsize; 15648c2ecf20Sopenharmony_ci if (chip->use_cache) 15658c2ecf20Sopenharmony_ci chip->buffer_size += NM_TOTAL_COEFF_COUNT * 4; 15668c2ecf20Sopenharmony_ci else 15678c2ecf20Sopenharmony_ci chip->buffer_size += NM_MAX_PLAYBACK_COEF_SIZE + NM_MAX_RECORD_COEF_SIZE; 15688c2ecf20Sopenharmony_ci 15698c2ecf20Sopenharmony_ci if (buffer_top >= chip->buffer_size && buffer_top < chip->buffer_end) 15708c2ecf20Sopenharmony_ci chip->buffer_end = buffer_top; 15718c2ecf20Sopenharmony_ci else { 15728c2ecf20Sopenharmony_ci /* get buffer end pointer from signature */ 15738c2ecf20Sopenharmony_ci if ((err = snd_nm256_peek_for_sig(chip)) < 0) 15748c2ecf20Sopenharmony_ci goto __error; 15758c2ecf20Sopenharmony_ci } 15768c2ecf20Sopenharmony_ci 15778c2ecf20Sopenharmony_ci chip->buffer_start = chip->buffer_end - chip->buffer_size; 15788c2ecf20Sopenharmony_ci chip->buffer_addr += chip->buffer_start; 15798c2ecf20Sopenharmony_ci 15808c2ecf20Sopenharmony_ci dev_info(card->dev, "Mapping port 1 from 0x%x - 0x%x\n", 15818c2ecf20Sopenharmony_ci chip->buffer_start, chip->buffer_end); 15828c2ecf20Sopenharmony_ci 15838c2ecf20Sopenharmony_ci chip->res_buffer = request_mem_region(chip->buffer_addr, 15848c2ecf20Sopenharmony_ci chip->buffer_size, 15858c2ecf20Sopenharmony_ci card->driver); 15868c2ecf20Sopenharmony_ci if (chip->res_buffer == NULL) { 15878c2ecf20Sopenharmony_ci dev_err(card->dev, "buffer 0x%lx (size 0x%x) busy\n", 15888c2ecf20Sopenharmony_ci chip->buffer_addr, chip->buffer_size); 15898c2ecf20Sopenharmony_ci err = -EBUSY; 15908c2ecf20Sopenharmony_ci goto __error; 15918c2ecf20Sopenharmony_ci } 15928c2ecf20Sopenharmony_ci chip->buffer = ioremap(chip->buffer_addr, chip->buffer_size); 15938c2ecf20Sopenharmony_ci if (chip->buffer == NULL) { 15948c2ecf20Sopenharmony_ci err = -ENOMEM; 15958c2ecf20Sopenharmony_ci dev_err(card->dev, "unable to map ring buffer at %lx\n", 15968c2ecf20Sopenharmony_ci chip->buffer_addr); 15978c2ecf20Sopenharmony_ci goto __error; 15988c2ecf20Sopenharmony_ci } 15998c2ecf20Sopenharmony_ci 16008c2ecf20Sopenharmony_ci /* set offsets */ 16018c2ecf20Sopenharmony_ci addr = chip->buffer_start; 16028c2ecf20Sopenharmony_ci chip->streams[SNDRV_PCM_STREAM_PLAYBACK].buf = addr; 16038c2ecf20Sopenharmony_ci addr += chip->streams[SNDRV_PCM_STREAM_PLAYBACK].bufsize; 16048c2ecf20Sopenharmony_ci chip->streams[SNDRV_PCM_STREAM_CAPTURE].buf = addr; 16058c2ecf20Sopenharmony_ci addr += chip->streams[SNDRV_PCM_STREAM_CAPTURE].bufsize; 16068c2ecf20Sopenharmony_ci if (chip->use_cache) { 16078c2ecf20Sopenharmony_ci chip->all_coeff_buf = addr; 16088c2ecf20Sopenharmony_ci } else { 16098c2ecf20Sopenharmony_ci chip->coeff_buf[SNDRV_PCM_STREAM_PLAYBACK] = addr; 16108c2ecf20Sopenharmony_ci addr += NM_MAX_PLAYBACK_COEF_SIZE; 16118c2ecf20Sopenharmony_ci chip->coeff_buf[SNDRV_PCM_STREAM_CAPTURE] = addr; 16128c2ecf20Sopenharmony_ci } 16138c2ecf20Sopenharmony_ci 16148c2ecf20Sopenharmony_ci /* Fixed setting. */ 16158c2ecf20Sopenharmony_ci chip->mixer_base = NM_MIXER_OFFSET; 16168c2ecf20Sopenharmony_ci 16178c2ecf20Sopenharmony_ci chip->coeffs_current = 0; 16188c2ecf20Sopenharmony_ci 16198c2ecf20Sopenharmony_ci snd_nm256_init_chip(chip); 16208c2ecf20Sopenharmony_ci 16218c2ecf20Sopenharmony_ci // pci_set_master(pci); /* needed? */ 16228c2ecf20Sopenharmony_ci 16238c2ecf20Sopenharmony_ci if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) 16248c2ecf20Sopenharmony_ci goto __error; 16258c2ecf20Sopenharmony_ci 16268c2ecf20Sopenharmony_ci *chip_ret = chip; 16278c2ecf20Sopenharmony_ci return 0; 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_ci__error: 16308c2ecf20Sopenharmony_ci snd_nm256_free(chip); 16318c2ecf20Sopenharmony_ci return err; 16328c2ecf20Sopenharmony_ci} 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ci 16358c2ecf20Sopenharmony_cienum { NM_IGNORED, NM_RESET_WORKAROUND, NM_RESET_WORKAROUND_2 }; 16368c2ecf20Sopenharmony_ci 16378c2ecf20Sopenharmony_cistatic const struct snd_pci_quirk nm256_quirks[] = { 16388c2ecf20Sopenharmony_ci /* HP omnibook 4150 has cs4232 codec internally */ 16398c2ecf20Sopenharmony_ci SND_PCI_QUIRK(0x103c, 0x0007, "HP omnibook 4150", NM_IGNORED), 16408c2ecf20Sopenharmony_ci /* Reset workarounds to avoid lock-ups */ 16418c2ecf20Sopenharmony_ci SND_PCI_QUIRK(0x104d, 0x8041, "Sony PCG-F305", NM_RESET_WORKAROUND), 16428c2ecf20Sopenharmony_ci SND_PCI_QUIRK(0x1028, 0x0080, "Dell Latitude LS", NM_RESET_WORKAROUND), 16438c2ecf20Sopenharmony_ci SND_PCI_QUIRK(0x1028, 0x0091, "Dell Latitude CSx", NM_RESET_WORKAROUND_2), 16448c2ecf20Sopenharmony_ci { } /* terminator */ 16458c2ecf20Sopenharmony_ci}; 16468c2ecf20Sopenharmony_ci 16478c2ecf20Sopenharmony_ci 16488c2ecf20Sopenharmony_cistatic int snd_nm256_probe(struct pci_dev *pci, 16498c2ecf20Sopenharmony_ci const struct pci_device_id *pci_id) 16508c2ecf20Sopenharmony_ci{ 16518c2ecf20Sopenharmony_ci struct snd_card *card; 16528c2ecf20Sopenharmony_ci struct nm256 *chip; 16538c2ecf20Sopenharmony_ci int err; 16548c2ecf20Sopenharmony_ci const struct snd_pci_quirk *q; 16558c2ecf20Sopenharmony_ci 16568c2ecf20Sopenharmony_ci q = snd_pci_quirk_lookup(pci, nm256_quirks); 16578c2ecf20Sopenharmony_ci if (q) { 16588c2ecf20Sopenharmony_ci dev_dbg(&pci->dev, "Enabled quirk for %s.\n", 16598c2ecf20Sopenharmony_ci snd_pci_quirk_name(q)); 16608c2ecf20Sopenharmony_ci switch (q->value) { 16618c2ecf20Sopenharmony_ci case NM_IGNORED: 16628c2ecf20Sopenharmony_ci dev_info(&pci->dev, 16638c2ecf20Sopenharmony_ci "The device is on the denylist. Loading stopped\n"); 16648c2ecf20Sopenharmony_ci return -ENODEV; 16658c2ecf20Sopenharmony_ci case NM_RESET_WORKAROUND_2: 16668c2ecf20Sopenharmony_ci reset_workaround_2 = 1; 16678c2ecf20Sopenharmony_ci fallthrough; 16688c2ecf20Sopenharmony_ci case NM_RESET_WORKAROUND: 16698c2ecf20Sopenharmony_ci reset_workaround = 1; 16708c2ecf20Sopenharmony_ci break; 16718c2ecf20Sopenharmony_ci } 16728c2ecf20Sopenharmony_ci } 16738c2ecf20Sopenharmony_ci 16748c2ecf20Sopenharmony_ci err = snd_card_new(&pci->dev, index, id, THIS_MODULE, 0, &card); 16758c2ecf20Sopenharmony_ci if (err < 0) 16768c2ecf20Sopenharmony_ci return err; 16778c2ecf20Sopenharmony_ci 16788c2ecf20Sopenharmony_ci switch (pci->device) { 16798c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO: 16808c2ecf20Sopenharmony_ci strcpy(card->driver, "NM256AV"); 16818c2ecf20Sopenharmony_ci break; 16828c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO: 16838c2ecf20Sopenharmony_ci strcpy(card->driver, "NM256ZX"); 16848c2ecf20Sopenharmony_ci break; 16858c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_NEOMAGIC_NM256XL_PLUS_AUDIO: 16868c2ecf20Sopenharmony_ci strcpy(card->driver, "NM256XL+"); 16878c2ecf20Sopenharmony_ci break; 16888c2ecf20Sopenharmony_ci default: 16898c2ecf20Sopenharmony_ci dev_err(&pci->dev, "invalid device id 0x%x\n", pci->device); 16908c2ecf20Sopenharmony_ci snd_card_free(card); 16918c2ecf20Sopenharmony_ci return -EINVAL; 16928c2ecf20Sopenharmony_ci } 16938c2ecf20Sopenharmony_ci 16948c2ecf20Sopenharmony_ci if (vaio_hack) 16958c2ecf20Sopenharmony_ci buffer_top = 0x25a800; /* this avoids conflicts with XFree86 server */ 16968c2ecf20Sopenharmony_ci 16978c2ecf20Sopenharmony_ci if (playback_bufsize < 4) 16988c2ecf20Sopenharmony_ci playback_bufsize = 4; 16998c2ecf20Sopenharmony_ci if (playback_bufsize > 128) 17008c2ecf20Sopenharmony_ci playback_bufsize = 128; 17018c2ecf20Sopenharmony_ci if (capture_bufsize < 4) 17028c2ecf20Sopenharmony_ci capture_bufsize = 4; 17038c2ecf20Sopenharmony_ci if (capture_bufsize > 128) 17048c2ecf20Sopenharmony_ci capture_bufsize = 128; 17058c2ecf20Sopenharmony_ci if ((err = snd_nm256_create(card, pci, &chip)) < 0) { 17068c2ecf20Sopenharmony_ci snd_card_free(card); 17078c2ecf20Sopenharmony_ci return err; 17088c2ecf20Sopenharmony_ci } 17098c2ecf20Sopenharmony_ci card->private_data = chip; 17108c2ecf20Sopenharmony_ci 17118c2ecf20Sopenharmony_ci if (reset_workaround) { 17128c2ecf20Sopenharmony_ci dev_dbg(&pci->dev, "reset_workaround activated\n"); 17138c2ecf20Sopenharmony_ci chip->reset_workaround = 1; 17148c2ecf20Sopenharmony_ci } 17158c2ecf20Sopenharmony_ci 17168c2ecf20Sopenharmony_ci if (reset_workaround_2) { 17178c2ecf20Sopenharmony_ci dev_dbg(&pci->dev, "reset_workaround_2 activated\n"); 17188c2ecf20Sopenharmony_ci chip->reset_workaround_2 = 1; 17198c2ecf20Sopenharmony_ci } 17208c2ecf20Sopenharmony_ci 17218c2ecf20Sopenharmony_ci if ((err = snd_nm256_pcm(chip, 0)) < 0 || 17228c2ecf20Sopenharmony_ci (err = snd_nm256_mixer(chip)) < 0) { 17238c2ecf20Sopenharmony_ci snd_card_free(card); 17248c2ecf20Sopenharmony_ci return err; 17258c2ecf20Sopenharmony_ci } 17268c2ecf20Sopenharmony_ci 17278c2ecf20Sopenharmony_ci sprintf(card->shortname, "NeoMagic %s", card->driver); 17288c2ecf20Sopenharmony_ci sprintf(card->longname, "%s at 0x%lx & 0x%lx, irq %d", 17298c2ecf20Sopenharmony_ci card->shortname, 17308c2ecf20Sopenharmony_ci chip->buffer_addr, chip->cport_addr, chip->irq); 17318c2ecf20Sopenharmony_ci 17328c2ecf20Sopenharmony_ci if ((err = snd_card_register(card)) < 0) { 17338c2ecf20Sopenharmony_ci snd_card_free(card); 17348c2ecf20Sopenharmony_ci return err; 17358c2ecf20Sopenharmony_ci } 17368c2ecf20Sopenharmony_ci 17378c2ecf20Sopenharmony_ci pci_set_drvdata(pci, card); 17388c2ecf20Sopenharmony_ci return 0; 17398c2ecf20Sopenharmony_ci} 17408c2ecf20Sopenharmony_ci 17418c2ecf20Sopenharmony_cistatic void snd_nm256_remove(struct pci_dev *pci) 17428c2ecf20Sopenharmony_ci{ 17438c2ecf20Sopenharmony_ci snd_card_free(pci_get_drvdata(pci)); 17448c2ecf20Sopenharmony_ci} 17458c2ecf20Sopenharmony_ci 17468c2ecf20Sopenharmony_ci 17478c2ecf20Sopenharmony_cistatic struct pci_driver nm256_driver = { 17488c2ecf20Sopenharmony_ci .name = KBUILD_MODNAME, 17498c2ecf20Sopenharmony_ci .id_table = snd_nm256_ids, 17508c2ecf20Sopenharmony_ci .probe = snd_nm256_probe, 17518c2ecf20Sopenharmony_ci .remove = snd_nm256_remove, 17528c2ecf20Sopenharmony_ci .driver = { 17538c2ecf20Sopenharmony_ci .pm = NM256_PM_OPS, 17548c2ecf20Sopenharmony_ci }, 17558c2ecf20Sopenharmony_ci}; 17568c2ecf20Sopenharmony_ci 17578c2ecf20Sopenharmony_cimodule_pci_driver(nm256_driver); 1758