// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Routines for control of ESS ES1688/688/488 chip
*/
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <linux/module.h>
#include <linux/io.h>
#include <sound/core.h>
#include <sound/es1688.h>
#include <sound/initval.h>
#include <asm/dma.h>
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("ESS ESx688 lowlevel module");
MODULE_LICENSE("GPL");
static int snd_es1688_dsp_command(struct snd_es1688 *chip, unsigned char val)
{
int i;
for (i = 10000; i; i--)
if ((inb(ES1688P(chip, STATUS)) & 0x80) == 0) {
outb(val, ES1688P(chip, COMMAND));
return 1;
}
#ifdef CONFIG_SND_DEBUG
printk(KERN_DEBUG "snd_es1688_dsp_command: timeout (0x%x)\n", val);
#endif
return 0;
}
static int snd_es1688_dsp_get_byte(struct snd_es1688 *chip)
{
int i;
for (i = 1000; i; i--)
if (inb(ES1688P(chip, DATA_AVAIL)) & 0x80)
return inb(ES1688P(chip, READ));
snd_printd("es1688 get byte failed: 0x%lx = 0x%x!!!\n", ES1688P(chip, DATA_AVAIL), inb(ES1688P(chip, DATA_AVAIL)));
return -ENODEV;
}
static int snd_es1688_write(struct snd_es1688 *chip,
unsigned char reg, unsigned char data)
{
if (!snd_es1688_dsp_command(chip, reg))
return 0;
return snd_es1688_dsp_command(chip, data);
}
static int snd_es1688_read(struct snd_es1688 *chip, unsigned char reg)
{
/* Read a byte from an extended mode register of ES1688 */
if (!snd_es1688_dsp_command(chip, 0xc0))
return -1;
if (!snd_es1688_dsp_command(chip, reg))
return -1;
return snd_es1688_dsp_get_byte(chip);
}
void snd_es1688_mixer_write(struct snd_es1688 *chip,
unsigned char reg, unsigned char data)
{
outb(reg, ES1688P(chip, MIXER_ADDR));
udelay(10);
outb(data, ES1688P(chip, MIXER_DATA));
udelay(10);
}
static unsigned char snd_es1688_mixer_read(struct snd_es1688 *chip, unsigned char reg)
{
unsigned char result;
outb(reg, ES1688P(chip, MIXER_ADDR));
udelay(10);
result = inb(ES1688P(chip, MIXER_DATA));
udelay(10);
return result;
}
int snd_es1688_reset(struct snd_es1688 *chip)
{
int i;
outb(3, ES1688P(chip, RESET)); /* valid only for ESS chips, SB -> 1 */
udelay(10);
outb(0, ES1688P(chip, RESET));
udelay(30);
for (i = 0; i < 1000 && !(inb(ES1688P(chip, DATA_AVAIL)) & 0x80); i++);
if (inb(ES1688P(chip, READ)) != 0xaa) {
snd_printd("ess_reset at 0x%lx: failed!!!\n", chip->port);
return -ENODEV;
}
snd_es1688_dsp_command(chip, 0xc6); /* enable extended mode */
return 0;
}
EXPORT_SYMBOL(snd_es1688_reset);
static int snd_es1688_probe(struct snd_es1688 *chip)
{
unsigned long flags;
unsigned short major, minor;
int i;
/*
* initialization sequence
*/
spin_lock_irqsave(&chip->reg_lock, flags); /* Some ESS1688 cards need this */
inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */
inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */
inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */
inb(ES1688P(chip, ENABLE2)); /* ENABLE2 */
inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */
inb(ES1688P(chip, ENABLE2)); /* ENABLE2 */
inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */
inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */
inb(ES1688P(chip, ENABLE2)); /* ENABLE2 */
inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */
inb(ES1688P(chip, ENABLE0)); /* ENABLE0 */
if (snd_es1688_reset(chip) < 0