18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * The driver for the ForteMedia FM801 based soundcards 48c2ecf20Sopenharmony_ci * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/delay.h> 88c2ecf20Sopenharmony_ci#include <linux/init.h> 98c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 108c2ecf20Sopenharmony_ci#include <linux/io.h> 118c2ecf20Sopenharmony_ci#include <linux/pci.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <sound/core.h> 158c2ecf20Sopenharmony_ci#include <sound/pcm.h> 168c2ecf20Sopenharmony_ci#include <sound/tlv.h> 178c2ecf20Sopenharmony_ci#include <sound/ac97_codec.h> 188c2ecf20Sopenharmony_ci#include <sound/mpu401.h> 198c2ecf20Sopenharmony_ci#include <sound/opl3.h> 208c2ecf20Sopenharmony_ci#include <sound/initval.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_FM801_TEA575X_BOOL 238c2ecf20Sopenharmony_ci#include <media/drv-intf/tea575x.h> 248c2ecf20Sopenharmony_ci#endif 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); 278c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ForteMedia FM801"); 288c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 298c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("{{ForteMedia,FM801}," 308c2ecf20Sopenharmony_ci "{Genius,SoundMaker Live 5.1}}"); 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ 338c2ecf20Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ 348c2ecf20Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ 358c2ecf20Sopenharmony_ci/* 368c2ecf20Sopenharmony_ci * Enable TEA575x tuner 378c2ecf20Sopenharmony_ci * 1 = MediaForte 256-PCS 388c2ecf20Sopenharmony_ci * 2 = MediaForte 256-PCP 398c2ecf20Sopenharmony_ci * 3 = MediaForte 64-PCR 408c2ecf20Sopenharmony_ci * 16 = setup tuner only (this is additional bit), i.e. SF64-PCR FM card 418c2ecf20Sopenharmony_ci * High 16-bits are video (radio) device number + 1 428c2ecf20Sopenharmony_ci */ 438c2ecf20Sopenharmony_cistatic int tea575x_tuner[SNDRV_CARDS]; 448c2ecf20Sopenharmony_cistatic int radio_nr[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1}; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cimodule_param_array(index, int, NULL, 0444); 478c2ecf20Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for the FM801 soundcard."); 488c2ecf20Sopenharmony_cimodule_param_array(id, charp, NULL, 0444); 498c2ecf20Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for the FM801 soundcard."); 508c2ecf20Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444); 518c2ecf20Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable FM801 soundcard."); 528c2ecf20Sopenharmony_cimodule_param_array(tea575x_tuner, int, NULL, 0444); 538c2ecf20Sopenharmony_ciMODULE_PARM_DESC(tea575x_tuner, "TEA575x tuner access method (0 = auto, 1 = SF256-PCS, 2=SF256-PCP, 3=SF64-PCR, 8=disable, +16=tuner-only)."); 548c2ecf20Sopenharmony_cimodule_param_array(radio_nr, int, NULL, 0444); 558c2ecf20Sopenharmony_ciMODULE_PARM_DESC(radio_nr, "Radio device numbers"); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci#define TUNER_DISABLED (1<<3) 598c2ecf20Sopenharmony_ci#define TUNER_ONLY (1<<4) 608c2ecf20Sopenharmony_ci#define TUNER_TYPE_MASK (~TUNER_ONLY & 0xFFFF) 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci/* 638c2ecf20Sopenharmony_ci * Direct registers 648c2ecf20Sopenharmony_ci */ 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci#define fm801_writew(chip,reg,value) outw((value), chip->port + FM801_##reg) 678c2ecf20Sopenharmony_ci#define fm801_readw(chip,reg) inw(chip->port + FM801_##reg) 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci#define fm801_writel(chip,reg,value) outl((value), chip->port + FM801_##reg) 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci#define FM801_PCM_VOL 0x00 /* PCM Output Volume */ 728c2ecf20Sopenharmony_ci#define FM801_FM_VOL 0x02 /* FM Output Volume */ 738c2ecf20Sopenharmony_ci#define FM801_I2S_VOL 0x04 /* I2S Volume */ 748c2ecf20Sopenharmony_ci#define FM801_REC_SRC 0x06 /* Record Source */ 758c2ecf20Sopenharmony_ci#define FM801_PLY_CTRL 0x08 /* Playback Control */ 768c2ecf20Sopenharmony_ci#define FM801_PLY_COUNT 0x0a /* Playback Count */ 778c2ecf20Sopenharmony_ci#define FM801_PLY_BUF1 0x0c /* Playback Bufer I */ 788c2ecf20Sopenharmony_ci#define FM801_PLY_BUF2 0x10 /* Playback Buffer II */ 798c2ecf20Sopenharmony_ci#define FM801_CAP_CTRL 0x14 /* Capture Control */ 808c2ecf20Sopenharmony_ci#define FM801_CAP_COUNT 0x16 /* Capture Count */ 818c2ecf20Sopenharmony_ci#define FM801_CAP_BUF1 0x18 /* Capture Buffer I */ 828c2ecf20Sopenharmony_ci#define FM801_CAP_BUF2 0x1c /* Capture Buffer II */ 838c2ecf20Sopenharmony_ci#define FM801_CODEC_CTRL 0x22 /* Codec Control */ 848c2ecf20Sopenharmony_ci#define FM801_I2S_MODE 0x24 /* I2S Mode Control */ 858c2ecf20Sopenharmony_ci#define FM801_VOLUME 0x26 /* Volume Up/Down/Mute Status */ 868c2ecf20Sopenharmony_ci#define FM801_I2C_CTRL 0x29 /* I2C Control */ 878c2ecf20Sopenharmony_ci#define FM801_AC97_CMD 0x2a /* AC'97 Command */ 888c2ecf20Sopenharmony_ci#define FM801_AC97_DATA 0x2c /* AC'97 Data */ 898c2ecf20Sopenharmony_ci#define FM801_MPU401_DATA 0x30 /* MPU401 Data */ 908c2ecf20Sopenharmony_ci#define FM801_MPU401_CMD 0x31 /* MPU401 Command */ 918c2ecf20Sopenharmony_ci#define FM801_GPIO_CTRL 0x52 /* General Purpose I/O Control */ 928c2ecf20Sopenharmony_ci#define FM801_GEN_CTRL 0x54 /* General Control */ 938c2ecf20Sopenharmony_ci#define FM801_IRQ_MASK 0x56 /* Interrupt Mask */ 948c2ecf20Sopenharmony_ci#define FM801_IRQ_STATUS 0x5a /* Interrupt Status */ 958c2ecf20Sopenharmony_ci#define FM801_OPL3_BANK0 0x68 /* OPL3 Status Read / Bank 0 Write */ 968c2ecf20Sopenharmony_ci#define FM801_OPL3_DATA0 0x69 /* OPL3 Data 0 Write */ 978c2ecf20Sopenharmony_ci#define FM801_OPL3_BANK1 0x6a /* OPL3 Bank 1 Write */ 988c2ecf20Sopenharmony_ci#define FM801_OPL3_DATA1 0x6b /* OPL3 Bank 1 Write */ 998c2ecf20Sopenharmony_ci#define FM801_POWERDOWN 0x70 /* Blocks Power Down Control */ 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci/* codec access */ 1028c2ecf20Sopenharmony_ci#define FM801_AC97_READ (1<<7) /* read=1, write=0 */ 1038c2ecf20Sopenharmony_ci#define FM801_AC97_VALID (1<<8) /* port valid=1 */ 1048c2ecf20Sopenharmony_ci#define FM801_AC97_BUSY (1<<9) /* busy=1 */ 1058c2ecf20Sopenharmony_ci#define FM801_AC97_ADDR_SHIFT 10 /* codec id (2bit) */ 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci/* playback and record control register bits */ 1088c2ecf20Sopenharmony_ci#define FM801_BUF1_LAST (1<<1) 1098c2ecf20Sopenharmony_ci#define FM801_BUF2_LAST (1<<2) 1108c2ecf20Sopenharmony_ci#define FM801_START (1<<5) 1118c2ecf20Sopenharmony_ci#define FM801_PAUSE (1<<6) 1128c2ecf20Sopenharmony_ci#define FM801_IMMED_STOP (1<<7) 1138c2ecf20Sopenharmony_ci#define FM801_RATE_SHIFT 8 1148c2ecf20Sopenharmony_ci#define FM801_RATE_MASK (15 << FM801_RATE_SHIFT) 1158c2ecf20Sopenharmony_ci#define FM801_CHANNELS_4 (1<<12) /* playback only */ 1168c2ecf20Sopenharmony_ci#define FM801_CHANNELS_6 (2<<12) /* playback only */ 1178c2ecf20Sopenharmony_ci#define FM801_CHANNELS_6MS (3<<12) /* playback only */ 1188c2ecf20Sopenharmony_ci#define FM801_CHANNELS_MASK (3<<12) 1198c2ecf20Sopenharmony_ci#define FM801_16BIT (1<<14) 1208c2ecf20Sopenharmony_ci#define FM801_STEREO (1<<15) 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci/* IRQ status bits */ 1238c2ecf20Sopenharmony_ci#define FM801_IRQ_PLAYBACK (1<<8) 1248c2ecf20Sopenharmony_ci#define FM801_IRQ_CAPTURE (1<<9) 1258c2ecf20Sopenharmony_ci#define FM801_IRQ_VOLUME (1<<14) 1268c2ecf20Sopenharmony_ci#define FM801_IRQ_MPU (1<<15) 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci/* GPIO control register */ 1298c2ecf20Sopenharmony_ci#define FM801_GPIO_GP0 (1<<0) /* read/write */ 1308c2ecf20Sopenharmony_ci#define FM801_GPIO_GP1 (1<<1) 1318c2ecf20Sopenharmony_ci#define FM801_GPIO_GP2 (1<<2) 1328c2ecf20Sopenharmony_ci#define FM801_GPIO_GP3 (1<<3) 1338c2ecf20Sopenharmony_ci#define FM801_GPIO_GP(x) (1<<(0+(x))) 1348c2ecf20Sopenharmony_ci#define FM801_GPIO_GD0 (1<<8) /* directions: 1 = input, 0 = output*/ 1358c2ecf20Sopenharmony_ci#define FM801_GPIO_GD1 (1<<9) 1368c2ecf20Sopenharmony_ci#define FM801_GPIO_GD2 (1<<10) 1378c2ecf20Sopenharmony_ci#define FM801_GPIO_GD3 (1<<11) 1388c2ecf20Sopenharmony_ci#define FM801_GPIO_GD(x) (1<<(8+(x))) 1398c2ecf20Sopenharmony_ci#define FM801_GPIO_GS0 (1<<12) /* function select: */ 1408c2ecf20Sopenharmony_ci#define FM801_GPIO_GS1 (1<<13) /* 1 = GPIO */ 1418c2ecf20Sopenharmony_ci#define FM801_GPIO_GS2 (1<<14) /* 0 = other (S/PDIF, VOL) */ 1428c2ecf20Sopenharmony_ci#define FM801_GPIO_GS3 (1<<15) 1438c2ecf20Sopenharmony_ci#define FM801_GPIO_GS(x) (1<<(12+(x))) 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci/** 1468c2ecf20Sopenharmony_ci * struct fm801 - describes FM801 chip 1478c2ecf20Sopenharmony_ci * @dev: device for this chio 1488c2ecf20Sopenharmony_ci * @irq: irq number 1498c2ecf20Sopenharmony_ci * @port: I/O port number 1508c2ecf20Sopenharmony_ci * @multichannel: multichannel support 1518c2ecf20Sopenharmony_ci * @secondary: secondary codec 1528c2ecf20Sopenharmony_ci * @secondary_addr: address of the secondary codec 1538c2ecf20Sopenharmony_ci * @tea575x_tuner: tuner access method & flags 1548c2ecf20Sopenharmony_ci * @ply_ctrl: playback control 1558c2ecf20Sopenharmony_ci * @cap_ctrl: capture control 1568c2ecf20Sopenharmony_ci * @ply_buffer: playback buffer 1578c2ecf20Sopenharmony_ci * @ply_buf: playback buffer index 1588c2ecf20Sopenharmony_ci * @ply_count: playback buffer count 1598c2ecf20Sopenharmony_ci * @ply_size: playback buffer size 1608c2ecf20Sopenharmony_ci * @ply_pos: playback position 1618c2ecf20Sopenharmony_ci * @cap_buffer: capture buffer 1628c2ecf20Sopenharmony_ci * @cap_buf: capture buffer index 1638c2ecf20Sopenharmony_ci * @cap_count: capture buffer count 1648c2ecf20Sopenharmony_ci * @cap_size: capture buffer size 1658c2ecf20Sopenharmony_ci * @cap_pos: capture position 1668c2ecf20Sopenharmony_ci * @ac97_bus: ac97 bus handle 1678c2ecf20Sopenharmony_ci * @ac97: ac97 handle 1688c2ecf20Sopenharmony_ci * @ac97_sec: ac97 secondary handle 1698c2ecf20Sopenharmony_ci * @card: ALSA card 1708c2ecf20Sopenharmony_ci * @pcm: PCM devices 1718c2ecf20Sopenharmony_ci * @rmidi: rmidi device 1728c2ecf20Sopenharmony_ci * @playback_substream: substream for playback 1738c2ecf20Sopenharmony_ci * @capture_substream: substream for capture 1748c2ecf20Sopenharmony_ci * @p_dma_size: playback DMA size 1758c2ecf20Sopenharmony_ci * @c_dma_size: capture DMA size 1768c2ecf20Sopenharmony_ci * @reg_lock: lock 1778c2ecf20Sopenharmony_ci * @proc_entry: /proc entry 1788c2ecf20Sopenharmony_ci * @v4l2_dev: v4l2 device 1798c2ecf20Sopenharmony_ci * @tea: tea575a structure 1808c2ecf20Sopenharmony_ci * @saved_regs: context saved during suspend 1818c2ecf20Sopenharmony_ci */ 1828c2ecf20Sopenharmony_cistruct fm801 { 1838c2ecf20Sopenharmony_ci struct device *dev; 1848c2ecf20Sopenharmony_ci int irq; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci unsigned long port; 1878c2ecf20Sopenharmony_ci unsigned int multichannel: 1, 1888c2ecf20Sopenharmony_ci secondary: 1; 1898c2ecf20Sopenharmony_ci unsigned char secondary_addr; 1908c2ecf20Sopenharmony_ci unsigned int tea575x_tuner; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci unsigned short ply_ctrl; 1938c2ecf20Sopenharmony_ci unsigned short cap_ctrl; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci unsigned long ply_buffer; 1968c2ecf20Sopenharmony_ci unsigned int ply_buf; 1978c2ecf20Sopenharmony_ci unsigned int ply_count; 1988c2ecf20Sopenharmony_ci unsigned int ply_size; 1998c2ecf20Sopenharmony_ci unsigned int ply_pos; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci unsigned long cap_buffer; 2028c2ecf20Sopenharmony_ci unsigned int cap_buf; 2038c2ecf20Sopenharmony_ci unsigned int cap_count; 2048c2ecf20Sopenharmony_ci unsigned int cap_size; 2058c2ecf20Sopenharmony_ci unsigned int cap_pos; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci struct snd_ac97_bus *ac97_bus; 2088c2ecf20Sopenharmony_ci struct snd_ac97 *ac97; 2098c2ecf20Sopenharmony_ci struct snd_ac97 *ac97_sec; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci struct snd_card *card; 2128c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 2138c2ecf20Sopenharmony_ci struct snd_rawmidi *rmidi; 2148c2ecf20Sopenharmony_ci struct snd_pcm_substream *playback_substream; 2158c2ecf20Sopenharmony_ci struct snd_pcm_substream *capture_substream; 2168c2ecf20Sopenharmony_ci unsigned int p_dma_size; 2178c2ecf20Sopenharmony_ci unsigned int c_dma_size; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci spinlock_t reg_lock; 2208c2ecf20Sopenharmony_ci struct snd_info_entry *proc_entry; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_FM801_TEA575X_BOOL 2238c2ecf20Sopenharmony_ci struct v4l2_device v4l2_dev; 2248c2ecf20Sopenharmony_ci struct snd_tea575x tea; 2258c2ecf20Sopenharmony_ci#endif 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 2288c2ecf20Sopenharmony_ci u16 saved_regs[0x20]; 2298c2ecf20Sopenharmony_ci#endif 2308c2ecf20Sopenharmony_ci}; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci/* 2338c2ecf20Sopenharmony_ci * IO accessors 2348c2ecf20Sopenharmony_ci */ 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic inline void fm801_iowrite16(struct fm801 *chip, unsigned short offset, u16 value) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci outw(value, chip->port + offset); 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic inline u16 fm801_ioread16(struct fm801 *chip, unsigned short offset) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci return inw(chip->port + offset); 2448c2ecf20Sopenharmony_ci} 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cistatic const struct pci_device_id snd_fm801_ids[] = { 2478c2ecf20Sopenharmony_ci { 0x1319, 0x0801, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0, }, /* FM801 */ 2488c2ecf20Sopenharmony_ci { 0x5213, 0x0510, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0, }, /* Gallant Odyssey Sound 4 */ 2498c2ecf20Sopenharmony_ci { 0, } 2508c2ecf20Sopenharmony_ci}; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, snd_fm801_ids); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci/* 2558c2ecf20Sopenharmony_ci * common I/O routines 2568c2ecf20Sopenharmony_ci */ 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_cistatic bool fm801_ac97_is_ready(struct fm801 *chip, unsigned int iterations) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci unsigned int idx; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci for (idx = 0; idx < iterations; idx++) { 2638c2ecf20Sopenharmony_ci if (!(fm801_readw(chip, AC97_CMD) & FM801_AC97_BUSY)) 2648c2ecf20Sopenharmony_ci return true; 2658c2ecf20Sopenharmony_ci udelay(10); 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci return false; 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistatic bool fm801_ac97_is_valid(struct fm801 *chip, unsigned int iterations) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci unsigned int idx; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci for (idx = 0; idx < iterations; idx++) { 2758c2ecf20Sopenharmony_ci if (fm801_readw(chip, AC97_CMD) & FM801_AC97_VALID) 2768c2ecf20Sopenharmony_ci return true; 2778c2ecf20Sopenharmony_ci udelay(10); 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci return false; 2808c2ecf20Sopenharmony_ci} 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cistatic int snd_fm801_update_bits(struct fm801 *chip, unsigned short reg, 2838c2ecf20Sopenharmony_ci unsigned short mask, unsigned short value) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci int change; 2868c2ecf20Sopenharmony_ci unsigned long flags; 2878c2ecf20Sopenharmony_ci unsigned short old, new; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 2908c2ecf20Sopenharmony_ci old = fm801_ioread16(chip, reg); 2918c2ecf20Sopenharmony_ci new = (old & ~mask) | value; 2928c2ecf20Sopenharmony_ci change = old != new; 2938c2ecf20Sopenharmony_ci if (change) 2948c2ecf20Sopenharmony_ci fm801_iowrite16(chip, reg, new); 2958c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 2968c2ecf20Sopenharmony_ci return change; 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_cistatic void snd_fm801_codec_write(struct snd_ac97 *ac97, 3008c2ecf20Sopenharmony_ci unsigned short reg, 3018c2ecf20Sopenharmony_ci unsigned short val) 3028c2ecf20Sopenharmony_ci{ 3038c2ecf20Sopenharmony_ci struct fm801 *chip = ac97->private_data; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci /* 3068c2ecf20Sopenharmony_ci * Wait until the codec interface is not ready.. 3078c2ecf20Sopenharmony_ci */ 3088c2ecf20Sopenharmony_ci if (!fm801_ac97_is_ready(chip, 100)) { 3098c2ecf20Sopenharmony_ci dev_err(chip->card->dev, "AC'97 interface is busy (1)\n"); 3108c2ecf20Sopenharmony_ci return; 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci /* write data and address */ 3148c2ecf20Sopenharmony_ci fm801_writew(chip, AC97_DATA, val); 3158c2ecf20Sopenharmony_ci fm801_writew(chip, AC97_CMD, reg | (ac97->addr << FM801_AC97_ADDR_SHIFT)); 3168c2ecf20Sopenharmony_ci /* 3178c2ecf20Sopenharmony_ci * Wait until the write command is not completed.. 3188c2ecf20Sopenharmony_ci */ 3198c2ecf20Sopenharmony_ci if (!fm801_ac97_is_ready(chip, 1000)) 3208c2ecf20Sopenharmony_ci dev_err(chip->card->dev, "AC'97 interface #%d is busy (2)\n", 3218c2ecf20Sopenharmony_ci ac97->num); 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cistatic unsigned short snd_fm801_codec_read(struct snd_ac97 *ac97, unsigned short reg) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci struct fm801 *chip = ac97->private_data; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci /* 3298c2ecf20Sopenharmony_ci * Wait until the codec interface is not ready.. 3308c2ecf20Sopenharmony_ci */ 3318c2ecf20Sopenharmony_ci if (!fm801_ac97_is_ready(chip, 100)) { 3328c2ecf20Sopenharmony_ci dev_err(chip->card->dev, "AC'97 interface is busy (1)\n"); 3338c2ecf20Sopenharmony_ci return 0; 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci /* read command */ 3378c2ecf20Sopenharmony_ci fm801_writew(chip, AC97_CMD, 3388c2ecf20Sopenharmony_ci reg | (ac97->addr << FM801_AC97_ADDR_SHIFT) | FM801_AC97_READ); 3398c2ecf20Sopenharmony_ci if (!fm801_ac97_is_ready(chip, 100)) { 3408c2ecf20Sopenharmony_ci dev_err(chip->card->dev, "AC'97 interface #%d is busy (2)\n", 3418c2ecf20Sopenharmony_ci ac97->num); 3428c2ecf20Sopenharmony_ci return 0; 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci if (!fm801_ac97_is_valid(chip, 1000)) { 3468c2ecf20Sopenharmony_ci dev_err(chip->card->dev, 3478c2ecf20Sopenharmony_ci "AC'97 interface #%d is not valid (2)\n", ac97->num); 3488c2ecf20Sopenharmony_ci return 0; 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci return fm801_readw(chip, AC97_DATA); 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic const unsigned int rates[] = { 3558c2ecf20Sopenharmony_ci 5500, 8000, 9600, 11025, 3568c2ecf20Sopenharmony_ci 16000, 19200, 22050, 32000, 3578c2ecf20Sopenharmony_ci 38400, 44100, 48000 3588c2ecf20Sopenharmony_ci}; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_cistatic const struct snd_pcm_hw_constraint_list hw_constraints_rates = { 3618c2ecf20Sopenharmony_ci .count = ARRAY_SIZE(rates), 3628c2ecf20Sopenharmony_ci .list = rates, 3638c2ecf20Sopenharmony_ci .mask = 0, 3648c2ecf20Sopenharmony_ci}; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_cistatic const unsigned int channels[] = { 3678c2ecf20Sopenharmony_ci 2, 4, 6 3688c2ecf20Sopenharmony_ci}; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_cistatic const struct snd_pcm_hw_constraint_list hw_constraints_channels = { 3718c2ecf20Sopenharmony_ci .count = ARRAY_SIZE(channels), 3728c2ecf20Sopenharmony_ci .list = channels, 3738c2ecf20Sopenharmony_ci .mask = 0, 3748c2ecf20Sopenharmony_ci}; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci/* 3778c2ecf20Sopenharmony_ci * Sample rate routines 3788c2ecf20Sopenharmony_ci */ 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_cistatic unsigned short snd_fm801_rate_bits(unsigned int rate) 3818c2ecf20Sopenharmony_ci{ 3828c2ecf20Sopenharmony_ci unsigned int idx; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(rates); idx++) 3858c2ecf20Sopenharmony_ci if (rates[idx] == rate) 3868c2ecf20Sopenharmony_ci return idx; 3878c2ecf20Sopenharmony_ci snd_BUG(); 3888c2ecf20Sopenharmony_ci return ARRAY_SIZE(rates) - 1; 3898c2ecf20Sopenharmony_ci} 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci/* 3928c2ecf20Sopenharmony_ci * PCM part 3938c2ecf20Sopenharmony_ci */ 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_cistatic int snd_fm801_playback_trigger(struct snd_pcm_substream *substream, 3968c2ecf20Sopenharmony_ci int cmd) 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci struct fm801 *chip = snd_pcm_substream_chip(substream); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci spin_lock(&chip->reg_lock); 4018c2ecf20Sopenharmony_ci switch (cmd) { 4028c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 4038c2ecf20Sopenharmony_ci chip->ply_ctrl &= ~(FM801_BUF1_LAST | 4048c2ecf20Sopenharmony_ci FM801_BUF2_LAST | 4058c2ecf20Sopenharmony_ci FM801_PAUSE); 4068c2ecf20Sopenharmony_ci chip->ply_ctrl |= FM801_START | 4078c2ecf20Sopenharmony_ci FM801_IMMED_STOP; 4088c2ecf20Sopenharmony_ci break; 4098c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 4108c2ecf20Sopenharmony_ci chip->ply_ctrl &= ~(FM801_START | FM801_PAUSE); 4118c2ecf20Sopenharmony_ci break; 4128c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 4138c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 4148c2ecf20Sopenharmony_ci chip->ply_ctrl |= FM801_PAUSE; 4158c2ecf20Sopenharmony_ci break; 4168c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 4178c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 4188c2ecf20Sopenharmony_ci chip->ply_ctrl &= ~FM801_PAUSE; 4198c2ecf20Sopenharmony_ci break; 4208c2ecf20Sopenharmony_ci default: 4218c2ecf20Sopenharmony_ci spin_unlock(&chip->reg_lock); 4228c2ecf20Sopenharmony_ci snd_BUG(); 4238c2ecf20Sopenharmony_ci return -EINVAL; 4248c2ecf20Sopenharmony_ci } 4258c2ecf20Sopenharmony_ci fm801_writew(chip, PLY_CTRL, chip->ply_ctrl); 4268c2ecf20Sopenharmony_ci spin_unlock(&chip->reg_lock); 4278c2ecf20Sopenharmony_ci return 0; 4288c2ecf20Sopenharmony_ci} 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_cistatic int snd_fm801_capture_trigger(struct snd_pcm_substream *substream, 4318c2ecf20Sopenharmony_ci int cmd) 4328c2ecf20Sopenharmony_ci{ 4338c2ecf20Sopenharmony_ci struct fm801 *chip = snd_pcm_substream_chip(substream); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci spin_lock(&chip->reg_lock); 4368c2ecf20Sopenharmony_ci switch (cmd) { 4378c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 4388c2ecf20Sopenharmony_ci chip->cap_ctrl &= ~(FM801_BUF1_LAST | 4398c2ecf20Sopenharmony_ci FM801_BUF2_LAST | 4408c2ecf20Sopenharmony_ci FM801_PAUSE); 4418c2ecf20Sopenharmony_ci chip->cap_ctrl |= FM801_START | 4428c2ecf20Sopenharmony_ci FM801_IMMED_STOP; 4438c2ecf20Sopenharmony_ci break; 4448c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 4458c2ecf20Sopenharmony_ci chip->cap_ctrl &= ~(FM801_START | FM801_PAUSE); 4468c2ecf20Sopenharmony_ci break; 4478c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 4488c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 4498c2ecf20Sopenharmony_ci chip->cap_ctrl |= FM801_PAUSE; 4508c2ecf20Sopenharmony_ci break; 4518c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 4528c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 4538c2ecf20Sopenharmony_ci chip->cap_ctrl &= ~FM801_PAUSE; 4548c2ecf20Sopenharmony_ci break; 4558c2ecf20Sopenharmony_ci default: 4568c2ecf20Sopenharmony_ci spin_unlock(&chip->reg_lock); 4578c2ecf20Sopenharmony_ci snd_BUG(); 4588c2ecf20Sopenharmony_ci return -EINVAL; 4598c2ecf20Sopenharmony_ci } 4608c2ecf20Sopenharmony_ci fm801_writew(chip, CAP_CTRL, chip->cap_ctrl); 4618c2ecf20Sopenharmony_ci spin_unlock(&chip->reg_lock); 4628c2ecf20Sopenharmony_ci return 0; 4638c2ecf20Sopenharmony_ci} 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_cistatic int snd_fm801_playback_prepare(struct snd_pcm_substream *substream) 4668c2ecf20Sopenharmony_ci{ 4678c2ecf20Sopenharmony_ci struct fm801 *chip = snd_pcm_substream_chip(substream); 4688c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci chip->ply_size = snd_pcm_lib_buffer_bytes(substream); 4718c2ecf20Sopenharmony_ci chip->ply_count = snd_pcm_lib_period_bytes(substream); 4728c2ecf20Sopenharmony_ci spin_lock_irq(&chip->reg_lock); 4738c2ecf20Sopenharmony_ci chip->ply_ctrl &= ~(FM801_START | FM801_16BIT | 4748c2ecf20Sopenharmony_ci FM801_STEREO | FM801_RATE_MASK | 4758c2ecf20Sopenharmony_ci FM801_CHANNELS_MASK); 4768c2ecf20Sopenharmony_ci if (snd_pcm_format_width(runtime->format) == 16) 4778c2ecf20Sopenharmony_ci chip->ply_ctrl |= FM801_16BIT; 4788c2ecf20Sopenharmony_ci if (runtime->channels > 1) { 4798c2ecf20Sopenharmony_ci chip->ply_ctrl |= FM801_STEREO; 4808c2ecf20Sopenharmony_ci if (runtime->channels == 4) 4818c2ecf20Sopenharmony_ci chip->ply_ctrl |= FM801_CHANNELS_4; 4828c2ecf20Sopenharmony_ci else if (runtime->channels == 6) 4838c2ecf20Sopenharmony_ci chip->ply_ctrl |= FM801_CHANNELS_6; 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci chip->ply_ctrl |= snd_fm801_rate_bits(runtime->rate) << FM801_RATE_SHIFT; 4868c2ecf20Sopenharmony_ci chip->ply_buf = 0; 4878c2ecf20Sopenharmony_ci fm801_writew(chip, PLY_CTRL, chip->ply_ctrl); 4888c2ecf20Sopenharmony_ci fm801_writew(chip, PLY_COUNT, chip->ply_count - 1); 4898c2ecf20Sopenharmony_ci chip->ply_buffer = runtime->dma_addr; 4908c2ecf20Sopenharmony_ci chip->ply_pos = 0; 4918c2ecf20Sopenharmony_ci fm801_writel(chip, PLY_BUF1, chip->ply_buffer); 4928c2ecf20Sopenharmony_ci fm801_writel(chip, PLY_BUF2, 4938c2ecf20Sopenharmony_ci chip->ply_buffer + (chip->ply_count % chip->ply_size)); 4948c2ecf20Sopenharmony_ci spin_unlock_irq(&chip->reg_lock); 4958c2ecf20Sopenharmony_ci return 0; 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_cistatic int snd_fm801_capture_prepare(struct snd_pcm_substream *substream) 4998c2ecf20Sopenharmony_ci{ 5008c2ecf20Sopenharmony_ci struct fm801 *chip = snd_pcm_substream_chip(substream); 5018c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci chip->cap_size = snd_pcm_lib_buffer_bytes(substream); 5048c2ecf20Sopenharmony_ci chip->cap_count = snd_pcm_lib_period_bytes(substream); 5058c2ecf20Sopenharmony_ci spin_lock_irq(&chip->reg_lock); 5068c2ecf20Sopenharmony_ci chip->cap_ctrl &= ~(FM801_START | FM801_16BIT | 5078c2ecf20Sopenharmony_ci FM801_STEREO | FM801_RATE_MASK); 5088c2ecf20Sopenharmony_ci if (snd_pcm_format_width(runtime->format) == 16) 5098c2ecf20Sopenharmony_ci chip->cap_ctrl |= FM801_16BIT; 5108c2ecf20Sopenharmony_ci if (runtime->channels > 1) 5118c2ecf20Sopenharmony_ci chip->cap_ctrl |= FM801_STEREO; 5128c2ecf20Sopenharmony_ci chip->cap_ctrl |= snd_fm801_rate_bits(runtime->rate) << FM801_RATE_SHIFT; 5138c2ecf20Sopenharmony_ci chip->cap_buf = 0; 5148c2ecf20Sopenharmony_ci fm801_writew(chip, CAP_CTRL, chip->cap_ctrl); 5158c2ecf20Sopenharmony_ci fm801_writew(chip, CAP_COUNT, chip->cap_count - 1); 5168c2ecf20Sopenharmony_ci chip->cap_buffer = runtime->dma_addr; 5178c2ecf20Sopenharmony_ci chip->cap_pos = 0; 5188c2ecf20Sopenharmony_ci fm801_writel(chip, CAP_BUF1, chip->cap_buffer); 5198c2ecf20Sopenharmony_ci fm801_writel(chip, CAP_BUF2, 5208c2ecf20Sopenharmony_ci chip->cap_buffer + (chip->cap_count % chip->cap_size)); 5218c2ecf20Sopenharmony_ci spin_unlock_irq(&chip->reg_lock); 5228c2ecf20Sopenharmony_ci return 0; 5238c2ecf20Sopenharmony_ci} 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t snd_fm801_playback_pointer(struct snd_pcm_substream *substream) 5268c2ecf20Sopenharmony_ci{ 5278c2ecf20Sopenharmony_ci struct fm801 *chip = snd_pcm_substream_chip(substream); 5288c2ecf20Sopenharmony_ci size_t ptr; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci if (!(chip->ply_ctrl & FM801_START)) 5318c2ecf20Sopenharmony_ci return 0; 5328c2ecf20Sopenharmony_ci spin_lock(&chip->reg_lock); 5338c2ecf20Sopenharmony_ci ptr = chip->ply_pos + (chip->ply_count - 1) - fm801_readw(chip, PLY_COUNT); 5348c2ecf20Sopenharmony_ci if (fm801_readw(chip, IRQ_STATUS) & FM801_IRQ_PLAYBACK) { 5358c2ecf20Sopenharmony_ci ptr += chip->ply_count; 5368c2ecf20Sopenharmony_ci ptr %= chip->ply_size; 5378c2ecf20Sopenharmony_ci } 5388c2ecf20Sopenharmony_ci spin_unlock(&chip->reg_lock); 5398c2ecf20Sopenharmony_ci return bytes_to_frames(substream->runtime, ptr); 5408c2ecf20Sopenharmony_ci} 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t snd_fm801_capture_pointer(struct snd_pcm_substream *substream) 5438c2ecf20Sopenharmony_ci{ 5448c2ecf20Sopenharmony_ci struct fm801 *chip = snd_pcm_substream_chip(substream); 5458c2ecf20Sopenharmony_ci size_t ptr; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci if (!(chip->cap_ctrl & FM801_START)) 5488c2ecf20Sopenharmony_ci return 0; 5498c2ecf20Sopenharmony_ci spin_lock(&chip->reg_lock); 5508c2ecf20Sopenharmony_ci ptr = chip->cap_pos + (chip->cap_count - 1) - fm801_readw(chip, CAP_COUNT); 5518c2ecf20Sopenharmony_ci if (fm801_readw(chip, IRQ_STATUS) & FM801_IRQ_CAPTURE) { 5528c2ecf20Sopenharmony_ci ptr += chip->cap_count; 5538c2ecf20Sopenharmony_ci ptr %= chip->cap_size; 5548c2ecf20Sopenharmony_ci } 5558c2ecf20Sopenharmony_ci spin_unlock(&chip->reg_lock); 5568c2ecf20Sopenharmony_ci return bytes_to_frames(substream->runtime, ptr); 5578c2ecf20Sopenharmony_ci} 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_cistatic irqreturn_t snd_fm801_interrupt(int irq, void *dev_id) 5608c2ecf20Sopenharmony_ci{ 5618c2ecf20Sopenharmony_ci struct fm801 *chip = dev_id; 5628c2ecf20Sopenharmony_ci unsigned short status; 5638c2ecf20Sopenharmony_ci unsigned int tmp; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci status = fm801_readw(chip, IRQ_STATUS); 5668c2ecf20Sopenharmony_ci status &= FM801_IRQ_PLAYBACK|FM801_IRQ_CAPTURE|FM801_IRQ_MPU|FM801_IRQ_VOLUME; 5678c2ecf20Sopenharmony_ci if (! status) 5688c2ecf20Sopenharmony_ci return IRQ_NONE; 5698c2ecf20Sopenharmony_ci /* ack first */ 5708c2ecf20Sopenharmony_ci fm801_writew(chip, IRQ_STATUS, status); 5718c2ecf20Sopenharmony_ci if (chip->pcm && (status & FM801_IRQ_PLAYBACK) && chip->playback_substream) { 5728c2ecf20Sopenharmony_ci spin_lock(&chip->reg_lock); 5738c2ecf20Sopenharmony_ci chip->ply_buf++; 5748c2ecf20Sopenharmony_ci chip->ply_pos += chip->ply_count; 5758c2ecf20Sopenharmony_ci chip->ply_pos %= chip->ply_size; 5768c2ecf20Sopenharmony_ci tmp = chip->ply_pos + chip->ply_count; 5778c2ecf20Sopenharmony_ci tmp %= chip->ply_size; 5788c2ecf20Sopenharmony_ci if (chip->ply_buf & 1) 5798c2ecf20Sopenharmony_ci fm801_writel(chip, PLY_BUF1, chip->ply_buffer + tmp); 5808c2ecf20Sopenharmony_ci else 5818c2ecf20Sopenharmony_ci fm801_writel(chip, PLY_BUF2, chip->ply_buffer + tmp); 5828c2ecf20Sopenharmony_ci spin_unlock(&chip->reg_lock); 5838c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(chip->playback_substream); 5848c2ecf20Sopenharmony_ci } 5858c2ecf20Sopenharmony_ci if (chip->pcm && (status & FM801_IRQ_CAPTURE) && chip->capture_substream) { 5868c2ecf20Sopenharmony_ci spin_lock(&chip->reg_lock); 5878c2ecf20Sopenharmony_ci chip->cap_buf++; 5888c2ecf20Sopenharmony_ci chip->cap_pos += chip->cap_count; 5898c2ecf20Sopenharmony_ci chip->cap_pos %= chip->cap_size; 5908c2ecf20Sopenharmony_ci tmp = chip->cap_pos + chip->cap_count; 5918c2ecf20Sopenharmony_ci tmp %= chip->cap_size; 5928c2ecf20Sopenharmony_ci if (chip->cap_buf & 1) 5938c2ecf20Sopenharmony_ci fm801_writel(chip, CAP_BUF1, chip->cap_buffer + tmp); 5948c2ecf20Sopenharmony_ci else 5958c2ecf20Sopenharmony_ci fm801_writel(chip, CAP_BUF2, chip->cap_buffer + tmp); 5968c2ecf20Sopenharmony_ci spin_unlock(&chip->reg_lock); 5978c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(chip->capture_substream); 5988c2ecf20Sopenharmony_ci } 5998c2ecf20Sopenharmony_ci if (chip->rmidi && (status & FM801_IRQ_MPU)) 6008c2ecf20Sopenharmony_ci snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data); 6018c2ecf20Sopenharmony_ci if (status & FM801_IRQ_VOLUME) { 6028c2ecf20Sopenharmony_ci /* TODO */ 6038c2ecf20Sopenharmony_ci } 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci return IRQ_HANDLED; 6068c2ecf20Sopenharmony_ci} 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_fm801_playback = 6098c2ecf20Sopenharmony_ci{ 6108c2ecf20Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 6118c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 6128c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME | 6138c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID), 6148c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, 6158c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, 6168c2ecf20Sopenharmony_ci .rate_min = 5500, 6178c2ecf20Sopenharmony_ci .rate_max = 48000, 6188c2ecf20Sopenharmony_ci .channels_min = 1, 6198c2ecf20Sopenharmony_ci .channels_max = 2, 6208c2ecf20Sopenharmony_ci .buffer_bytes_max = (128*1024), 6218c2ecf20Sopenharmony_ci .period_bytes_min = 64, 6228c2ecf20Sopenharmony_ci .period_bytes_max = (128*1024), 6238c2ecf20Sopenharmony_ci .periods_min = 1, 6248c2ecf20Sopenharmony_ci .periods_max = 1024, 6258c2ecf20Sopenharmony_ci .fifo_size = 0, 6268c2ecf20Sopenharmony_ci}; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_fm801_capture = 6298c2ecf20Sopenharmony_ci{ 6308c2ecf20Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 6318c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 6328c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME | 6338c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID), 6348c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, 6358c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, 6368c2ecf20Sopenharmony_ci .rate_min = 5500, 6378c2ecf20Sopenharmony_ci .rate_max = 48000, 6388c2ecf20Sopenharmony_ci .channels_min = 1, 6398c2ecf20Sopenharmony_ci .channels_max = 2, 6408c2ecf20Sopenharmony_ci .buffer_bytes_max = (128*1024), 6418c2ecf20Sopenharmony_ci .period_bytes_min = 64, 6428c2ecf20Sopenharmony_ci .period_bytes_max = (128*1024), 6438c2ecf20Sopenharmony_ci .periods_min = 1, 6448c2ecf20Sopenharmony_ci .periods_max = 1024, 6458c2ecf20Sopenharmony_ci .fifo_size = 0, 6468c2ecf20Sopenharmony_ci}; 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_cistatic int snd_fm801_playback_open(struct snd_pcm_substream *substream) 6498c2ecf20Sopenharmony_ci{ 6508c2ecf20Sopenharmony_ci struct fm801 *chip = snd_pcm_substream_chip(substream); 6518c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 6528c2ecf20Sopenharmony_ci int err; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci chip->playback_substream = substream; 6558c2ecf20Sopenharmony_ci runtime->hw = snd_fm801_playback; 6568c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, 6578c2ecf20Sopenharmony_ci &hw_constraints_rates); 6588c2ecf20Sopenharmony_ci if (chip->multichannel) { 6598c2ecf20Sopenharmony_ci runtime->hw.channels_max = 6; 6608c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_list(runtime, 0, 6618c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_CHANNELS, 6628c2ecf20Sopenharmony_ci &hw_constraints_channels); 6638c2ecf20Sopenharmony_ci } 6648c2ecf20Sopenharmony_ci if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) 6658c2ecf20Sopenharmony_ci return err; 6668c2ecf20Sopenharmony_ci return 0; 6678c2ecf20Sopenharmony_ci} 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_cistatic int snd_fm801_capture_open(struct snd_pcm_substream *substream) 6708c2ecf20Sopenharmony_ci{ 6718c2ecf20Sopenharmony_ci struct fm801 *chip = snd_pcm_substream_chip(substream); 6728c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 6738c2ecf20Sopenharmony_ci int err; 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci chip->capture_substream = substream; 6768c2ecf20Sopenharmony_ci runtime->hw = snd_fm801_capture; 6778c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, 6788c2ecf20Sopenharmony_ci &hw_constraints_rates); 6798c2ecf20Sopenharmony_ci if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) 6808c2ecf20Sopenharmony_ci return err; 6818c2ecf20Sopenharmony_ci return 0; 6828c2ecf20Sopenharmony_ci} 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_cistatic int snd_fm801_playback_close(struct snd_pcm_substream *substream) 6858c2ecf20Sopenharmony_ci{ 6868c2ecf20Sopenharmony_ci struct fm801 *chip = snd_pcm_substream_chip(substream); 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci chip->playback_substream = NULL; 6898c2ecf20Sopenharmony_ci return 0; 6908c2ecf20Sopenharmony_ci} 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_cistatic int snd_fm801_capture_close(struct snd_pcm_substream *substream) 6938c2ecf20Sopenharmony_ci{ 6948c2ecf20Sopenharmony_ci struct fm801 *chip = snd_pcm_substream_chip(substream); 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci chip->capture_substream = NULL; 6978c2ecf20Sopenharmony_ci return 0; 6988c2ecf20Sopenharmony_ci} 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_fm801_playback_ops = { 7018c2ecf20Sopenharmony_ci .open = snd_fm801_playback_open, 7028c2ecf20Sopenharmony_ci .close = snd_fm801_playback_close, 7038c2ecf20Sopenharmony_ci .prepare = snd_fm801_playback_prepare, 7048c2ecf20Sopenharmony_ci .trigger = snd_fm801_playback_trigger, 7058c2ecf20Sopenharmony_ci .pointer = snd_fm801_playback_pointer, 7068c2ecf20Sopenharmony_ci}; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_fm801_capture_ops = { 7098c2ecf20Sopenharmony_ci .open = snd_fm801_capture_open, 7108c2ecf20Sopenharmony_ci .close = snd_fm801_capture_close, 7118c2ecf20Sopenharmony_ci .prepare = snd_fm801_capture_prepare, 7128c2ecf20Sopenharmony_ci .trigger = snd_fm801_capture_trigger, 7138c2ecf20Sopenharmony_ci .pointer = snd_fm801_capture_pointer, 7148c2ecf20Sopenharmony_ci}; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_cistatic int snd_fm801_pcm(struct fm801 *chip, int device) 7178c2ecf20Sopenharmony_ci{ 7188c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(chip->dev); 7198c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 7208c2ecf20Sopenharmony_ci int err; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci if ((err = snd_pcm_new(chip->card, "FM801", device, 1, 1, &pcm)) < 0) 7238c2ecf20Sopenharmony_ci return err; 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_fm801_playback_ops); 7268c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_fm801_capture_ops); 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci pcm->private_data = chip; 7298c2ecf20Sopenharmony_ci pcm->info_flags = 0; 7308c2ecf20Sopenharmony_ci strcpy(pcm->name, "FM801"); 7318c2ecf20Sopenharmony_ci chip->pcm = pcm; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &pdev->dev, 7348c2ecf20Sopenharmony_ci chip->multichannel ? 128*1024 : 64*1024, 128*1024); 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci return snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, 7378c2ecf20Sopenharmony_ci snd_pcm_alt_chmaps, 7388c2ecf20Sopenharmony_ci chip->multichannel ? 6 : 2, 0, 7398c2ecf20Sopenharmony_ci NULL); 7408c2ecf20Sopenharmony_ci} 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci/* 7438c2ecf20Sopenharmony_ci * TEA5757 radio 7448c2ecf20Sopenharmony_ci */ 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_FM801_TEA575X_BOOL 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci/* GPIO to TEA575x maps */ 7498c2ecf20Sopenharmony_cistruct snd_fm801_tea575x_gpio { 7508c2ecf20Sopenharmony_ci u8 data, clk, wren, most; 7518c2ecf20Sopenharmony_ci char *name; 7528c2ecf20Sopenharmony_ci}; 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_cistatic const struct snd_fm801_tea575x_gpio snd_fm801_tea575x_gpios[] = { 7558c2ecf20Sopenharmony_ci { .data = 1, .clk = 3, .wren = 2, .most = 0, .name = "SF256-PCS" }, 7568c2ecf20Sopenharmony_ci { .data = 1, .clk = 0, .wren = 2, .most = 3, .name = "SF256-PCP" }, 7578c2ecf20Sopenharmony_ci { .data = 2, .clk = 0, .wren = 1, .most = 3, .name = "SF64-PCR" }, 7588c2ecf20Sopenharmony_ci}; 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci#define get_tea575x_gpio(chip) \ 7618c2ecf20Sopenharmony_ci (&snd_fm801_tea575x_gpios[((chip)->tea575x_tuner & TUNER_TYPE_MASK) - 1]) 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_cistatic void snd_fm801_tea575x_set_pins(struct snd_tea575x *tea, u8 pins) 7648c2ecf20Sopenharmony_ci{ 7658c2ecf20Sopenharmony_ci struct fm801 *chip = tea->private_data; 7668c2ecf20Sopenharmony_ci unsigned short reg = fm801_readw(chip, GPIO_CTRL); 7678c2ecf20Sopenharmony_ci struct snd_fm801_tea575x_gpio gpio = *get_tea575x_gpio(chip); 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci reg &= ~(FM801_GPIO_GP(gpio.data) | 7708c2ecf20Sopenharmony_ci FM801_GPIO_GP(gpio.clk) | 7718c2ecf20Sopenharmony_ci FM801_GPIO_GP(gpio.wren)); 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci reg |= (pins & TEA575X_DATA) ? FM801_GPIO_GP(gpio.data) : 0; 7748c2ecf20Sopenharmony_ci reg |= (pins & TEA575X_CLK) ? FM801_GPIO_GP(gpio.clk) : 0; 7758c2ecf20Sopenharmony_ci /* WRITE_ENABLE is inverted */ 7768c2ecf20Sopenharmony_ci reg |= (pins & TEA575X_WREN) ? 0 : FM801_GPIO_GP(gpio.wren); 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci fm801_writew(chip, GPIO_CTRL, reg); 7798c2ecf20Sopenharmony_ci} 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_cistatic u8 snd_fm801_tea575x_get_pins(struct snd_tea575x *tea) 7828c2ecf20Sopenharmony_ci{ 7838c2ecf20Sopenharmony_ci struct fm801 *chip = tea->private_data; 7848c2ecf20Sopenharmony_ci unsigned short reg = fm801_readw(chip, GPIO_CTRL); 7858c2ecf20Sopenharmony_ci struct snd_fm801_tea575x_gpio gpio = *get_tea575x_gpio(chip); 7868c2ecf20Sopenharmony_ci u8 ret; 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci ret = 0; 7898c2ecf20Sopenharmony_ci if (reg & FM801_GPIO_GP(gpio.data)) 7908c2ecf20Sopenharmony_ci ret |= TEA575X_DATA; 7918c2ecf20Sopenharmony_ci if (reg & FM801_GPIO_GP(gpio.most)) 7928c2ecf20Sopenharmony_ci ret |= TEA575X_MOST; 7938c2ecf20Sopenharmony_ci return ret; 7948c2ecf20Sopenharmony_ci} 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_cistatic void snd_fm801_tea575x_set_direction(struct snd_tea575x *tea, bool output) 7978c2ecf20Sopenharmony_ci{ 7988c2ecf20Sopenharmony_ci struct fm801 *chip = tea->private_data; 7998c2ecf20Sopenharmony_ci unsigned short reg = fm801_readw(chip, GPIO_CTRL); 8008c2ecf20Sopenharmony_ci struct snd_fm801_tea575x_gpio gpio = *get_tea575x_gpio(chip); 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci /* use GPIO lines and set write enable bit */ 8038c2ecf20Sopenharmony_ci reg |= FM801_GPIO_GS(gpio.data) | 8048c2ecf20Sopenharmony_ci FM801_GPIO_GS(gpio.wren) | 8058c2ecf20Sopenharmony_ci FM801_GPIO_GS(gpio.clk) | 8068c2ecf20Sopenharmony_ci FM801_GPIO_GS(gpio.most); 8078c2ecf20Sopenharmony_ci if (output) { 8088c2ecf20Sopenharmony_ci /* all of lines are in the write direction */ 8098c2ecf20Sopenharmony_ci /* clear data and clock lines */ 8108c2ecf20Sopenharmony_ci reg &= ~(FM801_GPIO_GD(gpio.data) | 8118c2ecf20Sopenharmony_ci FM801_GPIO_GD(gpio.wren) | 8128c2ecf20Sopenharmony_ci FM801_GPIO_GD(gpio.clk) | 8138c2ecf20Sopenharmony_ci FM801_GPIO_GP(gpio.data) | 8148c2ecf20Sopenharmony_ci FM801_GPIO_GP(gpio.clk) | 8158c2ecf20Sopenharmony_ci FM801_GPIO_GP(gpio.wren)); 8168c2ecf20Sopenharmony_ci } else { 8178c2ecf20Sopenharmony_ci /* use GPIO lines, set data direction to input */ 8188c2ecf20Sopenharmony_ci reg |= FM801_GPIO_GD(gpio.data) | 8198c2ecf20Sopenharmony_ci FM801_GPIO_GD(gpio.most) | 8208c2ecf20Sopenharmony_ci FM801_GPIO_GP(gpio.data) | 8218c2ecf20Sopenharmony_ci FM801_GPIO_GP(gpio.most) | 8228c2ecf20Sopenharmony_ci FM801_GPIO_GP(gpio.wren); 8238c2ecf20Sopenharmony_ci /* all of lines are in the write direction, except data */ 8248c2ecf20Sopenharmony_ci /* clear data, write enable and clock lines */ 8258c2ecf20Sopenharmony_ci reg &= ~(FM801_GPIO_GD(gpio.wren) | 8268c2ecf20Sopenharmony_ci FM801_GPIO_GD(gpio.clk) | 8278c2ecf20Sopenharmony_ci FM801_GPIO_GP(gpio.clk)); 8288c2ecf20Sopenharmony_ci } 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci fm801_writew(chip, GPIO_CTRL, reg); 8318c2ecf20Sopenharmony_ci} 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_cistatic const struct snd_tea575x_ops snd_fm801_tea_ops = { 8348c2ecf20Sopenharmony_ci .set_pins = snd_fm801_tea575x_set_pins, 8358c2ecf20Sopenharmony_ci .get_pins = snd_fm801_tea575x_get_pins, 8368c2ecf20Sopenharmony_ci .set_direction = snd_fm801_tea575x_set_direction, 8378c2ecf20Sopenharmony_ci}; 8388c2ecf20Sopenharmony_ci#endif 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci/* 8418c2ecf20Sopenharmony_ci * Mixer routines 8428c2ecf20Sopenharmony_ci */ 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci#define FM801_SINGLE(xname, reg, shift, mask, invert) \ 8458c2ecf20Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_fm801_info_single, \ 8468c2ecf20Sopenharmony_ci .get = snd_fm801_get_single, .put = snd_fm801_put_single, \ 8478c2ecf20Sopenharmony_ci .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) } 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_cistatic int snd_fm801_info_single(struct snd_kcontrol *kcontrol, 8508c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 8518c2ecf20Sopenharmony_ci{ 8528c2ecf20Sopenharmony_ci int mask = (kcontrol->private_value >> 16) & 0xff; 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; 8558c2ecf20Sopenharmony_ci uinfo->count = 1; 8568c2ecf20Sopenharmony_ci uinfo->value.integer.min = 0; 8578c2ecf20Sopenharmony_ci uinfo->value.integer.max = mask; 8588c2ecf20Sopenharmony_ci return 0; 8598c2ecf20Sopenharmony_ci} 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_cistatic int snd_fm801_get_single(struct snd_kcontrol *kcontrol, 8628c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 8638c2ecf20Sopenharmony_ci{ 8648c2ecf20Sopenharmony_ci struct fm801 *chip = snd_kcontrol_chip(kcontrol); 8658c2ecf20Sopenharmony_ci int reg = kcontrol->private_value & 0xff; 8668c2ecf20Sopenharmony_ci int shift = (kcontrol->private_value >> 8) & 0xff; 8678c2ecf20Sopenharmony_ci int mask = (kcontrol->private_value >> 16) & 0xff; 8688c2ecf20Sopenharmony_ci int invert = (kcontrol->private_value >> 24) & 0xff; 8698c2ecf20Sopenharmony_ci long *value = ucontrol->value.integer.value; 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci value[0] = (fm801_ioread16(chip, reg) >> shift) & mask; 8728c2ecf20Sopenharmony_ci if (invert) 8738c2ecf20Sopenharmony_ci value[0] = mask - value[0]; 8748c2ecf20Sopenharmony_ci return 0; 8758c2ecf20Sopenharmony_ci} 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_cistatic int snd_fm801_put_single(struct snd_kcontrol *kcontrol, 8788c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 8798c2ecf20Sopenharmony_ci{ 8808c2ecf20Sopenharmony_ci struct fm801 *chip = snd_kcontrol_chip(kcontrol); 8818c2ecf20Sopenharmony_ci int reg = kcontrol->private_value & 0xff; 8828c2ecf20Sopenharmony_ci int shift = (kcontrol->private_value >> 8) & 0xff; 8838c2ecf20Sopenharmony_ci int mask = (kcontrol->private_value >> 16) & 0xff; 8848c2ecf20Sopenharmony_ci int invert = (kcontrol->private_value >> 24) & 0xff; 8858c2ecf20Sopenharmony_ci unsigned short val; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci val = (ucontrol->value.integer.value[0] & mask); 8888c2ecf20Sopenharmony_ci if (invert) 8898c2ecf20Sopenharmony_ci val = mask - val; 8908c2ecf20Sopenharmony_ci return snd_fm801_update_bits(chip, reg, mask << shift, val << shift); 8918c2ecf20Sopenharmony_ci} 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci#define FM801_DOUBLE(xname, reg, shift_left, shift_right, mask, invert) \ 8948c2ecf20Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_fm801_info_double, \ 8958c2ecf20Sopenharmony_ci .get = snd_fm801_get_double, .put = snd_fm801_put_double, \ 8968c2ecf20Sopenharmony_ci .private_value = reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24) } 8978c2ecf20Sopenharmony_ci#define FM801_DOUBLE_TLV(xname, reg, shift_left, shift_right, mask, invert, xtlv) \ 8988c2ecf20Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ 8998c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ 9008c2ecf20Sopenharmony_ci .name = xname, .info = snd_fm801_info_double, \ 9018c2ecf20Sopenharmony_ci .get = snd_fm801_get_double, .put = snd_fm801_put_double, \ 9028c2ecf20Sopenharmony_ci .private_value = reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24), \ 9038c2ecf20Sopenharmony_ci .tlv = { .p = (xtlv) } } 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_cistatic int snd_fm801_info_double(struct snd_kcontrol *kcontrol, 9068c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 9078c2ecf20Sopenharmony_ci{ 9088c2ecf20Sopenharmony_ci int mask = (kcontrol->private_value >> 16) & 0xff; 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; 9118c2ecf20Sopenharmony_ci uinfo->count = 2; 9128c2ecf20Sopenharmony_ci uinfo->value.integer.min = 0; 9138c2ecf20Sopenharmony_ci uinfo->value.integer.max = mask; 9148c2ecf20Sopenharmony_ci return 0; 9158c2ecf20Sopenharmony_ci} 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_cistatic int snd_fm801_get_double(struct snd_kcontrol *kcontrol, 9188c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 9198c2ecf20Sopenharmony_ci{ 9208c2ecf20Sopenharmony_ci struct fm801 *chip = snd_kcontrol_chip(kcontrol); 9218c2ecf20Sopenharmony_ci int reg = kcontrol->private_value & 0xff; 9228c2ecf20Sopenharmony_ci int shift_left = (kcontrol->private_value >> 8) & 0x0f; 9238c2ecf20Sopenharmony_ci int shift_right = (kcontrol->private_value >> 12) & 0x0f; 9248c2ecf20Sopenharmony_ci int mask = (kcontrol->private_value >> 16) & 0xff; 9258c2ecf20Sopenharmony_ci int invert = (kcontrol->private_value >> 24) & 0xff; 9268c2ecf20Sopenharmony_ci long *value = ucontrol->value.integer.value; 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci spin_lock_irq(&chip->reg_lock); 9298c2ecf20Sopenharmony_ci value[0] = (fm801_ioread16(chip, reg) >> shift_left) & mask; 9308c2ecf20Sopenharmony_ci value[1] = (fm801_ioread16(chip, reg) >> shift_right) & mask; 9318c2ecf20Sopenharmony_ci spin_unlock_irq(&chip->reg_lock); 9328c2ecf20Sopenharmony_ci if (invert) { 9338c2ecf20Sopenharmony_ci value[0] = mask - value[0]; 9348c2ecf20Sopenharmony_ci value[1] = mask - value[1]; 9358c2ecf20Sopenharmony_ci } 9368c2ecf20Sopenharmony_ci return 0; 9378c2ecf20Sopenharmony_ci} 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_cistatic int snd_fm801_put_double(struct snd_kcontrol *kcontrol, 9408c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 9418c2ecf20Sopenharmony_ci{ 9428c2ecf20Sopenharmony_ci struct fm801 *chip = snd_kcontrol_chip(kcontrol); 9438c2ecf20Sopenharmony_ci int reg = kcontrol->private_value & 0xff; 9448c2ecf20Sopenharmony_ci int shift_left = (kcontrol->private_value >> 8) & 0x0f; 9458c2ecf20Sopenharmony_ci int shift_right = (kcontrol->private_value >> 12) & 0x0f; 9468c2ecf20Sopenharmony_ci int mask = (kcontrol->private_value >> 16) & 0xff; 9478c2ecf20Sopenharmony_ci int invert = (kcontrol->private_value >> 24) & 0xff; 9488c2ecf20Sopenharmony_ci unsigned short val1, val2; 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci val1 = ucontrol->value.integer.value[0] & mask; 9518c2ecf20Sopenharmony_ci val2 = ucontrol->value.integer.value[1] & mask; 9528c2ecf20Sopenharmony_ci if (invert) { 9538c2ecf20Sopenharmony_ci val1 = mask - val1; 9548c2ecf20Sopenharmony_ci val2 = mask - val2; 9558c2ecf20Sopenharmony_ci } 9568c2ecf20Sopenharmony_ci return snd_fm801_update_bits(chip, reg, 9578c2ecf20Sopenharmony_ci (mask << shift_left) | (mask << shift_right), 9588c2ecf20Sopenharmony_ci (val1 << shift_left ) | (val2 << shift_right)); 9598c2ecf20Sopenharmony_ci} 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_cistatic int snd_fm801_info_mux(struct snd_kcontrol *kcontrol, 9628c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 9638c2ecf20Sopenharmony_ci{ 9648c2ecf20Sopenharmony_ci static const char * const texts[5] = { 9658c2ecf20Sopenharmony_ci "AC97 Primary", "FM", "I2S", "PCM", "AC97 Secondary" 9668c2ecf20Sopenharmony_ci }; 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci return snd_ctl_enum_info(uinfo, 1, 5, texts); 9698c2ecf20Sopenharmony_ci} 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_cistatic int snd_fm801_get_mux(struct snd_kcontrol *kcontrol, 9728c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 9738c2ecf20Sopenharmony_ci{ 9748c2ecf20Sopenharmony_ci struct fm801 *chip = snd_kcontrol_chip(kcontrol); 9758c2ecf20Sopenharmony_ci unsigned short val; 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci val = fm801_readw(chip, REC_SRC) & 7; 9788c2ecf20Sopenharmony_ci if (val > 4) 9798c2ecf20Sopenharmony_ci val = 4; 9808c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = val; 9818c2ecf20Sopenharmony_ci return 0; 9828c2ecf20Sopenharmony_ci} 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_cistatic int snd_fm801_put_mux(struct snd_kcontrol *kcontrol, 9858c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 9868c2ecf20Sopenharmony_ci{ 9878c2ecf20Sopenharmony_ci struct fm801 *chip = snd_kcontrol_chip(kcontrol); 9888c2ecf20Sopenharmony_ci unsigned short val; 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci if ((val = ucontrol->value.enumerated.item[0]) > 4) 9918c2ecf20Sopenharmony_ci return -EINVAL; 9928c2ecf20Sopenharmony_ci return snd_fm801_update_bits(chip, FM801_REC_SRC, 7, val); 9938c2ecf20Sopenharmony_ci} 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_dsp, -3450, 150, 0); 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci#define FM801_CONTROLS ARRAY_SIZE(snd_fm801_controls) 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_fm801_controls[] = { 10008c2ecf20Sopenharmony_ciFM801_DOUBLE_TLV("Wave Playback Volume", FM801_PCM_VOL, 0, 8, 31, 1, 10018c2ecf20Sopenharmony_ci db_scale_dsp), 10028c2ecf20Sopenharmony_ciFM801_SINGLE("Wave Playback Switch", FM801_PCM_VOL, 15, 1, 1), 10038c2ecf20Sopenharmony_ciFM801_DOUBLE_TLV("I2S Playback Volume", FM801_I2S_VOL, 0, 8, 31, 1, 10048c2ecf20Sopenharmony_ci db_scale_dsp), 10058c2ecf20Sopenharmony_ciFM801_SINGLE("I2S Playback Switch", FM801_I2S_VOL, 15, 1, 1), 10068c2ecf20Sopenharmony_ciFM801_DOUBLE_TLV("FM Playback Volume", FM801_FM_VOL, 0, 8, 31, 1, 10078c2ecf20Sopenharmony_ci db_scale_dsp), 10088c2ecf20Sopenharmony_ciFM801_SINGLE("FM Playback Switch", FM801_FM_VOL, 15, 1, 1), 10098c2ecf20Sopenharmony_ci{ 10108c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 10118c2ecf20Sopenharmony_ci .name = "Digital Capture Source", 10128c2ecf20Sopenharmony_ci .info = snd_fm801_info_mux, 10138c2ecf20Sopenharmony_ci .get = snd_fm801_get_mux, 10148c2ecf20Sopenharmony_ci .put = snd_fm801_put_mux, 10158c2ecf20Sopenharmony_ci} 10168c2ecf20Sopenharmony_ci}; 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci#define FM801_CONTROLS_MULTI ARRAY_SIZE(snd_fm801_controls_multi) 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_fm801_controls_multi[] = { 10218c2ecf20Sopenharmony_ciFM801_SINGLE("AC97 2ch->4ch Copy Switch", FM801_CODEC_CTRL, 7, 1, 0), 10228c2ecf20Sopenharmony_ciFM801_SINGLE("AC97 18-bit Switch", FM801_CODEC_CTRL, 10, 1, 0), 10238c2ecf20Sopenharmony_ciFM801_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), FM801_I2S_MODE, 8, 1, 0), 10248c2ecf20Sopenharmony_ciFM801_SINGLE(SNDRV_CTL_NAME_IEC958("Raw Data ",PLAYBACK,SWITCH), FM801_I2S_MODE, 9, 1, 0), 10258c2ecf20Sopenharmony_ciFM801_SINGLE(SNDRV_CTL_NAME_IEC958("Raw Data ",CAPTURE,SWITCH), FM801_I2S_MODE, 10, 1, 0), 10268c2ecf20Sopenharmony_ciFM801_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH), FM801_GEN_CTRL, 2, 1, 0), 10278c2ecf20Sopenharmony_ci}; 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_cistatic void snd_fm801_mixer_free_ac97_bus(struct snd_ac97_bus *bus) 10308c2ecf20Sopenharmony_ci{ 10318c2ecf20Sopenharmony_ci struct fm801 *chip = bus->private_data; 10328c2ecf20Sopenharmony_ci chip->ac97_bus = NULL; 10338c2ecf20Sopenharmony_ci} 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_cistatic void snd_fm801_mixer_free_ac97(struct snd_ac97 *ac97) 10368c2ecf20Sopenharmony_ci{ 10378c2ecf20Sopenharmony_ci struct fm801 *chip = ac97->private_data; 10388c2ecf20Sopenharmony_ci if (ac97->num == 0) { 10398c2ecf20Sopenharmony_ci chip->ac97 = NULL; 10408c2ecf20Sopenharmony_ci } else { 10418c2ecf20Sopenharmony_ci chip->ac97_sec = NULL; 10428c2ecf20Sopenharmony_ci } 10438c2ecf20Sopenharmony_ci} 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_cistatic int snd_fm801_mixer(struct fm801 *chip) 10468c2ecf20Sopenharmony_ci{ 10478c2ecf20Sopenharmony_ci struct snd_ac97_template ac97; 10488c2ecf20Sopenharmony_ci unsigned int i; 10498c2ecf20Sopenharmony_ci int err; 10508c2ecf20Sopenharmony_ci static const struct snd_ac97_bus_ops ops = { 10518c2ecf20Sopenharmony_ci .write = snd_fm801_codec_write, 10528c2ecf20Sopenharmony_ci .read = snd_fm801_codec_read, 10538c2ecf20Sopenharmony_ci }; 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci if ((err = snd_ac97_bus(chip->card, 0, &ops, chip, &chip->ac97_bus)) < 0) 10568c2ecf20Sopenharmony_ci return err; 10578c2ecf20Sopenharmony_ci chip->ac97_bus->private_free = snd_fm801_mixer_free_ac97_bus; 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci memset(&ac97, 0, sizeof(ac97)); 10608c2ecf20Sopenharmony_ci ac97.private_data = chip; 10618c2ecf20Sopenharmony_ci ac97.private_free = snd_fm801_mixer_free_ac97; 10628c2ecf20Sopenharmony_ci if ((err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97)) < 0) 10638c2ecf20Sopenharmony_ci return err; 10648c2ecf20Sopenharmony_ci if (chip->secondary) { 10658c2ecf20Sopenharmony_ci ac97.num = 1; 10668c2ecf20Sopenharmony_ci ac97.addr = chip->secondary_addr; 10678c2ecf20Sopenharmony_ci if ((err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97_sec)) < 0) 10688c2ecf20Sopenharmony_ci return err; 10698c2ecf20Sopenharmony_ci } 10708c2ecf20Sopenharmony_ci for (i = 0; i < FM801_CONTROLS; i++) { 10718c2ecf20Sopenharmony_ci err = snd_ctl_add(chip->card, 10728c2ecf20Sopenharmony_ci snd_ctl_new1(&snd_fm801_controls[i], chip)); 10738c2ecf20Sopenharmony_ci if (err < 0) 10748c2ecf20Sopenharmony_ci return err; 10758c2ecf20Sopenharmony_ci } 10768c2ecf20Sopenharmony_ci if (chip->multichannel) { 10778c2ecf20Sopenharmony_ci for (i = 0; i < FM801_CONTROLS_MULTI; i++) { 10788c2ecf20Sopenharmony_ci err = snd_ctl_add(chip->card, 10798c2ecf20Sopenharmony_ci snd_ctl_new1(&snd_fm801_controls_multi[i], chip)); 10808c2ecf20Sopenharmony_ci if (err < 0) 10818c2ecf20Sopenharmony_ci return err; 10828c2ecf20Sopenharmony_ci } 10838c2ecf20Sopenharmony_ci } 10848c2ecf20Sopenharmony_ci return 0; 10858c2ecf20Sopenharmony_ci} 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci/* 10888c2ecf20Sopenharmony_ci * initialization routines 10898c2ecf20Sopenharmony_ci */ 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_cistatic int wait_for_codec(struct fm801 *chip, unsigned int codec_id, 10928c2ecf20Sopenharmony_ci unsigned short reg, unsigned long waits) 10938c2ecf20Sopenharmony_ci{ 10948c2ecf20Sopenharmony_ci unsigned long timeout = jiffies + waits; 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci fm801_writew(chip, AC97_CMD, 10978c2ecf20Sopenharmony_ci reg | (codec_id << FM801_AC97_ADDR_SHIFT) | FM801_AC97_READ); 10988c2ecf20Sopenharmony_ci udelay(5); 10998c2ecf20Sopenharmony_ci do { 11008c2ecf20Sopenharmony_ci if ((fm801_readw(chip, AC97_CMD) & 11018c2ecf20Sopenharmony_ci (FM801_AC97_VALID | FM801_AC97_BUSY)) == FM801_AC97_VALID) 11028c2ecf20Sopenharmony_ci return 0; 11038c2ecf20Sopenharmony_ci schedule_timeout_uninterruptible(1); 11048c2ecf20Sopenharmony_ci } while (time_after(timeout, jiffies)); 11058c2ecf20Sopenharmony_ci return -EIO; 11068c2ecf20Sopenharmony_ci} 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_cistatic int reset_codec(struct fm801 *chip) 11098c2ecf20Sopenharmony_ci{ 11108c2ecf20Sopenharmony_ci /* codec cold reset + AC'97 warm reset */ 11118c2ecf20Sopenharmony_ci fm801_writew(chip, CODEC_CTRL, (1 << 5) | (1 << 6)); 11128c2ecf20Sopenharmony_ci fm801_readw(chip, CODEC_CTRL); /* flush posting data */ 11138c2ecf20Sopenharmony_ci udelay(100); 11148c2ecf20Sopenharmony_ci fm801_writew(chip, CODEC_CTRL, 0); 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci return wait_for_codec(chip, 0, AC97_RESET, msecs_to_jiffies(750)); 11178c2ecf20Sopenharmony_ci} 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_cistatic void snd_fm801_chip_multichannel_init(struct fm801 *chip) 11208c2ecf20Sopenharmony_ci{ 11218c2ecf20Sopenharmony_ci unsigned short cmdw; 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci if (chip->multichannel) { 11248c2ecf20Sopenharmony_ci if (chip->secondary_addr) { 11258c2ecf20Sopenharmony_ci wait_for_codec(chip, chip->secondary_addr, 11268c2ecf20Sopenharmony_ci AC97_VENDOR_ID1, msecs_to_jiffies(50)); 11278c2ecf20Sopenharmony_ci } else { 11288c2ecf20Sopenharmony_ci /* my card has the secondary codec */ 11298c2ecf20Sopenharmony_ci /* at address #3, so the loop is inverted */ 11308c2ecf20Sopenharmony_ci int i; 11318c2ecf20Sopenharmony_ci for (i = 3; i > 0; i--) { 11328c2ecf20Sopenharmony_ci if (!wait_for_codec(chip, i, AC97_VENDOR_ID1, 11338c2ecf20Sopenharmony_ci msecs_to_jiffies(50))) { 11348c2ecf20Sopenharmony_ci cmdw = fm801_readw(chip, AC97_DATA); 11358c2ecf20Sopenharmony_ci if (cmdw != 0xffff && cmdw != 0) { 11368c2ecf20Sopenharmony_ci chip->secondary = 1; 11378c2ecf20Sopenharmony_ci chip->secondary_addr = i; 11388c2ecf20Sopenharmony_ci break; 11398c2ecf20Sopenharmony_ci } 11408c2ecf20Sopenharmony_ci } 11418c2ecf20Sopenharmony_ci } 11428c2ecf20Sopenharmony_ci } 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci /* the recovery phase, it seems that probing for non-existing codec might */ 11458c2ecf20Sopenharmony_ci /* cause timeout problems */ 11468c2ecf20Sopenharmony_ci wait_for_codec(chip, 0, AC97_VENDOR_ID1, msecs_to_jiffies(750)); 11478c2ecf20Sopenharmony_ci } 11488c2ecf20Sopenharmony_ci} 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_cistatic void snd_fm801_chip_init(struct fm801 *chip) 11518c2ecf20Sopenharmony_ci{ 11528c2ecf20Sopenharmony_ci unsigned short cmdw; 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci /* init volume */ 11558c2ecf20Sopenharmony_ci fm801_writew(chip, PCM_VOL, 0x0808); 11568c2ecf20Sopenharmony_ci fm801_writew(chip, FM_VOL, 0x9f1f); 11578c2ecf20Sopenharmony_ci fm801_writew(chip, I2S_VOL, 0x8808); 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci /* I2S control - I2S mode */ 11608c2ecf20Sopenharmony_ci fm801_writew(chip, I2S_MODE, 0x0003); 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci /* interrupt setup */ 11638c2ecf20Sopenharmony_ci cmdw = fm801_readw(chip, IRQ_MASK); 11648c2ecf20Sopenharmony_ci if (chip->irq < 0) 11658c2ecf20Sopenharmony_ci cmdw |= 0x00c3; /* mask everything, no PCM nor MPU */ 11668c2ecf20Sopenharmony_ci else 11678c2ecf20Sopenharmony_ci cmdw &= ~0x0083; /* unmask MPU, PLAYBACK & CAPTURE */ 11688c2ecf20Sopenharmony_ci fm801_writew(chip, IRQ_MASK, cmdw); 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci /* interrupt clear */ 11718c2ecf20Sopenharmony_ci fm801_writew(chip, IRQ_STATUS, 11728c2ecf20Sopenharmony_ci FM801_IRQ_PLAYBACK | FM801_IRQ_CAPTURE | FM801_IRQ_MPU); 11738c2ecf20Sopenharmony_ci} 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_cistatic int snd_fm801_free(struct fm801 *chip) 11768c2ecf20Sopenharmony_ci{ 11778c2ecf20Sopenharmony_ci unsigned short cmdw; 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_ci if (chip->irq < 0) 11808c2ecf20Sopenharmony_ci goto __end_hw; 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci /* interrupt setup - mask everything */ 11838c2ecf20Sopenharmony_ci cmdw = fm801_readw(chip, IRQ_MASK); 11848c2ecf20Sopenharmony_ci cmdw |= 0x00c3; 11858c2ecf20Sopenharmony_ci fm801_writew(chip, IRQ_MASK, cmdw); 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci devm_free_irq(chip->dev, chip->irq, chip); 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci __end_hw: 11908c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_FM801_TEA575X_BOOL 11918c2ecf20Sopenharmony_ci if (!(chip->tea575x_tuner & TUNER_DISABLED)) { 11928c2ecf20Sopenharmony_ci snd_tea575x_exit(&chip->tea); 11938c2ecf20Sopenharmony_ci v4l2_device_unregister(&chip->v4l2_dev); 11948c2ecf20Sopenharmony_ci } 11958c2ecf20Sopenharmony_ci#endif 11968c2ecf20Sopenharmony_ci return 0; 11978c2ecf20Sopenharmony_ci} 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_cistatic int snd_fm801_dev_free(struct snd_device *device) 12008c2ecf20Sopenharmony_ci{ 12018c2ecf20Sopenharmony_ci struct fm801 *chip = device->device_data; 12028c2ecf20Sopenharmony_ci return snd_fm801_free(chip); 12038c2ecf20Sopenharmony_ci} 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_cistatic int snd_fm801_create(struct snd_card *card, 12068c2ecf20Sopenharmony_ci struct pci_dev *pci, 12078c2ecf20Sopenharmony_ci int tea575x_tuner, 12088c2ecf20Sopenharmony_ci int radio_nr, 12098c2ecf20Sopenharmony_ci struct fm801 **rchip) 12108c2ecf20Sopenharmony_ci{ 12118c2ecf20Sopenharmony_ci struct fm801 *chip; 12128c2ecf20Sopenharmony_ci int err; 12138c2ecf20Sopenharmony_ci static const struct snd_device_ops ops = { 12148c2ecf20Sopenharmony_ci .dev_free = snd_fm801_dev_free, 12158c2ecf20Sopenharmony_ci }; 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci *rchip = NULL; 12188c2ecf20Sopenharmony_ci if ((err = pcim_enable_device(pci)) < 0) 12198c2ecf20Sopenharmony_ci return err; 12208c2ecf20Sopenharmony_ci chip = devm_kzalloc(&pci->dev, sizeof(*chip), GFP_KERNEL); 12218c2ecf20Sopenharmony_ci if (chip == NULL) 12228c2ecf20Sopenharmony_ci return -ENOMEM; 12238c2ecf20Sopenharmony_ci spin_lock_init(&chip->reg_lock); 12248c2ecf20Sopenharmony_ci chip->card = card; 12258c2ecf20Sopenharmony_ci chip->dev = &pci->dev; 12268c2ecf20Sopenharmony_ci chip->irq = -1; 12278c2ecf20Sopenharmony_ci chip->tea575x_tuner = tea575x_tuner; 12288c2ecf20Sopenharmony_ci if ((err = pci_request_regions(pci, "FM801")) < 0) 12298c2ecf20Sopenharmony_ci return err; 12308c2ecf20Sopenharmony_ci chip->port = pci_resource_start(pci, 0); 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci if (pci->revision >= 0xb1) /* FM801-AU */ 12338c2ecf20Sopenharmony_ci chip->multichannel = 1; 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci if (!(chip->tea575x_tuner & TUNER_ONLY)) { 12368c2ecf20Sopenharmony_ci if (reset_codec(chip) < 0) { 12378c2ecf20Sopenharmony_ci dev_info(chip->card->dev, 12388c2ecf20Sopenharmony_ci "Primary AC'97 codec not found, assume SF64-PCR (tuner-only)\n"); 12398c2ecf20Sopenharmony_ci chip->tea575x_tuner = 3 | TUNER_ONLY; 12408c2ecf20Sopenharmony_ci } else { 12418c2ecf20Sopenharmony_ci snd_fm801_chip_multichannel_init(chip); 12428c2ecf20Sopenharmony_ci } 12438c2ecf20Sopenharmony_ci } 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_ci if ((chip->tea575x_tuner & TUNER_ONLY) == 0) { 12468c2ecf20Sopenharmony_ci if (devm_request_irq(&pci->dev, pci->irq, snd_fm801_interrupt, 12478c2ecf20Sopenharmony_ci IRQF_SHARED, KBUILD_MODNAME, chip)) { 12488c2ecf20Sopenharmony_ci dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq); 12498c2ecf20Sopenharmony_ci snd_fm801_free(chip); 12508c2ecf20Sopenharmony_ci return -EBUSY; 12518c2ecf20Sopenharmony_ci } 12528c2ecf20Sopenharmony_ci chip->irq = pci->irq; 12538c2ecf20Sopenharmony_ci card->sync_irq = chip->irq; 12548c2ecf20Sopenharmony_ci pci_set_master(pci); 12558c2ecf20Sopenharmony_ci } 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci snd_fm801_chip_init(chip); 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { 12608c2ecf20Sopenharmony_ci snd_fm801_free(chip); 12618c2ecf20Sopenharmony_ci return err; 12628c2ecf20Sopenharmony_ci } 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_FM801_TEA575X_BOOL 12658c2ecf20Sopenharmony_ci err = v4l2_device_register(&pci->dev, &chip->v4l2_dev); 12668c2ecf20Sopenharmony_ci if (err < 0) { 12678c2ecf20Sopenharmony_ci snd_fm801_free(chip); 12688c2ecf20Sopenharmony_ci return err; 12698c2ecf20Sopenharmony_ci } 12708c2ecf20Sopenharmony_ci chip->tea.v4l2_dev = &chip->v4l2_dev; 12718c2ecf20Sopenharmony_ci chip->tea.radio_nr = radio_nr; 12728c2ecf20Sopenharmony_ci chip->tea.private_data = chip; 12738c2ecf20Sopenharmony_ci chip->tea.ops = &snd_fm801_tea_ops; 12748c2ecf20Sopenharmony_ci sprintf(chip->tea.bus_info, "PCI:%s", pci_name(pci)); 12758c2ecf20Sopenharmony_ci if ((chip->tea575x_tuner & TUNER_TYPE_MASK) > 0 && 12768c2ecf20Sopenharmony_ci (chip->tea575x_tuner & TUNER_TYPE_MASK) < 4) { 12778c2ecf20Sopenharmony_ci if (snd_tea575x_init(&chip->tea, THIS_MODULE)) { 12788c2ecf20Sopenharmony_ci dev_err(card->dev, "TEA575x radio not found\n"); 12798c2ecf20Sopenharmony_ci snd_fm801_free(chip); 12808c2ecf20Sopenharmony_ci return -ENODEV; 12818c2ecf20Sopenharmony_ci } 12828c2ecf20Sopenharmony_ci } else if ((chip->tea575x_tuner & TUNER_TYPE_MASK) == 0) { 12838c2ecf20Sopenharmony_ci unsigned int tuner_only = chip->tea575x_tuner & TUNER_ONLY; 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci /* autodetect tuner connection */ 12868c2ecf20Sopenharmony_ci for (tea575x_tuner = 1; tea575x_tuner <= 3; tea575x_tuner++) { 12878c2ecf20Sopenharmony_ci chip->tea575x_tuner = tea575x_tuner; 12888c2ecf20Sopenharmony_ci if (!snd_tea575x_init(&chip->tea, THIS_MODULE)) { 12898c2ecf20Sopenharmony_ci dev_info(card->dev, 12908c2ecf20Sopenharmony_ci "detected TEA575x radio type %s\n", 12918c2ecf20Sopenharmony_ci get_tea575x_gpio(chip)->name); 12928c2ecf20Sopenharmony_ci break; 12938c2ecf20Sopenharmony_ci } 12948c2ecf20Sopenharmony_ci } 12958c2ecf20Sopenharmony_ci if (tea575x_tuner == 4) { 12968c2ecf20Sopenharmony_ci dev_err(card->dev, "TEA575x radio not found\n"); 12978c2ecf20Sopenharmony_ci chip->tea575x_tuner = TUNER_DISABLED; 12988c2ecf20Sopenharmony_ci } 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci chip->tea575x_tuner |= tuner_only; 13018c2ecf20Sopenharmony_ci } 13028c2ecf20Sopenharmony_ci if (!(chip->tea575x_tuner & TUNER_DISABLED)) { 13038c2ecf20Sopenharmony_ci strlcpy(chip->tea.card, get_tea575x_gpio(chip)->name, 13048c2ecf20Sopenharmony_ci sizeof(chip->tea.card)); 13058c2ecf20Sopenharmony_ci } 13068c2ecf20Sopenharmony_ci#endif 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci *rchip = chip; 13098c2ecf20Sopenharmony_ci return 0; 13108c2ecf20Sopenharmony_ci} 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_cistatic int snd_card_fm801_probe(struct pci_dev *pci, 13138c2ecf20Sopenharmony_ci const struct pci_device_id *pci_id) 13148c2ecf20Sopenharmony_ci{ 13158c2ecf20Sopenharmony_ci static int dev; 13168c2ecf20Sopenharmony_ci struct snd_card *card; 13178c2ecf20Sopenharmony_ci struct fm801 *chip; 13188c2ecf20Sopenharmony_ci struct snd_opl3 *opl3; 13198c2ecf20Sopenharmony_ci int err; 13208c2ecf20Sopenharmony_ci 13218c2ecf20Sopenharmony_ci if (dev >= SNDRV_CARDS) 13228c2ecf20Sopenharmony_ci return -ENODEV; 13238c2ecf20Sopenharmony_ci if (!enable[dev]) { 13248c2ecf20Sopenharmony_ci dev++; 13258c2ecf20Sopenharmony_ci return -ENOENT; 13268c2ecf20Sopenharmony_ci } 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE, 13298c2ecf20Sopenharmony_ci 0, &card); 13308c2ecf20Sopenharmony_ci if (err < 0) 13318c2ecf20Sopenharmony_ci return err; 13328c2ecf20Sopenharmony_ci if ((err = snd_fm801_create(card, pci, tea575x_tuner[dev], radio_nr[dev], &chip)) < 0) { 13338c2ecf20Sopenharmony_ci snd_card_free(card); 13348c2ecf20Sopenharmony_ci return err; 13358c2ecf20Sopenharmony_ci } 13368c2ecf20Sopenharmony_ci card->private_data = chip; 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ci strcpy(card->driver, "FM801"); 13398c2ecf20Sopenharmony_ci strcpy(card->shortname, "ForteMedia FM801-"); 13408c2ecf20Sopenharmony_ci strcat(card->shortname, chip->multichannel ? "AU" : "AS"); 13418c2ecf20Sopenharmony_ci sprintf(card->longname, "%s at 0x%lx, irq %i", 13428c2ecf20Sopenharmony_ci card->shortname, chip->port, chip->irq); 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_ci if (chip->tea575x_tuner & TUNER_ONLY) 13458c2ecf20Sopenharmony_ci goto __fm801_tuner_only; 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_ci if ((err = snd_fm801_pcm(chip, 0)) < 0) { 13488c2ecf20Sopenharmony_ci snd_card_free(card); 13498c2ecf20Sopenharmony_ci return err; 13508c2ecf20Sopenharmony_ci } 13518c2ecf20Sopenharmony_ci if ((err = snd_fm801_mixer(chip)) < 0) { 13528c2ecf20Sopenharmony_ci snd_card_free(card); 13538c2ecf20Sopenharmony_ci return err; 13548c2ecf20Sopenharmony_ci } 13558c2ecf20Sopenharmony_ci if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_FM801, 13568c2ecf20Sopenharmony_ci chip->port + FM801_MPU401_DATA, 13578c2ecf20Sopenharmony_ci MPU401_INFO_INTEGRATED | 13588c2ecf20Sopenharmony_ci MPU401_INFO_IRQ_HOOK, 13598c2ecf20Sopenharmony_ci -1, &chip->rmidi)) < 0) { 13608c2ecf20Sopenharmony_ci snd_card_free(card); 13618c2ecf20Sopenharmony_ci return err; 13628c2ecf20Sopenharmony_ci } 13638c2ecf20Sopenharmony_ci if ((err = snd_opl3_create(card, chip->port + FM801_OPL3_BANK0, 13648c2ecf20Sopenharmony_ci chip->port + FM801_OPL3_BANK1, 13658c2ecf20Sopenharmony_ci OPL3_HW_OPL3_FM801, 1, &opl3)) < 0) { 13668c2ecf20Sopenharmony_ci snd_card_free(card); 13678c2ecf20Sopenharmony_ci return err; 13688c2ecf20Sopenharmony_ci } 13698c2ecf20Sopenharmony_ci if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { 13708c2ecf20Sopenharmony_ci snd_card_free(card); 13718c2ecf20Sopenharmony_ci return err; 13728c2ecf20Sopenharmony_ci } 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ci __fm801_tuner_only: 13758c2ecf20Sopenharmony_ci if ((err = snd_card_register(card)) < 0) { 13768c2ecf20Sopenharmony_ci snd_card_free(card); 13778c2ecf20Sopenharmony_ci return err; 13788c2ecf20Sopenharmony_ci } 13798c2ecf20Sopenharmony_ci pci_set_drvdata(pci, card); 13808c2ecf20Sopenharmony_ci dev++; 13818c2ecf20Sopenharmony_ci return 0; 13828c2ecf20Sopenharmony_ci} 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_cistatic void snd_card_fm801_remove(struct pci_dev *pci) 13858c2ecf20Sopenharmony_ci{ 13868c2ecf20Sopenharmony_ci snd_card_free(pci_get_drvdata(pci)); 13878c2ecf20Sopenharmony_ci} 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 13908c2ecf20Sopenharmony_cistatic const unsigned char saved_regs[] = { 13918c2ecf20Sopenharmony_ci FM801_PCM_VOL, FM801_I2S_VOL, FM801_FM_VOL, FM801_REC_SRC, 13928c2ecf20Sopenharmony_ci FM801_PLY_CTRL, FM801_PLY_COUNT, FM801_PLY_BUF1, FM801_PLY_BUF2, 13938c2ecf20Sopenharmony_ci FM801_CAP_CTRL, FM801_CAP_COUNT, FM801_CAP_BUF1, FM801_CAP_BUF2, 13948c2ecf20Sopenharmony_ci FM801_CODEC_CTRL, FM801_I2S_MODE, FM801_VOLUME, FM801_GEN_CTRL, 13958c2ecf20Sopenharmony_ci}; 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_cistatic int snd_fm801_suspend(struct device *dev) 13988c2ecf20Sopenharmony_ci{ 13998c2ecf20Sopenharmony_ci struct snd_card *card = dev_get_drvdata(dev); 14008c2ecf20Sopenharmony_ci struct fm801 *chip = card->private_data; 14018c2ecf20Sopenharmony_ci int i; 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_ci snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(saved_regs); i++) 14068c2ecf20Sopenharmony_ci chip->saved_regs[i] = fm801_ioread16(chip, saved_regs[i]); 14078c2ecf20Sopenharmony_ci 14088c2ecf20Sopenharmony_ci if (chip->tea575x_tuner & TUNER_ONLY) { 14098c2ecf20Sopenharmony_ci /* FIXME: tea575x suspend */ 14108c2ecf20Sopenharmony_ci } else { 14118c2ecf20Sopenharmony_ci snd_ac97_suspend(chip->ac97); 14128c2ecf20Sopenharmony_ci snd_ac97_suspend(chip->ac97_sec); 14138c2ecf20Sopenharmony_ci } 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_ci return 0; 14168c2ecf20Sopenharmony_ci} 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_cistatic int snd_fm801_resume(struct device *dev) 14198c2ecf20Sopenharmony_ci{ 14208c2ecf20Sopenharmony_ci struct snd_card *card = dev_get_drvdata(dev); 14218c2ecf20Sopenharmony_ci struct fm801 *chip = card->private_data; 14228c2ecf20Sopenharmony_ci int i; 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_ci if (chip->tea575x_tuner & TUNER_ONLY) { 14258c2ecf20Sopenharmony_ci snd_fm801_chip_init(chip); 14268c2ecf20Sopenharmony_ci } else { 14278c2ecf20Sopenharmony_ci reset_codec(chip); 14288c2ecf20Sopenharmony_ci snd_fm801_chip_multichannel_init(chip); 14298c2ecf20Sopenharmony_ci snd_fm801_chip_init(chip); 14308c2ecf20Sopenharmony_ci snd_ac97_resume(chip->ac97); 14318c2ecf20Sopenharmony_ci snd_ac97_resume(chip->ac97_sec); 14328c2ecf20Sopenharmony_ci } 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(saved_regs); i++) 14358c2ecf20Sopenharmony_ci fm801_iowrite16(chip, saved_regs[i], chip->saved_regs[i]); 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_FM801_TEA575X_BOOL 14388c2ecf20Sopenharmony_ci if (!(chip->tea575x_tuner & TUNER_DISABLED)) 14398c2ecf20Sopenharmony_ci snd_tea575x_set_freq(&chip->tea); 14408c2ecf20Sopenharmony_ci#endif 14418c2ecf20Sopenharmony_ci 14428c2ecf20Sopenharmony_ci snd_power_change_state(card, SNDRV_CTL_POWER_D0); 14438c2ecf20Sopenharmony_ci return 0; 14448c2ecf20Sopenharmony_ci} 14458c2ecf20Sopenharmony_ci 14468c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(snd_fm801_pm, snd_fm801_suspend, snd_fm801_resume); 14478c2ecf20Sopenharmony_ci#define SND_FM801_PM_OPS &snd_fm801_pm 14488c2ecf20Sopenharmony_ci#else 14498c2ecf20Sopenharmony_ci#define SND_FM801_PM_OPS NULL 14508c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_cistatic struct pci_driver fm801_driver = { 14538c2ecf20Sopenharmony_ci .name = KBUILD_MODNAME, 14548c2ecf20Sopenharmony_ci .id_table = snd_fm801_ids, 14558c2ecf20Sopenharmony_ci .probe = snd_card_fm801_probe, 14568c2ecf20Sopenharmony_ci .remove = snd_card_fm801_remove, 14578c2ecf20Sopenharmony_ci .driver = { 14588c2ecf20Sopenharmony_ci .pm = SND_FM801_PM_OPS, 14598c2ecf20Sopenharmony_ci }, 14608c2ecf20Sopenharmony_ci}; 14618c2ecf20Sopenharmony_ci 14628c2ecf20Sopenharmony_cimodule_pci_driver(fm801_driver); 1463