162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 462306a36Sopenharmony_ci * Routines for control of ESS ES1688/688/488 chip 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/init.h> 862306a36Sopenharmony_ci#include <linux/interrupt.h> 962306a36Sopenharmony_ci#include <linux/delay.h> 1062306a36Sopenharmony_ci#include <linux/slab.h> 1162306a36Sopenharmony_ci#include <linux/ioport.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/io.h> 1462306a36Sopenharmony_ci#include <sound/core.h> 1562306a36Sopenharmony_ci#include <sound/es1688.h> 1662306a36Sopenharmony_ci#include <sound/initval.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <asm/dma.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ciMODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); 2162306a36Sopenharmony_ciMODULE_DESCRIPTION("ESS ESx688 lowlevel module"); 2262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic int snd_es1688_dsp_command(struct snd_es1688 *chip, unsigned char val) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci int i; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci for (i = 10000; i; i--) 2962306a36Sopenharmony_ci if ((inb(ES1688P(chip, STATUS)) & 0x80) == 0) { 3062306a36Sopenharmony_ci outb(val, ES1688P(chip, COMMAND)); 3162306a36Sopenharmony_ci return 1; 3262306a36Sopenharmony_ci } 3362306a36Sopenharmony_ci#ifdef CONFIG_SND_DEBUG 3462306a36Sopenharmony_ci printk(KERN_DEBUG "snd_es1688_dsp_command: timeout (0x%x)\n", val); 3562306a36Sopenharmony_ci#endif 3662306a36Sopenharmony_ci return 0; 3762306a36Sopenharmony_ci} 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic int snd_es1688_dsp_get_byte(struct snd_es1688 *chip) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci int i; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci for (i = 1000; i; i--) 4462306a36Sopenharmony_ci if (inb(ES1688P(chip, DATA_AVAIL)) & 0x80) 4562306a36Sopenharmony_ci return inb(ES1688P(chip, READ)); 4662306a36Sopenharmony_ci snd_printd("es1688 get byte failed: 0x%lx = 0x%x!!!\n", ES1688P(chip, DATA_AVAIL), inb(ES1688P(chip, DATA_AVAIL))); 4762306a36Sopenharmony_ci return -ENODEV; 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic int snd_es1688_write(struct snd_es1688 *chip, 5162306a36Sopenharmony_ci unsigned char reg, unsigned char data) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci if (!snd_es1688_dsp_command(chip, reg)) 5462306a36Sopenharmony_ci return 0; 5562306a36Sopenharmony_ci return snd_es1688_dsp_command(chip, data); 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic int snd_es1688_read(struct snd_es1688 *chip, unsigned char reg) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci /* Read a byte from an extended mode register of ES1688 */ 6162306a36Sopenharmony_ci if (!snd_es1688_dsp_command(chip, 0xc0)) 6262306a36Sopenharmony_ci return -1; 6362306a36Sopenharmony_ci if (!snd_es1688_dsp_command(chip, reg)) 6462306a36Sopenharmony_ci return -1; 6562306a36Sopenharmony_ci return snd_es1688_dsp_get_byte(chip); 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_civoid snd_es1688_mixer_write(struct snd_es1688 *chip, 6962306a36Sopenharmony_ci unsigned char reg, unsigned char data) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci outb(reg, ES1688P(chip, MIXER_ADDR)); 7262306a36Sopenharmony_ci udelay(10); 7362306a36Sopenharmony_ci outb(data, ES1688P(chip, MIXER_DATA)); 7462306a36Sopenharmony_ci udelay(10); 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic unsigned char snd_es1688_mixer_read(struct snd_es1688 *chip, unsigned char reg) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci unsigned char result; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci outb(reg, ES1688P(chip, MIXER_ADDR)); 8262306a36Sopenharmony_ci udelay(10); 8362306a36Sopenharmony_ci result = inb(ES1688P(chip, MIXER_DATA)); 8462306a36Sopenharmony_ci udelay(10); 8562306a36Sopenharmony_ci return result; 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ciint snd_es1688_reset(struct snd_es1688 *chip) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci int i; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci outb(3, ES1688P(chip, RESET)); /* valid only for ESS chips, SB -> 1 */ 9362306a36Sopenharmony_ci udelay(10); 9462306a36Sopenharmony_ci outb(0, ES1688P(chip, RESET)); 9562306a36Sopenharmony_ci udelay(30); 9662306a36Sopenharmony_ci for (i = 0; i < 1000 && !(inb(ES1688P(chip, DATA_AVAIL)) & 0x80); i++); 9762306a36Sopenharmony_ci if (inb(ES1688P(chip, READ)) != 0xaa) { 9862306a36Sopenharmony_ci snd_printd("ess_reset at 0x%lx: failed!!!\n", chip->port); 9962306a36Sopenharmony_ci return -ENODEV; 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci snd_es1688_dsp_command(chip, 0xc6); /* enable extended mode */ 10262306a36Sopenharmony_ci return 0; 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ciEXPORT_SYMBOL(snd_es1688_reset); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistatic int snd_es1688_probe(struct snd_es1688 *chip) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci unsigned long flags; 10962306a36Sopenharmony_ci unsigned short major, minor; 11062306a36Sopenharmony_ci int i; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci /* 11362306a36Sopenharmony_ci * initialization sequence 11462306a36Sopenharmony_ci */ 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); /* Some ESS1688 cards need this */ 11762306a36Sopenharmony_ci inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */ 11862306a36Sopenharmony_ci inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */ 11962306a36Sopenharmony_ci inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */ 12062306a36Sopenharmony_ci inb(ES1688P(chip, ENABLE2)); /* ENABLE2 */ 12162306a36Sopenharmony_ci inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */ 12262306a36Sopenharmony_ci inb(ES1688P(chip, ENABLE2)); /* ENABLE2 */ 12362306a36Sopenharmony_ci inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */ 12462306a36Sopenharmony_ci inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */ 12562306a36Sopenharmony_ci inb(ES1688P(chip, ENABLE2)); /* ENABLE2 */ 12662306a36Sopenharmony_ci inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */ 12762306a36Sopenharmony_ci inb(ES1688P(chip, ENABLE0)); /* ENABLE0 */ 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci if (snd_es1688_reset(chip) < 0) { 13062306a36Sopenharmony_ci snd_printdd("ESS: [0x%lx] reset failed... 0x%x\n", chip->port, inb(ES1688P(chip, READ))); 13162306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 13262306a36Sopenharmony_ci return -ENODEV; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci snd_es1688_dsp_command(chip, 0xe7); /* return identification */ 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci for (i = 1000, major = minor = 0; i; i--) { 13762306a36Sopenharmony_ci if (inb(ES1688P(chip, DATA_AVAIL)) & 0x80) { 13862306a36Sopenharmony_ci if (major == 0) { 13962306a36Sopenharmony_ci major = inb(ES1688P(chip, READ)); 14062306a36Sopenharmony_ci } else { 14162306a36Sopenharmony_ci minor = inb(ES1688P(chip, READ)); 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci snd_printdd("ESS: [0x%lx] found.. major = 0x%x, minor = 0x%x\n", chip->port, major, minor); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci chip->version = (major << 8) | minor; 15162306a36Sopenharmony_ci if (!chip->version) 15262306a36Sopenharmony_ci return -ENODEV; /* probably SB */ 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci switch (chip->version & 0xfff0) { 15562306a36Sopenharmony_ci case 0x4880: 15662306a36Sopenharmony_ci snd_printk(KERN_ERR "[0x%lx] ESS: AudioDrive ES488 detected, " 15762306a36Sopenharmony_ci "but driver is in another place\n", chip->port); 15862306a36Sopenharmony_ci return -ENODEV; 15962306a36Sopenharmony_ci case 0x6880: 16062306a36Sopenharmony_ci break; 16162306a36Sopenharmony_ci default: 16262306a36Sopenharmony_ci snd_printk(KERN_ERR "[0x%lx] ESS: unknown AudioDrive chip " 16362306a36Sopenharmony_ci "with version 0x%x (Jazz16 soundcard?)\n", 16462306a36Sopenharmony_ci chip->port, chip->version); 16562306a36Sopenharmony_ci return -ENODEV; 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 16962306a36Sopenharmony_ci snd_es1688_write(chip, 0xb1, 0x10); /* disable IRQ */ 17062306a36Sopenharmony_ci snd_es1688_write(chip, 0xb2, 0x00); /* disable DMA */ 17162306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci /* enable joystick, but disable OPL3 */ 17462306a36Sopenharmony_ci spin_lock_irqsave(&chip->mixer_lock, flags); 17562306a36Sopenharmony_ci snd_es1688_mixer_write(chip, 0x40, 0x01); 17662306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->mixer_lock, flags); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci return 0; 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic int snd_es1688_init(struct snd_es1688 * chip, int enable) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci static const int irqs[16] = {-1, -1, 0, -1, -1, 1, -1, 2, -1, 0, 3, -1, -1, -1, -1, -1}; 18462306a36Sopenharmony_ci unsigned long flags; 18562306a36Sopenharmony_ci int cfg, irq_bits, dma, dma_bits, tmp, tmp1; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci /* ok.. setup MPU-401 port and joystick and OPL3 */ 18862306a36Sopenharmony_ci cfg = 0x01; /* enable joystick, but disable OPL3 */ 18962306a36Sopenharmony_ci if (enable && chip->mpu_port >= 0x300 && chip->mpu_irq > 0 && chip->hardware != ES1688_HW_688) { 19062306a36Sopenharmony_ci tmp = (chip->mpu_port & 0x0f0) >> 4; 19162306a36Sopenharmony_ci if (tmp <= 3) { 19262306a36Sopenharmony_ci switch (chip->mpu_irq) { 19362306a36Sopenharmony_ci case 9: 19462306a36Sopenharmony_ci tmp1 = 4; 19562306a36Sopenharmony_ci break; 19662306a36Sopenharmony_ci case 5: 19762306a36Sopenharmony_ci tmp1 = 5; 19862306a36Sopenharmony_ci break; 19962306a36Sopenharmony_ci case 7: 20062306a36Sopenharmony_ci tmp1 = 6; 20162306a36Sopenharmony_ci break; 20262306a36Sopenharmony_ci case 10: 20362306a36Sopenharmony_ci tmp1 = 7; 20462306a36Sopenharmony_ci break; 20562306a36Sopenharmony_ci default: 20662306a36Sopenharmony_ci tmp1 = 0; 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci if (tmp1) { 20962306a36Sopenharmony_ci cfg |= (tmp << 3) | (tmp1 << 5); 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci#if 0 21462306a36Sopenharmony_ci snd_printk(KERN_DEBUG "mpu cfg = 0x%x\n", cfg); 21562306a36Sopenharmony_ci#endif 21662306a36Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 21762306a36Sopenharmony_ci snd_es1688_mixer_write(chip, 0x40, cfg); 21862306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 21962306a36Sopenharmony_ci /* --- */ 22062306a36Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 22162306a36Sopenharmony_ci snd_es1688_read(chip, 0xb1); 22262306a36Sopenharmony_ci snd_es1688_read(chip, 0xb2); 22362306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 22462306a36Sopenharmony_ci if (enable) { 22562306a36Sopenharmony_ci cfg = 0xf0; /* enable only DMA counter interrupt */ 22662306a36Sopenharmony_ci irq_bits = irqs[chip->irq & 0x0f]; 22762306a36Sopenharmony_ci if (irq_bits < 0) { 22862306a36Sopenharmony_ci snd_printk(KERN_ERR "[0x%lx] ESS: bad IRQ %d " 22962306a36Sopenharmony_ci "for ES1688 chip!!\n", 23062306a36Sopenharmony_ci chip->port, chip->irq); 23162306a36Sopenharmony_ci#if 0 23262306a36Sopenharmony_ci irq_bits = 0; 23362306a36Sopenharmony_ci cfg = 0x10; 23462306a36Sopenharmony_ci#endif 23562306a36Sopenharmony_ci return -EINVAL; 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 23862306a36Sopenharmony_ci snd_es1688_write(chip, 0xb1, cfg | (irq_bits << 2)); 23962306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 24062306a36Sopenharmony_ci cfg = 0xf0; /* extended mode DMA enable */ 24162306a36Sopenharmony_ci dma = chip->dma8; 24262306a36Sopenharmony_ci if (dma > 3 || dma == 2) { 24362306a36Sopenharmony_ci snd_printk(KERN_ERR "[0x%lx] ESS: bad DMA channel %d " 24462306a36Sopenharmony_ci "for ES1688 chip!!\n", chip->port, dma); 24562306a36Sopenharmony_ci#if 0 24662306a36Sopenharmony_ci dma_bits = 0; 24762306a36Sopenharmony_ci cfg = 0x00; /* disable all DMA */ 24862306a36Sopenharmony_ci#endif 24962306a36Sopenharmony_ci return -EINVAL; 25062306a36Sopenharmony_ci } else { 25162306a36Sopenharmony_ci dma_bits = dma; 25262306a36Sopenharmony_ci if (dma != 3) 25362306a36Sopenharmony_ci dma_bits++; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 25662306a36Sopenharmony_ci snd_es1688_write(chip, 0xb2, cfg | (dma_bits << 2)); 25762306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 25862306a36Sopenharmony_ci } else { 25962306a36Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 26062306a36Sopenharmony_ci snd_es1688_write(chip, 0xb1, 0x10); /* disable IRQ */ 26162306a36Sopenharmony_ci snd_es1688_write(chip, 0xb2, 0x00); /* disable DMA */ 26262306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 26562306a36Sopenharmony_ci snd_es1688_read(chip, 0xb1); 26662306a36Sopenharmony_ci snd_es1688_read(chip, 0xb2); 26762306a36Sopenharmony_ci snd_es1688_reset(chip); 26862306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 26962306a36Sopenharmony_ci return 0; 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci/* 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci */ 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_cistatic const struct snd_ratnum clocks[2] = { 27762306a36Sopenharmony_ci { 27862306a36Sopenharmony_ci .num = 795444, 27962306a36Sopenharmony_ci .den_min = 1, 28062306a36Sopenharmony_ci .den_max = 128, 28162306a36Sopenharmony_ci .den_step = 1, 28262306a36Sopenharmony_ci }, 28362306a36Sopenharmony_ci { 28462306a36Sopenharmony_ci .num = 397722, 28562306a36Sopenharmony_ci .den_min = 1, 28662306a36Sopenharmony_ci .den_max = 128, 28762306a36Sopenharmony_ci .den_step = 1, 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci}; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cistatic const struct snd_pcm_hw_constraint_ratnums hw_constraints_clocks = { 29262306a36Sopenharmony_ci .nrats = 2, 29362306a36Sopenharmony_ci .rats = clocks, 29462306a36Sopenharmony_ci}; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_cistatic void snd_es1688_set_rate(struct snd_es1688 *chip, struct snd_pcm_substream *substream) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 29962306a36Sopenharmony_ci unsigned int bits, divider; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci if (runtime->rate_num == clocks[0].num) 30262306a36Sopenharmony_ci bits = 256 - runtime->rate_den; 30362306a36Sopenharmony_ci else 30462306a36Sopenharmony_ci bits = 128 - runtime->rate_den; 30562306a36Sopenharmony_ci /* set filter register */ 30662306a36Sopenharmony_ci divider = 256 - 7160000*20/(8*82*runtime->rate); 30762306a36Sopenharmony_ci /* write result to hardware */ 30862306a36Sopenharmony_ci snd_es1688_write(chip, 0xa1, bits); 30962306a36Sopenharmony_ci snd_es1688_write(chip, 0xa2, divider); 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cistatic int snd_es1688_trigger(struct snd_es1688 *chip, int cmd, unsigned char value) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci int val; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci if (cmd == SNDRV_PCM_TRIGGER_STOP) { 31762306a36Sopenharmony_ci value = 0x00; 31862306a36Sopenharmony_ci } else if (cmd != SNDRV_PCM_TRIGGER_START) { 31962306a36Sopenharmony_ci return -EINVAL; 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci spin_lock(&chip->reg_lock); 32262306a36Sopenharmony_ci chip->trigger_value = value; 32362306a36Sopenharmony_ci val = snd_es1688_read(chip, 0xb8); 32462306a36Sopenharmony_ci if ((val < 0) || (val & 0x0f) == value) { 32562306a36Sopenharmony_ci spin_unlock(&chip->reg_lock); 32662306a36Sopenharmony_ci return -EINVAL; /* something is wrong */ 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci#if 0 32962306a36Sopenharmony_ci printk(KERN_DEBUG "trigger: val = 0x%x, value = 0x%x\n", val, value); 33062306a36Sopenharmony_ci printk(KERN_DEBUG "trigger: pointer = 0x%x\n", 33162306a36Sopenharmony_ci snd_dma_pointer(chip->dma8, chip->dma_size)); 33262306a36Sopenharmony_ci#endif 33362306a36Sopenharmony_ci snd_es1688_write(chip, 0xb8, (val & 0xf0) | value); 33462306a36Sopenharmony_ci spin_unlock(&chip->reg_lock); 33562306a36Sopenharmony_ci return 0; 33662306a36Sopenharmony_ci} 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_cistatic int snd_es1688_playback_prepare(struct snd_pcm_substream *substream) 33962306a36Sopenharmony_ci{ 34062306a36Sopenharmony_ci unsigned long flags; 34162306a36Sopenharmony_ci struct snd_es1688 *chip = snd_pcm_substream_chip(substream); 34262306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 34362306a36Sopenharmony_ci unsigned int size = snd_pcm_lib_buffer_bytes(substream); 34462306a36Sopenharmony_ci unsigned int count = snd_pcm_lib_period_bytes(substream); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci chip->dma_size = size; 34762306a36Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 34862306a36Sopenharmony_ci snd_es1688_reset(chip); 34962306a36Sopenharmony_ci snd_es1688_set_rate(chip, substream); 35062306a36Sopenharmony_ci snd_es1688_write(chip, 0xb8, 4); /* auto init DMA mode */ 35162306a36Sopenharmony_ci snd_es1688_write(chip, 0xa8, (snd_es1688_read(chip, 0xa8) & ~0x03) | (3 - runtime->channels)); 35262306a36Sopenharmony_ci snd_es1688_write(chip, 0xb9, 2); /* demand mode (4 bytes/request) */ 35362306a36Sopenharmony_ci if (runtime->channels == 1) { 35462306a36Sopenharmony_ci if (snd_pcm_format_width(runtime->format) == 8) { 35562306a36Sopenharmony_ci /* 8. bit mono */ 35662306a36Sopenharmony_ci snd_es1688_write(chip, 0xb6, 0x80); 35762306a36Sopenharmony_ci snd_es1688_write(chip, 0xb7, 0x51); 35862306a36Sopenharmony_ci snd_es1688_write(chip, 0xb7, 0xd0); 35962306a36Sopenharmony_ci } else { 36062306a36Sopenharmony_ci /* 16. bit mono */ 36162306a36Sopenharmony_ci snd_es1688_write(chip, 0xb6, 0x00); 36262306a36Sopenharmony_ci snd_es1688_write(chip, 0xb7, 0x71); 36362306a36Sopenharmony_ci snd_es1688_write(chip, 0xb7, 0xf4); 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci } else { 36662306a36Sopenharmony_ci if (snd_pcm_format_width(runtime->format) == 8) { 36762306a36Sopenharmony_ci /* 8. bit stereo */ 36862306a36Sopenharmony_ci snd_es1688_write(chip, 0xb6, 0x80); 36962306a36Sopenharmony_ci snd_es1688_write(chip, 0xb7, 0x51); 37062306a36Sopenharmony_ci snd_es1688_write(chip, 0xb7, 0x98); 37162306a36Sopenharmony_ci } else { 37262306a36Sopenharmony_ci /* 16. bit stereo */ 37362306a36Sopenharmony_ci snd_es1688_write(chip, 0xb6, 0x00); 37462306a36Sopenharmony_ci snd_es1688_write(chip, 0xb7, 0x71); 37562306a36Sopenharmony_ci snd_es1688_write(chip, 0xb7, 0xbc); 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci snd_es1688_write(chip, 0xb1, (snd_es1688_read(chip, 0xb1) & 0x0f) | 0x50); 37962306a36Sopenharmony_ci snd_es1688_write(chip, 0xb2, (snd_es1688_read(chip, 0xb2) & 0x0f) | 0x50); 38062306a36Sopenharmony_ci snd_es1688_dsp_command(chip, ES1688_DSP_CMD_SPKON); 38162306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 38262306a36Sopenharmony_ci /* --- */ 38362306a36Sopenharmony_ci count = -count; 38462306a36Sopenharmony_ci snd_dma_program(chip->dma8, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT); 38562306a36Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 38662306a36Sopenharmony_ci snd_es1688_write(chip, 0xa4, (unsigned char) count); 38762306a36Sopenharmony_ci snd_es1688_write(chip, 0xa5, (unsigned char) (count >> 8)); 38862306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 38962306a36Sopenharmony_ci return 0; 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_cistatic int snd_es1688_playback_trigger(struct snd_pcm_substream *substream, 39362306a36Sopenharmony_ci int cmd) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci struct snd_es1688 *chip = snd_pcm_substream_chip(substream); 39662306a36Sopenharmony_ci return snd_es1688_trigger(chip, cmd, 0x05); 39762306a36Sopenharmony_ci} 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_cistatic int snd_es1688_capture_prepare(struct snd_pcm_substream *substream) 40062306a36Sopenharmony_ci{ 40162306a36Sopenharmony_ci unsigned long flags; 40262306a36Sopenharmony_ci struct snd_es1688 *chip = snd_pcm_substream_chip(substream); 40362306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 40462306a36Sopenharmony_ci unsigned int size = snd_pcm_lib_buffer_bytes(substream); 40562306a36Sopenharmony_ci unsigned int count = snd_pcm_lib_period_bytes(substream); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci chip->dma_size = size; 40862306a36Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 40962306a36Sopenharmony_ci snd_es1688_reset(chip); 41062306a36Sopenharmony_ci snd_es1688_set_rate(chip, substream); 41162306a36Sopenharmony_ci snd_es1688_dsp_command(chip, ES1688_DSP_CMD_SPKOFF); 41262306a36Sopenharmony_ci snd_es1688_write(chip, 0xb8, 0x0e); /* auto init DMA mode */ 41362306a36Sopenharmony_ci snd_es1688_write(chip, 0xa8, (snd_es1688_read(chip, 0xa8) & ~0x03) | (3 - runtime->channels)); 41462306a36Sopenharmony_ci snd_es1688_write(chip, 0xb9, 2); /* demand mode (4 bytes/request) */ 41562306a36Sopenharmony_ci if (runtime->channels == 1) { 41662306a36Sopenharmony_ci if (snd_pcm_format_width(runtime->format) == 8) { 41762306a36Sopenharmony_ci /* 8. bit mono */ 41862306a36Sopenharmony_ci snd_es1688_write(chip, 0xb7, 0x51); 41962306a36Sopenharmony_ci snd_es1688_write(chip, 0xb7, 0xd0); 42062306a36Sopenharmony_ci } else { 42162306a36Sopenharmony_ci /* 16. bit mono */ 42262306a36Sopenharmony_ci snd_es1688_write(chip, 0xb7, 0x71); 42362306a36Sopenharmony_ci snd_es1688_write(chip, 0xb7, 0xf4); 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci } else { 42662306a36Sopenharmony_ci if (snd_pcm_format_width(runtime->format) == 8) { 42762306a36Sopenharmony_ci /* 8. bit stereo */ 42862306a36Sopenharmony_ci snd_es1688_write(chip, 0xb7, 0x51); 42962306a36Sopenharmony_ci snd_es1688_write(chip, 0xb7, 0x98); 43062306a36Sopenharmony_ci } else { 43162306a36Sopenharmony_ci /* 16. bit stereo */ 43262306a36Sopenharmony_ci snd_es1688_write(chip, 0xb7, 0x71); 43362306a36Sopenharmony_ci snd_es1688_write(chip, 0xb7, 0xbc); 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci } 43662306a36Sopenharmony_ci snd_es1688_write(chip, 0xb1, (snd_es1688_read(chip, 0xb1) & 0x0f) | 0x50); 43762306a36Sopenharmony_ci snd_es1688_write(chip, 0xb2, (snd_es1688_read(chip, 0xb2) & 0x0f) | 0x50); 43862306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 43962306a36Sopenharmony_ci /* --- */ 44062306a36Sopenharmony_ci count = -count; 44162306a36Sopenharmony_ci snd_dma_program(chip->dma8, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT); 44262306a36Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 44362306a36Sopenharmony_ci snd_es1688_write(chip, 0xa4, (unsigned char) count); 44462306a36Sopenharmony_ci snd_es1688_write(chip, 0xa5, (unsigned char) (count >> 8)); 44562306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 44662306a36Sopenharmony_ci return 0; 44762306a36Sopenharmony_ci} 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_cistatic int snd_es1688_capture_trigger(struct snd_pcm_substream *substream, 45062306a36Sopenharmony_ci int cmd) 45162306a36Sopenharmony_ci{ 45262306a36Sopenharmony_ci struct snd_es1688 *chip = snd_pcm_substream_chip(substream); 45362306a36Sopenharmony_ci return snd_es1688_trigger(chip, cmd, 0x0f); 45462306a36Sopenharmony_ci} 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_cistatic irqreturn_t snd_es1688_interrupt(int irq, void *dev_id) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci struct snd_es1688 *chip = dev_id; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci if (chip->trigger_value == 0x05) /* ok.. playback is active */ 46162306a36Sopenharmony_ci snd_pcm_period_elapsed(chip->playback_substream); 46262306a36Sopenharmony_ci if (chip->trigger_value == 0x0f) /* ok.. capture is active */ 46362306a36Sopenharmony_ci snd_pcm_period_elapsed(chip->capture_substream); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci inb(ES1688P(chip, DATA_AVAIL)); /* ack interrupt */ 46662306a36Sopenharmony_ci return IRQ_HANDLED; 46762306a36Sopenharmony_ci} 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_cistatic snd_pcm_uframes_t snd_es1688_playback_pointer(struct snd_pcm_substream *substream) 47062306a36Sopenharmony_ci{ 47162306a36Sopenharmony_ci struct snd_es1688 *chip = snd_pcm_substream_chip(substream); 47262306a36Sopenharmony_ci size_t ptr; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci if (chip->trigger_value != 0x05) 47562306a36Sopenharmony_ci return 0; 47662306a36Sopenharmony_ci ptr = snd_dma_pointer(chip->dma8, chip->dma_size); 47762306a36Sopenharmony_ci return bytes_to_frames(substream->runtime, ptr); 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_cistatic snd_pcm_uframes_t snd_es1688_capture_pointer(struct snd_pcm_substream *substream) 48162306a36Sopenharmony_ci{ 48262306a36Sopenharmony_ci struct snd_es1688 *chip = snd_pcm_substream_chip(substream); 48362306a36Sopenharmony_ci size_t ptr; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci if (chip->trigger_value != 0x0f) 48662306a36Sopenharmony_ci return 0; 48762306a36Sopenharmony_ci ptr = snd_dma_pointer(chip->dma8, chip->dma_size); 48862306a36Sopenharmony_ci return bytes_to_frames(substream->runtime, ptr); 48962306a36Sopenharmony_ci} 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci/* 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci */ 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_es1688_playback = 49662306a36Sopenharmony_ci{ 49762306a36Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 49862306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID), 49962306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, 50062306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, 50162306a36Sopenharmony_ci .rate_min = 4000, 50262306a36Sopenharmony_ci .rate_max = 48000, 50362306a36Sopenharmony_ci .channels_min = 1, 50462306a36Sopenharmony_ci .channels_max = 2, 50562306a36Sopenharmony_ci .buffer_bytes_max = 65536, 50662306a36Sopenharmony_ci .period_bytes_min = 64, 50762306a36Sopenharmony_ci .period_bytes_max = 65536, 50862306a36Sopenharmony_ci .periods_min = 1, 50962306a36Sopenharmony_ci .periods_max = 1024, 51062306a36Sopenharmony_ci .fifo_size = 0, 51162306a36Sopenharmony_ci}; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_es1688_capture = 51462306a36Sopenharmony_ci{ 51562306a36Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 51662306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID), 51762306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, 51862306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, 51962306a36Sopenharmony_ci .rate_min = 4000, 52062306a36Sopenharmony_ci .rate_max = 48000, 52162306a36Sopenharmony_ci .channels_min = 1, 52262306a36Sopenharmony_ci .channels_max = 2, 52362306a36Sopenharmony_ci .buffer_bytes_max = 65536, 52462306a36Sopenharmony_ci .period_bytes_min = 64, 52562306a36Sopenharmony_ci .period_bytes_max = 65536, 52662306a36Sopenharmony_ci .periods_min = 1, 52762306a36Sopenharmony_ci .periods_max = 1024, 52862306a36Sopenharmony_ci .fifo_size = 0, 52962306a36Sopenharmony_ci}; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci/* 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci */ 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_cistatic int snd_es1688_playback_open(struct snd_pcm_substream *substream) 53662306a36Sopenharmony_ci{ 53762306a36Sopenharmony_ci struct snd_es1688 *chip = snd_pcm_substream_chip(substream); 53862306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci if (chip->capture_substream != NULL) 54162306a36Sopenharmony_ci return -EAGAIN; 54262306a36Sopenharmony_ci chip->playback_substream = substream; 54362306a36Sopenharmony_ci runtime->hw = snd_es1688_playback; 54462306a36Sopenharmony_ci snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, 54562306a36Sopenharmony_ci &hw_constraints_clocks); 54662306a36Sopenharmony_ci return 0; 54762306a36Sopenharmony_ci} 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_cistatic int snd_es1688_capture_open(struct snd_pcm_substream *substream) 55062306a36Sopenharmony_ci{ 55162306a36Sopenharmony_ci struct snd_es1688 *chip = snd_pcm_substream_chip(substream); 55262306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci if (chip->playback_substream != NULL) 55562306a36Sopenharmony_ci return -EAGAIN; 55662306a36Sopenharmony_ci chip->capture_substream = substream; 55762306a36Sopenharmony_ci runtime->hw = snd_es1688_capture; 55862306a36Sopenharmony_ci snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, 55962306a36Sopenharmony_ci &hw_constraints_clocks); 56062306a36Sopenharmony_ci return 0; 56162306a36Sopenharmony_ci} 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_cistatic int snd_es1688_playback_close(struct snd_pcm_substream *substream) 56462306a36Sopenharmony_ci{ 56562306a36Sopenharmony_ci struct snd_es1688 *chip = snd_pcm_substream_chip(substream); 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci chip->playback_substream = NULL; 56862306a36Sopenharmony_ci return 0; 56962306a36Sopenharmony_ci} 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_cistatic int snd_es1688_capture_close(struct snd_pcm_substream *substream) 57262306a36Sopenharmony_ci{ 57362306a36Sopenharmony_ci struct snd_es1688 *chip = snd_pcm_substream_chip(substream); 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci chip->capture_substream = NULL; 57662306a36Sopenharmony_ci return 0; 57762306a36Sopenharmony_ci} 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_cistatic int snd_es1688_free(struct snd_es1688 *chip) 58062306a36Sopenharmony_ci{ 58162306a36Sopenharmony_ci if (chip->hardware != ES1688_HW_UNDEF) 58262306a36Sopenharmony_ci snd_es1688_init(chip, 0); 58362306a36Sopenharmony_ci release_and_free_resource(chip->res_port); 58462306a36Sopenharmony_ci if (chip->irq >= 0) 58562306a36Sopenharmony_ci free_irq(chip->irq, (void *) chip); 58662306a36Sopenharmony_ci if (chip->dma8 >= 0) { 58762306a36Sopenharmony_ci disable_dma(chip->dma8); 58862306a36Sopenharmony_ci free_dma(chip->dma8); 58962306a36Sopenharmony_ci } 59062306a36Sopenharmony_ci return 0; 59162306a36Sopenharmony_ci} 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_cistatic int snd_es1688_dev_free(struct snd_device *device) 59462306a36Sopenharmony_ci{ 59562306a36Sopenharmony_ci struct snd_es1688 *chip = device->device_data; 59662306a36Sopenharmony_ci return snd_es1688_free(chip); 59762306a36Sopenharmony_ci} 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_cistatic const char *snd_es1688_chip_id(struct snd_es1688 *chip) 60062306a36Sopenharmony_ci{ 60162306a36Sopenharmony_ci static char tmp[16]; 60262306a36Sopenharmony_ci sprintf(tmp, "ES%s688 rev %i", chip->hardware == ES1688_HW_688 ? "" : "1", chip->version & 0x0f); 60362306a36Sopenharmony_ci return tmp; 60462306a36Sopenharmony_ci} 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ciint snd_es1688_create(struct snd_card *card, 60762306a36Sopenharmony_ci struct snd_es1688 *chip, 60862306a36Sopenharmony_ci unsigned long port, 60962306a36Sopenharmony_ci unsigned long mpu_port, 61062306a36Sopenharmony_ci int irq, 61162306a36Sopenharmony_ci int mpu_irq, 61262306a36Sopenharmony_ci int dma8, 61362306a36Sopenharmony_ci unsigned short hardware) 61462306a36Sopenharmony_ci{ 61562306a36Sopenharmony_ci static const struct snd_device_ops ops = { 61662306a36Sopenharmony_ci .dev_free = snd_es1688_dev_free, 61762306a36Sopenharmony_ci }; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci int err; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci if (chip == NULL) 62262306a36Sopenharmony_ci return -ENOMEM; 62362306a36Sopenharmony_ci chip->irq = -1; 62462306a36Sopenharmony_ci chip->dma8 = -1; 62562306a36Sopenharmony_ci chip->hardware = ES1688_HW_UNDEF; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci chip->res_port = request_region(port + 4, 12, "ES1688"); 62862306a36Sopenharmony_ci if (chip->res_port == NULL) { 62962306a36Sopenharmony_ci snd_printk(KERN_ERR "es1688: can't grab port 0x%lx\n", port + 4); 63062306a36Sopenharmony_ci err = -EBUSY; 63162306a36Sopenharmony_ci goto exit; 63262306a36Sopenharmony_ci } 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci err = request_irq(irq, snd_es1688_interrupt, 0, "ES1688", (void *) chip); 63562306a36Sopenharmony_ci if (err < 0) { 63662306a36Sopenharmony_ci snd_printk(KERN_ERR "es1688: can't grab IRQ %d\n", irq); 63762306a36Sopenharmony_ci goto exit; 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci chip->irq = irq; 64162306a36Sopenharmony_ci card->sync_irq = chip->irq; 64262306a36Sopenharmony_ci err = request_dma(dma8, "ES1688"); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci if (err < 0) { 64562306a36Sopenharmony_ci snd_printk(KERN_ERR "es1688: can't grab DMA8 %d\n", dma8); 64662306a36Sopenharmony_ci goto exit; 64762306a36Sopenharmony_ci } 64862306a36Sopenharmony_ci chip->dma8 = dma8; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci spin_lock_init(&chip->reg_lock); 65162306a36Sopenharmony_ci spin_lock_init(&chip->mixer_lock); 65262306a36Sopenharmony_ci chip->port = port; 65362306a36Sopenharmony_ci mpu_port &= ~0x000f; 65462306a36Sopenharmony_ci if (mpu_port < 0x300 || mpu_port > 0x330) 65562306a36Sopenharmony_ci mpu_port = 0; 65662306a36Sopenharmony_ci chip->mpu_port = mpu_port; 65762306a36Sopenharmony_ci chip->mpu_irq = mpu_irq; 65862306a36Sopenharmony_ci chip->hardware = hardware; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci err = snd_es1688_probe(chip); 66162306a36Sopenharmony_ci if (err < 0) 66262306a36Sopenharmony_ci goto exit; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci err = snd_es1688_init(chip, 1); 66562306a36Sopenharmony_ci if (err < 0) 66662306a36Sopenharmony_ci goto exit; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci /* Register device */ 66962306a36Sopenharmony_ci err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); 67062306a36Sopenharmony_ciexit: 67162306a36Sopenharmony_ci if (err) 67262306a36Sopenharmony_ci snd_es1688_free(chip); 67362306a36Sopenharmony_ci return err; 67462306a36Sopenharmony_ci} 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_es1688_playback_ops = { 67762306a36Sopenharmony_ci .open = snd_es1688_playback_open, 67862306a36Sopenharmony_ci .close = snd_es1688_playback_close, 67962306a36Sopenharmony_ci .prepare = snd_es1688_playback_prepare, 68062306a36Sopenharmony_ci .trigger = snd_es1688_playback_trigger, 68162306a36Sopenharmony_ci .pointer = snd_es1688_playback_pointer, 68262306a36Sopenharmony_ci}; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_es1688_capture_ops = { 68562306a36Sopenharmony_ci .open = snd_es1688_capture_open, 68662306a36Sopenharmony_ci .close = snd_es1688_capture_close, 68762306a36Sopenharmony_ci .prepare = snd_es1688_capture_prepare, 68862306a36Sopenharmony_ci .trigger = snd_es1688_capture_trigger, 68962306a36Sopenharmony_ci .pointer = snd_es1688_capture_pointer, 69062306a36Sopenharmony_ci}; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ciint snd_es1688_pcm(struct snd_card *card, struct snd_es1688 *chip, int device) 69362306a36Sopenharmony_ci{ 69462306a36Sopenharmony_ci struct snd_pcm *pcm; 69562306a36Sopenharmony_ci int err; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci err = snd_pcm_new(card, "ESx688", device, 1, 1, &pcm); 69862306a36Sopenharmony_ci if (err < 0) 69962306a36Sopenharmony_ci return err; 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_es1688_playback_ops); 70262306a36Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_es1688_capture_ops); 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci pcm->private_data = chip; 70562306a36Sopenharmony_ci pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX; 70662306a36Sopenharmony_ci strcpy(pcm->name, snd_es1688_chip_id(chip)); 70762306a36Sopenharmony_ci chip->pcm = pcm; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, card->dev, 71062306a36Sopenharmony_ci 64*1024, 64*1024); 71162306a36Sopenharmony_ci return 0; 71262306a36Sopenharmony_ci} 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci/* 71562306a36Sopenharmony_ci * MIXER part 71662306a36Sopenharmony_ci */ 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_cistatic int snd_es1688_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) 71962306a36Sopenharmony_ci{ 72062306a36Sopenharmony_ci static const char * const texts[8] = { 72162306a36Sopenharmony_ci "Mic", "Mic Master", "CD", "AOUT", 72262306a36Sopenharmony_ci "Mic1", "Mix", "Line", "Master" 72362306a36Sopenharmony_ci }; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci return snd_ctl_enum_info(uinfo, 1, 8, texts); 72662306a36Sopenharmony_ci} 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_cistatic int snd_es1688_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 72962306a36Sopenharmony_ci{ 73062306a36Sopenharmony_ci struct snd_es1688 *chip = snd_kcontrol_chip(kcontrol); 73162306a36Sopenharmony_ci ucontrol->value.enumerated.item[0] = snd_es1688_mixer_read(chip, ES1688_REC_DEV) & 7; 73262306a36Sopenharmony_ci return 0; 73362306a36Sopenharmony_ci} 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_cistatic int snd_es1688_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 73662306a36Sopenharmony_ci{ 73762306a36Sopenharmony_ci struct snd_es1688 *chip = snd_kcontrol_chip(kcontrol); 73862306a36Sopenharmony_ci unsigned long flags; 73962306a36Sopenharmony_ci unsigned char oval, nval; 74062306a36Sopenharmony_ci int change; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci if (ucontrol->value.enumerated.item[0] > 8) 74362306a36Sopenharmony_ci return -EINVAL; 74462306a36Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 74562306a36Sopenharmony_ci oval = snd_es1688_mixer_read(chip, ES1688_REC_DEV); 74662306a36Sopenharmony_ci nval = (ucontrol->value.enumerated.item[0] & 7) | (oval & ~15); 74762306a36Sopenharmony_ci change = nval != oval; 74862306a36Sopenharmony_ci if (change) 74962306a36Sopenharmony_ci snd_es1688_mixer_write(chip, ES1688_REC_DEV, nval); 75062306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 75162306a36Sopenharmony_ci return change; 75262306a36Sopenharmony_ci} 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci#define ES1688_SINGLE(xname, xindex, reg, shift, mask, invert) \ 75562306a36Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ 75662306a36Sopenharmony_ci .info = snd_es1688_info_single, \ 75762306a36Sopenharmony_ci .get = snd_es1688_get_single, .put = snd_es1688_put_single, \ 75862306a36Sopenharmony_ci .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) } 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_cistatic int snd_es1688_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) 76162306a36Sopenharmony_ci{ 76262306a36Sopenharmony_ci int mask = (kcontrol->private_value >> 16) & 0xff; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; 76562306a36Sopenharmony_ci uinfo->count = 1; 76662306a36Sopenharmony_ci uinfo->value.integer.min = 0; 76762306a36Sopenharmony_ci uinfo->value.integer.max = mask; 76862306a36Sopenharmony_ci return 0; 76962306a36Sopenharmony_ci} 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_cistatic int snd_es1688_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 77262306a36Sopenharmony_ci{ 77362306a36Sopenharmony_ci struct snd_es1688 *chip = snd_kcontrol_chip(kcontrol); 77462306a36Sopenharmony_ci unsigned long flags; 77562306a36Sopenharmony_ci int reg = kcontrol->private_value & 0xff; 77662306a36Sopenharmony_ci int shift = (kcontrol->private_value >> 8) & 0xff; 77762306a36Sopenharmony_ci int mask = (kcontrol->private_value >> 16) & 0xff; 77862306a36Sopenharmony_ci int invert = (kcontrol->private_value >> 24) & 0xff; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 78162306a36Sopenharmony_ci ucontrol->value.integer.value[0] = (snd_es1688_mixer_read(chip, reg) >> shift) & mask; 78262306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 78362306a36Sopenharmony_ci if (invert) 78462306a36Sopenharmony_ci ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; 78562306a36Sopenharmony_ci return 0; 78662306a36Sopenharmony_ci} 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_cistatic int snd_es1688_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 78962306a36Sopenharmony_ci{ 79062306a36Sopenharmony_ci struct snd_es1688 *chip = snd_kcontrol_chip(kcontrol); 79162306a36Sopenharmony_ci unsigned long flags; 79262306a36Sopenharmony_ci int reg = kcontrol->private_value & 0xff; 79362306a36Sopenharmony_ci int shift = (kcontrol->private_value >> 8) & 0xff; 79462306a36Sopenharmony_ci int mask = (kcontrol->private_value >> 16) & 0xff; 79562306a36Sopenharmony_ci int invert = (kcontrol->private_value >> 24) & 0xff; 79662306a36Sopenharmony_ci int change; 79762306a36Sopenharmony_ci unsigned char oval, nval; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci nval = (ucontrol->value.integer.value[0] & mask); 80062306a36Sopenharmony_ci if (invert) 80162306a36Sopenharmony_ci nval = mask - nval; 80262306a36Sopenharmony_ci nval <<= shift; 80362306a36Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 80462306a36Sopenharmony_ci oval = snd_es1688_mixer_read(chip, reg); 80562306a36Sopenharmony_ci nval = (oval & ~(mask << shift)) | nval; 80662306a36Sopenharmony_ci change = nval != oval; 80762306a36Sopenharmony_ci if (change) 80862306a36Sopenharmony_ci snd_es1688_mixer_write(chip, reg, nval); 80962306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 81062306a36Sopenharmony_ci return change; 81162306a36Sopenharmony_ci} 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci#define ES1688_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ 81462306a36Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ 81562306a36Sopenharmony_ci .info = snd_es1688_info_double, \ 81662306a36Sopenharmony_ci .get = snd_es1688_get_double, .put = snd_es1688_put_double, \ 81762306a36Sopenharmony_ci .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_cistatic int snd_es1688_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) 82062306a36Sopenharmony_ci{ 82162306a36Sopenharmony_ci int mask = (kcontrol->private_value >> 24) & 0xff; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; 82462306a36Sopenharmony_ci uinfo->count = 2; 82562306a36Sopenharmony_ci uinfo->value.integer.min = 0; 82662306a36Sopenharmony_ci uinfo->value.integer.max = mask; 82762306a36Sopenharmony_ci return 0; 82862306a36Sopenharmony_ci} 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_cistatic int snd_es1688_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 83162306a36Sopenharmony_ci{ 83262306a36Sopenharmony_ci struct snd_es1688 *chip = snd_kcontrol_chip(kcontrol); 83362306a36Sopenharmony_ci unsigned long flags; 83462306a36Sopenharmony_ci int left_reg = kcontrol->private_value & 0xff; 83562306a36Sopenharmony_ci int right_reg = (kcontrol->private_value >> 8) & 0xff; 83662306a36Sopenharmony_ci int shift_left = (kcontrol->private_value >> 16) & 0x07; 83762306a36Sopenharmony_ci int shift_right = (kcontrol->private_value >> 19) & 0x07; 83862306a36Sopenharmony_ci int mask = (kcontrol->private_value >> 24) & 0xff; 83962306a36Sopenharmony_ci int invert = (kcontrol->private_value >> 22) & 1; 84062306a36Sopenharmony_ci unsigned char left, right; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 84362306a36Sopenharmony_ci if (left_reg < 0xa0) 84462306a36Sopenharmony_ci left = snd_es1688_mixer_read(chip, left_reg); 84562306a36Sopenharmony_ci else 84662306a36Sopenharmony_ci left = snd_es1688_read(chip, left_reg); 84762306a36Sopenharmony_ci if (left_reg != right_reg) { 84862306a36Sopenharmony_ci if (right_reg < 0xa0) 84962306a36Sopenharmony_ci right = snd_es1688_mixer_read(chip, right_reg); 85062306a36Sopenharmony_ci else 85162306a36Sopenharmony_ci right = snd_es1688_read(chip, right_reg); 85262306a36Sopenharmony_ci } else 85362306a36Sopenharmony_ci right = left; 85462306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 85562306a36Sopenharmony_ci ucontrol->value.integer.value[0] = (left >> shift_left) & mask; 85662306a36Sopenharmony_ci ucontrol->value.integer.value[1] = (right >> shift_right) & mask; 85762306a36Sopenharmony_ci if (invert) { 85862306a36Sopenharmony_ci ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; 85962306a36Sopenharmony_ci ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; 86062306a36Sopenharmony_ci } 86162306a36Sopenharmony_ci return 0; 86262306a36Sopenharmony_ci} 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_cistatic int snd_es1688_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 86562306a36Sopenharmony_ci{ 86662306a36Sopenharmony_ci struct snd_es1688 *chip = snd_kcontrol_chip(kcontrol); 86762306a36Sopenharmony_ci unsigned long flags; 86862306a36Sopenharmony_ci int left_reg = kcontrol->private_value & 0xff; 86962306a36Sopenharmony_ci int right_reg = (kcontrol->private_value >> 8) & 0xff; 87062306a36Sopenharmony_ci int shift_left = (kcontrol->private_value >> 16) & 0x07; 87162306a36Sopenharmony_ci int shift_right = (kcontrol->private_value >> 19) & 0x07; 87262306a36Sopenharmony_ci int mask = (kcontrol->private_value >> 24) & 0xff; 87362306a36Sopenharmony_ci int invert = (kcontrol->private_value >> 22) & 1; 87462306a36Sopenharmony_ci int change; 87562306a36Sopenharmony_ci unsigned char val1, val2, oval1, oval2; 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci val1 = ucontrol->value.integer.value[0] & mask; 87862306a36Sopenharmony_ci val2 = ucontrol->value.integer.value[1] & mask; 87962306a36Sopenharmony_ci if (invert) { 88062306a36Sopenharmony_ci val1 = mask - val1; 88162306a36Sopenharmony_ci val2 = mask - val2; 88262306a36Sopenharmony_ci } 88362306a36Sopenharmony_ci val1 <<= shift_left; 88462306a36Sopenharmony_ci val2 <<= shift_right; 88562306a36Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 88662306a36Sopenharmony_ci if (left_reg != right_reg) { 88762306a36Sopenharmony_ci if (left_reg < 0xa0) 88862306a36Sopenharmony_ci oval1 = snd_es1688_mixer_read(chip, left_reg); 88962306a36Sopenharmony_ci else 89062306a36Sopenharmony_ci oval1 = snd_es1688_read(chip, left_reg); 89162306a36Sopenharmony_ci if (right_reg < 0xa0) 89262306a36Sopenharmony_ci oval2 = snd_es1688_mixer_read(chip, right_reg); 89362306a36Sopenharmony_ci else 89462306a36Sopenharmony_ci oval2 = snd_es1688_read(chip, right_reg); 89562306a36Sopenharmony_ci val1 = (oval1 & ~(mask << shift_left)) | val1; 89662306a36Sopenharmony_ci val2 = (oval2 & ~(mask << shift_right)) | val2; 89762306a36Sopenharmony_ci change = val1 != oval1 || val2 != oval2; 89862306a36Sopenharmony_ci if (change) { 89962306a36Sopenharmony_ci if (left_reg < 0xa0) 90062306a36Sopenharmony_ci snd_es1688_mixer_write(chip, left_reg, val1); 90162306a36Sopenharmony_ci else 90262306a36Sopenharmony_ci snd_es1688_write(chip, left_reg, val1); 90362306a36Sopenharmony_ci if (right_reg < 0xa0) 90462306a36Sopenharmony_ci snd_es1688_mixer_write(chip, right_reg, val1); 90562306a36Sopenharmony_ci else 90662306a36Sopenharmony_ci snd_es1688_write(chip, right_reg, val1); 90762306a36Sopenharmony_ci } 90862306a36Sopenharmony_ci } else { 90962306a36Sopenharmony_ci if (left_reg < 0xa0) 91062306a36Sopenharmony_ci oval1 = snd_es1688_mixer_read(chip, left_reg); 91162306a36Sopenharmony_ci else 91262306a36Sopenharmony_ci oval1 = snd_es1688_read(chip, left_reg); 91362306a36Sopenharmony_ci val1 = (oval1 & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2; 91462306a36Sopenharmony_ci change = val1 != oval1; 91562306a36Sopenharmony_ci if (change) { 91662306a36Sopenharmony_ci if (left_reg < 0xa0) 91762306a36Sopenharmony_ci snd_es1688_mixer_write(chip, left_reg, val1); 91862306a36Sopenharmony_ci else 91962306a36Sopenharmony_ci snd_es1688_write(chip, left_reg, val1); 92062306a36Sopenharmony_ci } 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci } 92362306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 92462306a36Sopenharmony_ci return change; 92562306a36Sopenharmony_ci} 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_es1688_controls[] = { 92862306a36Sopenharmony_ciES1688_DOUBLE("Master Playback Volume", 0, ES1688_MASTER_DEV, ES1688_MASTER_DEV, 4, 0, 15, 0), 92962306a36Sopenharmony_ciES1688_DOUBLE("PCM Playback Volume", 0, ES1688_PCM_DEV, ES1688_PCM_DEV, 4, 0, 15, 0), 93062306a36Sopenharmony_ciES1688_DOUBLE("Line Playback Volume", 0, ES1688_LINE_DEV, ES1688_LINE_DEV, 4, 0, 15, 0), 93162306a36Sopenharmony_ciES1688_DOUBLE("CD Playback Volume", 0, ES1688_CD_DEV, ES1688_CD_DEV, 4, 0, 15, 0), 93262306a36Sopenharmony_ciES1688_DOUBLE("FM Playback Volume", 0, ES1688_FM_DEV, ES1688_FM_DEV, 4, 0, 15, 0), 93362306a36Sopenharmony_ciES1688_DOUBLE("Mic Playback Volume", 0, ES1688_MIC_DEV, ES1688_MIC_DEV, 4, 0, 15, 0), 93462306a36Sopenharmony_ciES1688_DOUBLE("Aux Playback Volume", 0, ES1688_AUX_DEV, ES1688_AUX_DEV, 4, 0, 15, 0), 93562306a36Sopenharmony_ciES1688_SINGLE("Beep Playback Volume", 0, ES1688_SPEAKER_DEV, 0, 7, 0), 93662306a36Sopenharmony_ciES1688_DOUBLE("Capture Volume", 0, ES1688_RECLEV_DEV, ES1688_RECLEV_DEV, 4, 0, 15, 0), 93762306a36Sopenharmony_ciES1688_SINGLE("Capture Switch", 0, ES1688_REC_DEV, 4, 1, 1), 93862306a36Sopenharmony_ci{ 93962306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 94062306a36Sopenharmony_ci .name = "Capture Source", 94162306a36Sopenharmony_ci .info = snd_es1688_info_mux, 94262306a36Sopenharmony_ci .get = snd_es1688_get_mux, 94362306a36Sopenharmony_ci .put = snd_es1688_put_mux, 94462306a36Sopenharmony_ci}, 94562306a36Sopenharmony_ci}; 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci#define ES1688_INIT_TABLE_SIZE (sizeof(snd_es1688_init_table)/2) 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_cistatic const unsigned char snd_es1688_init_table[][2] = { 95062306a36Sopenharmony_ci { ES1688_MASTER_DEV, 0 }, 95162306a36Sopenharmony_ci { ES1688_PCM_DEV, 0 }, 95262306a36Sopenharmony_ci { ES1688_LINE_DEV, 0 }, 95362306a36Sopenharmony_ci { ES1688_CD_DEV, 0 }, 95462306a36Sopenharmony_ci { ES1688_FM_DEV, 0 }, 95562306a36Sopenharmony_ci { ES1688_MIC_DEV, 0 }, 95662306a36Sopenharmony_ci { ES1688_AUX_DEV, 0 }, 95762306a36Sopenharmony_ci { ES1688_SPEAKER_DEV, 0 }, 95862306a36Sopenharmony_ci { ES1688_RECLEV_DEV, 0 }, 95962306a36Sopenharmony_ci { ES1688_REC_DEV, 0x17 } 96062306a36Sopenharmony_ci}; 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ciint snd_es1688_mixer(struct snd_card *card, struct snd_es1688 *chip) 96362306a36Sopenharmony_ci{ 96462306a36Sopenharmony_ci unsigned int idx; 96562306a36Sopenharmony_ci int err; 96662306a36Sopenharmony_ci unsigned char reg, val; 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci if (snd_BUG_ON(!chip || !card)) 96962306a36Sopenharmony_ci return -EINVAL; 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci strcpy(card->mixername, snd_es1688_chip_id(chip)); 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(snd_es1688_controls); idx++) { 97462306a36Sopenharmony_ci err = snd_ctl_add(card, snd_ctl_new1(&snd_es1688_controls[idx], chip)); 97562306a36Sopenharmony_ci if (err < 0) 97662306a36Sopenharmony_ci return err; 97762306a36Sopenharmony_ci } 97862306a36Sopenharmony_ci for (idx = 0; idx < ES1688_INIT_TABLE_SIZE; idx++) { 97962306a36Sopenharmony_ci reg = snd_es1688_init_table[idx][0]; 98062306a36Sopenharmony_ci val = snd_es1688_init_table[idx][1]; 98162306a36Sopenharmony_ci if (reg < 0xa0) 98262306a36Sopenharmony_ci snd_es1688_mixer_write(chip, reg, val); 98362306a36Sopenharmony_ci else 98462306a36Sopenharmony_ci snd_es1688_write(chip, reg, val); 98562306a36Sopenharmony_ci } 98662306a36Sopenharmony_ci return 0; 98762306a36Sopenharmony_ci} 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ciEXPORT_SYMBOL(snd_es1688_mixer_write); 99062306a36Sopenharmony_ciEXPORT_SYMBOL(snd_es1688_create); 99162306a36Sopenharmony_ciEXPORT_SYMBOL(snd_es1688_pcm); 99262306a36Sopenharmony_ciEXPORT_SYMBOL(snd_es1688_mixer); 993