linux_dsm_epyc7002/sound/isa/es1688/es1688_lib.c
Thomas Gleixner 1a59d1b8e0 treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 156
Based on 1 normalized pattern(s):

  this program is free software you can redistribute it and or modify
  it under the terms of the gnu general public license as published by
  the free software foundation either version 2 of the license or at
  your option any later version this program is distributed in the
  hope that it will be useful but without any warranty without even
  the implied warranty of merchantability or fitness for a particular
  purpose see the gnu general public license for more details you
  should have received a copy of the gnu general public license along
  with this program if not write to the free software foundation inc
  59 temple place suite 330 boston ma 02111 1307 usa

extracted by the scancode license scanner the SPDX license identifier

  GPL-2.0-or-later

has been chosen to replace the boilerplate/reference in 1334 file(s).

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Allison Randal <allison@lohutok.net>
Reviewed-by: Richard Fontana <rfontana@redhat.com>
Cc: linux-spdx@vger.kernel.org
Link: https://lkml.kernel.org/r/20190527070033.113240726@linutronix.de
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2019-05-30 11:26:35 -07:00

1015 lines
29 KiB
C

// 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) {
snd_printdd("ESS: [0x%lx] reset failed... 0x%x\n", chip->port, inb(ES1688P(chip, READ)));
spin_unlock_irqrestore(&chip->reg_lock, flags);
return -ENODEV;
}
snd_es1688_dsp_command(chip, 0xe7); /* return identification */
for (i = 1000, major = minor = 0; i; i--) {
if (inb(ES1688P(chip, DATA_AVAIL)) & 0x80) {
if (major == 0) {
major = inb(ES1688P(chip, READ));
} else {
minor = inb(ES1688P(chip, READ));
}
}
}
spin_unlock_irqrestore(&chip->reg_lock, flags);
snd_printdd("ESS: [0x%lx] found.. major = 0x%x, minor = 0x%x\n", chip->port, major, minor);
chip->version = (major << 8) | minor;
if (!chip->version)
return -ENODEV; /* probably SB */
switch (chip->version & 0xfff0) {
case 0x4880:
snd_printk(KERN_ERR "[0x%lx] ESS: AudioDrive ES488 detected, "
"but driver is in another place\n", chip->port);
return -ENODEV;
case 0x6880:
break;
default:
snd_printk(KERN_ERR "[0x%lx] ESS: unknown AudioDrive chip "
"with version 0x%x (Jazz16 soundcard?)\n",
chip->port, chip->version);
return -ENODEV;
}
spin_lock_irqsave(&chip->reg_lock, flags);
snd_es1688_write(chip, 0xb1, 0x10); /* disable IRQ */
snd_es1688_write(chip, 0xb2, 0x00); /* disable DMA */
spin_unlock_irqrestore(&chip->reg_lock, flags);
/* enable joystick, but disable OPL3 */
spin_lock_irqsave(&chip->mixer_lock, flags);
snd_es1688_mixer_write(chip, 0x40, 0x01);
spin_unlock_irqrestore(&chip->mixer_lock, flags);
return 0;
}
static int snd_es1688_init(struct snd_es1688 * chip, int enable)
{
static int irqs[16] = {-1, -1, 0, -1, -1, 1, -1, 2, -1, 0, 3, -1, -1, -1, -1, -1};
unsigned long flags;
int cfg, irq_bits, dma, dma_bits, tmp, tmp1;
/* ok.. setup MPU-401 port and joystick and OPL3 */
cfg = 0x01; /* enable joystick, but disable OPL3 */
if (enable && chip->mpu_port >= 0x300 && chip->mpu_irq > 0 && chip->hardware != ES1688_HW_688) {
tmp = (chip->mpu_port & 0x0f0) >> 4;
if (tmp <= 3) {
switch (chip->mpu_irq) {
case 9:
tmp1 = 4;
break;
case 5:
tmp1 = 5;
break;
case 7:
tmp1 = 6;
break;
case 10:
tmp1 = 7;
break;
default:
tmp1 = 0;
}
if (tmp1) {
cfg |= (tmp << 3) | (tmp1 << 5);
}
}
}
#if 0
snd_printk(KERN_DEBUG "mpu cfg = 0x%x\n", cfg);
#endif
spin_lock_irqsave(&chip->reg_lock, flags);
snd_es1688_mixer_write(chip, 0x40, cfg);
spin_unlock_irqrestore(&chip->reg_lock, flags);
/* --- */
spin_lock_irqsave(&chip->reg_lock, flags);
snd_es1688_read(chip, 0xb1);
snd_es1688_read(chip, 0xb2);
spin_unlock_irqrestore(&chip->reg_lock, flags);
if (enable) {
cfg = 0xf0; /* enable only DMA counter interrupt */
irq_bits = irqs[chip->irq & 0x0f];
if (irq_bits < 0) {
snd_printk(KERN_ERR "[0x%lx] ESS: bad IRQ %d "
"for ES1688 chip!!\n",
chip->port, chip->irq);
#if 0
irq_bits = 0;
cfg = 0x10;
#endif
return -EINVAL;
}
spin_lock_irqsave(&chip->reg_lock, flags);
snd_es1688_write(chip, 0xb1, cfg | (irq_bits << 2));
spin_unlock_irqrestore(&chip->reg_lock, flags);
cfg = 0xf0; /* extended mode DMA enable */
dma = chip->dma8;
if (dma > 3 || dma == 2) {
snd_printk(KERN_ERR "[0x%lx] ESS: bad DMA channel %d "
"for ES1688 chip!!\n", chip->port, dma);
#if 0
dma_bits = 0;
cfg = 0x00; /* disable all DMA */
#endif
return -EINVAL;
} else {
dma_bits = dma;
if (dma != 3)
dma_bits++;
}
spin_lock_irqsave(&chip->reg_lock, flags);
snd_es1688_write(chip, 0xb2, cfg | (dma_bits << 2));
spin_unlock_irqrestore(&chip->reg_lock, flags);
} else {
spin_lock_irqsave(&chip->reg_lock, flags);
snd_es1688_write(chip, 0xb1, 0x10); /* disable IRQ */
snd_es1688_write(chip, 0xb2, 0x00); /* disable DMA */
spin_unlock_irqrestore(&chip->reg_lock, flags);
}
spin_lock_irqsave(&chip->reg_lock, flags);
snd_es1688_read(chip, 0xb1);
snd_es1688_read(chip, 0xb2);
snd_es1688_reset(chip);
spin_unlock_irqrestore(&chip->reg_lock, flags);
return 0;
}
/*
*/
static const struct snd_ratnum clocks[2] = {
{
.num = 795444,
.den_min = 1,
.den_max = 128,
.den_step = 1,
},
{
.num = 397722,
.den_min = 1,
.den_max = 128,
.den_step = 1,
}
};
static const struct snd_pcm_hw_constraint_ratnums hw_constraints_clocks = {
.nrats = 2,
.rats = clocks,
};
static void snd_es1688_set_rate(struct snd_es1688 *chip, struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned int bits, divider;
if (runtime->rate_num == clocks[0].num)
bits = 256 - runtime->rate_den;
else
bits = 128 - runtime->rate_den;
/* set filter register */
divider = 256 - 7160000*20/(8*82*runtime->rate);
/* write result to hardware */
snd_es1688_write(chip, 0xa1, bits);
snd_es1688_write(chip, 0xa2, divider);
}
static int snd_es1688_ioctl(struct snd_pcm_substream *substream,
unsigned int cmd, void *arg)
{
return snd_pcm_lib_ioctl(substream, cmd, arg);
}
static int snd_es1688_trigger(struct snd_es1688 *chip, int cmd, unsigned char value)
{
int val;
if (cmd == SNDRV_PCM_TRIGGER_STOP) {
value = 0x00;
} else if (cmd != SNDRV_PCM_TRIGGER_START) {
return -EINVAL;
}
spin_lock(&chip->reg_lock);
chip->trigger_value = value;
val = snd_es1688_read(chip, 0xb8);
if ((val < 0) || (val & 0x0f) == value) {
spin_unlock(&chip->reg_lock);
return -EINVAL; /* something is wrong */
}
#if 0
printk(KERN_DEBUG "trigger: val = 0x%x, value = 0x%x\n", val, value);
printk(KERN_DEBUG "trigger: pointer = 0x%x\n",
snd_dma_pointer(chip->dma8, chip->dma_size));
#endif
snd_es1688_write(chip, 0xb8, (val & 0xf0) | value);
spin_unlock(&chip->reg_lock);
return 0;
}
static int snd_es1688_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
}
static int snd_es1688_hw_free(struct snd_pcm_substream *substream)
{
return snd_pcm_lib_free_pages(substream);
}
static int snd_es1688_playback_prepare(struct snd_pcm_substream *substream)
{
unsigned long flags;
struct snd_es1688 *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned int size = snd_pcm_lib_buffer_bytes(substream);
unsigned int count = snd_pcm_lib_period_bytes(substream);
chip->dma_size = size;
spin_lock_irqsave(&chip->reg_lock, flags);
snd_es1688_reset(chip);
snd_es1688_set_rate(chip, substream);
snd_es1688_write(chip, 0xb8, 4); /* auto init DMA mode */
snd_es1688_write(chip, 0xa8, (snd_es1688_read(chip, 0xa8) & ~0x03) | (3 - runtime->channels));
snd_es1688_write(chip, 0xb9, 2); /* demand mode (4 bytes/request) */
if (runtime->channels == 1) {
if (snd_pcm_format_width(runtime->format) == 8) {
/* 8. bit mono */
snd_es1688_write(chip, 0xb6, 0x80);
snd_es1688_write(chip, 0xb7, 0x51);
snd_es1688_write(chip, 0xb7, 0xd0);
} else {
/* 16. bit mono */
snd_es1688_write(chip, 0xb6, 0x00);
snd_es1688_write(chip, 0xb7, 0x71);
snd_es1688_write(chip, 0xb7, 0xf4);
}
} else {
if (snd_pcm_format_width(runtime->format) == 8) {
/* 8. bit stereo */
snd_es1688_write(chip, 0xb6, 0x80);
snd_es1688_write(chip, 0xb7, 0x51);
snd_es1688_write(chip, 0xb7, 0x98);
} else {
/* 16. bit stereo */
snd_es1688_write(chip, 0xb6, 0x00);
snd_es1688_write(chip, 0xb7, 0x71);
snd_es1688_write(chip, 0xb7, 0xbc);
}
}
snd_es1688_write(chip, 0xb1, (snd_es1688_read(chip, 0xb1) & 0x0f) | 0x50);
snd_es1688_write(chip, 0xb2, (snd_es1688_read(chip, 0xb2) & 0x0f) | 0x50);
snd_es1688_dsp_command(chip, ES1688_DSP_CMD_SPKON);
spin_unlock_irqrestore(&chip->reg_lock, flags);
/* --- */
count = -count;
snd_dma_program(chip->dma8, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT);
spin_lock_irqsave(&chip->reg_lock, flags);
snd_es1688_write(chip, 0xa4, (unsigned char) count);
snd_es1688_write(chip, 0xa5, (unsigned char) (count >> 8));
spin_unlock_irqrestore(&chip->reg_lock, flags);
return 0;
}
static int snd_es1688_playback_trigger(struct snd_pcm_substream *substream,
int cmd)
{
struct snd_es1688 *chip = snd_pcm_substream_chip(substream);
return snd_es1688_trigger(chip, cmd, 0x05);
}
static int snd_es1688_capture_prepare(struct snd_pcm_substream *substream)
{
unsigned long flags;
struct snd_es1688 *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned int size = snd_pcm_lib_buffer_bytes(substream);
unsigned int count = snd_pcm_lib_period_bytes(substream);
chip->dma_size = size;
spin_lock_irqsave(&chip->reg_lock, flags);
snd_es1688_reset(chip);
snd_es1688_set_rate(chip, substream);
snd_es1688_dsp_command(chip, ES1688_DSP_CMD_SPKOFF);
snd_es1688_write(chip, 0xb8, 0x0e); /* auto init DMA mode */
snd_es1688_write(chip, 0xa8, (snd_es1688_read(chip, 0xa8) & ~0x03) | (3 - runtime->channels));
snd_es1688_write(chip, 0xb9, 2); /* demand mode (4 bytes/request) */
if (runtime->channels == 1) {
if (snd_pcm_format_width(runtime->format) == 8) {
/* 8. bit mono */
snd_es1688_write(chip, 0xb7, 0x51);
snd_es1688_write(chip, 0xb7, 0xd0);
} else {
/* 16. bit mono */
snd_es1688_write(chip, 0xb7, 0x71);
snd_es1688_write(chip, 0xb7, 0xf4);
}
} else {
if (snd_pcm_format_width(runtime->format) == 8) {
/* 8. bit stereo */
snd_es1688_write(chip, 0xb7, 0x51);
snd_es1688_write(chip, 0xb7, 0x98);
} else {
/* 16. bit stereo */
snd_es1688_write(chip, 0xb7, 0x71);
snd_es1688_write(chip, 0xb7, 0xbc);
}
}
snd_es1688_write(chip, 0xb1, (snd_es1688_read(chip, 0xb1) & 0x0f) | 0x50);
snd_es1688_write(chip, 0xb2, (snd_es1688_read(chip, 0xb2) & 0x0f) | 0x50);
spin_unlock_irqrestore(&chip->reg_lock, flags);
/* --- */
count = -count;
snd_dma_program(chip->dma8, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT);
spin_lock_irqsave(&chip->reg_lock, flags);
snd_es1688_write(chip, 0xa4, (unsigned char) count);
snd_es1688_write(chip, 0xa5, (unsigned char) (count >> 8));
spin_unlock_irqrestore(&chip->reg_lock, flags);
return 0;
}
static int snd_es1688_capture_trigger(struct snd_pcm_substream *substream,
int cmd)
{
struct snd_es1688 *chip = snd_pcm_substream_chip(substream);
return snd_es1688_trigger(chip, cmd, 0x0f);
}
static irqreturn_t snd_es1688_interrupt(int irq, void *dev_id)
{
struct snd_es1688 *chip = dev_id;
if (chip->trigger_value == 0x05) /* ok.. playback is active */
snd_pcm_period_elapsed(chip->playback_substream);
if (chip->trigger_value == 0x0f) /* ok.. capture is active */
snd_pcm_period_elapsed(chip->capture_substream);
inb(ES1688P(chip, DATA_AVAIL)); /* ack interrupt */
return IRQ_HANDLED;
}
static snd_pcm_uframes_t snd_es1688_playback_pointer(struct snd_pcm_substream *substream)
{
struct snd_es1688 *chip = snd_pcm_substream_chip(substream);
size_t ptr;
if (chip->trigger_value != 0x05)
return 0;
ptr = snd_dma_pointer(chip->dma8, chip->dma_size);
return bytes_to_frames(substream->runtime, ptr);
}
static snd_pcm_uframes_t snd_es1688_capture_pointer(struct snd_pcm_substream *substream)
{
struct snd_es1688 *chip = snd_pcm_substream_chip(substream);
size_t ptr;
if (chip->trigger_value != 0x0f)
return 0;
ptr = snd_dma_pointer(chip->dma8, chip->dma_size);
return bytes_to_frames(substream->runtime, ptr);
}
/*
*/
static const struct snd_pcm_hardware snd_es1688_playback =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID),
.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
.rate_min = 4000,
.rate_max = 48000,
.channels_min = 1,
.channels_max = 2,
.buffer_bytes_max = 65536,
.period_bytes_min = 64,
.period_bytes_max = 65536,
.periods_min = 1,
.periods_max = 1024,
.fifo_size = 0,
};
static const struct snd_pcm_hardware snd_es1688_capture =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID),
.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
.rate_min = 4000,
.rate_max = 48000,
.channels_min = 1,
.channels_max = 2,
.buffer_bytes_max = 65536,
.period_bytes_min = 64,
.period_bytes_max = 65536,
.periods_min = 1,
.periods_max = 1024,
.fifo_size = 0,
};
/*
*/
static int snd_es1688_playback_open(struct snd_pcm_substream *substream)
{
struct snd_es1688 *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
if (chip->capture_substream != NULL)
return -EAGAIN;
chip->playback_substream = substream;
runtime->hw = snd_es1688_playback;
snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
&hw_constraints_clocks);
return 0;
}
static int snd_es1688_capture_open(struct snd_pcm_substream *substream)
{
struct snd_es1688 *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
if (chip->playback_substream != NULL)
return -EAGAIN;
chip->capture_substream = substream;
runtime->hw = snd_es1688_capture;
snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
&hw_constraints_clocks);
return 0;
}
static int snd_es1688_playback_close(struct snd_pcm_substream *substream)
{
struct snd_es1688 *chip = snd_pcm_substream_chip(substream);
chip->playback_substream = NULL;
return 0;
}
static int snd_es1688_capture_close(struct snd_pcm_substream *substream)
{
struct snd_es1688 *chip = snd_pcm_substream_chip(substream);
chip->capture_substream = NULL;
return 0;
}
static int snd_es1688_free(struct snd_es1688 *chip)
{
if (chip->hardware != ES1688_HW_UNDEF)
snd_es1688_init(chip, 0);
release_and_free_resource(chip->res_port);
if (chip->irq >= 0)
free_irq(chip->irq, (void *) chip);
if (chip->dma8 >= 0) {
disable_dma(chip->dma8);
free_dma(chip->dma8);
}
return 0;
}
static int snd_es1688_dev_free(struct snd_device *device)
{
struct snd_es1688 *chip = device->device_data;
return snd_es1688_free(chip);
}
static const char *snd_es1688_chip_id(struct snd_es1688 *chip)
{
static char tmp[16];
sprintf(tmp, "ES%s688 rev %i", chip->hardware == ES1688_HW_688 ? "" : "1", chip->version & 0x0f);
return tmp;
}
int snd_es1688_create(struct snd_card *card,
struct snd_es1688 *chip,
unsigned long port,
unsigned long mpu_port,
int irq,
int mpu_irq,
int dma8,
unsigned short hardware)
{
static struct snd_device_ops ops = {
.dev_free = snd_es1688_dev_free,
};
int err;
if (chip == NULL)
return -ENOMEM;
chip->irq = -1;
chip->dma8 = -1;
chip->hardware = ES1688_HW_UNDEF;
chip->res_port = request_region(port + 4, 12, "ES1688");
if (chip->res_port == NULL) {
snd_printk(KERN_ERR "es1688: can't grab port 0x%lx\n", port + 4);
err = -EBUSY;
goto exit;
}
err = request_irq(irq, snd_es1688_interrupt, 0, "ES1688", (void *) chip);
if (err < 0) {
snd_printk(KERN_ERR "es1688: can't grab IRQ %d\n", irq);
goto exit;
}
chip->irq = irq;
err = request_dma(dma8, "ES1688");
if (err < 0) {
snd_printk(KERN_ERR "es1688: can't grab DMA8 %d\n", dma8);
goto exit;
}
chip->dma8 = dma8;
spin_lock_init(&chip->reg_lock);
spin_lock_init(&chip->mixer_lock);
chip->port = port;
mpu_port &= ~0x000f;
if (mpu_port < 0x300 || mpu_port > 0x330)
mpu_port = 0;
chip->mpu_port = mpu_port;
chip->mpu_irq = mpu_irq;
chip->hardware = hardware;
err = snd_es1688_probe(chip);
if (err < 0)
goto exit;
err = snd_es1688_init(chip, 1);
if (err < 0)
goto exit;
/* Register device */
err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
exit:
if (err)
snd_es1688_free(chip);
return err;
}
static const struct snd_pcm_ops snd_es1688_playback_ops = {
.open = snd_es1688_playback_open,
.close = snd_es1688_playback_close,
.ioctl = snd_es1688_ioctl,
.hw_params = snd_es1688_hw_params,
.hw_free = snd_es1688_hw_free,
.prepare = snd_es1688_playback_prepare,
.trigger = snd_es1688_playback_trigger,
.pointer = snd_es1688_playback_pointer,
};
static const struct snd_pcm_ops snd_es1688_capture_ops = {
.open = snd_es1688_capture_open,
.close = snd_es1688_capture_close,
.ioctl = snd_es1688_ioctl,
.hw_params = snd_es1688_hw_params,
.hw_free = snd_es1688_hw_free,
.prepare = snd_es1688_capture_prepare,
.trigger = snd_es1688_capture_trigger,
.pointer = snd_es1688_capture_pointer,
};
int snd_es1688_pcm(struct snd_card *card, struct snd_es1688 *chip, int device)
{
struct snd_pcm *pcm;
int err;
err = snd_pcm_new(card, "ESx688", device, 1, 1, &pcm);
if (err < 0)
return err;
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_es1688_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_es1688_capture_ops);
pcm->private_data = chip;
pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX;
strcpy(pcm->name, snd_es1688_chip_id(chip));
chip->pcm = pcm;
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
card->dev,
64*1024, 64*1024);
return 0;
}
/*
* MIXER part
*/
static int snd_es1688_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
static const char * const texts[8] = {
"Mic", "Mic Master", "CD", "AOUT",
"Mic1", "Mix", "Line", "Master"
};
return snd_ctl_enum_info(uinfo, 1, 8, texts);
}
static int snd_es1688_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_es1688 *chip = snd_kcontrol_chip(kcontrol);
ucontrol->value.enumerated.item[0] = snd_es1688_mixer_read(chip, ES1688_REC_DEV) & 7;
return 0;
}
static int snd_es1688_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_es1688 *chip = snd_kcontrol_chip(kcontrol);
unsigned long flags;
unsigned char oval, nval;
int change;
if (ucontrol->value.enumerated.item[0] > 8)
return -EINVAL;
spin_lock_irqsave(&chip->reg_lock, flags);
oval = snd_es1688_mixer_read(chip, ES1688_REC_DEV);
nval = (ucontrol->value.enumerated.item[0] & 7) | (oval & ~15);
change = nval != oval;
if (change)
snd_es1688_mixer_write(chip, ES1688_REC_DEV, nval);
spin_unlock_irqrestore(&chip->reg_lock, flags);
return change;
}
#define ES1688_SINGLE(xname, xindex, reg, shift, mask, invert) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
.info = snd_es1688_info_single, \
.get = snd_es1688_get_single, .put = snd_es1688_put_single, \
.private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) }
static int snd_es1688_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
int mask = (kcontrol->private_value >> 16) & 0xff;
uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = mask;
return 0;
}
static int snd_es1688_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_es1688 *chip = snd_kcontrol_chip(kcontrol);
unsigned long flags;
int reg = kcontrol->private_value & 0xff;
int shift = (kcontrol->private_value >> 8) & 0xff;
int mask = (kcontrol->private_value >> 16) & 0xff;
int invert = (kcontrol->private_value >> 24) & 0xff;
spin_lock_irqsave(&chip->reg_lock, flags);
ucontrol->value.integer.value[0] = (snd_es1688_mixer_read(chip, reg) >> shift) & mask;
spin_unlock_irqrestore(&chip->reg_lock, flags);
if (invert)
ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
return 0;
}
static int snd_es1688_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_es1688 *chip = snd_kcontrol_chip(kcontrol);
unsigned long flags;
int reg = kcontrol->private_value & 0xff;
int shift = (kcontrol->private_value >> 8) & 0xff;
int mask = (kcontrol->private_value >> 16) & 0xff;
int invert = (kcontrol->private_value >> 24) & 0xff;
int change;
unsigned char oval, nval;
nval = (ucontrol->value.integer.value[0] & mask);
if (invert)
nval = mask - nval;
nval <<= shift;
spin_lock_irqsave(&chip->reg_lock, flags);
oval = snd_es1688_mixer_read(chip, reg);
nval = (oval & ~(mask << shift)) | nval;
change = nval != oval;
if (change)
snd_es1688_mixer_write(chip, reg, nval);
spin_unlock_irqrestore(&chip->reg_lock, flags);
return change;
}
#define ES1688_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
.info = snd_es1688_info_double, \
.get = snd_es1688_get_double, .put = snd_es1688_put_double, \
.private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
static int snd_es1688_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
int mask = (kcontrol->private_value >> 24) & 0xff;
uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 2;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = mask;
return 0;
}
static int snd_es1688_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_es1688 *chip = snd_kcontrol_chip(kcontrol);
unsigned long flags;
int left_reg = kcontrol->private_value & 0xff;
int right_reg = (kcontrol->private_value >> 8) & 0xff;
int shift_left = (kcontrol->private_value >> 16) & 0x07;
int shift_right = (kcontrol->private_value >> 19) & 0x07;
int mask = (kcontrol->private_value >> 24) & 0xff;
int invert = (kcontrol->private_value >> 22) & 1;
unsigned char left, right;
spin_lock_irqsave(&chip->reg_lock, flags);
if (left_reg < 0xa0)
left = snd_es1688_mixer_read(chip, left_reg);
else
left = snd_es1688_read(chip, left_reg);
if (left_reg != right_reg) {
if (right_reg < 0xa0)
right = snd_es1688_mixer_read(chip, right_reg);
else
right = snd_es1688_read(chip, right_reg);
} else
right = left;
spin_unlock_irqrestore(&chip->reg_lock, flags);
ucontrol->value.integer.value[0] = (left >> shift_left) & mask;
ucontrol->value.integer.value[1] = (right >> shift_right) & mask;
if (invert) {
ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
}
return 0;
}
static int snd_es1688_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_es1688 *chip = snd_kcontrol_chip(kcontrol);
unsigned long flags;
int left_reg = kcontrol->private_value & 0xff;
int right_reg = (kcontrol->private_value >> 8) & 0xff;
int shift_left = (kcontrol->private_value >> 16) & 0x07;
int shift_right = (kcontrol->private_value >> 19) & 0x07;
int mask = (kcontrol->private_value >> 24) & 0xff;
int invert = (kcontrol->private_value >> 22) & 1;
int change;
unsigned char val1, val2, oval1, oval2;
val1 = ucontrol->value.integer.value[0] & mask;
val2 = ucontrol->value.integer.value[1] & mask;
if (invert) {
val1 = mask - val1;
val2 = mask - val2;
}
val1 <<= shift_left;
val2 <<= shift_right;
spin_lock_irqsave(&chip->reg_lock, flags);
if (left_reg != right_reg) {
if (left_reg < 0xa0)
oval1 = snd_es1688_mixer_read(chip, left_reg);
else
oval1 = snd_es1688_read(chip, left_reg);
if (right_reg < 0xa0)
oval2 = snd_es1688_mixer_read(chip, right_reg);
else
oval2 = snd_es1688_read(chip, right_reg);
val1 = (oval1 & ~(mask << shift_left)) | val1;
val2 = (oval2 & ~(mask << shift_right)) | val2;
change = val1 != oval1 || val2 != oval2;
if (change) {
if (left_reg < 0xa0)
snd_es1688_mixer_write(chip, left_reg, val1);
else
snd_es1688_write(chip, left_reg, val1);
if (right_reg < 0xa0)
snd_es1688_mixer_write(chip, right_reg, val1);
else
snd_es1688_write(chip, right_reg, val1);
}
} else {
if (left_reg < 0xa0)
oval1 = snd_es1688_mixer_read(chip, left_reg);
else
oval1 = snd_es1688_read(chip, left_reg);
val1 = (oval1 & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2;
change = val1 != oval1;
if (change) {
if (left_reg < 0xa0)
snd_es1688_mixer_write(chip, left_reg, val1);
else
snd_es1688_write(chip, left_reg, val1);
}
}
spin_unlock_irqrestore(&chip->reg_lock, flags);
return change;
}
static struct snd_kcontrol_new snd_es1688_controls[] = {
ES1688_DOUBLE("Master Playback Volume", 0, ES1688_MASTER_DEV, ES1688_MASTER_DEV, 4, 0, 15, 0),
ES1688_DOUBLE("PCM Playback Volume", 0, ES1688_PCM_DEV, ES1688_PCM_DEV, 4, 0, 15, 0),
ES1688_DOUBLE("Line Playback Volume", 0, ES1688_LINE_DEV, ES1688_LINE_DEV, 4, 0, 15, 0),
ES1688_DOUBLE("CD Playback Volume", 0, ES1688_CD_DEV, ES1688_CD_DEV, 4, 0, 15, 0),
ES1688_DOUBLE("FM Playback Volume", 0, ES1688_FM_DEV, ES1688_FM_DEV, 4, 0, 15, 0),
ES1688_DOUBLE("Mic Playback Volume", 0, ES1688_MIC_DEV, ES1688_MIC_DEV, 4, 0, 15, 0),
ES1688_DOUBLE("Aux Playback Volume", 0, ES1688_AUX_DEV, ES1688_AUX_DEV, 4, 0, 15, 0),
ES1688_SINGLE("Beep Playback Volume", 0, ES1688_SPEAKER_DEV, 0, 7, 0),
ES1688_DOUBLE("Capture Volume", 0, ES1688_RECLEV_DEV, ES1688_RECLEV_DEV, 4, 0, 15, 0),
ES1688_SINGLE("Capture Switch", 0, ES1688_REC_DEV, 4, 1, 1),
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Capture Source",
.info = snd_es1688_info_mux,
.get = snd_es1688_get_mux,
.put = snd_es1688_put_mux,
},
};
#define ES1688_INIT_TABLE_SIZE (sizeof(snd_es1688_init_table)/2)
static unsigned char snd_es1688_init_table[][2] = {
{ ES1688_MASTER_DEV, 0 },
{ ES1688_PCM_DEV, 0 },
{ ES1688_LINE_DEV, 0 },
{ ES1688_CD_DEV, 0 },
{ ES1688_FM_DEV, 0 },
{ ES1688_MIC_DEV, 0 },
{ ES1688_AUX_DEV, 0 },
{ ES1688_SPEAKER_DEV, 0 },
{ ES1688_RECLEV_DEV, 0 },
{ ES1688_REC_DEV, 0x17 }
};
int snd_es1688_mixer(struct snd_card *card, struct snd_es1688 *chip)
{
unsigned int idx;
int err;
unsigned char reg, val;
if (snd_BUG_ON(!chip || !card))
return -EINVAL;
strcpy(card->mixername, snd_es1688_chip_id(chip));
for (idx = 0; idx < ARRAY_SIZE(snd_es1688_controls); idx++) {
if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es1688_controls[idx], chip))) < 0)
return err;
}
for (idx = 0; idx < ES1688_INIT_TABLE_SIZE; idx++) {
reg = snd_es1688_init_table[idx][0];
val = snd_es1688_init_table[idx][1];
if (reg < 0xa0)
snd_es1688_mixer_write(chip, reg, val);
else
snd_es1688_write(chip, reg, val);
}
return 0;
}
EXPORT_SYMBOL(snd_es1688_mixer_write);
EXPORT_SYMBOL(snd_es1688_create);
EXPORT_SYMBOL(snd_es1688_pcm);
EXPORT_SYMBOL(snd_es1688_mixer);