ce356b7017
- fix all warnings during compilation - fix obvious bugs - add support for more cards Now supported: - M-Audio Delta Dio 2496 - M-Audio Audiophile 2496 - Terratec DMX 6fire Known bugs (detected by Nokolas and Stefan): - $ kldunload snd_ak452x.ko Warning: memory type ak452x leaked memory on destroy (1 allocations, 64 bytes leaked). - No sound in KDE: Everything works fine at the console but when I load KDE (3.5.3) the sound stutters and plays at less then 1/2 speed. - 'mixer: WRITE_MIXER: Device not configured' The message repeats x times at system startup, x = whatever hw.snd.maxautovchans is set to. (this is because only vol, pcm and line are supported, but the driver shows more than those mixer devices and setting those additional mixers results in this error message) - vchans don't work - 24 bit playback not supported (only 16/32 bit) - after kld(un)loading some times, the card fails to be probed until reboot Datasheets are available from: http://www.nbritton.org/uploads/envy24/ http://www.asahi-kasei.co.jp/akm/en/product/ak4528/ak4528_f01e.pdf http://www.asahi-kasei.co.jp/akm/en/product/ak4528/ekd4528-01.pdf http://www.asahi-kasei.co.jp/akm/en/product/ak4524/ak4524_f03e.pdf http://www.asahi-kasei.co.jp/akm/en/product/ak4524/ekd4524.pdf http://www.wolfson.co.uk/uploads/documents/en/WM8728.pdf http://www.richtech.co.kr/down/richtek/RT9131.pdf http://xkodi.svobodno.com/xkodi/space71.html http://people.freebsd.org/~lofi/envy24.pdf http://people.freebsd.org/~lofi/4524.pdf Submitted by: Konstantin Dimitrov <kosio.dimitrov@gmail.com> Tested by: Nikolas Britton <nikolas.britton@gmail.com> Stefan Ehmann <shoesoft@gmx.net>
248 lines
6.5 KiB
C
248 lines
6.5 KiB
C
/*
|
|
* Copyright (c) 2001 Katsurajima Naoto <raven@katsurajima.seya.yokohama.jp>
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHERIN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
* $FreeBSD$
|
|
*/
|
|
|
|
#include <dev/sound/pcm/sound.h>
|
|
|
|
#include <dev/sound/pci/ak452x.h>
|
|
|
|
MALLOC_DEFINE(M_AK452X, "ak452x", "ak452x codec");
|
|
|
|
#define AK452X_NAMELEN 16
|
|
struct ak452x_info {
|
|
device_t dev;
|
|
ak452x_ctrl ctrl;
|
|
void *devinfo;
|
|
int num; /* number of this device */
|
|
unsigned int type; /* codec type */
|
|
unsigned int cif; /* Controll data Interface Format (0/1) */
|
|
unsigned int format; /* data format and master clock frequency */
|
|
unsigned int dvc; /* De-emphasis and Volume Control */
|
|
unsigned int left, right;
|
|
char name[AK452X_NAMELEN];
|
|
void *lock;
|
|
};
|
|
|
|
static void
|
|
ak452x_wrbit(struct ak452x_info *codec, int bit)
|
|
{
|
|
unsigned int cs, cdti;
|
|
if (codec->cif)
|
|
cs = 1;
|
|
else
|
|
cs = 0;
|
|
if (bit)
|
|
cdti = 1;
|
|
else
|
|
cdti = 0;
|
|
codec->ctrl(codec->devinfo, cs, 0, cdti);
|
|
DELAY(1);
|
|
codec->ctrl(codec->devinfo, cs, 1, cdti);
|
|
DELAY(1);
|
|
|
|
return;
|
|
}
|
|
|
|
static void
|
|
ak452x_wrcd(struct ak452x_info *codec, int reg, u_int8_t val)
|
|
{
|
|
int mask;
|
|
|
|
#if(0)
|
|
device_printf(codec->dev, "ak452x_wrcd(codec, 0x%02x, 0x%02x)\n", reg, val);
|
|
#endif
|
|
/* start */
|
|
if (codec->cif)
|
|
codec->ctrl(codec->devinfo, 1, 1, 0);
|
|
else
|
|
codec->ctrl(codec->devinfo, 0, 1, 0);
|
|
DELAY(1);
|
|
/* chip address */
|
|
ak452x_wrbit(codec, 1);
|
|
ak452x_wrbit(codec, 0);
|
|
/* write */
|
|
ak452x_wrbit(codec, 1);
|
|
/* register address */
|
|
for (mask = 0x10; mask != 0; mask >>= 1)
|
|
ak452x_wrbit(codec, reg & mask);
|
|
/* data */
|
|
for (mask = 0x80; mask != 0; mask >>= 1)
|
|
ak452x_wrbit(codec, val & mask);
|
|
/* stop */
|
|
DELAY(1);
|
|
if (codec->cif) {
|
|
codec->ctrl(codec->devinfo, 0, 1, 0);
|
|
DELAY(1);
|
|
codec->ctrl(codec->devinfo, 1, 1, 0);
|
|
}
|
|
else {
|
|
codec->ctrl(codec->devinfo, 1, 1, 0);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
struct ak452x_info *
|
|
ak452x_create(device_t dev, void *devinfo, int num, ak452x_ctrl ctrl)
|
|
{
|
|
struct ak452x_info *codec;
|
|
|
|
#if(0)
|
|
device_printf(dev, "ak452x_create(dev, devinfo, %d, ctrl)\n", num);
|
|
#endif
|
|
codec = (struct ak452x_info *)malloc(sizeof *codec, M_AK452X, M_NOWAIT);
|
|
if (codec == NULL)
|
|
return NULL;
|
|
|
|
snprintf(codec->name, AK452X_NAMELEN, "%s:ak452x%d", device_get_nameunit(dev), num);
|
|
codec->lock = snd_mtxcreate(codec->name, codec->name);
|
|
codec->dev = dev;
|
|
codec->ctrl = ctrl;
|
|
codec->devinfo = devinfo;
|
|
codec->num = num;
|
|
codec->type = AK452X_TYPE_4524;
|
|
codec->cif = 0;
|
|
codec->format = AK452X_FORMAT_I2S | AK452X_FORMAT_256FSN | AK452X_FORMAT_1X;
|
|
codec->dvc = AK452X_DVC_DEMOFF | AK452X_DVC_ZTM1024 | AK452X_DVC_ZCE;
|
|
|
|
return codec;
|
|
}
|
|
|
|
void
|
|
ak452x_destroy(struct ak452x_info *codec)
|
|
{
|
|
snd_mtxfree(codec->lock);
|
|
free(codec, M_AK452X);
|
|
}
|
|
|
|
void
|
|
ak452x_settype(struct ak452x_info *codec, unsigned int type)
|
|
{
|
|
snd_mtxlock(codec->lock);
|
|
codec->type = type;
|
|
snd_mtxunlock(codec->lock);
|
|
}
|
|
|
|
void
|
|
ak452x_setcif(struct ak452x_info *codec, unsigned int cif)
|
|
{
|
|
snd_mtxlock(codec->lock);
|
|
codec->cif = cif;
|
|
snd_mtxunlock(codec->lock);
|
|
}
|
|
|
|
void
|
|
ak452x_setformat(struct ak452x_info *codec, unsigned int format)
|
|
{
|
|
snd_mtxlock(codec->lock);
|
|
codec->format = format;
|
|
snd_mtxunlock(codec->lock);
|
|
}
|
|
|
|
void
|
|
ak452x_setdvc(struct ak452x_info *codec, unsigned int dvc)
|
|
{
|
|
snd_mtxlock(codec->lock);
|
|
codec->dvc = dvc;
|
|
snd_mtxunlock(codec->lock);
|
|
}
|
|
|
|
void
|
|
ak452x_init(struct ak452x_info *codec)
|
|
{
|
|
#if(0)
|
|
device_printf(codec->dev, "ak452x_init(codec)\n");
|
|
#endif
|
|
snd_mtxlock(codec->lock);
|
|
/* power off */
|
|
ak452x_wrcd(codec, AK4524_POWER, 0);
|
|
/* set parameter */
|
|
ak452x_wrcd(codec, AK4524_FORMAT, codec->format);
|
|
ak452x_wrcd(codec, AK4524_DVC, codec->dvc);
|
|
/* power on */
|
|
ak452x_wrcd(codec, AK4524_POWER, AK452X_POWER_PWDA | AK452X_POWER_PWAD | AK452X_POWER_PWVR);
|
|
/* free reset register */
|
|
ak452x_wrcd(codec, AK4524_RESET, AK452X_RESET_RSDA | AK452X_RESET_RSAD);
|
|
snd_mtxunlock(codec->lock);
|
|
}
|
|
|
|
void
|
|
ak452x_reinit(struct ak452x_info *codec)
|
|
{
|
|
snd_mtxlock(codec->lock);
|
|
/* reset */
|
|
ak452x_wrcd(codec, AK4524_RESET, 0);
|
|
/* set parameter */
|
|
ak452x_wrcd(codec, AK4524_FORMAT, codec->format);
|
|
ak452x_wrcd(codec, AK4524_DVC, codec->dvc);
|
|
/* free reset register */
|
|
ak452x_wrcd(codec, AK4524_RESET, AK452X_RESET_RSDA | AK452X_RESET_RSAD);
|
|
snd_mtxunlock(codec->lock);
|
|
}
|
|
|
|
void
|
|
ak452x_set(struct ak452x_info *codec, int dir, unsigned int left, unsigned int right)
|
|
{
|
|
#if(0)
|
|
device_printf(codec->dev, "ak452x_set(codec, %d, %d, %d)\n", dir, left, right);
|
|
#endif
|
|
snd_mtxlock(codec->lock);
|
|
if (left >= 100)
|
|
left = 127;
|
|
else
|
|
left = left * 127 / 100;
|
|
if (right >= 100)
|
|
right = 127;
|
|
else
|
|
right = right * 127 / 100;
|
|
if (dir == PCMDIR_REC && codec->type == AK452X_TYPE_4524) {
|
|
#if(0)
|
|
device_printf(codec->dev, "ak452x_set(): AK4524(REC) %d/%d\n", left, right);
|
|
#endif
|
|
ak452x_wrcd(codec, AK4524_LIPGA, left);
|
|
ak452x_wrcd(codec, AK4524_RIPGA, right);
|
|
}
|
|
if (dir == PCMDIR_PLAY && codec->type == AK452X_TYPE_4524) {
|
|
#if(0)
|
|
device_printf(codec->dev, "ak452x_set(): AK4524(PLAY) %d/%d\n", left, right);
|
|
#endif
|
|
ak452x_wrcd(codec, AK4524_LOATT, left);
|
|
ak452x_wrcd(codec, AK4524_ROATT, right);
|
|
}
|
|
if (dir == PCMDIR_PLAY && codec->type == AK452X_TYPE_4528) {
|
|
#if(0)
|
|
device_printf(codec->dev, "ak452x_set(): AK4528(PLAY) %d/%d\n", left, right);
|
|
#endif
|
|
ak452x_wrcd(codec, AK4528_LOATT, left);
|
|
ak452x_wrcd(codec, AK4528_ROATT, right);
|
|
}
|
|
snd_mtxunlock(codec->lock);
|
|
}
|
|
|
|
MODULE_DEPEND(snd_ak452x, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
|
|
MODULE_VERSION(snd_ak452x, 1);
|