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