⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 snd-aoa-codec-tas.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * Apple Onboard Audio driver for tas codec * * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> * * GPL v2, can be found in COPYING. * * Open questions: *  - How to distinguish between 3004 and versions? * * FIXMEs: *  - This codec driver doesn't honour the 'connected' *    property of the aoa_codec struct, hence if *    it is used in machines where not everything is *    connected it will display wrong mixer elements. *  - Driver assumes that the microphone is always *    monaureal and connected to the right channel of *    the input. This should also be a codec-dependent *    flag, maybe the codec should have 3 different *    bits for the three different possibilities how *    it can be hooked up... *    But as long as I don't see any hardware hooked *    up that way... *  - As Apple notes in their code, the tas3004 seems *    to delay the right channel by one sample. You can *    see this when for example recording stereo in *    audacity, or recording the tas output via cable *    on another machine (use a sinus generator or so). *    I tried programming the BiQuads but couldn't *    make the delay work, maybe someone can read the *    datasheet and fix it. The relevant Apple comment *    is in AppleTAS3004Audio.cpp lines 1637 ff. Note *    that their comment describing how they program *    the filters sucks... * * Other things: *  - this should actually register *two* aoa_codec *    structs since it has two inputs. Then it must *    use the prepare callback to forbid running the *    secondary output on a different clock. *    Also, whatever bus knows how to do this must *    provide two soundbus_dev devices and the fabric *    must be able to link them correctly. * *    I don't even know if Apple ever uses the second *    port on the tas3004 though, I don't think their *    i2s controllers can even do it. OTOH, they all *    derive the clocks from common clocks, so it *    might just be possible. The framework allows the *    codec to refine the transfer_info items in the *    usable callback, so we can simply remove the *    rates the second instance is not using when it *    actually is in use. *    Maybe we'll need to make the sound busses have *    a 'clock group id' value so the codec can *    determine if the two outputs can be driven at *    the same time. But that is likely overkill, up *    to the fabric to not link them up incorrectly, *    and up to the hardware designer to not wire *    them up in some weird unusable way. */#include <stddef.h>#include <linux/i2c.h>#include <asm/pmac_low_i2c.h>#include <asm/prom.h>#include <linux/delay.h>#include <linux/module.h>#include <linux/mutex.h>MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");MODULE_LICENSE("GPL");MODULE_DESCRIPTION("tas codec driver for snd-aoa");#include "snd-aoa-codec-tas.h"#include "snd-aoa-codec-tas-gain-table.h"#include "snd-aoa-codec-tas-basstreble.h"#include "../aoa.h"#include "../soundbus/soundbus.h"#define PFX "snd-aoa-codec-tas: "struct tas {	struct aoa_codec	codec;	struct i2c_client	i2c;	u32			mute_l:1, mute_r:1 ,				controls_created:1 ,				drc_enabled:1,				hw_enabled:1;	u8			cached_volume_l, cached_volume_r;	u8			mixer_l[3], mixer_r[3];	u8			bass, treble;	u8			acr;	int			drc_range;	/* protects hardware access against concurrency from	 * userspace when hitting controls and during	 * codec init/suspend/resume */	struct mutex		mtx;};static int tas_reset_init(struct tas *tas);static struct tas *codec_to_tas(struct aoa_codec *codec){	return container_of(codec, struct tas, codec);}static inline int tas_write_reg(struct tas *tas, u8 reg, u8 len, u8 *data){	if (len == 1)		return i2c_smbus_write_byte_data(&tas->i2c, reg, *data);	else		return i2c_smbus_write_i2c_block_data(&tas->i2c, reg, len, data);}static void tas3004_set_drc(struct tas *tas){	unsigned char val[6];	if (tas->drc_enabled)		val[0] = 0x50; /* 3:1 above threshold */	else		val[0] = 0x51; /* disabled */	val[1] = 0x02; /* 1:1 below threshold */	if (tas->drc_range > 0xef)		val[2] = 0xef;	else if (tas->drc_range < 0)		val[2] = 0x00;	else		val[2] = tas->drc_range;	val[3] = 0xb0;	val[4] = 0x60;	val[5] = 0xa0;	tas_write_reg(tas, TAS_REG_DRC, 6, val);}static void tas_set_treble(struct tas *tas){	u8 tmp;	tmp = tas3004_treble(tas->treble);	tas_write_reg(tas, TAS_REG_TREBLE, 1, &tmp);}static void tas_set_bass(struct tas *tas){	u8 tmp;	tmp = tas3004_bass(tas->bass);	tas_write_reg(tas, TAS_REG_BASS, 1, &tmp);}static void tas_set_volume(struct tas *tas){	u8 block[6];	int tmp;	u8 left, right;	left = tas->cached_volume_l;	right = tas->cached_volume_r;	if (left > 177) left = 177;	if (right > 177) right = 177;	if (tas->mute_l) left = 0;	if (tas->mute_r) right = 0;	/* analysing the volume and mixer tables shows	 * that they are similar enough when we shift	 * the mixer table down by 4 bits. The error	 * is miniscule, in just one item the error	 * is 1, at a value of 0x07f17b (mixer table	 * value is 0x07f17a) */	tmp = tas_gaintable[left];	block[0] = tmp>>20;	block[1] = tmp>>12;	block[2] = tmp>>4;	tmp = tas_gaintable[right];	block[3] = tmp>>20;	block[4] = tmp>>12;	block[5] = tmp>>4;	tas_write_reg(tas, TAS_REG_VOL, 6, block);}static void tas_set_mixer(struct tas *tas){	u8 block[9];	int tmp, i;	u8 val;	for (i=0;i<3;i++) {		val = tas->mixer_l[i];		if (val > 177) val = 177;		tmp = tas_gaintable[val];		block[3*i+0] = tmp>>16;		block[3*i+1] = tmp>>8;		block[3*i+2] = tmp;	}	tas_write_reg(tas, TAS_REG_LMIX, 9, block);	for (i=0;i<3;i++) {		val = tas->mixer_r[i];		if (val > 177) val = 177;		tmp = tas_gaintable[val];		block[3*i+0] = tmp>>16;		block[3*i+1] = tmp>>8;		block[3*i+2] = tmp;	}	tas_write_reg(tas, TAS_REG_RMIX, 9, block);}/* alsa stuff */static int tas_dev_register(struct snd_device *dev){	return 0;}static struct snd_device_ops ops = {	.dev_register = tas_dev_register,};static int tas_snd_vol_info(struct snd_kcontrol *kcontrol,	struct snd_ctl_elem_info *uinfo){	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;	uinfo->count = 2;	uinfo->value.integer.min = 0;	uinfo->value.integer.max = 177;	return 0;}static int tas_snd_vol_get(struct snd_kcontrol *kcontrol,	struct snd_ctl_elem_value *ucontrol){	struct tas *tas = snd_kcontrol_chip(kcontrol);	mutex_lock(&tas->mtx);	ucontrol->value.integer.value[0] = tas->cached_volume_l;	ucontrol->value.integer.value[1] = tas->cached_volume_r;	mutex_unlock(&tas->mtx);	return 0;}static int tas_snd_vol_put(struct snd_kcontrol *kcontrol,	struct snd_ctl_elem_value *ucontrol){	struct tas *tas = snd_kcontrol_chip(kcontrol);	mutex_lock(&tas->mtx);	if (tas->cached_volume_l == ucontrol->value.integer.value[0]	 && tas->cached_volume_r == ucontrol->value.integer.value[1]) {		mutex_unlock(&tas->mtx);		return 0;	}	tas->cached_volume_l = ucontrol->value.integer.value[0];	tas->cached_volume_r = ucontrol->value.integer.value[1];	if (tas->hw_enabled)		tas_set_volume(tas);	mutex_unlock(&tas->mtx);	return 1;}static struct snd_kcontrol_new volume_control = {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,	.name = "Master Playback Volume",	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,	.info = tas_snd_vol_info,	.get = tas_snd_vol_get,	.put = tas_snd_vol_put,};#define tas_snd_mute_info	snd_ctl_boolean_stereo_infostatic int tas_snd_mute_get(struct snd_kcontrol *kcontrol,	struct snd_ctl_elem_value *ucontrol){	struct tas *tas = snd_kcontrol_chip(kcontrol);	mutex_lock(&tas->mtx);	ucontrol->value.integer.value[0] = !tas->mute_l;	ucontrol->value.integer.value[1] = !tas->mute_r;	mutex_unlock(&tas->mtx);	return 0;}static int tas_snd_mute_put(struct snd_kcontrol *kcontrol,	struct snd_ctl_elem_value *ucontrol){	struct tas *tas = snd_kcontrol_chip(kcontrol);	mutex_lock(&tas->mtx);	if (tas->mute_l == !ucontrol->value.integer.value[0]	 && tas->mute_r == !ucontrol->value.integer.value[1]) {		mutex_unlock(&tas->mtx);		return 0;	}	tas->mute_l = !ucontrol->value.integer.value[0];	tas->mute_r = !ucontrol->value.integer.value[1];	if (tas->hw_enabled)		tas_set_volume(tas);	mutex_unlock(&tas->mtx);	return 1;}static struct snd_kcontrol_new mute_control = {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,	.name = "Master Playback Switch",	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,	.info = tas_snd_mute_info,	.get = tas_snd_mute_get,	.put = tas_snd_mute_put,};static int tas_snd_mixer_info(struct snd_kcontrol *kcontrol,	struct snd_ctl_elem_info *uinfo){	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;	uinfo->count = 2;	uinfo->value.integer.min = 0;	uinfo->value.integer.max = 177;	return 0;}static int tas_snd_mixer_get(struct snd_kcontrol *kcontrol,	struct snd_ctl_elem_value *ucontrol){	struct tas *tas = snd_kcontrol_chip(kcontrol);	int idx = kcontrol->private_value;	mutex_lock(&tas->mtx);	ucontrol->value.integer.value[0] = tas->mixer_l[idx];	ucontrol->value.integer.value[1] = tas->mixer_r[idx];	mutex_unlock(&tas->mtx);	return 0;}static int tas_snd_mixer_put(struct snd_kcontrol *kcontrol,	struct snd_ctl_elem_value *ucontrol){	struct tas *tas = snd_kcontrol_chip(kcontrol);	int idx = kcontrol->private_value;	mutex_lock(&tas->mtx);	if (tas->mixer_l[idx] == ucontrol->value.integer.value[0]	 && tas->mixer_r[idx] == ucontrol->value.integer.value[1]) {		mutex_unlock(&tas->mtx);		return 0;	}	tas->mixer_l[idx] = ucontrol->value.integer.value[0];	tas->mixer_r[idx] = ucontrol->value.integer.value[1];	if (tas->hw_enabled)		tas_set_mixer(tas);	mutex_unlock(&tas->mtx);	return 1;}#define MIXER_CONTROL(n,descr,idx)			\static struct snd_kcontrol_new n##_control = {		\	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,		\	.name = descr " Playback Volume",		\	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,	\	.info = tas_snd_mixer_info,			\	.get = tas_snd_mixer_get,			\	.put = tas_snd_mixer_put,			\	.private_value = idx,				\}MIXER_CONTROL(pcm1, "PCM", 0);MIXER_CONTROL(monitor, "Monitor", 2);static int tas_snd_drc_range_info(struct snd_kcontrol *kcontrol,	struct snd_ctl_elem_info *uinfo){	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;	uinfo->count = 1;	uinfo->value.integer.min = 0;	uinfo->value.integer.max = TAS3004_DRC_MAX;	return 0;}static int tas_snd_drc_range_get(struct snd_kcontrol *kcontrol,	struct snd_ctl_elem_value *ucontrol){	struct tas *tas = snd_kcontrol_chip(kcontrol);	mutex_lock(&tas->mtx);	ucontrol->value.integer.value[0] = tas->drc_range;	mutex_unlock(&tas->mtx);	return 0;}static int tas_snd_drc_range_put(struct snd_kcontrol *kcontrol,	struct snd_ctl_elem_value *ucontrol){	struct tas *tas = snd_kcontrol_chip(kcontrol);	mutex_lock(&tas->mtx);	if (tas->drc_range == ucontrol->value.integer.value[0]) {		mutex_unlock(&tas->mtx);		return 0;	}	tas->drc_range = ucontrol->value.integer.value[0];	if (tas->hw_enabled)		tas3004_set_drc(tas);	mutex_unlock(&tas->mtx);	return 1;}static struct snd_kcontrol_new drc_range_control = {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,	.name = "DRC Range",	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,	.info = tas_snd_drc_range_info,	.get = tas_snd_drc_range_get,	.put = tas_snd_drc_range_put,};#define tas_snd_drc_switch_info		snd_ctl_boolean_mono_infostatic int tas_snd_drc_switch_get(struct snd_kcontrol *kcontrol,	struct snd_ctl_elem_value *ucontrol){	struct tas *tas = snd_kcontrol_chip(kcontrol);	mutex_lock(&tas->mtx);	ucontrol->value.integer.value[0] = tas->drc_enabled;	mutex_unlock(&tas->mtx);	return 0;}static int tas_snd_drc_switch_put(struct snd_kcontrol *kcontrol,	struct snd_ctl_elem_value *ucontrol){	struct tas *tas = snd_kcontrol_chip(kcontrol);	mutex_lock(&tas->mtx);	if (tas->drc_enabled == ucontrol->value.integer.value[0]) {		mutex_unlock(&tas->mtx);		return 0;	}	tas->drc_enabled = ucontrol->value.integer.value[0];	if (tas->hw_enabled)		tas3004_set_drc(tas);	mutex_unlock(&tas->mtx);	return 1;}static struct snd_kcontrol_new drc_switch_control = {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,	.name = "DRC Range Switch",	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,	.info = tas_snd_drc_switch_info,	.get = tas_snd_drc_switch_get,	.put = tas_snd_drc_switch_put,};static int tas_snd_capture_source_info(struct snd_kcontrol *kcontrol,	struct snd_ctl_elem_info *uinfo){	static char *texts[] = { "Line-In", "Microphone" };	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;	uinfo->count = 1;	uinfo->value.enumerated.items = 2;	if (uinfo->value.enumerated.item > 1)		uinfo->value.enumerated.item = 1;	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);	return 0;}static int tas_snd_capture_source_get(struct snd_kcontrol *kcontrol,	struct snd_ctl_elem_value *ucontrol){	struct tas *tas = snd_kcontrol_chip(kcontrol);	mutex_lock(&tas->mtx);	ucontrol->value.enumerated.item[0] = !!(tas->acr & TAS_ACR_INPUT_B);	mutex_unlock(&tas->mtx);	return 0;}static int tas_snd_capture_source_put(struct snd_kcontrol *kcontrol,	struct snd_ctl_elem_value *ucontrol){	struct tas *tas = snd_kcontrol_chip(kcontrol);	int oldacr;	mutex_lock(&tas->mtx);	oldacr = tas->acr;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -