18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 48c2ecf20Sopenharmony_ci * Routines for control of ESS ES1688/688/488 chip 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/init.h> 88c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 98c2ecf20Sopenharmony_ci#include <linux/delay.h> 108c2ecf20Sopenharmony_ci#include <linux/slab.h> 118c2ecf20Sopenharmony_ci#include <linux/ioport.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/io.h> 148c2ecf20Sopenharmony_ci#include <sound/core.h> 158c2ecf20Sopenharmony_ci#include <sound/es1688.h> 168c2ecf20Sopenharmony_ci#include <sound/initval.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <asm/dma.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); 218c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ESS ESx688 lowlevel module"); 228c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic int snd_es1688_dsp_command(struct snd_es1688 *chip, unsigned char val) 258c2ecf20Sopenharmony_ci{ 268c2ecf20Sopenharmony_ci int i; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci for (i = 10000; i; i--) 298c2ecf20Sopenharmony_ci if ((inb(ES1688P(chip, STATUS)) & 0x80) == 0) { 308c2ecf20Sopenharmony_ci outb(val, ES1688P(chip, COMMAND)); 318c2ecf20Sopenharmony_ci return 1; 328c2ecf20Sopenharmony_ci } 338c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_DEBUG 348c2ecf20Sopenharmony_ci printk(KERN_DEBUG "snd_es1688_dsp_command: timeout (0x%x)\n", val); 358c2ecf20Sopenharmony_ci#endif 368c2ecf20Sopenharmony_ci return 0; 378c2ecf20Sopenharmony_ci} 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic int snd_es1688_dsp_get_byte(struct snd_es1688 *chip) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci int i; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci for (i = 1000; i; i--) 448c2ecf20Sopenharmony_ci if (inb(ES1688P(chip, DATA_AVAIL)) & 0x80) 458c2ecf20Sopenharmony_ci return inb(ES1688P(chip, READ)); 468c2ecf20Sopenharmony_ci snd_printd("es1688 get byte failed: 0x%lx = 0x%x!!!\n", ES1688P(chip, DATA_AVAIL), inb(ES1688P(chip, DATA_AVAIL))); 478c2ecf20Sopenharmony_ci return -ENODEV; 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic int snd_es1688_write(struct snd_es1688 *chip, 518c2ecf20Sopenharmony_ci unsigned char reg, unsigned char data) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci if (!snd_es1688_dsp_command(chip, reg)) 548c2ecf20Sopenharmony_ci return 0; 558c2ecf20Sopenharmony_ci return snd_es1688_dsp_command(chip, data); 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistatic int snd_es1688_read(struct snd_es1688 *chip, unsigned char reg) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci /* Read a byte from an extended mode register of ES1688 */ 618c2ecf20Sopenharmony_ci if (!snd_es1688_dsp_command(chip, 0xc0)) 628c2ecf20Sopenharmony_ci return -1; 638c2ecf20Sopenharmony_ci if (!snd_es1688_dsp_command(chip, reg)) 648c2ecf20Sopenharmony_ci return -1; 658c2ecf20Sopenharmony_ci return snd_es1688_dsp_get_byte(chip); 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_civoid snd_es1688_mixer_write(struct snd_es1688 *chip, 698c2ecf20Sopenharmony_ci unsigned char reg, unsigned char data) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci outb(reg, ES1688P(chip, MIXER_ADDR)); 728c2ecf20Sopenharmony_ci udelay(10); 738c2ecf20Sopenharmony_ci outb(data, ES1688P(chip, MIXER_DATA)); 748c2ecf20Sopenharmony_ci udelay(10); 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic unsigned char snd_es1688_mixer_read(struct snd_es1688 *chip, unsigned char reg) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci unsigned char result; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci outb(reg, ES1688P(chip, MIXER_ADDR)); 828c2ecf20Sopenharmony_ci udelay(10); 838c2ecf20Sopenharmony_ci result = inb(ES1688P(chip, MIXER_DATA)); 848c2ecf20Sopenharmony_ci udelay(10); 858c2ecf20Sopenharmony_ci return result; 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ciint snd_es1688_reset(struct snd_es1688 *chip) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci int i; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci outb(3, ES1688P(chip, RESET)); /* valid only for ESS chips, SB -> 1 */ 938c2ecf20Sopenharmony_ci udelay(10); 948c2ecf20Sopenharmony_ci outb(0, ES1688P(chip, RESET)); 958c2ecf20Sopenharmony_ci udelay(30); 968c2ecf20Sopenharmony_ci for (i = 0; i < 1000 && !(inb(ES1688P(chip, DATA_AVAIL)) & 0x80); i++); 978c2ecf20Sopenharmony_ci if (inb(ES1688P(chip, READ)) != 0xaa) { 988c2ecf20Sopenharmony_ci snd_printd("ess_reset at 0x%lx: failed!!!\n", chip->port); 998c2ecf20Sopenharmony_ci return -ENODEV; 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci snd_es1688_dsp_command(chip, 0xc6); /* enable extended mode */ 1028c2ecf20Sopenharmony_ci return 0; 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_es1688_reset); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic int snd_es1688_probe(struct snd_es1688 *chip) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci unsigned long flags; 1098c2ecf20Sopenharmony_ci unsigned short major, minor; 1108c2ecf20Sopenharmony_ci int i; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci /* 1138c2ecf20Sopenharmony_ci * initialization sequence 1148c2ecf20Sopenharmony_ci */ 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); /* Some ESS1688 cards need this */ 1178c2ecf20Sopenharmony_ci inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */ 1188c2ecf20Sopenharmony_ci inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */ 1198c2ecf20Sopenharmony_ci inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */ 1208c2ecf20Sopenharmony_ci inb(ES1688P(chip, ENABLE2)); /* ENABLE2 */ 1218c2ecf20Sopenharmony_ci inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */ 1228c2ecf20Sopenharmony_ci inb(ES1688P(chip, ENABLE2)); /* ENABLE2 */ 1238c2ecf20Sopenharmony_ci inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */ 1248c2ecf20Sopenharmony_ci inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */ 1258c2ecf20Sopenharmony_ci inb(ES1688P(chip, ENABLE2)); /* ENABLE2 */ 1268c2ecf20Sopenharmony_ci inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */ 1278c2ecf20Sopenharmony_ci inb(ES1688P(chip, ENABLE0)); /* ENABLE0 */ 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci if (snd_es1688_reset(chip) < 0) { 1308c2ecf20Sopenharmony_ci snd_printdd("ESS: [0x%lx] reset failed... 0x%x\n", chip->port, inb(ES1688P(chip, READ))); 1318c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 1328c2ecf20Sopenharmony_ci return -ENODEV; 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci snd_es1688_dsp_command(chip, 0xe7); /* return identification */ 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci for (i = 1000, major = minor = 0; i; i--) { 1378c2ecf20Sopenharmony_ci if (inb(ES1688P(chip, DATA_AVAIL)) & 0x80) { 1388c2ecf20Sopenharmony_ci if (major == 0) { 1398c2ecf20Sopenharmony_ci major = inb(ES1688P(chip, READ)); 1408c2ecf20Sopenharmony_ci } else { 1418c2ecf20Sopenharmony_ci minor = inb(ES1688P(chip, READ)); 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci snd_printdd("ESS: [0x%lx] found.. major = 0x%x, minor = 0x%x\n", chip->port, major, minor); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci chip->version = (major << 8) | minor; 1518c2ecf20Sopenharmony_ci if (!chip->version) 1528c2ecf20Sopenharmony_ci return -ENODEV; /* probably SB */ 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci switch (chip->version & 0xfff0) { 1558c2ecf20Sopenharmony_ci case 0x4880: 1568c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "[0x%lx] ESS: AudioDrive ES488 detected, " 1578c2ecf20Sopenharmony_ci "but driver is in another place\n", chip->port); 1588c2ecf20Sopenharmony_ci return -ENODEV; 1598c2ecf20Sopenharmony_ci case 0x6880: 1608c2ecf20Sopenharmony_ci break; 1618c2ecf20Sopenharmony_ci default: 1628c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "[0x%lx] ESS: unknown AudioDrive chip " 1638c2ecf20Sopenharmony_ci "with version 0x%x (Jazz16 soundcard?)\n", 1648c2ecf20Sopenharmony_ci chip->port, chip->version); 1658c2ecf20Sopenharmony_ci return -ENODEV; 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 1698c2ecf20Sopenharmony_ci snd_es1688_write(chip, 0xb1, 0x10); /* disable IRQ */ 1708c2ecf20Sopenharmony_ci snd_es1688_write(chip, 0xb2, 0x00); /* disable DMA */ 1718c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci /* enable joystick, but disable OPL3 */ 1748c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->mixer_lock, flags); 1758c2ecf20Sopenharmony_ci snd_es1688_mixer_write(chip, 0x40, 0x01); 1768c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->mixer_lock, flags); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci return 0; 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic int snd_es1688_init(struct snd_es1688 * chip, int enable) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci static const int irqs[16] = {-1, -1, 0, -1, -1, 1, -1, 2, -1, 0, 3, -1, -1, -1, -1, -1}; 1848c2ecf20Sopenharmony_ci unsigned long flags; 1858c2ecf20Sopenharmony_ci int cfg, irq_bits, dma, dma_bits, tmp, tmp1; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci /* ok.. setup MPU-401 port and joystick and OPL3 */ 1888c2ecf20Sopenharmony_ci cfg = 0x01; /* enable joystick, but disable OPL3 */ 1898c2ecf20Sopenharmony_ci if (enable && chip->mpu_port >= 0x300 && chip->mpu_irq > 0 && chip->hardware != ES1688_HW_688) { 1908c2ecf20Sopenharmony_ci tmp = (chip->mpu_port & 0x0f0) >> 4; 1918c2ecf20Sopenharmony_ci if (tmp <= 3) { 1928c2ecf20Sopenharmony_ci switch (chip->mpu_irq) { 1938c2ecf20Sopenharmony_ci case 9: 1948c2ecf20Sopenharmony_ci tmp1 = 4; 1958c2ecf20Sopenharmony_ci break; 1968c2ecf20Sopenharmony_ci case 5: 1978c2ecf20Sopenharmony_ci tmp1 = 5; 1988c2ecf20Sopenharmony_ci break; 1998c2ecf20Sopenharmony_ci case 7: 2008c2ecf20Sopenharmony_ci tmp1 = 6; 2018c2ecf20Sopenharmony_ci break; 2028c2ecf20Sopenharmony_ci case 10: 2038c2ecf20Sopenharmony_ci tmp1 = 7; 2048c2ecf20Sopenharmony_ci break; 2058c2ecf20Sopenharmony_ci default: 2068c2ecf20Sopenharmony_ci tmp1 = 0; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci if (tmp1) { 2098c2ecf20Sopenharmony_ci cfg |= (tmp << 3) | (tmp1 << 5); 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci#if 0 2148c2ecf20Sopenharmony_ci snd_printk(KERN_DEBUG "mpu cfg = 0x%x\n", cfg); 2158c2ecf20Sopenharmony_ci#endif 2168c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 2178c2ecf20Sopenharmony_ci snd_es1688_mixer_write(chip, 0x40, cfg); 2188c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 2198c2ecf20Sopenharmony_ci /* --- */ 2208c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 2218c2ecf20Sopenharmony_ci snd_es1688_read(chip, 0xb1); 2228c2ecf20Sopenharmony_ci snd_es1688_read(chip, 0xb2); 2238c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 2248c2ecf20Sopenharmony_ci if (enable) { 2258c2ecf20Sopenharmony_ci cfg = 0xf0; /* enable only DMA counter interrupt */ 2268c2ecf20Sopenharmony_ci irq_bits = irqs[chip->irq & 0x0f]; 2278c2ecf20Sopenharmony_ci if (irq_bits < 0) { 2288c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "[0x%lx] ESS: bad IRQ %d " 2298c2ecf20Sopenharmony_ci "for ES1688 chip!!\n", 2308c2ecf20Sopenharmony_ci chip->port, chip->irq); 2318c2ecf20Sopenharmony_ci#if 0 2328c2ecf20Sopenharmony_ci irq_bits = 0; 2338c2ecf20Sopenharmony_ci cfg = 0x10; 2348c2ecf20Sopenharmony_ci#endif 2358c2ecf20Sopenharmony_ci return -EINVAL; 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 2388c2ecf20Sopenharmony_ci snd_es1688_write(chip, 0xb1, cfg | (irq_bits << 2)); 2398c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 2408c2ecf20Sopenharmony_ci cfg = 0xf0; /* extended mode DMA enable */ 2418c2ecf20Sopenharmony_ci dma = chip->dma8; 2428c2ecf20Sopenharmony_ci if (dma > 3 || dma == 2) { 2438c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "[0x%lx] ESS: bad DMA channel %d " 2448c2ecf20Sopenharmony_ci "for ES1688 chip!!\n", chip->port, dma); 2458c2ecf20Sopenharmony_ci#if 0 2468c2ecf20Sopenharmony_ci dma_bits = 0; 2478c2ecf20Sopenharmony_ci cfg = 0x00; /* disable all DMA */ 2488c2ecf20Sopenharmony_ci#endif 2498c2ecf20Sopenharmony_ci return -EINVAL; 2508c2ecf20Sopenharmony_ci } else { 2518c2ecf20Sopenharmony_ci dma_bits = dma; 2528c2ecf20Sopenharmony_ci if (dma != 3) 2538c2ecf20Sopenharmony_ci dma_bits++; 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 2568c2ecf20Sopenharmony_ci snd_es1688_write(chip, 0xb2, cfg | (dma_bits << 2)); 2578c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 2588c2ecf20Sopenharmony_ci } else { 2598c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 2608c2ecf20Sopenharmony_ci snd_es1688_write(chip, 0xb1, 0x10); /* disable IRQ */ 2618c2ecf20Sopenharmony_ci snd_es1688_write(chip, 0xb2, 0x00); /* disable DMA */ 2628c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 2658c2ecf20Sopenharmony_ci snd_es1688_read(chip, 0xb1); 2668c2ecf20Sopenharmony_ci snd_es1688_read(chip, 0xb2); 2678c2ecf20Sopenharmony_ci snd_es1688_reset(chip); 2688c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 2698c2ecf20Sopenharmony_ci return 0; 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci/* 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci */ 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cistatic const struct snd_ratnum clocks[2] = { 2778c2ecf20Sopenharmony_ci { 2788c2ecf20Sopenharmony_ci .num = 795444, 2798c2ecf20Sopenharmony_ci .den_min = 1, 2808c2ecf20Sopenharmony_ci .den_max = 128, 2818c2ecf20Sopenharmony_ci .den_step = 1, 2828c2ecf20Sopenharmony_ci }, 2838c2ecf20Sopenharmony_ci { 2848c2ecf20Sopenharmony_ci .num = 397722, 2858c2ecf20Sopenharmony_ci .den_min = 1, 2868c2ecf20Sopenharmony_ci .den_max = 128, 2878c2ecf20Sopenharmony_ci .den_step = 1, 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci}; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cistatic const struct snd_pcm_hw_constraint_ratnums hw_constraints_clocks = { 2928c2ecf20Sopenharmony_ci .nrats = 2, 2938c2ecf20Sopenharmony_ci .rats = clocks, 2948c2ecf20Sopenharmony_ci}; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_cistatic void snd_es1688_set_rate(struct snd_es1688 *chip, struct snd_pcm_substream *substream) 2978c2ecf20Sopenharmony_ci{ 2988c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 2998c2ecf20Sopenharmony_ci unsigned int bits, divider; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci if (runtime->rate_num == clocks[0].num) 3028c2ecf20Sopenharmony_ci bits = 256 - runtime->rate_den; 3038c2ecf20Sopenharmony_ci else 3048c2ecf20Sopenharmony_ci bits = 128 - runtime->rate_den; 3058c2ecf20Sopenharmony_ci /* set filter register */ 3068c2ecf20Sopenharmony_ci divider = 256 - 7160000*20/(8*82*runtime->rate); 3078c2ecf20Sopenharmony_ci /* write result to hardware */ 3088c2ecf20Sopenharmony_ci snd_es1688_write(chip, 0xa1, bits); 3098c2ecf20Sopenharmony_ci snd_es1688_write(chip, 0xa2, divider); 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cistatic int snd_es1688_trigger(struct snd_es1688 *chip, int cmd, unsigned char value) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci int val; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci if (cmd == SNDRV_PCM_TRIGGER_STOP) { 3178c2ecf20Sopenharmony_ci value = 0x00; 3188c2ecf20Sopenharmony_ci } else if (cmd != SNDRV_PCM_TRIGGER_START) { 3198c2ecf20Sopenharmony_ci return -EINVAL; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci spin_lock(&chip->reg_lock); 3228c2ecf20Sopenharmony_ci chip->trigger_value = value; 3238c2ecf20Sopenharmony_ci val = snd_es1688_read(chip, 0xb8); 3248c2ecf20Sopenharmony_ci if ((val < 0) || (val & 0x0f) == value) { 3258c2ecf20Sopenharmony_ci spin_unlock(&chip->reg_lock); 3268c2ecf20Sopenharmony_ci return -EINVAL; /* something is wrong */ 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci#if 0 3298c2ecf20Sopenharmony_ci printk(KERN_DEBUG "trigger: val = 0x%x, value = 0x%x\n", val, value); 3308c2ecf20Sopenharmony_ci printk(KERN_DEBUG "trigger: pointer = 0x%x\n", 3318c2ecf20Sopenharmony_ci snd_dma_pointer(chip->dma8, chip->dma_size)); 3328c2ecf20Sopenharmony_ci#endif 3338c2ecf20Sopenharmony_ci snd_es1688_write(chip, 0xb8, (val & 0xf0) | value); 3348c2ecf20Sopenharmony_ci spin_unlock(&chip->reg_lock); 3358c2ecf20Sopenharmony_ci return 0; 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic int snd_es1688_playback_prepare(struct snd_pcm_substream *substream) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci unsigned long flags; 3418c2ecf20Sopenharmony_ci struct snd_es1688 *chip = snd_pcm_substream_chip(substream); 3428c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 3438c2ecf20Sopenharmony_ci unsigned int size = snd_pcm_lib_buffer_bytes(substream); 3448c2ecf20Sopenharmony_ci unsigned int count = snd_pcm_lib_period_bytes(substream); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci chip->dma_size = size; 3478c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 3488c2ecf20Sopenharmony_ci snd_es1688_reset(chip); 3498c2ecf20Sopenharmony_ci snd_es1688_set_rate(chip, substream); 3508c2ecf20Sopenharmony_ci snd_es1688_write(chip, 0xb8, 4); /* auto init DMA mode */ 3518c2ecf20Sopenharmony_ci snd_es1688_write(chip, 0xa8, (snd_es1688_read(chip, 0xa8) & ~0x03) | (3 - runtime->channels)); 3528c2ecf20Sopenharmony_ci snd_es1688_write(chip, 0xb9, 2); /* demand mode (4 bytes/request) */ 3538c2ecf20Sopenharmony_ci if (runtime->channels == 1) { 3548c2ecf20Sopenharmony_ci if (snd_pcm_format_width(runtime->format) == 8) { 3558c2ecf20Sopenharmony_ci /* 8. bit mono */ 3568c2ecf20Sopenharmony_ci snd_es1688_write(chip, 0xb6, 0x80); 3578c2ecf20Sopenharmony_ci snd_es1688_write(chip, 0xb7, 0x51); 3588c2ecf20Sopenharmony_ci snd_es1688_write(chip, 0xb7, 0xd0); 3598c2ecf20Sopenharmony_ci } else { 3608c2ecf20Sopenharmony_ci /* 16. bit mono */ 3618c2ecf20Sopenharmony_ci snd_es1688_write(chip, 0xb6, 0x00); 3628c2ecf20Sopenharmony_ci snd_es1688_write(chip, 0xb7, 0x71); 3638c2ecf20Sopenharmony_ci snd_es1688_write(chip, 0xb7, 0xf4); 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci } else { 3668c2ecf20Sopenharmony_ci if (snd_pcm_format_width(runtime->format) == 8) { 3678c2ecf20Sopenharmony_ci /* 8. bit stereo */ 3688c2ecf20Sopenharmony_ci snd_es1688_write(chip, 0xb6, 0x80); 3698c2ecf20Sopenharmony_ci snd_es1688_write(chip, 0xb7, 0x51); 3708c2ecf20Sopenharmony_ci snd_es1688_write(chip, 0xb7, 0x98); 3718c2ecf20Sopenharmony_ci } else { 3728c2ecf20Sopenharmony_ci /* 16. bit stereo */ 3738c2ecf20Sopenharmony_ci snd_es1688_write(chip, 0xb6, 0x00); 3748c2ecf20Sopenharmony_ci snd_es1688_write(chip, 0xb7, 0x71); 3758c2ecf20Sopenharmony_ci snd_es1688_write(chip, 0xb7, 0xbc); 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci snd_es1688_write(chip, 0xb1, (snd_es1688_read(chip, 0xb1) & 0x0f) | 0x50); 3798c2ecf20Sopenharmony_ci snd_es1688_write(chip, 0xb2, (snd_es1688_read(chip, 0xb2) & 0x0f) | 0x50); 3808c2ecf20Sopenharmony_ci snd_es1688_dsp_command(chip, ES1688_DSP_CMD_SPKON); 3818c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 3828c2ecf20Sopenharmony_ci /* --- */ 3838c2ecf20Sopenharmony_ci count = -count; 3848c2ecf20Sopenharmony_ci snd_dma_program(chip->dma8, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT); 3858c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 3868c2ecf20Sopenharmony_ci snd_es1688_write(chip, 0xa4, (unsigned char) count); 3878c2ecf20Sopenharmony_ci snd_es1688_write(chip, 0xa5, (unsigned char) (count >> 8)); 3888c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 3898c2ecf20Sopenharmony_ci return 0; 3908c2ecf20Sopenharmony_ci} 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_cistatic int snd_es1688_playback_trigger(struct snd_pcm_substream *substream, 3938c2ecf20Sopenharmony_ci int cmd) 3948c2ecf20Sopenharmony_ci{ 3958c2ecf20Sopenharmony_ci struct snd_es1688 *chip = snd_pcm_substream_chip(substream); 3968c2ecf20Sopenharmony_ci return snd_es1688_trigger(chip, cmd, 0x05); 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_cistatic int snd_es1688_capture_prepare(struct snd_pcm_substream *substream) 4008c2ecf20Sopenharmony_ci{ 4018c2ecf20Sopenharmony_ci unsigned long flags; 4028c2ecf20Sopenharmony_ci struct snd_es1688 *chip = snd_pcm_substream_chip(substream); 4038c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 4048c2ecf20Sopenharmony_ci unsigned int size = snd_pcm_lib_buffer_bytes(substream); 4058c2ecf20Sopenharmony_ci unsigned int count = snd_pcm_lib_period_bytes(substream); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci chip->dma_size = size; 4088c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 4098c2ecf20Sopenharmony_ci snd_es1688_reset(chip); 4108c2ecf20Sopenharmony_ci snd_es1688_set_rate(chip, substream); 4118c2ecf20Sopenharmony_ci snd_es1688_dsp_command(chip, ES1688_DSP_CMD_SPKOFF); 4128c2ecf20Sopenharmony_ci snd_es1688_write(chip, 0xb8, 0x0e); /* auto init DMA mode */ 4138c2ecf20Sopenharmony_ci snd_es1688_write(chip, 0xa8, (snd_es1688_read(chip, 0xa8) & ~0x03) | (3 - runtime->channels)); 4148c2ecf20Sopenharmony_ci snd_es1688_write(chip, 0xb9, 2); /* demand mode (4 bytes/request) */ 4158c2ecf20Sopenharmony_ci if (runtime->channels == 1) { 4168c2ecf20Sopenharmony_ci if (snd_pcm_format_width(runtime->format) == 8) { 4178c2ecf20Sopenharmony_ci /* 8. bit mono */ 4188c2ecf20Sopenharmony_ci snd_es1688_write(chip, 0xb7, 0x51); 4198c2ecf20Sopenharmony_ci snd_es1688_write(chip, 0xb7, 0xd0); 4208c2ecf20Sopenharmony_ci } else { 4218c2ecf20Sopenharmony_ci /* 16. bit mono */ 4228c2ecf20Sopenharmony_ci snd_es1688_write(chip, 0xb7, 0x71); 4238c2ecf20Sopenharmony_ci snd_es1688_write(chip, 0xb7, 0xf4); 4248c2ecf20Sopenharmony_ci } 4258c2ecf20Sopenharmony_ci } else { 4268c2ecf20Sopenharmony_ci if (snd_pcm_format_width(runtime->format) == 8) { 4278c2ecf20Sopenharmony_ci /* 8. bit stereo */ 4288c2ecf20Sopenharmony_ci snd_es1688_write(chip, 0xb7, 0x51); 4298c2ecf20Sopenharmony_ci snd_es1688_write(chip, 0xb7, 0x98); 4308c2ecf20Sopenharmony_ci } else { 4318c2ecf20Sopenharmony_ci /* 16. bit stereo */ 4328c2ecf20Sopenharmony_ci snd_es1688_write(chip, 0xb7, 0x71); 4338c2ecf20Sopenharmony_ci snd_es1688_write(chip, 0xb7, 0xbc); 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci snd_es1688_write(chip, 0xb1, (snd_es1688_read(chip, 0xb1) & 0x0f) | 0x50); 4378c2ecf20Sopenharmony_ci snd_es1688_write(chip, 0xb2, (snd_es1688_read(chip, 0xb2) & 0x0f) | 0x50); 4388c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 4398c2ecf20Sopenharmony_ci /* --- */ 4408c2ecf20Sopenharmony_ci count = -count; 4418c2ecf20Sopenharmony_ci snd_dma_program(chip->dma8, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT); 4428c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 4438c2ecf20Sopenharmony_ci snd_es1688_write(chip, 0xa4, (unsigned char) count); 4448c2ecf20Sopenharmony_ci snd_es1688_write(chip, 0xa5, (unsigned char) (count >> 8)); 4458c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 4468c2ecf20Sopenharmony_ci return 0; 4478c2ecf20Sopenharmony_ci} 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_cistatic int snd_es1688_capture_trigger(struct snd_pcm_substream *substream, 4508c2ecf20Sopenharmony_ci int cmd) 4518c2ecf20Sopenharmony_ci{ 4528c2ecf20Sopenharmony_ci struct snd_es1688 *chip = snd_pcm_substream_chip(substream); 4538c2ecf20Sopenharmony_ci return snd_es1688_trigger(chip, cmd, 0x0f); 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_cistatic irqreturn_t snd_es1688_interrupt(int irq, void *dev_id) 4578c2ecf20Sopenharmony_ci{ 4588c2ecf20Sopenharmony_ci struct snd_es1688 *chip = dev_id; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci if (chip->trigger_value == 0x05) /* ok.. playback is active */ 4618c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(chip->playback_substream); 4628c2ecf20Sopenharmony_ci if (chip->trigger_value == 0x0f) /* ok.. capture is active */ 4638c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(chip->capture_substream); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci inb(ES1688P(chip, DATA_AVAIL)); /* ack interrupt */ 4668c2ecf20Sopenharmony_ci return IRQ_HANDLED; 4678c2ecf20Sopenharmony_ci} 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t snd_es1688_playback_pointer(struct snd_pcm_substream *substream) 4708c2ecf20Sopenharmony_ci{ 4718c2ecf20Sopenharmony_ci struct snd_es1688 *chip = snd_pcm_substream_chip(substream); 4728c2ecf20Sopenharmony_ci size_t ptr; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci if (chip->trigger_value != 0x05) 4758c2ecf20Sopenharmony_ci return 0; 4768c2ecf20Sopenharmony_ci ptr = snd_dma_pointer(chip->dma8, chip->dma_size); 4778c2ecf20Sopenharmony_ci return bytes_to_frames(substream->runtime, ptr); 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t snd_es1688_capture_pointer(struct snd_pcm_substream *substream) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci struct snd_es1688 *chip = snd_pcm_substream_chip(substream); 4838c2ecf20Sopenharmony_ci size_t ptr; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci if (chip->trigger_value != 0x0f) 4868c2ecf20Sopenharmony_ci return 0; 4878c2ecf20Sopenharmony_ci ptr = snd_dma_pointer(chip->dma8, chip->dma_size); 4888c2ecf20Sopenharmony_ci return bytes_to_frames(substream->runtime, ptr); 4898c2ecf20Sopenharmony_ci} 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci/* 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci */ 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_es1688_playback = 4968c2ecf20Sopenharmony_ci{ 4978c2ecf20Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 4988c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID), 4998c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, 5008c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, 5018c2ecf20Sopenharmony_ci .rate_min = 4000, 5028c2ecf20Sopenharmony_ci .rate_max = 48000, 5038c2ecf20Sopenharmony_ci .channels_min = 1, 5048c2ecf20Sopenharmony_ci .channels_max = 2, 5058c2ecf20Sopenharmony_ci .buffer_bytes_max = 65536, 5068c2ecf20Sopenharmony_ci .period_bytes_min = 64, 5078c2ecf20Sopenharmony_ci .period_bytes_max = 65536, 5088c2ecf20Sopenharmony_ci .periods_min = 1, 5098c2ecf20Sopenharmony_ci .periods_max = 1024, 5108c2ecf20Sopenharmony_ci .fifo_size = 0, 5118c2ecf20Sopenharmony_ci}; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_es1688_capture = 5148c2ecf20Sopenharmony_ci{ 5158c2ecf20Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 5168c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID), 5178c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, 5188c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, 5198c2ecf20Sopenharmony_ci .rate_min = 4000, 5208c2ecf20Sopenharmony_ci .rate_max = 48000, 5218c2ecf20Sopenharmony_ci .channels_min = 1, 5228c2ecf20Sopenharmony_ci .channels_max = 2, 5238c2ecf20Sopenharmony_ci .buffer_bytes_max = 65536, 5248c2ecf20Sopenharmony_ci .period_bytes_min = 64, 5258c2ecf20Sopenharmony_ci .period_bytes_max = 65536, 5268c2ecf20Sopenharmony_ci .periods_min = 1, 5278c2ecf20Sopenharmony_ci .periods_max = 1024, 5288c2ecf20Sopenharmony_ci .fifo_size = 0, 5298c2ecf20Sopenharmony_ci}; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci/* 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci */ 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_cistatic int snd_es1688_playback_open(struct snd_pcm_substream *substream) 5368c2ecf20Sopenharmony_ci{ 5378c2ecf20Sopenharmony_ci struct snd_es1688 *chip = snd_pcm_substream_chip(substream); 5388c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci if (chip->capture_substream != NULL) 5418c2ecf20Sopenharmony_ci return -EAGAIN; 5428c2ecf20Sopenharmony_ci chip->playback_substream = substream; 5438c2ecf20Sopenharmony_ci runtime->hw = snd_es1688_playback; 5448c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, 5458c2ecf20Sopenharmony_ci &hw_constraints_clocks); 5468c2ecf20Sopenharmony_ci return 0; 5478c2ecf20Sopenharmony_ci} 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_cistatic int snd_es1688_capture_open(struct snd_pcm_substream *substream) 5508c2ecf20Sopenharmony_ci{ 5518c2ecf20Sopenharmony_ci struct snd_es1688 *chip = snd_pcm_substream_chip(substream); 5528c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci if (chip->playback_substream != NULL) 5558c2ecf20Sopenharmony_ci return -EAGAIN; 5568c2ecf20Sopenharmony_ci chip->capture_substream = substream; 5578c2ecf20Sopenharmony_ci runtime->hw = snd_es1688_capture; 5588c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, 5598c2ecf20Sopenharmony_ci &hw_constraints_clocks); 5608c2ecf20Sopenharmony_ci return 0; 5618c2ecf20Sopenharmony_ci} 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_cistatic int snd_es1688_playback_close(struct snd_pcm_substream *substream) 5648c2ecf20Sopenharmony_ci{ 5658c2ecf20Sopenharmony_ci struct snd_es1688 *chip = snd_pcm_substream_chip(substream); 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci chip->playback_substream = NULL; 5688c2ecf20Sopenharmony_ci return 0; 5698c2ecf20Sopenharmony_ci} 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_cistatic int snd_es1688_capture_close(struct snd_pcm_substream *substream) 5728c2ecf20Sopenharmony_ci{ 5738c2ecf20Sopenharmony_ci struct snd_es1688 *chip = snd_pcm_substream_chip(substream); 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci chip->capture_substream = NULL; 5768c2ecf20Sopenharmony_ci return 0; 5778c2ecf20Sopenharmony_ci} 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_cistatic int snd_es1688_free(struct snd_es1688 *chip) 5808c2ecf20Sopenharmony_ci{ 5818c2ecf20Sopenharmony_ci if (chip->hardware != ES1688_HW_UNDEF) 5828c2ecf20Sopenharmony_ci snd_es1688_init(chip, 0); 5838c2ecf20Sopenharmony_ci release_and_free_resource(chip->res_port); 5848c2ecf20Sopenharmony_ci if (chip->irq >= 0) 5858c2ecf20Sopenharmony_ci free_irq(chip->irq, (void *) chip); 5868c2ecf20Sopenharmony_ci if (chip->dma8 >= 0) { 5878c2ecf20Sopenharmony_ci disable_dma(chip->dma8); 5888c2ecf20Sopenharmony_ci free_dma(chip->dma8); 5898c2ecf20Sopenharmony_ci } 5908c2ecf20Sopenharmony_ci return 0; 5918c2ecf20Sopenharmony_ci} 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_cistatic int snd_es1688_dev_free(struct snd_device *device) 5948c2ecf20Sopenharmony_ci{ 5958c2ecf20Sopenharmony_ci struct snd_es1688 *chip = device->device_data; 5968c2ecf20Sopenharmony_ci return snd_es1688_free(chip); 5978c2ecf20Sopenharmony_ci} 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_cistatic const char *snd_es1688_chip_id(struct snd_es1688 *chip) 6008c2ecf20Sopenharmony_ci{ 6018c2ecf20Sopenharmony_ci static char tmp[16]; 6028c2ecf20Sopenharmony_ci sprintf(tmp, "ES%s688 rev %i", chip->hardware == ES1688_HW_688 ? "" : "1", chip->version & 0x0f); 6038c2ecf20Sopenharmony_ci return tmp; 6048c2ecf20Sopenharmony_ci} 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ciint snd_es1688_create(struct snd_card *card, 6078c2ecf20Sopenharmony_ci struct snd_es1688 *chip, 6088c2ecf20Sopenharmony_ci unsigned long port, 6098c2ecf20Sopenharmony_ci unsigned long mpu_port, 6108c2ecf20Sopenharmony_ci int irq, 6118c2ecf20Sopenharmony_ci int mpu_irq, 6128c2ecf20Sopenharmony_ci int dma8, 6138c2ecf20Sopenharmony_ci unsigned short hardware) 6148c2ecf20Sopenharmony_ci{ 6158c2ecf20Sopenharmony_ci static const struct snd_device_ops ops = { 6168c2ecf20Sopenharmony_ci .dev_free = snd_es1688_dev_free, 6178c2ecf20Sopenharmony_ci }; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci int err; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci if (chip == NULL) 6228c2ecf20Sopenharmony_ci return -ENOMEM; 6238c2ecf20Sopenharmony_ci chip->irq = -1; 6248c2ecf20Sopenharmony_ci chip->dma8 = -1; 6258c2ecf20Sopenharmony_ci chip->hardware = ES1688_HW_UNDEF; 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci chip->res_port = request_region(port + 4, 12, "ES1688"); 6288c2ecf20Sopenharmony_ci if (chip->res_port == NULL) { 6298c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "es1688: can't grab port 0x%lx\n", port + 4); 6308c2ecf20Sopenharmony_ci err = -EBUSY; 6318c2ecf20Sopenharmony_ci goto exit; 6328c2ecf20Sopenharmony_ci } 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci err = request_irq(irq, snd_es1688_interrupt, 0, "ES1688", (void *) chip); 6358c2ecf20Sopenharmony_ci if (err < 0) { 6368c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "es1688: can't grab IRQ %d\n", irq); 6378c2ecf20Sopenharmony_ci goto exit; 6388c2ecf20Sopenharmony_ci } 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci chip->irq = irq; 6418c2ecf20Sopenharmony_ci card->sync_irq = chip->irq; 6428c2ecf20Sopenharmony_ci err = request_dma(dma8, "ES1688"); 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci if (err < 0) { 6458c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "es1688: can't grab DMA8 %d\n", dma8); 6468c2ecf20Sopenharmony_ci goto exit; 6478c2ecf20Sopenharmony_ci } 6488c2ecf20Sopenharmony_ci chip->dma8 = dma8; 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci spin_lock_init(&chip->reg_lock); 6518c2ecf20Sopenharmony_ci spin_lock_init(&chip->mixer_lock); 6528c2ecf20Sopenharmony_ci chip->port = port; 6538c2ecf20Sopenharmony_ci mpu_port &= ~0x000f; 6548c2ecf20Sopenharmony_ci if (mpu_port < 0x300 || mpu_port > 0x330) 6558c2ecf20Sopenharmony_ci mpu_port = 0; 6568c2ecf20Sopenharmony_ci chip->mpu_port = mpu_port; 6578c2ecf20Sopenharmony_ci chip->mpu_irq = mpu_irq; 6588c2ecf20Sopenharmony_ci chip->hardware = hardware; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci err = snd_es1688_probe(chip); 6618c2ecf20Sopenharmony_ci if (err < 0) 6628c2ecf20Sopenharmony_ci goto exit; 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci err = snd_es1688_init(chip, 1); 6658c2ecf20Sopenharmony_ci if (err < 0) 6668c2ecf20Sopenharmony_ci goto exit; 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci /* Register device */ 6698c2ecf20Sopenharmony_ci err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); 6708c2ecf20Sopenharmony_ciexit: 6718c2ecf20Sopenharmony_ci if (err) 6728c2ecf20Sopenharmony_ci snd_es1688_free(chip); 6738c2ecf20Sopenharmony_ci return err; 6748c2ecf20Sopenharmony_ci} 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_es1688_playback_ops = { 6778c2ecf20Sopenharmony_ci .open = snd_es1688_playback_open, 6788c2ecf20Sopenharmony_ci .close = snd_es1688_playback_close, 6798c2ecf20Sopenharmony_ci .prepare = snd_es1688_playback_prepare, 6808c2ecf20Sopenharmony_ci .trigger = snd_es1688_playback_trigger, 6818c2ecf20Sopenharmony_ci .pointer = snd_es1688_playback_pointer, 6828c2ecf20Sopenharmony_ci}; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_es1688_capture_ops = { 6858c2ecf20Sopenharmony_ci .open = snd_es1688_capture_open, 6868c2ecf20Sopenharmony_ci .close = snd_es1688_capture_close, 6878c2ecf20Sopenharmony_ci .prepare = snd_es1688_capture_prepare, 6888c2ecf20Sopenharmony_ci .trigger = snd_es1688_capture_trigger, 6898c2ecf20Sopenharmony_ci .pointer = snd_es1688_capture_pointer, 6908c2ecf20Sopenharmony_ci}; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ciint snd_es1688_pcm(struct snd_card *card, struct snd_es1688 *chip, int device) 6938c2ecf20Sopenharmony_ci{ 6948c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 6958c2ecf20Sopenharmony_ci int err; 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci err = snd_pcm_new(card, "ESx688", device, 1, 1, &pcm); 6988c2ecf20Sopenharmony_ci if (err < 0) 6998c2ecf20Sopenharmony_ci return err; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_es1688_playback_ops); 7028c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_es1688_capture_ops); 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci pcm->private_data = chip; 7058c2ecf20Sopenharmony_ci pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX; 7068c2ecf20Sopenharmony_ci strcpy(pcm->name, snd_es1688_chip_id(chip)); 7078c2ecf20Sopenharmony_ci chip->pcm = pcm; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, card->dev, 7108c2ecf20Sopenharmony_ci 64*1024, 64*1024); 7118c2ecf20Sopenharmony_ci return 0; 7128c2ecf20Sopenharmony_ci} 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci/* 7158c2ecf20Sopenharmony_ci * MIXER part 7168c2ecf20Sopenharmony_ci */ 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_cistatic int snd_es1688_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) 7198c2ecf20Sopenharmony_ci{ 7208c2ecf20Sopenharmony_ci static const char * const texts[8] = { 7218c2ecf20Sopenharmony_ci "Mic", "Mic Master", "CD", "AOUT", 7228c2ecf20Sopenharmony_ci "Mic1", "Mix", "Line", "Master" 7238c2ecf20Sopenharmony_ci }; 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci return snd_ctl_enum_info(uinfo, 1, 8, texts); 7268c2ecf20Sopenharmony_ci} 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_cistatic int snd_es1688_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 7298c2ecf20Sopenharmony_ci{ 7308c2ecf20Sopenharmony_ci struct snd_es1688 *chip = snd_kcontrol_chip(kcontrol); 7318c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = snd_es1688_mixer_read(chip, ES1688_REC_DEV) & 7; 7328c2ecf20Sopenharmony_ci return 0; 7338c2ecf20Sopenharmony_ci} 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_cistatic int snd_es1688_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 7368c2ecf20Sopenharmony_ci{ 7378c2ecf20Sopenharmony_ci struct snd_es1688 *chip = snd_kcontrol_chip(kcontrol); 7388c2ecf20Sopenharmony_ci unsigned long flags; 7398c2ecf20Sopenharmony_ci unsigned char oval, nval; 7408c2ecf20Sopenharmony_ci int change; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci if (ucontrol->value.enumerated.item[0] > 8) 7438c2ecf20Sopenharmony_ci return -EINVAL; 7448c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 7458c2ecf20Sopenharmony_ci oval = snd_es1688_mixer_read(chip, ES1688_REC_DEV); 7468c2ecf20Sopenharmony_ci nval = (ucontrol->value.enumerated.item[0] & 7) | (oval & ~15); 7478c2ecf20Sopenharmony_ci change = nval != oval; 7488c2ecf20Sopenharmony_ci if (change) 7498c2ecf20Sopenharmony_ci snd_es1688_mixer_write(chip, ES1688_REC_DEV, nval); 7508c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 7518c2ecf20Sopenharmony_ci return change; 7528c2ecf20Sopenharmony_ci} 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci#define ES1688_SINGLE(xname, xindex, reg, shift, mask, invert) \ 7558c2ecf20Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ 7568c2ecf20Sopenharmony_ci .info = snd_es1688_info_single, \ 7578c2ecf20Sopenharmony_ci .get = snd_es1688_get_single, .put = snd_es1688_put_single, \ 7588c2ecf20Sopenharmony_ci .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) } 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_cistatic int snd_es1688_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) 7618c2ecf20Sopenharmony_ci{ 7628c2ecf20Sopenharmony_ci int mask = (kcontrol->private_value >> 16) & 0xff; 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; 7658c2ecf20Sopenharmony_ci uinfo->count = 1; 7668c2ecf20Sopenharmony_ci uinfo->value.integer.min = 0; 7678c2ecf20Sopenharmony_ci uinfo->value.integer.max = mask; 7688c2ecf20Sopenharmony_ci return 0; 7698c2ecf20Sopenharmony_ci} 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_cistatic int snd_es1688_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 7728c2ecf20Sopenharmony_ci{ 7738c2ecf20Sopenharmony_ci struct snd_es1688 *chip = snd_kcontrol_chip(kcontrol); 7748c2ecf20Sopenharmony_ci unsigned long flags; 7758c2ecf20Sopenharmony_ci int reg = kcontrol->private_value & 0xff; 7768c2ecf20Sopenharmony_ci int shift = (kcontrol->private_value >> 8) & 0xff; 7778c2ecf20Sopenharmony_ci int mask = (kcontrol->private_value >> 16) & 0xff; 7788c2ecf20Sopenharmony_ci int invert = (kcontrol->private_value >> 24) & 0xff; 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 7818c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = (snd_es1688_mixer_read(chip, reg) >> shift) & mask; 7828c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 7838c2ecf20Sopenharmony_ci if (invert) 7848c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; 7858c2ecf20Sopenharmony_ci return 0; 7868c2ecf20Sopenharmony_ci} 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_cistatic int snd_es1688_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 7898c2ecf20Sopenharmony_ci{ 7908c2ecf20Sopenharmony_ci struct snd_es1688 *chip = snd_kcontrol_chip(kcontrol); 7918c2ecf20Sopenharmony_ci unsigned long flags; 7928c2ecf20Sopenharmony_ci int reg = kcontrol->private_value & 0xff; 7938c2ecf20Sopenharmony_ci int shift = (kcontrol->private_value >> 8) & 0xff; 7948c2ecf20Sopenharmony_ci int mask = (kcontrol->private_value >> 16) & 0xff; 7958c2ecf20Sopenharmony_ci int invert = (kcontrol->private_value >> 24) & 0xff; 7968c2ecf20Sopenharmony_ci int change; 7978c2ecf20Sopenharmony_ci unsigned char oval, nval; 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci nval = (ucontrol->value.integer.value[0] & mask); 8008c2ecf20Sopenharmony_ci if (invert) 8018c2ecf20Sopenharmony_ci nval = mask - nval; 8028c2ecf20Sopenharmony_ci nval <<= shift; 8038c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 8048c2ecf20Sopenharmony_ci oval = snd_es1688_mixer_read(chip, reg); 8058c2ecf20Sopenharmony_ci nval = (oval & ~(mask << shift)) | nval; 8068c2ecf20Sopenharmony_ci change = nval != oval; 8078c2ecf20Sopenharmony_ci if (change) 8088c2ecf20Sopenharmony_ci snd_es1688_mixer_write(chip, reg, nval); 8098c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 8108c2ecf20Sopenharmony_ci return change; 8118c2ecf20Sopenharmony_ci} 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci#define ES1688_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ 8148c2ecf20Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ 8158c2ecf20Sopenharmony_ci .info = snd_es1688_info_double, \ 8168c2ecf20Sopenharmony_ci .get = snd_es1688_get_double, .put = snd_es1688_put_double, \ 8178c2ecf20Sopenharmony_ci .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_cistatic int snd_es1688_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) 8208c2ecf20Sopenharmony_ci{ 8218c2ecf20Sopenharmony_ci int mask = (kcontrol->private_value >> 24) & 0xff; 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; 8248c2ecf20Sopenharmony_ci uinfo->count = 2; 8258c2ecf20Sopenharmony_ci uinfo->value.integer.min = 0; 8268c2ecf20Sopenharmony_ci uinfo->value.integer.max = mask; 8278c2ecf20Sopenharmony_ci return 0; 8288c2ecf20Sopenharmony_ci} 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_cistatic int snd_es1688_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 8318c2ecf20Sopenharmony_ci{ 8328c2ecf20Sopenharmony_ci struct snd_es1688 *chip = snd_kcontrol_chip(kcontrol); 8338c2ecf20Sopenharmony_ci unsigned long flags; 8348c2ecf20Sopenharmony_ci int left_reg = kcontrol->private_value & 0xff; 8358c2ecf20Sopenharmony_ci int right_reg = (kcontrol->private_value >> 8) & 0xff; 8368c2ecf20Sopenharmony_ci int shift_left = (kcontrol->private_value >> 16) & 0x07; 8378c2ecf20Sopenharmony_ci int shift_right = (kcontrol->private_value >> 19) & 0x07; 8388c2ecf20Sopenharmony_ci int mask = (kcontrol->private_value >> 24) & 0xff; 8398c2ecf20Sopenharmony_ci int invert = (kcontrol->private_value >> 22) & 1; 8408c2ecf20Sopenharmony_ci unsigned char left, right; 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 8438c2ecf20Sopenharmony_ci if (left_reg < 0xa0) 8448c2ecf20Sopenharmony_ci left = snd_es1688_mixer_read(chip, left_reg); 8458c2ecf20Sopenharmony_ci else 8468c2ecf20Sopenharmony_ci left = snd_es1688_read(chip, left_reg); 8478c2ecf20Sopenharmony_ci if (left_reg != right_reg) { 8488c2ecf20Sopenharmony_ci if (right_reg < 0xa0) 8498c2ecf20Sopenharmony_ci right = snd_es1688_mixer_read(chip, right_reg); 8508c2ecf20Sopenharmony_ci else 8518c2ecf20Sopenharmony_ci right = snd_es1688_read(chip, right_reg); 8528c2ecf20Sopenharmony_ci } else 8538c2ecf20Sopenharmony_ci right = left; 8548c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 8558c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = (left >> shift_left) & mask; 8568c2ecf20Sopenharmony_ci ucontrol->value.integer.value[1] = (right >> shift_right) & mask; 8578c2ecf20Sopenharmony_ci if (invert) { 8588c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; 8598c2ecf20Sopenharmony_ci ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; 8608c2ecf20Sopenharmony_ci } 8618c2ecf20Sopenharmony_ci return 0; 8628c2ecf20Sopenharmony_ci} 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_cistatic int snd_es1688_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 8658c2ecf20Sopenharmony_ci{ 8668c2ecf20Sopenharmony_ci struct snd_es1688 *chip = snd_kcontrol_chip(kcontrol); 8678c2ecf20Sopenharmony_ci unsigned long flags; 8688c2ecf20Sopenharmony_ci int left_reg = kcontrol->private_value & 0xff; 8698c2ecf20Sopenharmony_ci int right_reg = (kcontrol->private_value >> 8) & 0xff; 8708c2ecf20Sopenharmony_ci int shift_left = (kcontrol->private_value >> 16) & 0x07; 8718c2ecf20Sopenharmony_ci int shift_right = (kcontrol->private_value >> 19) & 0x07; 8728c2ecf20Sopenharmony_ci int mask = (kcontrol->private_value >> 24) & 0xff; 8738c2ecf20Sopenharmony_ci int invert = (kcontrol->private_value >> 22) & 1; 8748c2ecf20Sopenharmony_ci int change; 8758c2ecf20Sopenharmony_ci unsigned char val1, val2, oval1, oval2; 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci val1 = ucontrol->value.integer.value[0] & mask; 8788c2ecf20Sopenharmony_ci val2 = ucontrol->value.integer.value[1] & mask; 8798c2ecf20Sopenharmony_ci if (invert) { 8808c2ecf20Sopenharmony_ci val1 = mask - val1; 8818c2ecf20Sopenharmony_ci val2 = mask - val2; 8828c2ecf20Sopenharmony_ci } 8838c2ecf20Sopenharmony_ci val1 <<= shift_left; 8848c2ecf20Sopenharmony_ci val2 <<= shift_right; 8858c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 8868c2ecf20Sopenharmony_ci if (left_reg != right_reg) { 8878c2ecf20Sopenharmony_ci if (left_reg < 0xa0) 8888c2ecf20Sopenharmony_ci oval1 = snd_es1688_mixer_read(chip, left_reg); 8898c2ecf20Sopenharmony_ci else 8908c2ecf20Sopenharmony_ci oval1 = snd_es1688_read(chip, left_reg); 8918c2ecf20Sopenharmony_ci if (right_reg < 0xa0) 8928c2ecf20Sopenharmony_ci oval2 = snd_es1688_mixer_read(chip, right_reg); 8938c2ecf20Sopenharmony_ci else 8948c2ecf20Sopenharmony_ci oval2 = snd_es1688_read(chip, right_reg); 8958c2ecf20Sopenharmony_ci val1 = (oval1 & ~(mask << shift_left)) | val1; 8968c2ecf20Sopenharmony_ci val2 = (oval2 & ~(mask << shift_right)) | val2; 8978c2ecf20Sopenharmony_ci change = val1 != oval1 || val2 != oval2; 8988c2ecf20Sopenharmony_ci if (change) { 8998c2ecf20Sopenharmony_ci if (left_reg < 0xa0) 9008c2ecf20Sopenharmony_ci snd_es1688_mixer_write(chip, left_reg, val1); 9018c2ecf20Sopenharmony_ci else 9028c2ecf20Sopenharmony_ci snd_es1688_write(chip, left_reg, val1); 9038c2ecf20Sopenharmony_ci if (right_reg < 0xa0) 9048c2ecf20Sopenharmony_ci snd_es1688_mixer_write(chip, right_reg, val1); 9058c2ecf20Sopenharmony_ci else 9068c2ecf20Sopenharmony_ci snd_es1688_write(chip, right_reg, val1); 9078c2ecf20Sopenharmony_ci } 9088c2ecf20Sopenharmony_ci } else { 9098c2ecf20Sopenharmony_ci if (left_reg < 0xa0) 9108c2ecf20Sopenharmony_ci oval1 = snd_es1688_mixer_read(chip, left_reg); 9118c2ecf20Sopenharmony_ci else 9128c2ecf20Sopenharmony_ci oval1 = snd_es1688_read(chip, left_reg); 9138c2ecf20Sopenharmony_ci val1 = (oval1 & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2; 9148c2ecf20Sopenharmony_ci change = val1 != oval1; 9158c2ecf20Sopenharmony_ci if (change) { 9168c2ecf20Sopenharmony_ci if (left_reg < 0xa0) 9178c2ecf20Sopenharmony_ci snd_es1688_mixer_write(chip, left_reg, val1); 9188c2ecf20Sopenharmony_ci else 9198c2ecf20Sopenharmony_ci snd_es1688_write(chip, left_reg, val1); 9208c2ecf20Sopenharmony_ci } 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci } 9238c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 9248c2ecf20Sopenharmony_ci return change; 9258c2ecf20Sopenharmony_ci} 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_es1688_controls[] = { 9288c2ecf20Sopenharmony_ciES1688_DOUBLE("Master Playback Volume", 0, ES1688_MASTER_DEV, ES1688_MASTER_DEV, 4, 0, 15, 0), 9298c2ecf20Sopenharmony_ciES1688_DOUBLE("PCM Playback Volume", 0, ES1688_PCM_DEV, ES1688_PCM_DEV, 4, 0, 15, 0), 9308c2ecf20Sopenharmony_ciES1688_DOUBLE("Line Playback Volume", 0, ES1688_LINE_DEV, ES1688_LINE_DEV, 4, 0, 15, 0), 9318c2ecf20Sopenharmony_ciES1688_DOUBLE("CD Playback Volume", 0, ES1688_CD_DEV, ES1688_CD_DEV, 4, 0, 15, 0), 9328c2ecf20Sopenharmony_ciES1688_DOUBLE("FM Playback Volume", 0, ES1688_FM_DEV, ES1688_FM_DEV, 4, 0, 15, 0), 9338c2ecf20Sopenharmony_ciES1688_DOUBLE("Mic Playback Volume", 0, ES1688_MIC_DEV, ES1688_MIC_DEV, 4, 0, 15, 0), 9348c2ecf20Sopenharmony_ciES1688_DOUBLE("Aux Playback Volume", 0, ES1688_AUX_DEV, ES1688_AUX_DEV, 4, 0, 15, 0), 9358c2ecf20Sopenharmony_ciES1688_SINGLE("Beep Playback Volume", 0, ES1688_SPEAKER_DEV, 0, 7, 0), 9368c2ecf20Sopenharmony_ciES1688_DOUBLE("Capture Volume", 0, ES1688_RECLEV_DEV, ES1688_RECLEV_DEV, 4, 0, 15, 0), 9378c2ecf20Sopenharmony_ciES1688_SINGLE("Capture Switch", 0, ES1688_REC_DEV, 4, 1, 1), 9388c2ecf20Sopenharmony_ci{ 9398c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 9408c2ecf20Sopenharmony_ci .name = "Capture Source", 9418c2ecf20Sopenharmony_ci .info = snd_es1688_info_mux, 9428c2ecf20Sopenharmony_ci .get = snd_es1688_get_mux, 9438c2ecf20Sopenharmony_ci .put = snd_es1688_put_mux, 9448c2ecf20Sopenharmony_ci}, 9458c2ecf20Sopenharmony_ci}; 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci#define ES1688_INIT_TABLE_SIZE (sizeof(snd_es1688_init_table)/2) 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_cistatic const unsigned char snd_es1688_init_table[][2] = { 9508c2ecf20Sopenharmony_ci { ES1688_MASTER_DEV, 0 }, 9518c2ecf20Sopenharmony_ci { ES1688_PCM_DEV, 0 }, 9528c2ecf20Sopenharmony_ci { ES1688_LINE_DEV, 0 }, 9538c2ecf20Sopenharmony_ci { ES1688_CD_DEV, 0 }, 9548c2ecf20Sopenharmony_ci { ES1688_FM_DEV, 0 }, 9558c2ecf20Sopenharmony_ci { ES1688_MIC_DEV, 0 }, 9568c2ecf20Sopenharmony_ci { ES1688_AUX_DEV, 0 }, 9578c2ecf20Sopenharmony_ci { ES1688_SPEAKER_DEV, 0 }, 9588c2ecf20Sopenharmony_ci { ES1688_RECLEV_DEV, 0 }, 9598c2ecf20Sopenharmony_ci { ES1688_REC_DEV, 0x17 } 9608c2ecf20Sopenharmony_ci}; 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ciint snd_es1688_mixer(struct snd_card *card, struct snd_es1688 *chip) 9638c2ecf20Sopenharmony_ci{ 9648c2ecf20Sopenharmony_ci unsigned int idx; 9658c2ecf20Sopenharmony_ci int err; 9668c2ecf20Sopenharmony_ci unsigned char reg, val; 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci if (snd_BUG_ON(!chip || !card)) 9698c2ecf20Sopenharmony_ci return -EINVAL; 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci strcpy(card->mixername, snd_es1688_chip_id(chip)); 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(snd_es1688_controls); idx++) { 9748c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es1688_controls[idx], chip))) < 0) 9758c2ecf20Sopenharmony_ci return err; 9768c2ecf20Sopenharmony_ci } 9778c2ecf20Sopenharmony_ci for (idx = 0; idx < ES1688_INIT_TABLE_SIZE; idx++) { 9788c2ecf20Sopenharmony_ci reg = snd_es1688_init_table[idx][0]; 9798c2ecf20Sopenharmony_ci val = snd_es1688_init_table[idx][1]; 9808c2ecf20Sopenharmony_ci if (reg < 0xa0) 9818c2ecf20Sopenharmony_ci snd_es1688_mixer_write(chip, reg, val); 9828c2ecf20Sopenharmony_ci else 9838c2ecf20Sopenharmony_ci snd_es1688_write(chip, reg, val); 9848c2ecf20Sopenharmony_ci } 9858c2ecf20Sopenharmony_ci return 0; 9868c2ecf20Sopenharmony_ci} 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_es1688_mixer_write); 9898c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_es1688_create); 9908c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_es1688_pcm); 9918c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_es1688_mixer); 992