Alexander Leidinger ce356b7017 - fix compatibility with newer versions of FreeBSD
- 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>
2006-06-17 15:11:36 +00:00

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);