aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorLiam Girdwood <lrg@slimlogic.co.uk>2010-11-03 14:11:27 +0000
committerLiam Girdwood <lrg@slimlogic.co.uk>2010-11-03 14:11:27 +0000
commit8f987768eb99631374f4ab0bb19cd062baf1397d (patch)
treeb89aa5c207f7ba6a688f45657424b937f17ceb8a /sound
parent63f7526f26f0a9291ac3f7a986aa18ebfb61ec19 (diff)
parentc8ddb2713c624f432fa5fe3c7ecffcdda46ea0d4 (diff)
downloadmrst-s0i3-test-8f987768eb99631374f4ab0bb19cd062baf1397d.tar.gz
mrst-s0i3-test-8f987768eb99631374f4ab0bb19cd062baf1397d.tar.xz
mrst-s0i3-test-8f987768eb99631374f4ab0bb19cd062baf1397d.zip
Merge commit 'v2.6.37-rc1' into for-2.6.37
Diffstat (limited to 'sound')
-rw-r--r--sound/core/control.c5
-rw-r--r--sound/core/init.c20
-rw-r--r--sound/core/oss/mixer_oss.c34
-rw-r--r--sound/core/pcm.c42
-rw-r--r--sound/core/pcm_lib.c14
-rw-r--r--sound/core/pcm_native.c10
-rw-r--r--sound/core/rawmidi.c6
-rw-r--r--sound/core/seq/oss/seq_oss.c1
-rw-r--r--sound/core/seq/oss/seq_oss_init.c9
-rw-r--r--sound/core/sound.c3
-rw-r--r--sound/drivers/Kconfig19
-rw-r--r--sound/drivers/Makefile2
-rw-r--r--sound/drivers/aloop.c1258
-rw-r--r--sound/drivers/virmidi.c2
-rw-r--r--sound/i2c/other/ak4xxx-adda.c4
-rw-r--r--sound/isa/Kconfig36
-rw-r--r--sound/isa/Makefile4
-rw-r--r--sound/isa/ad1816a/ad1816a.c2
-rw-r--r--sound/isa/azt2320.c2
-rw-r--r--sound/isa/galaxy/Makefile10
-rw-r--r--sound/isa/galaxy/azt1605.c91
-rw-r--r--sound/isa/galaxy/azt2316.c111
-rw-r--r--sound/isa/galaxy/galaxy.c652
-rw-r--r--sound/isa/gus/gusmax.c4
-rw-r--r--sound/isa/msnd/msnd_pinnacle.c8
-rw-r--r--sound/isa/sb/sb8.c2
-rw-r--r--sound/isa/sgalaxy.c369
-rw-r--r--sound/oss/Kconfig8
-rw-r--r--sound/oss/Makefile1
-rw-r--r--sound/oss/ac97_codec.c7
-rw-r--r--sound/oss/au1550_ac97.c48
-rw-r--r--sound/oss/dmasound/dmasound_core.c41
-rw-r--r--sound/oss/msnd_pinnacle.c16
-rw-r--r--sound/oss/sb_ess.c1
-rw-r--r--sound/oss/sh_dac_audio.c325
-rw-r--r--sound/oss/sound_timer.c2
-rw-r--r--sound/oss/soundcard.c47
-rw-r--r--sound/oss/swarm_cs4297a.c20
-rw-r--r--sound/oss/vwsnd.c30
-rw-r--r--sound/pci/Kconfig17
-rw-r--r--sound/pci/asihpi/hpi6205.c7
-rw-r--r--sound/pci/au88x0/au88x0_mixer.c2
-rw-r--r--sound/pci/ca0106/ca0106.h5
-rw-r--r--sound/pci/ca0106/ca0106_main.c136
-rw-r--r--sound/pci/ca0106/ca0106_mixer.c93
-rw-r--r--sound/pci/emu10k1/emu10k1.c4
-rw-r--r--sound/pci/emu10k1/emumpu401.c2
-rw-r--r--sound/pci/emu10k1/emupcm.c30
-rw-r--r--sound/pci/emu10k1/memory.c4
-rw-r--r--sound/pci/ens1370.c2
-rw-r--r--sound/pci/hda/Kconfig39
-rw-r--r--sound/pci/hda/Makefile15
-rw-r--r--sound/pci/hda/hda_codec.c304
-rw-r--r--sound/pci/hda/hda_codec.h15
-rw-r--r--sound/pci/hda/hda_eld.c11
-rw-r--r--sound/pci/hda/hda_generic.c41
-rw-r--r--sound/pci/hda/hda_intel.c104
-rw-r--r--sound/pci/hda/hda_local.h51
-rw-r--r--sound/pci/hda/patch_analog.c49
-rw-r--r--sound/pci/hda/patch_atihdmi.c224
-rw-r--r--sound/pci/hda/patch_ca0110.c10
-rw-r--r--sound/pci/hda/patch_cirrus.c144
-rw-r--r--sound/pci/hda/patch_conexant.c710
-rw-r--r--sound/pci/hda/patch_hdmi.c818
-rw-r--r--sound/pci/hda/patch_intelhdmi.c228
-rw-r--r--sound/pci/hda/patch_nvhdmi.c616
-rw-r--r--sound/pci/hda/patch_realtek.c1050
-rw-r--r--sound/pci/hda/patch_sigmatel.c474
-rw-r--r--sound/pci/hda/patch_via.c587
-rw-r--r--sound/pci/ice1712/delta.c10
-rw-r--r--sound/pci/ice1712/delta.h4
-rw-r--r--sound/pci/ice1712/pontis.c6
-rw-r--r--sound/pci/ice1712/prodigy192.c2
-rw-r--r--sound/pci/intel8x0.c8
-rw-r--r--sound/pci/oxygen/oxygen.c8
-rw-r--r--sound/pci/oxygen/oxygen.h2
-rw-r--r--sound/pci/oxygen/oxygen_lib.c76
-rw-r--r--sound/pci/oxygen/oxygen_mixer.c5
-rw-r--r--sound/pci/oxygen/oxygen_pcm.c12
-rw-r--r--sound/pci/oxygen/oxygen_regs.h10
-rw-r--r--sound/pci/oxygen/virtuoso.c6
-rw-r--r--sound/pci/oxygen/xonar_cs43xx.c8
-rw-r--r--sound/pci/oxygen/xonar_pcm179x.c29
-rw-r--r--sound/pci/oxygen/xonar_wm87x6.c143
-rw-r--r--sound/pci/riptide/riptide.c11
-rw-r--r--sound/pci/rme96.c8
-rw-r--r--sound/pci/rme9652/hdsp.c9
-rw-r--r--sound/pci/rme9652/hdspm.c1
-rw-r--r--sound/pcmcia/pdaudiocf/pdaudiocf.c16
-rw-r--r--sound/pcmcia/pdaudiocf/pdaudiocf.h1
-rw-r--r--sound/pcmcia/vx/vxpocket.c15
-rw-r--r--sound/pcmcia/vx/vxpocket.h1
-rw-r--r--sound/ppc/snd_ps3.c2
-rw-r--r--sound/ppc/tumbler.c2
-rw-r--r--sound/sh/aica.c2
-rw-r--r--sound/soc/codecs/Kconfig4
-rw-r--r--sound/soc/codecs/Makefile4
-rw-r--r--sound/soc/codecs/ac97.c7
-rw-r--r--sound/soc/codecs/ad1980.c2
-rw-r--r--sound/soc/codecs/ad73311.c2
-rw-r--r--sound/soc/codecs/ak4642.c64
-rw-r--r--sound/soc/codecs/cs4270.c2
-rw-r--r--sound/soc/codecs/max98088.c2097
-rw-r--r--sound/soc/codecs/max98088.h193
-rw-r--r--sound/soc/codecs/wm8580.c4
-rw-r--r--sound/soc/codecs/wm8804.c8
-rw-r--r--sound/soc/codecs/wm8962-tables.c42299
-rw-r--r--sound/soc/codecs/wm8962.c1836
-rw-r--r--sound/soc/codecs/wm8962.h11
-rw-r--r--sound/soc/codecs/wm8985.c8
-rw-r--r--sound/soc/codecs/wm8994.c3150
-rw-r--r--sound/soc/codecs/wm9090.c2
-rw-r--r--sound/soc/davinci/davinci-mcasp.c2
-rw-r--r--sound/soc/davinci/davinci-sffsdr.c2
-rw-r--r--sound/soc/ep93xx/Kconfig16
-rw-r--r--sound/soc/ep93xx/Makefile4
-rw-r--r--sound/soc/ep93xx/ep93xx-ac97.c468
-rw-r--r--sound/soc/ep93xx/simone.c89
-rw-r--r--sound/soc/fsl/pcm030-audio-fabric.c3
-rw-r--r--sound/soc/omap/omap-mcbsp.c119
-rw-r--r--sound/soc/omap/omap-mcpdm.c1
-rw-r--r--sound/soc/s3c24xx/Kconfig11
-rw-r--r--sound/soc/s3c24xx/Makefile4
-rw-r--r--sound/soc/s3c24xx/neo1973_gta02_wm8753.c2
-rw-r--r--sound/soc/s3c24xx/neo1973_wm8753.c2
-rw-r--r--sound/soc/s3c24xx/s3c-dma.c3
-rw-r--r--sound/soc/s3c24xx/smdk_spdif.c223
-rw-r--r--sound/soc/s3c24xx/spdif.c501
-rw-r--r--sound/soc/s3c24xx/spdif.h19
-rw-r--r--sound/soc/sh/fsi.c216
-rw-r--r--sound/soc/sh/sh7760-ac97.c4
-rw-r--r--sound/soc/sh/siu_pcm.c2
-rw-r--r--sound/soc/soc-core.c79
-rw-r--r--sound/soc/soc-dapm.c1
-rw-r--r--sound/soc/soc-jack.c11
-rw-r--r--sound/sound_core.c1
-rw-r--r--sound/synth/emux/emux_hwdep.c3
-rw-r--r--sound/usb/Kconfig2
-rw-r--r--sound/usb/caiaq/audio.c175
-rw-r--r--sound/usb/caiaq/control.c208
-rw-r--r--sound/usb/caiaq/device.c10
-rw-r--r--sound/usb/caiaq/device.h6
-rw-r--r--sound/usb/caiaq/input.c248
-rw-r--r--sound/usb/card.c50
-rw-r--r--sound/usb/card.h2
-rw-r--r--sound/usb/clock.c3
-rw-r--r--sound/usb/endpoint.c13
-rw-r--r--sound/usb/format.c22
-rw-r--r--sound/usb/helper.c17
-rw-r--r--sound/usb/midi.c16
-rw-r--r--sound/usb/mixer.c19
-rw-r--r--sound/usb/mixer_quirks.c1
-rw-r--r--sound/usb/pcm.c9
-rw-r--r--sound/usb/proc.c7
-rw-r--r--sound/usb/quirks-table.h184
-rw-r--r--sound/usb/quirks.c2
-rw-r--r--sound/usb/urb.c172
-rw-r--r--sound/usb/usbaudio.h2
-rw-r--r--sound/usb/usx2y/usx2yhwdeppcm.c6
159 files changed, 14361 insertions, 47747 deletions
diff --git a/sound/core/control.c b/sound/core/control.c
index 070aab49019..45a818002d9 100644
--- a/sound/core/control.c
+++ b/sound/core/control.c
@@ -31,6 +31,7 @@
/* max number of user-defined controls */
#define MAX_USER_CONTROLS 32
+#define MAX_CONTROL_COUNT 1028
struct snd_kctl_ioctl {
struct list_head list; /* list of all ioctls */
@@ -195,6 +196,10 @@ static struct snd_kcontrol *snd_ctl_new(struct snd_kcontrol *control,
if (snd_BUG_ON(!control || !control->count))
return NULL;
+
+ if (control->count > MAX_CONTROL_COUNT)
+ return NULL;
+
kctl = kzalloc(sizeof(*kctl) + sizeof(struct snd_kcontrol_volatile) * control->count, GFP_KERNEL);
if (kctl == NULL) {
snd_printk(KERN_ERR "Cannot allocate control instance\n");
diff --git a/sound/core/init.c b/sound/core/init.c
index ec4a50ce565..57b792e2439 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -395,12 +395,10 @@ int snd_card_disconnect(struct snd_card *card)
snd_printk(KERN_ERR "not all devices for card %i can be disconnected\n", card->number);
snd_info_card_disconnect(card);
-#ifndef CONFIG_SYSFS_DEPRECATED
if (card->card_dev) {
device_unregister(card->card_dev);
card->card_dev = NULL;
}
-#endif
#ifdef CONFIG_PM
wake_up(&card->power_sleep);
#endif
@@ -573,7 +571,6 @@ void snd_card_set_id(struct snd_card *card, const char *nid)
}
EXPORT_SYMBOL(snd_card_set_id);
-#ifndef CONFIG_SYSFS_DEPRECATED
static ssize_t
card_id_show_attr(struct device *dev,
struct device_attribute *attr, char *buf)
@@ -607,11 +604,16 @@ card_id_store_attr(struct device *dev, struct device_attribute *attr,
return -EEXIST;
}
for (idx = 0; idx < snd_ecards_limit; idx++) {
- if (snd_cards[idx] && !strcmp(snd_cards[idx]->id, buf1))
- goto __exist;
+ if (snd_cards[idx] && !strcmp(snd_cards[idx]->id, buf1)) {
+ if (card == snd_cards[idx])
+ goto __ok;
+ else
+ goto __exist;
+ }
}
strcpy(card->id, buf1);
snd_info_card_id_change(card);
+__ok:
mutex_unlock(&snd_card_mutex);
return count;
@@ -630,7 +632,6 @@ card_number_show_attr(struct device *dev,
static struct device_attribute card_number_attrs =
__ATTR(number, S_IRUGO, card_number_show_attr, NULL);
-#endif /* CONFIG_SYSFS_DEPRECATED */
/**
* snd_card_register - register the soundcard
@@ -649,7 +650,7 @@ int snd_card_register(struct snd_card *card)
if (snd_BUG_ON(!card))
return -EINVAL;
-#ifndef CONFIG_SYSFS_DEPRECATED
+
if (!card->card_dev) {
card->card_dev = device_create(sound_class, card->dev,
MKDEV(0, 0), card,
@@ -657,7 +658,7 @@ int snd_card_register(struct snd_card *card)
if (IS_ERR(card->card_dev))
card->card_dev = NULL;
}
-#endif
+
if ((err = snd_device_register_all(card)) < 0)
return err;
mutex_lock(&snd_card_mutex);
@@ -674,7 +675,6 @@ int snd_card_register(struct snd_card *card)
if (snd_mixer_oss_notify_callback)
snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_REGISTER);
#endif
-#ifndef CONFIG_SYSFS_DEPRECATED
if (card->card_dev) {
err = device_create_file(card->card_dev, &card_id_attrs);
if (err < 0)
@@ -683,7 +683,7 @@ int snd_card_register(struct snd_card *card)
if (err < 0)
return err;
}
-#endif
+
return 0;
}
diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c
index f50ebf20df9..822dd56993c 100644
--- a/sound/core/oss/mixer_oss.c
+++ b/sound/core/oss/mixer_oss.c
@@ -77,7 +77,7 @@ static int snd_mixer_oss_release(struct inode *inode, struct file *file)
struct snd_mixer_oss_file *fmixer;
if (file->private_data) {
- fmixer = (struct snd_mixer_oss_file *) file->private_data;
+ fmixer = file->private_data;
module_put(fmixer->card->module);
snd_card_file_remove(fmixer->card, file);
kfree(fmixer);
@@ -368,7 +368,7 @@ static int snd_mixer_oss_ioctl1(struct snd_mixer_oss_file *fmixer, unsigned int
static long snd_mixer_oss_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
- return snd_mixer_oss_ioctl1((struct snd_mixer_oss_file *) file->private_data, cmd, arg);
+ return snd_mixer_oss_ioctl1(file->private_data, cmd, arg);
}
int snd_mixer_oss_ioctl_card(struct snd_card *card, unsigned int cmd, unsigned long arg)
@@ -582,7 +582,7 @@ static int snd_mixer_oss_get_volume1(struct snd_mixer_oss_file *fmixer,
struct snd_mixer_oss_slot *pslot,
int *left, int *right)
{
- struct slot *slot = (struct slot *)pslot->private_data;
+ struct slot *slot = pslot->private_data;
*left = *right = 100;
if (slot->present & SNDRV_MIXER_OSS_PRESENT_PVOLUME) {
@@ -618,8 +618,10 @@ static void snd_mixer_oss_put_volume1_vol(struct snd_mixer_oss_file *fmixer,
if (numid == ID_UNKNOWN)
return;
down_read(&card->controls_rwsem);
- if ((kctl = snd_ctl_find_numid(card, numid)) == NULL)
+ if ((kctl = snd_ctl_find_numid(card, numid)) == NULL) {
+ up_read(&card->controls_rwsem);
return;
+ }
uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL);
uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
if (uinfo == NULL || uctl == NULL)
@@ -658,7 +660,7 @@ static void snd_mixer_oss_put_volume1_sw(struct snd_mixer_oss_file *fmixer,
return;
down_read(&card->controls_rwsem);
if ((kctl = snd_ctl_find_numid(card, numid)) == NULL) {
- up_read(&fmixer->card->controls_rwsem);
+ up_read(&card->controls_rwsem);
return;
}
uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL);
@@ -691,7 +693,7 @@ static int snd_mixer_oss_put_volume1(struct snd_mixer_oss_file *fmixer,
struct snd_mixer_oss_slot *pslot,
int left, int right)
{
- struct slot *slot = (struct slot *)pslot->private_data;
+ struct slot *slot = pslot->private_data;
if (slot->present & SNDRV_MIXER_OSS_PRESENT_PVOLUME) {
snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PVOLUME], left, right);
@@ -740,7 +742,7 @@ static int snd_mixer_oss_get_recsrc1_sw(struct snd_mixer_oss_file *fmixer,
struct snd_mixer_oss_slot *pslot,
int *active)
{
- struct slot *slot = (struct slot *)pslot->private_data;
+ struct slot *slot = pslot->private_data;
int left, right;
left = right = 1;
@@ -753,7 +755,7 @@ static int snd_mixer_oss_get_recsrc1_route(struct snd_mixer_oss_file *fmixer,
struct snd_mixer_oss_slot *pslot,
int *active)
{
- struct slot *slot = (struct slot *)pslot->private_data;
+ struct slot *slot = pslot->private_data;
int left, right;
left = right = 1;
@@ -766,7 +768,7 @@ static int snd_mixer_oss_put_recsrc1_sw(struct snd_mixer_oss_file *fmixer,
struct snd_mixer_oss_slot *pslot,
int active)
{
- struct slot *slot = (struct slot *)pslot->private_data;
+ struct slot *slot = pslot->private_data;
snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CSWITCH], active, active, 0);
return 0;
@@ -776,7 +778,7 @@ static int snd_mixer_oss_put_recsrc1_route(struct snd_mixer_oss_file *fmixer,
struct snd_mixer_oss_slot *pslot,
int active)
{
- struct slot *slot = (struct slot *)pslot->private_data;
+ struct slot *slot = pslot->private_data;
snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CROUTE], active, active, 1);
return 0;
@@ -797,7 +799,7 @@ static int snd_mixer_oss_get_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned
uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
if (uinfo == NULL || uctl == NULL) {
err = -ENOMEM;
- goto __unlock;
+ goto __free_only;
}
down_read(&card->controls_rwsem);
kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0);
@@ -813,7 +815,7 @@ static int snd_mixer_oss_get_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned
if (!(mixer->mask_recsrc & (1 << idx)))
continue;
pslot = &mixer->slots[idx];
- slot = (struct slot *)pslot->private_data;
+ slot = pslot->private_data;
if (slot->signature != SNDRV_MIXER_OSS_SIGNATURE)
continue;
if (!(slot->present & SNDRV_MIXER_OSS_PRESENT_CAPTURE))
@@ -826,6 +828,7 @@ static int snd_mixer_oss_get_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned
err = 0;
__unlock:
up_read(&card->controls_rwsem);
+ __free_only:
kfree(uctl);
kfree(uinfo);
return err;
@@ -847,7 +850,7 @@ static int snd_mixer_oss_put_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned
uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
if (uinfo == NULL || uctl == NULL) {
err = -ENOMEM;
- goto __unlock;
+ goto __free_only;
}
down_read(&card->controls_rwsem);
kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0);
@@ -861,7 +864,7 @@ static int snd_mixer_oss_put_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned
if (!(mixer->mask_recsrc & (1 << idx)))
continue;
pslot = &mixer->slots[idx];
- slot = (struct slot *)pslot->private_data;
+ slot = pslot->private_data;
if (slot->signature != SNDRV_MIXER_OSS_SIGNATURE)
continue;
if (!(slot->present & SNDRV_MIXER_OSS_PRESENT_CAPTURE))
@@ -880,6 +883,7 @@ static int snd_mixer_oss_put_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned
err = 0;
__unlock:
up_read(&card->controls_rwsem);
+ __free_only:
kfree(uctl);
kfree(uinfo);
return err;
@@ -925,7 +929,7 @@ static int snd_mixer_oss_build_test(struct snd_mixer_oss *mixer, struct slot *sl
static void snd_mixer_oss_slot_free(struct snd_mixer_oss_slot *chn)
{
- struct slot *p = (struct slot *)chn->private_data;
+ struct slot *p = chn->private_data;
if (p) {
if (p->allocated && p->assigned) {
kfree(p->assigned->name);
diff --git a/sound/core/pcm.c b/sound/core/pcm.c
index cbe815dfbdc..6b4b1287b31 100644
--- a/sound/core/pcm.c
+++ b/sound/core/pcm.c
@@ -203,10 +203,16 @@ static char *snd_pcm_format_names[] = {
FORMAT(S18_3BE),
FORMAT(U18_3LE),
FORMAT(U18_3BE),
+ FORMAT(G723_24),
+ FORMAT(G723_24_1B),
+ FORMAT(G723_40),
+ FORMAT(G723_40_1B),
};
const char *snd_pcm_format_name(snd_pcm_format_t format)
{
+ if (format >= ARRAY_SIZE(snd_pcm_format_names))
+ return "Unknown";
return snd_pcm_format_names[format];
}
EXPORT_SYMBOL_GPL(snd_pcm_format_name);
@@ -358,22 +364,24 @@ static void snd_pcm_stream_proc_info_read(struct snd_info_entry *entry,
static void snd_pcm_substream_proc_info_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
- snd_pcm_proc_info_read((struct snd_pcm_substream *)entry->private_data,
- buffer);
+ snd_pcm_proc_info_read(entry->private_data, buffer);
}
static void snd_pcm_substream_proc_hw_params_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_pcm_substream *substream = entry->private_data;
- struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_pcm_runtime *runtime;
+
+ mutex_lock(&substream->pcm->open_mutex);
+ runtime = substream->runtime;
if (!runtime) {
snd_iprintf(buffer, "closed\n");
- return;
+ goto unlock;
}
if (runtime->status->state == SNDRV_PCM_STATE_OPEN) {
snd_iprintf(buffer, "no setup\n");
- return;
+ goto unlock;
}
snd_iprintf(buffer, "access: %s\n", snd_pcm_access_name(runtime->access));
snd_iprintf(buffer, "format: %s\n", snd_pcm_format_name(runtime->format));
@@ -392,20 +400,25 @@ static void snd_pcm_substream_proc_hw_params_read(struct snd_info_entry *entry,
snd_iprintf(buffer, "OSS period frames: %lu\n", (unsigned long)runtime->oss.period_frames);
}
#endif
+ unlock:
+ mutex_unlock(&substream->pcm->open_mutex);
}
static void snd_pcm_substream_proc_sw_params_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_pcm_substream *substream = entry->private_data;
- struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_pcm_runtime *runtime;
+
+ mutex_lock(&substream->pcm->open_mutex);
+ runtime = substream->runtime;
if (!runtime) {
snd_iprintf(buffer, "closed\n");
- return;
+ goto unlock;
}
if (runtime->status->state == SNDRV_PCM_STATE_OPEN) {
snd_iprintf(buffer, "no setup\n");
- return;
+ goto unlock;
}
snd_iprintf(buffer, "tstamp_mode: %s\n", snd_pcm_tstamp_mode_name(runtime->tstamp_mode));
snd_iprintf(buffer, "period_step: %u\n", runtime->period_step);
@@ -415,24 +428,29 @@ static void snd_pcm_substream_proc_sw_params_read(struct snd_info_entry *entry,
snd_iprintf(buffer, "silence_threshold: %lu\n", runtime->silence_threshold);
snd_iprintf(buffer, "silence_size: %lu\n", runtime->silence_size);
snd_iprintf(buffer, "boundary: %lu\n", runtime->boundary);
+ unlock:
+ mutex_unlock(&substream->pcm->open_mutex);
}
static void snd_pcm_substream_proc_status_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_pcm_substream *substream = entry->private_data;
- struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_pcm_runtime *runtime;
struct snd_pcm_status status;
int err;
+
+ mutex_lock(&substream->pcm->open_mutex);
+ runtime = substream->runtime;
if (!runtime) {
snd_iprintf(buffer, "closed\n");
- return;
+ goto unlock;
}
memset(&status, 0, sizeof(status));
err = snd_pcm_status(substream, &status);
if (err < 0) {
snd_iprintf(buffer, "error %d\n", err);
- return;
+ goto unlock;
}
snd_iprintf(buffer, "state: %s\n", snd_pcm_state_name(status.state));
snd_iprintf(buffer, "owner_pid : %d\n", pid_vnr(substream->pid));
@@ -446,6 +464,8 @@ static void snd_pcm_substream_proc_status_read(struct snd_info_entry *entry,
snd_iprintf(buffer, "-----\n");
snd_iprintf(buffer, "hw_ptr : %ld\n", runtime->status->hw_ptr);
snd_iprintf(buffer, "appl_ptr : %ld\n", runtime->control->appl_ptr);
+ unlock:
+ mutex_unlock(&substream->pcm->open_mutex);
}
#ifdef CONFIG_SND_PCM_XRUN_DEBUG
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index e23e0e7ab26..a1707cca9c6 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -334,11 +334,15 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
/* delta = "expected next hw_ptr" for in_interrupt != 0 */
delta = runtime->hw_ptr_interrupt + runtime->period_size;
if (delta > new_hw_ptr) {
- hw_base += runtime->buffer_size;
- if (hw_base >= runtime->boundary)
- hw_base = 0;
- new_hw_ptr = hw_base + pos;
- goto __delta;
+ /* check for double acknowledged interrupts */
+ hdelta = jiffies - runtime->hw_ptr_jiffies;
+ if (hdelta > runtime->hw_ptr_buffer_jiffies/2) {
+ hw_base += runtime->buffer_size;
+ if (hw_base >= runtime->boundary)
+ hw_base = 0;
+ new_hw_ptr = hw_base + pos;
+ goto __delta;
+ }
}
}
/* new_hw_ptr might be lower than old_hw_ptr in case when */
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index a3b2a647924..8bc7cb3db33 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -142,7 +142,7 @@ int snd_pcm_info_user(struct snd_pcm_substream *substream,
#ifdef RULES_DEBUG
#define HW_PARAM(v) [SNDRV_PCM_HW_PARAM_##v] = #v
-char *snd_pcm_hw_param_names[] = {
+static const char * const snd_pcm_hw_param_names[] = {
HW_PARAM(ACCESS),
HW_PARAM(FORMAT),
HW_PARAM(SUBFORMAT),
@@ -864,6 +864,8 @@ static void snd_pcm_post_start(struct snd_pcm_substream *substream, int state)
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_trigger_tstamp(substream);
runtime->hw_ptr_jiffies = jiffies;
+ runtime->hw_ptr_buffer_jiffies = (runtime->buffer_size * HZ) /
+ runtime->rate;
runtime->status->state = state;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
runtime->silence_size > 0)
@@ -978,6 +980,10 @@ static int snd_pcm_do_pause(struct snd_pcm_substream *substream, int push)
{
if (substream->runtime->trigger_master != substream)
return 0;
+ /* some drivers might use hw_ptr to recover from the pause -
+ update the hw_ptr now */
+ if (push)
+ snd_pcm_update_hw_ptr(substream);
/* The jiffies check in snd_pcm_update_hw_ptr*() is done by
* a delta betwen the current jiffies, this gives a large enough
* delta, effectively to skip the check once.
@@ -1988,6 +1994,8 @@ void snd_pcm_release_substream(struct snd_pcm_substream *substream)
substream->ops->close(substream);
substream->hw_opened = 0;
}
+ if (pm_qos_request_active(&substream->latency_pm_qos_req))
+ pm_qos_remove_request(&substream->latency_pm_qos_req);
if (substream->pcm_release) {
substream->pcm_release(substream);
substream->pcm_release = NULL;
diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c
index eb68326c37d..cbbed0db9e5 100644
--- a/sound/core/rawmidi.c
+++ b/sound/core/rawmidi.c
@@ -535,13 +535,15 @@ static int snd_rawmidi_release(struct inode *inode, struct file *file)
{
struct snd_rawmidi_file *rfile;
struct snd_rawmidi *rmidi;
+ struct module *module;
rfile = file->private_data;
rmidi = rfile->rmidi;
rawmidi_release_priv(rfile);
kfree(rfile);
+ module = rmidi->card->module;
snd_card_file_remove(rmidi->card, file);
- module_put(rmidi->card->module);
+ module_put(module);
return 0;
}
@@ -829,6 +831,8 @@ static int snd_rawmidi_control_ioctl(struct snd_card *card,
if (get_user(device, (int __user *)argp))
return -EFAULT;
+ if (device >= SNDRV_RAWMIDI_DEVICES) /* next device is -1 */
+ device = SNDRV_RAWMIDI_DEVICES - 1;
mutex_lock(&register_mutex);
device = device < 0 ? 0 : device + 1;
while (device < SNDRV_RAWMIDI_DEVICES) {
diff --git a/sound/core/seq/oss/seq_oss.c b/sound/core/seq/oss/seq_oss.c
index f25e3cc7ddf..a1f1a2f00cc 100644
--- a/sound/core/seq/oss/seq_oss.c
+++ b/sound/core/seq/oss/seq_oss.c
@@ -220,6 +220,7 @@ static const struct file_operations seq_oss_f_ops =
.poll = odev_poll,
.unlocked_ioctl = odev_ioctl,
.compat_ioctl = odev_ioctl_compat,
+ .llseek = noop_llseek,
};
static int __init
diff --git a/sound/core/seq/oss/seq_oss_init.c b/sound/core/seq/oss/seq_oss_init.c
index 685712276ac..69cd7b3c362 100644
--- a/sound/core/seq/oss/seq_oss_init.c
+++ b/sound/core/seq/oss/seq_oss_init.c
@@ -281,13 +281,10 @@ snd_seq_oss_open(struct file *file, int level)
return 0;
_error:
- snd_seq_oss_writeq_delete(dp->writeq);
- snd_seq_oss_readq_delete(dp->readq);
snd_seq_oss_synth_cleanup(dp);
snd_seq_oss_midi_cleanup(dp);
- delete_port(dp);
delete_seq_queue(dp->queue);
- kfree(dp);
+ delete_port(dp);
return rc;
}
@@ -350,8 +347,10 @@ create_port(struct seq_oss_devinfo *dp)
static int
delete_port(struct seq_oss_devinfo *dp)
{
- if (dp->port < 0)
+ if (dp->port < 0) {
+ kfree(dp);
return 0;
+ }
debug_printk(("delete_port %i\n", dp->port));
return snd_seq_event_port_detach(dp->cseq, dp->port);
diff --git a/sound/core/sound.c b/sound/core/sound.c
index ac42af42b78..62a093efb45 100644
--- a/sound/core/sound.c
+++ b/sound/core/sound.c
@@ -184,7 +184,8 @@ static int snd_open(struct inode *inode, struct file *file)
static const struct file_operations snd_fops =
{
.owner = THIS_MODULE,
- .open = snd_open
+ .open = snd_open,
+ .llseek = noop_llseek,
};
#ifdef CONFIG_SND_DYNAMIC_MINORS
diff --git a/sound/drivers/Kconfig b/sound/drivers/Kconfig
index 480c38623da..c8961165277 100644
--- a/sound/drivers/Kconfig
+++ b/sound/drivers/Kconfig
@@ -74,6 +74,25 @@ config SND_DUMMY
To compile this driver as a module, choose M here: the module
will be called snd-dummy.
+config SND_ALOOP
+ tristate "Generic loopback driver (PCM)"
+ select SND_PCM
+ help
+ Say 'Y' or 'M' to include support for the PCM loopback device.
+ This module returns played samples back to the user space using
+ the standard ALSA PCM device. The devices are routed 0->1 and
+ 1->0, where first number is the playback PCM device and second
+ number is the capture device. Module creates two PCM devices and
+ configured number of substreams (see the pcm_substreams module
+ parameter).
+
+ The looback device allow time sychronization with an external
+ timing source using the time shift universal control (+-20%
+ of system time).
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-aloop.
+
config SND_VIRMIDI
tristate "Virtual MIDI soundcard"
depends on SND_SEQUENCER
diff --git a/sound/drivers/Makefile b/sound/drivers/Makefile
index d4a07f9ff2c..1a8440c8b13 100644
--- a/sound/drivers/Makefile
+++ b/sound/drivers/Makefile
@@ -4,6 +4,7 @@
#
snd-dummy-objs := dummy.o
+snd-aloop-objs := aloop.o
snd-mtpav-objs := mtpav.o
snd-mts64-objs := mts64.o
snd-portman2x4-objs := portman2x4.o
@@ -13,6 +14,7 @@ snd-ml403-ac97cr-objs := ml403-ac97cr.o pcm-indirect2.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_DUMMY) += snd-dummy.o
+obj-$(CONFIG_SND_ALOOP) += snd-aloop.o
obj-$(CONFIG_SND_VIRMIDI) += snd-virmidi.o
obj-$(CONFIG_SND_SERIAL_U16550) += snd-serial-u16550.o
obj-$(CONFIG_SND_MTPAV) += snd-mtpav.o
diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c
new file mode 100644
index 00000000000..12b44b0b677
--- /dev/null
+++ b/sound/drivers/aloop.c
@@ -0,0 +1,1258 @@
+/*
+ * Loopback soundcard
+ *
+ * Original code:
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ *
+ * More accurate positioning and full-duplex support:
+ * Copyright (c) Ahmet İnan <ainan at mathematik.uni-freiburg.de>
+ *
+ * Major (almost complete) rewrite:
+ * Copyright (c) by Takashi Iwai <tiwai@suse.de>
+ *
+ * A next major update in 2010 (separate timers for playback and capture):
+ * Copyright (c) Jaroslav Kysela <perex@perex.cz>
+ *
+ * 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
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/jiffies.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/info.h>
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
+MODULE_DESCRIPTION("A loopback soundcard");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{ALSA,Loopback soundcard}}");
+
+#define MAX_PCM_SUBSTREAMS 8
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
+static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0};
+static int pcm_substreams[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8};
+static int pcm_notify[SNDRV_CARDS];
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for loopback soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for loopback soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable this loopback soundcard.");
+module_param_array(pcm_substreams, int, NULL, 0444);
+MODULE_PARM_DESC(pcm_substreams, "PCM substreams # (1-8) for loopback driver.");
+module_param_array(pcm_notify, int, NULL, 0444);
+MODULE_PARM_DESC(pcm_notify, "Break capture when PCM format/rate/channels changes.");
+
+#define NO_PITCH 100000
+
+struct loopback_pcm;
+
+struct loopback_cable {
+ spinlock_t lock;
+ struct loopback_pcm *streams[2];
+ struct snd_pcm_hardware hw;
+ /* flags */
+ unsigned int valid;
+ unsigned int running;
+ unsigned int pause;
+};
+
+struct loopback_setup {
+ unsigned int notify: 1;
+ unsigned int rate_shift;
+ unsigned int format;
+ unsigned int rate;
+ unsigned int channels;
+ struct snd_ctl_elem_id active_id;
+ struct snd_ctl_elem_id format_id;
+ struct snd_ctl_elem_id rate_id;
+ struct snd_ctl_elem_id channels_id;
+};
+
+struct loopback {
+ struct snd_card *card;
+ struct mutex cable_lock;
+ struct loopback_cable *cables[MAX_PCM_SUBSTREAMS][2];
+ struct snd_pcm *pcm[2];
+ struct loopback_setup setup[MAX_PCM_SUBSTREAMS][2];
+};
+
+struct loopback_pcm {
+ struct loopback *loopback;
+ struct snd_pcm_substream *substream;
+ struct loopback_cable *cable;
+ unsigned int pcm_buffer_size;
+ unsigned int buf_pos; /* position in buffer */
+ unsigned int silent_size;
+ /* PCM parameters */
+ unsigned int pcm_period_size;
+ unsigned int pcm_bps; /* bytes per second */
+ unsigned int pcm_salign; /* bytes per sample * channels */
+ unsigned int pcm_rate_shift; /* rate shift value */
+ /* flags */
+ unsigned int period_update_pending :1;
+ /* timer stuff */
+ unsigned int irq_pos; /* fractional IRQ position */
+ unsigned int period_size_frac;
+ unsigned long last_jiffies;
+ struct timer_list timer;
+};
+
+static struct platform_device *devices[SNDRV_CARDS];
+
+static inline unsigned int byte_pos(struct loopback_pcm *dpcm, unsigned int x)
+{
+ if (dpcm->pcm_rate_shift == NO_PITCH) {
+ x /= HZ;
+ } else {
+ x = div_u64(NO_PITCH * (unsigned long long)x,
+ HZ * (unsigned long long)dpcm->pcm_rate_shift);
+ }
+ return x - (x % dpcm->pcm_salign);
+}
+
+static inline unsigned int frac_pos(struct loopback_pcm *dpcm, unsigned int x)
+{
+ if (dpcm->pcm_rate_shift == NO_PITCH) { /* no pitch */
+ return x * HZ;
+ } else {
+ x = div_u64(dpcm->pcm_rate_shift * (unsigned long long)x * HZ,
+ NO_PITCH);
+ }
+ return x;
+}
+
+static inline struct loopback_setup *get_setup(struct loopback_pcm *dpcm)
+{
+ int device = dpcm->substream->pstr->pcm->device;
+
+ if (dpcm->substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ device ^= 1;
+ return &dpcm->loopback->setup[dpcm->substream->number][device];
+}
+
+static inline unsigned int get_notify(struct loopback_pcm *dpcm)
+{
+ return get_setup(dpcm)->notify;
+}
+
+static inline unsigned int get_rate_shift(struct loopback_pcm *dpcm)
+{
+ return get_setup(dpcm)->rate_shift;
+}
+
+static void loopback_timer_start(struct loopback_pcm *dpcm)
+{
+ unsigned long tick;
+ unsigned int rate_shift = get_rate_shift(dpcm);
+
+ if (rate_shift != dpcm->pcm_rate_shift) {
+ dpcm->pcm_rate_shift = rate_shift;
+ dpcm->period_size_frac = frac_pos(dpcm, dpcm->pcm_period_size);
+ }
+ if (dpcm->period_size_frac <= dpcm->irq_pos) {
+ dpcm->irq_pos %= dpcm->period_size_frac;
+ dpcm->period_update_pending = 1;
+ }
+ tick = dpcm->period_size_frac - dpcm->irq_pos;
+ tick = (tick + dpcm->pcm_bps - 1) / dpcm->pcm_bps;
+ dpcm->timer.expires = jiffies + tick;
+ add_timer(&dpcm->timer);
+}
+
+static inline void loopback_timer_stop(struct loopback_pcm *dpcm)
+{
+ del_timer(&dpcm->timer);
+ dpcm->timer.expires = 0;
+}
+
+#define CABLE_VALID_PLAYBACK (1 << SNDRV_PCM_STREAM_PLAYBACK)
+#define CABLE_VALID_CAPTURE (1 << SNDRV_PCM_STREAM_CAPTURE)
+#define CABLE_VALID_BOTH (CABLE_VALID_PLAYBACK|CABLE_VALID_CAPTURE)
+
+static int loopback_check_format(struct loopback_cable *cable, int stream)
+{
+ struct snd_pcm_runtime *runtime, *cruntime;
+ struct loopback_setup *setup;
+ struct snd_card *card;
+ int check;
+
+ if (cable->valid != CABLE_VALID_BOTH) {
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ goto __notify;
+ return 0;
+ }
+ runtime = cable->streams[SNDRV_PCM_STREAM_PLAYBACK]->
+ substream->runtime;
+ cruntime = cable->streams[SNDRV_PCM_STREAM_CAPTURE]->
+ substream->runtime;
+ check = runtime->format != cruntime->format ||
+ runtime->rate != cruntime->rate ||
+ runtime->channels != cruntime->channels;
+ if (!check)
+ return 0;
+ if (stream == SNDRV_PCM_STREAM_CAPTURE) {
+ return -EIO;
+ } else {
+ snd_pcm_stop(cable->streams[SNDRV_PCM_STREAM_CAPTURE]->
+ substream, SNDRV_PCM_STATE_DRAINING);
+ __notify:
+ runtime = cable->streams[SNDRV_PCM_STREAM_PLAYBACK]->
+ substream->runtime;
+ setup = get_setup(cable->streams[SNDRV_PCM_STREAM_PLAYBACK]);
+ card = cable->streams[SNDRV_PCM_STREAM_PLAYBACK]->loopback->card;
+ if (setup->format != runtime->format) {
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &setup->format_id);
+ setup->format = runtime->format;
+ }
+ if (setup->rate != runtime->rate) {
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &setup->rate_id);
+ setup->rate = runtime->rate;
+ }
+ if (setup->channels != runtime->channels) {
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &setup->channels_id);
+ setup->channels = runtime->channels;
+ }
+ }
+ return 0;
+}
+
+static void loopback_active_notify(struct loopback_pcm *dpcm)
+{
+ snd_ctl_notify(dpcm->loopback->card,
+ SNDRV_CTL_EVENT_MASK_VALUE,
+ &get_setup(dpcm)->active_id);
+}
+
+static int loopback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct loopback_pcm *dpcm = runtime->private_data;
+ struct loopback_cable *cable = dpcm->cable;
+ int err, stream = 1 << substream->stream;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ err = loopback_check_format(cable, substream->stream);
+ if (err < 0)
+ return err;
+ dpcm->last_jiffies = jiffies;
+ dpcm->pcm_rate_shift = 0;
+ spin_lock(&cable->lock);
+ cable->running |= stream;
+ cable->pause &= ~stream;
+ spin_unlock(&cable->lock);
+ loopback_timer_start(dpcm);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ loopback_active_notify(dpcm);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ spin_lock(&cable->lock);
+ cable->running &= ~stream;
+ cable->pause &= ~stream;
+ spin_unlock(&cable->lock);
+ loopback_timer_stop(dpcm);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ loopback_active_notify(dpcm);
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ spin_lock(&cable->lock);
+ cable->pause |= stream;
+ spin_unlock(&cable->lock);
+ loopback_timer_stop(dpcm);
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ spin_lock(&cable->lock);
+ dpcm->last_jiffies = jiffies;
+ cable->pause &= ~stream;
+ spin_unlock(&cable->lock);
+ loopback_timer_start(dpcm);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void params_change_substream(struct loopback_pcm *dpcm,
+ struct snd_pcm_runtime *runtime)
+{
+ struct snd_pcm_runtime *dst_runtime;
+
+ if (dpcm == NULL || dpcm->substream == NULL)
+ return;
+ dst_runtime = dpcm->substream->runtime;
+ if (dst_runtime == NULL)
+ return;
+ dst_runtime->hw = dpcm->cable->hw;
+}
+
+static void params_change(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct loopback_pcm *dpcm = runtime->private_data;
+ struct loopback_cable *cable = dpcm->cable;
+
+ cable->hw.formats = (1ULL << runtime->format);
+ cable->hw.rate_min = runtime->rate;
+ cable->hw.rate_max = runtime->rate;
+ cable->hw.channels_min = runtime->channels;
+ cable->hw.channels_max = runtime->channels;
+ params_change_substream(cable->streams[SNDRV_PCM_STREAM_PLAYBACK],
+ runtime);
+ params_change_substream(cable->streams[SNDRV_PCM_STREAM_CAPTURE],
+ runtime);
+}
+
+static int loopback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct loopback_pcm *dpcm = runtime->private_data;
+ struct loopback_cable *cable = dpcm->cable;
+ int bps, salign;
+
+ salign = (snd_pcm_format_width(runtime->format) *
+ runtime->channels) / 8;
+ bps = salign * runtime->rate;
+ if (bps <= 0 || salign <= 0)
+ return -EINVAL;
+
+ dpcm->buf_pos = 0;
+ dpcm->pcm_buffer_size = frames_to_bytes(runtime, runtime->buffer_size);
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ /* clear capture buffer */
+ dpcm->silent_size = dpcm->pcm_buffer_size;
+ snd_pcm_format_set_silence(runtime->format, runtime->dma_area,
+ runtime->buffer_size * runtime->channels);
+ }
+
+ dpcm->irq_pos = 0;
+ dpcm->period_update_pending = 0;
+ dpcm->pcm_bps = bps;
+ dpcm->pcm_salign = salign;
+ dpcm->pcm_period_size = frames_to_bytes(runtime, runtime->period_size);
+
+ mutex_lock(&dpcm->loopback->cable_lock);
+ if (!(cable->valid & ~(1 << substream->stream)) ||
+ (get_setup(dpcm)->notify &&
+ substream->stream == SNDRV_PCM_STREAM_PLAYBACK))
+ params_change(substream);
+ cable->valid |= 1 << substream->stream;
+ mutex_unlock(&dpcm->loopback->cable_lock);
+
+ return 0;
+}
+
+static void clear_capture_buf(struct loopback_pcm *dpcm, unsigned int bytes)
+{
+ struct snd_pcm_runtime *runtime = dpcm->substream->runtime;
+ char *dst = runtime->dma_area;
+ unsigned int dst_off = dpcm->buf_pos;
+
+ if (dpcm->silent_size >= dpcm->pcm_buffer_size)
+ return;
+ if (dpcm->silent_size + bytes > dpcm->pcm_buffer_size)
+ bytes = dpcm->pcm_buffer_size - dpcm->silent_size;
+
+ for (;;) {
+ unsigned int size = bytes;
+ if (dst_off + size > dpcm->pcm_buffer_size)
+ size = dpcm->pcm_buffer_size - dst_off;
+ snd_pcm_format_set_silence(runtime->format, dst + dst_off,
+ bytes_to_frames(runtime, size) *
+ runtime->channels);
+ dpcm->silent_size += size;
+ bytes -= size;
+ if (!bytes)
+ break;
+ dst_off = 0;
+ }
+}
+
+static void copy_play_buf(struct loopback_pcm *play,
+ struct loopback_pcm *capt,
+ unsigned int bytes)
+{
+ struct snd_pcm_runtime *runtime = play->substream->runtime;
+ char *src = runtime->dma_area;
+ char *dst = capt->substream->runtime->dma_area;
+ unsigned int src_off = play->buf_pos;
+ unsigned int dst_off = capt->buf_pos;
+ unsigned int clear_bytes = 0;
+
+ /* check if playback is draining, trim the capture copy size
+ * when our pointer is at the end of playback ring buffer */
+ if (runtime->status->state == SNDRV_PCM_STATE_DRAINING &&
+ snd_pcm_playback_hw_avail(runtime) < runtime->buffer_size) {
+ snd_pcm_uframes_t appl_ptr, appl_ptr1, diff;
+ appl_ptr = appl_ptr1 = runtime->control->appl_ptr;
+ appl_ptr1 -= appl_ptr1 % runtime->buffer_size;
+ appl_ptr1 += play->buf_pos / play->pcm_salign;
+ if (appl_ptr < appl_ptr1)
+ appl_ptr1 -= runtime->buffer_size;
+ diff = (appl_ptr - appl_ptr1) * play->pcm_salign;
+ if (diff < bytes) {
+ clear_bytes = bytes - diff;
+ bytes = diff;
+ }
+ }
+
+ for (;;) {
+ unsigned int size = bytes;
+ if (src_off + size > play->pcm_buffer_size)
+ size = play->pcm_buffer_size - src_off;
+ if (dst_off + size > capt->pcm_buffer_size)
+ size = capt->pcm_buffer_size - dst_off;
+ memcpy(dst + dst_off, src + src_off, size);
+ capt->silent_size = 0;
+ bytes -= size;
+ if (!bytes)
+ break;
+ src_off = (src_off + size) % play->pcm_buffer_size;
+ dst_off = (dst_off + size) % capt->pcm_buffer_size;
+ }
+
+ if (clear_bytes > 0) {
+ clear_capture_buf(capt, clear_bytes);
+ capt->silent_size = 0;
+ }
+}
+
+#define BYTEPOS_UPDATE_POSONLY 0
+#define BYTEPOS_UPDATE_CLEAR 1
+#define BYTEPOS_UPDATE_COPY 2
+
+static void loopback_bytepos_update(struct loopback_pcm *dpcm,
+ unsigned int delta,
+ unsigned int cmd)
+{
+ unsigned int count;
+ unsigned long last_pos;
+
+ last_pos = byte_pos(dpcm, dpcm->irq_pos);
+ dpcm->irq_pos += delta * dpcm->pcm_bps;
+ count = byte_pos(dpcm, dpcm->irq_pos) - last_pos;
+ if (!count)
+ return;
+ if (cmd == BYTEPOS_UPDATE_CLEAR)
+ clear_capture_buf(dpcm, count);
+ else if (cmd == BYTEPOS_UPDATE_COPY)
+ copy_play_buf(dpcm->cable->streams[SNDRV_PCM_STREAM_PLAYBACK],
+ dpcm->cable->streams[SNDRV_PCM_STREAM_CAPTURE],
+ count);
+ dpcm->buf_pos += count;
+ dpcm->buf_pos %= dpcm->pcm_buffer_size;
+ if (dpcm->irq_pos >= dpcm->period_size_frac) {
+ dpcm->irq_pos %= dpcm->period_size_frac;
+ dpcm->period_update_pending = 1;
+ }
+}
+
+static unsigned int loopback_pos_update(struct loopback_cable *cable)
+{
+ struct loopback_pcm *dpcm_play =
+ cable->streams[SNDRV_PCM_STREAM_PLAYBACK];
+ struct loopback_pcm *dpcm_capt =
+ cable->streams[SNDRV_PCM_STREAM_CAPTURE];
+ unsigned long delta_play = 0, delta_capt = 0;
+ unsigned int running;
+
+ spin_lock(&cable->lock);
+ running = cable->running ^ cable->pause;
+ if (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) {
+ delta_play = jiffies - dpcm_play->last_jiffies;
+ dpcm_play->last_jiffies += delta_play;
+ }
+
+ if (running & (1 << SNDRV_PCM_STREAM_CAPTURE)) {
+ delta_capt = jiffies - dpcm_capt->last_jiffies;
+ dpcm_capt->last_jiffies += delta_capt;
+ }
+
+ if (delta_play == 0 && delta_capt == 0) {
+ spin_unlock(&cable->lock);
+ return running;
+ }
+
+ if (delta_play > delta_capt) {
+ loopback_bytepos_update(dpcm_play, delta_play - delta_capt,
+ BYTEPOS_UPDATE_POSONLY);
+ delta_play = delta_capt;
+ } else if (delta_play < delta_capt) {
+ loopback_bytepos_update(dpcm_capt, delta_capt - delta_play,
+ BYTEPOS_UPDATE_CLEAR);
+ delta_capt = delta_play;
+ }
+
+ if (delta_play == 0 && delta_capt == 0) {
+ spin_unlock(&cable->lock);
+ return running;
+ }
+ /* note delta_capt == delta_play at this moment */
+ loopback_bytepos_update(dpcm_capt, delta_capt, BYTEPOS_UPDATE_COPY);
+ loopback_bytepos_update(dpcm_play, delta_play, BYTEPOS_UPDATE_POSONLY);
+ spin_unlock(&cable->lock);
+ return running;
+}
+
+static void loopback_timer_function(unsigned long data)
+{
+ struct loopback_pcm *dpcm = (struct loopback_pcm *)data;
+ unsigned int running;
+
+ running = loopback_pos_update(dpcm->cable);
+ if (running & (1 << dpcm->substream->stream)) {
+ loopback_timer_start(dpcm);
+ if (dpcm->period_update_pending) {
+ dpcm->period_update_pending = 0;
+ snd_pcm_period_elapsed(dpcm->substream);
+ }
+ }
+}
+
+static snd_pcm_uframes_t loopback_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct loopback_pcm *dpcm = runtime->private_data;
+
+ loopback_pos_update(dpcm->cable);
+ return bytes_to_frames(runtime, dpcm->buf_pos);
+}
+
+static struct snd_pcm_hardware loopback_pcm_hardware =
+{
+ .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |
+ SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE |
+ SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE),
+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_192000,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .channels_min = 1,
+ .channels_max = 32,
+ .buffer_bytes_max = 2 * 1024 * 1024,
+ .period_bytes_min = 64,
+ /* note check overflow in frac_pos() using pcm_rate_shift before
+ changing period_bytes_max value */
+ .period_bytes_max = 1024 * 1024,
+ .periods_min = 1,
+ .periods_max = 1024,
+ .fifo_size = 0,
+};
+
+static void loopback_runtime_free(struct snd_pcm_runtime *runtime)
+{
+ struct loopback_pcm *dpcm = runtime->private_data;
+ kfree(dpcm);
+}
+
+static int loopback_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+}
+
+static int loopback_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct loopback_pcm *dpcm = runtime->private_data;
+ struct loopback_cable *cable = dpcm->cable;
+
+ mutex_lock(&dpcm->loopback->cable_lock);
+ cable->valid &= ~(1 << substream->stream);
+ mutex_unlock(&dpcm->loopback->cable_lock);
+ return snd_pcm_lib_free_pages(substream);
+}
+
+static unsigned int get_cable_index(struct snd_pcm_substream *substream)
+{
+ if (!substream->pcm->device)
+ return substream->stream;
+ else
+ return !substream->stream;
+}
+
+static int rule_format(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+
+ struct snd_pcm_hardware *hw = rule->private;
+ struct snd_mask *maskp = hw_param_mask(params, rule->var);
+
+ maskp->bits[0] &= (u_int32_t)hw->formats;
+ maskp->bits[1] &= (u_int32_t)(hw->formats >> 32);
+ memset(maskp->bits + 2, 0, (SNDRV_MASK_MAX-64) / 8); /* clear rest */
+ if (! maskp->bits[0] && ! maskp->bits[1])
+ return -EINVAL;
+ return 0;
+}
+
+static int rule_rate(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_pcm_hardware *hw = rule->private;
+ struct snd_interval t;
+
+ t.min = hw->rate_min;
+ t.max = hw->rate_max;
+ t.openmin = t.openmax = 0;
+ t.integer = 0;
+ return snd_interval_refine(hw_param_interval(params, rule->var), &t);
+}
+
+static int rule_channels(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_pcm_hardware *hw = rule->private;
+ struct snd_interval t;
+
+ t.min = hw->channels_min;
+ t.max = hw->channels_max;
+ t.openmin = t.openmax = 0;
+ t.integer = 0;
+ return snd_interval_refine(hw_param_interval(params, rule->var), &t);
+}
+
+static int loopback_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct loopback *loopback = substream->private_data;
+ struct loopback_pcm *dpcm;
+ struct loopback_cable *cable;
+ int err = 0;
+ int dev = get_cable_index(substream);
+
+ mutex_lock(&loopback->cable_lock);
+ dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL);
+ if (!dpcm) {
+ err = -ENOMEM;
+ goto unlock;
+ }
+ dpcm->loopback = loopback;
+ dpcm->substream = substream;
+ setup_timer(&dpcm->timer, loopback_timer_function,
+ (unsigned long)dpcm);
+
+ cable = loopback->cables[substream->number][dev];
+ if (!cable) {
+ cable = kzalloc(sizeof(*cable), GFP_KERNEL);
+ if (!cable) {
+ kfree(dpcm);
+ err = -ENOMEM;
+ goto unlock;
+ }
+ spin_lock_init(&cable->lock);
+ cable->hw = loopback_pcm_hardware;
+ loopback->cables[substream->number][dev] = cable;
+ }
+ dpcm->cable = cable;
+ cable->streams[substream->stream] = dpcm;
+
+ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+
+ /* use dynamic rules based on actual runtime->hw values */
+ /* note that the default rules created in the PCM midlevel code */
+ /* are cached -> they do not reflect the actual state */
+ err = snd_pcm_hw_rule_add(runtime, 0,
+ SNDRV_PCM_HW_PARAM_FORMAT,
+ rule_format, &runtime->hw,
+ SNDRV_PCM_HW_PARAM_FORMAT, -1);
+ if (err < 0)
+ goto unlock;
+ err = snd_pcm_hw_rule_add(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ rule_rate, &runtime->hw,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+ if (err < 0)
+ goto unlock;
+ err = snd_pcm_hw_rule_add(runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ rule_channels, &runtime->hw,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ if (err < 0)
+ goto unlock;
+
+ runtime->private_data = dpcm;
+ runtime->private_free = loopback_runtime_free;
+ if (get_notify(dpcm))
+ runtime->hw = loopback_pcm_hardware;
+ else
+ runtime->hw = cable->hw;
+ unlock:
+ mutex_unlock(&loopback->cable_lock);
+ return err;
+}
+
+static int loopback_close(struct snd_pcm_substream *substream)
+{
+ struct loopback *loopback = substream->private_data;
+ struct loopback_pcm *dpcm = substream->runtime->private_data;
+ struct loopback_cable *cable;
+ int dev = get_cable_index(substream);
+
+ loopback_timer_stop(dpcm);
+ mutex_lock(&loopback->cable_lock);
+ cable = loopback->cables[substream->number][dev];
+ if (cable->streams[!substream->stream]) {
+ /* other stream is still alive */
+ cable->streams[substream->stream] = NULL;
+ } else {
+ /* free the cable */
+ loopback->cables[substream->number][dev] = NULL;
+ kfree(cable);
+ }
+ mutex_unlock(&loopback->cable_lock);
+ return 0;
+}
+
+static struct snd_pcm_ops loopback_playback_ops = {
+ .open = loopback_open,
+ .close = loopback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = loopback_hw_params,
+ .hw_free = loopback_hw_free,
+ .prepare = loopback_prepare,
+ .trigger = loopback_trigger,
+ .pointer = loopback_pointer,
+};
+
+static struct snd_pcm_ops loopback_capture_ops = {
+ .open = loopback_open,
+ .close = loopback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = loopback_hw_params,
+ .hw_free = loopback_hw_free,
+ .prepare = loopback_prepare,
+ .trigger = loopback_trigger,
+ .pointer = loopback_pointer,
+};
+
+static int __devinit loopback_pcm_new(struct loopback *loopback,
+ int device, int substreams)
+{
+ struct snd_pcm *pcm;
+ int err;
+
+ err = snd_pcm_new(loopback->card, "Loopback PCM", device,
+ substreams, substreams, &pcm);
+ if (err < 0)
+ return err;
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &loopback_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &loopback_capture_ops);
+
+ pcm->private_data = loopback;
+ pcm->info_flags = 0;
+ strcpy(pcm->name, "Loopback PCM");
+
+ loopback->pcm[device] = pcm;
+
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
+ snd_dma_continuous_data(GFP_KERNEL),
+ 0, 2 * 1024 * 1024);
+ return 0;
+}
+
+static int loopback_rate_shift_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 = 80000;
+ uinfo->value.integer.max = 120000;
+ uinfo->value.integer.step = 1;
+ return 0;
+}
+
+static int loopback_rate_shift_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct loopback *loopback = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] =
+ loopback->setup[kcontrol->id.subdevice]
+ [kcontrol->id.device].rate_shift;
+ return 0;
+}
+
+static int loopback_rate_shift_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct loopback *loopback = snd_kcontrol_chip(kcontrol);
+ unsigned int val;
+ int change = 0;
+
+ val = ucontrol->value.integer.value[0];
+ if (val < 80000)
+ val = 80000;
+ if (val > 120000)
+ val = 120000;
+ mutex_lock(&loopback->cable_lock);
+ if (val != loopback->setup[kcontrol->id.subdevice]
+ [kcontrol->id.device].rate_shift) {
+ loopback->setup[kcontrol->id.subdevice]
+ [kcontrol->id.device].rate_shift = val;
+ change = 1;
+ }
+ mutex_unlock(&loopback->cable_lock);
+ return change;
+}
+
+static int loopback_notify_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct loopback *loopback = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] =
+ loopback->setup[kcontrol->id.subdevice]
+ [kcontrol->id.device].notify;
+ return 0;
+}
+
+static int loopback_notify_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct loopback *loopback = snd_kcontrol_chip(kcontrol);
+ unsigned int val;
+ int change = 0;
+
+ val = ucontrol->value.integer.value[0] ? 1 : 0;
+ if (val != loopback->setup[kcontrol->id.subdevice]
+ [kcontrol->id.device].notify) {
+ loopback->setup[kcontrol->id.subdevice]
+ [kcontrol->id.device].notify = val;
+ change = 1;
+ }
+ return change;
+}
+
+static int loopback_active_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct loopback *loopback = snd_kcontrol_chip(kcontrol);
+ struct loopback_cable *cable = loopback->cables
+ [kcontrol->id.subdevice][kcontrol->id.device ^ 1];
+ unsigned int val = 0;
+
+ if (cable != NULL)
+ val = (cable->running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) ?
+ 1 : 0;
+ ucontrol->value.integer.value[0] = val;
+ return 0;
+}
+
+static int loopback_format_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 = SNDRV_PCM_FORMAT_LAST;
+ uinfo->value.integer.step = 1;
+ return 0;
+}
+
+static int loopback_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct loopback *loopback = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] =
+ loopback->setup[kcontrol->id.subdevice]
+ [kcontrol->id.device].format;
+ return 0;
+}
+
+static int loopback_rate_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 = 192000;
+ uinfo->value.integer.step = 1;
+ return 0;
+}
+
+static int loopback_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct loopback *loopback = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] =
+ loopback->setup[kcontrol->id.subdevice]
+ [kcontrol->id.device].rate;
+ return 0;
+}
+
+static int loopback_channels_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 = 1;
+ uinfo->value.integer.max = 1024;
+ uinfo->value.integer.step = 1;
+ return 0;
+}
+
+static int loopback_channels_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct loopback *loopback = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] =
+ loopback->setup[kcontrol->id.subdevice]
+ [kcontrol->id.device].channels;
+ return 0;
+}
+
+static struct snd_kcontrol_new loopback_controls[] __devinitdata = {
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "PCM Rate Shift 100000",
+ .info = loopback_rate_shift_info,
+ .get = loopback_rate_shift_get,
+ .put = loopback_rate_shift_put,
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "PCM Notify",
+ .info = snd_ctl_boolean_mono_info,
+ .get = loopback_notify_get,
+ .put = loopback_notify_put,
+},
+#define ACTIVE_IDX 2
+{
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "PCM Slave Active",
+ .info = snd_ctl_boolean_mono_info,
+ .get = loopback_active_get,
+},
+#define FORMAT_IDX 3
+{
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "PCM Slave Format",
+ .info = loopback_format_info,
+ .get = loopback_format_get
+},
+#define RATE_IDX 4
+{
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "PCM Slave Rate",
+ .info = loopback_rate_info,
+ .get = loopback_rate_get
+},
+#define CHANNELS_IDX 5
+{
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "PCM Slave Channels",
+ .info = loopback_channels_info,
+ .get = loopback_channels_get
+}
+};
+
+static int __devinit loopback_mixer_new(struct loopback *loopback, int notify)
+{
+ struct snd_card *card = loopback->card;
+ struct snd_pcm *pcm;
+ struct snd_kcontrol *kctl;
+ struct loopback_setup *setup;
+ int err, dev, substr, substr_count, idx;
+
+ strcpy(card->mixername, "Loopback Mixer");
+ for (dev = 0; dev < 2; dev++) {
+ pcm = loopback->pcm[dev];
+ substr_count =
+ pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream_count;
+ for (substr = 0; substr < substr_count; substr++) {
+ setup = &loopback->setup[substr][dev];
+ setup->notify = notify;
+ setup->rate_shift = NO_PITCH;
+ setup->format = SNDRV_PCM_FORMAT_S16_LE;
+ setup->rate = 48000;
+ setup->channels = 2;
+ for (idx = 0; idx < ARRAY_SIZE(loopback_controls);
+ idx++) {
+ kctl = snd_ctl_new1(&loopback_controls[idx],
+ loopback);
+ if (!kctl)
+ return -ENOMEM;
+ kctl->id.device = dev;
+ kctl->id.subdevice = substr;
+ switch (idx) {
+ case ACTIVE_IDX:
+ setup->active_id = kctl->id;
+ break;
+ case FORMAT_IDX:
+ setup->format_id = kctl->id;
+ break;
+ case RATE_IDX:
+ setup->rate_id = kctl->id;
+ break;
+ case CHANNELS_IDX:
+ setup->channels_id = kctl->id;
+ break;
+ default:
+ break;
+ }
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
+ return err;
+ }
+ }
+ }
+ return 0;
+}
+
+#ifdef CONFIG_PROC_FS
+
+static void print_dpcm_info(struct snd_info_buffer *buffer,
+ struct loopback_pcm *dpcm,
+ const char *id)
+{
+ snd_iprintf(buffer, " %s\n", id);
+ if (dpcm == NULL) {
+ snd_iprintf(buffer, " inactive\n");
+ return;
+ }
+ snd_iprintf(buffer, " buffer_size:\t%u\n", dpcm->pcm_buffer_size);
+ snd_iprintf(buffer, " buffer_pos:\t\t%u\n", dpcm->buf_pos);
+ snd_iprintf(buffer, " silent_size:\t%u\n", dpcm->silent_size);
+ snd_iprintf(buffer, " period_size:\t%u\n", dpcm->pcm_period_size);
+ snd_iprintf(buffer, " bytes_per_sec:\t%u\n", dpcm->pcm_bps);
+ snd_iprintf(buffer, " sample_align:\t%u\n", dpcm->pcm_salign);
+ snd_iprintf(buffer, " rate_shift:\t\t%u\n", dpcm->pcm_rate_shift);
+ snd_iprintf(buffer, " update_pending:\t%u\n",
+ dpcm->period_update_pending);
+ snd_iprintf(buffer, " irq_pos:\t\t%u\n", dpcm->irq_pos);
+ snd_iprintf(buffer, " period_frac:\t%u\n", dpcm->period_size_frac);
+ snd_iprintf(buffer, " last_jiffies:\t%lu (%lu)\n",
+ dpcm->last_jiffies, jiffies);
+ snd_iprintf(buffer, " timer_expires:\t%lu\n", dpcm->timer.expires);
+}
+
+static void print_substream_info(struct snd_info_buffer *buffer,
+ struct loopback *loopback,
+ int sub,
+ int num)
+{
+ struct loopback_cable *cable = loopback->cables[sub][num];
+
+ snd_iprintf(buffer, "Cable %i substream %i:\n", num, sub);
+ if (cable == NULL) {
+ snd_iprintf(buffer, " inactive\n");
+ return;
+ }
+ snd_iprintf(buffer, " valid: %u\n", cable->valid);
+ snd_iprintf(buffer, " running: %u\n", cable->running);
+ snd_iprintf(buffer, " pause: %u\n", cable->pause);
+ print_dpcm_info(buffer, cable->streams[0], "Playback");
+ print_dpcm_info(buffer, cable->streams[1], "Capture");
+}
+
+static void print_cable_info(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct loopback *loopback = entry->private_data;
+ int sub, num;
+
+ mutex_lock(&loopback->cable_lock);
+ num = entry->name[strlen(entry->name)-1];
+ num = num == '0' ? 0 : 1;
+ for (sub = 0; sub < MAX_PCM_SUBSTREAMS; sub++)
+ print_substream_info(buffer, loopback, sub, num);
+ mutex_unlock(&loopback->cable_lock);
+}
+
+static int __devinit loopback_proc_new(struct loopback *loopback, int cidx)
+{
+ char name[32];
+ struct snd_info_entry *entry;
+ int err;
+
+ snprintf(name, sizeof(name), "cable#%d", cidx);
+ err = snd_card_proc_new(loopback->card, name, &entry);
+ if (err < 0)
+ return err;
+
+ snd_info_set_text_ops(entry, loopback, print_cable_info);
+ return 0;
+}
+
+#else /* !CONFIG_PROC_FS */
+
+#define loopback_proc_new(loopback, cidx) do { } while (0)
+
+#endif
+
+static int __devinit loopback_probe(struct platform_device *devptr)
+{
+ struct snd_card *card;
+ struct loopback *loopback;
+ int dev = devptr->id;
+ int err;
+
+ err = snd_card_create(index[dev], id[dev], THIS_MODULE,
+ sizeof(struct loopback), &card);
+ if (err < 0)
+ return err;
+ loopback = card->private_data;
+
+ if (pcm_substreams[dev] < 1)
+ pcm_substreams[dev] = 1;
+ if (pcm_substreams[dev] > MAX_PCM_SUBSTREAMS)
+ pcm_substreams[dev] = MAX_PCM_SUBSTREAMS;
+
+ loopback->card = card;
+ mutex_init(&loopback->cable_lock);
+
+ err = loopback_pcm_new(loopback, 0, pcm_substreams[dev]);
+ if (err < 0)
+ goto __nodev;
+ err = loopback_pcm_new(loopback, 1, pcm_substreams[dev]);
+ if (err < 0)
+ goto __nodev;
+ err = loopback_mixer_new(loopback, pcm_notify[dev] ? 1 : 0);
+ if (err < 0)
+ goto __nodev;
+ loopback_proc_new(loopback, 0);
+ loopback_proc_new(loopback, 1);
+ strcpy(card->driver, "Loopback");
+ strcpy(card->shortname, "Loopback");
+ sprintf(card->longname, "Loopback %i", dev + 1);
+ err = snd_card_register(card);
+ if (!err) {
+ platform_set_drvdata(devptr, card);
+ return 0;
+ }
+ __nodev:
+ snd_card_free(card);
+ return err;
+}
+
+static int __devexit loopback_remove(struct platform_device *devptr)
+{
+ snd_card_free(platform_get_drvdata(devptr));
+ platform_set_drvdata(devptr, NULL);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int loopback_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ struct snd_card *card = platform_get_drvdata(pdev);
+ struct loopback *loopback = card->private_data;
+
+ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+
+ snd_pcm_suspend_all(loopback->pcm[0]);
+ snd_pcm_suspend_all(loopback->pcm[1]);
+ return 0;
+}
+
+static int loopback_resume(struct platform_device *pdev)
+{
+ struct snd_card *card = platform_get_drvdata(pdev);
+
+ snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+ return 0;
+}
+#endif
+
+#define SND_LOOPBACK_DRIVER "snd_aloop"
+
+static struct platform_driver loopback_driver = {
+ .probe = loopback_probe,
+ .remove = __devexit_p(loopback_remove),
+#ifdef CONFIG_PM
+ .suspend = loopback_suspend,
+ .resume = loopback_resume,
+#endif
+ .driver = {
+ .name = SND_LOOPBACK_DRIVER
+ },
+};
+
+static void loopback_unregister_all(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(devices); ++i)
+ platform_device_unregister(devices[i]);
+ platform_driver_unregister(&loopback_driver);
+}
+
+static int __init alsa_card_loopback_init(void)
+{
+ int i, err, cards;
+
+ err = platform_driver_register(&loopback_driver);
+ if (err < 0)
+ return err;
+
+
+ cards = 0;
+ for (i = 0; i < SNDRV_CARDS; i++) {
+ struct platform_device *device;
+ if (!enable[i])
+ continue;
+ device = platform_device_register_simple(SND_LOOPBACK_DRIVER,
+ i, NULL, 0);
+ if (IS_ERR(device))
+ continue;
+ if (!platform_get_drvdata(device)) {
+ platform_device_unregister(device);
+ continue;
+ }
+ devices[i] = device;
+ cards++;
+ }
+ if (!cards) {
+#ifdef MODULE
+ printk(KERN_ERR "aloop: No loopback enabled\n");
+#endif
+ loopback_unregister_all();
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static void __exit alsa_card_loopback_exit(void)
+{
+ loopback_unregister_all();
+}
+
+module_init(alsa_card_loopback_init)
+module_exit(alsa_card_loopback_exit)
diff --git a/sound/drivers/virmidi.c b/sound/drivers/virmidi.c
index 0e631c3221e..f4cd49336f3 100644
--- a/sound/drivers/virmidi.c
+++ b/sound/drivers/virmidi.c
@@ -94,7 +94,7 @@ static int __devinit snd_virmidi_probe(struct platform_device *devptr)
sizeof(struct snd_card_virmidi), &card);
if (err < 0)
return err;
- vmidi = (struct snd_card_virmidi *)card->private_data;
+ vmidi = card->private_data;
vmidi->card = card;
if (midi_devs[dev] > MAX_MIDI_DEVICES) {
diff --git a/sound/i2c/other/ak4xxx-adda.c b/sound/i2c/other/ak4xxx-adda.c
index 1adb8a3c2b6..57ccba88700 100644
--- a/sound/i2c/other/ak4xxx-adda.c
+++ b/sound/i2c/other/ak4xxx-adda.c
@@ -878,7 +878,7 @@ static int build_deemphasis(struct snd_akm4xxx *ak, int num_emphs)
static void proc_regs_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
- struct snd_akm4xxx *ak = (struct snd_akm4xxx *)entry->private_data;
+ struct snd_akm4xxx *ak = entry->private_data;
int reg, val, chip;
for (chip = 0; chip < ak->num_chips; chip++) {
for (reg = 0; reg < ak->total_regs; reg++) {
@@ -900,7 +900,7 @@ static int proc_init(struct snd_akm4xxx *ak)
return 0;
}
#else /* !CONFIG_PROC_FS */
-static int proc_init(struct snd_akm4xxx *ak) {}
+static int proc_init(struct snd_akm4xxx *ak) { return 0; }
#endif
int snd_akm4xxx_build_controls(struct snd_akm4xxx *ak)
diff --git a/sound/isa/Kconfig b/sound/isa/Kconfig
index c6990c68079..52064cfa91f 100644
--- a/sound/isa/Kconfig
+++ b/sound/isa/Kconfig
@@ -77,6 +77,32 @@ config SND_ALS100
To compile this driver as a module, choose M here: the module
will be called snd-als100.
+config SND_AZT1605
+ tristate "Aztech AZT1605 Driver"
+ depends on SND
+ select SND_WSS_LIB
+ select SND_MPU401_UART
+ select SND_OPL3_LIB
+ help
+ Say Y here to include support for Aztech Sound Galaxy cards
+ based on the AZT1605 chipset.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-azt1605.
+
+config SND_AZT2316
+ tristate "Aztech AZT2316 Driver"
+ depends on SND
+ select SND_WSS_LIB
+ select SND_MPU401_UART
+ select SND_OPL3_LIB
+ help
+ Say Y here to include support for Aztech Sound Galaxy cards
+ based on the AZT2316 chipset.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-azt2316.
+
config SND_AZT2320
tristate "Aztech Systems AZT2320"
depends on PNP
@@ -351,16 +377,6 @@ config SND_SB16_CSP
coprocessor can do variable tasks like various compression and
decompression algorithms.
-config SND_SGALAXY
- tristate "Aztech Sound Galaxy"
- select SND_WSS_LIB
- help
- Say Y here to include support for Aztech Sound Galaxy
- soundcards.
-
- To compile this driver as a module, choose M here: the module
- will be called snd-sgalaxy.
-
config SND_SSCAPE
tristate "Ensoniq SoundScape driver"
select SND_MPU401_UART
diff --git a/sound/isa/Makefile b/sound/isa/Makefile
index c73d30c4f46..8d781e419e2 100644
--- a/sound/isa/Makefile
+++ b/sound/isa/Makefile
@@ -10,7 +10,6 @@ snd-cmi8330-objs := cmi8330.o
snd-es18xx-objs := es18xx.o
snd-opl3sa2-objs := opl3sa2.o
snd-sc6000-objs := sc6000.o
-snd-sgalaxy-objs := sgalaxy.o
snd-sscape-objs := sscape.o
# Toplevel Module Dependency
@@ -21,8 +20,7 @@ obj-$(CONFIG_SND_CMI8330) += snd-cmi8330.o
obj-$(CONFIG_SND_ES18XX) += snd-es18xx.o
obj-$(CONFIG_SND_OPL3SA2) += snd-opl3sa2.o
obj-$(CONFIG_SND_SC6000) += snd-sc6000.o
-obj-$(CONFIG_SND_SGALAXY) += snd-sgalaxy.o
obj-$(CONFIG_SND_SSCAPE) += snd-sscape.o
-obj-$(CONFIG_SND) += ad1816a/ ad1848/ cs423x/ es1688/ gus/ msnd/ opti9xx/ \
+obj-$(CONFIG_SND) += ad1816a/ ad1848/ cs423x/ es1688/ galaxy/ gus/ msnd/ opti9xx/ \
sb/ wavefront/ wss/
diff --git a/sound/isa/ad1816a/ad1816a.c b/sound/isa/ad1816a/ad1816a.c
index bbcbf92a8eb..3cb75bc9769 100644
--- a/sound/isa/ad1816a/ad1816a.c
+++ b/sound/isa/ad1816a/ad1816a.c
@@ -162,7 +162,7 @@ static int __devinit snd_card_ad1816a_probe(int dev, struct pnp_card_link *pcard
sizeof(struct snd_card_ad1816a), &card);
if (error < 0)
return error;
- acard = (struct snd_card_ad1816a *)card->private_data;
+ acard = card->private_data;
if ((error = snd_card_ad1816a_pnp(dev, acard, pcard, pid))) {
snd_card_free(card);
diff --git a/sound/isa/azt2320.c b/sound/isa/azt2320.c
index f7aa637b0d1..aac8dc15c2f 100644
--- a/sound/isa/azt2320.c
+++ b/sound/isa/azt2320.c
@@ -188,7 +188,7 @@ static int __devinit snd_card_azt2320_probe(int dev,
sizeof(struct snd_card_azt2320), &card);
if (error < 0)
return error;
- acard = (struct snd_card_azt2320 *)card->private_data;
+ acard = card->private_data;
if ((error = snd_card_azt2320_pnp(dev, acard, pcard, pid))) {
snd_card_free(card);
diff --git a/sound/isa/galaxy/Makefile b/sound/isa/galaxy/Makefile
new file mode 100644
index 00000000000..e307066d431
--- /dev/null
+++ b/sound/isa/galaxy/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+snd-azt1605-objs := azt1605.o
+snd-azt2316-objs := azt2316.o
+
+obj-$(CONFIG_SND_AZT1605) += snd-azt1605.o
+obj-$(CONFIG_SND_AZT2316) += snd-azt2316.o
diff --git a/sound/isa/galaxy/azt1605.c b/sound/isa/galaxy/azt1605.c
new file mode 100644
index 00000000000..9a97643cb71
--- /dev/null
+++ b/sound/isa/galaxy/azt1605.c
@@ -0,0 +1,91 @@
+/*
+ * Aztech AZT1605 Driver
+ * Copyright (C) 2007,2010 Rene Herman
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#define AZT1605
+
+#define CRD_NAME "Aztech AZT1605"
+#define DRV_NAME "AZT1605"
+#define DEV_NAME "azt1605"
+
+#define GALAXY_DSP_MAJOR 2
+#define GALAXY_DSP_MINOR 1
+
+#define GALAXY_CONFIG_SIZE 3
+
+/*
+ * 24-bit config register
+ */
+
+#define GALAXY_CONFIG_SBA_220 (0 << 0)
+#define GALAXY_CONFIG_SBA_240 (1 << 0)
+#define GALAXY_CONFIG_SBA_260 (2 << 0)
+#define GALAXY_CONFIG_SBA_280 (3 << 0)
+#define GALAXY_CONFIG_SBA_MASK GALAXY_CONFIG_SBA_280
+
+#define GALAXY_CONFIG_MPUA_300 (0 << 2)
+#define GALAXY_CONFIG_MPUA_330 (1 << 2)
+
+#define GALAXY_CONFIG_MPU_ENABLE (1 << 3)
+
+#define GALAXY_CONFIG_GAME_ENABLE (1 << 4)
+
+#define GALAXY_CONFIG_CD_PANASONIC (1 << 5)
+#define GALAXY_CONFIG_CD_MITSUMI (1 << 6)
+#define GALAXY_CONFIG_CD_MASK (\
+ GALAXY_CONFIG_CD_PANASONIC | GALAXY_CONFIG_CD_MITSUMI)
+
+#define GALAXY_CONFIG_UNUSED (1 << 7)
+#define GALAXY_CONFIG_UNUSED_MASK GALAXY_CONFIG_UNUSED
+
+#define GALAXY_CONFIG_SBIRQ_2 (1 << 8)
+#define GALAXY_CONFIG_SBIRQ_3 (1 << 9)
+#define GALAXY_CONFIG_SBIRQ_5 (1 << 10)
+#define GALAXY_CONFIG_SBIRQ_7 (1 << 11)
+
+#define GALAXY_CONFIG_MPUIRQ_2 (1 << 12)
+#define GALAXY_CONFIG_MPUIRQ_3 (1 << 13)
+#define GALAXY_CONFIG_MPUIRQ_5 (1 << 14)
+#define GALAXY_CONFIG_MPUIRQ_7 (1 << 15)
+
+#define GALAXY_CONFIG_WSSA_530 (0 << 16)
+#define GALAXY_CONFIG_WSSA_604 (1 << 16)
+#define GALAXY_CONFIG_WSSA_E80 (2 << 16)
+#define GALAXY_CONFIG_WSSA_F40 (3 << 16)
+
+#define GALAXY_CONFIG_WSS_ENABLE (1 << 18)
+
+#define GALAXY_CONFIG_CDIRQ_11 (1 << 19)
+#define GALAXY_CONFIG_CDIRQ_12 (1 << 20)
+#define GALAXY_CONFIG_CDIRQ_15 (1 << 21)
+#define GALAXY_CONFIG_CDIRQ_MASK (\
+ GALAXY_CONFIG_CDIRQ_11 | GALAXY_CONFIG_CDIRQ_12 |\
+ GALAXY_CONFIG_CDIRQ_15)
+
+#define GALAXY_CONFIG_CDDMA_DISABLE (0 << 22)
+#define GALAXY_CONFIG_CDDMA_0 (1 << 22)
+#define GALAXY_CONFIG_CDDMA_1 (2 << 22)
+#define GALAXY_CONFIG_CDDMA_3 (3 << 22)
+#define GALAXY_CONFIG_CDDMA_MASK GALAXY_CONFIG_CDDMA_3
+
+#define GALAXY_CONFIG_MASK (\
+ GALAXY_CONFIG_SBA_MASK | GALAXY_CONFIG_CD_MASK |\
+ GALAXY_CONFIG_UNUSED_MASK | GALAXY_CONFIG_CDIRQ_MASK |\
+ GALAXY_CONFIG_CDDMA_MASK)
+
+#include "galaxy.c"
diff --git a/sound/isa/galaxy/azt2316.c b/sound/isa/galaxy/azt2316.c
new file mode 100644
index 00000000000..189441141df
--- /dev/null
+++ b/sound/isa/galaxy/azt2316.c
@@ -0,0 +1,111 @@
+/*
+ * Aztech AZT2316 Driver
+ * Copyright (C) 2007,2010 Rene Herman
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#define AZT2316
+
+#define CRD_NAME "Aztech AZT2316"
+#define DRV_NAME "AZT2316"
+#define DEV_NAME "azt2316"
+
+#define GALAXY_DSP_MAJOR 3
+#define GALAXY_DSP_MINOR 1
+
+#define GALAXY_CONFIG_SIZE 4
+
+/*
+ * 32-bit config register
+ */
+
+#define GALAXY_CONFIG_SBA_220 (0 << 0)
+#define GALAXY_CONFIG_SBA_240 (1 << 0)
+#define GALAXY_CONFIG_SBA_260 (2 << 0)
+#define GALAXY_CONFIG_SBA_280 (3 << 0)
+#define GALAXY_CONFIG_SBA_MASK GALAXY_CONFIG_SBA_280
+
+#define GALAXY_CONFIG_SBIRQ_2 (1 << 2)
+#define GALAXY_CONFIG_SBIRQ_5 (1 << 3)
+#define GALAXY_CONFIG_SBIRQ_7 (1 << 4)
+#define GALAXY_CONFIG_SBIRQ_10 (1 << 5)
+
+#define GALAXY_CONFIG_SBDMA_DISABLE (0 << 6)
+#define GALAXY_CONFIG_SBDMA_0 (1 << 6)
+#define GALAXY_CONFIG_SBDMA_1 (2 << 6)
+#define GALAXY_CONFIG_SBDMA_3 (3 << 6)
+
+#define GALAXY_CONFIG_WSSA_530 (0 << 8)
+#define GALAXY_CONFIG_WSSA_604 (1 << 8)
+#define GALAXY_CONFIG_WSSA_E80 (2 << 8)
+#define GALAXY_CONFIG_WSSA_F40 (3 << 8)
+
+#define GALAXY_CONFIG_WSS_ENABLE (1 << 10)
+
+#define GALAXY_CONFIG_GAME_ENABLE (1 << 11)
+
+#define GALAXY_CONFIG_MPUA_300 (0 << 12)
+#define GALAXY_CONFIG_MPUA_330 (1 << 12)
+
+#define GALAXY_CONFIG_MPU_ENABLE (1 << 13)
+
+#define GALAXY_CONFIG_CDA_310 (0 << 14)
+#define GALAXY_CONFIG_CDA_320 (1 << 14)
+#define GALAXY_CONFIG_CDA_340 (2 << 14)
+#define GALAXY_CONFIG_CDA_350 (3 << 14)
+#define GALAXY_CONFIG_CDA_MASK GALAXY_CONFIG_CDA_350
+
+#define GALAXY_CONFIG_CD_DISABLE (0 << 16)
+#define GALAXY_CONFIG_CD_PANASONIC (1 << 16)
+#define GALAXY_CONFIG_CD_SONY (2 << 16)
+#define GALAXY_CONFIG_CD_MITSUMI (3 << 16)
+#define GALAXY_CONFIG_CD_AZTECH (4 << 16)
+#define GALAXY_CONFIG_CD_UNUSED_5 (5 << 16)
+#define GALAXY_CONFIG_CD_UNUSED_6 (6 << 16)
+#define GALAXY_CONFIG_CD_UNUSED_7 (7 << 16)
+#define GALAXY_CONFIG_CD_MASK GALAXY_CONFIG_CD_UNUSED_7
+
+#define GALAXY_CONFIG_CDDMA8_DISABLE (0 << 20)
+#define GALAXY_CONFIG_CDDMA8_0 (1 << 20)
+#define GALAXY_CONFIG_CDDMA8_1 (2 << 20)
+#define GALAXY_CONFIG_CDDMA8_3 (3 << 20)
+#define GALAXY_CONFIG_CDDMA8_MASK GALAXY_CONFIG_CDDMA8_3
+
+#define GALAXY_CONFIG_CDDMA16_DISABLE (0 << 22)
+#define GALAXY_CONFIG_CDDMA16_5 (1 << 22)
+#define GALAXY_CONFIG_CDDMA16_6 (2 << 22)
+#define GALAXY_CONFIG_CDDMA16_7 (3 << 22)
+#define GALAXY_CONFIG_CDDMA16_MASK GALAXY_CONFIG_CDDMA16_7
+
+#define GALAXY_CONFIG_MPUIRQ_2 (1 << 24)
+#define GALAXY_CONFIG_MPUIRQ_5 (1 << 25)
+#define GALAXY_CONFIG_MPUIRQ_7 (1 << 26)
+#define GALAXY_CONFIG_MPUIRQ_10 (1 << 27)
+
+#define GALAXY_CONFIG_CDIRQ_5 (1 << 28)
+#define GALAXY_CONFIG_CDIRQ_11 (1 << 29)
+#define GALAXY_CONFIG_CDIRQ_12 (1 << 30)
+#define GALAXY_CONFIG_CDIRQ_15 (1 << 31)
+#define GALAXY_CONFIG_CDIRQ_MASK (\
+ GALAXY_CONFIG_CDIRQ_5 | GALAXY_CONFIG_CDIRQ_11 |\
+ GALAXY_CONFIG_CDIRQ_12 | GALAXY_CONFIG_CDIRQ_15)
+
+#define GALAXY_CONFIG_MASK (\
+ GALAXY_CONFIG_SBA_MASK | GALAXY_CONFIG_CDA_MASK |\
+ GALAXY_CONFIG_CD_MASK | GALAXY_CONFIG_CDDMA16_MASK |\
+ GALAXY_CONFIG_CDDMA8_MASK | GALAXY_CONFIG_CDIRQ_MASK)
+
+#include "galaxy.c"
diff --git a/sound/isa/galaxy/galaxy.c b/sound/isa/galaxy/galaxy.c
new file mode 100644
index 00000000000..ee54df082b9
--- /dev/null
+++ b/sound/isa/galaxy/galaxy.c
@@ -0,0 +1,652 @@
+/*
+ * Aztech AZT1605/AZT2316 Driver
+ * Copyright (C) 2007,2010 Rene Herman
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/isa.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <asm/processor.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/wss.h>
+#include <sound/mpu401.h>
+#include <sound/opl3.h>
+
+MODULE_DESCRIPTION(CRD_NAME);
+MODULE_AUTHOR("Rene Herman");
+MODULE_LICENSE("GPL");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for " CRD_NAME " soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for " CRD_NAME " soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable " CRD_NAME " soundcard.");
+
+static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
+static long wss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
+static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
+static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
+static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
+static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
+static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;
+static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;
+
+module_param_array(port, long, NULL, 0444);
+MODULE_PARM_DESC(port, "Port # for " CRD_NAME " driver.");
+module_param_array(wss_port, long, NULL, 0444);
+MODULE_PARM_DESC(wss_port, "WSS port # for " CRD_NAME " driver.");
+module_param_array(mpu_port, long, NULL, 0444);
+MODULE_PARM_DESC(mpu_port, "MPU-401 port # for " CRD_NAME " driver.");
+module_param_array(fm_port, long, NULL, 0444);
+MODULE_PARM_DESC(fm_port, "FM port # for " CRD_NAME " driver.");
+module_param_array(irq, int, NULL, 0444);
+MODULE_PARM_DESC(irq, "IRQ # for " CRD_NAME " driver.");
+module_param_array(mpu_irq, int, NULL, 0444);
+MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for " CRD_NAME " driver.");
+module_param_array(dma1, int, NULL, 0444);
+MODULE_PARM_DESC(dma1, "Playback DMA # for " CRD_NAME " driver.");
+module_param_array(dma2, int, NULL, 0444);
+MODULE_PARM_DESC(dma2, "Capture DMA # for " CRD_NAME " driver.");
+
+/*
+ * Generic SB DSP support routines
+ */
+
+#define DSP_PORT_RESET 0x6
+#define DSP_PORT_READ 0xa
+#define DSP_PORT_COMMAND 0xc
+#define DSP_PORT_STATUS 0xc
+#define DSP_PORT_DATA_AVAIL 0xe
+
+#define DSP_SIGNATURE 0xaa
+
+#define DSP_COMMAND_GET_VERSION 0xe1
+
+static int __devinit dsp_get_byte(void __iomem *port, u8 *val)
+{
+ int loops = 1000;
+
+ while (!(ioread8(port + DSP_PORT_DATA_AVAIL) & 0x80)) {
+ if (!loops--)
+ return -EIO;
+ cpu_relax();
+ }
+ *val = ioread8(port + DSP_PORT_READ);
+ return 0;
+}
+
+static int __devinit dsp_reset(void __iomem *port)
+{
+ u8 val;
+
+ iowrite8(1, port + DSP_PORT_RESET);
+ udelay(10);
+ iowrite8(0, port + DSP_PORT_RESET);
+
+ if (dsp_get_byte(port, &val) < 0 || val != DSP_SIGNATURE)
+ return -ENODEV;
+
+ return 0;
+}
+
+static int __devinit dsp_command(void __iomem *port, u8 cmd)
+{
+ int loops = 1000;
+
+ while (ioread8(port + DSP_PORT_STATUS) & 0x80) {
+ if (!loops--)
+ return -EIO;
+ cpu_relax();
+ }
+ iowrite8(cmd, port + DSP_PORT_COMMAND);
+ return 0;
+}
+
+static int __devinit dsp_get_version(void __iomem *port, u8 *major, u8 *minor)
+{
+ int err;
+
+ err = dsp_command(port, DSP_COMMAND_GET_VERSION);
+ if (err < 0)
+ return err;
+
+ err = dsp_get_byte(port, major);
+ if (err < 0)
+ return err;
+
+ err = dsp_get_byte(port, minor);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+/*
+ * Generic WSS support routines
+ */
+
+#define WSS_CONFIG_DMA_0 (1 << 0)
+#define WSS_CONFIG_DMA_1 (2 << 0)
+#define WSS_CONFIG_DMA_3 (3 << 0)
+#define WSS_CONFIG_DUPLEX (1 << 2)
+#define WSS_CONFIG_IRQ_7 (1 << 3)
+#define WSS_CONFIG_IRQ_9 (2 << 3)
+#define WSS_CONFIG_IRQ_10 (3 << 3)
+#define WSS_CONFIG_IRQ_11 (4 << 3)
+
+#define WSS_PORT_CONFIG 0
+#define WSS_PORT_SIGNATURE 3
+
+#define WSS_SIGNATURE 4
+
+static int __devinit wss_detect(void __iomem *wss_port)
+{
+ if ((ioread8(wss_port + WSS_PORT_SIGNATURE) & 0x3f) != WSS_SIGNATURE)
+ return -ENODEV;
+
+ return 0;
+}
+
+static void wss_set_config(void __iomem *wss_port, u8 wss_config)
+{
+ iowrite8(wss_config, wss_port + WSS_PORT_CONFIG);
+}
+
+/*
+ * Aztech Sound Galaxy specifics
+ */
+
+#define GALAXY_PORT_CONFIG 1024
+#define CONFIG_PORT_SET 4
+
+#define DSP_COMMAND_GALAXY_8 8
+#define GALAXY_COMMAND_GET_TYPE 5
+
+#define DSP_COMMAND_GALAXY_9 9
+#define GALAXY_COMMAND_WSSMODE 0
+#define GALAXY_COMMAND_SB8MODE 1
+
+#define GALAXY_MODE_WSS GALAXY_COMMAND_WSSMODE
+#define GALAXY_MODE_SB8 GALAXY_COMMAND_SB8MODE
+
+struct snd_galaxy {
+ void __iomem *port;
+ void __iomem *config_port;
+ void __iomem *wss_port;
+ u32 config;
+ struct resource *res_port;
+ struct resource *res_config_port;
+ struct resource *res_wss_port;
+};
+
+static u32 config[SNDRV_CARDS];
+static u8 wss_config[SNDRV_CARDS];
+
+static int __devinit snd_galaxy_match(struct device *dev, unsigned int n)
+{
+ if (!enable[n])
+ return 0;
+
+ switch (port[n]) {
+ case SNDRV_AUTO_PORT:
+ dev_err(dev, "please specify port\n");
+ return 0;
+ case 0x220:
+ config[n] |= GALAXY_CONFIG_SBA_220;
+ break;
+ case 0x240:
+ config[n] |= GALAXY_CONFIG_SBA_240;
+ break;
+ case 0x260:
+ config[n] |= GALAXY_CONFIG_SBA_260;
+ break;
+ case 0x280:
+ config[n] |= GALAXY_CONFIG_SBA_280;
+ break;
+ default:
+ dev_err(dev, "invalid port %#lx\n", port[n]);
+ return 0;
+ }
+
+ switch (wss_port[n]) {
+ case SNDRV_AUTO_PORT:
+ dev_err(dev, "please specify wss_port\n");
+ return 0;
+ case 0x530:
+ config[n] |= GALAXY_CONFIG_WSS_ENABLE | GALAXY_CONFIG_WSSA_530;
+ break;
+ case 0x604:
+ config[n] |= GALAXY_CONFIG_WSS_ENABLE | GALAXY_CONFIG_WSSA_604;
+ break;
+ case 0xe80:
+ config[n] |= GALAXY_CONFIG_WSS_ENABLE | GALAXY_CONFIG_WSSA_E80;
+ break;
+ case 0xf40:
+ config[n] |= GALAXY_CONFIG_WSS_ENABLE | GALAXY_CONFIG_WSSA_F40;
+ break;
+ default:
+ dev_err(dev, "invalid WSS port %#lx\n", wss_port[n]);
+ return 0;
+ }
+
+ switch (irq[n]) {
+ case SNDRV_AUTO_IRQ:
+ dev_err(dev, "please specify irq\n");
+ return 0;
+ case 7:
+ wss_config[n] |= WSS_CONFIG_IRQ_7;
+ break;
+ case 2:
+ irq[n] = 9;
+ case 9:
+ wss_config[n] |= WSS_CONFIG_IRQ_9;
+ break;
+ case 10:
+ wss_config[n] |= WSS_CONFIG_IRQ_10;
+ break;
+ case 11:
+ wss_config[n] |= WSS_CONFIG_IRQ_11;
+ break;
+ default:
+ dev_err(dev, "invalid IRQ %d\n", irq[n]);
+ return 0;
+ }
+
+ switch (dma1[n]) {
+ case SNDRV_AUTO_DMA:
+ dev_err(dev, "please specify dma1\n");
+ return 0;
+ case 0:
+ wss_config[n] |= WSS_CONFIG_DMA_0;
+ break;
+ case 1:
+ wss_config[n] |= WSS_CONFIG_DMA_1;
+ break;
+ case 3:
+ wss_config[n] |= WSS_CONFIG_DMA_3;
+ break;
+ default:
+ dev_err(dev, "invalid playback DMA %d\n", dma1[n]);
+ return 0;
+ }
+
+ if (dma2[n] == SNDRV_AUTO_DMA || dma2[n] == dma1[n]) {
+ dma2[n] = -1;
+ goto mpu;
+ }
+
+ wss_config[n] |= WSS_CONFIG_DUPLEX;
+ switch (dma2[n]) {
+ case 0:
+ break;
+ case 1:
+ if (dma1[n] == 0)
+ break;
+ default:
+ dev_err(dev, "invalid capture DMA %d\n", dma2[n]);
+ return 0;
+ }
+
+mpu:
+ switch (mpu_port[n]) {
+ case SNDRV_AUTO_PORT:
+ dev_warn(dev, "mpu_port not specified; not using MPU-401\n");
+ mpu_port[n] = -1;
+ goto fm;
+ case 0x300:
+ config[n] |= GALAXY_CONFIG_MPU_ENABLE | GALAXY_CONFIG_MPUA_300;
+ break;
+ case 0x330:
+ config[n] |= GALAXY_CONFIG_MPU_ENABLE | GALAXY_CONFIG_MPUA_330;
+ break;
+ default:
+ dev_err(dev, "invalid MPU port %#lx\n", mpu_port[n]);
+ return 0;
+ }
+
+ switch (mpu_irq[n]) {
+ case SNDRV_AUTO_IRQ:
+ dev_warn(dev, "mpu_irq not specified: using polling mode\n");
+ mpu_irq[n] = -1;
+ break;
+ case 2:
+ mpu_irq[n] = 9;
+ case 9:
+ config[n] |= GALAXY_CONFIG_MPUIRQ_2;
+ break;
+#ifdef AZT1605
+ case 3:
+ config[n] |= GALAXY_CONFIG_MPUIRQ_3;
+ break;
+#endif
+ case 5:
+ config[n] |= GALAXY_CONFIG_MPUIRQ_5;
+ break;
+ case 7:
+ config[n] |= GALAXY_CONFIG_MPUIRQ_7;
+ break;
+#ifdef AZT2316
+ case 10:
+ config[n] |= GALAXY_CONFIG_MPUIRQ_10;
+ break;
+#endif
+ default:
+ dev_err(dev, "invalid MPU IRQ %d\n", mpu_irq[n]);
+ return 0;
+ }
+
+ if (mpu_irq[n] == irq[n]) {
+ dev_err(dev, "cannot share IRQ between WSS and MPU-401\n");
+ return 0;
+ }
+
+fm:
+ switch (fm_port[n]) {
+ case SNDRV_AUTO_PORT:
+ dev_warn(dev, "fm_port not specified: not using OPL3\n");
+ fm_port[n] = -1;
+ break;
+ case 0x388:
+ break;
+ default:
+ dev_err(dev, "illegal FM port %#lx\n", fm_port[n]);
+ return 0;
+ }
+
+ config[n] |= GALAXY_CONFIG_GAME_ENABLE;
+ return 1;
+}
+
+static int __devinit galaxy_init(struct snd_galaxy *galaxy, u8 *type)
+{
+ u8 major;
+ u8 minor;
+ int err;
+
+ err = dsp_reset(galaxy->port);
+ if (err < 0)
+ return err;
+
+ err = dsp_get_version(galaxy->port, &major, &minor);
+ if (err < 0)
+ return err;
+
+ if (major != GALAXY_DSP_MAJOR || minor != GALAXY_DSP_MINOR)
+ return -ENODEV;
+
+ err = dsp_command(galaxy->port, DSP_COMMAND_GALAXY_8);
+ if (err < 0)
+ return err;
+
+ err = dsp_command(galaxy->port, GALAXY_COMMAND_GET_TYPE);
+ if (err < 0)
+ return err;
+
+ err = dsp_get_byte(galaxy->port, type);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int __devinit galaxy_set_mode(struct snd_galaxy *galaxy, u8 mode)
+{
+ int err;
+
+ err = dsp_command(galaxy->port, DSP_COMMAND_GALAXY_9);
+ if (err < 0)
+ return err;
+
+ err = dsp_command(galaxy->port, mode);
+ if (err < 0)
+ return err;
+
+#ifdef AZT1605
+ /*
+ * Needed for MPU IRQ on AZT1605, but AZT2316 loses WSS again
+ */
+ err = dsp_reset(galaxy->port);
+ if (err < 0)
+ return err;
+#endif
+
+ return 0;
+}
+
+static void galaxy_set_config(struct snd_galaxy *galaxy, u32 config)
+{
+ u8 tmp = ioread8(galaxy->config_port + CONFIG_PORT_SET);
+ int i;
+
+ iowrite8(tmp | 0x80, galaxy->config_port + CONFIG_PORT_SET);
+ for (i = 0; i < GALAXY_CONFIG_SIZE; i++) {
+ iowrite8(config, galaxy->config_port + i);
+ config >>= 8;
+ }
+ iowrite8(tmp & 0x7f, galaxy->config_port + CONFIG_PORT_SET);
+ msleep(10);
+}
+
+static void __devinit galaxy_config(struct snd_galaxy *galaxy, u32 config)
+{
+ int i;
+
+ for (i = GALAXY_CONFIG_SIZE; i; i--) {
+ u8 tmp = ioread8(galaxy->config_port + i - 1);
+ galaxy->config = (galaxy->config << 8) | tmp;
+ }
+ config |= galaxy->config & GALAXY_CONFIG_MASK;
+ galaxy_set_config(galaxy, config);
+}
+
+static int __devinit galaxy_wss_config(struct snd_galaxy *galaxy, u8 wss_config)
+{
+ int err;
+
+ err = wss_detect(galaxy->wss_port);
+ if (err < 0)
+ return err;
+
+ wss_set_config(galaxy->wss_port, wss_config);
+
+ err = galaxy_set_mode(galaxy, GALAXY_MODE_WSS);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static void snd_galaxy_free(struct snd_card *card)
+{
+ struct snd_galaxy *galaxy = card->private_data;
+
+ if (galaxy->wss_port) {
+ wss_set_config(galaxy->wss_port, 0);
+ ioport_unmap(galaxy->wss_port);
+ release_and_free_resource(galaxy->res_wss_port);
+ }
+ if (galaxy->config_port) {
+ galaxy_set_config(galaxy, galaxy->config);
+ ioport_unmap(galaxy->config_port);
+ release_and_free_resource(galaxy->res_config_port);
+ }
+ if (galaxy->port) {
+ ioport_unmap(galaxy->port);
+ release_and_free_resource(galaxy->res_port);
+ }
+}
+
+static int __devinit snd_galaxy_probe(struct device *dev, unsigned int n)
+{
+ struct snd_galaxy *galaxy;
+ struct snd_wss *chip;
+ struct snd_card *card;
+ u8 type;
+ int err;
+
+ err = snd_card_create(index[n], id[n], THIS_MODULE, sizeof *galaxy,
+ &card);
+ if (err < 0)
+ return err;
+
+ snd_card_set_dev(card, dev);
+
+ card->private_free = snd_galaxy_free;
+ galaxy = card->private_data;
+
+ galaxy->res_port = request_region(port[n], 16, DRV_NAME);
+ if (!galaxy->res_port) {
+ dev_err(dev, "could not grab ports %#lx-%#lx\n", port[n],
+ port[n] + 15);
+ err = -EBUSY;
+ goto error;
+ }
+ galaxy->port = ioport_map(port[n], 16);
+
+ err = galaxy_init(galaxy, &type);
+ if (err < 0) {
+ dev_err(dev, "did not find a Sound Galaxy at %#lx\n", port[n]);
+ goto error;
+ }
+ dev_info(dev, "Sound Galaxy (type %d) found at %#lx\n", type, port[n]);
+
+ galaxy->res_config_port = request_region(port[n] + GALAXY_PORT_CONFIG,
+ 16, DRV_NAME);
+ if (!galaxy->res_config_port) {
+ dev_err(dev, "could not grab ports %#lx-%#lx\n",
+ port[n] + GALAXY_PORT_CONFIG,
+ port[n] + GALAXY_PORT_CONFIG + 15);
+ err = -EBUSY;
+ goto error;
+ }
+ galaxy->config_port = ioport_map(port[n] + GALAXY_PORT_CONFIG, 16);
+
+ galaxy_config(galaxy, config[n]);
+
+ galaxy->res_wss_port = request_region(wss_port[n], 4, DRV_NAME);
+ if (!galaxy->res_wss_port) {
+ dev_err(dev, "could not grab ports %#lx-%#lx\n", wss_port[n],
+ wss_port[n] + 3);
+ err = -EBUSY;
+ goto error;
+ }
+ galaxy->wss_port = ioport_map(wss_port[n], 4);
+
+ err = galaxy_wss_config(galaxy, wss_config[n]);
+ if (err < 0) {
+ dev_err(dev, "could not configure WSS\n");
+ goto error;
+ }
+
+ strcpy(card->driver, DRV_NAME);
+ strcpy(card->shortname, DRV_NAME);
+ sprintf(card->longname, "%s at %#lx/%#lx, irq %d, dma %d/%d",
+ card->shortname, port[n], wss_port[n], irq[n], dma1[n],
+ dma2[n]);
+
+ err = snd_wss_create(card, wss_port[n] + 4, -1, irq[n], dma1[n],
+ dma2[n], WSS_HW_DETECT, 0, &chip);
+ if (err < 0)
+ goto error;
+
+ err = snd_wss_pcm(chip, 0, NULL);
+ if (err < 0)
+ goto error;
+
+ err = snd_wss_mixer(chip);
+ if (err < 0)
+ goto error;
+
+ err = snd_wss_timer(chip, 0, NULL);
+ if (err < 0)
+ goto error;
+
+ if (mpu_port[n] >= 0) {
+ err = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
+ mpu_port[n], 0, mpu_irq[n],
+ IRQF_DISABLED, NULL);
+ if (err < 0)
+ goto error;
+ }
+
+ if (fm_port[n] >= 0) {
+ struct snd_opl3 *opl3;
+
+ err = snd_opl3_create(card, fm_port[n], fm_port[n] + 2,
+ OPL3_HW_AUTO, 0, &opl3);
+ if (err < 0) {
+ dev_err(dev, "no OPL device at %#lx\n", fm_port[n]);
+ goto error;
+ }
+ err = snd_opl3_timer_new(opl3, 1, 2);
+ if (err < 0)
+ goto error;
+
+ err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
+ if (err < 0)
+ goto error;
+ }
+
+ err = snd_card_register(card);
+ if (err < 0)
+ goto error;
+
+ dev_set_drvdata(dev, card);
+ return 0;
+
+error:
+ snd_card_free(card);
+ return err;
+}
+
+static int __devexit snd_galaxy_remove(struct device *dev, unsigned int n)
+{
+ snd_card_free(dev_get_drvdata(dev));
+ dev_set_drvdata(dev, NULL);
+ return 0;
+}
+
+static struct isa_driver snd_galaxy_driver = {
+ .match = snd_galaxy_match,
+ .probe = snd_galaxy_probe,
+ .remove = __devexit_p(snd_galaxy_remove),
+
+ .driver = {
+ .name = DEV_NAME
+ }
+};
+
+static int __init alsa_card_galaxy_init(void)
+{
+ return isa_register_driver(&snd_galaxy_driver, SNDRV_CARDS);
+}
+
+static void __exit alsa_card_galaxy_exit(void)
+{
+ isa_unregister_driver(&snd_galaxy_driver);
+}
+
+module_init(alsa_card_galaxy_init);
+module_exit(alsa_card_galaxy_exit);
diff --git a/sound/isa/gus/gusmax.c b/sound/isa/gus/gusmax.c
index f26eac8d811..3e4a58b7291 100644
--- a/sound/isa/gus/gusmax.c
+++ b/sound/isa/gus/gusmax.c
@@ -191,7 +191,7 @@ static int __devinit snd_gusmax_mixer(struct snd_wss *chip)
static void snd_gusmax_free(struct snd_card *card)
{
- struct snd_gusmax *maxcard = (struct snd_gusmax *)card->private_data;
+ struct snd_gusmax *maxcard = card->private_data;
if (maxcard == NULL)
return;
@@ -219,7 +219,7 @@ static int __devinit snd_gusmax_probe(struct device *pdev, unsigned int dev)
if (err < 0)
return err;
card->private_free = snd_gusmax_free;
- maxcard = (struct snd_gusmax *)card->private_data;
+ maxcard = card->private_data;
maxcard->card = card;
maxcard->irq = -1;
diff --git a/sound/isa/msnd/msnd_pinnacle.c b/sound/isa/msnd/msnd_pinnacle.c
index 5f3e68401f9..91d6023a63e 100644
--- a/sound/isa/msnd/msnd_pinnacle.c
+++ b/sound/isa/msnd/msnd_pinnacle.c
@@ -764,9 +764,9 @@ static long io[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
static long mem[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
+#ifndef MSND_CLASSIC
static long cfg[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
-#ifndef MSND_CLASSIC
/* Extra Peripheral Configuration (Default: Disable) */
static long ide_io0[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
static long ide_io1[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
@@ -894,7 +894,11 @@ static int __devinit snd_msnd_isa_probe(struct device *pdev, unsigned int idx)
struct snd_card *card;
struct snd_msnd *chip;
- if (has_isapnp(idx) || cfg[idx] == SNDRV_AUTO_PORT) {
+ if (has_isapnp(idx)
+#ifndef MSND_CLASSIC
+ || cfg[idx] == SNDRV_AUTO_PORT
+#endif
+ ) {
printk(KERN_INFO LOGNAME ": Assuming PnP mode\n");
return -ENODEV;
}
diff --git a/sound/isa/sb/sb8.c b/sound/isa/sb/sb8.c
index 81284a8fa0c..2259e3f726a 100644
--- a/sound/isa/sb/sb8.c
+++ b/sound/isa/sb/sb8.c
@@ -72,7 +72,7 @@ static irqreturn_t snd_sb8_interrupt(int irq, void *dev_id)
static void snd_sb8_free(struct snd_card *card)
{
- struct snd_sb8 *acard = (struct snd_sb8 *)card->private_data;
+ struct snd_sb8 *acard = card->private_data;
if (acard == NULL)
return;
diff --git a/sound/isa/sgalaxy.c b/sound/isa/sgalaxy.c
deleted file mode 100644
index 6fe27b9d944..00000000000
--- a/sound/isa/sgalaxy.c
+++ /dev/null
@@ -1,369 +0,0 @@
-/*
- * Driver for Aztech Sound Galaxy cards
- * Copyright (c) by Christopher Butler <chrisb@sandy.force9.co.uk.
- *
- * I don't have documentation for this card, I based this driver on the
- * driver for OSS/Free included in the kernel source (drivers/sound/sgalaxy.c)
- *
- * 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
- *
- */
-
-#include <linux/init.h>
-#include <linux/err.h>
-#include <linux/isa.h>
-#include <linux/delay.h>
-#include <linux/time.h>
-#include <linux/interrupt.h>
-#include <linux/moduleparam.h>
-#include <asm/dma.h>
-#include <sound/core.h>
-#include <sound/sb.h>
-#include <sound/wss.h>
-#include <sound/control.h>
-#define SNDRV_LEGACY_FIND_FREE_IRQ
-#define SNDRV_LEGACY_FIND_FREE_DMA
-#include <sound/initval.h>
-
-MODULE_AUTHOR("Christopher Butler <chrisb@sandy.force9.co.uk>");
-MODULE_DESCRIPTION("Aztech Sound Galaxy");
-MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Aztech Systems,Sound Galaxy}}");
-
-static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
-static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
-static long sbport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240 */
-static long wssport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x530,0xe80,0xf40,0x604 */
-static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 7,9,10,11 */
-static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3 */
-
-module_param_array(index, int, NULL, 0444);
-MODULE_PARM_DESC(index, "Index value for Sound Galaxy soundcard.");
-module_param_array(id, charp, NULL, 0444);
-MODULE_PARM_DESC(id, "ID string for Sound Galaxy soundcard.");
-module_param_array(sbport, long, NULL, 0444);
-MODULE_PARM_DESC(sbport, "Port # for Sound Galaxy SB driver.");
-module_param_array(wssport, long, NULL, 0444);
-MODULE_PARM_DESC(wssport, "Port # for Sound Galaxy WSS driver.");
-module_param_array(irq, int, NULL, 0444);
-MODULE_PARM_DESC(irq, "IRQ # for Sound Galaxy driver.");
-module_param_array(dma1, int, NULL, 0444);
-MODULE_PARM_DESC(dma1, "DMA1 # for Sound Galaxy driver.");
-
-#define SGALAXY_AUXC_LEFT 18
-#define SGALAXY_AUXC_RIGHT 19
-
-#define PFX "sgalaxy: "
-
-/*
-
- */
-
-#define AD1848P1( port, x ) ( port + c_d_c_AD1848##x )
-
-/* from lowlevel/sb/sb.c - to avoid having to allocate a struct snd_sb for the */
-/* short time we actually need it.. */
-
-static int snd_sgalaxy_sbdsp_reset(unsigned long port)
-{
- int i;
-
- outb(1, SBP1(port, RESET));
- udelay(10);
- outb(0, SBP1(port, RESET));
- udelay(30);
- for (i = 0; i < 1000 && !(inb(SBP1(port, DATA_AVAIL)) & 0x80); i++);
- if (inb(SBP1(port, READ)) != 0xaa) {
- snd_printd("sb_reset: failed at 0x%lx!!!\n", port);
- return -ENODEV;
- }
- return 0;
-}
-
-static int __devinit snd_sgalaxy_sbdsp_command(unsigned long port,
- unsigned char val)
-{
- int i;
-
- for (i = 10000; i; i--)
- if ((inb(SBP1(port, STATUS)) & 0x80) == 0) {
- outb(val, SBP1(port, COMMAND));
- return 1;
- }
-
- return 0;
-}
-
-static irqreturn_t snd_sgalaxy_dummy_interrupt(int irq, void *dev_id)
-{
- return IRQ_NONE;
-}
-
-static int __devinit snd_sgalaxy_setup_wss(unsigned long port, int irq, int dma)
-{
- static int interrupt_bits[] = {-1, -1, -1, -1, -1, -1, -1, 0x08, -1,
- 0x10, 0x18, 0x20, -1, -1, -1, -1};
- static int dma_bits[] = {1, 2, 0, 3};
- int tmp, tmp1;
-
- if ((tmp = inb(port + 3)) == 0xff)
- {
- snd_printdd("I/O address dead (0x%lx)\n", port);
- return 0;
- }
-#if 0
- snd_printdd("WSS signature = 0x%x\n", tmp);
-#endif
-
- if ((tmp & 0x3f) != 0x04 &&
- (tmp & 0x3f) != 0x0f &&
- (tmp & 0x3f) != 0x00) {
- snd_printdd("No WSS signature detected on port 0x%lx\n",
- port + 3);
- return 0;
- }
-
-#if 0
- snd_printdd(PFX "setting up IRQ/DMA for WSS\n");
-#endif
-
- /* initialize IRQ for WSS codec */
- tmp = interrupt_bits[irq % 16];
- if (tmp < 0)
- return -EINVAL;
-
- if (request_irq(irq, snd_sgalaxy_dummy_interrupt, IRQF_DISABLED, "sgalaxy", NULL)) {
- snd_printk(KERN_ERR "sgalaxy: can't grab irq %d\n", irq);
- return -EIO;
- }
-
- outb(tmp | 0x40, port);
- tmp1 = dma_bits[dma % 4];
- outb(tmp | tmp1, port);
-
- free_irq(irq, NULL);
-
- return 0;
-}
-
-static int __devinit snd_sgalaxy_detect(int dev, int irq, int dma)
-{
-#if 0
- snd_printdd(PFX "switching to WSS mode\n");
-#endif
-
- /* switch to WSS mode */
- snd_sgalaxy_sbdsp_reset(sbport[dev]);
-
- snd_sgalaxy_sbdsp_command(sbport[dev], 9);
- snd_sgalaxy_sbdsp_command(sbport[dev], 0);
-
- udelay(400);
- return snd_sgalaxy_setup_wss(wssport[dev], irq, dma);
-}
-
-static struct snd_kcontrol_new snd_sgalaxy_controls[] = {
-WSS_DOUBLE("Aux Playback Switch", 0,
- SGALAXY_AUXC_LEFT, SGALAXY_AUXC_RIGHT, 7, 7, 1, 1),
-WSS_DOUBLE("Aux Playback Volume", 0,
- SGALAXY_AUXC_LEFT, SGALAXY_AUXC_RIGHT, 0, 0, 31, 0)
-};
-
-static int __devinit snd_sgalaxy_mixer(struct snd_wss *chip)
-{
- struct snd_card *card = chip->card;
- struct snd_ctl_elem_id id1, id2;
- unsigned int idx;
- int err;
-
- memset(&id1, 0, sizeof(id1));
- memset(&id2, 0, sizeof(id2));
- id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- /* reassign AUX0 to LINE */
- strcpy(id1.name, "Aux Playback Switch");
- strcpy(id2.name, "Line Playback Switch");
- if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
- return err;
- strcpy(id1.name, "Aux Playback Volume");
- strcpy(id2.name, "Line Playback Volume");
- if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
- return err;
- /* reassign AUX1 to FM */
- strcpy(id1.name, "Aux Playback Switch"); id1.index = 1;
- strcpy(id2.name, "FM Playback Switch");
- if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
- return err;
- strcpy(id1.name, "Aux Playback Volume");
- strcpy(id2.name, "FM Playback Volume");
- if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
- return err;
- /* build AUX2 input */
- for (idx = 0; idx < ARRAY_SIZE(snd_sgalaxy_controls); idx++) {
- err = snd_ctl_add(card,
- snd_ctl_new1(&snd_sgalaxy_controls[idx], chip));
- if (err < 0)
- return err;
- }
- return 0;
-}
-
-static int __devinit snd_sgalaxy_match(struct device *devptr, unsigned int dev)
-{
- if (!enable[dev])
- return 0;
- if (sbport[dev] == SNDRV_AUTO_PORT) {
- snd_printk(KERN_ERR PFX "specify SB port\n");
- return 0;
- }
- if (wssport[dev] == SNDRV_AUTO_PORT) {
- snd_printk(KERN_ERR PFX "specify WSS port\n");
- return 0;
- }
- return 1;
-}
-
-static int __devinit snd_sgalaxy_probe(struct device *devptr, unsigned int dev)
-{
- static int possible_irqs[] = {7, 9, 10, 11, -1};
- static int possible_dmas[] = {1, 3, 0, -1};
- int err, xirq, xdma1;
- struct snd_card *card;
- struct snd_wss *chip;
-
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
- if (err < 0)
- return err;
-
- xirq = irq[dev];
- if (xirq == SNDRV_AUTO_IRQ) {
- if ((xirq = snd_legacy_find_free_irq(possible_irqs)) < 0) {
- snd_printk(KERN_ERR PFX "unable to find a free IRQ\n");
- err = -EBUSY;
- goto _err;
- }
- }
- xdma1 = dma1[dev];
- if (xdma1 == SNDRV_AUTO_DMA) {
- if ((xdma1 = snd_legacy_find_free_dma(possible_dmas)) < 0) {
- snd_printk(KERN_ERR PFX "unable to find a free DMA\n");
- err = -EBUSY;
- goto _err;
- }
- }
-
- if ((err = snd_sgalaxy_detect(dev, xirq, xdma1)) < 0)
- goto _err;
-
- err = snd_wss_create(card, wssport[dev] + 4, -1,
- xirq, xdma1, -1,
- WSS_HW_DETECT, 0, &chip);
- if (err < 0)
- goto _err;
- card->private_data = chip;
-
- err = snd_wss_pcm(chip, 0, NULL);
- if (err < 0) {
- snd_printdd(PFX "error creating new WSS PCM device\n");
- goto _err;
- }
- err = snd_wss_mixer(chip);
- if (err < 0) {
- snd_printdd(PFX "error creating new WSS mixer\n");
- goto _err;
- }
- if ((err = snd_sgalaxy_mixer(chip)) < 0) {
- snd_printdd(PFX "the mixer rewrite failed\n");
- goto _err;
- }
-
- strcpy(card->driver, "Sound Galaxy");
- strcpy(card->shortname, "Sound Galaxy");
- sprintf(card->longname, "Sound Galaxy at 0x%lx, irq %d, dma %d",
- wssport[dev], xirq, xdma1);
-
- snd_card_set_dev(card, devptr);
-
- if ((err = snd_card_register(card)) < 0)
- goto _err;
-
- dev_set_drvdata(devptr, card);
- return 0;
-
- _err:
- snd_card_free(card);
- return err;
-}
-
-static int __devexit snd_sgalaxy_remove(struct device *devptr, unsigned int dev)
-{
- snd_card_free(dev_get_drvdata(devptr));
- dev_set_drvdata(devptr, NULL);
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int snd_sgalaxy_suspend(struct device *pdev, unsigned int n,
- pm_message_t state)
-{
- struct snd_card *card = dev_get_drvdata(pdev);
- struct snd_wss *chip = card->private_data;
-
- snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- chip->suspend(chip);
- return 0;
-}
-
-static int snd_sgalaxy_resume(struct device *pdev, unsigned int n)
-{
- struct snd_card *card = dev_get_drvdata(pdev);
- struct snd_wss *chip = card->private_data;
-
- chip->resume(chip);
- snd_wss_out(chip, SGALAXY_AUXC_LEFT, chip->image[SGALAXY_AUXC_LEFT]);
- snd_wss_out(chip, SGALAXY_AUXC_RIGHT, chip->image[SGALAXY_AUXC_RIGHT]);
-
- snd_power_change_state(card, SNDRV_CTL_POWER_D0);
- return 0;
-}
-#endif
-
-#define DEV_NAME "sgalaxy"
-
-static struct isa_driver snd_sgalaxy_driver = {
- .match = snd_sgalaxy_match,
- .probe = snd_sgalaxy_probe,
- .remove = __devexit_p(snd_sgalaxy_remove),
-#ifdef CONFIG_PM
- .suspend = snd_sgalaxy_suspend,
- .resume = snd_sgalaxy_resume,
-#endif
- .driver = {
- .name = DEV_NAME
- },
-};
-
-static int __init alsa_card_sgalaxy_init(void)
-{
- return isa_register_driver(&snd_sgalaxy_driver, SNDRV_CARDS);
-}
-
-static void __exit alsa_card_sgalaxy_exit(void)
-{
- isa_unregister_driver(&snd_sgalaxy_driver);
-}
-
-module_init(alsa_card_sgalaxy_init)
-module_exit(alsa_card_sgalaxy_exit)
diff --git a/sound/oss/Kconfig b/sound/oss/Kconfig
index a513651fa14..76c09021807 100644
--- a/sound/oss/Kconfig
+++ b/sound/oss/Kconfig
@@ -545,11 +545,3 @@ config SOUND_KAHLUA
endif # SOUND_OSS
-config SOUND_SH_DAC_AUDIO
- tristate "SuperH DAC audio support"
- depends on CPU_SH3 && HIGH_RES_TIMERS
-
-config SOUND_SH_DAC_AUDIO_CHANNEL
- int "DAC channel"
- default "1"
- depends on SOUND_SH_DAC_AUDIO
diff --git a/sound/oss/Makefile b/sound/oss/Makefile
index 567b8a74178..96f14dcd0cd 100644
--- a/sound/oss/Makefile
+++ b/sound/oss/Makefile
@@ -9,7 +9,6 @@ obj-$(CONFIG_SOUND_OSS) += sound.o
# Please leave it as is, cause the link order is significant !
-obj-$(CONFIG_SOUND_SH_DAC_AUDIO) += sh_dac_audio.o
obj-$(CONFIG_SOUND_AEDSP16) += aedsp16.o
obj-$(CONFIG_SOUND_PSS) += pss.o ad1848.o mpu401.o
obj-$(CONFIG_SOUND_TRIX) += trix.o ad1848.o sb_lib.o uart401.o
diff --git a/sound/oss/ac97_codec.c b/sound/oss/ac97_codec.c
index 456a1b4d783..854c303264d 100644
--- a/sound/oss/ac97_codec.c
+++ b/sound/oss/ac97_codec.c
@@ -21,11 +21,8 @@
*
**************************************************************************
*
- * The Intel Audio Codec '97 specification is available at the Intel
- * audio homepage: http://developer.intel.com/ial/scalableplatforms/audio/
- *
- * The specification itself is currently available at:
- * ftp://download.intel.com/ial/scalableplatforms/ac97r22.pdf
+ * The Intel Audio Codec '97 specification is available at:
+ * http://download.intel.com/support/motherboards/desktop/sb/ac97_r23.pdf
*
**************************************************************************
*
diff --git a/sound/oss/au1550_ac97.c b/sound/oss/au1550_ac97.c
index c6f2621221b..a8f626d99c5 100644
--- a/sound/oss/au1550_ac97.c
+++ b/sound/oss/au1550_ac97.c
@@ -43,7 +43,6 @@
#include <linux/sound.h>
#include <linux/slab.h>
#include <linux/soundcard.h>
-#include <linux/smp_lock.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
@@ -77,6 +76,7 @@
/* Boot options
* 0 = no VRA, 1 = use VRA if codec supports it
*/
+static DEFINE_MUTEX(au1550_ac97_mutex);
static int vra = 1;
module_param(vra, bool, 0);
MODULE_PARM_DESC(vra, "if 1 use VRA if codec supports it");
@@ -171,7 +171,7 @@ au1550_delay(int msec)
static u16
rdcodec(struct ac97_codec *codec, u8 addr)
{
- struct au1550_state *s = (struct au1550_state *)codec->private_data;
+ struct au1550_state *s = codec->private_data;
unsigned long flags;
u32 cmd, val;
u16 data;
@@ -239,7 +239,7 @@ rdcodec(struct ac97_codec *codec, u8 addr)
static void
wrcodec(struct ac97_codec *codec, u8 addr, u16 data)
{
- struct au1550_state *s = (struct au1550_state *)codec->private_data;
+ struct au1550_state *s = codec->private_data;
unsigned long flags;
u32 cmd, val;
int i;
@@ -798,9 +798,9 @@ au1550_llseek(struct file *file, loff_t offset, int origin)
static int
au1550_open_mixdev(struct inode *inode, struct file *file)
{
- lock_kernel();
+ mutex_lock(&au1550_ac97_mutex);
file->private_data = &au1550_state;
- unlock_kernel();
+ mutex_unlock(&au1550_ac97_mutex);
return 0;
}
@@ -820,13 +820,13 @@ mixdev_ioctl(struct ac97_codec *codec, unsigned int cmd,
static long
au1550_ioctl_mixdev(struct file *file, unsigned int cmd, unsigned long arg)
{
- struct au1550_state *s = (struct au1550_state *)file->private_data;
+ struct au1550_state *s = file->private_data;
struct ac97_codec *codec = s->codec;
int ret;
- lock_kernel();
+ mutex_lock(&au1550_ac97_mutex);
ret = mixdev_ioctl(codec, cmd, arg);
- unlock_kernel();
+ mutex_unlock(&au1550_ac97_mutex);
return ret;
}
@@ -1031,7 +1031,7 @@ copy_dmabuf_user(struct dmabuf *db, char* userbuf, int count, int to_user)
static ssize_t
au1550_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
{
- struct au1550_state *s = (struct au1550_state *)file->private_data;
+ struct au1550_state *s = file->private_data;
struct dmabuf *db = &s->dma_adc;
DECLARE_WAITQUEUE(wait, current);
ssize_t ret;
@@ -1111,7 +1111,7 @@ out2:
static ssize_t
au1550_write(struct file *file, const char *buffer, size_t count, loff_t * ppos)
{
- struct au1550_state *s = (struct au1550_state *)file->private_data;
+ struct au1550_state *s = file->private_data;
struct dmabuf *db = &s->dma_dac;
DECLARE_WAITQUEUE(wait, current);
ssize_t ret = 0;
@@ -1211,7 +1211,7 @@ out2:
static unsigned int
au1550_poll(struct file *file, struct poll_table_struct *wait)
{
- struct au1550_state *s = (struct au1550_state *)file->private_data;
+ struct au1550_state *s = file->private_data;
unsigned long flags;
unsigned int mask = 0;
@@ -1250,12 +1250,12 @@ au1550_poll(struct file *file, struct poll_table_struct *wait)
static int
au1550_mmap(struct file *file, struct vm_area_struct *vma)
{
- struct au1550_state *s = (struct au1550_state *)file->private_data;
+ struct au1550_state *s = file->private_data;
struct dmabuf *db;
unsigned long size;
int ret = 0;
- lock_kernel();
+ mutex_lock(&au1550_ac97_mutex);
mutex_lock(&s->sem);
if (vma->vm_flags & VM_WRITE)
db = &s->dma_dac;
@@ -1283,7 +1283,7 @@ au1550_mmap(struct file *file, struct vm_area_struct *vma)
db->mapped = 1;
out:
mutex_unlock(&s->sem);
- unlock_kernel();
+ mutex_unlock(&au1550_ac97_mutex);
return ret;
}
@@ -1342,7 +1342,7 @@ dma_count_done(struct dmabuf *db)
static int
au1550_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
- struct au1550_state *s = (struct au1550_state *)file->private_data;
+ struct au1550_state *s = file->private_data;
unsigned long flags;
audio_buf_info abinfo;
count_info cinfo;
@@ -1781,9 +1781,9 @@ au1550_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int ret;
- lock_kernel();
+ mutex_lock(&au1550_ac97_mutex);
ret = au1550_ioctl(file, cmd, arg);
- unlock_kernel();
+ mutex_unlock(&au1550_ac97_mutex);
return ret;
}
@@ -1804,7 +1804,7 @@ au1550_open(struct inode *inode, struct file *file)
#endif
file->private_data = s;
- lock_kernel();
+ mutex_lock(&au1550_ac97_mutex);
/* wait for device to become free */
mutex_lock(&s->open_mutex);
while (s->open_mode & file->f_mode) {
@@ -1861,21 +1861,21 @@ au1550_open(struct inode *inode, struct file *file)
out:
mutex_unlock(&s->open_mutex);
out2:
- unlock_kernel();
+ mutex_unlock(&au1550_ac97_mutex);
return ret;
}
static int
au1550_release(struct inode *inode, struct file *file)
{
- struct au1550_state *s = (struct au1550_state *)file->private_data;
+ struct au1550_state *s = file->private_data;
- lock_kernel();
+ mutex_lock(&au1550_ac97_mutex);
if (file->f_mode & FMODE_WRITE) {
- unlock_kernel();
+ mutex_unlock(&au1550_ac97_mutex);
drain_dac(s, file->f_flags & O_NONBLOCK);
- lock_kernel();
+ mutex_lock(&au1550_ac97_mutex);
}
mutex_lock(&s->open_mutex);
@@ -1892,7 +1892,7 @@ au1550_release(struct inode *inode, struct file *file)
s->open_mode &= ((~file->f_mode) & (FMODE_READ|FMODE_WRITE));
mutex_unlock(&s->open_mutex);
wake_up(&s->open_wait);
- unlock_kernel();
+ mutex_unlock(&au1550_ac97_mutex);
return 0;
}
diff --git a/sound/oss/dmasound/dmasound_core.c b/sound/oss/dmasound/dmasound_core.c
index 6ecd41abb06..87e2c72651f 100644
--- a/sound/oss/dmasound/dmasound_core.c
+++ b/sound/oss/dmasound/dmasound_core.c
@@ -181,7 +181,7 @@
#include <linux/init.h>
#include <linux/soundcard.h>
#include <linux/poll.h>
-#include <linux/smp_lock.h>
+#include <linux/mutex.h>
#include <asm/uaccess.h>
@@ -194,6 +194,7 @@
* Declarations
*/
+static DEFINE_MUTEX(dmasound_core_mutex);
int dmasound_catchRadius = 0;
module_param(dmasound_catchRadius, int, 0);
@@ -323,22 +324,22 @@ static struct {
static int mixer_open(struct inode *inode, struct file *file)
{
- lock_kernel();
+ mutex_lock(&dmasound_core_mutex);
if (!try_module_get(dmasound.mach.owner)) {
- unlock_kernel();
+ mutex_unlock(&dmasound_core_mutex);
return -ENODEV;
}
mixer.busy = 1;
- unlock_kernel();
+ mutex_unlock(&dmasound_core_mutex);
return 0;
}
static int mixer_release(struct inode *inode, struct file *file)
{
- lock_kernel();
+ mutex_lock(&dmasound_core_mutex);
mixer.busy = 0;
module_put(dmasound.mach.owner);
- unlock_kernel();
+ mutex_unlock(&dmasound_core_mutex);
return 0;
}
@@ -370,9 +371,9 @@ static long mixer_unlocked_ioctl(struct file *file, u_int cmd, u_long arg)
{
int ret;
- lock_kernel();
+ mutex_lock(&dmasound_core_mutex);
ret = mixer_ioctl(file, cmd, arg);
- unlock_kernel();
+ mutex_unlock(&dmasound_core_mutex);
return ret;
}
@@ -752,9 +753,9 @@ static int sq_open(struct inode *inode, struct file *file)
{
int rc;
- lock_kernel();
+ mutex_lock(&dmasound_core_mutex);
if (!try_module_get(dmasound.mach.owner)) {
- unlock_kernel();
+ mutex_unlock(&dmasound_core_mutex);
return -ENODEV;
}
@@ -799,11 +800,11 @@ static int sq_open(struct inode *inode, struct file *file)
sound_set_format(AFMT_MU_LAW);
}
#endif
- unlock_kernel();
+ mutex_unlock(&dmasound_core_mutex);
return 0;
out:
module_put(dmasound.mach.owner);
- unlock_kernel();
+ mutex_unlock(&dmasound_core_mutex);
return rc;
}
@@ -869,7 +870,7 @@ static int sq_release(struct inode *inode, struct file *file)
{
int rc = 0;
- lock_kernel();
+ mutex_lock(&dmasound_core_mutex);
if (file->f_mode & FMODE_WRITE) {
if (write_sq.busy)
@@ -900,7 +901,7 @@ static int sq_release(struct inode *inode, struct file *file)
write_sq_wake_up(file); /* checks f_mode */
#endif /* blocking open() */
- unlock_kernel();
+ mutex_unlock(&dmasound_core_mutex);
return rc;
}
@@ -1141,9 +1142,9 @@ static long sq_unlocked_ioctl(struct file *file, u_int cmd, u_long arg)
{
int ret;
- lock_kernel();
+ mutex_lock(&dmasound_core_mutex);
ret = sq_ioctl(file, cmd, arg);
- unlock_kernel();
+ mutex_unlock(&dmasound_core_mutex);
return ret;
}
@@ -1257,7 +1258,7 @@ static int state_open(struct inode *inode, struct file *file)
int len = 0;
int ret;
- lock_kernel();
+ mutex_lock(&dmasound_core_mutex);
ret = -EBUSY;
if (state.busy)
goto out;
@@ -1329,16 +1330,16 @@ printk("dmasound: stat buffer used %d bytes\n", len) ;
state.len = len;
ret = 0;
out:
- unlock_kernel();
+ mutex_unlock(&dmasound_core_mutex);
return ret;
}
static int state_release(struct inode *inode, struct file *file)
{
- lock_kernel();
+ mutex_lock(&dmasound_core_mutex);
state.busy = 0;
module_put(dmasound.mach.owner);
- unlock_kernel();
+ mutex_unlock(&dmasound_core_mutex);
return 0;
}
diff --git a/sound/oss/msnd_pinnacle.c b/sound/oss/msnd_pinnacle.c
index 2e48b17667d..7b5c77b32a9 100644
--- a/sound/oss/msnd_pinnacle.c
+++ b/sound/oss/msnd_pinnacle.c
@@ -39,7 +39,7 @@
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
-#include <linux/smp_lock.h>
+#include <linux/mutex.h>
#include <linux/gfp.h>
#include <asm/irq.h>
#include <asm/io.h>
@@ -79,6 +79,7 @@
dev.rec_sample_rate / \
dev.rec_channels)
+static DEFINE_MUTEX(msnd_pinnacle_mutex);
static multisound_dev_t dev;
#ifndef HAVE_DSPCODEH
@@ -651,12 +652,12 @@ static long dev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
ret = -EINVAL;
- lock_kernel();
+ mutex_lock(&msnd_pinnacle_mutex);
if (minor == dev.dsp_minor)
ret = dsp_ioctl(file, cmd, arg);
else if (minor == dev.mixer_minor)
ret = mixer_ioctl(cmd, arg);
- unlock_kernel();
+ mutex_unlock(&msnd_pinnacle_mutex);
return ret;
}
@@ -761,7 +762,7 @@ static int dev_open(struct inode *inode, struct file *file)
int minor = iminor(inode);
int err = 0;
- lock_kernel();
+ mutex_lock(&msnd_pinnacle_mutex);
if (minor == dev.dsp_minor) {
if ((file->f_mode & FMODE_WRITE &&
test_bit(F_AUDIO_WRITE_INUSE, &dev.flags)) ||
@@ -791,7 +792,7 @@ static int dev_open(struct inode *inode, struct file *file)
} else
err = -EINVAL;
out:
- unlock_kernel();
+ mutex_unlock(&msnd_pinnacle_mutex);
return err;
}
@@ -800,14 +801,14 @@ static int dev_release(struct inode *inode, struct file *file)
int minor = iminor(inode);
int err = 0;
- lock_kernel();
+ mutex_lock(&msnd_pinnacle_mutex);
if (minor == dev.dsp_minor)
err = dsp_release(file);
else if (minor == dev.mixer_minor) {
/* nothing */
} else
err = -EINVAL;
- unlock_kernel();
+ mutex_unlock(&msnd_pinnacle_mutex);
return err;
}
@@ -1117,6 +1118,7 @@ static const struct file_operations dev_fileops = {
.unlocked_ioctl = dev_ioctl,
.open = dev_open,
.release = dev_release,
+ .llseek = noop_llseek,
};
static int reset_dsp(void)
diff --git a/sound/oss/sb_ess.c b/sound/oss/sb_ess.c
index 51a3d381a59..9890cf2066f 100644
--- a/sound/oss/sb_ess.c
+++ b/sound/oss/sb_ess.c
@@ -1722,7 +1722,6 @@ printk (KERN_INFO "FKS: es_rec_set_recmask mask = %x\n", mask);
right = (value & 0x0000ff00) >> 8;
} else { /* Turn it off (3) */
left = 0;
- left = 0;
right = 0;
}
sb_common_mixer_set(devc, i + ES_REC_MIXER_RECDIFF, left, right);
diff --git a/sound/oss/sh_dac_audio.c b/sound/oss/sh_dac_audio.c
deleted file mode 100644
index 479e3025a8a..00000000000
--- a/sound/oss/sh_dac_audio.c
+++ /dev/null
@@ -1,325 +0,0 @@
-/*
- * sound/oss/sh_dac_audio.c
- *
- * SH DAC based sound :(
- *
- * Copyright (C) 2004,2005 Andriy Skulysh
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- */
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/sched.h>
-#include <linux/linkage.h>
-#include <linux/slab.h>
-#include <linux/fs.h>
-#include <linux/sound.h>
-#include <linux/smp_lock.h>
-#include <linux/soundcard.h>
-#include <linux/interrupt.h>
-#include <linux/hrtimer.h>
-#include <asm/io.h>
-#include <asm/uaccess.h>
-#include <asm/irq.h>
-#include <asm/delay.h>
-#include <asm/clock.h>
-#include <cpu/dac.h>
-#include <asm/machvec.h>
-#include <mach/hp6xx.h>
-#include <asm/hd64461.h>
-
-#define MODNAME "sh_dac_audio"
-
-#define BUFFER_SIZE 48000
-
-static int rate;
-static int empty;
-static char *data_buffer, *buffer_begin, *buffer_end;
-static int in_use, device_major;
-static struct hrtimer hrtimer;
-static ktime_t wakeups_per_second;
-
-static void dac_audio_start_timer(void)
-{
- hrtimer_start(&hrtimer, wakeups_per_second, HRTIMER_MODE_REL);
-}
-
-static void dac_audio_stop_timer(void)
-{
- hrtimer_cancel(&hrtimer);
-}
-
-static void dac_audio_reset(void)
-{
- dac_audio_stop_timer();
- buffer_begin = buffer_end = data_buffer;
- empty = 1;
-}
-
-static void dac_audio_sync(void)
-{
- while (!empty)
- schedule();
-}
-
-static void dac_audio_start(void)
-{
- if (mach_is_hp6xx()) {
- u16 v = __raw_readw(HD64461_GPADR);
- v &= ~HD64461_GPADR_SPEAKER;
- __raw_writew(v, HD64461_GPADR);
- }
-
- sh_dac_enable(CONFIG_SOUND_SH_DAC_AUDIO_CHANNEL);
-}
-static void dac_audio_stop(void)
-{
- dac_audio_stop_timer();
-
- if (mach_is_hp6xx()) {
- u16 v = __raw_readw(HD64461_GPADR);
- v |= HD64461_GPADR_SPEAKER;
- __raw_writew(v, HD64461_GPADR);
- }
-
- sh_dac_output(0, CONFIG_SOUND_SH_DAC_AUDIO_CHANNEL);
- sh_dac_disable(CONFIG_SOUND_SH_DAC_AUDIO_CHANNEL);
-}
-
-static void dac_audio_set_rate(void)
-{
- wakeups_per_second = ktime_set(0, 1000000000 / rate);
-}
-
-static int dac_audio_ioctl(struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- int val;
-
- switch (cmd) {
- case OSS_GETVERSION:
- return put_user(SOUND_VERSION, (int *)arg);
-
- case SNDCTL_DSP_SYNC:
- dac_audio_sync();
- return 0;
-
- case SNDCTL_DSP_RESET:
- dac_audio_reset();
- return 0;
-
- case SNDCTL_DSP_GETFMTS:
- return put_user(AFMT_U8, (int *)arg);
-
- case SNDCTL_DSP_SETFMT:
- return put_user(AFMT_U8, (int *)arg);
-
- case SNDCTL_DSP_NONBLOCK:
- spin_lock(&file->f_lock);
- file->f_flags |= O_NONBLOCK;
- spin_unlock(&file->f_lock);
- return 0;
-
- case SNDCTL_DSP_GETCAPS:
- return 0;
-
- case SOUND_PCM_WRITE_RATE:
- val = *(int *)arg;
- if (val > 0) {
- rate = val;
- dac_audio_set_rate();
- }
- return put_user(rate, (int *)arg);
-
- case SNDCTL_DSP_STEREO:
- return put_user(0, (int *)arg);
-
- case SOUND_PCM_WRITE_CHANNELS:
- return put_user(1, (int *)arg);
-
- case SNDCTL_DSP_SETDUPLEX:
- return -EINVAL;
-
- case SNDCTL_DSP_PROFILE:
- return -EINVAL;
-
- case SNDCTL_DSP_GETBLKSIZE:
- return put_user(BUFFER_SIZE, (int *)arg);
-
- case SNDCTL_DSP_SETFRAGMENT:
- return 0;
-
- default:
- printk(KERN_ERR "sh_dac_audio: unimplemented ioctl=0x%x\n",
- cmd);
- return -EINVAL;
- }
- return -EINVAL;
-}
-
-static long dac_audio_unlocked_ioctl(struct file *file, u_int cmd, u_long arg)
-{
- int ret;
-
- lock_kernel();
- ret = dac_audio_ioctl(file, cmd, arg);
- unlock_kernel();
-
- return ret;
-}
-
-static ssize_t dac_audio_write(struct file *file, const char *buf, size_t count,
- loff_t * ppos)
-{
- int free;
- int nbytes;
-
- if (!count) {
- dac_audio_sync();
- return 0;
- }
-
- free = buffer_begin - buffer_end;
-
- if (free < 0)
- free += BUFFER_SIZE;
- if ((free == 0) && (empty))
- free = BUFFER_SIZE;
- if (count > free)
- count = free;
- if (buffer_begin > buffer_end) {
- if (copy_from_user((void *)buffer_end, buf, count))
- return -EFAULT;
-
- buffer_end += count;
- } else {
- nbytes = data_buffer + BUFFER_SIZE - buffer_end;
- if (nbytes > count) {
- if (copy_from_user((void *)buffer_end, buf, count))
- return -EFAULT;
- buffer_end += count;
- } else {
- if (copy_from_user((void *)buffer_end, buf, nbytes))
- return -EFAULT;
- if (copy_from_user
- ((void *)data_buffer, buf + nbytes, count - nbytes))
- return -EFAULT;
- buffer_end = data_buffer + count - nbytes;
- }
- }
-
- if (empty) {
- empty = 0;
- dac_audio_start_timer();
- }
-
- return count;
-}
-
-static ssize_t dac_audio_read(struct file *file, char *buf, size_t count,
- loff_t * ppos)
-{
- return -EINVAL;
-}
-
-static int dac_audio_open(struct inode *inode, struct file *file)
-{
- if (file->f_mode & FMODE_READ)
- return -ENODEV;
-
- lock_kernel();
- if (in_use) {
- unlock_kernel();
- return -EBUSY;
- }
-
- in_use = 1;
-
- dac_audio_start();
- unlock_kernel();
- return 0;
-}
-
-static int dac_audio_release(struct inode *inode, struct file *file)
-{
- dac_audio_sync();
- dac_audio_stop();
- in_use = 0;
-
- return 0;
-}
-
-const struct file_operations dac_audio_fops = {
- .read = dac_audio_read,
- .write = dac_audio_write,
- .unlocked_ioctl = dac_audio_unlocked_ioctl,
- .open = dac_audio_open,
- .release = dac_audio_release,
-};
-
-static enum hrtimer_restart sh_dac_audio_timer(struct hrtimer *handle)
-{
- if (!empty) {
- sh_dac_output(*buffer_begin, CONFIG_SOUND_SH_DAC_AUDIO_CHANNEL);
- buffer_begin++;
-
- if (buffer_begin == data_buffer + BUFFER_SIZE)
- buffer_begin = data_buffer;
- if (buffer_begin == buffer_end)
- empty = 1;
- }
-
- if (!empty)
- hrtimer_start(&hrtimer, wakeups_per_second, HRTIMER_MODE_REL);
-
- return HRTIMER_NORESTART;
-}
-
-static int __init dac_audio_init(void)
-{
- if ((device_major = register_sound_dsp(&dac_audio_fops, -1)) < 0) {
- printk(KERN_ERR "Cannot register dsp device");
- return device_major;
- }
-
- in_use = 0;
-
- data_buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL);
- if (data_buffer == NULL)
- return -ENOMEM;
-
- dac_audio_reset();
- rate = 8000;
- dac_audio_set_rate();
-
- /* Today: High Resolution Timer driven DAC playback.
- * The timer callback gets called once per sample. Ouch.
- *
- * Future: A much better approach would be to use the
- * SH7720 CMT+DMAC+DAC hardware combination like this:
- * - Program sample rate using CMT0 or CMT1
- * - Program DMAC to use CMT for timing and output to DAC
- * - Play sound using DMAC, let CPU sleep.
- * - While at it, rewrite this driver to use ALSA.
- */
-
- hrtimer_init(&hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- hrtimer.function = sh_dac_audio_timer;
-
- return 0;
-}
-
-static void __exit dac_audio_exit(void)
-{
- unregister_sound_dsp(device_major);
- kfree((void *)data_buffer);
-}
-
-module_init(dac_audio_init);
-module_exit(dac_audio_exit);
-
-MODULE_AUTHOR("Andriy Skulysh, askulysh@image.kiev.ua");
-MODULE_DESCRIPTION("SH DAC sound driver");
-MODULE_LICENSE("GPL");
diff --git a/sound/oss/sound_timer.c b/sound/oss/sound_timer.c
index f0f0c19fbff..48cda6c4c25 100644
--- a/sound/oss/sound_timer.c
+++ b/sound/oss/sound_timer.c
@@ -26,7 +26,7 @@ static unsigned long prev_event_time;
static volatile unsigned long usecs_per_tmr; /* Length of the current interval */
static struct sound_lowlev_timer *tmr;
-static spinlock_t lock;
+static DEFINE_SPINLOCK(lock);
static unsigned long tmr2ticks(int tmr_value)
{
diff --git a/sound/oss/soundcard.c b/sound/oss/soundcard.c
index 92aa762ffb7..46c0d03dbec 100644
--- a/sound/oss/soundcard.c
+++ b/sound/oss/soundcard.c
@@ -40,7 +40,7 @@
#include <linux/major.h>
#include <linux/delay.h>
#include <linux/proc_fs.h>
-#include <linux/smp_lock.h>
+#include <linux/mutex.h>
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/device.h>
@@ -56,6 +56,7 @@
* Table for permanently allocated memory (used when unloading the module)
*/
void * sound_mem_blocks[MAX_MEM_BLOCKS];
+static DEFINE_MUTEX(soundcard_mutex);
int sound_nblocks = 0;
/* Persistent DMA buffers */
@@ -151,7 +152,7 @@ static ssize_t sound_read(struct file *file, char __user *buf, size_t count, lof
* big one anyway, we might as well bandage here..
*/
- lock_kernel();
+ mutex_lock(&soundcard_mutex);
DEB(printk("sound_read(dev=%d, count=%d)\n", dev, count));
switch (dev & 0x0f) {
@@ -169,7 +170,7 @@ static ssize_t sound_read(struct file *file, char __user *buf, size_t count, lof
case SND_DEV_MIDIN:
ret = MIDIbuf_read(dev, file, buf, count);
}
- unlock_kernel();
+ mutex_unlock(&soundcard_mutex);
return ret;
}
@@ -178,7 +179,7 @@ static ssize_t sound_write(struct file *file, const char __user *buf, size_t cou
int dev = iminor(file->f_path.dentry->d_inode);
int ret = -EINVAL;
- lock_kernel();
+ mutex_lock(&soundcard_mutex);
DEB(printk("sound_write(dev=%d, count=%d)\n", dev, count));
switch (dev & 0x0f) {
case SND_DEV_SEQ:
@@ -196,7 +197,7 @@ static ssize_t sound_write(struct file *file, const char __user *buf, size_t cou
ret = MIDIbuf_write(dev, file, buf, count);
break;
}
- unlock_kernel();
+ mutex_unlock(&soundcard_mutex);
return ret;
}
@@ -210,7 +211,7 @@ static int sound_open(struct inode *inode, struct file *file)
printk(KERN_ERR "Invalid minor device %d\n", dev);
return -ENXIO;
}
- lock_kernel();
+ mutex_lock(&soundcard_mutex);
switch (dev & 0x0f) {
case SND_DEV_CTL:
dev >>= 4;
@@ -247,15 +248,15 @@ static int sound_open(struct inode *inode, struct file *file)
retval = -ENXIO;
}
- unlock_kernel();
- return 0;
+ mutex_unlock(&soundcard_mutex);
+ return retval;
}
static int sound_release(struct inode *inode, struct file *file)
{
int dev = iminor(inode);
- lock_kernel();
+ mutex_lock(&soundcard_mutex);
DEB(printk("sound_release(dev=%d)\n", dev));
switch (dev & 0x0f) {
case SND_DEV_CTL:
@@ -280,7 +281,7 @@ static int sound_release(struct inode *inode, struct file *file)
default:
printk(KERN_ERR "Sound error: Releasing unknown device 0x%02x\n", dev);
}
- unlock_kernel();
+ mutex_unlock(&soundcard_mutex);
return 0;
}
@@ -354,7 +355,7 @@ static long sound_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
if (cmd == OSS_GETVERSION)
return __put_user(SOUND_VERSION, (int __user *)p);
- lock_kernel();
+ mutex_lock(&soundcard_mutex);
if (_IOC_TYPE(cmd) == 'M' && num_mixers > 0 && /* Mixer ioctl */
(dev & 0x0f) != SND_DEV_CTL) {
dtype = dev & 0x0f;
@@ -369,7 +370,7 @@ static long sound_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
ret = sound_mixer_ioctl(dev >> 4, cmd, p);
break;
}
- unlock_kernel();
+ mutex_unlock(&soundcard_mutex);
return ret;
}
@@ -391,15 +392,15 @@ static long sound_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
case SND_DEV_DSP:
case SND_DEV_DSP16:
case SND_DEV_AUDIO:
- return audio_ioctl(dev, file, cmd, p);
+ ret = audio_ioctl(dev, file, cmd, p);
break;
case SND_DEV_MIDIN:
- return MIDIbuf_ioctl(dev, file, cmd, p);
+ ret = MIDIbuf_ioctl(dev, file, cmd, p);
break;
}
- unlock_kernel();
+ mutex_unlock(&soundcard_mutex);
return ret;
}
@@ -439,35 +440,35 @@ static int sound_mmap(struct file *file, struct vm_area_struct *vma)
printk(KERN_ERR "Sound: mmap() not supported for other than audio devices\n");
return -EINVAL;
}
- lock_kernel();
+ mutex_lock(&soundcard_mutex);
if (vma->vm_flags & VM_WRITE) /* Map write and read/write to the output buf */
dmap = audio_devs[dev]->dmap_out;
else if (vma->vm_flags & VM_READ)
dmap = audio_devs[dev]->dmap_in;
else {
printk(KERN_ERR "Sound: Undefined mmap() access\n");
- unlock_kernel();
+ mutex_unlock(&soundcard_mutex);
return -EINVAL;
}
if (dmap == NULL) {
printk(KERN_ERR "Sound: mmap() error. dmap == NULL\n");
- unlock_kernel();
+ mutex_unlock(&soundcard_mutex);
return -EIO;
}
if (dmap->raw_buf == NULL) {
printk(KERN_ERR "Sound: mmap() called when raw_buf == NULL\n");
- unlock_kernel();
+ mutex_unlock(&soundcard_mutex);
return -EIO;
}
if (dmap->mapping_flags) {
printk(KERN_ERR "Sound: mmap() called twice for the same DMA buffer\n");
- unlock_kernel();
+ mutex_unlock(&soundcard_mutex);
return -EIO;
}
if (vma->vm_pgoff != 0) {
printk(KERN_ERR "Sound: mmap() offset must be 0.\n");
- unlock_kernel();
+ mutex_unlock(&soundcard_mutex);
return -EINVAL;
}
size = vma->vm_end - vma->vm_start;
@@ -478,7 +479,7 @@ static int sound_mmap(struct file *file, struct vm_area_struct *vma)
if (remap_pfn_range(vma, vma->vm_start,
virt_to_phys(dmap->raw_buf) >> PAGE_SHIFT,
vma->vm_end - vma->vm_start, vma->vm_page_prot)) {
- unlock_kernel();
+ mutex_unlock(&soundcard_mutex);
return -EAGAIN;
}
@@ -490,7 +491,7 @@ static int sound_mmap(struct file *file, struct vm_area_struct *vma)
memset(dmap->raw_buf,
dmap->neutral_byte,
dmap->bytes_in_use);
- unlock_kernel();
+ mutex_unlock(&soundcard_mutex);
return 0;
}
diff --git a/sound/oss/swarm_cs4297a.c b/sound/oss/swarm_cs4297a.c
index b15840ad252..44357d877a2 100644
--- a/sound/oss/swarm_cs4297a.c
+++ b/sound/oss/swarm_cs4297a.c
@@ -68,7 +68,6 @@
#include <linux/delay.h>
#include <linux/sound.h>
#include <linux/slab.h>
-#include <linux/smp_lock.h>
#include <linux/soundcard.h>
#include <linux/ac97_codec.h>
#include <linux/pci.h>
@@ -94,6 +93,7 @@
struct cs4297a_state;
+static DEFINE_MUTEX(swarm_cs4297a_mutex);
static void stop_dac(struct cs4297a_state *s);
static void stop_adc(struct cs4297a_state *s);
static void start_dac(struct cs4297a_state *s);
@@ -1535,7 +1535,7 @@ static int cs4297a_open_mixdev(struct inode *inode, struct file *file)
CS_DBGOUT(CS_FUNCTION | CS_OPEN, 4,
printk(KERN_INFO "cs4297a: cs4297a_open_mixdev()+\n"));
- lock_kernel();
+ mutex_lock(&swarm_cs4297a_mutex);
list_for_each(entry, &cs4297a_devs)
{
s = list_entry(entry, struct cs4297a_state, list);
@@ -1547,7 +1547,7 @@ static int cs4297a_open_mixdev(struct inode *inode, struct file *file)
CS_DBGOUT(CS_FUNCTION | CS_OPEN | CS_ERROR, 2,
printk(KERN_INFO "cs4297a: cs4297a_open_mixdev()- -ENODEV\n"));
- unlock_kernel();
+ mutex_unlock(&swarm_cs4297a_mutex);
return -ENODEV;
}
VALIDATE_STATE(s);
@@ -1555,7 +1555,7 @@ static int cs4297a_open_mixdev(struct inode *inode, struct file *file)
CS_DBGOUT(CS_FUNCTION | CS_OPEN, 4,
printk(KERN_INFO "cs4297a: cs4297a_open_mixdev()- 0\n"));
- unlock_kernel();
+ mutex_unlock(&swarm_cs4297a_mutex);
return nonseekable_open(inode, file);
}
@@ -1575,10 +1575,10 @@ static int cs4297a_ioctl_mixdev(struct file *file,
unsigned int cmd, unsigned long arg)
{
int ret;
- lock_kernel();
+ mutex_lock(&swarm_cs4297a_mutex);
ret = mixer_ioctl((struct cs4297a_state *) file->private_data, cmd,
arg);
- unlock_kernel();
+ mutex_unlock(&swarm_cs4297a_mutex);
return ret;
}
@@ -2350,9 +2350,9 @@ static long cs4297a_unlocked_ioctl(struct file *file, u_int cmd, u_long arg)
{
int ret;
- lock_kernel();
+ mutex_lock(&swarm_cs4297a_mutex);
ret = cs4297a_ioctl(file, cmd, arg);
- unlock_kernel();
+ mutex_unlock(&swarm_cs4297a_mutex);
return ret;
}
@@ -2509,9 +2509,9 @@ static int cs4297a_open(struct inode *inode, struct file *file)
{
int ret;
- lock_kernel();
+ mutex_lock(&swarm_cs4297a_mutex);
ret = cs4297a_open(inode, file);
- unlock_kernel();
+ mutex_unlock(&swarm_cs4297a_mutex);
return ret;
}
diff --git a/sound/oss/vwsnd.c b/sound/oss/vwsnd.c
index 8cd73cdd88a..643f1113b1d 100644
--- a/sound/oss/vwsnd.c
+++ b/sound/oss/vwsnd.c
@@ -145,7 +145,6 @@
#include <linux/init.h>
#include <linux/spinlock.h>
-#include <linux/smp_lock.h>
#include <linux/wait.h>
#include <linux/interrupt.h>
#include <linux/mutex.h>
@@ -160,6 +159,7 @@
#ifdef VWSND_DEBUG
+static DEFINE_MUTEX(vwsnd_mutex);
static int shut_up = 1;
/*
@@ -2891,11 +2891,11 @@ static long vwsnd_audio_ioctl(struct file *file,
vwsnd_dev_t *devc = (vwsnd_dev_t *) file->private_data;
int ret;
- lock_kernel();
+ mutex_lock(&vwsnd_mutex);
mutex_lock(&devc->io_mutex);
ret = vwsnd_audio_do_ioctl(file, cmd, arg);
mutex_unlock(&devc->io_mutex);
- unlock_kernel();
+ mutex_unlock(&vwsnd_mutex);
return ret;
}
@@ -2922,7 +2922,7 @@ static int vwsnd_audio_open(struct inode *inode, struct file *file)
DBGE("(inode=0x%p, file=0x%p)\n", inode, file);
- lock_kernel();
+ mutex_lock(&vwsnd_mutex);
INC_USE_COUNT;
for (devc = vwsnd_dev_list; devc; devc = devc->next_dev)
if ((devc->audio_minor & ~0x0F) == (minor & ~0x0F))
@@ -2930,7 +2930,7 @@ static int vwsnd_audio_open(struct inode *inode, struct file *file)
if (devc == NULL) {
DEC_USE_COUNT;
- unlock_kernel();
+ mutex_unlock(&vwsnd_mutex);
return -ENODEV;
}
@@ -2939,13 +2939,13 @@ static int vwsnd_audio_open(struct inode *inode, struct file *file)
mutex_unlock(&devc->open_mutex);
if (file->f_flags & O_NONBLOCK) {
DEC_USE_COUNT;
- unlock_kernel();
+ mutex_unlock(&vwsnd_mutex);
return -EBUSY;
}
interruptible_sleep_on(&devc->open_wait);
if (signal_pending(current)) {
DEC_USE_COUNT;
- unlock_kernel();
+ mutex_unlock(&vwsnd_mutex);
return -ERESTARTSYS;
}
mutex_lock(&devc->open_mutex);
@@ -2998,7 +2998,7 @@ static int vwsnd_audio_open(struct inode *inode, struct file *file)
file->private_data = devc;
DBGRV();
- unlock_kernel();
+ mutex_unlock(&vwsnd_mutex);
return 0;
}
@@ -3012,7 +3012,7 @@ static int vwsnd_audio_release(struct inode *inode, struct file *file)
vwsnd_port_t *wport = NULL, *rport = NULL;
int err = 0;
- lock_kernel();
+ mutex_lock(&vwsnd_mutex);
mutex_lock(&devc->io_mutex);
{
DBGEV("(inode=0x%p, file=0x%p)\n", inode, file);
@@ -3040,7 +3040,7 @@ static int vwsnd_audio_release(struct inode *inode, struct file *file)
wake_up(&devc->open_wait);
DEC_USE_COUNT;
DBGR();
- unlock_kernel();
+ mutex_unlock(&vwsnd_mutex);
return err;
}
@@ -3068,18 +3068,18 @@ static int vwsnd_mixer_open(struct inode *inode, struct file *file)
DBGEV("(inode=0x%p, file=0x%p)\n", inode, file);
INC_USE_COUNT;
- lock_kernel();
+ mutex_lock(&vwsnd_mutex);
for (devc = vwsnd_dev_list; devc; devc = devc->next_dev)
if (devc->mixer_minor == iminor(inode))
break;
if (devc == NULL) {
DEC_USE_COUNT;
- unlock_kernel();
+ mutex_unlock(&vwsnd_mutex);
return -ENODEV;
}
file->private_data = devc;
- unlock_kernel();
+ mutex_unlock(&vwsnd_mutex);
return 0;
}
@@ -3223,7 +3223,7 @@ static long vwsnd_mixer_ioctl(struct file *file,
DBGEV("(devc=0x%p, cmd=0x%x, arg=0x%lx)\n", devc, cmd, arg);
- lock_kernel();
+ mutex_lock(&vwsnd_mutex);
mutex_lock(&devc->mix_mutex);
{
if ((cmd & ~nrmask) == MIXER_READ(0))
@@ -3234,7 +3234,7 @@ static long vwsnd_mixer_ioctl(struct file *file,
retval = -EINVAL;
}
mutex_unlock(&devc->mix_mutex);
- unlock_kernel();
+ mutex_unlock(&vwsnd_mutex);
return retval;
}
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig
index e7a8cd058ef..12e34653b8a 100644
--- a/sound/pci/Kconfig
+++ b/sound/pci/Kconfig
@@ -207,12 +207,12 @@ config SND_CMIPCI
config SND_OXYGEN_LIB
tristate
- select SND_PCM
- select SND_MPU401_UART
config SND_OXYGEN
tristate "C-Media 8788 (Oxygen)"
select SND_OXYGEN_LIB
+ select SND_PCM
+ select SND_MPU401_UART
help
Say Y here to include support for sound cards based on the
C-Media CMI8788 (Oxygen HD Audio) chip:
@@ -581,6 +581,8 @@ config SND_HDSPM
config SND_HIFIER
tristate "TempoTec HiFier Fantasia"
select SND_OXYGEN_LIB
+ select SND_PCM
+ select SND_MPU401_UART
help
Say Y here to include support for the MediaTek/TempoTec HiFier
Fantasia sound card.
@@ -815,14 +817,17 @@ config SND_VIA82XX_MODEM
will be called snd-via82xx-modem.
config SND_VIRTUOSO
- tristate "Asus Virtuoso 100/200 (Xonar)"
+ tristate "Asus Virtuoso 66/100/200 (Xonar)"
select SND_OXYGEN_LIB
+ select SND_PCM
+ select SND_MPU401_UART
+ select SND_JACK if INPUT=y || INPUT=SND
help
Say Y here to include support for sound cards based on the
- Asus AV100/AV200 chips, i.e., Xonar D1, DX, D2, D2X,
+ Asus AV66/AV100/AV200 chips, i.e., Xonar D1, DX, D2, D2X, DS,
Essence ST (Deluxe), and Essence STX.
- Support for the DS is experimental.
- Support for the HDAV1.3 (Deluxe) is very experimental.
+ Support for the HDAV1.3 (Deluxe) is incomplete; for the
+ HDAV1.3 Slim and Xense, missing.
To compile this driver as a module, choose M here: the module
will be called snd-virtuoso.
diff --git a/sound/pci/asihpi/hpi6205.c b/sound/pci/asihpi/hpi6205.c
index 3b441344822..22c5fc62553 100644
--- a/sound/pci/asihpi/hpi6205.c
+++ b/sound/pci/asihpi/hpi6205.c
@@ -941,8 +941,7 @@ static void outstream_host_buffer_free(struct hpi_adapter_obj *pao,
}
-static u32 outstream_get_space_available(struct hpi_hostbuffer_status
- *status)
+static u32 outstream_get_space_available(struct hpi_hostbuffer_status *status)
{
return status->size_in_bytes - (status->host_index -
status->dSP_index);
@@ -987,6 +986,10 @@ static void outstream_write(struct hpi_adapter_obj *pao,
/* write it */
phm->function = HPI_OSTREAM_WRITE;
hw_message(pao, phm, phr);
+
+ if (phr->error)
+ return;
+
/* update status information that the DSP would typically
* update (and will update next time the DSP
* buffer update task reads data from the host BBM buffer)
diff --git a/sound/pci/au88x0/au88x0_mixer.c b/sound/pci/au88x0/au88x0_mixer.c
index c92f493d341..557c782ae4f 100644
--- a/sound/pci/au88x0/au88x0_mixer.c
+++ b/sound/pci/au88x0/au88x0_mixer.c
@@ -23,7 +23,7 @@ static int __devinit snd_vortex_mixer(vortex_t * vortex)
if ((err = snd_ac97_bus(vortex->card, 0, &ops, NULL, &pbus)) < 0)
return err;
memset(&ac97, 0, sizeof(ac97));
- // Intialize AC97 codec stuff.
+ // Initialize AC97 codec stuff.
ac97.private_data = vortex;
ac97.scaps = AC97_SCAP_NO_SPDIF;
err = snd_ac97_mixer(pbus, &ac97, &vortex->codec);
diff --git a/sound/pci/ca0106/ca0106.h b/sound/pci/ca0106/ca0106.h
index 14b8d9a91aa..f19c1107725 100644
--- a/sound/pci/ca0106/ca0106.h
+++ b/sound/pci/ca0106/ca0106.h
@@ -670,8 +670,9 @@ struct snd_ca0106_details {
gpio_type = 2 -> shared side-out/line-in. */
int i2c_adc; /* with i2c_adc=1, the driver adds some capture volume
controls, phone, mic, line-in and aux. */
- int spi_dac; /* spi_dac=1 adds the mute switch for each analog
- output, front, rear, etc. */
+ u16 spi_dac; /* spi_dac = 0 -> no spi interface for DACs
+ spi_dac = 0x<front><rear><center-lfe><side>
+ -> specifies DAC id for each channel pair. */
};
// definition of the chip-specific record
diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c
index 0a3d3d6e77b..d2d12c08f93 100644
--- a/sound/pci/ca0106/ca0106_main.c
+++ b/sound/pci/ca0106/ca0106_main.c
@@ -227,7 +227,7 @@ static struct snd_ca0106_details ca0106_chip_details[] = {
.name = "Audigy SE [SB0570]",
.gpio_type = 1,
.i2c_adc = 1,
- .spi_dac = 1 } ,
+ .spi_dac = 0x4021 } ,
/* New Audigy LS. Has a different DAC. */
/* SB0570:
* CTRL:CA0106-DAT
@@ -238,7 +238,17 @@ static struct snd_ca0106_details ca0106_chip_details[] = {
.name = "Audigy SE OEM [SB0570a]",
.gpio_type = 1,
.i2c_adc = 1,
- .spi_dac = 1 } ,
+ .spi_dac = 0x4021 } ,
+ /* Sound Blaster 5.1vx
+ * Tested: Playback on front, rear, center/lfe speakers
+ * Not-Tested: Capture
+ */
+ { .serial = 0x10041102,
+ .name = "Sound Blaster 5.1vx [SB1070]",
+ .gpio_type = 1,
+ .i2c_adc = 0,
+ .spi_dac = 0x0124
+ } ,
/* MSI K8N Diamond Motherboard with onboard SB Live 24bit without AC97 */
/* SB0438
* CTRL:CA0106-DAT
@@ -254,7 +264,7 @@ static struct snd_ca0106_details ca0106_chip_details[] = {
.name = "MSI K8N Diamond MB",
.gpio_type = 2,
.i2c_adc = 1,
- .spi_dac = 1 } ,
+ .spi_dac = 0x4021 } ,
/* Giga-byte GA-G1975X mobo
* Novell bnc#395807
*/
@@ -483,16 +493,18 @@ static void snd_ca0106_pcm_free_substream(struct snd_pcm_runtime *runtime)
}
static const int spi_dacd_reg[] = {
- [PCM_FRONT_CHANNEL] = SPI_DACD4_REG,
- [PCM_REAR_CHANNEL] = SPI_DACD0_REG,
- [PCM_CENTER_LFE_CHANNEL]= SPI_DACD2_REG,
- [PCM_UNKNOWN_CHANNEL] = SPI_DACD1_REG,
+ SPI_DACD0_REG,
+ SPI_DACD1_REG,
+ SPI_DACD2_REG,
+ 0,
+ SPI_DACD4_REG,
};
static const int spi_dacd_bit[] = {
- [PCM_FRONT_CHANNEL] = SPI_DACD4_BIT,
- [PCM_REAR_CHANNEL] = SPI_DACD0_BIT,
- [PCM_CENTER_LFE_CHANNEL]= SPI_DACD2_BIT,
- [PCM_UNKNOWN_CHANNEL] = SPI_DACD1_BIT,
+ SPI_DACD0_BIT,
+ SPI_DACD1_BIT,
+ SPI_DACD2_BIT,
+ 0,
+ SPI_DACD4_BIT,
};
static void restore_spdif_bits(struct snd_ca0106 *chip, int idx)
@@ -504,6 +516,45 @@ static void restore_spdif_bits(struct snd_ca0106 *chip, int idx)
}
}
+static int snd_ca0106_channel_dac(struct snd_ca0106_details *details,
+ int channel_id)
+{
+ switch (channel_id) {
+ case PCM_FRONT_CHANNEL:
+ return (details->spi_dac & 0xf000) >> (4 * 3);
+ case PCM_REAR_CHANNEL:
+ return (details->spi_dac & 0x0f00) >> (4 * 2);
+ case PCM_CENTER_LFE_CHANNEL:
+ return (details->spi_dac & 0x00f0) >> (4 * 1);
+ case PCM_UNKNOWN_CHANNEL:
+ return (details->spi_dac & 0x000f) >> (4 * 0);
+ default:
+ snd_printk(KERN_DEBUG "ca0106: unknown channel_id %d\n",
+ channel_id);
+ }
+ return 0;
+}
+
+static int snd_ca0106_pcm_power_dac(struct snd_ca0106 *chip, int channel_id,
+ int power)
+{
+ if (chip->details->spi_dac) {
+ const int dac = snd_ca0106_channel_dac(chip->details,
+ channel_id);
+ const int reg = spi_dacd_reg[dac];
+ const int bit = spi_dacd_bit[dac];
+
+ if (power)
+ /* Power up */
+ chip->spi_dac_reg[reg] &= ~bit;
+ else
+ /* Power down */
+ chip->spi_dac_reg[reg] |= bit;
+ return snd_ca0106_spi_write(chip, chip->spi_dac_reg[reg]);
+ }
+ return 0;
+}
+
/* open_playback callback */
static int snd_ca0106_pcm_open_playback_channel(struct snd_pcm_substream *substream,
int channel_id)
@@ -543,12 +594,9 @@ static int snd_ca0106_pcm_open_playback_channel(struct snd_pcm_substream *substr
return err;
snd_pcm_set_sync(substream);
- if (chip->details->spi_dac && channel_id != PCM_FRONT_CHANNEL) {
- const int reg = spi_dacd_reg[channel_id];
-
- /* Power up dac */
- chip->spi_dac_reg[reg] &= ~spi_dacd_bit[channel_id];
- err = snd_ca0106_spi_write(chip, chip->spi_dac_reg[reg]);
+ /* Front channel dac should already be on */
+ if (channel_id != PCM_FRONT_CHANNEL) {
+ err = snd_ca0106_pcm_power_dac(chip, channel_id, 1);
if (err < 0)
return err;
}
@@ -568,13 +616,14 @@ static int snd_ca0106_pcm_close_playback(struct snd_pcm_substream *substream)
restore_spdif_bits(chip, epcm->channel_id);
- if (chip->details->spi_dac && epcm->channel_id != PCM_FRONT_CHANNEL) {
- const int reg = spi_dacd_reg[epcm->channel_id];
-
- /* Power down DAC */
- chip->spi_dac_reg[reg] |= spi_dacd_bit[epcm->channel_id];
- snd_ca0106_spi_write(chip, chip->spi_dac_reg[reg]);
+ /* Front channel dac should stay on */
+ if (epcm->channel_id != PCM_FRONT_CHANNEL) {
+ int err;
+ err = snd_ca0106_pcm_power_dac(chip, epcm->channel_id, 0);
+ if (err < 0)
+ return err;
}
+
/* FIXME: maybe zero others */
return 0;
}
@@ -1002,29 +1051,27 @@ snd_ca0106_pcm_pointer_playback(struct snd_pcm_substream *substream)
struct snd_ca0106 *emu = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_ca0106_pcm *epcm = runtime->private_data;
- snd_pcm_uframes_t ptr, ptr1, ptr2,ptr3,ptr4 = 0;
+ unsigned int ptr, prev_ptr;
int channel = epcm->channel_id;
+ int timeout = 10;
if (!epcm->running)
return 0;
- ptr3 = snd_ca0106_ptr_read(emu, PLAYBACK_LIST_PTR, channel);
- ptr1 = snd_ca0106_ptr_read(emu, PLAYBACK_POINTER, channel);
- ptr4 = snd_ca0106_ptr_read(emu, PLAYBACK_LIST_PTR, channel);
- if (ptr3 != ptr4) ptr1 = snd_ca0106_ptr_read(emu, PLAYBACK_POINTER, channel);
- ptr2 = bytes_to_frames(runtime, ptr1);
- ptr2+= (ptr4 >> 3) * runtime->period_size;
- ptr=ptr2;
- if (ptr >= runtime->buffer_size)
- ptr -= runtime->buffer_size;
- /*
- printk(KERN_DEBUG "ptr1 = 0x%lx, ptr2=0x%lx, ptr=0x%lx, "
- "buffer_size = 0x%x, period_size = 0x%x, bits=%d, rate=%d\n",
- ptr1, ptr2, ptr, (int)runtime->buffer_size,
- (int)runtime->period_size, (int)runtime->frame_bits,
- (int)runtime->rate);
- */
- return ptr;
+ prev_ptr = -1;
+ do {
+ ptr = snd_ca0106_ptr_read(emu, PLAYBACK_LIST_PTR, channel);
+ ptr = (ptr >> 3) * runtime->period_size;
+ ptr += bytes_to_frames(runtime,
+ snd_ca0106_ptr_read(emu, PLAYBACK_POINTER, channel));
+ if (ptr >= runtime->buffer_size)
+ ptr -= runtime->buffer_size;
+ if (prev_ptr == ptr)
+ return ptr;
+ prev_ptr = ptr;
+ } while (--timeout);
+ snd_printk(KERN_WARNING "ca0106: unstable DMA pointer!\n");
+ return 0;
}
/* pointer_capture callback */
@@ -1362,7 +1409,7 @@ static unsigned int spi_dac_init[] = {
SPI_REG(12, 0x00),
SPI_REG(SPI_LDA4_REG, SPI_DA_BIT_0dB),
SPI_REG(SPI_RDA4_REG, SPI_DA_BIT_0dB | SPI_DA_BIT_UPDATE),
- SPI_REG(SPI_DACD4_REG, 0x00),
+ SPI_REG(SPI_DACD4_REG, SPI_DACD4_BIT),
};
static unsigned int i2c_adc_init[][2] = {
@@ -1541,7 +1588,7 @@ static void ca0106_init_chip(struct snd_ca0106 *chip, int resume)
/* snd_ca0106_i2c_write(chip, ADC_MUX, ADC_MUX_LINEIN); */
}
- if (chip->details->spi_dac == 1) {
+ if (chip->details->spi_dac) {
/* The SB0570 use SPI to control DAC. */
int size, n;
@@ -1553,6 +1600,9 @@ static void ca0106_init_chip(struct snd_ca0106 *chip, int resume)
if (reg < ARRAY_SIZE(chip->spi_dac_reg))
chip->spi_dac_reg[reg] = spi_dac_init[n];
}
+
+ /* Enable front dac only */
+ snd_ca0106_pcm_power_dac(chip, PCM_FRONT_CHANNEL, 1);
}
}
diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c
index 85fd315d999..630aa499818 100644
--- a/sound/pci/ca0106/ca0106_mixer.c
+++ b/sound/pci/ca0106/ca0106_mixer.c
@@ -676,28 +676,65 @@ static struct snd_kcontrol_new snd_ca0106_volume_i2c_adc_ctls[] __devinitdata =
I2C_VOLUME("Aux Capture Volume", 3),
};
-#define SPI_SWITCH(xname,reg,bit) \
-{ \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
- .info = spi_mute_info, \
- .get = spi_mute_get, \
- .put = spi_mute_put, \
- .private_value = (reg<<SPI_REG_SHIFT) | (bit) \
-}
-
-static struct snd_kcontrol_new snd_ca0106_volume_spi_dac_ctls[]
-__devinitdata = {
- SPI_SWITCH("Analog Front Playback Switch",
- SPI_DMUTE4_REG, SPI_DMUTE4_BIT),
- SPI_SWITCH("Analog Rear Playback Switch",
- SPI_DMUTE0_REG, SPI_DMUTE0_BIT),
- SPI_SWITCH("Analog Center/LFE Playback Switch",
- SPI_DMUTE2_REG, SPI_DMUTE2_BIT),
- SPI_SWITCH("Analog Side Playback Switch",
- SPI_DMUTE1_REG, SPI_DMUTE1_BIT),
+static const int spi_dmute_reg[] = {
+ SPI_DMUTE0_REG,
+ SPI_DMUTE1_REG,
+ SPI_DMUTE2_REG,
+ 0,
+ SPI_DMUTE4_REG,
+};
+static const int spi_dmute_bit[] = {
+ SPI_DMUTE0_BIT,
+ SPI_DMUTE1_BIT,
+ SPI_DMUTE2_BIT,
+ 0,
+ SPI_DMUTE4_BIT,
};
+static struct snd_kcontrol_new __devinit
+snd_ca0106_volume_spi_dac_ctl(struct snd_ca0106_details *details,
+ int channel_id)
+{
+ struct snd_kcontrol_new spi_switch = {0};
+ int reg, bit;
+ int dac_id;
+
+ spi_switch.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ spi_switch.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
+ spi_switch.info = spi_mute_info;
+ spi_switch.get = spi_mute_get;
+ spi_switch.put = spi_mute_put;
+
+ switch (channel_id) {
+ case PCM_FRONT_CHANNEL:
+ spi_switch.name = "Analog Front Playback Switch";
+ dac_id = (details->spi_dac & 0xf000) >> (4 * 3);
+ break;
+ case PCM_REAR_CHANNEL:
+ spi_switch.name = "Analog Rear Playback Switch";
+ dac_id = (details->spi_dac & 0x0f00) >> (4 * 2);
+ break;
+ case PCM_CENTER_LFE_CHANNEL:
+ spi_switch.name = "Analog Center/LFE Playback Switch";
+ dac_id = (details->spi_dac & 0x00f0) >> (4 * 1);
+ break;
+ case PCM_UNKNOWN_CHANNEL:
+ spi_switch.name = "Analog Side Playback Switch";
+ dac_id = (details->spi_dac & 0x000f) >> (4 * 0);
+ break;
+ default:
+ /* Unused channel */
+ spi_switch.name = NULL;
+ dac_id = 0;
+ }
+ reg = spi_dmute_reg[dac_id];
+ bit = spi_dmute_bit[dac_id];
+
+ spi_switch.private_value = (reg << SPI_REG_SHIFT) | bit;
+
+ return spi_switch;
+}
+
static int __devinit remove_ctl(struct snd_card *card, const char *name)
{
struct snd_ctl_elem_id id;
@@ -832,8 +869,18 @@ int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu)
if (err < 0)
return err;
}
- if (emu->details->spi_dac == 1)
- ADD_CTLS(emu, snd_ca0106_volume_spi_dac_ctls);
+ if (emu->details->spi_dac) {
+ int i;
+ for (i = 0;; i++) {
+ struct snd_kcontrol_new ctl;
+ ctl = snd_ca0106_volume_spi_dac_ctl(emu->details, i);
+ if (!ctl.name)
+ break;
+ err = snd_ctl_add(card, snd_ctl_new1(&ctl, emu));
+ if (err < 0)
+ return err;
+ }
+ }
/* Create virtual master controls */
vmaster = snd_ctl_make_virtual_master("Master Playback Volume",
@@ -845,7 +892,7 @@ int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu)
return err;
add_slaves(card, vmaster, slave_vols);
- if (emu->details->spi_dac == 1) {
+ if (emu->details->spi_dac) {
vmaster = snd_ctl_make_virtual_master("Master Playback Switch",
NULL);
if (!vmaster)
diff --git a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c
index 4203782d7cb..aff8387c45c 100644
--- a/sound/pci/emu10k1/emu10k1.c
+++ b/sound/pci/emu10k1/emu10k1.c
@@ -52,6 +52,7 @@ static int max_synth_voices[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 64};
static int max_buffer_size[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 128};
static int enable_ir[SNDRV_CARDS];
static uint subsystem[SNDRV_CARDS]; /* Force card subsystem model */
+static uint delay_pcm_irq[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for the EMU10K1 soundcard.");
@@ -73,6 +74,8 @@ module_param_array(enable_ir, bool, NULL, 0444);
MODULE_PARM_DESC(enable_ir, "Enable IR.");
module_param_array(subsystem, uint, NULL, 0444);
MODULE_PARM_DESC(subsystem, "Force card subsystem model.");
+module_param_array(delay_pcm_irq, uint, NULL, 0444);
+MODULE_PARM_DESC(delay_pcm_irq, "Delay PCM interrupt by specified number of samples (default 0).");
/*
* Class 0401: 1102:0008 (rev 00) Subsystem: 1102:1001 -> Audigy2 Value Model:SB0400
*/
@@ -127,6 +130,7 @@ static int __devinit snd_card_emu10k1_probe(struct pci_dev *pci,
&emu)) < 0)
goto error;
card->private_data = emu;
+ emu->delay_pcm_irq = delay_pcm_irq[dev] & 0x1f;
if ((err = snd_emu10k1_pcm(emu, 0, NULL)) < 0)
goto error;
if ((err = snd_emu10k1_pcm_mic(emu, 1, NULL)) < 0)
diff --git a/sound/pci/emu10k1/emumpu401.c b/sound/pci/emu10k1/emumpu401.c
index 8578c70c61f..bab564824ef 100644
--- a/sound/pci/emu10k1/emumpu401.c
+++ b/sound/pci/emu10k1/emumpu401.c
@@ -321,7 +321,7 @@ static struct snd_rawmidi_ops snd_emu10k1_midi_input =
static void snd_emu10k1_midi_free(struct snd_rawmidi *rmidi)
{
- struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)rmidi->private_data;
+ struct snd_emu10k1_midi *midi = rmidi->private_data;
midi->interrupt = NULL;
midi->rmidi = NULL;
}
diff --git a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c
index 55b83ef73c6..622bace148e 100644
--- a/sound/pci/emu10k1/emupcm.c
+++ b/sound/pci/emu10k1/emupcm.c
@@ -332,7 +332,7 @@ static void snd_emu10k1_pcm_init_voice(struct snd_emu10k1 *emu,
evoice->epcm->ccca_start_addr = start_addr + ccis;
if (extra) {
start_addr += ccis;
- end_addr += ccis;
+ end_addr += ccis + emu->delay_pcm_irq;
}
if (stereo && !extra) {
snd_emu10k1_ptr_write(emu, CPF, voice, CPF_STEREO_MASK);
@@ -360,7 +360,9 @@ static void snd_emu10k1_pcm_init_voice(struct snd_emu10k1 *emu,
/* Assumption that PT is already 0 so no harm overwriting */
snd_emu10k1_ptr_write(emu, PTRX, voice, (send_amount[0] << 8) | send_amount[1]);
snd_emu10k1_ptr_write(emu, DSL, voice, end_addr | (send_amount[3] << 24));
- snd_emu10k1_ptr_write(emu, PSST, voice, start_addr | (send_amount[2] << 24));
+ snd_emu10k1_ptr_write(emu, PSST, voice,
+ (start_addr + (extra ? emu->delay_pcm_irq : 0)) |
+ (send_amount[2] << 24));
if (emu->card_capabilities->emu_model)
pitch_target = PITCH_48000; /* Disable interpolators on emu1010 card */
else
@@ -732,6 +734,23 @@ static void snd_emu10k1_playback_stop_voice(struct snd_emu10k1 *emu, struct snd_
snd_emu10k1_ptr_write(emu, IP, voice, 0);
}
+static inline void snd_emu10k1_playback_mangle_extra(struct snd_emu10k1 *emu,
+ struct snd_emu10k1_pcm *epcm,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_runtime *runtime)
+{
+ unsigned int ptr, period_pos;
+
+ /* try to sychronize the current position for the interrupt
+ source voice */
+ period_pos = runtime->status->hw_ptr - runtime->hw_ptr_interrupt;
+ period_pos %= runtime->period_size;
+ ptr = snd_emu10k1_ptr_read(emu, CCCA, epcm->extra->number);
+ ptr &= ~0x00ffffff;
+ ptr |= epcm->ccca_start_addr + period_pos;
+ snd_emu10k1_ptr_write(emu, CCCA, epcm->extra->number, ptr);
+}
+
static int snd_emu10k1_playback_trigger(struct snd_pcm_substream *substream,
int cmd)
{
@@ -753,6 +772,8 @@ static int snd_emu10k1_playback_trigger(struct snd_pcm_substream *substream,
/* follow thru */
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
case SNDRV_PCM_TRIGGER_RESUME:
+ if (cmd == SNDRV_PCM_TRIGGER_PAUSE_RELEASE)
+ snd_emu10k1_playback_mangle_extra(emu, epcm, substream, runtime);
mix = &emu->pcm_mixer[substream->number];
snd_emu10k1_playback_prepare_voice(emu, epcm->voices[0], 1, 0, mix);
snd_emu10k1_playback_prepare_voice(emu, epcm->voices[1], 0, 0, mix);
@@ -869,8 +890,9 @@ static snd_pcm_uframes_t snd_emu10k1_playback_pointer(struct snd_pcm_substream *
#endif
/*
printk(KERN_DEBUG
- "ptr = 0x%x, buffer_size = 0x%x, period_size = 0x%x\n",
- ptr, runtime->buffer_size, runtime->period_size);
+ "ptr = 0x%lx, buffer_size = 0x%lx, period_size = 0x%lx\n",
+ (long)ptr, (long)runtime->buffer_size,
+ (long)runtime->period_size);
*/
return ptr;
}
diff --git a/sound/pci/emu10k1/memory.c b/sound/pci/emu10k1/memory.c
index ffb1ddb8dc2..957a311514c 100644
--- a/sound/pci/emu10k1/memory.c
+++ b/sound/pci/emu10k1/memory.c
@@ -310,8 +310,10 @@ snd_emu10k1_alloc_pages(struct snd_emu10k1 *emu, struct snd_pcm_substream *subst
if (snd_BUG_ON(!hdr))
return NULL;
+ idx = runtime->period_size >= runtime->buffer_size ?
+ (emu->delay_pcm_irq * 2) : 0;
mutex_lock(&hdr->block_mutex);
- blk = search_empty(emu, runtime->dma_bytes);
+ blk = search_empty(emu, runtime->dma_bytes + idx);
if (blk == NULL) {
mutex_unlock(&hdr->block_mutex);
return NULL;
diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c
index c7fba537981..537cfba829a 100644
--- a/sound/pci/ens1370.c
+++ b/sound/pci/ens1370.c
@@ -22,7 +22,7 @@
/* Power-Management-Code ( CONFIG_PM )
* for ens1371 only ( FIXME )
* derived from cs4281.c, atiixp.c and via82xx.c
- * using http://www.alsa-project.org/~iwai/writing-an-alsa-driver/c1540.htm
+ * using http://www.alsa-project.org/~tiwai/writing-an-alsa-driver/
* by Kurt J. Bosch
*/
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig
index 9194c3c1d04..0ea5cc60ac7 100644
--- a/sound/pci/hda/Kconfig
+++ b/sound/pci/hda/Kconfig
@@ -119,47 +119,20 @@ config SND_HDA_CODEC_VIA
snd-hda-codec-via.
This module is automatically loaded at probing.
-config SND_HDA_CODEC_ATIHDMI
- bool "Build ATI HDMI HD-audio codec support"
- default y
- help
- Say Y here to include ATI HDMI HD-audio codec support in
- snd-hda-intel driver, such as ATI RS600 HDMI.
-
- When the HD-audio driver is built as a module, the codec
- support code is also built as another module,
- snd-hda-codec-atihdmi.
- This module is automatically loaded at probing.
-
-config SND_HDA_CODEC_NVHDMI
- bool "Build NVIDIA HDMI HD-audio codec support"
- default y
- help
- Say Y here to include NVIDIA HDMI HD-audio codec support in
- snd-hda-intel driver, such as NVIDIA MCP78 HDMI.
-
- When the HD-audio driver is built as a module, the codec
- support code is also built as another module,
- snd-hda-codec-nvhdmi.
- This module is automatically loaded at probing.
-
-config SND_HDA_CODEC_INTELHDMI
- bool "Build INTEL HDMI HD-audio codec support"
+config SND_HDA_CODEC_HDMI
+ bool "Build HDMI/DisplayPort HD-audio codec support"
select SND_DYNAMIC_MINORS
default y
help
- Say Y here to include INTEL HDMI HD-audio codec support in
- snd-hda-intel driver, such as Eaglelake integrated HDMI.
+ Say Y here to include HDMI and DisplayPort HD-audio codec
+ support in snd-hda-intel driver. This includes all AMD/ATI,
+ Intel and Nvidia HDMI/DisplayPort codecs.
When the HD-audio driver is built as a module, the codec
support code is also built as another module,
- snd-hda-codec-intelhdmi.
+ snd-hda-codec-hdmi.
This module is automatically loaded at probing.
-config SND_HDA_ELD
- def_bool y
- depends on SND_HDA_CODEC_INTELHDMI || SND_HDA_CODEC_NVHDMI
-
config SND_HDA_CODEC_CIRRUS
bool "Build Cirrus Logic codec support"
depends on SND_HDA_INTEL
diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile
index 24bc195b02d..17ef3658f34 100644
--- a/sound/pci/hda/Makefile
+++ b/sound/pci/hda/Makefile
@@ -3,7 +3,6 @@ snd-hda-intel-objs := hda_intel.o
snd-hda-codec-y := hda_codec.o
snd-hda-codec-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o
snd-hda-codec-$(CONFIG_PROC_FS) += hda_proc.o
-snd-hda-codec-$(CONFIG_SND_HDA_ELD) += hda_eld.o
snd-hda-codec-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o
snd-hda-codec-$(CONFIG_SND_HDA_INPUT_BEEP) += hda_beep.o
@@ -12,13 +11,11 @@ snd-hda-codec-cmedia-objs := patch_cmedia.o
snd-hda-codec-analog-objs := patch_analog.o
snd-hda-codec-idt-objs := patch_sigmatel.o
snd-hda-codec-si3054-objs := patch_si3054.o
-snd-hda-codec-atihdmi-objs := patch_atihdmi.o
snd-hda-codec-cirrus-objs := patch_cirrus.o
snd-hda-codec-ca0110-objs := patch_ca0110.o
snd-hda-codec-conexant-objs := patch_conexant.o
snd-hda-codec-via-objs := patch_via.o
-snd-hda-codec-nvhdmi-objs := patch_nvhdmi.o
-snd-hda-codec-intelhdmi-objs := patch_intelhdmi.o
+snd-hda-codec-hdmi-objs := patch_hdmi.o hda_eld.o
# common driver
obj-$(CONFIG_SND_HDA_INTEL) := snd-hda-codec.o
@@ -39,9 +36,6 @@ endif
ifdef CONFIG_SND_HDA_CODEC_SI3054
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-si3054.o
endif
-ifdef CONFIG_SND_HDA_CODEC_ATIHDMI
-obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-atihdmi.o
-endif
ifdef CONFIG_SND_HDA_CODEC_CIRRUS
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-cirrus.o
endif
@@ -54,11 +48,8 @@ endif
ifdef CONFIG_SND_HDA_CODEC_VIA
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-via.o
endif
-ifdef CONFIG_SND_HDA_CODEC_NVHDMI
-obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-nvhdmi.o
-endif
-ifdef CONFIG_SND_HDA_CODEC_INTELHDMI
-obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-intelhdmi.o
+ifdef CONFIG_SND_HDA_CODEC_HDMI
+obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-hdmi.o
endif
# this must be the last entry after codec drivers;
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index dd8fb86c842..644e3f14f8c 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -589,6 +589,7 @@ int /*__devinit*/ snd_hda_bus_new(struct snd_card *card,
bus->ops = temp->ops;
mutex_init(&bus->cmd_mutex);
+ mutex_init(&bus->prepare_mutex);
INIT_LIST_HEAD(&bus->codec_list);
snprintf(bus->workq_name, sizeof(bus->workq_name),
@@ -1068,7 +1069,6 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus,
codec->addr = codec_addr;
mutex_init(&codec->spdif_mutex);
mutex_init(&codec->control_mutex);
- mutex_init(&codec->prepare_mutex);
init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
snd_array_init(&codec->mixers, sizeof(struct hda_nid_item), 32);
@@ -1213,8 +1213,10 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
u32 stream_tag,
int channel_id, int format)
{
+ struct hda_codec *c;
struct hda_cvt_setup *p;
unsigned int oldval, newval;
+ int type;
int i;
if (!nid)
@@ -1253,10 +1255,14 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
p->dirty = 0;
/* make other inactive cvts with the same stream-tag dirty */
- for (i = 0; i < codec->cvt_setups.used; i++) {
- p = snd_array_elem(&codec->cvt_setups, i);
- if (!p->active && p->stream_tag == stream_tag)
- p->dirty = 1;
+ type = get_wcaps_type(get_wcaps(codec, nid));
+ list_for_each_entry(c, &codec->bus->codec_list, list) {
+ for (i = 0; i < c->cvt_setups.used; i++) {
+ p = snd_array_elem(&c->cvt_setups, i);
+ if (!p->active && p->stream_tag == stream_tag &&
+ get_wcaps_type(get_wcaps(codec, p->nid)) == type)
+ p->dirty = 1;
+ }
}
}
EXPORT_SYMBOL_HDA(snd_hda_codec_setup_stream);
@@ -1278,6 +1284,9 @@ void __snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid,
if (!nid)
return;
+ if (codec->no_sticky_stream)
+ do_now = 1;
+
snd_printdd("hda_codec_cleanup_stream: NID=0x%x\n", nid);
p = get_hda_cvt_setup(codec, nid);
if (p) {
@@ -1306,12 +1315,16 @@ static void really_cleanup_stream(struct hda_codec *codec,
/* clean up the all conflicting obsolete streams */
static void purify_inactive_streams(struct hda_codec *codec)
{
+ struct hda_codec *c;
int i;
- for (i = 0; i < codec->cvt_setups.used; i++) {
- struct hda_cvt_setup *p = snd_array_elem(&codec->cvt_setups, i);
- if (p->dirty)
- really_cleanup_stream(codec, p);
+ list_for_each_entry(c, &codec->bus->codec_list, list) {
+ for (i = 0; i < c->cvt_setups.used; i++) {
+ struct hda_cvt_setup *p;
+ p = snd_array_elem(&c->cvt_setups, i);
+ if (p->dirty)
+ really_cleanup_stream(c, p);
+ }
}
}
@@ -1824,6 +1837,7 @@ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
hda_nid_t nid = get_amp_nid(kcontrol);
int dir = get_amp_direction(kcontrol);
unsigned int ofs = get_amp_offset(kcontrol);
+ bool min_mute = get_amp_min_mute(kcontrol);
u32 caps, val1, val2;
if (size < 4 * sizeof(unsigned int))
@@ -1834,6 +1848,8 @@ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
val1 = -((caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT);
val1 += ofs;
val1 = ((int)val1) * ((int)val2);
+ if (min_mute)
+ val2 |= TLV_DB_SCALE_MUTE;
if (put_user(SNDRV_CTL_TLVT_DB_SCALE, _tlv))
return -EFAULT;
if (put_user(2 * sizeof(unsigned int), _tlv + 1))
@@ -2221,10 +2237,7 @@ int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
change |= snd_hda_codec_amp_update(codec, nid, 1, dir, idx,
HDA_AMP_MUTE,
*valp ? 0 : HDA_AMP_MUTE);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (codec->patch_ops.check_power_status)
- codec->patch_ops.check_power_status(codec, nid);
-#endif
+ hda_call_check_power_status(codec, nid);
snd_hda_power_down(codec);
return change;
}
@@ -3502,11 +3515,11 @@ int snd_hda_codec_prepare(struct hda_codec *codec,
struct snd_pcm_substream *substream)
{
int ret;
- mutex_lock(&codec->prepare_mutex);
+ mutex_lock(&codec->bus->prepare_mutex);
ret = hinfo->ops.prepare(hinfo, codec, stream, format, substream);
if (ret >= 0)
purify_inactive_streams(codec);
- mutex_unlock(&codec->prepare_mutex);
+ mutex_unlock(&codec->bus->prepare_mutex);
return ret;
}
EXPORT_SYMBOL_HDA(snd_hda_codec_prepare);
@@ -3515,9 +3528,9 @@ void snd_hda_codec_cleanup(struct hda_codec *codec,
struct hda_pcm_stream *hinfo,
struct snd_pcm_substream *substream)
{
- mutex_lock(&codec->prepare_mutex);
+ mutex_lock(&codec->bus->prepare_mutex);
hinfo->ops.cleanup(hinfo, codec, substream);
- mutex_unlock(&codec->prepare_mutex);
+ mutex_unlock(&codec->bus->prepare_mutex);
}
EXPORT_SYMBOL_HDA(snd_hda_codec_cleanup);
@@ -4365,6 +4378,34 @@ static void sort_pins_by_sequence(hda_nid_t *pins, short *sequences,
}
+/* add the found input-pin to the cfg->inputs[] table */
+static void add_auto_cfg_input_pin(struct auto_pin_cfg *cfg, hda_nid_t nid,
+ int type)
+{
+ if (cfg->num_inputs < AUTO_CFG_MAX_INS) {
+ cfg->inputs[cfg->num_inputs].pin = nid;
+ cfg->inputs[cfg->num_inputs].type = type;
+ cfg->num_inputs++;
+ }
+}
+
+/* sort inputs in the order of AUTO_PIN_* type */
+static void sort_autocfg_input_pins(struct auto_pin_cfg *cfg)
+{
+ int i, j;
+
+ for (i = 0; i < cfg->num_inputs; i++) {
+ for (j = i + 1; j < cfg->num_inputs; j++) {
+ if (cfg->inputs[i].type > cfg->inputs[j].type) {
+ struct auto_pin_cfg_item tmp;
+ tmp = cfg->inputs[i];
+ cfg->inputs[i] = cfg->inputs[j];
+ cfg->inputs[j] = tmp;
+ }
+ }
+ }
+}
+
/*
* Parse all pin widgets and store the useful pin nids to cfg
*
@@ -4378,7 +4419,7 @@ static void sort_pins_by_sequence(hda_nid_t *pins, short *sequences,
* output, i.e. to line_out_pins[0]. So, line_outs is always positive
* if any analog output exists.
*
- * The analog input pins are assigned to input_pins array.
+ * The analog input pins are assigned to inputs array.
* The digital input/output pins are assigned to dig_in_pin and dig_out_pin,
* respectively.
*/
@@ -4391,6 +4432,7 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
short sequences_line_out[ARRAY_SIZE(cfg->line_out_pins)];
short sequences_speaker[ARRAY_SIZE(cfg->speaker_pins)];
short sequences_hp[ARRAY_SIZE(cfg->hp_pins)];
+ int i;
memset(cfg, 0, sizeof(*cfg));
@@ -4461,33 +4503,17 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
sequences_hp[cfg->hp_outs] = (assoc << 4) | seq;
cfg->hp_outs++;
break;
- case AC_JACK_MIC_IN: {
- int preferred, alt;
- if (loc == AC_JACK_LOC_FRONT ||
- (loc & 0x30) == AC_JACK_LOC_INTERNAL) {
- preferred = AUTO_PIN_FRONT_MIC;
- alt = AUTO_PIN_MIC;
- } else {
- preferred = AUTO_PIN_MIC;
- alt = AUTO_PIN_FRONT_MIC;
- }
- if (!cfg->input_pins[preferred])
- cfg->input_pins[preferred] = nid;
- else if (!cfg->input_pins[alt])
- cfg->input_pins[alt] = nid;
+ case AC_JACK_MIC_IN:
+ add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_MIC);
break;
- }
case AC_JACK_LINE_IN:
- if (loc == AC_JACK_LOC_FRONT)
- cfg->input_pins[AUTO_PIN_FRONT_LINE] = nid;
- else
- cfg->input_pins[AUTO_PIN_LINE] = nid;
+ add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_LINE_IN);
break;
case AC_JACK_CD:
- cfg->input_pins[AUTO_PIN_CD] = nid;
+ add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_CD);
break;
case AC_JACK_AUX:
- cfg->input_pins[AUTO_PIN_AUX] = nid;
+ add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_AUX);
break;
case AC_JACK_SPDIF_OUT:
case AC_JACK_DIG_OTHER_OUT:
@@ -4529,9 +4555,11 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
cfg->hp_outs--;
memmove(cfg->hp_pins + i, cfg->hp_pins + i + 1,
sizeof(cfg->hp_pins[0]) * (cfg->hp_outs - i));
- memmove(sequences_hp + i - 1, sequences_hp + i,
+ memmove(sequences_hp + i, sequences_hp + i + 1,
sizeof(sequences_hp[0]) * (cfg->hp_outs - i));
}
+ memset(cfg->hp_pins + cfg->hp_outs, 0,
+ sizeof(hda_nid_t) * (AUTO_CFG_MAX_OUTS - cfg->hp_outs));
}
/* sort by sequence */
@@ -4542,21 +4570,6 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
sort_pins_by_sequence(cfg->hp_pins, sequences_hp,
cfg->hp_outs);
- /* if we have only one mic, make it AUTO_PIN_MIC */
- if (!cfg->input_pins[AUTO_PIN_MIC] &&
- cfg->input_pins[AUTO_PIN_FRONT_MIC]) {
- cfg->input_pins[AUTO_PIN_MIC] =
- cfg->input_pins[AUTO_PIN_FRONT_MIC];
- cfg->input_pins[AUTO_PIN_FRONT_MIC] = 0;
- }
- /* ditto for line-in */
- if (!cfg->input_pins[AUTO_PIN_LINE] &&
- cfg->input_pins[AUTO_PIN_FRONT_LINE]) {
- cfg->input_pins[AUTO_PIN_LINE] =
- cfg->input_pins[AUTO_PIN_FRONT_LINE];
- cfg->input_pins[AUTO_PIN_FRONT_LINE] = 0;
- }
-
/*
* FIX-UP: if no line-outs are detected, try to use speaker or HP pin
* as a primary output
@@ -4595,6 +4608,8 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
break;
}
+ sort_autocfg_input_pins(cfg);
+
/*
* debug prints of the parsed results
*/
@@ -4614,14 +4629,13 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
if (cfg->dig_outs)
snd_printd(" dig-out=0x%x/0x%x\n",
cfg->dig_out_pins[0], cfg->dig_out_pins[1]);
- snd_printd(" inputs: mic=0x%x, fmic=0x%x, line=0x%x, fline=0x%x,"
- " cd=0x%x, aux=0x%x\n",
- cfg->input_pins[AUTO_PIN_MIC],
- cfg->input_pins[AUTO_PIN_FRONT_MIC],
- cfg->input_pins[AUTO_PIN_LINE],
- cfg->input_pins[AUTO_PIN_FRONT_LINE],
- cfg->input_pins[AUTO_PIN_CD],
- cfg->input_pins[AUTO_PIN_AUX]);
+ snd_printd(" inputs:");
+ for (i = 0; i < cfg->num_inputs; i++) {
+ snd_printdd(" %s=0x%x",
+ hda_get_autocfg_input_label(codec, cfg, i),
+ cfg->inputs[i].pin);
+ }
+ snd_printd("\n");
if (cfg->dig_in_pin)
snd_printd(" dig-in=0x%x\n", cfg->dig_in_pin);
@@ -4629,11 +4643,165 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
}
EXPORT_SYMBOL_HDA(snd_hda_parse_pin_def_config);
-/* labels for input pins */
-const char *auto_pin_cfg_labels[AUTO_PIN_LAST] = {
- "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux"
-};
-EXPORT_SYMBOL_HDA(auto_pin_cfg_labels);
+int snd_hda_get_input_pin_attr(unsigned int def_conf)
+{
+ unsigned int loc = get_defcfg_location(def_conf);
+ unsigned int conn = get_defcfg_connect(def_conf);
+ if (conn == AC_JACK_PORT_NONE)
+ return INPUT_PIN_ATTR_UNUSED;
+ /* Windows may claim the internal mic to be BOTH, too */
+ if (conn == AC_JACK_PORT_FIXED || conn == AC_JACK_PORT_BOTH)
+ return INPUT_PIN_ATTR_INT;
+ if ((loc & 0x30) == AC_JACK_LOC_INTERNAL)
+ return INPUT_PIN_ATTR_INT;
+ if ((loc & 0x30) == AC_JACK_LOC_SEPARATE)
+ return INPUT_PIN_ATTR_DOCK;
+ if (loc == AC_JACK_LOC_REAR)
+ return INPUT_PIN_ATTR_REAR;
+ if (loc == AC_JACK_LOC_FRONT)
+ return INPUT_PIN_ATTR_FRONT;
+ return INPUT_PIN_ATTR_NORMAL;
+}
+EXPORT_SYMBOL_HDA(snd_hda_get_input_pin_attr);
+
+/**
+ * hda_get_input_pin_label - Give a label for the given input pin
+ *
+ * When check_location is true, the function checks the pin location
+ * for mic and line-in pins, and set an appropriate prefix like "Front",
+ * "Rear", "Internal".
+ */
+
+const char *hda_get_input_pin_label(struct hda_codec *codec, hda_nid_t pin,
+ int check_location)
+{
+ unsigned int def_conf;
+ static const char *mic_names[] = {
+ "Internal Mic", "Dock Mic", "Mic", "Front Mic", "Rear Mic",
+ };
+ int attr;
+
+ def_conf = snd_hda_codec_get_pincfg(codec, pin);
+
+ switch (get_defcfg_device(def_conf)) {
+ case AC_JACK_MIC_IN:
+ if (!check_location)
+ return "Mic";
+ attr = snd_hda_get_input_pin_attr(def_conf);
+ if (!attr)
+ return "None";
+ return mic_names[attr - 1];
+ case AC_JACK_LINE_IN:
+ if (!check_location)
+ return "Line";
+ attr = snd_hda_get_input_pin_attr(def_conf);
+ if (!attr)
+ return "None";
+ if (attr == INPUT_PIN_ATTR_DOCK)
+ return "Dock Line";
+ return "Line";
+ case AC_JACK_AUX:
+ return "Aux";
+ case AC_JACK_CD:
+ return "CD";
+ case AC_JACK_SPDIF_IN:
+ return "SPDIF In";
+ case AC_JACK_DIG_OTHER_IN:
+ return "Digital In";
+ default:
+ return "Misc";
+ }
+}
+EXPORT_SYMBOL_HDA(hda_get_input_pin_label);
+
+/* Check whether the location prefix needs to be added to the label.
+ * If all mic-jacks are in the same location (e.g. rear panel), we don't
+ * have to put "Front" prefix to each label. In such a case, returns false.
+ */
+static int check_mic_location_need(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg,
+ int input)
+{
+ unsigned int defc;
+ int i, attr, attr2;
+
+ defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[input].pin);
+ attr = snd_hda_get_input_pin_attr(defc);
+ /* for internal or docking mics, we need locations */
+ if (attr <= INPUT_PIN_ATTR_NORMAL)
+ return 1;
+
+ attr = 0;
+ for (i = 0; i < cfg->num_inputs; i++) {
+ defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[i].pin);
+ attr2 = snd_hda_get_input_pin_attr(defc);
+ if (attr2 >= INPUT_PIN_ATTR_NORMAL) {
+ if (attr && attr != attr2)
+ return 1; /* different locations found */
+ attr = attr2;
+ }
+ }
+ return 0;
+}
+
+/**
+ * hda_get_autocfg_input_label - Get a label for the given input
+ *
+ * Get a label for the given input pin defined by the autocfg item.
+ * Unlike hda_get_input_pin_label(), this function checks all inputs
+ * defined in autocfg and avoids the redundant mic/line prefix as much as
+ * possible.
+ */
+const char *hda_get_autocfg_input_label(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg,
+ int input)
+{
+ int type = cfg->inputs[input].type;
+ int has_multiple_pins = 0;
+
+ if ((input > 0 && cfg->inputs[input - 1].type == type) ||
+ (input < cfg->num_inputs - 1 && cfg->inputs[input + 1].type == type))
+ has_multiple_pins = 1;
+ if (has_multiple_pins && type == AUTO_PIN_MIC)
+ has_multiple_pins &= check_mic_location_need(codec, cfg, input);
+ return hda_get_input_pin_label(codec, cfg->inputs[input].pin,
+ has_multiple_pins);
+}
+EXPORT_SYMBOL_HDA(hda_get_autocfg_input_label);
+
+/**
+ * snd_hda_add_imux_item - Add an item to input_mux
+ *
+ * When the same label is used already in the existing items, the number
+ * suffix is appended to the label. This label index number is stored
+ * to type_idx when non-NULL pointer is given.
+ */
+int snd_hda_add_imux_item(struct hda_input_mux *imux, const char *label,
+ int index, int *type_idx)
+{
+ int i, label_idx = 0;
+ if (imux->num_items >= HDA_MAX_NUM_INPUTS) {
+ snd_printd(KERN_ERR "hda_codec: Too many imux items!\n");
+ return -EINVAL;
+ }
+ for (i = 0; i < imux->num_items; i++) {
+ if (!strncmp(label, imux->items[i].label, strlen(label)))
+ label_idx++;
+ }
+ if (type_idx)
+ *type_idx = label_idx;
+ if (label_idx > 0)
+ snprintf(imux->items[imux->num_items].label,
+ sizeof(imux->items[imux->num_items].label),
+ "%s %d", label, label_idx);
+ else
+ strlcpy(imux->items[imux->num_items].label, label,
+ sizeof(imux->items[imux->num_items].label));
+ imux->items[imux->num_items].index = index;
+ imux->num_items++;
+ return 0;
+}
+EXPORT_SYMBOL_HDA(snd_hda_add_imux_item);
#ifdef CONFIG_PM
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index 4303353feda..fdf8d44f8b6 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -648,6 +648,7 @@ struct hda_bus {
struct hda_codec *caddr_tbl[HDA_MAX_CODEC_ADDRESS + 1];
struct mutex cmd_mutex;
+ struct mutex prepare_mutex;
/* unsolicited event queue */
struct hda_bus_unsolicited *unsol;
@@ -826,7 +827,6 @@ struct hda_codec {
struct mutex spdif_mutex;
struct mutex control_mutex;
- struct mutex prepare_mutex;
unsigned int spdif_status; /* IEC958 status bits */
unsigned short spdif_ctls; /* SPDIF control bits */
unsigned int spdif_in_enable; /* SPDIF input enable? */
@@ -850,6 +850,7 @@ struct hda_codec {
unsigned int pin_amp_workaround:1; /* pin out-amp takes index
* (e.g. Conexant codecs)
*/
+ unsigned int no_sticky_stream:1; /* no sticky-PCM stream assignment */
unsigned int pins_shutup:1; /* pins are shut up */
unsigned int no_trigger_sense:1; /* don't trigger at pin-sensing */
#ifdef CONFIG_SND_HDA_POWER_SAVE
@@ -989,6 +990,18 @@ int snd_hda_suspend(struct hda_bus *bus);
int snd_hda_resume(struct hda_bus *bus);
#endif
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static inline
+int hda_call_check_power_status(struct hda_codec *codec, hda_nid_t nid)
+{
+ if (codec->patch_ops.check_power_status)
+ return codec->patch_ops.check_power_status(codec, nid);
+ return 0;
+}
+#else
+#define hda_call_check_power_status(codec, nid) 0
+#endif
+
/*
* get widget information
*/
diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c
index 803b298f741..cb0c23a6b47 100644
--- a/sound/pci/hda/hda_eld.c
+++ b/sound/pci/hda/hda_eld.c
@@ -332,7 +332,6 @@ int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid)
return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_SIZE,
AC_DIPSIZE_ELD_BUF);
}
-EXPORT_SYMBOL_HDA(snd_hdmi_get_eld_size);
int snd_hdmi_get_eld(struct hdmi_eld *eld,
struct hda_codec *codec, hda_nid_t nid)
@@ -368,7 +367,6 @@ int snd_hdmi_get_eld(struct hdmi_eld *eld,
kfree(buf);
return ret;
}
-EXPORT_SYMBOL_HDA(snd_hdmi_get_eld);
static void hdmi_show_short_audio_desc(struct cea_sad *a)
{
@@ -407,7 +405,6 @@ void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen)
}
buf[j] = '\0'; /* necessary when j == 0 */
}
-EXPORT_SYMBOL_HDA(snd_print_channel_allocation);
void snd_hdmi_show_eld(struct hdmi_eld *e)
{
@@ -426,7 +423,6 @@ void snd_hdmi_show_eld(struct hdmi_eld *e)
for (i = 0; i < e->sad_count; i++)
hdmi_show_short_audio_desc(e->sad + i);
}
-EXPORT_SYMBOL_HDA(snd_hdmi_show_eld);
#ifdef CONFIG_PROC_FS
@@ -585,7 +581,6 @@ int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld,
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_eld_proc_new);
void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld)
{
@@ -594,7 +589,8 @@ void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld)
eld->proc_entry = NULL;
}
}
-EXPORT_SYMBOL_HDA(snd_hda_eld_proc_free);
+
+#endif /* CONFIG_PROC_FS */
/* update PCM info based on ELD */
void hdmi_eld_update_pcm_info(struct hdmi_eld *eld, struct hda_pcm_stream *pcm,
@@ -643,6 +639,3 @@ void hdmi_eld_update_pcm_info(struct hdmi_eld *eld, struct hda_pcm_stream *pcm,
pcm->channels_max = min(pcm->channels_max, codec_pars->channels_max);
pcm->maxbps = min(pcm->maxbps, codec_pars->maxbps);
}
-EXPORT_SYMBOL_HDA(hdmi_eld_update_pcm_info);
-
-#endif /* CONFIG_PROC_FS */
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 5ea21285ee1..fb0582f8d72 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -61,7 +61,6 @@ struct hda_gspec {
struct hda_gnode *cap_vol_node; /* Node for capture volume */
unsigned int cur_cap_src; /* current capture source */
struct hda_input_mux input_mux;
- char cap_labels[HDA_MAX_NUM_INPUTS][16];
unsigned int def_amp_in_caps;
unsigned int def_amp_out_caps;
@@ -506,11 +505,10 @@ static const char *get_input_type(struct hda_gnode *node, unsigned int *pinctl)
* returns 0 if not found, 1 if found, or a negative error code.
*/
static int parse_adc_sub_nodes(struct hda_codec *codec, struct hda_gspec *spec,
- struct hda_gnode *node)
+ struct hda_gnode *node, int idx)
{
int i, err;
unsigned int pinctl;
- char *label;
const char *type;
if (node->checked)
@@ -523,7 +521,7 @@ static int parse_adc_sub_nodes(struct hda_codec *codec, struct hda_gspec *spec,
child = hda_get_node(spec, node->conn_list[i]);
if (! child)
continue;
- err = parse_adc_sub_nodes(codec, spec, child);
+ err = parse_adc_sub_nodes(codec, spec, child, idx);
if (err < 0)
return err;
if (err > 0) {
@@ -564,9 +562,7 @@ static int parse_adc_sub_nodes(struct hda_codec *codec, struct hda_gspec *spec,
return 0;
type = "Input";
}
- label = spec->cap_labels[spec->input_mux.num_items];
- strcpy(label, type);
- spec->input_mux.items[spec->input_mux.num_items].label = label;
+ snd_hda_add_imux_item(&spec->input_mux, type, idx, NULL);
/* unmute the PIN external input */
unmute_input(codec, node, 0); /* index = 0? */
@@ -577,29 +573,6 @@ static int parse_adc_sub_nodes(struct hda_codec *codec, struct hda_gspec *spec,
return 1; /* found */
}
-/* add a capture source element */
-static void add_cap_src(struct hda_gspec *spec, int idx)
-{
- struct hda_input_mux_item *csrc;
- char *buf;
- int num, ocap;
-
- num = spec->input_mux.num_items;
- csrc = &spec->input_mux.items[num];
- buf = spec->cap_labels[num];
- for (ocap = 0; ocap < num; ocap++) {
- if (! strcmp(buf, spec->cap_labels[ocap])) {
- /* same label already exists,
- * put the index number to be unique
- */
- sprintf(buf, "%s %d", spec->cap_labels[ocap], num);
- break;
- }
- }
- csrc->index = idx;
- spec->input_mux.num_items++;
-}
-
/*
* parse input
*/
@@ -624,22 +597,18 @@ static int parse_input_path(struct hda_codec *codec, struct hda_gnode *adc_node)
for (i = 0; i < adc_node->nconns; i++) {
node = hda_get_node(spec, adc_node->conn_list[i]);
if (node && node->type == AC_WID_PIN) {
- err = parse_adc_sub_nodes(codec, spec, node);
+ err = parse_adc_sub_nodes(codec, spec, node, i);
if (err < 0)
return err;
- else if (err > 0)
- add_cap_src(spec, i);
}
}
/* ... then check the rests, more complicated connections */
for (i = 0; i < adc_node->nconns; i++) {
node = hda_get_node(spec, adc_node->conn_list[i]);
if (node && node->type != AC_WID_PIN) {
- err = parse_adc_sub_nodes(codec, spec, node);
+ err = parse_adc_sub_nodes(codec, spec, node, i);
if (err < 0)
return err;
- else if (err > 0)
- add_cap_src(spec, i);
}
}
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 1053fff4bd0..21aa9b0e28f 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -78,8 +78,8 @@ MODULE_PARM_DESC(enable, "Enable Intel HD audio interface.");
module_param_array(model, charp, NULL, 0444);
MODULE_PARM_DESC(model, "Use the given board model.");
module_param_array(position_fix, int, NULL, 0444);
-MODULE_PARM_DESC(position_fix, "Fix DMA pointer "
- "(0 = auto, 1 = none, 2 = POSBUF).");
+MODULE_PARM_DESC(position_fix, "DMA pointer read method."
+ "(0 = auto, 1 = LPIB, 2 = POSBUF, 3 = VIACOMBO).");
module_param_array(bdl_pos_adj, int, NULL, 0644);
MODULE_PARM_DESC(bdl_pos_adj, "BDL position adjustment offset.");
module_param_array(probe_mask, int, NULL, 0444);
@@ -126,6 +126,7 @@ MODULE_SUPPORTED_DEVICE("{{Intel, ICH6},"
"{Intel, ICH10},"
"{Intel, PCH},"
"{Intel, CPT},"
+ "{Intel, PBG},"
"{Intel, SCH},"
"{ATI, SB450},"
"{ATI, SB600},"
@@ -304,6 +305,7 @@ enum {
POS_FIX_AUTO,
POS_FIX_LPIB,
POS_FIX_POSBUF,
+ POS_FIX_VIACOMBO,
};
/* Defines for ATI HD Audio support in SB450 south bridge */
@@ -432,7 +434,6 @@ struct azx {
unsigned int polling_mode :1;
unsigned int msi :1;
unsigned int irq_pending_warned :1;
- unsigned int via_dmapos_patch :1; /* enable DMA-position fix for VIA */
unsigned int probing :1; /* codec probing phase */
/* for debugging */
@@ -457,6 +458,7 @@ enum {
AZX_DRIVER_ULI,
AZX_DRIVER_NVIDIA,
AZX_DRIVER_TERA,
+ AZX_DRIVER_CTX,
AZX_DRIVER_GENERIC,
AZX_NUM_DRIVERS, /* keep this as last entry */
};
@@ -472,6 +474,7 @@ static char *driver_short_names[] __devinitdata = {
[AZX_DRIVER_ULI] = "HDA ULI M5461",
[AZX_DRIVER_NVIDIA] = "HDA NVidia",
[AZX_DRIVER_TERA] = "HDA Teradici",
+ [AZX_DRIVER_CTX] = "HDA Creative",
[AZX_DRIVER_GENERIC] = "HD-Audio Generic",
};
@@ -562,7 +565,10 @@ static void azx_init_cmd_io(struct azx *chip)
/* reset the rirb hw write pointer */
azx_writew(chip, RIRBWP, ICH6_RIRBWP_RST);
/* set N=1, get RIRB response interrupt for new entry */
- azx_writew(chip, RINTCNT, 1);
+ if (chip->driver_type == AZX_DRIVER_CTX)
+ azx_writew(chip, RINTCNT, 0xc0);
+ else
+ azx_writew(chip, RINTCNT, 1);
/* enable rirb dma and response irq */
azx_writeb(chip, RIRBCTL, ICH6_RBCTL_DMA_EN | ICH6_RBCTL_IRQ_EN);
spin_unlock_irq(&chip->reg_lock);
@@ -1135,8 +1141,11 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id)
/* clear rirb int */
status = azx_readb(chip, RIRBSTS);
if (status & RIRB_INT_MASK) {
- if (status & RIRB_INT_RESPONSE)
+ if (status & RIRB_INT_RESPONSE) {
+ if (chip->driver_type == AZX_DRIVER_CTX)
+ udelay(80);
azx_update_rirb(chip);
+ }
azx_writeb(chip, RIRBSTS, RIRB_INT_MASK);
}
@@ -1308,11 +1317,8 @@ static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev)
azx_sd_writel(azx_dev, SD_BDLPU, upper_32_bits(azx_dev->bdl.addr));
/* enable the position buffer */
- if (chip->position_fix[0] == POS_FIX_POSBUF ||
- chip->position_fix[0] == POS_FIX_AUTO ||
- chip->position_fix[1] == POS_FIX_POSBUF ||
- chip->position_fix[1] == POS_FIX_AUTO ||
- chip->via_dmapos_patch) {
+ if (chip->position_fix[0] != POS_FIX_LPIB ||
+ chip->position_fix[1] != POS_FIX_LPIB) {
if (!(azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE))
azx_writel(chip, DPLBASE,
(u32)chip->posbuf.addr | ICH6_DPLBASE_ENABLE);
@@ -1646,7 +1652,7 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream)
struct azx_dev *azx_dev = get_azx_dev(substream);
struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned int bufsize, period_bytes, format_val;
+ unsigned int bufsize, period_bytes, format_val, stream_tag;
int err;
azx_stream_reset(chip, azx_dev);
@@ -1688,7 +1694,12 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream)
else
azx_dev->fifo_size = 0;
- return snd_hda_codec_prepare(apcm->codec, hinfo, azx_dev->stream_tag,
+ stream_tag = azx_dev->stream_tag;
+ /* CA-IBG chips need the playback stream starting from 1 */
+ if (chip->driver_type == AZX_DRIVER_CTX &&
+ stream_tag > chip->capture_streams)
+ stream_tag -= chip->capture_streams;
+ return snd_hda_codec_prepare(apcm->codec, hinfo, stream_tag,
azx_dev->format_val, substream);
}
@@ -1851,20 +1862,21 @@ static unsigned int azx_get_position(struct azx *chip,
struct azx_dev *azx_dev)
{
unsigned int pos;
+ int stream = azx_dev->substream->stream;
- if (chip->via_dmapos_patch)
+ switch (chip->position_fix[stream]) {
+ case POS_FIX_LPIB:
+ /* read LPIB */
+ pos = azx_sd_readl(azx_dev, SD_LPIB);
+ break;
+ case POS_FIX_VIACOMBO:
pos = azx_via_get_position(chip, azx_dev);
- else {
- int stream = azx_dev->substream->stream;
- if (chip->position_fix[stream] == POS_FIX_POSBUF ||
- chip->position_fix[stream] == POS_FIX_AUTO) {
- /* use the position buffer */
- pos = le32_to_cpu(*azx_dev->posbuf);
- } else {
- /* read LPIB */
- pos = azx_sd_readl(azx_dev, SD_LPIB);
- }
+ break;
+ default:
+ /* use the position buffer */
+ pos = le32_to_cpu(*azx_dev->posbuf);
}
+
if (pos >= azx_dev->bufsize)
pos = 0;
return pos;
@@ -2312,19 +2324,10 @@ static int __devinit check_position_fix(struct azx *chip, int fix)
switch (fix) {
case POS_FIX_LPIB:
case POS_FIX_POSBUF:
+ case POS_FIX_VIACOMBO:
return fix;
}
- /* Check VIA/ATI HD Audio Controller exist */
- switch (chip->driver_type) {
- case AZX_DRIVER_VIA:
- case AZX_DRIVER_ATI:
- chip->via_dmapos_patch = 1;
- /* Use link position directly, avoid any transfer problem. */
- return POS_FIX_LPIB;
- }
- chip->via_dmapos_patch = 0;
-
q = snd_pci_quirk_lookup(chip->pci, position_fix_list);
if (q) {
printk(KERN_INFO
@@ -2333,6 +2336,15 @@ static int __devinit check_position_fix(struct azx *chip, int fix)
q->value, q->subvendor, q->subdevice);
return q->value;
}
+
+ /* Check VIA/ATI HD Audio Controller exist */
+ switch (chip->driver_type) {
+ case AZX_DRIVER_VIA:
+ case AZX_DRIVER_ATI:
+ /* Use link position directly, avoid any transfer problem. */
+ return POS_FIX_VIACOMBO;
+ }
+
return POS_FIX_AUTO;
}
@@ -2734,23 +2746,17 @@ static void __devexit azx_remove(struct pci_dev *pci)
/* PCI IDs */
static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
- /* ICH 6..10 */
- { PCI_DEVICE(0x8086, 0x2668), .driver_data = AZX_DRIVER_ICH },
- { PCI_DEVICE(0x8086, 0x27d8), .driver_data = AZX_DRIVER_ICH },
- { PCI_DEVICE(0x8086, 0x269a), .driver_data = AZX_DRIVER_ICH },
- { PCI_DEVICE(0x8086, 0x284b), .driver_data = AZX_DRIVER_ICH },
- { PCI_DEVICE(0x8086, 0x2911), .driver_data = AZX_DRIVER_ICH },
- { PCI_DEVICE(0x8086, 0x293e), .driver_data = AZX_DRIVER_ICH },
- { PCI_DEVICE(0x8086, 0x293f), .driver_data = AZX_DRIVER_ICH },
- { PCI_DEVICE(0x8086, 0x3a3e), .driver_data = AZX_DRIVER_ICH },
- { PCI_DEVICE(0x8086, 0x3a6e), .driver_data = AZX_DRIVER_ICH },
- /* PCH */
- { PCI_DEVICE(0x8086, 0x3b56), .driver_data = AZX_DRIVER_ICH },
- { PCI_DEVICE(0x8086, 0x3b57), .driver_data = AZX_DRIVER_ICH },
/* CPT */
{ PCI_DEVICE(0x8086, 0x1c20), .driver_data = AZX_DRIVER_PCH },
+ /* PBG */
+ { PCI_DEVICE(0x8086, 0x1d20), .driver_data = AZX_DRIVER_PCH },
/* SCH */
{ PCI_DEVICE(0x8086, 0x811b), .driver_data = AZX_DRIVER_SCH },
+ /* Generic Intel */
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_ANY_ID),
+ .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
+ .class_mask = 0xffffff,
+ .driver_data = AZX_DRIVER_ICH },
/* ATI SB 450/600 */
{ PCI_DEVICE(0x1002, 0x437b), .driver_data = AZX_DRIVER_ATI },
{ PCI_DEVICE(0x1002, 0x4383), .driver_data = AZX_DRIVER_ATI },
@@ -2791,11 +2797,13 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
{ PCI_DEVICE(PCI_VENDOR_ID_CREATIVE, PCI_ANY_ID),
.class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
.class_mask = 0xffffff,
- .driver_data = AZX_DRIVER_GENERIC },
+ .driver_data = AZX_DRIVER_CTX },
#else
/* this entry seems still valid -- i.e. without emu20kx chip */
- { PCI_DEVICE(0x1102, 0x0009), .driver_data = AZX_DRIVER_GENERIC },
+ { PCI_DEVICE(0x1102, 0x0009), .driver_data = AZX_DRIVER_CTX },
#endif
+ /* Vortex86MX */
+ { PCI_DEVICE(0x17f3, 0x3010), .driver_data = AZX_DRIVER_GENERIC },
/* AMD/ATI Generic, PCI class code and Vendor ID for HD Audio */
{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_ANY_ID),
.class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index 28ab4aead48..46bbefe2e4a 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -38,10 +38,11 @@
*/
#define HDA_COMPOSE_AMP_VAL_OFS(nid,chs,idx,dir,ofs) \
((nid) | ((chs)<<16) | ((dir)<<18) | ((idx)<<19) | ((ofs)<<23))
+#define HDA_AMP_VAL_MIN_MUTE (1<<29)
#define HDA_COMPOSE_AMP_VAL(nid,chs,idx,dir) \
HDA_COMPOSE_AMP_VAL_OFS(nid, chs, idx, dir, 0)
/* mono volume with index (index=0,1,...) (channel=1,2) */
-#define HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \
+#define HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, channel, xindex, dir, flags) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \
.subdevice = HDA_SUBDEV_AMP_FLAG, \
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
@@ -51,16 +52,20 @@
.get = snd_hda_mixer_amp_volume_get, \
.put = snd_hda_mixer_amp_volume_put, \
.tlv = { .c = snd_hda_mixer_amp_tlv }, \
- .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, direction) }
+ .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, dir) | flags }
/* stereo volume with index */
#define HDA_CODEC_VOLUME_IDX(xname, xcidx, nid, xindex, direction) \
- HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, 3, xindex, direction)
+ HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, 3, xindex, direction, 0)
/* mono volume */
#define HDA_CODEC_VOLUME_MONO(xname, nid, channel, xindex, direction) \
- HDA_CODEC_VOLUME_MONO_IDX(xname, 0, nid, channel, xindex, direction)
+ HDA_CODEC_VOLUME_MONO_IDX(xname, 0, nid, channel, xindex, direction, 0)
/* stereo volume */
#define HDA_CODEC_VOLUME(xname, nid, xindex, direction) \
HDA_CODEC_VOLUME_MONO(xname, nid, 3, xindex, direction)
+/* stereo volume with min=mute */
+#define HDA_CODEC_VOLUME_MIN_MUTE(xname, nid, xindex, direction) \
+ HDA_CODEC_VOLUME_MONO_IDX(xname, 0, nid, 3, xindex, direction, \
+ HDA_AMP_VAL_MIN_MUTE)
/* mono mute switch with index (index=0,1,...) (channel=1,2) */
#define HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \
@@ -215,7 +220,7 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid);
*/
#define HDA_MAX_NUM_INPUTS 16
struct hda_input_mux_item {
- const char *label;
+ char label[32];
unsigned int index;
};
struct hda_input_mux {
@@ -366,9 +371,7 @@ struct hda_bus_unsolicited {
enum {
AUTO_PIN_MIC,
- AUTO_PIN_FRONT_MIC,
- AUTO_PIN_LINE,
- AUTO_PIN_FRONT_LINE,
+ AUTO_PIN_LINE_IN,
AUTO_PIN_CD,
AUTO_PIN_AUX,
AUTO_PIN_LAST
@@ -380,9 +383,33 @@ enum {
AUTO_PIN_HP_OUT
};
-extern const char *auto_pin_cfg_labels[AUTO_PIN_LAST];
-
#define AUTO_CFG_MAX_OUTS 5
+#define AUTO_CFG_MAX_INS 8
+
+struct auto_pin_cfg_item {
+ hda_nid_t pin;
+ int type;
+};
+
+struct auto_pin_cfg;
+const char *hda_get_input_pin_label(struct hda_codec *codec, hda_nid_t pin,
+ int check_location);
+const char *hda_get_autocfg_input_label(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg,
+ int input);
+int snd_hda_add_imux_item(struct hda_input_mux *imux, const char *label,
+ int index, int *type_index_ret);
+
+enum {
+ INPUT_PIN_ATTR_UNUSED, /* pin not connected */
+ INPUT_PIN_ATTR_INT, /* internal mic/line-in */
+ INPUT_PIN_ATTR_DOCK, /* docking mic/line-in */
+ INPUT_PIN_ATTR_NORMAL, /* mic/line-in jack */
+ INPUT_PIN_ATTR_FRONT, /* mic/line-in jack in front */
+ INPUT_PIN_ATTR_REAR, /* mic/line-in jack in rear */
+};
+
+int snd_hda_get_input_pin_attr(unsigned int def_conf);
struct auto_pin_cfg {
int line_outs;
@@ -393,7 +420,8 @@ struct auto_pin_cfg {
int hp_outs;
int line_out_type; /* AUTO_PIN_XXX_OUT */
hda_nid_t hp_pins[AUTO_CFG_MAX_OUTS];
- hda_nid_t input_pins[AUTO_PIN_LAST];
+ int num_inputs;
+ struct auto_pin_cfg_item inputs[AUTO_CFG_MAX_INS];
int dig_outs;
hda_nid_t dig_out_pins[2];
hda_nid_t dig_in_pin;
@@ -558,6 +586,7 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,
#define get_amp_direction(kc) (((kc)->private_value >> 18) & 0x1)
#define get_amp_index(kc) (((kc)->private_value >> 19) & 0xf)
#define get_amp_offset(kc) (((kc)->private_value >> 23) & 0x3f)
+#define get_amp_min_mute(kc) (((kc)->private_value >> 29) & 0x1)
/*
* CEA Short Audio Descriptor data
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index b697fd2a6f8..f7ff3f7ccb8 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -1276,6 +1276,7 @@ static int patch_ad1986a(struct hda_codec *codec)
spec->multiout.no_share_stream = 1;
codec->no_trigger_sense = 1;
+ codec->no_sticky_stream = 1;
return 0;
}
@@ -1463,6 +1464,7 @@ static int patch_ad1983(struct hda_codec *codec)
codec->patch_ops = ad198x_patch_ops;
codec->no_trigger_sense = 1;
+ codec->no_sticky_stream = 1;
return 0;
}
@@ -1917,6 +1919,7 @@ static int patch_ad1981(struct hda_codec *codec)
}
codec->no_trigger_sense = 1;
+ codec->no_sticky_stream = 1;
return 0;
}
@@ -2880,7 +2883,7 @@ static int ad1988_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
/* create input playback/capture controls for the given pin */
static int new_analog_input(struct ad198x_spec *spec, hda_nid_t pin,
- const char *ctlname, int boost)
+ const char *ctlname, int ctlidx, int boost)
{
char name[32];
int err, idx;
@@ -2909,25 +2912,27 @@ static int new_analog_input(struct ad198x_spec *spec, hda_nid_t pin,
}
/* create playback/capture controls for input pins */
-static int ad1988_auto_create_analog_input_ctls(struct ad198x_spec *spec,
+static int ad1988_auto_create_analog_input_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg)
{
+ struct ad198x_spec *spec = codec->spec;
struct hda_input_mux *imux = &spec->private_imux;
- int i, err;
-
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- err = new_analog_input(spec, cfg->input_pins[i],
- auto_pin_cfg_labels[i],
- i <= AUTO_PIN_FRONT_MIC);
+ int i, err, type, type_idx;
+
+ for (i = 0; i < cfg->num_inputs; i++) {
+ const char *label;
+ type = cfg->inputs[i].type;
+ label = hda_get_autocfg_input_label(codec, cfg, i);
+ snd_hda_add_imux_item(imux, label,
+ ad1988_pin_to_adc_idx(cfg->inputs[i].pin),
+ &type_idx);
+ err = new_analog_input(spec, cfg->inputs[i].pin,
+ label, type_idx,
+ type == AUTO_PIN_MIC);
if (err < 0)
return err;
- imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
- imux->items[imux->num_items].index = ad1988_pin_to_adc_idx(cfg->input_pins[i]);
- imux->num_items++;
}
- imux->items[imux->num_items].label = "Mix";
- imux->items[imux->num_items].index = 9;
- imux->num_items++;
+ snd_hda_add_imux_item(imux, "Mix", 9, NULL);
if ((err = add_control(spec, AD_CTL_WIDGET_VOL,
"Analog Mix Playback Volume",
@@ -2994,12 +2999,11 @@ static void ad1988_auto_init_extra_out(struct hda_codec *codec)
static void ad1988_auto_init_analog_input(struct hda_codec *codec)
{
struct ad198x_spec *spec = codec->spec;
+ const struct auto_pin_cfg *cfg = &spec->autocfg;
int i, idx;
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- hda_nid_t nid = spec->autocfg.input_pins[i];
- if (! nid)
- continue;
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
switch (nid) {
case 0x15: /* port-C */
snd_hda_codec_write(codec, 0x33, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
@@ -3009,7 +3013,7 @@ static void ad1988_auto_init_analog_input(struct hda_codec *codec)
break;
}
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- i <= AUTO_PIN_FRONT_MIC ? PIN_VREF80 : PIN_IN);
+ i == AUTO_PIN_MIC ? PIN_VREF80 : PIN_IN);
if (nid != AD1988_PIN_CD_NID)
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
AMP_OUT_MUTE);
@@ -3040,7 +3044,7 @@ static int ad1988_parse_auto_config(struct hda_codec *codec)
"Speaker")) < 0 ||
(err = ad1988_auto_create_extra_out(codec, spec->autocfg.hp_pins[0],
"Headphone")) < 0 ||
- (err = ad1988_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
+ (err = ad1988_auto_create_analog_input_ctls(codec, &spec->autocfg)) < 0)
return err;
spec->multiout.max_channels = spec->multiout.num_dacs * 2;
@@ -3235,6 +3239,7 @@ static int patch_ad1988(struct hda_codec *codec)
spec->vmaster_nid = 0x04;
codec->no_trigger_sense = 1;
+ codec->no_sticky_stream = 1;
return 0;
}
@@ -3449,6 +3454,7 @@ static int patch_ad1884(struct hda_codec *codec)
codec->patch_ops = ad198x_patch_ops;
codec->no_trigger_sense = 1;
+ codec->no_sticky_stream = 1;
return 0;
}
@@ -3641,6 +3647,7 @@ static struct snd_pci_quirk ad1984_cfg_tbl[] = {
/* Lenovo Thinkpad T61/X61 */
SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1984_THINKPAD),
SND_PCI_QUIRK(0x1028, 0x0214, "Dell T3400", AD1984_DELL_DESKTOP),
+ SND_PCI_QUIRK(0x1028, 0x0233, "Dell Latitude E6400", AD1984_DELL_DESKTOP),
{}
};
@@ -4421,6 +4428,7 @@ static int patch_ad1884a(struct hda_codec *codec)
}
codec->no_trigger_sense = 1;
+ codec->no_sticky_stream = 1;
return 0;
}
@@ -4760,6 +4768,7 @@ static int patch_ad1882(struct hda_codec *codec)
}
codec->no_trigger_sense = 1;
+ codec->no_sticky_stream = 1;
return 0;
}
diff --git a/sound/pci/hda/patch_atihdmi.c b/sound/pci/hda/patch_atihdmi.c
deleted file mode 100644
index fb684f00156..00000000000
--- a/sound/pci/hda/patch_atihdmi.c
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Universal Interface for Intel High Definition Audio Codec
- *
- * HD audio interface patch for ATI HDMI codecs
- *
- * Copyright (c) 2006 ATI Technologies Inc.
- *
- *
- * This driver 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 driver 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
- */
-
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <sound/core.h>
-#include "hda_codec.h"
-#include "hda_local.h"
-
-struct atihdmi_spec {
- struct hda_multi_out multiout;
-
- struct hda_pcm pcm_rec;
-};
-
-#define CVT_NID 0x02 /* audio converter */
-#define PIN_NID 0x03 /* HDMI output pin */
-
-static struct hda_verb atihdmi_basic_init[] = {
- /* enable digital output on pin widget */
- { 0x03, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- {} /* terminator */
-};
-
-/*
- * Controls
- */
-static int atihdmi_build_controls(struct hda_codec *codec)
-{
- struct atihdmi_spec *spec = codec->spec;
- int err;
-
- err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
- if (err < 0)
- return err;
-
- return 0;
-}
-
-static int atihdmi_init(struct hda_codec *codec)
-{
- snd_hda_sequence_write(codec, atihdmi_basic_init);
- /* SI codec requires to unmute the pin */
- if (get_wcaps(codec, PIN_NID) & AC_WCAP_OUT_AMP)
- snd_hda_codec_write(codec, PIN_NID, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_UNMUTE);
- return 0;
-}
-
-/*
- * Digital out
- */
-static int atihdmi_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct atihdmi_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_open(codec, &spec->multiout);
-}
-
-static int atihdmi_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct atihdmi_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_close(codec, &spec->multiout);
-}
-
-static int atihdmi_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct atihdmi_spec *spec = codec->spec;
- int chans = substream->runtime->channels;
- int i, err;
-
- err = snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
- format, substream);
- if (err < 0)
- return err;
- snd_hda_codec_write(codec, CVT_NID, 0, AC_VERB_SET_CVT_CHAN_COUNT,
- chans - 1);
- /* FIXME: XXX */
- for (i = 0; i < chans; i++) {
- snd_hda_codec_write(codec, CVT_NID, 0,
- AC_VERB_SET_HDMI_CHAN_SLOT,
- (i << 4) | i);
- }
- return 0;
-}
-
-static struct hda_pcm_stream atihdmi_pcm_digital_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .nid = CVT_NID, /* NID to query formats and rates and setup streams */
- .ops = {
- .open = atihdmi_dig_playback_pcm_open,
- .close = atihdmi_dig_playback_pcm_close,
- .prepare = atihdmi_dig_playback_pcm_prepare
- },
-};
-
-static int atihdmi_build_pcms(struct hda_codec *codec)
-{
- struct atihdmi_spec *spec = codec->spec;
- struct hda_pcm *info = &spec->pcm_rec;
- unsigned int chans;
-
- codec->num_pcms = 1;
- codec->pcm_info = info;
-
- info->name = "ATI HDMI";
- info->pcm_type = HDA_PCM_TYPE_HDMI;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] = atihdmi_pcm_digital_playback;
-
- /* FIXME: we must check ELD and change the PCM parameters dynamically
- */
- chans = get_wcaps(codec, CVT_NID);
- chans = get_wcaps_channels(chans);
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = chans;
-
- return 0;
-}
-
-static void atihdmi_free(struct hda_codec *codec)
-{
- kfree(codec->spec);
-}
-
-static struct hda_codec_ops atihdmi_patch_ops = {
- .build_controls = atihdmi_build_controls,
- .build_pcms = atihdmi_build_pcms,
- .init = atihdmi_init,
- .free = atihdmi_free,
-};
-
-static int patch_atihdmi(struct hda_codec *codec)
-{
- struct atihdmi_spec *spec;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
-
- spec->multiout.num_dacs = 0; /* no analog */
- spec->multiout.max_channels = 2;
- /* NID for copying analog to digital,
- * seems to be unused in pure-digital
- * case.
- */
- spec->multiout.dig_out_nid = CVT_NID;
-
- codec->patch_ops = atihdmi_patch_ops;
-
- return 0;
-}
-
-/*
- * patch entries
- */
-static struct hda_codec_preset snd_hda_preset_atihdmi[] = {
- { .id = 0x1002793c, .name = "RS600 HDMI", .patch = patch_atihdmi },
- { .id = 0x10027919, .name = "RS600 HDMI", .patch = patch_atihdmi },
- { .id = 0x1002791a, .name = "RS690/780 HDMI", .patch = patch_atihdmi },
- { .id = 0x1002aa01, .name = "R6xx HDMI", .patch = patch_atihdmi },
- { .id = 0x10951390, .name = "SiI1390 HDMI", .patch = patch_atihdmi },
- { .id = 0x17e80047, .name = "Chrontel HDMI", .patch = patch_atihdmi },
- {} /* terminator */
-};
-
-MODULE_ALIAS("snd-hda-codec-id:1002793c");
-MODULE_ALIAS("snd-hda-codec-id:10027919");
-MODULE_ALIAS("snd-hda-codec-id:1002791a");
-MODULE_ALIAS("snd-hda-codec-id:1002aa01");
-MODULE_ALIAS("snd-hda-codec-id:10951390");
-MODULE_ALIAS("snd-hda-codec-id:17e80047");
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("ATI HDMI HD-audio codec");
-
-static struct hda_codec_preset_list atihdmi_list = {
- .preset = snd_hda_preset_atihdmi,
- .owner = THIS_MODULE,
-};
-
-static int __init patch_atihdmi_init(void)
-{
- return snd_hda_add_codec_preset(&atihdmi_list);
-}
-
-static void __exit patch_atihdmi_exit(void)
-{
- snd_hda_delete_codec_preset(&atihdmi_list);
-}
-
-module_init(patch_atihdmi_init)
-module_exit(patch_atihdmi_exit)
diff --git a/sound/pci/hda/patch_ca0110.c b/sound/pci/hda/patch_ca0110.c
index af478019088..46c8bf48c31 100644
--- a/sound/pci/hda/patch_ca0110.c
+++ b/sound/pci/hda/patch_ca0110.c
@@ -468,13 +468,13 @@ static void parse_input(struct hda_codec *codec)
spec->dig_in = nid;
continue;
}
- for (j = 0; j < AUTO_PIN_LAST; j++)
- if (cfg->input_pins[j] == pin)
+ for (j = 0; j < cfg->num_inputs; j++)
+ if (cfg->inputs[j].pin == pin)
break;
- if (j >= AUTO_PIN_LAST)
+ if (j >= cfg->num_inputs)
continue;
spec->input_pins[n] = pin;
- spec->input_labels[n] = auto_pin_cfg_labels[j];
+ spec->input_labels[n] = hda_get_input_pin_label(codec, pin, 1);
spec->adcs[n] = nid;
n++;
}
@@ -489,7 +489,7 @@ static void parse_digital(struct hda_codec *codec)
if (cfg->dig_outs &&
snd_hda_get_connections(codec, cfg->dig_out_pins[0],
&spec->dig_out, 1) == 1)
- spec->multiout.dig_out_nid = cfg->dig_out_pins[0];
+ spec->multiout.dig_out_nid = spec->dig_out;
}
static int ca0110_parse_auto_config(struct hda_codec *codec)
diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c
index 4ef5efaaaef..460fb2ef7e3 100644
--- a/sound/pci/hda/patch_cirrus.c
+++ b/sound/pci/hda/patch_cirrus.c
@@ -65,6 +65,7 @@ struct cs_spec {
/* available models */
enum {
+ CS420X_MBP53,
CS420X_MBP55,
CS420X_IMAC27,
CS420X_AUTO,
@@ -329,12 +330,12 @@ static int is_ext_mic(struct hda_codec *codec, unsigned int idx)
{
struct cs_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
- hda_nid_t pin = cfg->input_pins[idx];
+ hda_nid_t pin = cfg->inputs[idx].pin;
unsigned int val = snd_hda_query_pin_caps(codec, pin);
if (!(val & AC_PINCAP_PRES_DETECT))
return 0;
val = snd_hda_codec_get_pincfg(codec, pin);
- return (get_defcfg_connect(val) == AC_JACK_PORT_COMPLEX);
+ return (snd_hda_get_input_pin_attr(val) != INPUT_PIN_ATTR_INT);
}
static hda_nid_t get_adc(struct hda_codec *codec, hda_nid_t pin,
@@ -424,10 +425,8 @@ static int parse_input(struct hda_codec *codec)
struct auto_pin_cfg *cfg = &spec->autocfg;
int i;
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- hda_nid_t pin = cfg->input_pins[i];
- if (!pin)
- continue;
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t pin = cfg->inputs[i].pin;
spec->input_idx[spec->num_inputs] = i;
spec->capsrc_idx[i] = spec->num_inputs++;
spec->cur_input = i;
@@ -438,16 +437,17 @@ static int parse_input(struct hda_codec *codec)
/* check whether the automatic mic switch is available */
if (spec->num_inputs == 2 &&
- spec->adc_nid[AUTO_PIN_MIC] && spec->adc_nid[AUTO_PIN_FRONT_MIC]) {
- if (is_ext_mic(codec, cfg->input_pins[AUTO_PIN_FRONT_MIC])) {
- if (!is_ext_mic(codec, cfg->input_pins[AUTO_PIN_MIC])) {
+ cfg->inputs[0].type == AUTO_PIN_MIC &&
+ cfg->inputs[1].type == AUTO_PIN_MIC) {
+ if (is_ext_mic(codec, cfg->inputs[0].pin)) {
+ if (!is_ext_mic(codec, cfg->inputs[1].pin)) {
spec->mic_detect = 1;
- spec->automic_idx = AUTO_PIN_FRONT_MIC;
+ spec->automic_idx = 0;
}
} else {
- if (is_ext_mic(codec, cfg->input_pins[AUTO_PIN_MIC])) {
+ if (is_ext_mic(codec, cfg->inputs[1].pin)) {
spec->mic_detect = 1;
- spec->automic_idx = AUTO_PIN_MIC;
+ spec->automic_idx = 1;
}
}
}
@@ -674,6 +674,7 @@ static int cs_capture_source_info(struct snd_kcontrol *kcontrol,
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct cs_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
unsigned int idx;
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
@@ -682,7 +683,8 @@ static int cs_capture_source_info(struct snd_kcontrol *kcontrol,
if (uinfo->value.enumerated.item >= spec->num_inputs)
uinfo->value.enumerated.item = spec->num_inputs - 1;
idx = spec->input_idx[uinfo->value.enumerated.item];
- strcpy(uinfo->value.enumerated.name, auto_pin_cfg_labels[idx]);
+ strcpy(uinfo->value.enumerated.name,
+ hda_get_input_pin_label(codec, cfg->inputs[idx].pin, 1));
return 0;
}
@@ -740,6 +742,27 @@ static struct hda_bind_ctls *make_bind_capture(struct hda_codec *codec,
return bind;
}
+/* add a (input-boost) volume control to the given input pin */
+static int add_input_volume_control(struct hda_codec *codec,
+ struct auto_pin_cfg *cfg,
+ int item)
+{
+ hda_nid_t pin = cfg->inputs[item].pin;
+ u32 caps;
+ const char *label;
+ struct snd_kcontrol *kctl;
+
+ if (!(get_wcaps(codec, pin) & AC_WCAP_IN_AMP))
+ return 0;
+ caps = query_amp_caps(codec, pin, HDA_INPUT);
+ caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
+ if (caps <= 1)
+ return 0;
+ label = hda_get_autocfg_input_label(codec, cfg, item);
+ return add_volume(codec, label, 0,
+ HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT), 1, &kctl);
+}
+
static int build_input(struct hda_codec *codec)
{
struct cs_spec *spec = codec->spec;
@@ -779,6 +802,12 @@ static int build_input(struct hda_codec *codec)
return err;
}
+ for (i = 0; i < spec->num_inputs; i++) {
+ err = add_input_volume_control(codec, &spec->autocfg, i);
+ if (err < 0)
+ return err;
+ }
+
return 0;
}
@@ -838,7 +867,8 @@ static void cs_automute(struct hda_codec *codec)
AC_VERB_SET_PIN_WIDGET_CONTROL,
hp_present ? 0 : PIN_OUT);
}
- if (spec->board_config == CS420X_MBP55 ||
+ if (spec->board_config == CS420X_MBP53 ||
+ spec->board_config == CS420X_MBP55 ||
spec->board_config == CS420X_IMAC27) {
unsigned int gpio = hp_present ? 0x02 : 0x08;
snd_hda_codec_write(codec, 0x01, 0,
@@ -853,15 +883,12 @@ static void cs_automic(struct hda_codec *codec)
hda_nid_t nid;
unsigned int present;
- nid = cfg->input_pins[spec->automic_idx];
+ nid = cfg->inputs[spec->automic_idx].pin;
present = snd_hda_jack_detect(codec, nid);
if (present)
change_cur_input(codec, spec->automic_idx, 0);
- else {
- unsigned int imic = (spec->automic_idx == AUTO_PIN_MIC) ?
- AUTO_PIN_FRONT_MIC : AUTO_PIN_MIC;
- change_cur_input(codec, imic, 0);
- }
+ else
+ change_cur_input(codec, !spec->automic_idx, 0);
}
/*
@@ -918,14 +945,14 @@ static void init_input(struct hda_codec *codec)
unsigned int coef;
int i;
- for (i = 0; i < AUTO_PIN_LAST; i++) {
+ for (i = 0; i < cfg->num_inputs; i++) {
unsigned int ctl;
- hda_nid_t pin = cfg->input_pins[i];
- if (!pin || !spec->adc_nid[i])
+ hda_nid_t pin = cfg->inputs[i].pin;
+ if (!spec->adc_nid[i])
continue;
/* set appropriate pin control and mute first */
ctl = PIN_IN;
- if (i <= AUTO_PIN_FRONT_MIC) {
+ if (cfg->inputs[i].type == AUTO_PIN_MIC) {
unsigned int caps = snd_hda_query_pin_caps(codec, pin);
caps >>= AC_PINCAP_VREF_SHIFT;
if (caps & AC_PINCAP_VREF_80)
@@ -972,6 +999,53 @@ static struct hda_verb cs_coef_init_verbs[] = {
{} /* terminator */
};
+/* Errata: CS4207 rev C0/C1/C2 Silicon
+ *
+ * http://www.cirrus.com/en/pubs/errata/ER880C3.pdf
+ *
+ * 6. At high temperature (TA > +85°C), the digital supply current (IVD)
+ * may be excessive (up to an additional 200 μA), which is most easily
+ * observed while the part is being held in reset (RESET# active low).
+ *
+ * Root Cause: At initial powerup of the device, the logic that drives
+ * the clock and write enable to the S/PDIF SRC RAMs is not properly
+ * initialized.
+ * Certain random patterns will cause a steady leakage current in those
+ * RAM cells. The issue will resolve once the SRCs are used (turned on).
+ *
+ * Workaround: The following verb sequence briefly turns on the S/PDIF SRC
+ * blocks, which will alleviate the issue.
+ */
+
+static struct hda_verb cs_errata_init_verbs[] = {
+ {0x01, AC_VERB_SET_POWER_STATE, 0x00}, /* AFG: D0 */
+ {0x11, AC_VERB_SET_PROC_STATE, 0x01}, /* VPW: processing on */
+
+ {0x11, AC_VERB_SET_COEF_INDEX, 0x0008},
+ {0x11, AC_VERB_SET_PROC_COEF, 0x9999},
+ {0x11, AC_VERB_SET_COEF_INDEX, 0x0017},
+ {0x11, AC_VERB_SET_PROC_COEF, 0xa412},
+ {0x11, AC_VERB_SET_COEF_INDEX, 0x0001},
+ {0x11, AC_VERB_SET_PROC_COEF, 0x0009},
+
+ {0x07, AC_VERB_SET_POWER_STATE, 0x00}, /* S/PDIF Rx: D0 */
+ {0x08, AC_VERB_SET_POWER_STATE, 0x00}, /* S/PDIF Tx: D0 */
+
+ {0x11, AC_VERB_SET_COEF_INDEX, 0x0017},
+ {0x11, AC_VERB_SET_PROC_COEF, 0x2412},
+ {0x11, AC_VERB_SET_COEF_INDEX, 0x0008},
+ {0x11, AC_VERB_SET_PROC_COEF, 0x0000},
+ {0x11, AC_VERB_SET_COEF_INDEX, 0x0001},
+ {0x11, AC_VERB_SET_PROC_COEF, 0x0008},
+ {0x11, AC_VERB_SET_PROC_STATE, 0x00},
+
+ {0x07, AC_VERB_SET_POWER_STATE, 0x03}, /* S/PDIF Rx: D3 */
+ {0x08, AC_VERB_SET_POWER_STATE, 0x03}, /* S/PDIF Tx: D3 */
+ /*{0x01, AC_VERB_SET_POWER_STATE, 0x03},*/ /* AFG: D3 This is already handled */
+
+ {} /* terminator */
+};
+
/* SPDIF setup */
static void init_digital(struct hda_codec *codec)
{
@@ -991,6 +1065,9 @@ static int cs_init(struct hda_codec *codec)
{
struct cs_spec *spec = codec->spec;
+ /* init_verb sequence for C0/C1/C2 errata*/
+ snd_hda_sequence_write(codec, cs_errata_init_verbs);
+
snd_hda_sequence_write(codec, cs_coef_init_verbs);
if (spec->gpio_mask) {
@@ -1080,6 +1157,7 @@ static int cs_parse_auto_config(struct hda_codec *codec)
}
static const char *cs420x_models[CS420X_MODELS] = {
+ [CS420X_MBP53] = "mbp53",
[CS420X_MBP55] = "mbp55",
[CS420X_IMAC27] = "imac27",
[CS420X_AUTO] = "auto",
@@ -1087,7 +1165,9 @@ static const char *cs420x_models[CS420X_MODELS] = {
static struct snd_pci_quirk cs420x_cfg_tbl[] = {
+ SND_PCI_QUIRK(0x10de, 0x0ac0, "MacBookPro 5,3", CS420X_MBP53),
SND_PCI_QUIRK(0x10de, 0xcb79, "MacBookPro 5,5", CS420X_MBP55),
+ SND_PCI_QUIRK(0x10de, 0xcb89, "MacBookPro 7,1", CS420X_MBP55),
SND_PCI_QUIRK(0x8086, 0x7270, "IMac 27 Inch", CS420X_IMAC27),
{} /* terminator */
};
@@ -1097,6 +1177,20 @@ struct cs_pincfg {
u32 val;
};
+static struct cs_pincfg mbp53_pincfgs[] = {
+ { 0x09, 0x012b4050 },
+ { 0x0a, 0x90100141 },
+ { 0x0b, 0x90100140 },
+ { 0x0c, 0x018b3020 },
+ { 0x0d, 0x90a00110 },
+ { 0x0e, 0x400000f0 },
+ { 0x0f, 0x01cbe030 },
+ { 0x10, 0x014be060 },
+ { 0x12, 0x400000f0 },
+ { 0x15, 0x400000f0 },
+ {} /* terminator */
+};
+
static struct cs_pincfg mbp55_pincfgs[] = {
{ 0x09, 0x012b4030 },
{ 0x0a, 0x90100121 },
@@ -1126,6 +1220,7 @@ static struct cs_pincfg imac27_pincfgs[] = {
};
static struct cs_pincfg *cs_pincfgs[CS420X_MODELS] = {
+ [CS420X_MBP53] = mbp53_pincfgs,
[CS420X_MBP55] = mbp55_pincfgs,
[CS420X_IMAC27] = imac27_pincfgs,
};
@@ -1158,6 +1253,7 @@ static int patch_cs420x(struct hda_codec *codec)
switch (spec->board_config) {
case CS420X_IMAC27:
+ case CS420X_MBP53:
case CS420X_MBP55:
/* GPIO1 = headphones */
/* GPIO3 = speakers */
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 31b5d9eeba6..6361f752b5f 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -57,6 +57,12 @@ struct conexant_jack {
};
+struct pin_dac_pair {
+ hda_nid_t pin;
+ hda_nid_t dac;
+ int type;
+};
+
struct conexant_spec {
struct snd_kcontrol_new *mixers[5];
@@ -77,6 +83,7 @@ struct conexant_spec {
unsigned int cur_eapd;
unsigned int hp_present;
unsigned int auto_mic;
+ int auto_mic_ext; /* autocfg.inputs[] index for ext mic */
unsigned int need_dac_fix;
/* capture */
@@ -110,12 +117,16 @@ struct conexant_spec {
struct auto_pin_cfg autocfg;
struct hda_input_mux private_imux;
hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
+ struct pin_dac_pair dac_info[8];
+ int dac_info_filled;
- unsigned int dell_automute;
unsigned int port_d_mode;
+ unsigned int auto_mute:1; /* used in auto-parser */
+ unsigned int dell_automute:1;
unsigned int dell_vostro:1;
unsigned int ideapad:1;
unsigned int thinkpad:1;
+ unsigned int hp_laptop:1;
unsigned int ext_mic_present;
unsigned int recording;
@@ -2299,6 +2310,18 @@ static void cxt5066_ideapad_automic(struct hda_codec *codec)
}
}
+/* toggle input of built-in digital mic and mic jack appropriately */
+static void cxt5066_hp_laptop_automic(struct hda_codec *codec)
+{
+ unsigned int present;
+
+ present = snd_hda_jack_detect(codec, 0x1b);
+ snd_printdd("CXT5066: external microphone present=%d\n", present);
+ snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_CONNECT_SEL,
+ present ? 1 : 3);
+}
+
+
/* toggle input of built-in digital mic and mic jack appropriately
order is: external mic -> dock mic -> interal mic */
static void cxt5066_thinkpad_automic(struct hda_codec *codec)
@@ -2408,6 +2431,20 @@ static void cxt5066_ideapad_event(struct hda_codec *codec, unsigned int res)
}
/* unsolicited event for jack sensing */
+static void cxt5066_hp_laptop_event(struct hda_codec *codec, unsigned int res)
+{
+ snd_printdd("CXT5066_hp_laptop: unsol event %x (%x)\n", res, res >> 26);
+ switch (res >> 26) {
+ case CONEXANT_HP_EVENT:
+ cxt5066_hp_automute(codec);
+ break;
+ case CONEXANT_MIC_EVENT:
+ cxt5066_hp_laptop_automic(codec);
+ break;
+ }
+}
+
+/* unsolicited event for jack sensing */
static void cxt5066_thinkpad_event(struct hda_codec *codec, unsigned int res)
{
snd_printdd("CXT5066_thinkpad: unsol event %x (%x)\n", res, res >> 26);
@@ -2989,6 +3026,14 @@ static struct hda_verb cxt5066_init_verbs_portd_lo[] = {
{ } /* end */
};
+
+static struct hda_verb cxt5066_init_verbs_hp_laptop[] = {
+ {0x14, AC_VERB_SET_CONNECT_SEL, 0x0},
+ {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
+ {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT},
+ { } /* end */
+};
+
/* initialize jack-sensing, too */
static int cxt5066_init(struct hda_codec *codec)
{
@@ -3004,6 +3049,8 @@ static int cxt5066_init(struct hda_codec *codec)
cxt5066_ideapad_automic(codec);
else if (spec->thinkpad)
cxt5066_thinkpad_automic(codec);
+ else if (spec->hp_laptop)
+ cxt5066_hp_laptop_automic(codec);
}
cxt5066_set_mic_boost(codec);
return 0;
@@ -3028,9 +3075,10 @@ enum {
CXT5066_LAPTOP, /* Laptops w/ EAPD support */
CXT5066_DELL_LAPTOP, /* Dell Laptop */
CXT5066_OLPC_XO_1_5, /* OLPC XO 1.5 */
- CXT5066_DELL_VOSTO, /* Dell Vostro 1015i */
+ CXT5066_DELL_VOSTRO, /* Dell Vostro 1015i */
CXT5066_IDEAPAD, /* Lenovo IdeaPad U150 */
CXT5066_THINKPAD, /* Lenovo ThinkPad T410s, others? */
+ CXT5066_HP_LAPTOP, /* HP Laptop */
CXT5066_MODELS
};
@@ -3038,26 +3086,33 @@ static const char *cxt5066_models[CXT5066_MODELS] = {
[CXT5066_LAPTOP] = "laptop",
[CXT5066_DELL_LAPTOP] = "dell-laptop",
[CXT5066_OLPC_XO_1_5] = "olpc-xo-1_5",
- [CXT5066_DELL_VOSTO] = "dell-vostro",
+ [CXT5066_DELL_VOSTRO] = "dell-vostro",
[CXT5066_IDEAPAD] = "ideapad",
[CXT5066_THINKPAD] = "thinkpad",
+ [CXT5066_HP_LAPTOP] = "hp-laptop",
};
static struct snd_pci_quirk cxt5066_cfg_tbl[] = {
- SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board",
- CXT5066_LAPTOP),
+ SND_PCI_QUIRK_MASK(0x1025, 0xff00, 0x0400, "Acer", CXT5066_IDEAPAD),
+ SND_PCI_QUIRK(0x1028, 0x02d8, "Dell Vostro", CXT5066_DELL_VOSTRO),
SND_PCI_QUIRK(0x1028, 0x02f5, "Dell",
CXT5066_DELL_LAPTOP),
- SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT5066_OLPC_XO_1_5),
- SND_PCI_QUIRK(0x1028, 0x0402, "Dell Vostro", CXT5066_DELL_VOSTO),
+ SND_PCI_QUIRK(0x1028, 0x0402, "Dell Vostro", CXT5066_DELL_VOSTRO),
SND_PCI_QUIRK(0x1028, 0x0408, "Dell Inspiron One 19T", CXT5066_IDEAPAD),
+ SND_PCI_QUIRK(0x103c, 0x360b, "HP G60", CXT5066_HP_LAPTOP),
+ SND_PCI_QUIRK(0x1179, 0xff1e, "Toshiba Satellite C650D", CXT5066_IDEAPAD),
SND_PCI_QUIRK(0x1179, 0xff50, "Toshiba Satellite P500-PSPGSC-01800T", CXT5066_OLPC_XO_1_5),
SND_PCI_QUIRK(0x1179, 0xffe0, "Toshiba Satellite Pro T130-15F", CXT5066_OLPC_XO_1_5),
+ SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board",
+ CXT5066_LAPTOP),
+ SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT5066_OLPC_XO_1_5),
+ SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400s", CXT5066_THINKPAD),
SND_PCI_QUIRK(0x17aa, 0x21b2, "Thinkpad X100e", CXT5066_IDEAPAD),
SND_PCI_QUIRK(0x17aa, 0x21b3, "Thinkpad Edge 13 (197)", CXT5066_IDEAPAD),
SND_PCI_QUIRK(0x17aa, 0x21b4, "Thinkpad Edge", CXT5066_IDEAPAD),
SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo Thinkpad", CXT5066_THINKPAD),
SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo G series", CXT5066_IDEAPAD),
+ SND_PCI_QUIRK(0x17aa, 0x390a, "Lenovo S10-3t", CXT5066_IDEAPAD),
SND_PCI_QUIRK(0x17aa, 0x3938, "Lenovo G series (AMD)", CXT5066_IDEAPAD),
SND_PCI_QUIRK(0x17aa, 0x3a0d, "ideapad", CXT5066_IDEAPAD),
{}
@@ -3114,6 +3169,23 @@ static int patch_cxt5066(struct hda_codec *codec)
spec->num_init_verbs++;
spec->dell_automute = 1;
break;
+ case CXT5066_HP_LAPTOP:
+ codec->patch_ops.init = cxt5066_init;
+ codec->patch_ops.unsol_event = cxt5066_hp_laptop_event;
+ spec->init_verbs[spec->num_init_verbs] =
+ cxt5066_init_verbs_hp_laptop;
+ spec->num_init_verbs++;
+ spec->hp_laptop = 1;
+ spec->mixers[spec->num_mixers++] = cxt5066_mixer_master;
+ spec->mixers[spec->num_mixers++] = cxt5066_mixers;
+ /* no S/PDIF out */
+ spec->multiout.dig_out_nid = 0;
+ /* input source automatically selected */
+ spec->input_mux = NULL;
+ spec->port_d_mode = 0;
+ spec->mic_boost = 3; /* default 30dB gain */
+ break;
+
case CXT5066_OLPC_XO_1_5:
codec->patch_ops.init = cxt5066_olpc_init;
codec->patch_ops.unsol_event = cxt5066_olpc_unsol_event;
@@ -3135,7 +3207,7 @@ static int patch_cxt5066(struct hda_codec *codec)
spec->capture_prepare = cxt5066_olpc_capture_prepare;
spec->capture_cleanup = cxt5066_olpc_capture_cleanup;
break;
- case CXT5066_DELL_VOSTO:
+ case CXT5066_DELL_VOSTRO:
codec->patch_ops.init = cxt5066_init;
codec->patch_ops.unsol_event = cxt5066_vostro_event;
spec->init_verbs[0] = cxt5066_init_verbs_vostro;
@@ -3193,6 +3265,604 @@ static int patch_cxt5066(struct hda_codec *codec)
}
/*
+ * Automatic parser for CX20641 & co
+ */
+
+static hda_nid_t cx_auto_adc_nids[] = { 0x14 };
+
+/* get the connection index of @nid in the widget @mux */
+static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
+ hda_nid_t nid)
+{
+ hda_nid_t conn[HDA_MAX_NUM_INPUTS];
+ int i, nums;
+
+ nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
+ for (i = 0; i < nums; i++)
+ if (conn[i] == nid)
+ return i;
+ return -1;
+}
+
+/* get an unassigned DAC from the given list.
+ * Return the nid if found and reduce the DAC list, or return zero if
+ * not found
+ */
+static hda_nid_t get_unassigned_dac(struct hda_codec *codec, hda_nid_t pin,
+ hda_nid_t *dacs, int *num_dacs)
+{
+ int i, nums = *num_dacs;
+ hda_nid_t ret = 0;
+
+ for (i = 0; i < nums; i++) {
+ if (get_connection_index(codec, pin, dacs[i]) >= 0) {
+ ret = dacs[i];
+ break;
+ }
+ }
+ if (!ret)
+ return 0;
+ if (--nums > 0)
+ memmove(dacs, dacs + 1, nums * sizeof(hda_nid_t));
+ *num_dacs = nums;
+ return ret;
+}
+
+#define MAX_AUTO_DACS 5
+
+/* fill analog DAC list from the widget tree */
+static int fill_cx_auto_dacs(struct hda_codec *codec, hda_nid_t *dacs)
+{
+ hda_nid_t nid, end_nid;
+ int nums = 0;
+
+ end_nid = codec->start_nid + codec->num_nodes;
+ for (nid = codec->start_nid; nid < end_nid; nid++) {
+ unsigned int wcaps = get_wcaps(codec, nid);
+ unsigned int type = get_wcaps_type(wcaps);
+ if (type == AC_WID_AUD_OUT && !(wcaps & AC_WCAP_DIGITAL)) {
+ dacs[nums++] = nid;
+ if (nums >= MAX_AUTO_DACS)
+ break;
+ }
+ }
+ return nums;
+}
+
+/* fill pin_dac_pair list from the pin and dac list */
+static int fill_dacs_for_pins(struct hda_codec *codec, hda_nid_t *pins,
+ int num_pins, hda_nid_t *dacs, int *rest,
+ struct pin_dac_pair *filled, int type)
+{
+ int i, nums;
+
+ nums = 0;
+ for (i = 0; i < num_pins; i++) {
+ filled[nums].pin = pins[i];
+ filled[nums].type = type;
+ filled[nums].dac = get_unassigned_dac(codec, pins[i], dacs, rest);
+ nums++;
+ }
+ return nums;
+}
+
+/* parse analog output paths */
+static void cx_auto_parse_output(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ hda_nid_t dacs[MAX_AUTO_DACS];
+ int i, j, nums, rest;
+
+ rest = fill_cx_auto_dacs(codec, dacs);
+ /* parse all analog output pins */
+ nums = fill_dacs_for_pins(codec, cfg->line_out_pins, cfg->line_outs,
+ dacs, &rest, spec->dac_info,
+ AUTO_PIN_LINE_OUT);
+ nums += fill_dacs_for_pins(codec, cfg->hp_pins, cfg->hp_outs,
+ dacs, &rest, spec->dac_info + nums,
+ AUTO_PIN_HP_OUT);
+ nums += fill_dacs_for_pins(codec, cfg->speaker_pins, cfg->speaker_outs,
+ dacs, &rest, spec->dac_info + nums,
+ AUTO_PIN_SPEAKER_OUT);
+ spec->dac_info_filled = nums;
+ /* fill multiout struct */
+ for (i = 0; i < nums; i++) {
+ hda_nid_t dac = spec->dac_info[i].dac;
+ if (!dac)
+ continue;
+ switch (spec->dac_info[i].type) {
+ case AUTO_PIN_LINE_OUT:
+ spec->private_dac_nids[spec->multiout.num_dacs] = dac;
+ spec->multiout.num_dacs++;
+ break;
+ case AUTO_PIN_HP_OUT:
+ case AUTO_PIN_SPEAKER_OUT:
+ if (!spec->multiout.hp_nid) {
+ spec->multiout.hp_nid = dac;
+ break;
+ }
+ for (j = 0; j < ARRAY_SIZE(spec->multiout.extra_out_nid); j++)
+ if (!spec->multiout.extra_out_nid[j]) {
+ spec->multiout.extra_out_nid[j] = dac;
+ break;
+ }
+ break;
+ }
+ }
+ spec->multiout.dac_nids = spec->private_dac_nids;
+ spec->multiout.max_channels = nums * 2;
+
+ if (cfg->hp_outs > 0)
+ spec->auto_mute = 1;
+ spec->vmaster_nid = spec->private_dac_nids[0];
+}
+
+/* auto-mute/unmute speaker and line outs according to headphone jack */
+static void cx_auto_hp_automute(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i, present;
+
+ if (!spec->auto_mute)
+ return;
+ present = 0;
+ for (i = 0; i < cfg->hp_outs; i++) {
+ if (snd_hda_jack_detect(codec, cfg->hp_pins[i])) {
+ present = 1;
+ break;
+ }
+ }
+ for (i = 0; i < cfg->line_outs; i++) {
+ snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL,
+ present ? 0 : PIN_OUT);
+ }
+ for (i = 0; i < cfg->speaker_outs; i++) {
+ snd_hda_codec_write(codec, cfg->speaker_pins[i], 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL,
+ present ? 0 : PIN_OUT);
+ }
+}
+
+/* automatic switch internal and external mic */
+static void cx_auto_automic(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ struct hda_input_mux *imux = &spec->private_imux;
+ int ext_idx = spec->auto_mic_ext;
+
+ if (!spec->auto_mic)
+ return;
+ if (snd_hda_jack_detect(codec, cfg->inputs[ext_idx].pin)) {
+ snd_hda_codec_write(codec, spec->adc_nids[0], 0,
+ AC_VERB_SET_CONNECT_SEL,
+ imux->items[ext_idx].index);
+ } else {
+ snd_hda_codec_write(codec, spec->adc_nids[0], 0,
+ AC_VERB_SET_CONNECT_SEL,
+ imux->items[!ext_idx].index);
+ }
+}
+
+static void cx_auto_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+ int nid = (res & AC_UNSOL_RES_SUBTAG) >> 20;
+ switch (res >> 26) {
+ case CONEXANT_HP_EVENT:
+ cx_auto_hp_automute(codec);
+ conexant_report_jack(codec, nid);
+ break;
+ case CONEXANT_MIC_EVENT:
+ cx_auto_automic(codec);
+ conexant_report_jack(codec, nid);
+ break;
+ }
+}
+
+/* return true if it's an internal-mic pin */
+static int is_int_mic(struct hda_codec *codec, hda_nid_t pin)
+{
+ unsigned int def_conf = snd_hda_codec_get_pincfg(codec, pin);
+ return get_defcfg_device(def_conf) == AC_JACK_MIC_IN &&
+ snd_hda_get_input_pin_attr(def_conf) == INPUT_PIN_ATTR_INT;
+}
+
+/* return true if it's an external-mic pin */
+static int is_ext_mic(struct hda_codec *codec, hda_nid_t pin)
+{
+ unsigned int def_conf = snd_hda_codec_get_pincfg(codec, pin);
+ return get_defcfg_device(def_conf) == AC_JACK_MIC_IN &&
+ snd_hda_get_input_pin_attr(def_conf) >= INPUT_PIN_ATTR_NORMAL &&
+ (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_PRES_DETECT);
+}
+
+/* check whether the pin config is suitable for auto-mic switching;
+ * auto-mic is enabled only when one int-mic and one-ext mic exist
+ */
+static void cx_auto_check_auto_mic(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+
+ if (is_ext_mic(codec, cfg->inputs[0].pin) &&
+ is_int_mic(codec, cfg->inputs[1].pin)) {
+ spec->auto_mic = 1;
+ spec->auto_mic_ext = 1;
+ return;
+ }
+ if (is_int_mic(codec, cfg->inputs[1].pin) &&
+ is_ext_mic(codec, cfg->inputs[0].pin)) {
+ spec->auto_mic = 1;
+ spec->auto_mic_ext = 0;
+ return;
+ }
+}
+
+static void cx_auto_parse_input(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ struct hda_input_mux *imux;
+ int i;
+
+ imux = &spec->private_imux;
+ for (i = 0; i < cfg->num_inputs; i++) {
+ int idx = get_connection_index(codec, spec->adc_nids[0],
+ cfg->inputs[i].pin);
+ if (idx >= 0) {
+ const char *label;
+ label = hda_get_autocfg_input_label(codec, cfg, i);
+ snd_hda_add_imux_item(imux, label, idx, NULL);
+ }
+ }
+ if (imux->num_items == 2 && cfg->num_inputs == 2)
+ cx_auto_check_auto_mic(codec);
+ if (imux->num_items > 1 && !spec->auto_mic)
+ spec->input_mux = imux;
+}
+
+/* get digital-input audio widget corresponding to the given pin */
+static hda_nid_t cx_auto_get_dig_in(struct hda_codec *codec, hda_nid_t pin)
+{
+ hda_nid_t nid, end_nid;
+
+ end_nid = codec->start_nid + codec->num_nodes;
+ for (nid = codec->start_nid; nid < end_nid; nid++) {
+ unsigned int wcaps = get_wcaps(codec, nid);
+ unsigned int type = get_wcaps_type(wcaps);
+ if (type == AC_WID_AUD_IN && (wcaps & AC_WCAP_DIGITAL)) {
+ if (get_connection_index(codec, nid, pin) >= 0)
+ return nid;
+ }
+ }
+ return 0;
+}
+
+static void cx_auto_parse_digital(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ hda_nid_t nid;
+
+ if (cfg->dig_outs &&
+ snd_hda_get_connections(codec, cfg->dig_out_pins[0], &nid, 1) == 1)
+ spec->multiout.dig_out_nid = nid;
+ if (cfg->dig_in_pin)
+ spec->dig_in_nid = cx_auto_get_dig_in(codec, cfg->dig_in_pin);
+}
+
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+static void cx_auto_parse_beep(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ hda_nid_t nid, end_nid;
+
+ end_nid = codec->start_nid + codec->num_nodes;
+ for (nid = codec->start_nid; nid < end_nid; nid++)
+ if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP) {
+ set_beep_amp(spec, nid, 0, HDA_OUTPUT);
+ break;
+ }
+}
+#else
+#define cx_auto_parse_beep(codec)
+#endif
+
+static int cx_auto_parse_auto_config(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ int err;
+
+ err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
+ if (err < 0)
+ return err;
+
+ cx_auto_parse_output(codec);
+ cx_auto_parse_input(codec);
+ cx_auto_parse_digital(codec);
+ cx_auto_parse_beep(codec);
+ return 0;
+}
+
+static void cx_auto_turn_on_eapd(struct hda_codec *codec, int num_pins,
+ hda_nid_t *pins)
+{
+ int i;
+ for (i = 0; i < num_pins; i++) {
+ if (snd_hda_query_pin_caps(codec, pins[i]) & AC_PINCAP_EAPD)
+ snd_hda_codec_write(codec, pins[i], 0,
+ AC_VERB_SET_EAPD_BTLENABLE, 0x02);
+ }
+}
+
+static void select_connection(struct hda_codec *codec, hda_nid_t pin,
+ hda_nid_t src)
+{
+ int idx = get_connection_index(codec, pin, src);
+ if (idx >= 0)
+ snd_hda_codec_write(codec, pin, 0,
+ AC_VERB_SET_CONNECT_SEL, idx);
+}
+
+static void cx_auto_init_output(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ hda_nid_t nid;
+ int i;
+
+ for (i = 0; i < spec->multiout.num_dacs; i++)
+ snd_hda_codec_write(codec, spec->multiout.dac_nids[i], 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+
+ for (i = 0; i < cfg->hp_outs; i++)
+ snd_hda_codec_write(codec, cfg->hp_pins[i], 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP);
+ if (spec->auto_mute) {
+ for (i = 0; i < cfg->hp_outs; i++) {
+ snd_hda_codec_write(codec, cfg->hp_pins[i], 0,
+ AC_VERB_SET_UNSOLICITED_ENABLE,
+ AC_USRSP_EN | CONEXANT_HP_EVENT);
+ }
+ cx_auto_hp_automute(codec);
+ } else {
+ for (i = 0; i < cfg->line_outs; i++)
+ snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+ for (i = 0; i < cfg->speaker_outs; i++)
+ snd_hda_codec_write(codec, cfg->speaker_pins[i], 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+ }
+
+ for (i = 0; i < spec->dac_info_filled; i++) {
+ nid = spec->dac_info[i].dac;
+ if (!nid)
+ nid = spec->multiout.dac_nids[0];
+ select_connection(codec, spec->dac_info[i].pin, nid);
+ }
+
+ /* turn on EAPD */
+ cx_auto_turn_on_eapd(codec, cfg->line_outs, cfg->line_out_pins);
+ cx_auto_turn_on_eapd(codec, cfg->hp_outs, cfg->hp_pins);
+ cx_auto_turn_on_eapd(codec, cfg->speaker_outs, cfg->speaker_pins);
+}
+
+static void cx_auto_init_input(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i;
+
+ for (i = 0; i < spec->num_adc_nids; i++)
+ snd_hda_codec_write(codec, spec->adc_nids[i], 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0));
+
+ for (i = 0; i < cfg->num_inputs; i++) {
+ unsigned int type;
+ if (cfg->inputs[i].type == AUTO_PIN_MIC)
+ type = PIN_VREF80;
+ else
+ type = PIN_IN;
+ snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, type);
+ }
+
+ if (spec->auto_mic) {
+ int ext_idx = spec->auto_mic_ext;
+ snd_hda_codec_write(codec, cfg->inputs[ext_idx].pin, 0,
+ AC_VERB_SET_UNSOLICITED_ENABLE,
+ AC_USRSP_EN | CONEXANT_MIC_EVENT);
+ cx_auto_automic(codec);
+ } else {
+ for (i = 0; i < spec->num_adc_nids; i++) {
+ snd_hda_codec_write(codec, spec->adc_nids[i], 0,
+ AC_VERB_SET_CONNECT_SEL,
+ spec->private_imux.items[0].index);
+ }
+ }
+}
+
+static void cx_auto_init_digital(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+
+ if (spec->multiout.dig_out_nid)
+ snd_hda_codec_write(codec, cfg->dig_out_pins[0], 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+ if (spec->dig_in_nid)
+ snd_hda_codec_write(codec, cfg->dig_in_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
+}
+
+static int cx_auto_init(struct hda_codec *codec)
+{
+ /*snd_hda_sequence_write(codec, cx_auto_init_verbs);*/
+ cx_auto_init_output(codec);
+ cx_auto_init_input(codec);
+ cx_auto_init_digital(codec);
+ return 0;
+}
+
+static int cx_auto_add_volume(struct hda_codec *codec, const char *basename,
+ const char *dir, int cidx,
+ hda_nid_t nid, int hda_dir)
+{
+ static char name[32];
+ static struct snd_kcontrol_new knew[] = {
+ HDA_CODEC_VOLUME(name, 0, 0, 0),
+ HDA_CODEC_MUTE(name, 0, 0, 0),
+ };
+ static char *sfx[2] = { "Volume", "Switch" };
+ int i, err;
+
+ for (i = 0; i < 2; i++) {
+ struct snd_kcontrol *kctl;
+ knew[i].private_value = HDA_COMPOSE_AMP_VAL(nid, 3, 0, hda_dir);
+ knew[i].subdevice = HDA_SUBDEV_AMP_FLAG;
+ knew[i].index = cidx;
+ snprintf(name, sizeof(name), "%s%s %s", basename, dir, sfx[i]);
+ kctl = snd_ctl_new1(&knew[i], codec);
+ if (!kctl)
+ return -ENOMEM;
+ err = snd_hda_ctl_add(codec, nid, kctl);
+ if (err < 0)
+ return err;
+ if (!(query_amp_caps(codec, nid, hda_dir) & AC_AMPCAP_MUTE))
+ break;
+ }
+ return 0;
+}
+
+#define cx_auto_add_pb_volume(codec, nid, str, idx) \
+ cx_auto_add_volume(codec, str, " Playback", idx, nid, HDA_OUTPUT)
+
+static int cx_auto_build_output_controls(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ int i, err;
+ int num_line = 0, num_hp = 0, num_spk = 0;
+ static const char *texts[3] = { "Front", "Surround", "CLFE" };
+
+ if (spec->dac_info_filled == 1)
+ return cx_auto_add_pb_volume(codec, spec->dac_info[0].dac,
+ "Master", 0);
+ for (i = 0; i < spec->dac_info_filled; i++) {
+ const char *label;
+ int idx, type;
+ if (!spec->dac_info[i].dac)
+ continue;
+ type = spec->dac_info[i].type;
+ if (type == AUTO_PIN_LINE_OUT)
+ type = spec->autocfg.line_out_type;
+ switch (type) {
+ case AUTO_PIN_LINE_OUT:
+ default:
+ label = texts[num_line++];
+ idx = 0;
+ break;
+ case AUTO_PIN_HP_OUT:
+ label = "Headphone";
+ idx = num_hp++;
+ break;
+ case AUTO_PIN_SPEAKER_OUT:
+ label = "Speaker";
+ idx = num_spk++;
+ break;
+ }
+ err = cx_auto_add_pb_volume(codec, spec->dac_info[i].dac,
+ label, idx);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+static int cx_auto_build_input_controls(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ static const char *prev_label;
+ int i, err, cidx;
+
+ err = cx_auto_add_volume(codec, "Capture", "", 0, spec->adc_nids[0],
+ HDA_INPUT);
+ if (err < 0)
+ return err;
+ prev_label = NULL;
+ cidx = 0;
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
+ const char *label;
+ if (!(get_wcaps(codec, nid) & AC_WCAP_IN_AMP))
+ continue;
+ label = hda_get_autocfg_input_label(codec, cfg, i);
+ if (label == prev_label)
+ cidx++;
+ else
+ cidx = 0;
+ prev_label = label;
+ err = cx_auto_add_volume(codec, label, " Capture", cidx,
+ nid, HDA_INPUT);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+static int cx_auto_build_controls(struct hda_codec *codec)
+{
+ int err;
+
+ err = cx_auto_build_output_controls(codec);
+ if (err < 0)
+ return err;
+ err = cx_auto_build_input_controls(codec);
+ if (err < 0)
+ return err;
+ return conexant_build_controls(codec);
+}
+
+static struct hda_codec_ops cx_auto_patch_ops = {
+ .build_controls = cx_auto_build_controls,
+ .build_pcms = conexant_build_pcms,
+ .init = cx_auto_init,
+ .free = conexant_free,
+ .unsol_event = cx_auto_unsol_event,
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+ .suspend = conexant_suspend,
+#endif
+ .reboot_notify = snd_hda_shutup_pins,
+};
+
+static int patch_conexant_auto(struct hda_codec *codec)
+{
+ struct conexant_spec *spec;
+ int err;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+ codec->spec = spec;
+ spec->adc_nids = cx_auto_adc_nids;
+ spec->num_adc_nids = ARRAY_SIZE(cx_auto_adc_nids);
+ spec->capsrc_nids = spec->adc_nids;
+ err = cx_auto_parse_auto_config(codec);
+ if (err < 0) {
+ kfree(codec->spec);
+ codec->spec = NULL;
+ return err;
+ }
+ codec->patch_ops = cx_auto_patch_ops;
+ if (spec->beep_amp)
+ snd_hda_attach_beep_device(codec, spec->beep_amp);
+ return 0;
+}
+
+/*
*/
static struct hda_codec_preset snd_hda_preset_conexant[] = {
@@ -3210,6 +3880,22 @@ static struct hda_codec_preset snd_hda_preset_conexant[] = {
.patch = patch_cxt5066 },
{ .id = 0x14f15069, .name = "CX20585",
.patch = patch_cxt5066 },
+ { .id = 0x14f15097, .name = "CX20631",
+ .patch = patch_conexant_auto },
+ { .id = 0x14f15098, .name = "CX20632",
+ .patch = patch_conexant_auto },
+ { .id = 0x14f150a1, .name = "CX20641",
+ .patch = patch_conexant_auto },
+ { .id = 0x14f150a2, .name = "CX20642",
+ .patch = patch_conexant_auto },
+ { .id = 0x14f150ab, .name = "CX20651",
+ .patch = patch_conexant_auto },
+ { .id = 0x14f150ac, .name = "CX20652",
+ .patch = patch_conexant_auto },
+ { .id = 0x14f150b8, .name = "CX20664",
+ .patch = patch_conexant_auto },
+ { .id = 0x14f150b9, .name = "CX20665",
+ .patch = patch_conexant_auto },
{} /* terminator */
};
@@ -3220,6 +3906,14 @@ MODULE_ALIAS("snd-hda-codec-id:14f15066");
MODULE_ALIAS("snd-hda-codec-id:14f15067");
MODULE_ALIAS("snd-hda-codec-id:14f15068");
MODULE_ALIAS("snd-hda-codec-id:14f15069");
+MODULE_ALIAS("snd-hda-codec-id:14f15097");
+MODULE_ALIAS("snd-hda-codec-id:14f15098");
+MODULE_ALIAS("snd-hda-codec-id:14f150a1");
+MODULE_ALIAS("snd-hda-codec-id:14f150a2");
+MODULE_ALIAS("snd-hda-codec-id:14f150ab");
+MODULE_ALIAS("snd-hda-codec-id:14f150ac");
+MODULE_ALIAS("snd-hda-codec-id:14f150b8");
+MODULE_ALIAS("snd-hda-codec-id:14f150b9");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Conexant HD-audio codec");
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index 2bc0f07cf33..d3e49aa5b9e 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -3,6 +3,9 @@
* patch_hdmi.c - routines for HDMI/DisplayPort codecs
*
* Copyright(c) 2008-2010 Intel Corporation. All rights reserved.
+ * Copyright (c) 2006 ATI Technologies Inc.
+ * Copyright (c) 2008 NVIDIA Corp. All rights reserved.
+ * Copyright (c) 2008 Wei Ni <wni@nvidia.com>
*
* Authors:
* Wu Fengguang <wfg@linux.intel.com>
@@ -25,6 +28,22 @@
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include "hda_codec.h"
+#include "hda_local.h"
+
+/*
+ * The HDMI/DisplayPort configuration can be highly dynamic. A graphics device
+ * could support two independent pipes, each of them can be connected to one or
+ * more ports (DVI, HDMI or DisplayPort).
+ *
+ * The HDA correspondence of pipes/ports are converter/pin nodes.
+ */
+#define MAX_HDMI_CVTS 3
+#define MAX_HDMI_PINS 3
struct hdmi_spec {
int num_cvts;
@@ -49,10 +68,10 @@ struct hdmi_spec {
struct hda_pcm_stream codec_pcm_pars[MAX_HDMI_CVTS];
/*
- * nvhdmi specific
+ * ati/nvhdmi specific
*/
struct hda_multi_out multiout;
- unsigned int codec_type;
+ struct hda_pcm_stream *pcm_playback;
/* misc flags */
/* PD bit indicates only the update, not the current state */
@@ -65,13 +84,25 @@ struct hdmi_audio_infoframe {
u8 ver; /* 0x01 */
u8 len; /* 0x0a */
- u8 checksum; /* PB0 */
+ u8 checksum;
+
u8 CC02_CT47; /* CC in bits 0:2, CT in 4:7 */
u8 SS01_SF24;
u8 CXT04;
u8 CA;
u8 LFEPBL01_LSV36_DM_INH7;
- u8 reserved[5]; /* PB6 - PB10 */
+};
+
+struct dp_audio_infoframe {
+ u8 type; /* 0x84 */
+ u8 len; /* 0x1b */
+ u8 ver; /* 0x11 << 2 */
+
+ u8 CC02_CT47; /* match with HDMI infoframe from this on */
+ u8 SS01_SF24;
+ u8 CXT04;
+ u8 CA;
+ u8 LFEPBL01_LSV36_DM_INH7;
};
/*
@@ -162,7 +193,7 @@ static int hdmi_channel_mapping[0x32][8] = {
/* 4ch */
[0x03] = { 0x00, 0x11, 0x23, 0x32, 0x44, 0xf5, 0xf6, 0xf7 },
/* surround41 */
- [0x09] = { 0x00, 0x11, 0x24, 0x34, 0x43, 0xf2, 0xf6, 0xf7 },
+ [0x09] = { 0x00, 0x11, 0x24, 0x35, 0x42, 0xf3, 0xf6, 0xf7 },
/* surround50 */
[0x0a] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0xf2, 0xf6, 0xf7 },
/* surround51 */
@@ -175,7 +206,7 @@ static int hdmi_channel_mapping[0x32][8] = {
* This is an ordered list!
*
* The preceding ones have better chances to be selected by
- * hdmi_setup_channel_allocation().
+ * hdmi_channel_allocation().
*/
static struct cea_channel_speaker_allocation channel_allocations[] = {
/* channel: 7 6 5 4 3 2 1 0 */
@@ -352,14 +383,14 @@ static void init_channel_allocations(void)
*
* TODO: it could select the wrong CA from multiple candidates.
*/
-static int hdmi_setup_channel_allocation(struct hda_codec *codec, hda_nid_t nid,
- struct hdmi_audio_infoframe *ai)
+static int hdmi_channel_allocation(struct hda_codec *codec, hda_nid_t nid,
+ int channels)
{
struct hdmi_spec *spec = codec->spec;
struct hdmi_eld *eld;
int i;
+ int ca = 0;
int spk_mask = 0;
- int channels = 1 + (ai->CC02_CT47 & 0x7);
char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
/*
@@ -397,16 +428,16 @@ static int hdmi_setup_channel_allocation(struct hda_codec *codec, hda_nid_t nid,
if (channels == channel_allocations[i].channels &&
(spk_mask & channel_allocations[i].spk_mask) ==
channel_allocations[i].spk_mask) {
- ai->CA = channel_allocations[i].ca_index;
+ ca = channel_allocations[i].ca_index;
break;
}
}
snd_print_channel_allocation(eld->spk_alloc, buf, sizeof(buf));
snd_printdd("HDMI: select CA 0x%x for %d-channel allocation: %s\n",
- ai->CA, channels, buf);
+ ca, channels, buf);
- return ai->CA;
+ return ca;
}
static void hdmi_debug_channel_mapping(struct hda_codec *codec,
@@ -428,10 +459,9 @@ static void hdmi_debug_channel_mapping(struct hda_codec *codec,
static void hdmi_setup_channel_mapping(struct hda_codec *codec,
hda_nid_t pin_nid,
- struct hdmi_audio_infoframe *ai)
+ int ca)
{
int i;
- int ca = ai->CA;
int err;
if (hdmi_channel_mapping[ca][1] == 0) {
@@ -528,41 +558,37 @@ static void hdmi_clear_dip_buffers(struct hda_codec *codec, hda_nid_t pin_nid)
#endif
}
-static void hdmi_checksum_audio_infoframe(struct hdmi_audio_infoframe *ai)
+static void hdmi_checksum_audio_infoframe(struct hdmi_audio_infoframe *hdmi_ai)
{
- u8 *bytes = (u8 *)ai;
+ u8 *bytes = (u8 *)hdmi_ai;
u8 sum = 0;
int i;
- ai->checksum = 0;
+ hdmi_ai->checksum = 0;
- for (i = 0; i < sizeof(*ai); i++)
+ for (i = 0; i < sizeof(*hdmi_ai); i++)
sum += bytes[i];
- ai->checksum = -sum;
+ hdmi_ai->checksum = -sum;
}
static void hdmi_fill_audio_infoframe(struct hda_codec *codec,
hda_nid_t pin_nid,
- struct hdmi_audio_infoframe *ai)
+ u8 *dip, int size)
{
- u8 *bytes = (u8 *)ai;
int i;
hdmi_debug_dip_size(codec, pin_nid);
hdmi_clear_dip_buffers(codec, pin_nid); /* be paranoid */
- hdmi_checksum_audio_infoframe(ai);
-
hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
- for (i = 0; i < sizeof(*ai); i++)
- hdmi_write_dip_byte(codec, pin_nid, bytes[i]);
+ for (i = 0; i < size; i++)
+ hdmi_write_dip_byte(codec, pin_nid, dip[i]);
}
static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid,
- struct hdmi_audio_infoframe *ai)
+ u8 *dip, int size)
{
- u8 *bytes = (u8 *)ai;
u8 val;
int i;
@@ -571,10 +597,10 @@ static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid,
return false;
hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
- for (i = 0; i < sizeof(*ai); i++) {
+ for (i = 0; i < size; i++) {
val = snd_hda_codec_read(codec, pin_nid, 0,
AC_VERB_GET_HDMI_DIP_DATA, 0);
- if (val != bytes[i])
+ if (val != dip[i])
return false;
}
@@ -586,15 +612,13 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid,
{
struct hdmi_spec *spec = codec->spec;
hda_nid_t pin_nid;
+ int channels = substream->runtime->channels;
+ int ca;
int i;
- struct hdmi_audio_infoframe ai = {
- .type = 0x84,
- .ver = 0x01,
- .len = 0x0a,
- .CC02_CT47 = substream->runtime->channels - 1,
- };
+ u8 ai[max(sizeof(struct hdmi_audio_infoframe),
+ sizeof(struct dp_audio_infoframe))];
- hdmi_setup_channel_allocation(codec, nid, &ai);
+ ca = hdmi_channel_allocation(codec, nid, channels);
for (i = 0; i < spec->num_pins; i++) {
if (spec->pin_cvt[i] != nid)
@@ -603,14 +627,45 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid,
continue;
pin_nid = spec->pin[i];
- if (!hdmi_infoframe_uptodate(codec, pin_nid, &ai)) {
+
+ memset(ai, 0, sizeof(ai));
+ if (spec->sink_eld[i].conn_type == 0) { /* HDMI */
+ struct hdmi_audio_infoframe *hdmi_ai;
+
+ hdmi_ai = (struct hdmi_audio_infoframe *)ai;
+ hdmi_ai->type = 0x84;
+ hdmi_ai->ver = 0x01;
+ hdmi_ai->len = 0x0a;
+ hdmi_ai->CC02_CT47 = channels - 1;
+ hdmi_checksum_audio_infoframe(hdmi_ai);
+ } else if (spec->sink_eld[i].conn_type == 1) { /* DisplayPort */
+ struct dp_audio_infoframe *dp_ai;
+
+ dp_ai = (struct dp_audio_infoframe *)ai;
+ dp_ai->type = 0x84;
+ dp_ai->len = 0x1b;
+ dp_ai->ver = 0x11 << 2;
+ dp_ai->CC02_CT47 = channels - 1;
+ } else {
+ snd_printd("HDMI: unknown connection type at pin %d\n",
+ pin_nid);
+ continue;
+ }
+
+ /*
+ * sizeof(ai) is used instead of sizeof(*hdmi_ai) or
+ * sizeof(*dp_ai) to avoid partial match/update problems when
+ * the user switches between HDMI/DP monitors.
+ */
+ if (!hdmi_infoframe_uptodate(codec, pin_nid, ai, sizeof(ai))) {
snd_printdd("hdmi_setup_audio_infoframe: "
"cvt=%d pin=%d channels=%d\n",
nid, pin_nid,
- substream->runtime->channels);
- hdmi_setup_channel_mapping(codec, pin_nid, &ai);
+ channels);
+ hdmi_setup_channel_mapping(codec, pin_nid, ca);
hdmi_stop_infoframe_trans(codec, pin_nid);
- hdmi_fill_audio_infoframe(codec, pin_nid, &ai);
+ hdmi_fill_audio_infoframe(codec, pin_nid,
+ ai, sizeof(ai));
hdmi_start_infoframe_trans(codec, pin_nid);
}
}
@@ -707,8 +762,6 @@ static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t nid,
u32 stream_tag, int format)
{
struct hdmi_spec *spec = codec->spec;
- int tag;
- int fmt;
int pinctl;
int new_pinctl = 0;
int i;
@@ -745,24 +798,7 @@ static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t nid,
return -EINVAL;
}
- tag = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0) >> 4;
- fmt = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_STREAM_FORMAT, 0);
-
- snd_printdd("hdmi_setup_stream: "
- "NID=0x%x, %sstream=0x%x, %sformat=0x%x\n",
- nid,
- tag == stream_tag ? "" : "new-",
- stream_tag,
- fmt == format ? "" : "new-",
- format);
-
- if (tag != stream_tag)
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_CHANNEL_STREAMID,
- stream_tag << 4);
- if (fmt != format)
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_STREAM_FORMAT, format);
+ snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format);
return 0;
}
@@ -810,7 +846,6 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
/*
* HDA/HDMI auto parsing
*/
-
static int hdmi_read_pin_conn(struct hda_codec *codec, hda_nid_t pin_nid)
{
struct hdmi_spec *spec = codec->spec;
@@ -941,3 +976,664 @@ static int hdmi_parse_codec(struct hda_codec *codec)
return 0;
}
+/*
+ */
+static char *generic_hdmi_pcm_names[MAX_HDMI_CVTS] = {
+ "HDMI 0",
+ "HDMI 1",
+ "HDMI 2",
+};
+
+/*
+ * HDMI callbacks
+ */
+
+static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ hdmi_set_channel_count(codec, hinfo->nid,
+ substream->runtime->channels);
+
+ hdmi_setup_audio_infoframe(codec, hinfo->nid, substream);
+
+ return hdmi_setup_stream(codec, hinfo->nid, stream_tag, format);
+}
+
+static struct hda_pcm_stream generic_hdmi_pcm_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .ops = {
+ .open = hdmi_pcm_open,
+ .prepare = generic_hdmi_playback_pcm_prepare,
+ },
+};
+
+static int generic_hdmi_build_pcms(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ struct hda_pcm *info = spec->pcm_rec;
+ int i;
+
+ codec->num_pcms = spec->num_cvts;
+ codec->pcm_info = info;
+
+ for (i = 0; i < codec->num_pcms; i++, info++) {
+ unsigned int chans;
+ struct hda_pcm_stream *pstr;
+
+ chans = get_wcaps(codec, spec->cvt[i]);
+ chans = get_wcaps_channels(chans);
+
+ info->name = generic_hdmi_pcm_names[i];
+ info->pcm_type = HDA_PCM_TYPE_HDMI;
+ pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK];
+ if (spec->pcm_playback)
+ *pstr = *spec->pcm_playback;
+ else
+ *pstr = generic_hdmi_pcm_playback;
+ pstr->nid = spec->cvt[i];
+ if (pstr->channels_max <= 2 && chans && chans <= 16)
+ pstr->channels_max = chans;
+ }
+
+ return 0;
+}
+
+static int generic_hdmi_build_controls(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int err;
+ int i;
+
+ for (i = 0; i < codec->num_pcms; i++) {
+ err = snd_hda_create_spdif_out_ctls(codec, spec->cvt[i]);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static int generic_hdmi_init(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int i;
+
+ for (i = 0; spec->pin[i]; i++) {
+ hdmi_enable_output(codec, spec->pin[i]);
+ snd_hda_codec_write(codec, spec->pin[i], 0,
+ AC_VERB_SET_UNSOLICITED_ENABLE,
+ AC_USRSP_EN | spec->pin[i]);
+ }
+ return 0;
+}
+
+static void generic_hdmi_free(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int i;
+
+ for (i = 0; i < spec->num_pins; i++)
+ snd_hda_eld_proc_free(codec, &spec->sink_eld[i]);
+
+ kfree(spec);
+}
+
+static struct hda_codec_ops generic_hdmi_patch_ops = {
+ .init = generic_hdmi_init,
+ .free = generic_hdmi_free,
+ .build_pcms = generic_hdmi_build_pcms,
+ .build_controls = generic_hdmi_build_controls,
+ .unsol_event = hdmi_unsol_event,
+};
+
+static int patch_generic_hdmi(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec;
+ int i;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return -ENOMEM;
+
+ codec->spec = spec;
+ if (hdmi_parse_codec(codec) < 0) {
+ codec->spec = NULL;
+ kfree(spec);
+ return -EINVAL;
+ }
+ codec->patch_ops = generic_hdmi_patch_ops;
+
+ for (i = 0; i < spec->num_pins; i++)
+ snd_hda_eld_proc_new(codec, &spec->sink_eld[i], i);
+
+ init_channel_allocations();
+
+ return 0;
+}
+
+/*
+ * Nvidia specific implementations
+ */
+
+#define Nv_VERB_SET_Channel_Allocation 0xF79
+#define Nv_VERB_SET_Info_Frame_Checksum 0xF7A
+#define Nv_VERB_SET_Audio_Protection_On 0xF98
+#define Nv_VERB_SET_Audio_Protection_Off 0xF99
+
+#define nvhdmi_master_con_nid_7x 0x04
+#define nvhdmi_master_pin_nid_7x 0x05
+
+static hda_nid_t nvhdmi_con_nids_7x[4] = {
+ /*front, rear, clfe, rear_surr */
+ 0x6, 0x8, 0xa, 0xc,
+};
+
+static struct hda_verb nvhdmi_basic_init_7x[] = {
+ /* set audio protect on */
+ { 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1},
+ /* enable digital output on pin widget */
+ { 0x5, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
+ { 0x7, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
+ { 0x9, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
+ { 0xb, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
+ { 0xd, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
+ {} /* terminator */
+};
+
+#ifdef LIMITED_RATE_FMT_SUPPORT
+/* support only the safe format and rate */
+#define SUPPORTED_RATES SNDRV_PCM_RATE_48000
+#define SUPPORTED_MAXBPS 16
+#define SUPPORTED_FORMATS SNDRV_PCM_FMTBIT_S16_LE
+#else
+/* support all rates and formats */
+#define SUPPORTED_RATES \
+ (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |\
+ SNDRV_PCM_RATE_192000)
+#define SUPPORTED_MAXBPS 24
+#define SUPPORTED_FORMATS \
+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE)
+#endif
+
+static int nvhdmi_7x_init(struct hda_codec *codec)
+{
+ snd_hda_sequence_write(codec, nvhdmi_basic_init_7x);
+ return 0;
+}
+
+static int simple_playback_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hdmi_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_open(codec, &spec->multiout);
+}
+
+static int simple_playback_pcm_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hdmi_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+static int simple_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct hdmi_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
+ stream_tag, format, substream);
+}
+
+static int nvhdmi_8ch_7x_pcm_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int i;
+
+ snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x,
+ 0, AC_VERB_SET_CHANNEL_STREAMID, 0);
+ for (i = 0; i < 4; i++) {
+ /* set the stream id */
+ snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0,
+ AC_VERB_SET_CHANNEL_STREAMID, 0);
+ /* set the stream format */
+ snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0,
+ AC_VERB_SET_STREAM_FORMAT, 0);
+ }
+
+ return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ int chs;
+ unsigned int dataDCC1, dataDCC2, chan, chanmask, channel_id;
+ int i;
+
+ mutex_lock(&codec->spdif_mutex);
+
+ chs = substream->runtime->channels;
+ chan = chs ? (chs - 1) : 1;
+
+ switch (chs) {
+ default:
+ case 0:
+ case 2:
+ chanmask = 0x00;
+ break;
+ case 4:
+ chanmask = 0x08;
+ break;
+ case 6:
+ chanmask = 0x0b;
+ break;
+ case 8:
+ chanmask = 0x13;
+ break;
+ }
+ dataDCC1 = AC_DIG1_ENABLE | AC_DIG1_COPYRIGHT;
+ dataDCC2 = 0x2;
+
+ /* set the Audio InforFrame Channel Allocation */
+ snd_hda_codec_write(codec, 0x1, 0,
+ Nv_VERB_SET_Channel_Allocation, chanmask);
+
+ /* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
+ if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE))
+ snd_hda_codec_write(codec,
+ nvhdmi_master_con_nid_7x,
+ 0,
+ AC_VERB_SET_DIGI_CONVERT_1,
+ codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
+
+ /* set the stream id */
+ snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0,
+ AC_VERB_SET_CHANNEL_STREAMID, (stream_tag << 4) | 0x0);
+
+ /* set the stream format */
+ snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0,
+ AC_VERB_SET_STREAM_FORMAT, format);
+
+ /* turn on again (if needed) */
+ /* enable and set the channel status audio/data flag */
+ if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE)) {
+ snd_hda_codec_write(codec,
+ nvhdmi_master_con_nid_7x,
+ 0,
+ AC_VERB_SET_DIGI_CONVERT_1,
+ codec->spdif_ctls & 0xff);
+ snd_hda_codec_write(codec,
+ nvhdmi_master_con_nid_7x,
+ 0,
+ AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
+ }
+
+ for (i = 0; i < 4; i++) {
+ if (chs == 2)
+ channel_id = 0;
+ else
+ channel_id = i * 2;
+
+ /* turn off SPDIF once;
+ *otherwise the IEC958 bits won't be updated
+ */
+ if (codec->spdif_status_reset &&
+ (codec->spdif_ctls & AC_DIG1_ENABLE))
+ snd_hda_codec_write(codec,
+ nvhdmi_con_nids_7x[i],
+ 0,
+ AC_VERB_SET_DIGI_CONVERT_1,
+ codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
+ /* set the stream id */
+ snd_hda_codec_write(codec,
+ nvhdmi_con_nids_7x[i],
+ 0,
+ AC_VERB_SET_CHANNEL_STREAMID,
+ (stream_tag << 4) | channel_id);
+ /* set the stream format */
+ snd_hda_codec_write(codec,
+ nvhdmi_con_nids_7x[i],
+ 0,
+ AC_VERB_SET_STREAM_FORMAT,
+ format);
+ /* turn on again (if needed) */
+ /* enable and set the channel status audio/data flag */
+ if (codec->spdif_status_reset &&
+ (codec->spdif_ctls & AC_DIG1_ENABLE)) {
+ snd_hda_codec_write(codec,
+ nvhdmi_con_nids_7x[i],
+ 0,
+ AC_VERB_SET_DIGI_CONVERT_1,
+ codec->spdif_ctls & 0xff);
+ snd_hda_codec_write(codec,
+ nvhdmi_con_nids_7x[i],
+ 0,
+ AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
+ }
+ }
+
+ /* set the Audio Info Frame Checksum */
+ snd_hda_codec_write(codec, 0x1, 0,
+ Nv_VERB_SET_Info_Frame_Checksum,
+ (0x71 - chan - chanmask));
+
+ mutex_unlock(&codec->spdif_mutex);
+ return 0;
+}
+
+static struct hda_pcm_stream nvhdmi_pcm_playback_8ch_7x = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 8,
+ .nid = nvhdmi_master_con_nid_7x,
+ .rates = SUPPORTED_RATES,
+ .maxbps = SUPPORTED_MAXBPS,
+ .formats = SUPPORTED_FORMATS,
+ .ops = {
+ .open = simple_playback_pcm_open,
+ .close = nvhdmi_8ch_7x_pcm_close,
+ .prepare = nvhdmi_8ch_7x_pcm_prepare
+ },
+};
+
+static struct hda_pcm_stream nvhdmi_pcm_playback_2ch = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ .nid = nvhdmi_master_con_nid_7x,
+ .rates = SUPPORTED_RATES,
+ .maxbps = SUPPORTED_MAXBPS,
+ .formats = SUPPORTED_FORMATS,
+ .ops = {
+ .open = simple_playback_pcm_open,
+ .close = simple_playback_pcm_close,
+ .prepare = simple_playback_pcm_prepare
+ },
+};
+
+static struct hda_codec_ops nvhdmi_patch_ops_8ch_7x = {
+ .build_controls = generic_hdmi_build_controls,
+ .build_pcms = generic_hdmi_build_pcms,
+ .init = nvhdmi_7x_init,
+ .free = generic_hdmi_free,
+};
+
+static struct hda_codec_ops nvhdmi_patch_ops_2ch = {
+ .build_controls = generic_hdmi_build_controls,
+ .build_pcms = generic_hdmi_build_pcms,
+ .init = nvhdmi_7x_init,
+ .free = generic_hdmi_free,
+};
+
+static int patch_nvhdmi_8ch_89(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec;
+ int err = patch_generic_hdmi(codec);
+
+ if (err < 0)
+ return err;
+ spec = codec->spec;
+ spec->old_pin_detect = 1;
+ return 0;
+}
+
+static int patch_nvhdmi_2ch(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return -ENOMEM;
+
+ codec->spec = spec;
+
+ spec->multiout.num_dacs = 0; /* no analog */
+ spec->multiout.max_channels = 2;
+ spec->multiout.dig_out_nid = nvhdmi_master_con_nid_7x;
+ spec->old_pin_detect = 1;
+ spec->num_cvts = 1;
+ spec->cvt[0] = nvhdmi_master_con_nid_7x;
+ spec->pcm_playback = &nvhdmi_pcm_playback_2ch;
+
+ codec->patch_ops = nvhdmi_patch_ops_2ch;
+
+ return 0;
+}
+
+static int patch_nvhdmi_8ch_7x(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec;
+ int err = patch_nvhdmi_2ch(codec);
+
+ if (err < 0)
+ return err;
+ spec = codec->spec;
+ spec->multiout.max_channels = 8;
+ spec->pcm_playback = &nvhdmi_pcm_playback_8ch_7x;
+ codec->patch_ops = nvhdmi_patch_ops_8ch_7x;
+ return 0;
+}
+
+/*
+ * ATI-specific implementations
+ *
+ * FIXME: we may omit the whole this and use the generic code once after
+ * it's confirmed to work.
+ */
+
+#define ATIHDMI_CVT_NID 0x02 /* audio converter */
+#define ATIHDMI_PIN_NID 0x03 /* HDMI output pin */
+
+static int atihdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int chans = substream->runtime->channels;
+ int i, err;
+
+ err = simple_playback_pcm_prepare(hinfo, codec, stream_tag, format,
+ substream);
+ if (err < 0)
+ return err;
+ snd_hda_codec_write(codec, spec->cvt[0], 0, AC_VERB_SET_CVT_CHAN_COUNT,
+ chans - 1);
+ /* FIXME: XXX */
+ for (i = 0; i < chans; i++) {
+ snd_hda_codec_write(codec, spec->cvt[0], 0,
+ AC_VERB_SET_HDMI_CHAN_SLOT,
+ (i << 4) | i);
+ }
+ return 0;
+}
+
+static struct hda_pcm_stream atihdmi_pcm_digital_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ .nid = ATIHDMI_CVT_NID,
+ .ops = {
+ .open = simple_playback_pcm_open,
+ .close = simple_playback_pcm_close,
+ .prepare = atihdmi_playback_pcm_prepare
+ },
+};
+
+static struct hda_verb atihdmi_basic_init[] = {
+ /* enable digital output on pin widget */
+ { 0x03, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ {} /* terminator */
+};
+
+static int atihdmi_init(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+
+ snd_hda_sequence_write(codec, atihdmi_basic_init);
+ /* SI codec requires to unmute the pin */
+ if (get_wcaps(codec, spec->pin[0]) & AC_WCAP_OUT_AMP)
+ snd_hda_codec_write(codec, spec->pin[0], 0,
+ AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_OUT_UNMUTE);
+ return 0;
+}
+
+static struct hda_codec_ops atihdmi_patch_ops = {
+ .build_controls = generic_hdmi_build_controls,
+ .build_pcms = generic_hdmi_build_pcms,
+ .init = atihdmi_init,
+ .free = generic_hdmi_free,
+};
+
+
+static int patch_atihdmi(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return -ENOMEM;
+
+ codec->spec = spec;
+
+ spec->multiout.num_dacs = 0; /* no analog */
+ spec->multiout.max_channels = 2;
+ spec->multiout.dig_out_nid = ATIHDMI_CVT_NID;
+ spec->num_cvts = 1;
+ spec->cvt[0] = ATIHDMI_CVT_NID;
+ spec->pin[0] = ATIHDMI_PIN_NID;
+ spec->pcm_playback = &atihdmi_pcm_digital_playback;
+
+ codec->patch_ops = atihdmi_patch_ops;
+
+ return 0;
+}
+
+
+/*
+ * patch entries
+ */
+static struct hda_codec_preset snd_hda_preset_hdmi[] = {
+{ .id = 0x1002793c, .name = "RS600 HDMI", .patch = patch_atihdmi },
+{ .id = 0x10027919, .name = "RS600 HDMI", .patch = patch_atihdmi },
+{ .id = 0x1002791a, .name = "RS690/780 HDMI", .patch = patch_atihdmi },
+{ .id = 0x1002aa01, .name = "R6xx HDMI", .patch = patch_atihdmi },
+{ .id = 0x10951390, .name = "SiI1390 HDMI", .patch = patch_generic_hdmi },
+{ .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_generic_hdmi },
+{ .id = 0x17e80047, .name = "Chrontel HDMI", .patch = patch_generic_hdmi },
+{ .id = 0x10de0002, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
+{ .id = 0x10de0003, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
+{ .id = 0x10de0005, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
+{ .id = 0x10de0006, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
+{ .id = 0x10de0007, .name = "MCP79/7A HDMI", .patch = patch_nvhdmi_8ch_7x },
+{ .id = 0x10de000a, .name = "GPU 0a HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de000b, .name = "GPU 0b HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de000c, .name = "MCP89 HDMI", .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de000d, .name = "GPU 0d HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0010, .name = "GPU 10 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0011, .name = "GPU 11 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0012, .name = "GPU 12 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0013, .name = "GPU 13 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0014, .name = "GPU 14 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0018, .name = "GPU 18 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0019, .name = "GPU 19 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de001a, .name = "GPU 1a HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de001b, .name = "GPU 1b HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de001c, .name = "GPU 1c HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0040, .name = "GPU 40 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0041, .name = "GPU 41 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0042, .name = "GPU 42 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0043, .name = "GPU 43 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0044, .name = "GPU 44 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch },
+{ .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi_2ch },
+{ .id = 0x80860054, .name = "IbexPeak HDMI", .patch = patch_generic_hdmi },
+{ .id = 0x80862801, .name = "Bearlake HDMI", .patch = patch_generic_hdmi },
+{ .id = 0x80862802, .name = "Cantiga HDMI", .patch = patch_generic_hdmi },
+{ .id = 0x80862803, .name = "Eaglelake HDMI", .patch = patch_generic_hdmi },
+{ .id = 0x80862804, .name = "IbexPeak HDMI", .patch = patch_generic_hdmi },
+{ .id = 0x80862805, .name = "CougarPoint HDMI", .patch = patch_generic_hdmi },
+{ .id = 0x808629fb, .name = "Crestline HDMI", .patch = patch_generic_hdmi },
+{} /* terminator */
+};
+
+MODULE_ALIAS("snd-hda-codec-id:1002793c");
+MODULE_ALIAS("snd-hda-codec-id:10027919");
+MODULE_ALIAS("snd-hda-codec-id:1002791a");
+MODULE_ALIAS("snd-hda-codec-id:1002aa01");
+MODULE_ALIAS("snd-hda-codec-id:10951390");
+MODULE_ALIAS("snd-hda-codec-id:10951392");
+MODULE_ALIAS("snd-hda-codec-id:10de0002");
+MODULE_ALIAS("snd-hda-codec-id:10de0003");
+MODULE_ALIAS("snd-hda-codec-id:10de0005");
+MODULE_ALIAS("snd-hda-codec-id:10de0006");
+MODULE_ALIAS("snd-hda-codec-id:10de0007");
+MODULE_ALIAS("snd-hda-codec-id:10de000a");
+MODULE_ALIAS("snd-hda-codec-id:10de000b");
+MODULE_ALIAS("snd-hda-codec-id:10de000c");
+MODULE_ALIAS("snd-hda-codec-id:10de000d");
+MODULE_ALIAS("snd-hda-codec-id:10de0010");
+MODULE_ALIAS("snd-hda-codec-id:10de0011");
+MODULE_ALIAS("snd-hda-codec-id:10de0012");
+MODULE_ALIAS("snd-hda-codec-id:10de0013");
+MODULE_ALIAS("snd-hda-codec-id:10de0014");
+MODULE_ALIAS("snd-hda-codec-id:10de0018");
+MODULE_ALIAS("snd-hda-codec-id:10de0019");
+MODULE_ALIAS("snd-hda-codec-id:10de001a");
+MODULE_ALIAS("snd-hda-codec-id:10de001b");
+MODULE_ALIAS("snd-hda-codec-id:10de001c");
+MODULE_ALIAS("snd-hda-codec-id:10de0040");
+MODULE_ALIAS("snd-hda-codec-id:10de0041");
+MODULE_ALIAS("snd-hda-codec-id:10de0042");
+MODULE_ALIAS("snd-hda-codec-id:10de0043");
+MODULE_ALIAS("snd-hda-codec-id:10de0044");
+MODULE_ALIAS("snd-hda-codec-id:10de0067");
+MODULE_ALIAS("snd-hda-codec-id:10de8001");
+MODULE_ALIAS("snd-hda-codec-id:17e80047");
+MODULE_ALIAS("snd-hda-codec-id:80860054");
+MODULE_ALIAS("snd-hda-codec-id:80862801");
+MODULE_ALIAS("snd-hda-codec-id:80862802");
+MODULE_ALIAS("snd-hda-codec-id:80862803");
+MODULE_ALIAS("snd-hda-codec-id:80862804");
+MODULE_ALIAS("snd-hda-codec-id:80862805");
+MODULE_ALIAS("snd-hda-codec-id:808629fb");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("HDMI HD-audio codec");
+MODULE_ALIAS("snd-hda-codec-intelhdmi");
+MODULE_ALIAS("snd-hda-codec-nvhdmi");
+MODULE_ALIAS("snd-hda-codec-atihdmi");
+
+static struct hda_codec_preset_list intel_list = {
+ .preset = snd_hda_preset_hdmi,
+ .owner = THIS_MODULE,
+};
+
+static int __init patch_hdmi_init(void)
+{
+ return snd_hda_add_codec_preset(&intel_list);
+}
+
+static void __exit patch_hdmi_exit(void)
+{
+ snd_hda_delete_codec_preset(&intel_list);
+}
+
+module_init(patch_hdmi_init)
+module_exit(patch_hdmi_exit)
diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c
deleted file mode 100644
index d382d3c81c0..00000000000
--- a/sound/pci/hda/patch_intelhdmi.c
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- *
- * patch_intelhdmi.c - Patch for Intel HDMI codecs
- *
- * Copyright(c) 2008 Intel Corporation. All rights reserved.
- *
- * Authors:
- * Jiang Zhe <zhe.jiang@intel.com>
- * Wu Fengguang <wfg@linux.intel.com>
- *
- * Maintained by:
- * Wu Fengguang <wfg@linux.intel.com>
- *
- * 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.
- */
-
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <sound/core.h>
-#include "hda_codec.h"
-#include "hda_local.h"
-
-/*
- * The HDMI/DisplayPort configuration can be highly dynamic. A graphics device
- * could support two independent pipes, each of them can be connected to one or
- * more ports (DVI, HDMI or DisplayPort).
- *
- * The HDA correspondence of pipes/ports are converter/pin nodes.
- */
-#define MAX_HDMI_CVTS 3
-#define MAX_HDMI_PINS 3
-
-#include "patch_hdmi.c"
-
-static char *intel_hdmi_pcm_names[MAX_HDMI_CVTS] = {
- "INTEL HDMI 0",
- "INTEL HDMI 1",
- "INTEL HDMI 2",
-};
-
-/*
- * HDMI callbacks
- */
-
-static int intel_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- hdmi_set_channel_count(codec, hinfo->nid,
- substream->runtime->channels);
-
- hdmi_setup_audio_infoframe(codec, hinfo->nid, substream);
-
- return hdmi_setup_stream(codec, hinfo->nid, stream_tag, format);
-}
-
-static int intel_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- return 0;
-}
-
-static struct hda_pcm_stream intel_hdmi_pcm_playback = {
- .substreams = 1,
- .channels_min = 2,
- .ops = {
- .open = hdmi_pcm_open,
- .prepare = intel_hdmi_playback_pcm_prepare,
- .cleanup = intel_hdmi_playback_pcm_cleanup,
- },
-};
-
-static int intel_hdmi_build_pcms(struct hda_codec *codec)
-{
- struct hdmi_spec *spec = codec->spec;
- struct hda_pcm *info = spec->pcm_rec;
- int i;
-
- codec->num_pcms = spec->num_cvts;
- codec->pcm_info = info;
-
- for (i = 0; i < codec->num_pcms; i++, info++) {
- unsigned int chans;
-
- chans = get_wcaps(codec, spec->cvt[i]);
- chans = get_wcaps_channels(chans);
-
- info->name = intel_hdmi_pcm_names[i];
- info->pcm_type = HDA_PCM_TYPE_HDMI;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
- intel_hdmi_pcm_playback;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->cvt[i];
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = chans;
- }
-
- return 0;
-}
-
-static int intel_hdmi_build_controls(struct hda_codec *codec)
-{
- struct hdmi_spec *spec = codec->spec;
- int err;
- int i;
-
- for (i = 0; i < codec->num_pcms; i++) {
- err = snd_hda_create_spdif_out_ctls(codec, spec->cvt[i]);
- if (err < 0)
- return err;
- }
-
- return 0;
-}
-
-static int intel_hdmi_init(struct hda_codec *codec)
-{
- struct hdmi_spec *spec = codec->spec;
- int i;
-
- for (i = 0; spec->pin[i]; i++) {
- hdmi_enable_output(codec, spec->pin[i]);
- snd_hda_codec_write(codec, spec->pin[i], 0,
- AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | spec->pin[i]);
- }
- return 0;
-}
-
-static void intel_hdmi_free(struct hda_codec *codec)
-{
- struct hdmi_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < spec->num_pins; i++)
- snd_hda_eld_proc_free(codec, &spec->sink_eld[i]);
-
- kfree(spec);
-}
-
-static struct hda_codec_ops intel_hdmi_patch_ops = {
- .init = intel_hdmi_init,
- .free = intel_hdmi_free,
- .build_pcms = intel_hdmi_build_pcms,
- .build_controls = intel_hdmi_build_controls,
- .unsol_event = hdmi_unsol_event,
-};
-
-static int patch_intel_hdmi(struct hda_codec *codec)
-{
- struct hdmi_spec *spec;
- int i;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
- if (hdmi_parse_codec(codec) < 0) {
- codec->spec = NULL;
- kfree(spec);
- return -EINVAL;
- }
- codec->patch_ops = intel_hdmi_patch_ops;
-
- for (i = 0; i < spec->num_pins; i++)
- snd_hda_eld_proc_new(codec, &spec->sink_eld[i], i);
-
- init_channel_allocations();
-
- return 0;
-}
-
-static struct hda_codec_preset snd_hda_preset_intelhdmi[] = {
-{ .id = 0x808629fb, .name = "Crestline HDMI", .patch = patch_intel_hdmi },
-{ .id = 0x80862801, .name = "Bearlake HDMI", .patch = patch_intel_hdmi },
-{ .id = 0x80862802, .name = "Cantiga HDMI", .patch = patch_intel_hdmi },
-{ .id = 0x80862803, .name = "Eaglelake HDMI", .patch = patch_intel_hdmi },
-{ .id = 0x80862804, .name = "IbexPeak HDMI", .patch = patch_intel_hdmi },
-{ .id = 0x80860054, .name = "IbexPeak HDMI", .patch = patch_intel_hdmi },
-{ .id = 0x80862805, .name = "CougarPoint HDMI", .patch = patch_intel_hdmi },
-{ .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_intel_hdmi },
-{} /* terminator */
-};
-
-MODULE_ALIAS("snd-hda-codec-id:808629fb");
-MODULE_ALIAS("snd-hda-codec-id:80862801");
-MODULE_ALIAS("snd-hda-codec-id:80862802");
-MODULE_ALIAS("snd-hda-codec-id:80862803");
-MODULE_ALIAS("snd-hda-codec-id:80862804");
-MODULE_ALIAS("snd-hda-codec-id:80862805");
-MODULE_ALIAS("snd-hda-codec-id:80860054");
-MODULE_ALIAS("snd-hda-codec-id:10951392");
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Intel HDMI HD-audio codec");
-
-static struct hda_codec_preset_list intel_list = {
- .preset = snd_hda_preset_intelhdmi,
- .owner = THIS_MODULE,
-};
-
-static int __init patch_intelhdmi_init(void)
-{
- return snd_hda_add_codec_preset(&intel_list);
-}
-
-static void __exit patch_intelhdmi_exit(void)
-{
- snd_hda_delete_codec_preset(&intel_list);
-}
-
-module_init(patch_intelhdmi_init)
-module_exit(patch_intelhdmi_exit)
diff --git a/sound/pci/hda/patch_nvhdmi.c b/sound/pci/hda/patch_nvhdmi.c
deleted file mode 100644
index f636870dc71..00000000000
--- a/sound/pci/hda/patch_nvhdmi.c
+++ /dev/null
@@ -1,616 +0,0 @@
-/*
- * Universal Interface for Intel High Definition Audio Codec
- *
- * HD audio interface patch for NVIDIA HDMI codecs
- *
- * Copyright (c) 2008 NVIDIA Corp. All rights reserved.
- * Copyright (c) 2008 Wei Ni <wni@nvidia.com>
- *
- *
- * This driver 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 driver 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
- */
-
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <sound/core.h>
-#include "hda_codec.h"
-#include "hda_local.h"
-
-#define MAX_HDMI_CVTS 1
-#define MAX_HDMI_PINS 1
-
-#include "patch_hdmi.c"
-
-static char *nvhdmi_pcm_names[MAX_HDMI_CVTS] = {
- "NVIDIA HDMI",
-};
-
-/* define below to restrict the supported rates and formats */
-/* #define LIMITED_RATE_FMT_SUPPORT */
-
-enum HDACodec {
- HDA_CODEC_NVIDIA_MCP7X,
- HDA_CODEC_NVIDIA_MCP89,
- HDA_CODEC_NVIDIA_GT21X,
- HDA_CODEC_INVALID
-};
-
-#define Nv_VERB_SET_Channel_Allocation 0xF79
-#define Nv_VERB_SET_Info_Frame_Checksum 0xF7A
-#define Nv_VERB_SET_Audio_Protection_On 0xF98
-#define Nv_VERB_SET_Audio_Protection_Off 0xF99
-
-#define nvhdmi_master_con_nid_7x 0x04
-#define nvhdmi_master_pin_nid_7x 0x05
-
-#define nvhdmi_master_con_nid_89 0x04
-#define nvhdmi_master_pin_nid_89 0x05
-
-static hda_nid_t nvhdmi_con_nids_7x[4] = {
- /*front, rear, clfe, rear_surr */
- 0x6, 0x8, 0xa, 0xc,
-};
-
-static struct hda_verb nvhdmi_basic_init_7x[] = {
- /* set audio protect on */
- { 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1},
- /* enable digital output on pin widget */
- { 0x5, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
- { 0x7, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
- { 0x9, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
- { 0xb, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
- { 0xd, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
- {} /* terminator */
-};
-
-#ifdef LIMITED_RATE_FMT_SUPPORT
-/* support only the safe format and rate */
-#define SUPPORTED_RATES SNDRV_PCM_RATE_48000
-#define SUPPORTED_MAXBPS 16
-#define SUPPORTED_FORMATS SNDRV_PCM_FMTBIT_S16_LE
-#else
-/* support all rates and formats */
-#define SUPPORTED_RATES \
- (SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
- SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |\
- SNDRV_PCM_RATE_192000)
-#define SUPPORTED_MAXBPS 24
-#define SUPPORTED_FORMATS \
- (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE)
-#endif
-
-/*
- * Controls
- */
-static int nvhdmi_build_controls(struct hda_codec *codec)
-{
- struct hdmi_spec *spec = codec->spec;
- int err;
- int i;
-
- if ((spec->codec_type == HDA_CODEC_NVIDIA_MCP89)
- || (spec->codec_type == HDA_CODEC_NVIDIA_GT21X)) {
- for (i = 0; i < codec->num_pcms; i++) {
- err = snd_hda_create_spdif_out_ctls(codec,
- spec->cvt[i]);
- if (err < 0)
- return err;
- }
- } else {
- err = snd_hda_create_spdif_out_ctls(codec,
- spec->multiout.dig_out_nid);
- if (err < 0)
- return err;
- }
-
- return 0;
-}
-
-static int nvhdmi_init(struct hda_codec *codec)
-{
- struct hdmi_spec *spec = codec->spec;
- int i;
- if ((spec->codec_type == HDA_CODEC_NVIDIA_MCP89)
- || (spec->codec_type == HDA_CODEC_NVIDIA_GT21X)) {
- for (i = 0; spec->pin[i]; i++) {
- hdmi_enable_output(codec, spec->pin[i]);
- snd_hda_codec_write(codec, spec->pin[i], 0,
- AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | spec->pin[i]);
- }
- } else {
- snd_hda_sequence_write(codec, nvhdmi_basic_init_7x);
- }
- return 0;
-}
-
-static void nvhdmi_free(struct hda_codec *codec)
-{
- struct hdmi_spec *spec = codec->spec;
- int i;
-
- if ((spec->codec_type == HDA_CODEC_NVIDIA_MCP89)
- || (spec->codec_type == HDA_CODEC_NVIDIA_GT21X)) {
- for (i = 0; i < spec->num_pins; i++)
- snd_hda_eld_proc_free(codec, &spec->sink_eld[i]);
- }
-
- kfree(spec);
-}
-
-/*
- * Digital out
- */
-static int nvhdmi_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct hdmi_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_open(codec, &spec->multiout);
-}
-
-static int nvhdmi_dig_playback_pcm_close_8ch_7x(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct hdmi_spec *spec = codec->spec;
- int i;
-
- snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x,
- 0, AC_VERB_SET_CHANNEL_STREAMID, 0);
- for (i = 0; i < 4; i++) {
- /* set the stream id */
- snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0,
- AC_VERB_SET_CHANNEL_STREAMID, 0);
- /* set the stream format */
- snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0,
- AC_VERB_SET_STREAM_FORMAT, 0);
- }
-
- return snd_hda_multi_out_dig_close(codec, &spec->multiout);
-}
-
-static int nvhdmi_dig_playback_pcm_close_2ch(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct hdmi_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_close(codec, &spec->multiout);
-}
-
-static int nvhdmi_dig_playback_pcm_prepare_8ch_89(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- hdmi_set_channel_count(codec, hinfo->nid,
- substream->runtime->channels);
-
- hdmi_setup_audio_infoframe(codec, hinfo->nid, substream);
-
- return hdmi_setup_stream(codec, hinfo->nid, stream_tag, format);
-}
-
-static int nvhdmi_dig_playback_pcm_prepare_8ch(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- int chs;
- unsigned int dataDCC1, dataDCC2, chan, chanmask, channel_id;
- int i;
-
- mutex_lock(&codec->spdif_mutex);
-
- chs = substream->runtime->channels;
- chan = chs ? (chs - 1) : 1;
-
- switch (chs) {
- default:
- case 0:
- case 2:
- chanmask = 0x00;
- break;
- case 4:
- chanmask = 0x08;
- break;
- case 6:
- chanmask = 0x0b;
- break;
- case 8:
- chanmask = 0x13;
- break;
- }
- dataDCC1 = AC_DIG1_ENABLE | AC_DIG1_COPYRIGHT;
- dataDCC2 = 0x2;
-
- /* set the Audio InforFrame Channel Allocation */
- snd_hda_codec_write(codec, 0x1, 0,
- Nv_VERB_SET_Channel_Allocation, chanmask);
-
- /* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
- if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE))
- snd_hda_codec_write(codec,
- nvhdmi_master_con_nid_7x,
- 0,
- AC_VERB_SET_DIGI_CONVERT_1,
- codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
-
- /* set the stream id */
- snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0,
- AC_VERB_SET_CHANNEL_STREAMID, (stream_tag << 4) | 0x0);
-
- /* set the stream format */
- snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0,
- AC_VERB_SET_STREAM_FORMAT, format);
-
- /* turn on again (if needed) */
- /* enable and set the channel status audio/data flag */
- if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE)) {
- snd_hda_codec_write(codec,
- nvhdmi_master_con_nid_7x,
- 0,
- AC_VERB_SET_DIGI_CONVERT_1,
- codec->spdif_ctls & 0xff);
- snd_hda_codec_write(codec,
- nvhdmi_master_con_nid_7x,
- 0,
- AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
- }
-
- for (i = 0; i < 4; i++) {
- if (chs == 2)
- channel_id = 0;
- else
- channel_id = i * 2;
-
- /* turn off SPDIF once;
- *otherwise the IEC958 bits won't be updated
- */
- if (codec->spdif_status_reset &&
- (codec->spdif_ctls & AC_DIG1_ENABLE))
- snd_hda_codec_write(codec,
- nvhdmi_con_nids_7x[i],
- 0,
- AC_VERB_SET_DIGI_CONVERT_1,
- codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
- /* set the stream id */
- snd_hda_codec_write(codec,
- nvhdmi_con_nids_7x[i],
- 0,
- AC_VERB_SET_CHANNEL_STREAMID,
- (stream_tag << 4) | channel_id);
- /* set the stream format */
- snd_hda_codec_write(codec,
- nvhdmi_con_nids_7x[i],
- 0,
- AC_VERB_SET_STREAM_FORMAT,
- format);
- /* turn on again (if needed) */
- /* enable and set the channel status audio/data flag */
- if (codec->spdif_status_reset &&
- (codec->spdif_ctls & AC_DIG1_ENABLE)) {
- snd_hda_codec_write(codec,
- nvhdmi_con_nids_7x[i],
- 0,
- AC_VERB_SET_DIGI_CONVERT_1,
- codec->spdif_ctls & 0xff);
- snd_hda_codec_write(codec,
- nvhdmi_con_nids_7x[i],
- 0,
- AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
- }
- }
-
- /* set the Audio Info Frame Checksum */
- snd_hda_codec_write(codec, 0x1, 0,
- Nv_VERB_SET_Info_Frame_Checksum,
- (0x71 - chan - chanmask));
-
- mutex_unlock(&codec->spdif_mutex);
- return 0;
-}
-
-static int nvhdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- return 0;
-}
-
-static int nvhdmi_dig_playback_pcm_prepare_2ch(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct hdmi_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
- format, substream);
-}
-
-static struct hda_pcm_stream nvhdmi_pcm_digital_playback_8ch_89 = {
- .substreams = 1,
- .channels_min = 2,
- .ops = {
- .open = hdmi_pcm_open,
- .prepare = nvhdmi_dig_playback_pcm_prepare_8ch_89,
- .cleanup = nvhdmi_playback_pcm_cleanup,
- },
-};
-
-static struct hda_pcm_stream nvhdmi_pcm_digital_playback_8ch_7x = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 8,
- .nid = nvhdmi_master_con_nid_7x,
- .rates = SUPPORTED_RATES,
- .maxbps = SUPPORTED_MAXBPS,
- .formats = SUPPORTED_FORMATS,
- .ops = {
- .open = nvhdmi_dig_playback_pcm_open,
- .close = nvhdmi_dig_playback_pcm_close_8ch_7x,
- .prepare = nvhdmi_dig_playback_pcm_prepare_8ch
- },
-};
-
-static struct hda_pcm_stream nvhdmi_pcm_digital_playback_2ch = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .nid = nvhdmi_master_con_nid_7x,
- .rates = SUPPORTED_RATES,
- .maxbps = SUPPORTED_MAXBPS,
- .formats = SUPPORTED_FORMATS,
- .ops = {
- .open = nvhdmi_dig_playback_pcm_open,
- .close = nvhdmi_dig_playback_pcm_close_2ch,
- .prepare = nvhdmi_dig_playback_pcm_prepare_2ch
- },
-};
-
-static int nvhdmi_build_pcms_8ch_89(struct hda_codec *codec)
-{
- struct hdmi_spec *spec = codec->spec;
- struct hda_pcm *info = spec->pcm_rec;
- int i;
-
- codec->num_pcms = spec->num_cvts;
- codec->pcm_info = info;
-
- for (i = 0; i < codec->num_pcms; i++, info++) {
- unsigned int chans;
-
- chans = get_wcaps(codec, spec->cvt[i]);
- chans = get_wcaps_channels(chans);
-
- info->name = nvhdmi_pcm_names[i];
- info->pcm_type = HDA_PCM_TYPE_HDMI;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK]
- = nvhdmi_pcm_digital_playback_8ch_89;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->cvt[i];
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = chans;
- }
-
- return 0;
-}
-
-static int nvhdmi_build_pcms_8ch_7x(struct hda_codec *codec)
-{
- struct hdmi_spec *spec = codec->spec;
- struct hda_pcm *info = spec->pcm_rec;
-
- codec->num_pcms = 1;
- codec->pcm_info = info;
-
- info->name = "NVIDIA HDMI";
- info->pcm_type = HDA_PCM_TYPE_HDMI;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK]
- = nvhdmi_pcm_digital_playback_8ch_7x;
-
- return 0;
-}
-
-static int nvhdmi_build_pcms_2ch(struct hda_codec *codec)
-{
- struct hdmi_spec *spec = codec->spec;
- struct hda_pcm *info = spec->pcm_rec;
-
- codec->num_pcms = 1;
- codec->pcm_info = info;
-
- info->name = "NVIDIA HDMI";
- info->pcm_type = HDA_PCM_TYPE_HDMI;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK]
- = nvhdmi_pcm_digital_playback_2ch;
-
- return 0;
-}
-
-static struct hda_codec_ops nvhdmi_patch_ops_8ch_89 = {
- .build_controls = nvhdmi_build_controls,
- .build_pcms = nvhdmi_build_pcms_8ch_89,
- .init = nvhdmi_init,
- .free = nvhdmi_free,
- .unsol_event = hdmi_unsol_event,
-};
-
-static struct hda_codec_ops nvhdmi_patch_ops_8ch_7x = {
- .build_controls = nvhdmi_build_controls,
- .build_pcms = nvhdmi_build_pcms_8ch_7x,
- .init = nvhdmi_init,
- .free = nvhdmi_free,
-};
-
-static struct hda_codec_ops nvhdmi_patch_ops_2ch = {
- .build_controls = nvhdmi_build_controls,
- .build_pcms = nvhdmi_build_pcms_2ch,
- .init = nvhdmi_init,
- .free = nvhdmi_free,
-};
-
-static int patch_nvhdmi_8ch_89(struct hda_codec *codec)
-{
- struct hdmi_spec *spec;
- int i;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
- spec->codec_type = HDA_CODEC_NVIDIA_MCP89;
- spec->old_pin_detect = 1;
-
- if (hdmi_parse_codec(codec) < 0) {
- codec->spec = NULL;
- kfree(spec);
- return -EINVAL;
- }
- codec->patch_ops = nvhdmi_patch_ops_8ch_89;
-
- for (i = 0; i < spec->num_pins; i++)
- snd_hda_eld_proc_new(codec, &spec->sink_eld[i], i);
-
- init_channel_allocations();
-
- return 0;
-}
-
-static int patch_nvhdmi_8ch_7x(struct hda_codec *codec)
-{
- struct hdmi_spec *spec;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
-
- spec->multiout.num_dacs = 0; /* no analog */
- spec->multiout.max_channels = 8;
- spec->multiout.dig_out_nid = nvhdmi_master_con_nid_7x;
- spec->codec_type = HDA_CODEC_NVIDIA_MCP7X;
- spec->old_pin_detect = 1;
-
- codec->patch_ops = nvhdmi_patch_ops_8ch_7x;
-
- return 0;
-}
-
-static int patch_nvhdmi_2ch(struct hda_codec *codec)
-{
- struct hdmi_spec *spec;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
-
- spec->multiout.num_dacs = 0; /* no analog */
- spec->multiout.max_channels = 2;
- spec->multiout.dig_out_nid = nvhdmi_master_con_nid_7x;
- spec->codec_type = HDA_CODEC_NVIDIA_MCP7X;
- spec->old_pin_detect = 1;
-
- codec->patch_ops = nvhdmi_patch_ops_2ch;
-
- return 0;
-}
-
-/*
- * patch entries
- */
-static struct hda_codec_preset snd_hda_preset_nvhdmi[] = {
- { .id = 0x10de0002, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
- { .id = 0x10de0003, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
- { .id = 0x10de0005, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
- { .id = 0x10de0006, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
- { .id = 0x10de0007, .name = "MCP79/7A HDMI", .patch = patch_nvhdmi_8ch_7x },
- { .id = 0x10de000a, .name = "GPU 0a HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
- { .id = 0x10de000b, .name = "GPU 0b HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
- { .id = 0x10de000c, .name = "MCP89 HDMI", .patch = patch_nvhdmi_8ch_89 },
- { .id = 0x10de000d, .name = "GPU 0d HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
- { .id = 0x10de0010, .name = "GPU 10 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
- { .id = 0x10de0011, .name = "GPU 11 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
- { .id = 0x10de0012, .name = "GPU 12 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
- { .id = 0x10de0013, .name = "GPU 13 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
- { .id = 0x10de0014, .name = "GPU 14 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
- { .id = 0x10de0018, .name = "GPU 18 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
- { .id = 0x10de0019, .name = "GPU 19 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
- { .id = 0x10de001a, .name = "GPU 1a HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
- { .id = 0x10de001b, .name = "GPU 1b HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
- { .id = 0x10de001c, .name = "GPU 1c HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
- { .id = 0x10de0040, .name = "GPU 40 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
- { .id = 0x10de0041, .name = "GPU 41 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
- { .id = 0x10de0042, .name = "GPU 42 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
- { .id = 0x10de0043, .name = "GPU 43 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
- { .id = 0x10de0044, .name = "GPU 44 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
- { .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch },
- { .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi_2ch },
- {} /* terminator */
-};
-
-MODULE_ALIAS("snd-hda-codec-id:10de0002");
-MODULE_ALIAS("snd-hda-codec-id:10de0003");
-MODULE_ALIAS("snd-hda-codec-id:10de0005");
-MODULE_ALIAS("snd-hda-codec-id:10de0006");
-MODULE_ALIAS("snd-hda-codec-id:10de0007");
-MODULE_ALIAS("snd-hda-codec-id:10de000a");
-MODULE_ALIAS("snd-hda-codec-id:10de000b");
-MODULE_ALIAS("snd-hda-codec-id:10de000c");
-MODULE_ALIAS("snd-hda-codec-id:10de000d");
-MODULE_ALIAS("snd-hda-codec-id:10de0010");
-MODULE_ALIAS("snd-hda-codec-id:10de0011");
-MODULE_ALIAS("snd-hda-codec-id:10de0012");
-MODULE_ALIAS("snd-hda-codec-id:10de0013");
-MODULE_ALIAS("snd-hda-codec-id:10de0014");
-MODULE_ALIAS("snd-hda-codec-id:10de0018");
-MODULE_ALIAS("snd-hda-codec-id:10de0019");
-MODULE_ALIAS("snd-hda-codec-id:10de001a");
-MODULE_ALIAS("snd-hda-codec-id:10de001b");
-MODULE_ALIAS("snd-hda-codec-id:10de001c");
-MODULE_ALIAS("snd-hda-codec-id:10de0040");
-MODULE_ALIAS("snd-hda-codec-id:10de0041");
-MODULE_ALIAS("snd-hda-codec-id:10de0042");
-MODULE_ALIAS("snd-hda-codec-id:10de0043");
-MODULE_ALIAS("snd-hda-codec-id:10de0044");
-MODULE_ALIAS("snd-hda-codec-id:10de0067");
-MODULE_ALIAS("snd-hda-codec-id:10de8001");
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("NVIDIA HDMI HD-audio codec");
-
-static struct hda_codec_preset_list nvhdmi_list = {
- .preset = snd_hda_preset_nvhdmi,
- .owner = THIS_MODULE,
-};
-
-static int __init patch_nvhdmi_init(void)
-{
- return snd_hda_add_codec_preset(&nvhdmi_list);
-}
-
-static void __exit patch_nvhdmi_exit(void)
-{
- snd_hda_delete_codec_preset(&nvhdmi_list);
-}
-
-module_init(patch_nvhdmi_init)
-module_exit(patch_nvhdmi_exit)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 2cd1ae809e4..5f00589cb79 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -28,6 +28,7 @@
#include <linux/slab.h>
#include <linux/pci.h>
#include <sound/core.h>
+#include <sound/jack.h>
#include "hda_codec.h"
#include "hda_local.h"
#include "hda_beep.h"
@@ -282,6 +283,12 @@ struct alc_mic_route {
unsigned char amix_idx;
};
+struct alc_jack {
+ hda_nid_t nid;
+ int type;
+ struct snd_jack *jack;
+};
+
#define MUX_IDX_UNDEF ((unsigned char)-1)
struct alc_customize_define {
@@ -294,6 +301,7 @@ struct alc_customize_define {
unsigned int platform_type:1;
unsigned int swap:1;
unsigned int override:1;
+ unsigned int fixup:1; /* Means that this sku is set by driver, not read from hw */
};
struct alc_spec {
@@ -357,6 +365,9 @@ struct alc_spec {
/* PCM information */
struct hda_pcm pcm_rec[3]; /* used in alc_build_pcms() */
+ /* jack detection */
+ struct snd_array jacks;
+
/* dynamic controls, init_verbs and input_mux */
struct auto_pin_cfg autocfg;
struct alc_customize_define cdefine;
@@ -383,6 +394,7 @@ struct alc_spec {
unsigned int no_analog :1; /* digital I/O only */
unsigned int dual_adc_switch:1; /* switch ADCs (for ALC275) */
int init_amp;
+ int codec_variant; /* flag for other variants */
/* for virtual master */
hda_nid_t vmaster_nid;
@@ -846,7 +858,7 @@ static void alc_set_input_pin(struct hda_codec *codec, hda_nid_t nid,
{
unsigned int val = PIN_IN;
- if (auto_pin_type <= AUTO_PIN_FRONT_MIC) {
+ if (auto_pin_type == AUTO_PIN_MIC) {
unsigned int pincap;
unsigned int oldval;
oldval = snd_hda_codec_read(codec, nid, 0,
@@ -866,6 +878,28 @@ static void alc_set_input_pin(struct hda_codec *codec, hda_nid_t nid,
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, val);
}
+static void alc_fixup_autocfg_pin_nums(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+
+ if (!cfg->line_outs) {
+ while (cfg->line_outs < AUTO_CFG_MAX_OUTS &&
+ cfg->line_out_pins[cfg->line_outs])
+ cfg->line_outs++;
+ }
+ if (!cfg->speaker_outs) {
+ while (cfg->speaker_outs < AUTO_CFG_MAX_OUTS &&
+ cfg->speaker_pins[cfg->speaker_outs])
+ cfg->speaker_outs++;
+ }
+ if (!cfg->hp_outs) {
+ while (cfg->hp_outs < AUTO_CFG_MAX_OUTS &&
+ cfg->hp_pins[cfg->hp_outs])
+ cfg->hp_outs++;
+ }
+}
+
/*
*/
static void add_mixer(struct alc_spec *spec, struct snd_kcontrol_new *mix)
@@ -934,6 +968,8 @@ static void setup_preset(struct hda_codec *codec,
if (preset->setup)
preset->setup(codec);
+
+ alc_fixup_autocfg_pin_nums(codec);
}
/* Enable GPIO mask and set output */
@@ -990,25 +1026,136 @@ static void alc_fix_pll_init(struct hda_codec *codec, hda_nid_t nid,
alc_fix_pll(codec);
}
-static void alc_automute_pin(struct hda_codec *codec)
+#ifdef CONFIG_SND_HDA_INPUT_JACK
+static void alc_free_jack_priv(struct snd_jack *jack)
+{
+ struct alc_jack *jacks = jack->private_data;
+ jacks->nid = 0;
+ jacks->jack = NULL;
+}
+
+static int alc_add_jack(struct hda_codec *codec,
+ hda_nid_t nid, int type)
+{
+ struct alc_spec *spec;
+ struct alc_jack *jack;
+ const char *name;
+ int err;
+
+ spec = codec->spec;
+ snd_array_init(&spec->jacks, sizeof(*jack), 32);
+ jack = snd_array_new(&spec->jacks);
+ if (!jack)
+ return -ENOMEM;
+
+ jack->nid = nid;
+ jack->type = type;
+ name = (type == SND_JACK_HEADPHONE) ? "Headphone" : "Mic" ;
+
+ err = snd_jack_new(codec->bus->card, name, type, &jack->jack);
+ if (err < 0)
+ return err;
+ jack->jack->private_data = jack;
+ jack->jack->private_free = alc_free_jack_priv;
+ return 0;
+}
+
+static void alc_report_jack(struct hda_codec *codec, hda_nid_t nid)
{
struct alc_spec *spec = codec->spec;
- unsigned int nid = spec->autocfg.hp_pins[0];
+ struct alc_jack *jacks = spec->jacks.list;
+
+ if (jacks) {
+ int i;
+ for (i = 0; i < spec->jacks.used; i++) {
+ if (jacks->nid == nid) {
+ unsigned int present;
+ present = snd_hda_jack_detect(codec, nid);
+
+ present = (present) ? jacks->type : 0;
+
+ snd_jack_report(jacks->jack, present);
+ }
+ jacks++;
+ }
+ }
+}
+
+static int alc_init_jacks(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ int err;
+ unsigned int hp_nid = spec->autocfg.hp_pins[0];
+ unsigned int mic_nid = spec->ext_mic.pin;
+
+ if (hp_nid) {
+ err = alc_add_jack(codec, hp_nid, SND_JACK_HEADPHONE);
+ if (err < 0)
+ return err;
+ alc_report_jack(codec, hp_nid);
+ }
+
+ if (mic_nid) {
+ err = alc_add_jack(codec, mic_nid, SND_JACK_MICROPHONE);
+ if (err < 0)
+ return err;
+ alc_report_jack(codec, mic_nid);
+ }
+
+ return 0;
+}
+#else
+static inline void alc_report_jack(struct hda_codec *codec, hda_nid_t nid)
+{
+}
+
+static inline int alc_init_jacks(struct hda_codec *codec)
+{
+ return 0;
+}
+#endif
+
+static void alc_automute_speaker(struct hda_codec *codec, int pinctl)
+{
+ struct alc_spec *spec = codec->spec;
+ unsigned int mute;
+ hda_nid_t nid;
int i;
- if (!nid)
- return;
- spec->jack_present = snd_hda_jack_detect(codec, nid);
+ spec->jack_present = 0;
+ for (i = 0; i < ARRAY_SIZE(spec->autocfg.hp_pins); i++) {
+ nid = spec->autocfg.hp_pins[i];
+ if (!nid)
+ break;
+ if (snd_hda_jack_detect(codec, nid)) {
+ spec->jack_present = 1;
+ break;
+ }
+ alc_report_jack(codec, spec->autocfg.hp_pins[i]);
+ }
+
+ mute = spec->jack_present ? HDA_AMP_MUTE : 0;
+ /* Toggle internal speakers muting */
for (i = 0; i < ARRAY_SIZE(spec->autocfg.speaker_pins); i++) {
nid = spec->autocfg.speaker_pins[i];
if (!nid)
break;
- snd_hda_codec_write(codec, nid, 0,
+ if (pinctl) {
+ snd_hda_codec_write(codec, nid, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL,
spec->jack_present ? 0 : PIN_OUT);
+ } else {
+ snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, mute);
+ }
}
}
+static void alc_automute_pin(struct hda_codec *codec)
+{
+ alc_automute_speaker(codec, 1);
+}
+
static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
hda_nid_t nid)
{
@@ -1090,6 +1237,7 @@ static void alc_mic_automute(struct hda_codec *codec)
AC_VERB_SET_CONNECT_SEL,
alive->mux_idx);
}
+ alc_report_jack(codec, spec->ext_mic.pin);
/* FIXME: analog mixer */
}
@@ -1236,24 +1384,35 @@ static void alc_auto_init_amp(struct hda_codec *codec, int type)
static void alc_init_auto_hp(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i;
- if (!spec->autocfg.hp_pins[0])
- return;
+ if (!cfg->hp_pins[0]) {
+ if (cfg->line_out_type != AUTO_PIN_HP_OUT)
+ return;
+ }
- if (!spec->autocfg.speaker_pins[0]) {
- if (spec->autocfg.line_out_pins[0] &&
- spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT)
- spec->autocfg.speaker_pins[0] =
- spec->autocfg.line_out_pins[0];
- else
+ if (!cfg->speaker_pins[0]) {
+ if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
return;
+ memcpy(cfg->speaker_pins, cfg->line_out_pins,
+ sizeof(cfg->speaker_pins));
+ cfg->speaker_outs = cfg->line_outs;
}
- snd_printdd("realtek: Enable HP auto-muting on NID 0x%x\n",
- spec->autocfg.hp_pins[0]);
- snd_hda_codec_write_cache(codec, spec->autocfg.hp_pins[0], 0,
+ if (!cfg->hp_pins[0]) {
+ memcpy(cfg->hp_pins, cfg->line_out_pins,
+ sizeof(cfg->hp_pins));
+ cfg->hp_outs = cfg->line_outs;
+ }
+
+ for (i = 0; i < cfg->hp_outs; i++) {
+ snd_printdd("realtek: Enable HP auto-muting on NID 0x%x\n",
+ cfg->hp_pins[i]);
+ snd_hda_codec_write_cache(codec, cfg->hp_pins[i], 0,
AC_VERB_SET_UNSOLICITED_ENABLE,
AC_USRSP_EN | ALC880_HP_EVENT);
+ }
spec->unsol_event = alc_sku_unsol_event;
}
@@ -1265,30 +1424,28 @@ static void alc_init_auto_mic(struct hda_codec *codec)
int i;
/* there must be only two mic inputs exclusively */
- for (i = AUTO_PIN_LINE; i < AUTO_PIN_LAST; i++)
- if (cfg->input_pins[i])
+ for (i = 0; i < cfg->num_inputs; i++)
+ if (cfg->inputs[i].type >= AUTO_PIN_LINE_IN)
return;
fixed = ext = 0;
- for (i = AUTO_PIN_MIC; i <= AUTO_PIN_FRONT_MIC; i++) {
- hda_nid_t nid = cfg->input_pins[i];
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
unsigned int defcfg;
- if (!nid)
- return;
defcfg = snd_hda_codec_get_pincfg(codec, nid);
- switch (get_defcfg_connect(defcfg)) {
- case AC_JACK_PORT_FIXED:
+ switch (snd_hda_get_input_pin_attr(defcfg)) {
+ case INPUT_PIN_ATTR_INT:
if (fixed)
return; /* already occupied */
fixed = nid;
break;
- case AC_JACK_PORT_COMPLEX:
+ case INPUT_PIN_ATTR_UNUSED:
+ return; /* invalid entry */
+ default:
if (ext)
return; /* already occupied */
ext = nid;
break;
- default:
- return; /* invalid entry */
}
}
if (!ext || !fixed)
@@ -1308,6 +1465,11 @@ static void alc_init_auto_mic(struct hda_codec *codec)
spec->unsol_event = alc_sku_unsol_event;
}
+/* Could be any non-zero and even value. When used as fixup, tells
+ * the driver to ignore any present sku defines.
+ */
+#define ALC_FIXUP_SKU_IGNORE (2)
+
static int alc_auto_parse_customize_define(struct hda_codec *codec)
{
unsigned int ass, tmp, i;
@@ -1316,6 +1478,13 @@ static int alc_auto_parse_customize_define(struct hda_codec *codec)
spec->cdefine.enable_pcbeep = 1; /* assume always enabled */
+ if (spec->cdefine.fixup) {
+ ass = spec->cdefine.sku_cfg;
+ if (ass == ALC_FIXUP_SKU_IGNORE)
+ return -1;
+ goto do_sku;
+ }
+
ass = codec->subsystem_id & 0xffff;
if (ass != codec->bus->pci->subsystem_device && (ass & 1))
goto do_sku;
@@ -1383,6 +1552,13 @@ static int alc_subsystem_id(struct hda_codec *codec,
unsigned nid;
struct alc_spec *spec = codec->spec;
+ if (spec->cdefine.fixup) {
+ ass = spec->cdefine.sku_cfg;
+ if (ass == ALC_FIXUP_SKU_IGNORE)
+ return 0;
+ goto do_sku;
+ }
+
ass = codec->subsystem_id & 0xffff;
if ((ass != codec->bus->pci->subsystem_device) && (ass & 1))
goto do_sku;
@@ -1502,6 +1678,7 @@ struct alc_pincfg {
};
struct alc_fixup {
+ unsigned int sku;
const struct alc_pincfg *pins;
const struct hda_verb *verbs;
};
@@ -1512,12 +1689,22 @@ static void alc_pick_fixup(struct hda_codec *codec,
int pre_init)
{
const struct alc_pincfg *cfg;
+ struct alc_spec *spec;
quirk = snd_pci_quirk_lookup(codec->bus->pci, quirk);
if (!quirk)
return;
fix += quirk->value;
cfg = fix->pins;
+ if (pre_init && fix->sku) {
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+ snd_printdd(KERN_INFO "hda_codec: %s: Apply sku override for %s\n",
+ codec->chip_name, quirk->name);
+#endif
+ spec = codec->spec;
+ spec->cdefine.sku_cfg = fix->sku;
+ spec->cdefine.fixup = 1;
+ }
if (pre_init && cfg) {
#ifdef CONFIG_SND_DEBUG_VERBOSE
snd_printdd(KERN_INFO "hda_codec: %s: Apply pincfg for %s\n",
@@ -1546,6 +1733,15 @@ static int alc_read_coef_idx(struct hda_codec *codec,
return val;
}
+static void alc_write_coef_idx(struct hda_codec *codec, unsigned int coef_idx,
+ unsigned int coef_val)
+{
+ snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX,
+ coef_idx);
+ snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF,
+ coef_val);
+}
+
/* set right pin controls for digital I/O */
static void alc_auto_init_digital(struct hda_codec *codec)
{
@@ -1594,12 +1790,22 @@ static void alc_auto_parse_digital(struct hda_codec *codec)
}
if (spec->autocfg.dig_in_pin) {
- hda_nid_t dig_nid;
- err = snd_hda_get_connections(codec,
- spec->autocfg.dig_in_pin,
- &dig_nid, 1);
- if (err > 0)
- spec->dig_in_nid = dig_nid;
+ dig_nid = codec->start_nid;
+ for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
+ unsigned int wcaps = get_wcaps(codec, dig_nid);
+ if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
+ continue;
+ if (!(wcaps & AC_WCAP_DIGITAL))
+ continue;
+ if (!(wcaps & AC_WCAP_CONN_LIST))
+ continue;
+ err = get_connection_index(codec, dig_nid,
+ spec->autocfg.dig_in_pin);
+ if (err >= 0) {
+ spec->dig_in_nid = dig_nid;
+ break;
+ }
+ }
}
}
@@ -1713,31 +1919,7 @@ static struct hda_verb alc888_fujitsu_xa3530_verbs[] = {
static void alc_automute_amp(struct hda_codec *codec)
{
- struct alc_spec *spec = codec->spec;
- unsigned int mute;
- hda_nid_t nid;
- int i;
-
- spec->jack_present = 0;
- for (i = 0; i < ARRAY_SIZE(spec->autocfg.hp_pins); i++) {
- nid = spec->autocfg.hp_pins[i];
- if (!nid)
- break;
- if (snd_hda_jack_detect(codec, nid)) {
- spec->jack_present = 1;
- break;
- }
- }
-
- mute = spec->jack_present ? HDA_AMP_MUTE : 0;
- /* Toggle internal speakers muting */
- for (i = 0; i < ARRAY_SIZE(spec->autocfg.speaker_pins); i++) {
- nid = spec->autocfg.speaker_pins[i];
- if (!nid)
- break;
- snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, mute);
- }
+ alc_automute_speaker(codec, 0);
}
static void alc_automute_amp_unsol_event(struct hda_codec *codec,
@@ -3592,10 +3774,7 @@ static int alc_init(struct hda_codec *codec)
if (spec->init_hook)
spec->init_hook(codec);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (codec->patch_ops.check_power_status)
- codec->patch_ops.check_power_status(codec, 0x01);
-#endif
+ hda_call_check_power_status(codec, 0x01);
return 0;
}
@@ -3991,10 +4170,7 @@ static int alc_resume(struct hda_codec *codec)
codec->patch_ops.init(codec);
snd_hda_codec_resume_amp(codec);
snd_hda_codec_resume_cache(codec);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (codec->patch_ops.check_power_status)
- codec->patch_ops.check_power_status(codec, 0x01);
-#endif
+ hda_call_check_power_status(codec, 0x01);
return 0;
}
#endif
@@ -4719,7 +4895,7 @@ static struct snd_kcontrol_new alc880_control_templates[] = {
/* add dynamic controls */
static int add_control(struct alc_spec *spec, int type, const char *name,
- unsigned long val)
+ int cidx, unsigned long val)
{
struct snd_kcontrol_new *knew;
@@ -4731,6 +4907,7 @@ static int add_control(struct alc_spec *spec, int type, const char *name,
knew->name = kstrdup(name, GFP_KERNEL);
if (!knew->name)
return -ENOMEM;
+ knew->index = cidx;
if (get_amp_nid_(val))
knew->subdevice = HDA_SUBDEV_AMP_FLAG;
knew->private_value = val;
@@ -4739,17 +4916,21 @@ static int add_control(struct alc_spec *spec, int type, const char *name,
static int add_control_with_pfx(struct alc_spec *spec, int type,
const char *pfx, const char *dir,
- const char *sfx, unsigned long val)
+ const char *sfx, int cidx, unsigned long val)
{
char name[32];
snprintf(name, sizeof(name), "%s %s %s", pfx, dir, sfx);
- return add_control(spec, type, name, val);
+ return add_control(spec, type, name, cidx, val);
}
-#define add_pb_vol_ctrl(spec, type, pfx, val) \
- add_control_with_pfx(spec, type, pfx, "Playback", "Volume", val)
-#define add_pb_sw_ctrl(spec, type, pfx, val) \
- add_control_with_pfx(spec, type, pfx, "Playback", "Switch", val)
+#define add_pb_vol_ctrl(spec, type, pfx, val) \
+ add_control_with_pfx(spec, type, pfx, "Playback", "Volume", 0, val)
+#define add_pb_sw_ctrl(spec, type, pfx, val) \
+ add_control_with_pfx(spec, type, pfx, "Playback", "Switch", 0, val)
+#define __add_pb_vol_ctrl(spec, type, pfx, cidx, val) \
+ add_control_with_pfx(spec, type, pfx, "Playback", "Volume", cidx, val)
+#define __add_pb_sw_ctrl(spec, type, pfx, cidx, val) \
+ add_control_with_pfx(spec, type, pfx, "Playback", "Switch", cidx, val)
#define alc880_is_fixed_pin(nid) ((nid) >= 0x14 && (nid) <= 0x17)
#define alc880_fixed_pin_idx(nid) ((nid) - 0x14)
@@ -4902,16 +5083,16 @@ static int alc880_auto_create_extra_out(struct alc_spec *spec, hda_nid_t pin,
/* create input playback/capture controls for the given pin */
static int new_analog_input(struct alc_spec *spec, hda_nid_t pin,
- const char *ctlname,
+ const char *ctlname, int ctlidx,
int idx, hda_nid_t mix_nid)
{
int err;
- err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname,
+ err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname, ctlidx,
HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
if (err < 0)
return err;
- err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname,
+ err = __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname, ctlidx,
HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
if (err < 0)
return err;
@@ -4932,20 +5113,27 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec,
{
struct alc_spec *spec = codec->spec;
struct hda_input_mux *imux = &spec->private_imux[0];
- int i, err, idx;
+ int i, err, idx, type, type_idx = 0;
- for (i = 0; i < AUTO_PIN_LAST; i++) {
+ for (i = 0; i < cfg->num_inputs; i++) {
hda_nid_t pin;
+ const char *label;
- pin = cfg->input_pins[i];
+ pin = cfg->inputs[i].pin;
if (!alc_is_input_pin(codec, pin))
continue;
+ type = cfg->inputs[i].type;
+ if (i > 0 && type == cfg->inputs[i - 1].type)
+ type_idx++;
+ else
+ type_idx = 0;
+ label = hda_get_autocfg_input_label(codec, cfg, i);
if (mixer) {
idx = get_connection_index(codec, mixer, pin);
if (idx >= 0) {
err = new_analog_input(spec, pin,
- auto_pin_cfg_labels[i],
+ label, type_idx,
idx, mixer);
if (err < 0)
return err;
@@ -4957,12 +5145,8 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec,
idx = get_connection_index(codec, cap1, pin);
if (idx < 0 && cap2)
idx = get_connection_index(codec, cap2, pin);
- if (idx >= 0) {
- imux->items[imux->num_items].label =
- auto_pin_cfg_labels[i];
- imux->items[imux->num_items].index = idx;
- imux->num_items++;
- }
+ if (idx >= 0)
+ snd_hda_add_imux_item(imux, label, idx, NULL);
}
return 0;
}
@@ -5034,12 +5218,13 @@ static void alc880_auto_init_extra_out(struct hda_codec *codec)
static void alc880_auto_init_analog_input(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
int i;
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- hda_nid_t nid = spec->autocfg.input_pins[i];
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
if (alc_is_input_pin(codec, nid)) {
- alc_set_input_pin(codec, nid, i);
+ alc_set_input_pin(codec, nid, cfg->inputs[i].type);
if (nid != ALC880_PIN_CD_NID &&
(get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
snd_hda_codec_write(codec, nid, 0,
@@ -5204,19 +5389,13 @@ static int init_capsrc_for_pin(struct hda_codec *codec, hda_nid_t pin)
static void fixup_single_adc(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- hda_nid_t pin = 0;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
int i;
/* search for the input pin; there must be only one */
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- if (spec->autocfg.input_pins[i]) {
- pin = spec->autocfg.input_pins[i];
- break;
- }
- }
- if (!pin)
+ if (cfg->num_inputs != 1)
return;
- i = init_capsrc_for_pin(codec, pin);
+ i = init_capsrc_for_pin(codec, cfg->inputs[0].pin);
if (i >= 0) {
/* use only this ADC */
if (spec->capsrc_nids)
@@ -5269,6 +5448,7 @@ static void fillup_priv_adc_nids(struct hda_codec *codec, hda_nid_t *nids,
int num_nids)
{
struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
int n;
hda_nid_t fallback_adc = 0, fallback_cap = 0;
@@ -5294,10 +5474,8 @@ static void fillup_priv_adc_nids(struct hda_codec *codec, hda_nid_t *nids,
fallback_adc = adc;
fallback_cap = cap;
}
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- hda_nid_t nid = spec->autocfg.input_pins[i];
- if (!nid)
- continue;
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
for (j = 0; j < nconns; j++) {
if (conn[j] == nid)
break;
@@ -5305,7 +5483,7 @@ static void fillup_priv_adc_nids(struct hda_codec *codec, hda_nid_t *nids,
if (j >= nconns)
break;
}
- if (i >= AUTO_PIN_LAST) {
+ if (i >= cfg->num_inputs) {
int num_adcs = spec->num_adc_nids;
spec->private_adc_nids[num_adcs] = adc;
spec->private_capsrc_nids[num_adcs] = cap;
@@ -5334,6 +5512,7 @@ static void fillup_priv_adc_nids(struct hda_codec *codec, hda_nid_t *nids,
static struct snd_pci_quirk beep_white_list[] = {
SND_PCI_QUIRK(0x1043, 0x829f, "ASUS", 1),
+ SND_PCI_QUIRK(0x1043, 0x83ce, "EeePC", 1),
SND_PCI_QUIRK(0x8086, 0xd613, "Intel", 1),
{}
};
@@ -6672,12 +6851,13 @@ static void alc260_auto_init_multi_out(struct hda_codec *codec)
static void alc260_auto_init_analog_input(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
int i;
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- hda_nid_t nid = spec->autocfg.input_pins[i];
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
if (nid >= 0x12) {
- alc_set_input_pin(codec, nid, i);
+ alc_set_input_pin(codec, nid, cfg->inputs[i].type);
if (nid != ALC260_PIN_CD_NID &&
(get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
snd_hda_codec_write(codec, nid, 0,
@@ -6799,14 +6979,12 @@ enum {
PINFIX_HP_DC5750,
};
-static struct alc_pincfg alc260_hp_dc5750_pinfix[] = {
- { 0x11, 0x90130110 }, /* speaker */
- { }
-};
-
static const struct alc_fixup alc260_fixups[] = {
[PINFIX_HP_DC5750] = {
- .pins = alc260_hp_dc5750_pinfix
+ .pins = (const struct alc_pincfg[]) {
+ { 0x11, 0x90130110 }, /* speaker */
+ { }
+ }
},
};
@@ -10450,32 +10628,33 @@ static struct alc_config_preset alc882_presets[] = {
enum {
PINFIX_ABIT_AW9D_MAX,
PINFIX_PB_M5210,
-};
-
-static struct alc_pincfg alc882_abit_aw9d_pinfix[] = {
- { 0x15, 0x01080104 }, /* side */
- { 0x16, 0x01011012 }, /* rear */
- { 0x17, 0x01016011 }, /* clfe */
- { }
-};
-
-static const struct hda_verb pb_m5210_verbs[] = {
- { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50 },
- {}
+ PINFIX_ACER_ASPIRE_7736,
};
static const struct alc_fixup alc882_fixups[] = {
[PINFIX_ABIT_AW9D_MAX] = {
- .pins = alc882_abit_aw9d_pinfix
+ .pins = (const struct alc_pincfg[]) {
+ { 0x15, 0x01080104 }, /* side */
+ { 0x16, 0x01011012 }, /* rear */
+ { 0x17, 0x01016011 }, /* clfe */
+ { }
+ }
},
[PINFIX_PB_M5210] = {
- .verbs = pb_m5210_verbs
+ .verbs = (const struct hda_verb[]) {
+ { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50 },
+ {}
+ }
+ },
+ [PINFIX_ACER_ASPIRE_7736] = {
+ .sku = ALC_FIXUP_SKU_IGNORE,
},
};
static struct snd_pci_quirk alc882_fixup_tbl[] = {
SND_PCI_QUIRK(0x1025, 0x0155, "Packard-Bell M5120", PINFIX_PB_M5210),
SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", PINFIX_ABIT_AW9D_MAX),
+ SND_PCI_QUIRK(0x1025, 0x0296, "Acer Aspire 7736z", PINFIX_ACER_ASPIRE_7736),
{}
};
@@ -10524,16 +10703,21 @@ static void alc882_auto_init_hp_out(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
hda_nid_t pin, dac;
+ int i;
- pin = spec->autocfg.hp_pins[0];
- if (pin) {
+ for (i = 0; i < ARRAY_SIZE(spec->autocfg.hp_pins); i++) {
+ pin = spec->autocfg.hp_pins[i];
+ if (!pin)
+ break;
dac = spec->multiout.hp_nid;
if (!dac)
dac = spec->multiout.dac_nids[0]; /* to front */
alc882_auto_set_output_and_unmute(codec, pin, PIN_HP, dac);
}
- pin = spec->autocfg.speaker_pins[0];
- if (pin) {
+ for (i = 0; i < ARRAY_SIZE(spec->autocfg.speaker_pins); i++) {
+ pin = spec->autocfg.speaker_pins[i];
+ if (!pin)
+ break;
dac = spec->multiout.extra_out_nid[0];
if (!dac)
dac = spec->multiout.dac_nids[0]; /* to front */
@@ -10544,13 +10728,12 @@ static void alc882_auto_init_hp_out(struct hda_codec *codec)
static void alc882_auto_init_analog_input(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
int i;
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- hda_nid_t nid = spec->autocfg.input_pins[i];
- if (!nid)
- continue;
- alc_set_input_pin(codec, nid, i);
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
+ alc_set_input_pin(codec, nid, cfg->inputs[i].type);
if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)
snd_hda_codec_write(codec, nid, 0,
AC_VERB_SET_AMP_GAIN_MUTE,
@@ -10612,24 +10795,23 @@ static void alc882_auto_init_input_src(struct hda_codec *codec)
static int alc_auto_add_mic_boost(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- int err;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i, err;
hda_nid_t nid;
- nid = spec->autocfg.input_pins[AUTO_PIN_MIC];
- if (nid && (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) {
- err = add_control(spec, ALC_CTL_WIDGET_VOL,
- "Mic Boost",
- HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT));
- if (err < 0)
- return err;
- }
- nid = spec->autocfg.input_pins[AUTO_PIN_FRONT_MIC];
- if (nid && (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) {
- err = add_control(spec, ALC_CTL_WIDGET_VOL,
- "Front Mic Boost",
+ for (i = 0; i < cfg->num_inputs; i++) {
+ if (cfg->inputs[i].type > AUTO_PIN_MIC)
+ break;
+ nid = cfg->inputs[i].pin;
+ if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) {
+ char label[32];
+ snprintf(label, sizeof(label), "%s Boost",
+ hda_get_autocfg_input_label(codec, cfg, i));
+ err = add_control(spec, ALC_CTL_WIDGET_VOL, label, 0,
HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT));
- if (err < 0)
- return err;
+ if (err < 0)
+ return err;
+ }
}
return 0;
}
@@ -10715,8 +10897,6 @@ static int patch_alc882(struct hda_codec *codec)
codec->spec = spec;
- alc_auto_parse_customize_define(codec);
-
switch (codec->vendor_id) {
case 0x10ec0882:
case 0x10ec0885:
@@ -10744,6 +10924,8 @@ static int patch_alc882(struct hda_codec *codec)
if (board_config == ALC882_AUTO)
alc_pick_fixup(codec, alc882_fixup_tbl, alc882_fixups, 1);
+ alc_auto_parse_customize_define(codec);
+
if (board_config == ALC882_AUTO) {
/* automatic parse from the BIOS config */
err = alc882_parse_auto_config(codec);
@@ -10824,6 +11006,8 @@ static int patch_alc882(struct hda_codec *codec)
codec->patch_ops = alc_patch_ops;
if (board_config == ALC882_AUTO)
spec->init_hook = alc882_auto_init;
+
+ alc_init_jacks(codec);
#ifdef CONFIG_SND_HDA_POWER_SAVE
if (!spec->loopback.amplist)
spec->loopback.amplist = alc882_loopbacks;
@@ -11820,7 +12004,7 @@ static int alc262_check_volbit(hda_nid_t nid)
}
static int alc262_add_out_vol_ctl(struct alc_spec *spec, hda_nid_t nid,
- const char *pfx, int *vbits)
+ const char *pfx, int *vbits, int idx)
{
unsigned long val;
int vbit;
@@ -11835,11 +12019,11 @@ static int alc262_add_out_vol_ctl(struct alc_spec *spec, hda_nid_t nid,
val = HDA_COMPOSE_AMP_VAL(0x0e, 2, 0, HDA_OUTPUT);
else
val = HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT);
- return add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, val);
+ return __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, idx, val);
}
static int alc262_add_out_sw_ctl(struct alc_spec *spec, hda_nid_t nid,
- const char *pfx)
+ const char *pfx, int idx)
{
unsigned long val;
@@ -11849,7 +12033,7 @@ static int alc262_add_out_sw_ctl(struct alc_spec *spec, hda_nid_t nid,
val = HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT);
else
val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
- return add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, val);
+ return __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, idx, val);
}
/* add playback controls from the parsed DAC table */
@@ -11858,7 +12042,7 @@ static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec,
{
const char *pfx;
int vbits;
- int err;
+ int i, err;
spec->multiout.num_dacs = 1; /* only use one dac */
spec->multiout.dac_nids = spec->private_dac_nids;
@@ -11868,39 +12052,52 @@ static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec,
pfx = "Master";
else if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
pfx = "Speaker";
+ else if (cfg->line_out_type == AUTO_PIN_HP_OUT)
+ pfx = "Headphone";
else
pfx = "Front";
- err = alc262_add_out_sw_ctl(spec, cfg->line_out_pins[0], pfx);
- if (err < 0)
- return err;
- err = alc262_add_out_sw_ctl(spec, cfg->speaker_pins[0], "Speaker");
- if (err < 0)
- return err;
- err = alc262_add_out_sw_ctl(spec, cfg->hp_pins[0], "Headphone");
- if (err < 0)
- return err;
+ for (i = 0; i < 2; i++) {
+ err = alc262_add_out_sw_ctl(spec, cfg->line_out_pins[i], pfx, i);
+ if (err < 0)
+ return err;
+ if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
+ err = alc262_add_out_sw_ctl(spec, cfg->speaker_pins[i],
+ "Speaker", i);
+ if (err < 0)
+ return err;
+ }
+ if (cfg->line_out_type != AUTO_PIN_HP_OUT) {
+ err = alc262_add_out_sw_ctl(spec, cfg->hp_pins[i],
+ "Headphone", i);
+ if (err < 0)
+ return err;
+ }
+ }
vbits = alc262_check_volbit(cfg->line_out_pins[0]) |
alc262_check_volbit(cfg->speaker_pins[0]) |
alc262_check_volbit(cfg->hp_pins[0]);
if (vbits == 1 || vbits == 2)
pfx = "Master"; /* only one mixer is used */
- else if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
- pfx = "Speaker";
- else
- pfx = "Front";
vbits = 0;
- err = alc262_add_out_vol_ctl(spec, cfg->line_out_pins[0], pfx, &vbits);
- if (err < 0)
- return err;
- err = alc262_add_out_vol_ctl(spec, cfg->speaker_pins[0], "Speaker",
- &vbits);
- if (err < 0)
- return err;
- err = alc262_add_out_vol_ctl(spec, cfg->hp_pins[0], "Headphone",
- &vbits);
- if (err < 0)
- return err;
+ for (i = 0; i < 2; i++) {
+ err = alc262_add_out_vol_ctl(spec, cfg->line_out_pins[i], pfx,
+ &vbits, i);
+ if (err < 0)
+ return err;
+ if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
+ err = alc262_add_out_vol_ctl(spec, cfg->speaker_pins[i],
+ "Speaker", &vbits, i);
+ if (err < 0)
+ return err;
+ }
+ if (cfg->line_out_type != AUTO_PIN_HP_OUT) {
+ err = alc262_add_out_vol_ctl(spec, cfg->hp_pins[i],
+ "Headphone", &vbits, i);
+ if (err < 0)
+ return err;
+ }
+ }
return 0;
}
@@ -12188,6 +12385,35 @@ static struct hda_verb alc262_toshiba_rx1_unsol_verbs[] = {
{}
};
+/*
+ * Pin config fixes
+ */
+enum {
+ PINFIX_FSC_H270,
+};
+
+static const struct alc_fixup alc262_fixups[] = {
+ [PINFIX_FSC_H270] = {
+ .pins = (const struct alc_pincfg[]) {
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x15, 0x0221142f }, /* front HP */
+ { 0x1b, 0x0121141f }, /* rear HP */
+ { }
+ }
+ },
+ [PINFIX_PB_M5210] = {
+ .verbs = (const struct hda_verb[]) {
+ { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50 },
+ {}
+ }
+ },
+};
+
+static struct snd_pci_quirk alc262_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x1734, 0x1147, "FSC Celsius H270", PINFIX_FSC_H270),
+ {}
+};
+
#ifdef CONFIG_SND_HDA_POWER_SAVE
#define alc262_loopbacks alc880_loopbacks
@@ -12611,6 +12837,9 @@ static int patch_alc262(struct hda_codec *codec)
board_config = ALC262_AUTO;
}
+ if (board_config == ALC262_AUTO)
+ alc_pick_fixup(codec, alc262_fixup_tbl, alc262_fixups, 1);
+
if (board_config == ALC262_AUTO) {
/* automatic parse from the BIOS config */
err = alc262_parse_auto_config(codec);
@@ -12679,11 +12908,16 @@ static int patch_alc262(struct hda_codec *codec)
if (!spec->no_analog && has_cdefine_beep(codec))
set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
+ if (board_config == ALC262_AUTO)
+ alc_pick_fixup(codec, alc262_fixup_tbl, alc262_fixups, 0);
+
spec->vmaster_nid = 0x0c;
codec->patch_ops = alc_patch_ops;
if (board_config == ALC262_AUTO)
spec->init_hook = alc262_auto_init;
+
+ alc_init_jacks(codec);
#ifdef CONFIG_SND_HDA_POWER_SAVE
if (!spec->loopback.amplist)
spec->loopback.amplist = alc262_loopbacks;
@@ -13299,8 +13533,10 @@ static void alc268_auto_set_output_and_unmute(struct hda_codec *codec,
static void alc268_auto_init_multi_out(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- hda_nid_t nid = spec->autocfg.line_out_pins[0];
- if (nid) {
+ int i;
+
+ for (i = 0; i < spec->autocfg.line_outs; i++) {
+ hda_nid_t nid = spec->autocfg.line_out_pins[i];
int pin_type = get_pin_type(spec->autocfg.line_out_type);
alc268_auto_set_output_and_unmute(codec, nid, pin_type);
}
@@ -13310,13 +13546,19 @@ static void alc268_auto_init_hp_out(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
hda_nid_t pin;
+ int i;
- pin = spec->autocfg.hp_pins[0];
- if (pin)
+ for (i = 0; i < spec->autocfg.hp_outs; i++) {
+ pin = spec->autocfg.hp_pins[i];
alc268_auto_set_output_and_unmute(codec, pin, PIN_HP);
- pin = spec->autocfg.speaker_pins[0];
- if (pin)
+ }
+ for (i = 0; i < spec->autocfg.speaker_outs; i++) {
+ pin = spec->autocfg.speaker_pins[i];
alc268_auto_set_output_and_unmute(codec, pin, PIN_OUT);
+ }
+ if (spec->autocfg.mono_out_pin)
+ snd_hda_codec_write(codec, spec->autocfg.mono_out_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
}
static void alc268_auto_init_mono_speaker_out(struct hda_codec *codec)
@@ -13755,6 +13997,8 @@ static int patch_alc268(struct hda_codec *codec)
if (board_config == ALC268_AUTO)
spec->init_hook = alc268_auto_init;
+ alc_init_jacks(codec);
+
return 0;
}
@@ -14121,6 +14365,7 @@ static void alc269_speaker_automute(struct hda_codec *codec)
HDA_AMP_MUTE, bits);
snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
HDA_AMP_MUTE, bits);
+ alc_report_jack(codec, nid);
}
/* unsolicited event for HP jack sensing */
@@ -14375,6 +14620,13 @@ static int alc275_setup_dual_adc(struct hda_codec *codec)
return 0;
}
+/* different alc269-variants */
+enum {
+ ALC269_TYPE_NORMAL,
+ ALC269_TYPE_ALC259,
+ ALC269_TYPE_ALC271X,
+};
+
/*
* BIOS auto configuration
*/
@@ -14392,7 +14644,11 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
err = alc269_auto_create_multi_out_ctls(spec, &spec->autocfg);
if (err < 0)
return err;
- err = alc269_auto_create_input_ctls(codec, &spec->autocfg);
+ if (spec->codec_variant == ALC269_TYPE_NORMAL)
+ err = alc269_auto_create_input_ctls(codec, &spec->autocfg);
+ else
+ err = alc_auto_create_input_ctls(codec, &spec->autocfg, 0,
+ 0x22, 0);
if (err < 0)
return err;
@@ -14403,7 +14659,7 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
if (spec->kctls.list)
add_mixer(spec, spec->kctls.list);
- if ((alc_read_coef_idx(codec, 0) & 0x00f0) == 0x0010) {
+ if (spec->codec_variant != ALC269_TYPE_NORMAL) {
add_verb(spec, alc269vb_init_verbs);
alc_ssid_check(codec, 0, 0x1b, 0x14, 0x21);
} else {
@@ -14450,23 +14706,85 @@ static void alc269_auto_init(struct hda_codec *codec)
alc_inithook(codec);
}
+#ifdef SND_HDA_NEEDS_RESUME
+static void alc269_toggle_power_output(struct hda_codec *codec, int power_up)
+{
+ int val = alc_read_coef_idx(codec, 0x04);
+ if (power_up)
+ val |= 1 << 11;
+ else
+ val &= ~(1 << 11);
+ alc_write_coef_idx(codec, 0x04, val);
+}
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static int alc269_suspend(struct hda_codec *codec, pm_message_t state)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x017)
+ alc269_toggle_power_output(codec, 0);
+ if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x018) {
+ alc269_toggle_power_output(codec, 0);
+ msleep(150);
+ }
+
+ alc_shutup(codec);
+ if (spec && spec->power_hook)
+ spec->power_hook(codec);
+ return 0;
+}
+#endif /* CONFIG_SND_HDA_POWER_SAVE */
+
+static int alc269_resume(struct hda_codec *codec)
+{
+ if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x018) {
+ alc269_toggle_power_output(codec, 0);
+ msleep(150);
+ }
+
+ codec->patch_ops.init(codec);
+
+ if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x017) {
+ alc269_toggle_power_output(codec, 1);
+ msleep(200);
+ }
+
+ if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x018)
+ alc269_toggle_power_output(codec, 1);
+
+ snd_hda_codec_resume_amp(codec);
+ snd_hda_codec_resume_cache(codec);
+ hda_call_check_power_status(codec, 0x01);
+ return 0;
+}
+#endif /* SND_HDA_NEEDS_RESUME */
+
enum {
ALC269_FIXUP_SONY_VAIO,
-};
-
-static const struct hda_verb alc269_sony_vaio_fixup_verbs[] = {
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREFGRD},
- {}
+ ALC269_FIXUP_DELL_M101Z,
};
static const struct alc_fixup alc269_fixups[] = {
[ALC269_FIXUP_SONY_VAIO] = {
- .verbs = alc269_sony_vaio_fixup_verbs
+ .verbs = (const struct hda_verb[]) {
+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREFGRD},
+ {}
+ }
+ },
+ [ALC269_FIXUP_DELL_M101Z] = {
+ .verbs = (const struct hda_verb[]) {
+ /* Enables internal speaker */
+ {0x20, AC_VERB_SET_COEF_INDEX, 13},
+ {0x20, AC_VERB_SET_PROC_COEF, 0x4040},
+ {}
+ }
},
};
static struct snd_pci_quirk alc269_fixup_tbl[] = {
- SND_PCI_QUIRK(0x104d, 0x9071, "Sony VAIO", ALC269_FIXUP_SONY_VAIO),
+ SND_PCI_QUIRK_VENDOR(0x104d, "Sony VAIO", ALC269_FIXUP_SONY_VAIO),
+ SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z),
{}
};
@@ -14667,12 +14985,46 @@ static struct alc_config_preset alc269_presets[] = {
},
};
+static int alc269_fill_coef(struct hda_codec *codec)
+{
+ int val;
+
+ if ((alc_read_coef_idx(codec, 0) & 0x00ff) < 0x015) {
+ alc_write_coef_idx(codec, 0xf, 0x960b);
+ alc_write_coef_idx(codec, 0xe, 0x8817);
+ }
+
+ if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x016) {
+ alc_write_coef_idx(codec, 0xf, 0x960b);
+ alc_write_coef_idx(codec, 0xe, 0x8814);
+ }
+
+ if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x017) {
+ val = alc_read_coef_idx(codec, 0x04);
+ /* Power up output pin */
+ alc_write_coef_idx(codec, 0x04, val | (1<<11));
+ }
+
+ if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x018) {
+ val = alc_read_coef_idx(codec, 0xd);
+ if ((val & 0x0c00) >> 10 != 0x1) {
+ /* Capless ramp up clock control */
+ alc_write_coef_idx(codec, 0xd, val | 1<<10);
+ }
+ val = alc_read_coef_idx(codec, 0x17);
+ if ((val & 0x01c0) >> 6 != 0x4) {
+ /* Class D power on reset */
+ alc_write_coef_idx(codec, 0x17, val | 1<<7);
+ }
+ }
+ return 0;
+}
+
static int patch_alc269(struct hda_codec *codec)
{
struct alc_spec *spec;
int board_config;
int err;
- int is_alc269vb = 0;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL)
@@ -14684,14 +15036,18 @@ static int patch_alc269(struct hda_codec *codec)
if ((alc_read_coef_idx(codec, 0) & 0x00f0) == 0x0010){
if (codec->bus->pci->subsystem_vendor == 0x1025 &&
- spec->cdefine.platform_type == 1)
+ spec->cdefine.platform_type == 1) {
alc_codec_rename(codec, "ALC271X");
- else
+ spec->codec_variant = ALC269_TYPE_ALC271X;
+ } else {
alc_codec_rename(codec, "ALC259");
- is_alc269vb = 1;
+ spec->codec_variant = ALC269_TYPE_ALC259;
+ }
} else
alc_fix_pll_init(codec, 0x20, 0x04, 15);
+ alc269_fill_coef(codec);
+
board_config = snd_hda_check_board_config(codec, ALC269_MODEL_LAST,
alc269_models,
alc269_cfg_tbl);
@@ -14748,7 +15104,7 @@ static int patch_alc269(struct hda_codec *codec)
spec->stream_digital_capture = &alc269_pcm_digital_capture;
if (!spec->adc_nids) { /* wasn't filled automatically? use default */
- if (!is_alc269vb) {
+ if (spec->codec_variant != ALC269_TYPE_NORMAL) {
spec->adc_nids = alc269_adc_nids;
spec->num_adc_nids = ARRAY_SIZE(alc269_adc_nids);
spec->capsrc_nids = alc269_capsrc_nids;
@@ -14770,8 +15126,16 @@ static int patch_alc269(struct hda_codec *codec)
spec->vmaster_nid = 0x02;
codec->patch_ops = alc_patch_ops;
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+ codec->patch_ops.suspend = alc269_suspend;
+#endif
+#ifdef SND_HDA_NEEDS_RESUME
+ codec->patch_ops.resume = alc269_resume;
+#endif
if (board_config == ALC269_AUTO)
spec->init_hook = alc269_auto_init;
+
+ alc_init_jacks(codec);
#ifdef CONFIG_SND_HDA_POWER_SAVE
if (!spec->loopback.amplist)
spec->loopback.amplist = alc269_loopbacks;
@@ -15584,12 +15948,13 @@ static void alc861_auto_init_hp_out(struct hda_codec *codec)
static void alc861_auto_init_analog_input(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
int i;
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- hda_nid_t nid = spec->autocfg.input_pins[i];
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
if (nid >= 0x0c && nid <= 0x11)
- alc_set_input_pin(codec, nid, i);
+ alc_set_input_pin(codec, nid, cfg->inputs[i].type);
}
}
@@ -15818,15 +16183,13 @@ enum {
PINFIX_FSC_AMILO_PI1505,
};
-static struct alc_pincfg alc861_fsc_amilo_pi1505_pinfix[] = {
- { 0x0b, 0x0221101f }, /* HP */
- { 0x0f, 0x90170310 }, /* speaker */
- { }
-};
-
static const struct alc_fixup alc861_fixups[] = {
[PINFIX_FSC_AMILO_PI1505] = {
- .pins = alc861_fsc_amilo_pi1505_pinfix
+ .pins = (const struct alc_pincfg[]) {
+ { 0x0b, 0x0221101f }, /* HP */
+ { 0x0f, 0x90170310 }, /* speaker */
+ { }
+ }
},
};
@@ -16578,12 +16941,13 @@ static void alc861vd_auto_init_hp_out(struct hda_codec *codec)
static void alc861vd_auto_init_analog_input(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
int i;
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- hda_nid_t nid = spec->autocfg.input_pins[i];
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
if (alc_is_input_pin(codec, nid)) {
- alc_set_input_pin(codec, nid, i);
+ alc_set_input_pin(codec, nid, cfg->inputs[i].type);
if (nid != ALC861VD_PIN_CD_NID &&
(get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
snd_hda_codec_write(codec, nid, 0,
@@ -16793,16 +17157,14 @@ enum {
};
/* reset GPIO1 */
-static const struct hda_verb alc660vd_fix_asus_gpio1_verbs[] = {
- {0x01, AC_VERB_SET_GPIO_MASK, 0x03},
- {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01},
- {0x01, AC_VERB_SET_GPIO_DATA, 0x01},
- { }
-};
-
static const struct alc_fixup alc861vd_fixups[] = {
[ALC660VD_FIX_ASUS_GPIO1] = {
- .verbs = alc660vd_fix_asus_gpio1_verbs,
+ .verbs = (const struct hda_verb[]) {
+ {0x01, AC_VERB_SET_GPIO_MASK, 0x03},
+ {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01},
+ {0x01, AC_VERB_SET_GPIO_DATA, 0x01},
+ { }
+ }
},
};
@@ -18816,12 +19178,13 @@ static void alc662_auto_init_hp_out(struct hda_codec *codec)
static void alc662_auto_init_analog_input(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
int i;
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- hda_nid_t nid = spec->autocfg.input_pins[i];
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
if (alc_is_input_pin(codec, nid)) {
- alc_set_input_pin(codec, nid, i);
+ alc_set_input_pin(codec, nid, cfg->inputs[i].type);
if (nid != ALC662_PIN_CD_NID &&
(get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
snd_hda_codec_write(codec, nid, 0,
@@ -18913,10 +19276,40 @@ static void alc662_auto_init(struct hda_codec *codec)
alc_inithook(codec);
}
+enum {
+ ALC662_FIXUP_ASPIRE,
+ ALC662_FIXUP_IDEAPAD,
+};
+
+static const struct alc_fixup alc662_fixups[] = {
+ [ALC662_FIXUP_ASPIRE] = {
+ .pins = (const struct alc_pincfg[]) {
+ { 0x15, 0x99130112 }, /* subwoofer */
+ { }
+ }
+ },
+ [ALC662_FIXUP_IDEAPAD] = {
+ .pins = (const struct alc_pincfg[]) {
+ { 0x17, 0x99130112 }, /* subwoofer */
+ { }
+ }
+ },
+};
+
+static struct snd_pci_quirk alc662_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x1025, 0x038b, "Acer Aspire 8943G", ALC662_FIXUP_ASPIRE),
+ SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo Ideapad Y550P", ALC662_FIXUP_IDEAPAD),
+ SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Ideapad Y550", ALC662_FIXUP_IDEAPAD),
+ {}
+};
+
+
+
static int patch_alc662(struct hda_codec *codec)
{
struct alc_spec *spec;
int err, board_config;
+ int coef;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (!spec)
@@ -18928,12 +19321,15 @@ static int patch_alc662(struct hda_codec *codec)
alc_fix_pll_init(codec, 0x20, 0x04, 15);
- if (alc_read_coef_idx(codec, 0) == 0x8020)
+ coef = alc_read_coef_idx(codec, 0);
+ if (coef == 0x8020 || coef == 0x8011)
alc_codec_rename(codec, "ALC661");
- else if ((alc_read_coef_idx(codec, 0) & (1 << 14)) &&
- codec->bus->pci->subsystem_vendor == 0x1025 &&
- spec->cdefine.platform_type == 1)
+ else if (coef & (1 << 14) &&
+ codec->bus->pci->subsystem_vendor == 0x1025 &&
+ spec->cdefine.platform_type == 1)
alc_codec_rename(codec, "ALC272X");
+ else if (coef == 0x4011)
+ alc_codec_rename(codec, "ALC656");
board_config = snd_hda_check_board_config(codec, ALC662_MODEL_LAST,
alc662_models,
@@ -18945,6 +19341,7 @@ static int patch_alc662(struct hda_codec *codec)
}
if (board_config == ALC662_AUTO) {
+ alc_pick_fixup(codec, alc662_fixup_tbl, alc662_fixups, 1);
/* automatic parse from the BIOS config */
err = alc662_parse_auto_config(codec);
if (err < 0) {
@@ -19003,8 +19400,13 @@ static int patch_alc662(struct hda_codec *codec)
spec->vmaster_nid = 0x02;
codec->patch_ops = alc_patch_ops;
- if (board_config == ALC662_AUTO)
+ if (board_config == ALC662_AUTO) {
spec->init_hook = alc662_auto_init;
+ alc_pick_fixup(codec, alc662_fixup_tbl, alc662_fixups, 0);
+ }
+
+ alc_init_jacks(codec);
+
#ifdef CONFIG_SND_HDA_POWER_SAVE
if (!spec->loopback.amplist)
spec->loopback.amplist = alc662_loopbacks;
@@ -19030,6 +19432,7 @@ static int patch_alc888(struct hda_codec *codec)
/*
* ALC680 support
*/
+#define ALC680_DIGIN_NID ALC880_DIGIN_NID
#define ALC680_DIGOUT_NID ALC880_DIGOUT_NID
#define alc680_modes alc260_modes
@@ -19044,23 +19447,114 @@ static hda_nid_t alc680_adc_nids[3] = {
0x07, 0x08, 0x09
};
+/*
+ * Analog capture ADC cgange
+ */
+static void alc680_rec_autoswitch(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int pin_found = 0;
+ int type_found = AUTO_PIN_LAST;
+ hda_nid_t nid;
+ int i;
+
+ for (i = 0; i < cfg->num_inputs; i++) {
+ nid = cfg->inputs[i].pin;
+ if (!(snd_hda_query_pin_caps(codec, nid) &
+ AC_PINCAP_PRES_DETECT))
+ continue;
+ if (snd_hda_jack_detect(codec, nid)) {
+ if (cfg->inputs[i].type < type_found) {
+ type_found = cfg->inputs[i].type;
+ pin_found = nid;
+ }
+ }
+ }
+
+ nid = 0x07;
+ if (pin_found)
+ snd_hda_get_connections(codec, pin_found, &nid, 1);
+
+ if (nid != spec->cur_adc)
+ __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
+ spec->cur_adc = nid;
+ snd_hda_codec_setup_stream(codec, nid, spec->cur_adc_stream_tag, 0,
+ spec->cur_adc_format);
+}
+
+static int alc680_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct alc_spec *spec = codec->spec;
+
+ spec->cur_adc = 0x07;
+ spec->cur_adc_stream_tag = stream_tag;
+ spec->cur_adc_format = format;
+
+ alc680_rec_autoswitch(codec);
+ return 0;
+}
+
+static int alc680_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ snd_hda_codec_cleanup_stream(codec, 0x07);
+ snd_hda_codec_cleanup_stream(codec, 0x08);
+ snd_hda_codec_cleanup_stream(codec, 0x09);
+ return 0;
+}
+
+static struct hda_pcm_stream alc680_pcm_analog_auto_capture = {
+ .substreams = 1, /* can be overridden */
+ .channels_min = 2,
+ .channels_max = 2,
+ /* NID is set in alc_build_pcms */
+ .ops = {
+ .prepare = alc680_capture_pcm_prepare,
+ .cleanup = alc680_capture_pcm_cleanup
+ },
+};
+
static struct snd_kcontrol_new alc680_base_mixer[] = {
/* output mixer control */
HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Headphone Playback Volume", 0x4, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Headphone Playback Switch", 0x16, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Int Mic Boost", 0x12, 0, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Line In Boost", 0x19, 0, HDA_INPUT),
{ }
};
-static struct snd_kcontrol_new alc680_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x09, 0x0, HDA_INPUT),
+static struct hda_bind_ctls alc680_bind_cap_vol = {
+ .ops = &snd_hda_bind_vol,
+ .values = {
+ HDA_COMPOSE_AMP_VAL(0x07, 3, 0, HDA_INPUT),
+ HDA_COMPOSE_AMP_VAL(0x08, 3, 0, HDA_INPUT),
+ HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_INPUT),
+ 0
+ },
+};
+
+static struct hda_bind_ctls alc680_bind_cap_switch = {
+ .ops = &snd_hda_bind_sw,
+ .values = {
+ HDA_COMPOSE_AMP_VAL(0x07, 3, 0, HDA_INPUT),
+ HDA_COMPOSE_AMP_VAL(0x08, 3, 0, HDA_INPUT),
+ HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_INPUT),
+ 0
+ },
+};
+
+static struct snd_kcontrol_new alc680_master_capture_mixer[] = {
+ HDA_BIND_VOL("Capture Volume", &alc680_bind_cap_vol),
+ HDA_BIND_SW("Capture Switch", &alc680_bind_cap_switch),
{ } /* end */
};
@@ -19068,25 +19562,60 @@ static struct snd_kcontrol_new alc680_capture_mixer[] = {
* generic initialization of ADC, input mixers and output mixers
*/
static struct hda_verb alc680_init_verbs[] = {
- /* Unmute DAC0-1 and set vol = 0 */
- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0},
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+ {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+
+ {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+ {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN},
+ {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN},
+
{ }
};
+/* toggle speaker-output according to the hp-jack state */
+static void alc680_base_setup(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ spec->autocfg.hp_pins[0] = 0x16;
+ spec->autocfg.speaker_pins[0] = 0x14;
+ spec->autocfg.speaker_pins[1] = 0x15;
+ spec->autocfg.num_inputs = 2;
+ spec->autocfg.inputs[0].pin = 0x18;
+ spec->autocfg.inputs[0].type = AUTO_PIN_MIC;
+ spec->autocfg.inputs[1].pin = 0x19;
+ spec->autocfg.inputs[1].type = AUTO_PIN_LINE_IN;
+}
+
+static void alc680_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ if ((res >> 26) == ALC880_HP_EVENT)
+ alc_automute_amp(codec);
+ if ((res >> 26) == ALC880_MIC_EVENT)
+ alc680_rec_autoswitch(codec);
+}
+
+static void alc680_inithook(struct hda_codec *codec)
+{
+ alc_automute_amp(codec);
+ alc680_rec_autoswitch(codec);
+}
+
/* create input playback/capture controls for the given pin */
static int alc680_new_analog_output(struct alc_spec *spec, hda_nid_t nid,
const char *ctlname, int idx)
@@ -19197,13 +19726,7 @@ static void alc680_auto_init_hp_out(struct hda_codec *codec)
#define alc680_pcm_analog_capture alc880_pcm_analog_capture
#define alc680_pcm_analog_alt_capture alc880_pcm_analog_alt_capture
#define alc680_pcm_digital_playback alc880_pcm_digital_playback
-
-static struct hda_input_mux alc680_capture_source = {
- .num_items = 1,
- .items = {
- { "Mic", 0x0 },
- },
-};
+#define alc680_pcm_digital_capture alc880_pcm_digital_capture
/*
* BIOS auto configuration
@@ -19218,6 +19741,7 @@ static int alc680_parse_auto_config(struct hda_codec *codec)
alc680_ignore);
if (err < 0)
return err;
+
if (!spec->autocfg.line_outs) {
if (spec->autocfg.dig_outs || spec->autocfg.dig_in_pin) {
spec->multiout.max_channels = 2;
@@ -19239,8 +19763,6 @@ static int alc680_parse_auto_config(struct hda_codec *codec)
add_mixer(spec, spec->kctls.list);
add_verb(spec, alc680_init_verbs);
- spec->num_mux_defs = 1;
- spec->input_mux = &alc680_capture_source;
err = alc_auto_add_mic_boost(codec);
if (err < 0)
@@ -19279,17 +19801,17 @@ static struct snd_pci_quirk alc680_cfg_tbl[] = {
static struct alc_config_preset alc680_presets[] = {
[ALC680_BASE] = {
.mixers = { alc680_base_mixer },
- .cap_mixer = alc680_capture_mixer,
+ .cap_mixer = alc680_master_capture_mixer,
.init_verbs = { alc680_init_verbs },
.num_dacs = ARRAY_SIZE(alc680_dac_nids),
.dac_nids = alc680_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc680_adc_nids),
- .adc_nids = alc680_adc_nids,
- .hp_nid = 0x04,
.dig_out_nid = ALC680_DIGOUT_NID,
.num_channel_mode = ARRAY_SIZE(alc680_modes),
.channel_mode = alc680_modes,
- .input_mux = &alc680_capture_source,
+ .unsol_event = alc680_unsol_event,
+ .setup = alc680_base_setup,
+ .init_hook = alc680_inithook,
+
},
};
@@ -19333,9 +19855,9 @@ static int patch_alc680(struct hda_codec *codec)
setup_preset(codec, &alc680_presets[board_config]);
spec->stream_analog_playback = &alc680_pcm_analog_playback;
- spec->stream_analog_capture = &alc680_pcm_analog_capture;
- spec->stream_analog_alt_capture = &alc680_pcm_analog_alt_capture;
+ spec->stream_analog_capture = &alc680_pcm_analog_auto_capture;
spec->stream_digital_playback = &alc680_pcm_digital_playback;
+ spec->stream_digital_capture = &alc680_pcm_digital_capture;
if (!spec->adc_nids) {
spec->adc_nids = alc680_adc_nids;
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index f3f861bd1bf..93fa59cc60e 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -32,6 +32,7 @@
#include <sound/core.h>
#include <sound/asoundef.h>
#include <sound/jack.h>
+#include <sound/tlv.h>
#include "hda_codec.h"
#include "hda_local.h"
#include "hda_beep.h"
@@ -263,6 +264,7 @@ struct sigmatel_spec {
struct sigmatel_mic_route ext_mic;
struct sigmatel_mic_route int_mic;
+ struct sigmatel_mic_route dock_mic;
const char **spdif_labels;
@@ -382,6 +384,11 @@ static unsigned int stac92hd83xxx_pwr_mapping[4] = {
0x03, 0x0c, 0x20, 0x40,
};
+#define STAC92HD83XXX_NUM_DMICS 2
+static hda_nid_t stac92hd83xxx_dmic_nids[STAC92HD83XXX_NUM_DMICS + 1] = {
+ 0x11, 0x20, 0
+};
+
#define STAC92HD83XXX_NUM_CAPS 2
static unsigned long stac92hd83xxx_capvols[] = {
HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_OUTPUT),
@@ -986,7 +993,7 @@ static struct hda_verb stac9205_core_init[] = {
}
static struct snd_kcontrol_new stac9200_mixer[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0xb, 0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MIN_MUTE("Master Playback Volume", 0xb, 0, HDA_OUTPUT),
HDA_CODEC_MUTE("Master Playback Switch", 0xb, 0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Capture Volume", 0x0a, 0, HDA_OUTPUT),
HDA_CODEC_MUTE("Capture Switch", 0x0a, 0, HDA_OUTPUT),
@@ -1014,7 +1021,7 @@ static struct snd_kcontrol_new stac92hd71bxx_loopback[] = {
};
static struct snd_kcontrol_new stac925x_mixer[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x0e, 0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MIN_MUTE("Master Playback Volume", 0xe, 0, HDA_OUTPUT),
HDA_CODEC_MUTE("Master Playback Switch", 0x0e, 0, HDA_OUTPUT),
{ } /* end */
};
@@ -1105,9 +1112,7 @@ static int stac92xx_build_controls(struct hda_codec *codec)
struct hda_input_mux *smux = &spec->private_smux;
/* check for mute support on SPDIF out */
if (wcaps & AC_WCAP_OUT_AMP) {
- smux->items[smux->num_items].label = "Off";
- smux->items[smux->num_items].index = 0;
- smux->num_items++;
+ snd_hda_add_imux_item(smux, "Off", 0, NULL);
spec->spdif_mute = 1;
}
stac_smux_mixer.count = spec->num_smuxes;
@@ -1140,6 +1145,8 @@ static int stac92xx_build_controls(struct hda_codec *codec)
HDA_OUTPUT, vmaster_tlv);
/* correct volume offset */
vmaster_tlv[2] += vmaster_tlv[3] * spec->volume_offset;
+ /* minimum value is actually mute */
+ vmaster_tlv[3] |= TLV_DB_SCALE_MUTE;
err = snd_hda_add_vmaster(codec, "Master Playback Volume",
vmaster_tlv, slave_vols);
if (err < 0)
@@ -1180,14 +1187,11 @@ static int stac92xx_build_controls(struct hda_codec *codec)
if (err < 0)
return err;
}
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- nid = cfg->input_pins[i];
- if (nid) {
- err = stac92xx_add_jack(codec, nid,
- SND_JACK_MICROPHONE);
- if (err < 0)
- return err;
- }
+ for (i = 0; i < cfg->num_inputs; i++) {
+ nid = cfg->inputs[i].pin;
+ err = stac92xx_add_jack(codec, nid, SND_JACK_MICROPHONE);
+ if (err < 0)
+ return err;
}
return 0;
@@ -1747,6 +1751,8 @@ static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = {
"HP dv6", STAC_HP_DV5),
SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3061,
"HP dv6", STAC_HP_DV5), /* HP dv6-1110ax */
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x363e,
+ "HP DV6", STAC_HP_DV5),
SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x7010,
"HP", STAC_HP_DV5),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0233,
@@ -2777,7 +2783,7 @@ static inline int stac92xx_add_jack_mode_control(struct hda_codec *codec,
struct sigmatel_spec *spec = codec->spec;
char name[22];
- if (!((get_defcfg_connect(def_conf)) & AC_JACK_PORT_FIXED)) {
+ if (snd_hda_get_input_pin_attr(def_conf) != INPUT_PIN_ATTR_INT) {
if (stac92xx_get_default_vref(codec, nid) == AC_PINCTL_VREF_GRD
&& nid == spec->line_switch)
control = STAC_CTL_WIDGET_IO_SWITCH;
@@ -2789,7 +2795,7 @@ static inline int stac92xx_add_jack_mode_control(struct hda_codec *codec,
}
if (control) {
- strcpy(name, auto_pin_cfg_labels[idx]);
+ strcpy(name, hda_get_input_pin_label(codec, nid, 1));
return stac92xx_add_control(codec->spec, control,
strcat(name, " Jack Mode"), nid);
}
@@ -2821,41 +2827,49 @@ static hda_nid_t check_line_out_switch(struct hda_codec *codec)
struct auto_pin_cfg *cfg = &spec->autocfg;
hda_nid_t nid;
unsigned int pincap;
+ int i;
if (cfg->line_out_type != AUTO_PIN_LINE_OUT)
return 0;
- nid = cfg->input_pins[AUTO_PIN_LINE];
- pincap = snd_hda_query_pin_caps(codec, nid);
- if (pincap & AC_PINCAP_OUT)
- return nid;
+ for (i = 0; i < cfg->num_inputs; i++) {
+ if (cfg->inputs[i].type == AUTO_PIN_LINE_IN) {
+ nid = cfg->inputs[i].pin;
+ pincap = snd_hda_query_pin_caps(codec, nid);
+ if (pincap & AC_PINCAP_OUT)
+ return nid;
+ }
+ }
return 0;
}
+static hda_nid_t get_unassigned_dac(struct hda_codec *codec, hda_nid_t nid);
+
/* check whether the mic-input can be used as line-out */
-static hda_nid_t check_mic_out_switch(struct hda_codec *codec)
+static hda_nid_t check_mic_out_switch(struct hda_codec *codec, hda_nid_t *dac)
{
struct sigmatel_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
unsigned int def_conf, pincap;
- unsigned int mic_pin;
+ int i;
+ *dac = 0;
if (cfg->line_out_type != AUTO_PIN_LINE_OUT)
return 0;
- mic_pin = AUTO_PIN_MIC;
- for (;;) {
- hda_nid_t nid = cfg->input_pins[mic_pin];
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
+ if (cfg->inputs[i].type != AUTO_PIN_MIC)
+ continue;
def_conf = snd_hda_codec_get_pincfg(codec, nid);
/* some laptops have an internal analog microphone
* which can't be used as a output */
- if (get_defcfg_connect(def_conf) != AC_JACK_PORT_FIXED) {
+ if (snd_hda_get_input_pin_attr(def_conf) != INPUT_PIN_ATTR_INT) {
pincap = snd_hda_query_pin_caps(codec, nid);
- if (pincap & AC_PINCAP_OUT)
- return nid;
+ if (pincap & AC_PINCAP_OUT) {
+ *dac = get_unassigned_dac(codec, nid);
+ if (*dac)
+ return nid;
+ }
}
- if (mic_pin == AUTO_PIN_MIC)
- mic_pin = AUTO_PIN_FRONT_MIC;
- else
- break;
}
return 0;
}
@@ -3002,17 +3016,14 @@ static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec)
}
}
/* add mic as output */
- nid = check_mic_out_switch(codec);
- if (nid) {
- dac = get_unassigned_dac(codec, nid);
- if (dac) {
- snd_printdd("STAC: Add mic-in 0x%x as output %d\n",
- nid, cfg->line_outs);
- cfg->line_out_pins[cfg->line_outs] = nid;
- cfg->line_outs++;
- spec->mic_switch = nid;
- add_spec_dacs(spec, dac);
- }
+ nid = check_mic_out_switch(codec, &dac);
+ if (nid && dac) {
+ snd_printdd("STAC: Add mic-in 0x%x as output %d\n",
+ nid, cfg->line_outs);
+ cfg->line_out_pins[cfg->line_outs] = nid;
+ cfg->line_outs++;
+ spec->mic_switch = nid;
+ add_spec_dacs(spec, dac);
}
snd_printd("stac92xx: dac_nids=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
@@ -3202,13 +3213,13 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
return err;
}
- for (idx = AUTO_PIN_MIC; idx <= AUTO_PIN_FRONT_LINE; idx++) {
- nid = cfg->input_pins[idx];
- if (nid) {
- err = stac92xx_add_jack_mode_control(codec, nid, idx);
- if (err < 0)
- return err;
- }
+ for (idx = 0; idx < cfg->num_inputs; idx++) {
+ if (cfg->inputs[idx].type > AUTO_PIN_LINE_IN)
+ break;
+ nid = cfg->inputs[idx].pin;
+ err = stac92xx_add_jack_mode_control(codec, nid, idx);
+ if (err < 0)
+ return err;
}
return 0;
@@ -3254,12 +3265,9 @@ static int stac92xx_auto_create_mono_output_ctls(struct hda_codec *codec)
if (num_cons <= 0 || num_cons > ARRAY_SIZE(stac92xx_mono_labels))
return -EINVAL;
- for (i = 0; i < num_cons; i++) {
- mono_mux->items[mono_mux->num_items].label =
- stac92xx_mono_labels[i];
- mono_mux->items[mono_mux->num_items].index = i;
- mono_mux->num_items++;
- }
+ for (i = 0; i < num_cons; i++)
+ snd_hda_add_imux_item(mono_mux, stac92xx_mono_labels[i], i,
+ NULL);
return stac92xx_add_control(spec, STAC_CTL_WIDGET_MONO_MUX,
"Mono Mux", spec->mono_nid);
@@ -3384,11 +3392,8 @@ static int stac92xx_auto_create_spdif_mux_ctls(struct hda_codec *codec)
if (!labels)
labels = stac92xx_spdif_labels;
- for (i = 0; i < num_cons; i++) {
- spdif_mux->items[spdif_mux->num_items].label = labels[i];
- spdif_mux->items[spdif_mux->num_items].index = i;
- spdif_mux->num_items++;
- }
+ for (i = 0; i < num_cons; i++)
+ snd_hda_add_imux_item(spdif_mux, labels[i], i, NULL);
return 0;
}
@@ -3415,7 +3420,7 @@ static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
/* create a volume assigned to the given pin (only if supported) */
/* return 1 if the volume control is created */
static int create_elem_capture_vol(struct hda_codec *codec, hda_nid_t nid,
- const char *label, int direction)
+ const char *label, int idx, int direction)
{
unsigned int caps, nums;
char name[32];
@@ -3432,8 +3437,8 @@ static int create_elem_capture_vol(struct hda_codec *codec, hda_nid_t nid,
if (!nums)
return 0;
snprintf(name, sizeof(name), "%s Capture Volume", label);
- err = stac92xx_add_control(codec->spec, STAC_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(nid, 3, 0, direction));
+ err = stac92xx_add_control_idx(codec->spec, STAC_CTL_WIDGET_VOL, idx, name,
+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, direction));
if (err < 0)
return err;
return 1;
@@ -3446,27 +3451,14 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec,
struct sigmatel_spec *spec = codec->spec;
struct hda_input_mux *imux = &spec->private_imux;
struct hda_input_mux *dimux = &spec->private_dimux;
- int err, i, active_mics;
+ int err, i;
unsigned int def_conf;
- dimux->items[dimux->num_items].label = stac92xx_dmic_labels[0];
- dimux->items[dimux->num_items].index = 0;
- dimux->num_items++;
-
- active_mics = 0;
- for (i = 0; i < spec->num_dmics; i++) {
- /* check the validity: sometimes it's a dead vendor-spec node */
- if (get_wcaps_type(get_wcaps(codec, spec->dmic_nids[i]))
- != AC_WID_PIN)
- continue;
- def_conf = snd_hda_codec_get_pincfg(codec, spec->dmic_nids[i]);
- if (get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)
- active_mics++;
- }
+ snd_hda_add_imux_item(dimux, stac92xx_dmic_labels[0], 0, NULL);
for (i = 0; i < spec->num_dmics; i++) {
hda_nid_t nid;
- int index;
+ int index, type_idx;
const char *label;
nid = spec->dmic_nids[i];
@@ -3480,28 +3472,23 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec,
if (index < 0)
continue;
- if (active_mics == 1)
- label = "Digital Mic";
- else
- label = stac92xx_dmic_labels[dimux->num_items];
+ label = hda_get_input_pin_label(codec, nid, 1);
+ snd_hda_add_imux_item(dimux, label, index, &type_idx);
- err = create_elem_capture_vol(codec, nid, label, HDA_INPUT);
+ err = create_elem_capture_vol(codec, nid, label, type_idx,
+ HDA_INPUT);
if (err < 0)
return err;
if (!err) {
err = create_elem_capture_vol(codec, nid, label,
- HDA_OUTPUT);
+ type_idx, HDA_OUTPUT);
if (err < 0)
return err;
}
- dimux->items[dimux->num_items].label = label;
- dimux->items[dimux->num_items].index = index;
- dimux->num_items++;
if (snd_hda_get_bool_hint(codec, "separate_dmux") != 1) {
- imux->items[imux->num_items].label = label;
- imux->items[imux->num_items].index = index;
- imux->num_items++;
+ snd_hda_add_imux_item(imux, label, index, NULL);
+ spec->num_analog_muxes++;
}
}
@@ -3509,20 +3496,27 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec,
}
static int check_mic_pin(struct hda_codec *codec, hda_nid_t nid,
- hda_nid_t *fixed, hda_nid_t *ext)
+ hda_nid_t *fixed, hda_nid_t *ext, hda_nid_t *dock)
{
unsigned int cfg;
if (!nid)
return 0;
cfg = snd_hda_codec_get_pincfg(codec, nid);
- switch (get_defcfg_connect(cfg)) {
- case AC_JACK_PORT_FIXED:
+ switch (snd_hda_get_input_pin_attr(cfg)) {
+ case INPUT_PIN_ATTR_INT:
if (*fixed)
return 1; /* already occupied */
*fixed = nid;
break;
- case AC_JACK_PORT_COMPLEX:
+ case INPUT_PIN_ATTR_UNUSED:
+ break;
+ case INPUT_PIN_ATTR_DOCK:
+ if (*dock)
+ return 1; /* already occupied */
+ *dock = nid;
+ break;
+ default:
if (*ext)
return 1; /* already occupied */
*ext = nid;
@@ -3540,10 +3534,13 @@ static int set_mic_route(struct hda_codec *codec,
int i;
mic->pin = pin;
- for (i = AUTO_PIN_MIC; i <= AUTO_PIN_FRONT_MIC; i++)
- if (pin == cfg->input_pins[i])
+ if (pin == 0)
+ return 0;
+ for (i = 0; i < cfg->num_inputs; i++) {
+ if (pin == cfg->inputs[i].pin)
break;
- if (i <= AUTO_PIN_FRONT_MIC) {
+ }
+ if (i < cfg->num_inputs && cfg->inputs[i].type == AUTO_PIN_MIC) {
/* analog pin */
i = get_connection_index(codec, spec->mux_nids[0], pin);
if (i < 0)
@@ -3574,26 +3571,29 @@ static int stac_check_auto_mic(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
- hda_nid_t fixed, ext;
+ hda_nid_t fixed, ext, dock;
int i;
- for (i = AUTO_PIN_LINE; i < AUTO_PIN_LAST; i++) {
- if (cfg->input_pins[i])
+ for (i = 0; i < cfg->num_inputs; i++) {
+ if (cfg->inputs[i].type >= AUTO_PIN_LINE_IN)
return 0; /* must be exclusively mics */
}
- fixed = ext = 0;
- for (i = AUTO_PIN_MIC; i <= AUTO_PIN_FRONT_MIC; i++)
- if (check_mic_pin(codec, cfg->input_pins[i], &fixed, &ext))
+ fixed = ext = dock = 0;
+ for (i = 0; i < cfg->num_inputs; i++)
+ if (check_mic_pin(codec, cfg->inputs[i].pin,
+ &fixed, &ext, &dock))
return 0;
for (i = 0; i < spec->num_dmics; i++)
- if (check_mic_pin(codec, spec->dmic_nids[i], &fixed, &ext))
+ if (check_mic_pin(codec, spec->dmic_nids[i],
+ &fixed, &ext, &dock))
return 0;
- if (!fixed || !ext)
- return 0;
+ if (!fixed && !ext && !dock)
+ return 0; /* no input to switch */
if (!(get_wcaps(codec, ext) & AC_WCAP_UNSOL_CAP))
return 0; /* no unsol support */
if (set_mic_route(codec, &spec->ext_mic, ext) ||
- set_mic_route(codec, &spec->int_mic, fixed))
+ set_mic_route(codec, &spec->int_mic, fixed) ||
+ set_mic_route(codec, &spec->dock_mic, dock))
return 0; /* something is wrong */
return 1;
}
@@ -3604,13 +3604,12 @@ static int stac92xx_auto_create_analog_input_ctls(struct hda_codec *codec, const
struct sigmatel_spec *spec = codec->spec;
struct hda_input_mux *imux = &spec->private_imux;
int i, j;
+ const char *label;
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- hda_nid_t nid = cfg->input_pins[i];
- int index, err;
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
+ int index, err, type_idx;
- if (!nid)
- continue;
index = -1;
for (j = 0; j < spec->num_muxes; j++) {
index = get_connection_index(codec, spec->mux_nids[j],
@@ -3621,15 +3620,14 @@ static int stac92xx_auto_create_analog_input_ctls(struct hda_codec *codec, const
if (index < 0)
continue;
+ label = hda_get_autocfg_input_label(codec, cfg, i);
+ snd_hda_add_imux_item(imux, label, index, &type_idx);
+
err = create_elem_capture_vol(codec, nid,
- auto_pin_cfg_labels[i],
+ label, type_idx,
HDA_INPUT);
if (err < 0)
return err;
-
- imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
- imux->items[imux->num_items].index = index;
- imux->num_items++;
}
spec->num_analog_muxes = imux->num_items;
@@ -4303,38 +4301,38 @@ static int stac92xx_init(struct hda_codec *codec)
AC_VERB_SET_CONNECT_SEL, 0);
if (enable_pin_detect(codec, spec->ext_mic.pin, STAC_MIC_EVENT))
stac_issue_unsol_event(codec, spec->ext_mic.pin);
- }
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- hda_nid_t nid = cfg->input_pins[i];
- if (nid) {
- unsigned int pinctl, conf;
- if (i == AUTO_PIN_MIC || i == AUTO_PIN_FRONT_MIC) {
- /* for mic pins, force to initialize */
- pinctl = stac92xx_get_default_vref(codec, nid);
+ if (enable_pin_detect(codec, spec->dock_mic.pin,
+ STAC_MIC_EVENT))
+ stac_issue_unsol_event(codec, spec->dock_mic.pin);
+ }
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
+ int type = cfg->inputs[i].type;
+ unsigned int pinctl, conf;
+ if (type == AUTO_PIN_MIC) {
+ /* for mic pins, force to initialize */
+ pinctl = stac92xx_get_default_vref(codec, nid);
+ pinctl |= AC_PINCTL_IN_EN;
+ stac92xx_auto_set_pinctl(codec, nid, pinctl);
+ } else {
+ pinctl = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ /* if PINCTL already set then skip */
+ /* Also, if both INPUT and OUTPUT are set,
+ * it must be a BIOS bug; need to override, too
+ */
+ if (!(pinctl & AC_PINCTL_IN_EN) ||
+ (pinctl & AC_PINCTL_OUT_EN)) {
+ pinctl &= ~AC_PINCTL_OUT_EN;
pinctl |= AC_PINCTL_IN_EN;
stac92xx_auto_set_pinctl(codec, nid, pinctl);
- } else {
- pinctl = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- /* if PINCTL already set then skip */
- /* Also, if both INPUT and OUTPUT are set,
- * it must be a BIOS bug; need to override, too
- */
- if (!(pinctl & AC_PINCTL_IN_EN) ||
- (pinctl & AC_PINCTL_OUT_EN)) {
- pinctl &= ~AC_PINCTL_OUT_EN;
- pinctl |= AC_PINCTL_IN_EN;
- stac92xx_auto_set_pinctl(codec, nid,
- pinctl);
- }
- }
- conf = snd_hda_codec_get_pincfg(codec, nid);
- if (get_defcfg_connect(conf) != AC_JACK_PORT_FIXED) {
- if (enable_pin_detect(codec, nid,
- STAC_INSERT_EVENT))
- stac_issue_unsol_event(codec, nid);
}
}
+ conf = snd_hda_codec_get_pincfg(codec, nid);
+ if (get_defcfg_connect(conf) != AC_JACK_PORT_FIXED) {
+ if (enable_pin_detect(codec, nid, STAC_INSERT_EVENT))
+ stac_issue_unsol_event(codec, nid);
+ }
}
for (i = 0; i < spec->num_dmics; i++)
stac92xx_auto_set_pinctl(codec, spec->dmic_nids[i],
@@ -4381,11 +4379,9 @@ static int stac92xx_init(struct hda_codec *codec)
stac_issue_unsol_event(codec, nid);
}
-#ifdef CONFIG_SND_HDA_POWER_SAVE
/* sync mute LED */
- if (spec->gpio_led && codec->patch_ops.check_power_status)
- codec->patch_ops.check_power_status(codec, 0x01);
-#endif
+ if (spec->gpio_led)
+ hda_call_check_power_status(codec, 0x01);
if (spec->dac_list)
stac92xx_power_down(codec);
return 0;
@@ -4686,6 +4682,36 @@ static void stac92xx_report_jack(struct hda_codec *codec, hda_nid_t nid)
}
}
+/* get the pin connection (fixed, none, etc) */
+static unsigned int stac_get_defcfg_connect(struct hda_codec *codec, int idx)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ unsigned int cfg;
+
+ cfg = snd_hda_codec_get_pincfg(codec, spec->pin_nids[idx]);
+ return get_defcfg_connect(cfg);
+}
+
+static int stac92xx_connected_ports(struct hda_codec *codec,
+ hda_nid_t *nids, int num_nids)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ int idx, num;
+ unsigned int def_conf;
+
+ for (num = 0; num < num_nids; num++) {
+ for (idx = 0; idx < spec->num_pins; idx++)
+ if (spec->pin_nids[idx] == nids[num])
+ break;
+ if (idx >= spec->num_pins)
+ break;
+ def_conf = stac_get_defcfg_connect(codec, idx);
+ if (def_conf == AC_JACK_PORT_NONE)
+ break;
+ }
+ return num;
+}
+
static void stac92xx_mic_detect(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
@@ -4693,6 +4719,8 @@ static void stac92xx_mic_detect(struct hda_codec *codec)
if (get_pin_presence(codec, spec->ext_mic.pin))
mic = &spec->ext_mic;
+ else if (get_pin_presence(codec, spec->dock_mic.pin))
+ mic = &spec->dock_mic;
else
mic = &spec->int_mic;
if (mic->dmux_idx >= 0)
@@ -4935,11 +4963,9 @@ static int stac92xx_resume(struct hda_codec *codec)
stac_issue_unsol_event(codec,
spec->autocfg.line_out_pins[0]);
}
-#ifdef CONFIG_SND_HDA_POWER_SAVE
/* sync mute LED */
- if (spec->gpio_led && codec->patch_ops.check_power_status)
- codec->patch_ops.check_power_status(codec, 0x01);
-#endif
+ if (spec->gpio_led)
+ hda_call_check_power_status(codec, 0x01);
return 0;
}
@@ -5300,6 +5326,82 @@ again:
return 0;
}
+static int stac92hd83xxx_set_system_btl_amp(struct hda_codec *codec)
+{
+ if (codec->vendor_id != 0x111d7605 &&
+ codec->vendor_id != 0x111d76d1)
+ return 0;
+
+ switch (codec->subsystem_id) {
+ case 0x103c1618:
+ case 0x103c1619:
+ case 0x103c161a:
+ case 0x103c161b:
+ case 0x103c161c:
+ case 0x103c161d:
+ case 0x103c161e:
+ case 0x103c161f:
+ case 0x103c1620:
+ case 0x103c1621:
+ case 0x103c1622:
+ case 0x103c1623:
+
+ case 0x103c162a:
+ case 0x103c162b:
+
+ case 0x103c1630:
+ case 0x103c1631:
+
+ case 0x103c1633:
+
+ case 0x103c1635:
+
+ case 0x103c164f:
+
+ case 0x103c1676:
+ case 0x103c1677:
+ case 0x103c1678:
+ case 0x103c1679:
+ case 0x103c167a:
+ case 0x103c167b:
+ case 0x103c167c:
+ case 0x103c167d:
+ case 0x103c167e:
+ case 0x103c167f:
+ case 0x103c1680:
+ case 0x103c1681:
+ case 0x103c1682:
+ case 0x103c1683:
+ case 0x103c1684:
+ case 0x103c1685:
+ case 0x103c1686:
+ case 0x103c1687:
+ case 0x103c1688:
+ case 0x103c1689:
+ case 0x103c168a:
+ case 0x103c168b:
+ case 0x103c168c:
+ case 0x103c168d:
+ case 0x103c168e:
+ case 0x103c168f:
+ case 0x103c1690:
+ case 0x103c1691:
+ case 0x103c1692:
+
+ case 0x103c3587:
+ case 0x103c3588:
+ case 0x103c3589:
+ case 0x103c358a:
+
+ case 0x103c3667:
+ case 0x103c3668:
+ /* set BTL amp level to 13.43dB for louder speaker output */
+ return snd_hda_codec_write_cache(codec, codec->afg, 0,
+ 0x7F4, 0x14);
+ }
+ return 0;
+}
+
static int patch_stac92hd83xxx(struct hda_codec *codec)
{
struct sigmatel_spec *spec;
@@ -5311,11 +5413,16 @@ static int patch_stac92hd83xxx(struct hda_codec *codec)
if (spec == NULL)
return -ENOMEM;
+ /* reset pin power-down; Windows may leave these bits after reboot */
+ snd_hda_codec_write_cache(codec, codec->afg, 0, 0x7EC, 0);
+ snd_hda_codec_write_cache(codec, codec->afg, 0, 0x7ED, 0);
codec->no_trigger_sense = 1;
codec->spec = spec;
spec->linear_tone_beep = 1;
codec->slave_dig_outs = stac92hd83xxx_slave_dig_outs;
spec->digbeep_nid = 0x21;
+ spec->dmic_nids = stac92hd83xxx_dmic_nids;
+ spec->dmux_nids = stac92hd83xxx_mux_nids;
spec->mux_nids = stac92hd83xxx_mux_nids;
spec->num_muxes = ARRAY_SIZE(stac92hd83xxx_mux_nids);
spec->adc_nids = stac92hd83xxx_adc_nids;
@@ -5361,9 +5468,13 @@ again:
case 0x111d76d4:
case 0x111d7605:
case 0x111d76d5:
+ case 0x111d76e7:
if (spec->board_config == STAC_92HD83XXX_PWR_REF)
break;
spec->num_pwrs = 0;
+ spec->num_dmics = stac92xx_connected_ports(codec,
+ stac92hd83xxx_dmic_nids,
+ STAC92HD83XXX_NUM_DMICS);
break;
}
@@ -5417,41 +5528,13 @@ again:
AC_VERB_SET_CONNECT_SEL, num_dacs);
}
+ stac92hd83xxx_set_system_btl_amp(codec);
+
codec->proc_widget_hook = stac92hd_proc_hook;
return 0;
}
-/* get the pin connection (fixed, none, etc) */
-static unsigned int stac_get_defcfg_connect(struct hda_codec *codec, int idx)
-{
- struct sigmatel_spec *spec = codec->spec;
- unsigned int cfg;
-
- cfg = snd_hda_codec_get_pincfg(codec, spec->pin_nids[idx]);
- return get_defcfg_connect(cfg);
-}
-
-static int stac92hd71bxx_connected_ports(struct hda_codec *codec,
- hda_nid_t *nids, int num_nids)
-{
- struct sigmatel_spec *spec = codec->spec;
- int idx, num;
- unsigned int def_conf;
-
- for (num = 0; num < num_nids; num++) {
- for (idx = 0; idx < spec->num_pins; idx++)
- if (spec->pin_nids[idx] == nids[num])
- break;
- if (idx >= spec->num_pins)
- break;
- def_conf = stac_get_defcfg_connect(codec, idx);
- if (def_conf == AC_JACK_PORT_NONE)
- break;
- }
- return num;
-}
-
static int stac92hd71bxx_connected_smuxes(struct hda_codec *codec,
hda_nid_t dig0pin)
{
@@ -5590,7 +5673,7 @@ again:
case 0x111d76b5:
spec->init = stac92hd71bxx_core_init;
codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs;
- spec->num_dmics = stac92hd71bxx_connected_ports(codec,
+ spec->num_dmics = stac92xx_connected_ports(codec,
stac92hd71bxx_dmic_nids,
STAC92HD71BXX_NUM_DMICS);
break;
@@ -5622,7 +5705,7 @@ again:
snd_hda_codec_set_pincfg(codec, 0x0f, 0x40f000f0);
snd_hda_codec_set_pincfg(codec, 0x19, 0x40f000f3);
stac92hd71bxx_dmic_nids[STAC92HD71BXX_NUM_DMICS - 1] = 0;
- spec->num_dmics = stac92hd71bxx_connected_ports(codec,
+ spec->num_dmics = stac92xx_connected_ports(codec,
stac92hd71bxx_dmic_nids,
STAC92HD71BXX_NUM_DMICS - 1);
break;
@@ -5636,7 +5719,7 @@ again:
default:
spec->init = stac92hd71bxx_core_init;
codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs;
- spec->num_dmics = stac92hd71bxx_connected_ports(codec,
+ spec->num_dmics = stac92xx_connected_ports(codec,
stac92hd71bxx_dmic_nids,
STAC92HD71BXX_NUM_DMICS);
break;
@@ -6303,6 +6386,23 @@ static struct hda_codec_preset snd_hda_preset_sigmatel[] = {
{ .id = 0x111d76b5, .name = "92HD71B6X", .patch = patch_stac92hd71bxx },
{ .id = 0x111d76b6, .name = "92HD71B5X", .patch = patch_stac92hd71bxx },
{ .id = 0x111d76b7, .name = "92HD71B5X", .patch = patch_stac92hd71bxx },
+ { .id = 0x111d76c0, .name = "92HD89C3", .patch = patch_stac92hd73xx },
+ { .id = 0x111d76c1, .name = "92HD89C2", .patch = patch_stac92hd73xx },
+ { .id = 0x111d76c2, .name = "92HD89C1", .patch = patch_stac92hd73xx },
+ { .id = 0x111d76c3, .name = "92HD89B3", .patch = patch_stac92hd73xx },
+ { .id = 0x111d76c4, .name = "92HD89B2", .patch = patch_stac92hd73xx },
+ { .id = 0x111d76c5, .name = "92HD89B1", .patch = patch_stac92hd73xx },
+ { .id = 0x111d76c6, .name = "92HD89E3", .patch = patch_stac92hd73xx },
+ { .id = 0x111d76c7, .name = "92HD89E2", .patch = patch_stac92hd73xx },
+ { .id = 0x111d76c8, .name = "92HD89E1", .patch = patch_stac92hd73xx },
+ { .id = 0x111d76c9, .name = "92HD89D3", .patch = patch_stac92hd73xx },
+ { .id = 0x111d76ca, .name = "92HD89D2", .patch = patch_stac92hd73xx },
+ { .id = 0x111d76cb, .name = "92HD89D1", .patch = patch_stac92hd73xx },
+ { .id = 0x111d76cc, .name = "92HD89F3", .patch = patch_stac92hd73xx },
+ { .id = 0x111d76cd, .name = "92HD89F2", .patch = patch_stac92hd73xx },
+ { .id = 0x111d76ce, .name = "92HD89F1", .patch = patch_stac92hd73xx },
+ { .id = 0x111d76e0, .name = "92HD91BXX", .patch = patch_stac92hd83xxx},
+ { .id = 0x111d76e7, .name = "92HD90BXX", .patch = patch_stac92hd83xxx},
{} /* terminator */
};
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index ae3acb2b42d..d1c3f8defc4 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/pci/hda/patch_via.c
@@ -444,8 +444,8 @@ static hda_nid_t vt1812_adc_nids[2] = {
/* add dynamic controls */
-static int via_add_control(struct via_spec *spec, int type, const char *name,
- unsigned long val)
+static int __via_add_control(struct via_spec *spec, int type, const char *name,
+ int idx, unsigned long val)
{
struct snd_kcontrol_new *knew;
@@ -463,6 +463,9 @@ static int via_add_control(struct via_spec *spec, int type, const char *name,
return 0;
}
+#define via_add_control(spec, type, name, val) \
+ __via_add_control(spec, type, name, 0, val)
+
static struct snd_kcontrol_new *via_clone_control(struct via_spec *spec,
struct snd_kcontrol_new *tmpl)
{
@@ -494,18 +497,18 @@ static void via_free_kctls(struct hda_codec *codec)
/* create input playback/capture controls for the given pin */
static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
- int idx, int mix_nid)
+ int type_idx, int idx, int mix_nid)
{
char name[32];
int err;
sprintf(name, "%s Playback Volume", ctlname);
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
+ err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
if (err < 0)
return err;
sprintf(name, "%s Playback Switch", ctlname);
- err = via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name,
+ err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
if (err < 0)
return err;
@@ -557,17 +560,15 @@ static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin);
static void via_auto_init_analog_input(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
+ const struct auto_pin_cfg *cfg = &spec->autocfg;
unsigned int ctl;
int i;
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- hda_nid_t nid = spec->autocfg.input_pins[i];
- if (!nid)
- continue;
-
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
if (spec->smart51_enabled && is_smart51_pins(spec, nid))
ctl = PIN_OUT;
- else if (i <= AUTO_PIN_FRONT_MIC)
+ else if (i == AUTO_PIN_MIC)
ctl = PIN_VREF50;
else
ctl = PIN_IN;
@@ -1322,15 +1323,14 @@ static void mute_aa_path(struct hda_codec *codec, int mute)
}
static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin)
{
- int res = 0;
- int index;
- for (index = AUTO_PIN_MIC; index < AUTO_PIN_FRONT_LINE; index++) {
- if (pin == spec->autocfg.input_pins[index]) {
- res = 1;
- break;
- }
+ const struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i;
+
+ for (i = 0; i < cfg->num_inputs; i++) {
+ if (pin == cfg->inputs[i].pin)
+ return cfg->inputs[i].type <= AUTO_PIN_LINE_IN;
}
- return res;
+ return 0;
}
static int via_smart51_info(struct snd_kcontrol *kcontrol,
@@ -1348,25 +1348,21 @@ static int via_smart51_get(struct snd_kcontrol *kcontrol,
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct via_spec *spec = codec->spec;
- int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE };
+ const struct auto_pin_cfg *cfg = &spec->autocfg;
int on = 1;
int i;
- for (i = 0; i < ARRAY_SIZE(index); i++) {
- hda_nid_t nid = spec->autocfg.input_pins[index[i]];
- if (nid) {
- int ctl =
- snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL,
- 0);
- if (i == AUTO_PIN_FRONT_MIC
- && spec->hp_independent_mode
- && spec->codec_type != VT1718S)
- continue; /* ignore FMic for independent HP */
- if (ctl & AC_PINCTL_IN_EN
- && !(ctl & AC_PINCTL_OUT_EN))
- on = 0;
- }
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
+ int ctl = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
+ continue;
+ if (cfg->inputs[i].type == AUTO_PIN_MIC &&
+ spec->hp_independent_mode && spec->codec_type != VT1718S)
+ continue; /* ignore FMic for independent HP */
+ if ((ctl & AC_PINCTL_IN_EN) && !(ctl & AC_PINCTL_OUT_EN))
+ on = 0;
}
*ucontrol->value.integer.value = on;
return 0;
@@ -1377,36 +1373,38 @@ static int via_smart51_put(struct snd_kcontrol *kcontrol,
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct via_spec *spec = codec->spec;
+ const struct auto_pin_cfg *cfg = &spec->autocfg;
int out_in = *ucontrol->value.integer.value
? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
- int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE };
int i;
- for (i = 0; i < ARRAY_SIZE(index); i++) {
- hda_nid_t nid = spec->autocfg.input_pins[index[i]];
- if (i == AUTO_PIN_FRONT_MIC
- && spec->hp_independent_mode
- && spec->codec_type != VT1718S)
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
+ unsigned int parm;
+
+ if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
+ continue;
+ if (cfg->inputs[i].type == AUTO_PIN_MIC &&
+ spec->hp_independent_mode && spec->codec_type != VT1718S)
continue; /* don't retask FMic for independent HP */
- if (nid) {
- unsigned int parm = snd_hda_codec_read(
- codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
- parm |= out_in;
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- parm);
- if (out_in == AC_PINCTL_OUT_EN) {
- mute_aa_path(codec, 1);
- notify_aa_path_ctls(codec);
- }
- if (spec->codec_type == VT1718S)
- snd_hda_codec_amp_stereo(
+
+ parm = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
+ parm |= out_in;
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL,
+ parm);
+ if (out_in == AC_PINCTL_OUT_EN) {
+ mute_aa_path(codec, 1);
+ notify_aa_path_ctls(codec);
+ }
+ if (spec->codec_type == VT1718S) {
+ snd_hda_codec_amp_stereo(
codec, nid, HDA_OUTPUT, 0, HDA_AMP_MUTE,
HDA_AMP_UNMUTE);
}
- if (i == AUTO_PIN_FRONT_MIC) {
+ if (cfg->inputs[i].type == AUTO_PIN_MIC) {
if (spec->codec_type == VT1708S
|| spec->codec_type == VT1716S) {
/* input = index 1 (AOW3) */
@@ -1442,7 +1440,7 @@ static struct snd_kcontrol_new via_smart51_mixer[2] = {
static int via_smart51_build(struct via_spec *spec)
{
struct snd_kcontrol_new *knew;
- int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE };
+ const struct auto_pin_cfg *cfg = &spec->autocfg;
hda_nid_t nid;
int i;
@@ -1450,13 +1448,14 @@ static int via_smart51_build(struct via_spec *spec)
if (knew == NULL)
return -ENOMEM;
- for (i = 0; i < ARRAY_SIZE(index); i++) {
- nid = spec->autocfg.input_pins[index[i]];
- if (nid) {
+ for (i = 0; i < cfg->num_inputs; i++) {
+ nid = cfg->inputs[i].pin;
+ if (cfg->inputs[i].type <= AUTO_PIN_LINE_IN) {
knew = via_clone_control(spec, &via_smart51_mixer[1]);
if (knew == NULL)
return -ENOMEM;
knew->subdevice = nid;
+ break;
}
}
@@ -2375,13 +2374,8 @@ static void create_hp_imux(struct via_spec *spec)
static const char *texts[] = { "OFF", "ON", NULL};
/* for hp mode select */
- i = 0;
- while (texts[i] != NULL) {
- imux->items[imux->num_items].label = texts[i];
- imux->items[imux->num_items].index = i;
- imux->num_items++;
- i++;
- }
+ for (i = 0; texts[i]; i++)
+ snd_hda_add_imux_item(imux, texts[i], i, NULL);
spec->hp_mux = &spec->private_imux[1];
}
@@ -2413,51 +2407,53 @@ static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
}
/* create playback/capture controls for input pins */
-static int vt1708_auto_create_analog_input_ctls(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
+static int vt_auto_create_analog_input_ctls(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg,
+ hda_nid_t cap_nid,
+ hda_nid_t pin_idxs[], int num_idxs)
{
- static char *labels[] = {
- "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
- };
+ struct via_spec *spec = codec->spec;
struct hda_input_mux *imux = &spec->private_imux[0];
- int i, err, idx = 0;
+ int i, err, idx, type, type_idx = 0;
/* for internal loopback recording select */
- imux->items[imux->num_items].label = "Stereo Mixer";
- imux->items[imux->num_items].index = idx;
- imux->num_items++;
-
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- if (!cfg->input_pins[i])
- continue;
-
- switch (cfg->input_pins[i]) {
- case 0x1d: /* Mic */
- idx = 2;
- break;
-
- case 0x1e: /* Line In */
- idx = 3;
- break;
-
- case 0x21: /* Front Mic */
- idx = 4;
- break;
-
- case 0x24: /* CD */
- idx = 1;
+ for (idx = 0; idx < num_idxs; idx++) {
+ if (pin_idxs[idx] == 0xff) {
+ snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL);
break;
}
- err = via_new_analog_input(spec, labels[i], idx, 0x17);
+ }
+
+ for (i = 0; i < cfg->num_inputs; i++) {
+ const char *label;
+ type = cfg->inputs[i].type;
+ for (idx = 0; idx < num_idxs; idx++)
+ if (pin_idxs[idx] == cfg->inputs[i].pin)
+ break;
+ if (idx >= num_idxs)
+ continue;
+ if (i > 0 && type == cfg->inputs[i - 1].type)
+ type_idx++;
+ else
+ type_idx = 0;
+ label = hda_get_autocfg_input_label(codec, cfg, i);
+ err = via_new_analog_input(spec, label, type_idx, idx, cap_nid);
if (err < 0)
return err;
- imux->items[imux->num_items].label = labels[i];
- imux->items[imux->num_items].index = idx;
- imux->num_items++;
+ snd_hda_add_imux_item(imux, label, idx, NULL);
}
return 0;
}
+/* create playback/capture controls for input pins */
+static int vt1708_auto_create_analog_input_ctls(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg)
+{
+ static hda_nid_t pin_idxs[] = { 0xff, 0x24, 0x1d, 0x1e, 0x21 };
+ return vt_auto_create_analog_input_ctls(codec, cfg, 0x17, pin_idxs,
+ ARRAY_SIZE(pin_idxs));
+}
+
#ifdef CONFIG_SND_HDA_POWER_SAVE
static struct hda_amp_list vt1708_loopbacks[] = {
{ 0x17, HDA_INPUT, 1 },
@@ -2554,7 +2550,7 @@ static int vt1708_parse_auto_config(struct hda_codec *codec)
err = vt1708_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
if (err < 0)
return err;
- err = vt1708_auto_create_analog_input_ctls(spec, &spec->autocfg);
+ err = vt1708_auto_create_analog_input_ctls(codec, &spec->autocfg);
if (err < 0)
return err;
/* add jack detect on/off control */
@@ -3021,49 +3017,12 @@ static int vt1709_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
}
/* create playback/capture controls for input pins */
-static int vt1709_auto_create_analog_input_ctls(struct via_spec *spec,
+static int vt1709_auto_create_analog_input_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg)
{
- static char *labels[] = {
- "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
- };
- struct hda_input_mux *imux = &spec->private_imux[0];
- int i, err, idx = 0;
-
- /* for internal loopback recording select */
- imux->items[imux->num_items].label = "Stereo Mixer";
- imux->items[imux->num_items].index = idx;
- imux->num_items++;
-
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- if (!cfg->input_pins[i])
- continue;
-
- switch (cfg->input_pins[i]) {
- case 0x1d: /* Mic */
- idx = 2;
- break;
-
- case 0x1e: /* Line In */
- idx = 3;
- break;
-
- case 0x21: /* Front Mic */
- idx = 4;
- break;
-
- case 0x23: /* CD */
- idx = 1;
- break;
- }
- err = via_new_analog_input(spec, labels[i], idx, 0x18);
- if (err < 0)
- return err;
- imux->items[imux->num_items].label = labels[i];
- imux->items[imux->num_items].index = idx;
- imux->num_items++;
- }
- return 0;
+ static hda_nid_t pin_idxs[] = { 0xff, 0x23, 0x1d, 0x1e, 0x21 };
+ return vt_auto_create_analog_input_ctls(codec, cfg, 0x18, pin_idxs,
+ ARRAY_SIZE(pin_idxs));
}
static int vt1709_parse_auto_config(struct hda_codec *codec)
@@ -3086,7 +3045,7 @@ static int vt1709_parse_auto_config(struct hda_codec *codec)
err = vt1709_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
if (err < 0)
return err;
- err = vt1709_auto_create_analog_input_ctls(spec, &spec->autocfg);
+ err = vt1709_auto_create_analog_input_ctls(codec, &spec->autocfg);
if (err < 0)
return err;
@@ -3588,49 +3547,12 @@ static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
}
/* create playback/capture controls for input pins */
-static int vt1708B_auto_create_analog_input_ctls(struct via_spec *spec,
+static int vt1708B_auto_create_analog_input_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg)
{
- static char *labels[] = {
- "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
- };
- struct hda_input_mux *imux = &spec->private_imux[0];
- int i, err, idx = 0;
-
- /* for internal loopback recording select */
- imux->items[imux->num_items].label = "Stereo Mixer";
- imux->items[imux->num_items].index = idx;
- imux->num_items++;
-
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- if (!cfg->input_pins[i])
- continue;
-
- switch (cfg->input_pins[i]) {
- case 0x1a: /* Mic */
- idx = 2;
- break;
-
- case 0x1b: /* Line In */
- idx = 3;
- break;
-
- case 0x1e: /* Front Mic */
- idx = 4;
- break;
-
- case 0x1f: /* CD */
- idx = 1;
- break;
- }
- err = via_new_analog_input(spec, labels[i], idx, 0x16);
- if (err < 0)
- return err;
- imux->items[imux->num_items].label = labels[i];
- imux->items[imux->num_items].index = idx;
- imux->num_items++;
- }
- return 0;
+ static hda_nid_t pin_idxs[] = { 0xff, 0x1f, 0x1a, 0x1b, 0x1e };
+ return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs,
+ ARRAY_SIZE(pin_idxs));
}
static int vt1708B_parse_auto_config(struct hda_codec *codec)
@@ -3653,7 +3575,7 @@ static int vt1708B_parse_auto_config(struct hda_codec *codec)
err = vt1708B_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
if (err < 0)
return err;
- err = vt1708B_auto_create_analog_input_ctls(spec, &spec->autocfg);
+ err = vt1708B_auto_create_analog_input_ctls(codec, &spec->autocfg);
if (err < 0)
return err;
@@ -4061,49 +3983,12 @@ static int vt1708S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
}
/* create playback/capture controls for input pins */
-static int vt1708S_auto_create_analog_input_ctls(struct via_spec *spec,
+static int vt1708S_auto_create_analog_input_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg)
{
- static char *labels[] = {
- "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
- };
- struct hda_input_mux *imux = &spec->private_imux[0];
- int i, err, idx = 0;
-
- /* for internal loopback recording select */
- imux->items[imux->num_items].label = "Stereo Mixer";
- imux->items[imux->num_items].index = 5;
- imux->num_items++;
-
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- if (!cfg->input_pins[i])
- continue;
-
- switch (cfg->input_pins[i]) {
- case 0x1a: /* Mic */
- idx = 2;
- break;
-
- case 0x1b: /* Line In */
- idx = 3;
- break;
-
- case 0x1e: /* Front Mic */
- idx = 4;
- break;
-
- case 0x1f: /* CD */
- idx = 1;
- break;
- }
- err = via_new_analog_input(spec, labels[i], idx, 0x16);
- if (err < 0)
- return err;
- imux->items[imux->num_items].label = labels[i];
- imux->items[imux->num_items].index = idx-1;
- imux->num_items++;
- }
- return 0;
+ static hda_nid_t pin_idxs[] = { 0x1f, 0x1a, 0x1b, 0x1e, 0, 0xff };
+ return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs,
+ ARRAY_SIZE(pin_idxs));
}
/* fill out digital output widgets; one for master and one for slave outputs */
@@ -4151,7 +4036,7 @@ static int vt1708S_parse_auto_config(struct hda_codec *codec)
err = vt1708S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
if (err < 0)
return err;
- err = vt1708S_auto_create_analog_input_ctls(spec, &spec->autocfg);
+ err = vt1708S_auto_create_analog_input_ctls(codec, &spec->autocfg);
if (err < 0)
return err;
@@ -4441,58 +4326,20 @@ static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
imux = &spec->private_imux[1];
/* for hp mode select */
- i = 0;
- while (texts[i] != NULL) {
- imux->items[imux->num_items].label = texts[i];
- imux->items[imux->num_items].index = i;
- imux->num_items++;
- i++;
- }
+ for (i = 0; texts[i]; i++)
+ snd_hda_add_imux_item(imux, texts[i], i, NULL);
spec->hp_mux = &spec->private_imux[1];
return 0;
}
/* create playback/capture controls for input pins */
-static int vt1702_auto_create_analog_input_ctls(struct via_spec *spec,
+static int vt1702_auto_create_analog_input_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg)
{
- static char *labels[] = {
- "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
- };
- struct hda_input_mux *imux = &spec->private_imux[0];
- int i, err, idx = 0;
-
- /* for internal loopback recording select */
- imux->items[imux->num_items].label = "Stereo Mixer";
- imux->items[imux->num_items].index = 3;
- imux->num_items++;
-
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- if (!cfg->input_pins[i])
- continue;
-
- switch (cfg->input_pins[i]) {
- case 0x14: /* Mic */
- idx = 1;
- break;
-
- case 0x15: /* Line In */
- idx = 2;
- break;
-
- case 0x18: /* Front Mic */
- idx = 3;
- break;
- }
- err = via_new_analog_input(spec, labels[i], idx, 0x1A);
- if (err < 0)
- return err;
- imux->items[imux->num_items].label = labels[i];
- imux->items[imux->num_items].index = idx-1;
- imux->num_items++;
- }
- return 0;
+ static hda_nid_t pin_idxs[] = { 0x14, 0x15, 0x18, 0xff };
+ return vt_auto_create_analog_input_ctls(codec, cfg, 0x1a, pin_idxs,
+ ARRAY_SIZE(pin_idxs));
}
static int vt1702_parse_auto_config(struct hda_codec *codec)
@@ -4521,7 +4368,7 @@ static int vt1702_parse_auto_config(struct hda_codec *codec)
(0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
(0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
(1 << AC_AMPCAP_MUTE_SHIFT));
- err = vt1702_auto_create_analog_input_ctls(spec, &spec->autocfg);
+ err = vt1702_auto_create_analog_input_ctls(codec, &spec->autocfg);
if (err < 0)
return err;
@@ -4872,49 +4719,12 @@ static int vt1718S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
}
/* create playback/capture controls for input pins */
-static int vt1718S_auto_create_analog_input_ctls(struct via_spec *spec,
+static int vt1718S_auto_create_analog_input_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg)
{
- static char *labels[] = {
- "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
- };
- struct hda_input_mux *imux = &spec->private_imux[0];
- int i, err, idx = 0;
-
- /* for internal loopback recording select */
- imux->items[imux->num_items].label = "Stereo Mixer";
- imux->items[imux->num_items].index = 5;
- imux->num_items++;
-
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- if (!cfg->input_pins[i])
- continue;
-
- switch (cfg->input_pins[i]) {
- case 0x2b: /* Mic */
- idx = 1;
- break;
-
- case 0x2a: /* Line In */
- idx = 2;
- break;
-
- case 0x29: /* Front Mic */
- idx = 3;
- break;
-
- case 0x2c: /* CD */
- idx = 0;
- break;
- }
- err = via_new_analog_input(spec, labels[i], idx, 0x21);
- if (err < 0)
- return err;
- imux->items[imux->num_items].label = labels[i];
- imux->items[imux->num_items].index = idx;
- imux->num_items++;
- }
- return 0;
+ static hda_nid_t pin_idxs[] = { 0x2c, 0x2b, 0x2a, 0x29, 0, 0xff };
+ return vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs,
+ ARRAY_SIZE(pin_idxs));
}
static int vt1718S_parse_auto_config(struct hda_codec *codec)
@@ -4938,7 +4748,7 @@ static int vt1718S_parse_auto_config(struct hda_codec *codec)
err = vt1718S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
if (err < 0)
return err;
- err = vt1718S_auto_create_analog_input_ctls(spec, &spec->autocfg);
+ err = vt1718S_auto_create_analog_input_ctls(codec, &spec->autocfg);
if (err < 0)
return err;
@@ -5371,49 +5181,12 @@ static int vt1716S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
}
/* create playback/capture controls for input pins */
-static int vt1716S_auto_create_analog_input_ctls(struct via_spec *spec,
+static int vt1716S_auto_create_analog_input_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg)
{
- static char *labels[] = {
- "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
- };
- struct hda_input_mux *imux = &spec->private_imux[0];
- int i, err, idx = 0;
-
- /* for internal loopback recording select */
- imux->items[imux->num_items].label = "Stereo Mixer";
- imux->items[imux->num_items].index = 5;
- imux->num_items++;
-
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- if (!cfg->input_pins[i])
- continue;
-
- switch (cfg->input_pins[i]) {
- case 0x1a: /* Mic */
- idx = 2;
- break;
-
- case 0x1b: /* Line In */
- idx = 3;
- break;
-
- case 0x1e: /* Front Mic */
- idx = 4;
- break;
-
- case 0x1f: /* CD */
- idx = 1;
- break;
- }
- err = via_new_analog_input(spec, labels[i], idx, 0x16);
- if (err < 0)
- return err;
- imux->items[imux->num_items].label = labels[i];
- imux->items[imux->num_items].index = idx-1;
- imux->num_items++;
- }
- return 0;
+ static hda_nid_t pin_idxs[] = { 0x1f, 0x1a, 0x1b, 0x1e, 0, 0xff };
+ return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs,
+ ARRAY_SIZE(pin_idxs));
}
static int vt1716S_parse_auto_config(struct hda_codec *codec)
@@ -5436,7 +5209,7 @@ static int vt1716S_parse_auto_config(struct hda_codec *codec)
err = vt1716S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
if (err < 0)
return err;
- err = vt1716S_auto_create_analog_input_ctls(spec, &spec->autocfg);
+ err = vt1716S_auto_create_analog_input_ctls(codec, &spec->autocfg);
if (err < 0)
return err;
@@ -5717,54 +5490,25 @@ static int vt2002P_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
}
/* create playback/capture controls for input pins */
-static int vt2002P_auto_create_analog_input_ctls(struct via_spec *spec,
+static int vt2002P_auto_create_analog_input_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg)
{
- static char *labels[] = {
- "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
- };
+ struct via_spec *spec = codec->spec;
struct hda_input_mux *imux = &spec->private_imux[0];
- int i, err, idx = 0;
-
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- if (!cfg->input_pins[i])
- continue;
-
- switch (cfg->input_pins[i]) {
- case 0x2b: /* Mic */
- idx = 0;
- break;
-
- case 0x2a: /* Line In */
- idx = 1;
- break;
-
- case 0x29: /* Front Mic */
- idx = 2;
- break;
- }
- err = via_new_analog_input(spec, labels[i], idx, 0x21);
- if (err < 0)
- return err;
- imux->items[imux->num_items].label = labels[i];
- imux->items[imux->num_items].index = idx;
- imux->num_items++;
- }
+ static hda_nid_t pin_idxs[] = { 0x2b, 0x2a, 0x29, 0xff };
+ int err;
+ err = vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs,
+ ARRAY_SIZE(pin_idxs));
+ if (err < 0)
+ return err;
/* build volume/mute control of loopback */
- err = via_new_analog_input(spec, "Stereo Mixer", 3, 0x21);
+ err = via_new_analog_input(spec, "Stereo Mixer", 0, 3, 0x21);
if (err < 0)
return err;
- /* for internal loopback recording select */
- imux->items[imux->num_items].label = "Stereo Mixer";
- imux->items[imux->num_items].index = 3;
- imux->num_items++;
-
/* for digital mic select */
- imux->items[imux->num_items].label = "Digital Mic";
- imux->items[imux->num_items].index = 4;
- imux->num_items++;
+ snd_hda_add_imux_item(imux, "Digital Mic", 4, NULL);
return 0;
}
@@ -5792,7 +5536,7 @@ static int vt2002P_parse_auto_config(struct hda_codec *codec)
err = vt2002P_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
if (err < 0)
return err;
- err = vt2002P_auto_create_analog_input_ctls(spec, &spec->autocfg);
+ err = vt2002P_auto_create_analog_input_ctls(codec, &spec->autocfg);
if (err < 0)
return err;
@@ -6067,53 +5811,26 @@ static int vt1812_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
}
/* create playback/capture controls for input pins */
-static int vt1812_auto_create_analog_input_ctls(struct via_spec *spec,
+static int vt1812_auto_create_analog_input_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg)
{
- static char *labels[] = {
- "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
- };
+ struct via_spec *spec = codec->spec;
struct hda_input_mux *imux = &spec->private_imux[0];
- int i, err, idx = 0;
-
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- if (!cfg->input_pins[i])
- continue;
-
- switch (cfg->input_pins[i]) {
- case 0x2b: /* Mic */
- idx = 0;
- break;
+ static hda_nid_t pin_idxs[] = { 0x2b, 0x2a, 0x29, 0, 0, 0xff };
+ int err;
- case 0x2a: /* Line In */
- idx = 1;
- break;
+ err = vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs,
+ ARRAY_SIZE(pin_idxs));
+ if (err < 0)
+ return err;
- case 0x29: /* Front Mic */
- idx = 2;
- break;
- }
- err = via_new_analog_input(spec, labels[i], idx, 0x21);
- if (err < 0)
- return err;
- imux->items[imux->num_items].label = labels[i];
- imux->items[imux->num_items].index = idx;
- imux->num_items++;
- }
/* build volume/mute control of loopback */
- err = via_new_analog_input(spec, "Stereo Mixer", 5, 0x21);
+ err = via_new_analog_input(spec, "Stereo Mixer", 0, 5, 0x21);
if (err < 0)
return err;
- /* for internal loopback recording select */
- imux->items[imux->num_items].label = "Stereo Mixer";
- imux->items[imux->num_items].index = 5;
- imux->num_items++;
-
/* for digital mic select */
- imux->items[imux->num_items].label = "Digital Mic";
- imux->items[imux->num_items].index = 6;
- imux->num_items++;
+ snd_hda_add_imux_item(imux, "Digital Mic", 6, NULL);
return 0;
}
@@ -6141,7 +5858,7 @@ static int vt1812_parse_auto_config(struct hda_codec *codec)
err = vt1812_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
if (err < 0)
return err;
- err = vt1812_auto_create_analog_input_ctls(spec, &spec->autocfg);
+ err = vt1812_auto_create_analog_input_ctls(codec, &spec->autocfg);
if (err < 0)
return err;
diff --git a/sound/pci/ice1712/delta.c b/sound/pci/ice1712/delta.c
index d216362626d..712c1710f9a 100644
--- a/sound/pci/ice1712/delta.c
+++ b/sound/pci/ice1712/delta.c
@@ -563,6 +563,7 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice)
case ICE1712_SUBDEVICE_DELTA1010E:
case ICE1712_SUBDEVICE_DELTA1010LT:
case ICE1712_SUBDEVICE_MEDIASTATION:
+ case ICE1712_SUBDEVICE_EDIROLDA2496:
ice->num_total_dacs = 8;
ice->num_total_adcs = 8;
break;
@@ -635,6 +636,7 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice)
err = snd_ice1712_akm4xxx_init(ak, &akm_delta410, &akm_delta410_priv, ice);
break;
case ICE1712_SUBDEVICE_DELTA1010LT:
+ case ICE1712_SUBDEVICE_EDIROLDA2496:
err = snd_ice1712_akm4xxx_init(ak, &akm_delta1010lt, &akm_delta1010lt_priv, ice);
break;
case ICE1712_SUBDEVICE_DELTA66:
@@ -734,6 +736,7 @@ static int __devinit snd_ice1712_delta_add_controls(struct snd_ice1712 *ice)
case ICE1712_SUBDEVICE_DELTA66:
case ICE1712_SUBDEVICE_VX442:
case ICE1712_SUBDEVICE_DELTA66E:
+ case ICE1712_SUBDEVICE_EDIROLDA2496:
err = snd_ice1712_akm4xxx_build_controls(ice);
if (err < 0)
return err;
@@ -813,5 +816,12 @@ struct snd_ice1712_card_info snd_ice1712_delta_cards[] __devinitdata = {
.chip_init = snd_ice1712_delta_init,
.build_controls = snd_ice1712_delta_add_controls,
},
+ {
+ .subvendor = ICE1712_SUBDEVICE_EDIROLDA2496,
+ .name = "Edirol DA2496",
+ .model = "da2496",
+ .chip_init = snd_ice1712_delta_init,
+ .build_controls = snd_ice1712_delta_add_controls,
+ },
{ } /* terminator */
};
diff --git a/sound/pci/ice1712/delta.h b/sound/pci/ice1712/delta.h
index f7f14df81f2..1a0ac6cd650 100644
--- a/sound/pci/ice1712/delta.h
+++ b/sound/pci/ice1712/delta.h
@@ -34,7 +34,8 @@
"{MidiMan M Audio,Delta 410},"\
"{MidiMan M Audio,Audiophile 24/96},"\
"{Digigram,VX442},"\
- "{Lionstracs,Mediastation},"
+ "{Lionstracs,Mediastation},"\
+ "{Edirol,DA2496},"
#define ICE1712_SUBDEVICE_DELTA1010 0x121430d6
#define ICE1712_SUBDEVICE_DELTA1010E 0xff1430d6
@@ -47,6 +48,7 @@
#define ICE1712_SUBDEVICE_DELTA1010LT 0x12143bd6
#define ICE1712_SUBDEVICE_VX442 0x12143cd6
#define ICE1712_SUBDEVICE_MEDIASTATION 0x694c0100
+#define ICE1712_SUBDEVICE_EDIROLDA2496 0xce164010
/* entry point */
extern struct snd_ice1712_card_info snd_ice1712_delta_cards[];
diff --git a/sound/pci/ice1712/pontis.c b/sound/pci/ice1712/pontis.c
index 6bc3f91b728..cdb873f5da5 100644
--- a/sound/pci/ice1712/pontis.c
+++ b/sound/pci/ice1712/pontis.c
@@ -638,7 +638,7 @@ static struct snd_kcontrol_new pontis_controls[] __devinitdata = {
*/
static void wm_proc_regs_write(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
{
- struct snd_ice1712 *ice = (struct snd_ice1712 *)entry->private_data;
+ struct snd_ice1712 *ice = entry->private_data;
char line[64];
unsigned int reg, val;
mutex_lock(&ice->gpio_mutex);
@@ -653,7 +653,7 @@ static void wm_proc_regs_write(struct snd_info_entry *entry, struct snd_info_buf
static void wm_proc_regs_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
{
- struct snd_ice1712 *ice = (struct snd_ice1712 *)entry->private_data;
+ struct snd_ice1712 *ice = entry->private_data;
int reg, val;
mutex_lock(&ice->gpio_mutex);
@@ -676,7 +676,7 @@ static void wm_proc_init(struct snd_ice1712 *ice)
static void cs_proc_regs_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
{
- struct snd_ice1712 *ice = (struct snd_ice1712 *)entry->private_data;
+ struct snd_ice1712 *ice = entry->private_data;
int reg, val;
mutex_lock(&ice->gpio_mutex);
diff --git a/sound/pci/ice1712/prodigy192.c b/sound/pci/ice1712/prodigy192.c
index 2a8e5cd8f2d..e36ddb94c38 100644
--- a/sound/pci/ice1712/prodigy192.c
+++ b/sound/pci/ice1712/prodigy192.c
@@ -654,7 +654,7 @@ static int prodigy192_ak4114_init(struct snd_ice1712 *ice)
static void stac9460_proc_regs_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
- struct snd_ice1712 *ice = (struct snd_ice1712 *)entry->private_data;
+ struct snd_ice1712 *ice = entry->private_data;
int reg, val;
/* registers 0x0 - 0x14 */
for (reg = 0; reg <= 0x15; reg++) {
diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c
index 6433e65c950..400f9ebd243 100644
--- a/sound/pci/intel8x0.c
+++ b/sound/pci/intel8x0.c
@@ -716,7 +716,7 @@ static void snd_intel8x0_setup_periods(struct intel8x0 *chip, struct ichdev *ich
* Intel 82443MX running a 100MHz processor system bus has a hardware bug,
* which aborts PCI busmaster for audio transfer. A workaround is to set
* the pages as non-cached. For details, see the errata in
- * http://www.intel.com/design/chipsets/specupdt/245051.htm
+ * http://download.intel.com/design/chipsets/specupdt/24505108.pdf
*/
static void fill_nocache(void *buf, int size, int nocache)
{
@@ -1776,6 +1776,12 @@ static struct ac97_quirk ac97_quirks[] __devinitdata = {
},
{
.subvendor = 0x1014,
+ .subdevice = 0x0534,
+ .name = "ThinkPad X31",
+ .type = AC97_TUNE_INV_EAPD
+ },
+ {
+ .subvendor = 0x1014,
.subdevice = 0x1f00,
.name = "MS-9128",
.type = AC97_TUNE_ALC_JACK
diff --git a/sound/pci/oxygen/oxygen.c b/sound/pci/oxygen/oxygen.c
index 289cb4dacfc..98a8eb3c92f 100644
--- a/sound/pci/oxygen/oxygen.c
+++ b/sound/pci/oxygen/oxygen.c
@@ -79,6 +79,7 @@ static DEFINE_PCI_DEVICE_TABLE(oxygen_ids) = {
{ OXYGEN_PCI_SUBID(0x13f6, 0x0001), .driver_data = MODEL_CMEDIA_REF },
{ OXYGEN_PCI_SUBID(0x13f6, 0x0010), .driver_data = MODEL_CMEDIA_REF },
{ OXYGEN_PCI_SUBID(0x13f6, 0x8788), .driver_data = MODEL_CMEDIA_REF },
+ { OXYGEN_PCI_SUBID(0x13f6, 0xffff), .driver_data = MODEL_CMEDIA_REF },
{ OXYGEN_PCI_SUBID(0x147a, 0xa017), .driver_data = MODEL_CMEDIA_REF },
{ OXYGEN_PCI_SUBID(0x1a58, 0x0910), .driver_data = MODEL_CMEDIA_REF },
{ OXYGEN_PCI_SUBID(0x415a, 0x5431), .driver_data = MODEL_MERIDIAN },
@@ -505,7 +506,8 @@ static const struct oxygen_model model_generic = {
PLAYBACK_2_TO_AC97_1 |
CAPTURE_0_FROM_I2S_1 |
CAPTURE_1_FROM_SPDIF |
- CAPTURE_2_FROM_AC97_1,
+ CAPTURE_2_FROM_AC97_1 |
+ AC97_CD_INPUT,
.dac_channels = 8,
.dac_volume_min = 0,
.dac_volume_max = 255,
@@ -543,6 +545,10 @@ static int __devinit get_oxygen_model(struct oxygen *chip,
chip->model.suspend = claro_suspend;
chip->model.resume = claro_resume;
chip->model.set_adc_params = set_ak5385_params;
+ chip->model.device_config = PLAYBACK_0_TO_I2S |
+ PLAYBACK_1_TO_SPDIF |
+ CAPTURE_0_FROM_I2S_2 |
+ CAPTURE_1_FROM_SPDIF;
break;
}
if (id->driver_data == MODEL_MERIDIAN ||
diff --git a/sound/pci/oxygen/oxygen.h b/sound/pci/oxygen/oxygen.h
index 6147216af74..7d5222caa0a 100644
--- a/sound/pci/oxygen/oxygen.h
+++ b/sound/pci/oxygen/oxygen.h
@@ -34,6 +34,7 @@
/* CAPTURE_3_FROM_I2S_3 not implemented */
#define MIDI_OUTPUT 0x0800
#define MIDI_INPUT 0x1000
+#define AC97_CD_INPUT 0x2000
enum {
CONTROL_SPDIF_PCM,
@@ -155,6 +156,7 @@ void oxygen_pci_remove(struct pci_dev *pci);
int oxygen_pci_suspend(struct pci_dev *pci, pm_message_t state);
int oxygen_pci_resume(struct pci_dev *pci);
#endif
+void oxygen_pci_shutdown(struct pci_dev *pci);
/* oxygen_mixer.c */
diff --git a/sound/pci/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c
index fad03d64e3a..e5ebe56fb0c 100644
--- a/sound/pci/oxygen/oxygen_lib.c
+++ b/sound/pci/oxygen/oxygen_lib.c
@@ -308,25 +308,46 @@ static void oxygen_restore_eeprom(struct oxygen *chip,
}
}
-static void pci_bridge_magic(void)
+static void configure_pcie_bridge(struct pci_dev *pci)
{
- struct pci_dev *pci = NULL;
+ enum { PEX811X, PI7C9X110 };
+ static const struct pci_device_id bridge_ids[] = {
+ { PCI_VDEVICE(PLX, 0x8111), .driver_data = PEX811X },
+ { PCI_VDEVICE(PLX, 0x8112), .driver_data = PEX811X },
+ { PCI_DEVICE(0x12d8, 0xe110), .driver_data = PI7C9X110 },
+ { }
+ };
+ struct pci_dev *bridge;
+ const struct pci_device_id *id;
u32 tmp;
- for (;;) {
- /* If there is any Pericom PI7C9X110 PCI-E/PCI bridge ... */
- pci = pci_get_device(0x12d8, 0xe110, pci);
- if (!pci)
- break;
- /*
- * ... configure its secondary internal arbiter to park to
- * the secondary port, instead of to the last master.
- */
- if (!pci_read_config_dword(pci, 0x40, &tmp)) {
- tmp |= 1;
- pci_write_config_dword(pci, 0x40, tmp);
- }
- /* Why? Try asking C-Media. */
+ if (!pci->bus || !pci->bus->self)
+ return;
+ bridge = pci->bus->self;
+
+ id = pci_match_id(bridge_ids, bridge);
+ if (!id)
+ return;
+
+ switch (id->driver_data) {
+ case PEX811X: /* PLX PEX8111/PEX8112 PCIe/PCI bridge */
+ pci_read_config_dword(bridge, 0x48, &tmp);
+ tmp |= 1; /* enable blind prefetching */
+ tmp |= 1 << 11; /* enable beacon generation */
+ pci_write_config_dword(bridge, 0x48, tmp);
+
+ pci_write_config_dword(bridge, 0x84, 0x0c);
+ pci_read_config_dword(bridge, 0x88, &tmp);
+ tmp &= ~(7 << 27);
+ tmp |= 2 << 27; /* set prefetch size to 128 bytes */
+ pci_write_config_dword(bridge, 0x88, tmp);
+ break;
+
+ case PI7C9X110: /* Pericom PI7C9X110 PCIe/PCI bridge */
+ pci_read_config_dword(bridge, 0x40, &tmp);
+ tmp |= 1; /* park the PCI arbiter to the sound chip */
+ pci_write_config_dword(bridge, 0x40, tmp);
+ break;
}
}
@@ -519,16 +540,21 @@ static void oxygen_init(struct oxygen *chip)
}
}
-static void oxygen_card_free(struct snd_card *card)
+static void oxygen_shutdown(struct oxygen *chip)
{
- struct oxygen *chip = card->private_data;
-
spin_lock_irq(&chip->reg_lock);
chip->interrupt_mask = 0;
chip->pcm_running = 0;
oxygen_write16(chip, OXYGEN_DMA_STATUS, 0);
oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, 0);
spin_unlock_irq(&chip->reg_lock);
+}
+
+static void oxygen_card_free(struct snd_card *card)
+{
+ struct oxygen *chip = card->private_data;
+
+ oxygen_shutdown(chip);
if (chip->irq >= 0)
free_irq(chip->irq, chip);
flush_scheduled_work();
@@ -608,7 +634,7 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
snd_card_set_dev(card, &pci->dev);
card->private_free = oxygen_card_free;
- pci_bridge_magic();
+ configure_pcie_bridge(pci);
oxygen_init(chip);
chip->model.init(chip);
@@ -778,3 +804,13 @@ int oxygen_pci_resume(struct pci_dev *pci)
}
EXPORT_SYMBOL(oxygen_pci_resume);
#endif /* CONFIG_PM */
+
+void oxygen_pci_shutdown(struct pci_dev *pci)
+{
+ struct snd_card *card = pci_get_drvdata(pci);
+ struct oxygen *chip = card->private_data;
+
+ oxygen_shutdown(chip);
+ chip->model.cleanup(chip);
+}
+EXPORT_SYMBOL(oxygen_pci_shutdown);
diff --git a/sound/pci/oxygen/oxygen_mixer.c b/sound/pci/oxygen/oxygen_mixer.c
index f375b8a2786..2849b36f5f7 100644
--- a/sound/pci/oxygen/oxygen_mixer.c
+++ b/sound/pci/oxygen/oxygen_mixer.c
@@ -708,7 +708,7 @@ static int ac97_fp_rec_volume_put(struct snd_kcontrol *ctl,
.private_value = ((codec) << 24) | ((stereo) << 16) | (index), \
}
-static DECLARE_TLV_DB_SCALE(monitor_db_scale, -1000, 1000, 0);
+static DECLARE_TLV_DB_SCALE(monitor_db_scale, -600, 600, 0);
static DECLARE_TLV_DB_SCALE(ac97_db_scale, -3450, 150, 0);
static DECLARE_TLV_DB_SCALE(ac97_rec_db_scale, 0, 150, 0);
@@ -972,6 +972,9 @@ static int add_controls(struct oxygen *chip,
if (!strcmp(template.name, "Stereo Upmixing") &&
chip->model.dac_channels == 2)
continue;
+ if (!strncmp(template.name, "CD Capture ", 11) &&
+ !(chip->model.device_config & AC97_CD_INPUT))
+ continue;
if (!strcmp(template.name, "Master Playback Volume") &&
chip->model.dac_tlv) {
template.tlv.p = chip->model.dac_tlv;
diff --git a/sound/pci/oxygen/oxygen_pcm.c b/sound/pci/oxygen/oxygen_pcm.c
index 9dff6954c39..814667442eb 100644
--- a/sound/pci/oxygen/oxygen_pcm.c
+++ b/sound/pci/oxygen/oxygen_pcm.c
@@ -56,8 +56,8 @@ static const struct snd_pcm_hardware oxygen_stereo_hardware = {
.channels_max = 2,
.buffer_bytes_max = BUFFER_BYTES_MAX,
.period_bytes_min = PERIOD_BYTES_MIN,
- .period_bytes_max = BUFFER_BYTES_MAX / 2,
- .periods_min = 2,
+ .period_bytes_max = BUFFER_BYTES_MAX,
+ .periods_min = 1,
.periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN,
};
static const struct snd_pcm_hardware oxygen_multichannel_hardware = {
@@ -82,8 +82,8 @@ static const struct snd_pcm_hardware oxygen_multichannel_hardware = {
.channels_max = 8,
.buffer_bytes_max = BUFFER_BYTES_MAX_MULTICH,
.period_bytes_min = PERIOD_BYTES_MIN,
- .period_bytes_max = BUFFER_BYTES_MAX_MULTICH / 2,
- .periods_min = 2,
+ .period_bytes_max = BUFFER_BYTES_MAX_MULTICH,
+ .periods_min = 1,
.periods_max = BUFFER_BYTES_MAX_MULTICH / PERIOD_BYTES_MIN,
};
static const struct snd_pcm_hardware oxygen_ac97_hardware = {
@@ -100,8 +100,8 @@ static const struct snd_pcm_hardware oxygen_ac97_hardware = {
.channels_max = 2,
.buffer_bytes_max = BUFFER_BYTES_MAX,
.period_bytes_min = PERIOD_BYTES_MIN,
- .period_bytes_max = BUFFER_BYTES_MAX / 2,
- .periods_min = 2,
+ .period_bytes_max = BUFFER_BYTES_MAX,
+ .periods_min = 1,
.periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN,
};
diff --git a/sound/pci/oxygen/oxygen_regs.h b/sound/pci/oxygen/oxygen_regs.h
index 72de159d456..4dcd41b7825 100644
--- a/sound/pci/oxygen/oxygen_regs.h
+++ b/sound/pci/oxygen/oxygen_regs.h
@@ -436,13 +436,15 @@
/* OXYGEN_CHANNEL_* */
#define OXYGEN_CODEC_VERSION 0xe4
-#define OXYGEN_XCID_MASK 0x07
+#define OXYGEN_CODEC_ID_MASK 0x07
#define OXYGEN_REVISION 0xe6
-#define OXYGEN_REVISION_XPKGID_MASK 0x0007
+#define OXYGEN_PACKAGE_ID_MASK 0x0007
+#define OXYGEN_PACKAGE_ID_8786 0x0004
+#define OXYGEN_PACKAGE_ID_8787 0x0006
+#define OXYGEN_PACKAGE_ID_8788 0x0007
#define OXYGEN_REVISION_MASK 0xfff8
-#define OXYGEN_REVISION_2 0x0008 /* bit flag */
-#define OXYGEN_REVISION_8787 0x0014 /* 8 bits */
+#define OXYGEN_REVISION_2 0x0008
#define OXYGEN_OFFSIN_48K 0xe8
#define OXYGEN_OFFSBASE_48K 0xe9
diff --git a/sound/pci/oxygen/virtuoso.c b/sound/pci/oxygen/virtuoso.c
index f03a2f2cffe..469010a8b84 100644
--- a/sound/pci/oxygen/virtuoso.c
+++ b/sound/pci/oxygen/virtuoso.c
@@ -25,9 +25,9 @@
#include "xonar.h"
MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
-MODULE_DESCRIPTION("Asus AVx00 driver");
+MODULE_DESCRIPTION("Asus Virtuoso driver");
MODULE_LICENSE("GPL v2");
-MODULE_SUPPORTED_DEVICE("{{Asus,AV100},{Asus,AV200}}");
+MODULE_SUPPORTED_DEVICE("{{Asus,AV66},{Asus,AV100},{Asus,AV200}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
@@ -49,6 +49,7 @@ static DEFINE_PCI_DEVICE_TABLE(xonar_ids) = {
{ OXYGEN_PCI_SUBID(0x1043, 0x834f) },
{ OXYGEN_PCI_SUBID(0x1043, 0x835c) },
{ OXYGEN_PCI_SUBID(0x1043, 0x835d) },
+ { OXYGEN_PCI_SUBID(0x1043, 0x835e) },
{ OXYGEN_PCI_SUBID(0x1043, 0x838e) },
{ OXYGEN_PCI_SUBID_BROKEN_EEPROM },
{ }
@@ -95,6 +96,7 @@ static struct pci_driver xonar_driver = {
.suspend = oxygen_pci_suspend,
.resume = oxygen_pci_resume,
#endif
+ .shutdown = oxygen_pci_shutdown,
};
static int __init alsa_card_xonar_init(void)
diff --git a/sound/pci/oxygen/xonar_cs43xx.c b/sound/pci/oxygen/xonar_cs43xx.c
index 7c4986b27f2..aa27c31049a 100644
--- a/sound/pci/oxygen/xonar_cs43xx.c
+++ b/sound/pci/oxygen/xonar_cs43xx.c
@@ -367,13 +367,6 @@ static void xonar_d1_line_mic_ac97_switch(struct oxygen *chip,
static const DECLARE_TLV_DB_SCALE(cs4362a_db_scale, -6000, 100, 0);
-static int xonar_d1_control_filter(struct snd_kcontrol_new *template)
-{
- if (!strncmp(template->name, "CD Capture ", 11))
- return 1; /* no CD input */
- return 0;
-}
-
static int xonar_d1_mixer_init(struct oxygen *chip)
{
int err;
@@ -391,7 +384,6 @@ static const struct oxygen_model model_xonar_d1 = {
.longname = "Asus Virtuoso 100",
.chip = "AV200",
.init = xonar_d1_init,
- .control_filter = xonar_d1_control_filter,
.mixer_init = xonar_d1_mixer_init,
.cleanup = xonar_d1_cleanup,
.suspend = xonar_d1_suspend,
diff --git a/sound/pci/oxygen/xonar_pcm179x.c b/sound/pci/oxygen/xonar_pcm179x.c
index ba18fb546b4..d491fd6c0be 100644
--- a/sound/pci/oxygen/xonar_pcm179x.c
+++ b/sound/pci/oxygen/xonar_pcm179x.c
@@ -132,6 +132,18 @@
* GPIO 5 <- 0
*/
+/*
+ * Xonar HDAV1.3 Slim
+ * ------------------
+ *
+ * CMI8788:
+ *
+ * GPIO 1 -> enable output
+ *
+ * TXD -> HDMI controller
+ * RXD <- HDMI controller
+ */
+
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/mutex.h>
@@ -362,7 +374,6 @@ static void xonar_st_init_common(struct oxygen *chip)
{
struct xonar_pcm179x *data = chip->model_data;
- data->generic.anti_pop_delay = 100;
data->generic.output_enable_bit = GPIO_ST_OUTPUT_ENABLE;
data->dacs = chip->model.private_data ? 4 : 1;
data->hp_gain_offset = 2*-18;
@@ -408,6 +419,7 @@ static void xonar_st_init(struct oxygen *chip)
{
struct xonar_pcm179x *data = chip->model_data;
+ data->generic.anti_pop_delay = 100;
data->has_cs2000 = 1;
data->cs2000_fun_cfg_1 = CS2000_REF_CLK_DIV_1;
@@ -428,6 +440,7 @@ static void xonar_stx_init(struct oxygen *chip)
struct xonar_pcm179x *data = chip->model_data;
xonar_st_init_i2c(chip);
+ data->generic.anti_pop_delay = 800;
data->generic.ext_power_reg = OXYGEN_GPI_DATA;
data->generic.ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;
data->generic.ext_power_bit = GPI_EXT_POWER;
@@ -915,13 +928,6 @@ static int xonar_d2_control_filter(struct snd_kcontrol_new *template)
return 0;
}
-static int xonar_st_control_filter(struct snd_kcontrol_new *template)
-{
- if (!strncmp(template->name, "CD Capture ", 11))
- return 1; /* no CD input */
- return 0;
-}
-
static int add_pcm1796_controls(struct oxygen *chip)
{
int err;
@@ -991,7 +997,8 @@ static const struct oxygen_model model_xonar_d2 = {
CAPTURE_0_FROM_I2S_2 |
CAPTURE_1_FROM_SPDIF |
MIDI_OUTPUT |
- MIDI_INPUT,
+ MIDI_INPUT |
+ AC97_CD_INPUT,
.dac_channels = 8,
.dac_volume_min = 255 - 2*60,
.dac_volume_max = 255,
@@ -1037,7 +1044,6 @@ static const struct oxygen_model model_xonar_st = {
.longname = "Asus Virtuoso 100",
.chip = "AV200",
.init = xonar_st_init,
- .control_filter = xonar_st_control_filter,
.mixer_init = xonar_st_mixer_init,
.cleanup = xonar_st_cleanup,
.suspend = xonar_st_suspend,
@@ -1108,6 +1114,9 @@ int __devinit get_xonar_pcm179x_model(struct oxygen *chip,
chip->model.resume = xonar_stx_resume;
chip->model.set_dac_params = set_pcm1796_params;
break;
+ case 0x835e:
+ snd_printk(KERN_ERR "the HDAV1.3 Slim is not supported\n");
+ return -ENODEV;
default:
return -EINVAL;
}
diff --git a/sound/pci/oxygen/xonar_wm87x6.c b/sound/pci/oxygen/xonar_wm87x6.c
index dbc4b89d74e..200f7601276 100644
--- a/sound/pci/oxygen/xonar_wm87x6.c
+++ b/sound/pci/oxygen/xonar_wm87x6.c
@@ -25,16 +25,24 @@
* SPI 0 -> WM8766 (surround, center/LFE, back)
* SPI 1 -> WM8776 (front, input)
*
- * GPIO 4 <- headphone detect
- * GPIO 6 -> route input jack to input 1/2 (1/0)
- * GPIO 7 -> enable output to speakers
- * GPIO 8 -> enable output to speakers
+ * GPIO 4 <- headphone detect, 0 = plugged
+ * GPIO 6 -> route input jack to mic-in (0) or line-in (1)
+ * GPIO 7 -> enable output to front L/R speaker channels
+ * GPIO 8 -> enable output to other speaker channels and front panel headphone
+ *
+ * WM8766:
+ *
+ * input 1 <- line
+ * input 2 <- mic
+ * input 3 <- front mic
+ * input 4 <- aux
*/
#include <linux/pci.h>
#include <linux/delay.h>
#include <sound/control.h>
#include <sound/core.h>
+#include <sound/jack.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/tlv.h>
@@ -44,7 +52,8 @@
#define GPIO_DS_HP_DETECT 0x0010
#define GPIO_DS_INPUT_ROUTE 0x0040
-#define GPIO_DS_OUTPUT_ENABLE 0x0180
+#define GPIO_DS_OUTPUT_FRONTLR 0x0080
+#define GPIO_DS_OUTPUT_ENABLE 0x0100
#define LC_CONTROL_LIMITER 0x40000000
#define LC_CONTROL_ALC 0x20000000
@@ -53,7 +62,10 @@ struct xonar_wm87x6 {
struct xonar_generic generic;
u16 wm8776_regs[0x17];
u16 wm8766_regs[0x10];
+ struct snd_kcontrol *line_adcmux_control;
+ struct snd_kcontrol *mic_adcmux_control;
struct snd_kcontrol *lc_controls[13];
+ struct snd_jack *hp_jack;
};
static void wm8776_write(struct oxygen *chip,
@@ -95,8 +107,12 @@ static void wm8766_write(struct oxygen *chip,
(0 << OXYGEN_SPI_CODEC_SHIFT) |
OXYGEN_SPI_CEN_LATCH_CLOCK_LO,
(reg << 9) | value);
- if (reg < ARRAY_SIZE(data->wm8766_regs))
+ if (reg < ARRAY_SIZE(data->wm8766_regs)) {
+ if ((reg >= WM8766_LDA1 && reg <= WM8766_RDA1) ||
+ (reg >= WM8766_LDA2 && reg <= WM8766_MASTDA))
+ value &= ~WM8766_UPDATE;
data->wm8766_regs[reg] = value;
+ }
}
static void wm8766_write_cached(struct oxygen *chip,
@@ -105,12 +121,8 @@ static void wm8766_write_cached(struct oxygen *chip,
struct xonar_wm87x6 *data = chip->model_data;
if (reg >= ARRAY_SIZE(data->wm8766_regs) ||
- value != data->wm8766_regs[reg]) {
- if ((reg >= WM8766_LDA1 && reg <= WM8766_RDA1) ||
- (reg >= WM8766_LDA2 && reg <= WM8766_MASTDA))
- value &= ~WM8766_UPDATE;
+ value != data->wm8766_regs[reg])
wm8766_write(chip, reg, value);
- }
}
static void wm8776_registers_init(struct oxygen *chip)
@@ -139,7 +151,10 @@ static void wm8776_registers_init(struct oxygen *chip)
static void wm8766_registers_init(struct oxygen *chip)
{
+ struct xonar_wm87x6 *data = chip->model_data;
+
wm8766_write(chip, WM8766_RESET, 0);
+ wm8766_write(chip, WM8766_DAC_CTRL, data->wm8766_regs[WM8766_DAC_CTRL]);
wm8766_write(chip, WM8766_INT_CTRL, WM8766_FMT_LJUST | WM8766_IWL_24);
wm8766_write(chip, WM8766_DAC_CTRL2,
WM8766_ZCD | (chip->dac_mute ? WM8766_DMUTE_MASK : 0));
@@ -168,6 +183,40 @@ static void wm8776_init(struct oxygen *chip)
wm8776_registers_init(chip);
}
+static void wm8766_init(struct oxygen *chip)
+{
+ struct xonar_wm87x6 *data = chip->model_data;
+
+ data->wm8766_regs[WM8766_DAC_CTRL] =
+ WM8766_PL_LEFT_LEFT | WM8766_PL_RIGHT_RIGHT;
+ wm8766_registers_init(chip);
+}
+
+static void xonar_ds_handle_hp_jack(struct oxygen *chip)
+{
+ struct xonar_wm87x6 *data = chip->model_data;
+ bool hp_plugged;
+ unsigned int reg;
+
+ mutex_lock(&chip->mutex);
+
+ hp_plugged = !(oxygen_read16(chip, OXYGEN_GPIO_DATA) &
+ GPIO_DS_HP_DETECT);
+
+ oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
+ hp_plugged ? 0 : GPIO_DS_OUTPUT_FRONTLR,
+ GPIO_DS_OUTPUT_FRONTLR);
+
+ reg = data->wm8766_regs[WM8766_DAC_CTRL] & ~WM8766_MUTEALL;
+ if (hp_plugged)
+ reg |= WM8766_MUTEALL;
+ wm8766_write_cached(chip, WM8766_DAC_CTRL, reg);
+
+ snd_jack_report(data->hp_jack, hp_plugged ? SND_JACK_HEADPHONE : 0);
+
+ mutex_unlock(&chip->mutex);
+}
+
static void xonar_ds_init(struct oxygen *chip)
{
struct xonar_wm87x6 *data = chip->model_data;
@@ -176,16 +225,22 @@ static void xonar_ds_init(struct oxygen *chip)
data->generic.output_enable_bit = GPIO_DS_OUTPUT_ENABLE;
wm8776_init(chip);
- wm8766_registers_init(chip);
+ wm8766_init(chip);
- oxygen_write16_masked(chip, OXYGEN_GPIO_CONTROL, GPIO_DS_INPUT_ROUTE,
- GPIO_DS_HP_DETECT | GPIO_DS_INPUT_ROUTE);
+ oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
+ GPIO_DS_INPUT_ROUTE | GPIO_DS_OUTPUT_FRONTLR);
+ oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL,
+ GPIO_DS_HP_DETECT);
oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_DS_INPUT_ROUTE);
oxygen_set_bits16(chip, OXYGEN_GPIO_INTERRUPT_MASK, GPIO_DS_HP_DETECT);
chip->interrupt_mask |= OXYGEN_INT_GPIO;
xonar_enable_output(chip);
+ snd_jack_new(chip->card, "Headphone",
+ SND_JACK_HEADPHONE, &data->hp_jack);
+ xonar_ds_handle_hp_jack(chip);
+
snd_component_add(chip->card, "WM8776");
snd_component_add(chip->card, "WM8766");
}
@@ -193,6 +248,7 @@ static void xonar_ds_init(struct oxygen *chip)
static void xonar_ds_cleanup(struct oxygen *chip)
{
xonar_disable_output(chip);
+ wm8776_write(chip, WM8776_RESET, 0);
}
static void xonar_ds_suspend(struct oxygen *chip)
@@ -205,6 +261,7 @@ static void xonar_ds_resume(struct oxygen *chip)
wm8776_registers_init(chip);
wm8766_registers_init(chip);
xonar_enable_output(chip);
+ xonar_ds_handle_hp_jack(chip);
}
static void wm8776_adc_hardware_filter(unsigned int channel,
@@ -320,12 +377,27 @@ static void update_wm87x6_mute(struct oxygen *chip)
(chip->dac_mute ? WM8766_DMUTE_MASK : 0));
}
-static void xonar_ds_gpio_changed(struct oxygen *chip)
+static void update_wm8766_center_lfe_mix(struct oxygen *chip, bool mixed)
{
- u16 bits;
+ struct xonar_wm87x6 *data = chip->model_data;
+ unsigned int reg;
- bits = oxygen_read16(chip, OXYGEN_GPIO_DATA);
- snd_printk(KERN_INFO "HP detect: %d\n", !!(bits & GPIO_DS_HP_DETECT));
+ /*
+ * The WM8766 can mix left and right channels, but this setting
+ * applies to all three stereo pairs.
+ */
+ reg = data->wm8766_regs[WM8766_DAC_CTRL] &
+ ~(WM8766_PL_LEFT_MASK | WM8766_PL_RIGHT_MASK);
+ if (mixed)
+ reg |= WM8766_PL_LEFT_LRMIX | WM8766_PL_RIGHT_LRMIX;
+ else
+ reg |= WM8766_PL_LEFT_LEFT | WM8766_PL_RIGHT_RIGHT;
+ wm8766_write_cached(chip, WM8766_DAC_CTRL, reg);
+}
+
+static void xonar_ds_gpio_changed(struct oxygen *chip)
+{
+ xonar_ds_handle_hp_jack(chip);
}
static int wm8776_bit_switch_get(struct snd_kcontrol *ctl,
@@ -603,6 +675,7 @@ static int wm8776_input_mux_put(struct snd_kcontrol *ctl,
{
struct oxygen *chip = ctl->private_data;
struct xonar_wm87x6 *data = chip->model_data;
+ struct snd_kcontrol *other_ctl;
unsigned int mux_bit = ctl->private_value;
u16 reg;
int changed;
@@ -610,8 +683,18 @@ static int wm8776_input_mux_put(struct snd_kcontrol *ctl,
mutex_lock(&chip->mutex);
reg = data->wm8776_regs[WM8776_ADCMUX];
if (value->value.integer.value[0]) {
- reg &= ~0x003;
reg |= mux_bit;
+ /* line-in and mic-in are exclusive */
+ mux_bit ^= 3;
+ if (reg & mux_bit) {
+ reg &= ~mux_bit;
+ if (mux_bit == 1)
+ other_ctl = data->line_adcmux_control;
+ else
+ other_ctl = data->mic_adcmux_control;
+ snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &other_ctl->id);
+ }
} else
reg &= ~mux_bit;
changed = reg != data->wm8776_regs[WM8776_ADCMUX];
@@ -882,7 +965,10 @@ static const struct snd_kcontrol_new ds_controls[] = {
.put = wm8776_input_mux_put,
.private_value = 1 << 1,
},
- WM8776_BIT_SWITCH("Aux", WM8776_ADCMUX, 1 << 2, 0, 0),
+ WM8776_BIT_SWITCH("Front Mic Capture Switch",
+ WM8776_ADCMUX, 1 << 2, 0, 0),
+ WM8776_BIT_SWITCH("Aux Capture Switch",
+ WM8776_ADCMUX, 1 << 3, 0, 0),
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "ADC Filter Capture Enum",
@@ -942,13 +1028,6 @@ static const struct snd_kcontrol_new lc_controls[] = {
LC_CONTROL_ALC, wm8776_ngth_db_scale),
};
-static int xonar_ds_control_filter(struct snd_kcontrol_new *template)
-{
- if (!strncmp(template->name, "CD Capture ", 11))
- return 1; /* no CD input */
- return 0;
-}
-
static int xonar_ds_mixer_init(struct oxygen *chip)
{
struct xonar_wm87x6 *data = chip->model_data;
@@ -963,7 +1042,13 @@ static int xonar_ds_mixer_init(struct oxygen *chip)
err = snd_ctl_add(chip->card, ctl);
if (err < 0)
return err;
+ if (!strcmp(ctl->id.name, "Line Capture Switch"))
+ data->line_adcmux_control = ctl;
+ else if (!strcmp(ctl->id.name, "Mic Capture Switch"))
+ data->mic_adcmux_control = ctl;
}
+ if (!data->line_adcmux_control || !data->mic_adcmux_control)
+ return -ENXIO;
BUILD_BUG_ON(ARRAY_SIZE(lc_controls) != ARRAY_SIZE(data->lc_controls));
for (i = 0; i < ARRAY_SIZE(lc_controls); ++i) {
ctl = snd_ctl_new1(&lc_controls[i], chip);
@@ -979,10 +1064,9 @@ static int xonar_ds_mixer_init(struct oxygen *chip)
static const struct oxygen_model model_xonar_ds = {
.shortname = "Xonar DS",
- .longname = "Asus Virtuoso 200",
+ .longname = "Asus Virtuoso 66",
.chip = "AV200",
.init = xonar_ds_init,
- .control_filter = xonar_ds_control_filter,
.mixer_init = xonar_ds_mixer_init,
.cleanup = xonar_ds_cleanup,
.suspend = xonar_ds_suspend,
@@ -993,6 +1077,7 @@ static const struct oxygen_model model_xonar_ds = {
.set_adc_params = set_wm8776_adc_params,
.update_dac_volume = update_wm87x6_volume,
.update_dac_mute = update_wm87x6_mute,
+ .update_center_lfe_mix = update_wm8766_center_lfe_mix,
.gpio_changed = xonar_ds_gpio_changed,
.dac_tlv = wm87x6_dac_db_scale,
.model_data_size = sizeof(struct xonar_wm87x6),
diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c
index f64fb7d988c..ad5202efd7a 100644
--- a/sound/pci/riptide/riptide.c
+++ b/sound/pci/riptide/riptide.c
@@ -1224,15 +1224,14 @@ static int try_to_load_firmware(struct cmdif *cif, struct snd_riptide *chip)
firmware.firmware.ASIC, firmware.firmware.CODEC,
firmware.firmware.AUXDSP, firmware.firmware.PROG);
+ if (!chip)
+ return 1;
+
for (i = 0; i < FIRMWARE_VERSIONS; i++) {
if (!memcmp(&firmware_versions[i], &firmware, sizeof(firmware)))
- break;
- }
- if (i >= FIRMWARE_VERSIONS)
- return 0; /* no match */
+ return 1; /* OK */
- if (!chip)
- return 1; /* OK */
+ }
snd_printdd("Writing Firmware\n");
if (!chip->fw_entry) {
diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c
index d19dc052c39..d5f5b440fc4 100644
--- a/sound/pci/rme96.c
+++ b/sound/pci/rme96.c
@@ -1527,14 +1527,14 @@ snd_rme96_free(void *private_data)
static void
snd_rme96_free_spdif_pcm(struct snd_pcm *pcm)
{
- struct rme96 *rme96 = (struct rme96 *) pcm->private_data;
+ struct rme96 *rme96 = pcm->private_data;
rme96->spdif_pcm = NULL;
}
static void
snd_rme96_free_adat_pcm(struct snd_pcm *pcm)
{
- struct rme96 *rme96 = (struct rme96 *) pcm->private_data;
+ struct rme96 *rme96 = pcm->private_data;
rme96->adat_pcm = NULL;
}
@@ -1661,7 +1661,7 @@ static void
snd_rme96_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
{
int n;
- struct rme96 *rme96 = (struct rme96 *)entry->private_data;
+ struct rme96 *rme96 = entry->private_data;
rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER);
@@ -2348,7 +2348,7 @@ snd_rme96_probe(struct pci_dev *pci,
if (err < 0)
return err;
card->private_free = snd_rme96_card_free;
- rme96 = (struct rme96 *)card->private_data;
+ rme96 = card->private_data;
rme96->card = card;
rme96->pci = pci;
snd_card_set_dev(card, &pci->dev);
diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c
index b92adef8e81..0b720cf7783 100644
--- a/sound/pci/rme9652/hdsp.c
+++ b/sound/pci/rme9652/hdsp.c
@@ -3284,7 +3284,7 @@ static int snd_hdsp_create_controls(struct snd_card *card, struct hdsp *hdsp)
static void
snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
{
- struct hdsp *hdsp = (struct hdsp *) entry->private_data;
+ struct hdsp *hdsp = entry->private_data;
unsigned int status;
unsigned int status2;
char *pref_sync_ref;
@@ -4566,7 +4566,7 @@ static int hdsp_get_peak(struct hdsp *hdsp, struct hdsp_peak_rms __user *peak_rm
static int snd_hdsp_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, unsigned int cmd, unsigned long arg)
{
- struct hdsp *hdsp = (struct hdsp *)hw->private_data;
+ struct hdsp *hdsp = hw->private_data;
void __user *argp = (void __user *)arg;
int err;
@@ -4609,6 +4609,7 @@ static int snd_hdsp_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, unsigne
if (err < 0)
return err;
+ memset(&info, 0, sizeof(info));
spin_lock_irqsave(&hdsp->lock, flags);
info.pref_sync_ref = (unsigned char)hdsp_pref_sync_ref(hdsp);
info.wordclock_sync_check = (unsigned char)hdsp_wc_sync_check(hdsp);
@@ -5155,7 +5156,7 @@ static int snd_hdsp_free(struct hdsp *hdsp)
static void snd_hdsp_card_free(struct snd_card *card)
{
- struct hdsp *hdsp = (struct hdsp *) card->private_data;
+ struct hdsp *hdsp = card->private_data;
if (hdsp)
snd_hdsp_free(hdsp);
@@ -5181,7 +5182,7 @@ static int __devinit snd_hdsp_probe(struct pci_dev *pci,
if (err < 0)
return err;
- hdsp = (struct hdsp *) card->private_data;
+ hdsp = card->private_data;
card->private_free = snd_hdsp_card_free;
hdsp->dev = dev;
hdsp->pci = pci;
diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c
index 547b713d720..0c98ef9156d 100644
--- a/sound/pci/rme9652/hdspm.c
+++ b/sound/pci/rme9652/hdspm.c
@@ -4127,6 +4127,7 @@ static int snd_hdspm_hwdep_ioctl(struct snd_hwdep * hw, struct file *file,
case SNDRV_HDSPM_IOCTL_GET_CONFIG_INFO:
+ memset(&info, 0, sizeof(info));
spin_lock_irq(&hdspm->lock);
info.pref_sync_ref = hdspm_pref_sync_ref(hdspm);
info.wordclock_sync_check = hdspm_wc_sync_check(hdspm);
diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf.c b/sound/pcmcia/pdaudiocf/pdaudiocf.c
index 7ab9174a8a8..8cc4733698a 100644
--- a/sound/pcmcia/pdaudiocf/pdaudiocf.c
+++ b/sound/pcmcia/pdaudiocf/pdaudiocf.c
@@ -142,10 +142,9 @@ static int snd_pdacf_probe(struct pcmcia_device *link)
link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO;
link->resource[0]->end = 16;
- link->conf.Attributes = CONF_ENABLE_IRQ | CONF_ENABLE_PULSE_IRQ;
- link->conf.IntType = INT_MEMORY_AND_IO;
- link->conf.ConfigIndex = 1;
- link->conf.Present = PRESENT_OPTION;
+ link->config_flags = CONF_ENABLE_IRQ | CONF_ENABLE_PULSE_IRQ;
+ link->config_index = 1;
+ link->config_regs = PRESENT_OPTION;
return pdacf_config(link);
}
@@ -217,7 +216,8 @@ static int pdacf_config(struct pcmcia_device *link)
int ret;
snd_printdd(KERN_DEBUG "pdacf_config called\n");
- link->conf.ConfigIndex = 0x5;
+ link->config_index = 0x5;
+ link->config_flags |= CONF_ENABLE_IRQ | CONF_ENABLE_PULSE_IRQ;
ret = pcmcia_request_io(link);
if (ret)
@@ -227,7 +227,7 @@ static int pdacf_config(struct pcmcia_device *link)
if (ret)
goto failed;
- ret = pcmcia_request_configuration(link, &link->conf);
+ ret = pcmcia_enable_device(link);
if (ret)
goto failed;
@@ -287,9 +287,7 @@ MODULE_DEVICE_TABLE(pcmcia, snd_pdacf_ids);
static struct pcmcia_driver pdacf_cs_driver = {
.owner = THIS_MODULE,
- .drv = {
- .name = "snd-pdaudiocf",
- },
+ .name = "snd-pdaudiocf",
.probe = snd_pdacf_probe,
.remove = snd_pdacf_detach,
.id_table = snd_pdacf_ids,
diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf.h b/sound/pcmcia/pdaudiocf/pdaudiocf.h
index 5cc3e457307..bd26e092aea 100644
--- a/sound/pcmcia/pdaudiocf/pdaudiocf.h
+++ b/sound/pcmcia/pdaudiocf/pdaudiocf.h
@@ -24,7 +24,6 @@
#include <sound/pcm.h>
#include <asm/io.h>
#include <linux/interrupt.h>
-#include <pcmcia/cs.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/ds.h>
diff --git a/sound/pcmcia/vx/vxpocket.c b/sound/pcmcia/vx/vxpocket.c
index a6edfc3be29..80000d631f8 100644
--- a/sound/pcmcia/vx/vxpocket.c
+++ b/sound/pcmcia/vx/vxpocket.c
@@ -2,7 +2,7 @@
* Driver for Digigram VXpocket V2/440 soundcards
*
* Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>
- *
+
* 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
@@ -162,10 +162,9 @@ static int snd_vxpocket_new(struct snd_card *card, int ibl,
link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO;
link->resource[0]->end = 16;
- link->conf.Attributes = CONF_ENABLE_IRQ;
- link->conf.IntType = INT_MEMORY_AND_IO;
- link->conf.ConfigIndex = 1;
- link->conf.Present = PRESENT_OPTION;
+ link->config_flags |= CONF_ENABLE_IRQ;
+ link->config_index = 1;
+ link->config_regs = PRESENT_OPTION;
*chip_ret = vxp;
return 0;
@@ -234,7 +233,7 @@ static int vxpocket_config(struct pcmcia_device *link)
if (ret)
goto failed;
- ret = pcmcia_request_configuration(link, &link->conf);
+ ret = pcmcia_enable_device(link);
if (ret)
goto failed;
@@ -359,9 +358,7 @@ MODULE_DEVICE_TABLE(pcmcia, vxp_ids);
static struct pcmcia_driver vxp_cs_driver = {
.owner = THIS_MODULE,
- .drv = {
- .name = "snd-vxpocket",
- },
+ .name = "snd-vxpocket",
.probe = vxpocket_probe,
.remove = vxpocket_detach,
.id_table = vxp_ids,
diff --git a/sound/pcmcia/vx/vxpocket.h b/sound/pcmcia/vx/vxpocket.h
index d9110669d04..13d658c1a21 100644
--- a/sound/pcmcia/vx/vxpocket.h
+++ b/sound/pcmcia/vx/vxpocket.h
@@ -23,7 +23,6 @@
#include <sound/vx_core.h>
-#include <pcmcia/cs.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/ds.h>
diff --git a/sound/ppc/snd_ps3.c b/sound/ppc/snd_ps3.c
index 2f12da4da56..581a670e826 100644
--- a/sound/ppc/snd_ps3.c
+++ b/sound/ppc/snd_ps3.c
@@ -579,7 +579,7 @@ static int snd_ps3_delay_to_bytes(struct snd_pcm_substream *substream,
rate * delay_ms / 1000)
* substream->runtime->channels;
- pr_debug(KERN_ERR "%s: time=%d rate=%d bytes=%ld, frames=%d, ret=%d\n",
+ pr_debug("%s: time=%d rate=%d bytes=%ld, frames=%d, ret=%d\n",
__func__,
delay_ms,
rate,
diff --git a/sound/ppc/tumbler.c b/sound/ppc/tumbler.c
index 20afdf9772e..961d9829769 100644
--- a/sound/ppc/tumbler.c
+++ b/sound/ppc/tumbler.c
@@ -785,7 +785,7 @@ static int snapper_set_capture_source(struct pmac_tumbler *mix)
if (! mix->i2c.client)
return -ENODEV;
if (mix->capture_source)
- mix->acs = mix->acs |= 2;
+ mix->acs |= 2;
else
mix->acs &= ~2;
return i2c_smbus_write_byte_data(mix->i2c.client, TAS_REG_ACS, mix->acs);
diff --git a/sound/sh/aica.c b/sound/sh/aica.c
index a0df401ebb9..94c6ea7fa7c 100644
--- a/sound/sh/aica.c
+++ b/sound/sh/aica.c
@@ -188,7 +188,7 @@ static void spu_reset(void)
spu_memset(0, 0, 0x200000 / 4);
/* Put ARM7 in endless loop */
local_irq_save(flags);
- ctrl_outl(0xea000002, SPU_MEMORY_BASE);
+ __raw_writel(0xea000002, SPU_MEMORY_BASE);
local_irq_restore(flags);
spu_enable();
}
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index ff7b922a0f4..94a9d06b902 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -27,6 +27,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_CS4270 if I2C
select SND_SOC_DA7210 if I2C
select SND_SOC_JZ4740 if SOC_JZ4740
+ select SND_SOC_MAX98088 if I2C
select SND_SOC_MAX9877 if I2C
select SND_SOC_PCM3008
select SND_SOC_SPDIF
@@ -158,6 +159,9 @@ config SND_SOC_L3
config SND_SOC_DA7210
tristate
+config SND_SOC_MAX98088
+ tristate
+
config SND_SOC_PCM3008
tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 0c5f329240c..f67a2d6f7a4 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -15,6 +15,7 @@ snd-soc-cs4270-objs := cs4270.o
snd-soc-cx20442-objs := cx20442.o
snd-soc-da7210-objs := da7210.o
snd-soc-l3-objs := l3.o
+snd-soc-max98088-objs := max98088.o
snd-soc-pcm3008-objs := pcm3008.o
snd-soc-spdif-objs := spdif_transciever.o
snd-soc-ssm2602-objs := ssm2602.o
@@ -49,7 +50,7 @@ snd-soc-wm8940-objs := wm8940.o
snd-soc-wm8955-objs := wm8955.o
snd-soc-wm8960-objs := wm8960.o
snd-soc-wm8961-objs := wm8961.o
-snd-soc-wm8962-objs := wm8962.o wm8962-tables.o
+snd-soc-wm8962-objs := wm8962.o
snd-soc-wm8971-objs := wm8971.o
snd-soc-wm8974-objs := wm8974.o
snd-soc-wm8978-objs := wm8978.o
@@ -89,6 +90,7 @@ obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o
obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o
obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o
obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o
+obj-$(CONFIG_SND_SOC_MAX98088) += snd-soc-max98088.o
obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o
obj-$(CONFIG_SND_SOC_SPDIF) += snd-soc-spdif.o
obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o
diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c
index 2c824a58875..3c087936aa5 100644
--- a/sound/soc/codecs/ac97.c
+++ b/sound/soc/codecs/ac97.c
@@ -79,12 +79,6 @@ static int ac97_soc_probe(struct snd_soc_codec *codec)
struct snd_ac97_template ac97_template;
int ret;
- ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
- if (ret < 0) {
- printk(KERN_ERR "ASoC: failed to init generic ac97 glue\n");
- return ret;
- }
-
/* add codec as bus device for standard ac97 */
ret = snd_ac97_bus(codec->card->snd_card, 0, &soc_ac97_ops, NULL, &ac97_bus);
if (ret < 0)
@@ -168,3 +162,4 @@ module_exit(ac97_exit);
MODULE_DESCRIPTION("Soc Generic AC97 driver");
MODULE_AUTHOR("Liam Girdwood");
MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:ac97-codec");
diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c
index d36bd1c333b..410ccd5d41c 100644
--- a/sound/soc/codecs/ad1980.c
+++ b/sound/soc/codecs/ad1980.c
@@ -133,7 +133,7 @@ static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
return 0;
}
-struct snd_soc_dai_driver ad1980_dai = {
+static struct snd_soc_dai_driver ad1980_dai = {
.name = "ad1980-hifi",
.ac97_control = 1,
.playback = {
diff --git a/sound/soc/codecs/ad73311.c b/sound/soc/codecs/ad73311.c
index c53955fe17b..de799cd1ba7 100644
--- a/sound/soc/codecs/ad73311.c
+++ b/sound/soc/codecs/ad73311.c
@@ -47,7 +47,7 @@ static int ad73311_probe(struct platform_device *pdev)
&soc_codec_dev_ad73311, &ad73311_dai, 1);
}
-static int ad73311_remove(struct platform_device *pdev)
+static int __devexit ad73311_remove(struct platform_device *pdev)
{
snd_soc_unregister_codec(&pdev->dev);
return 0;
diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c
index a95ff4f9f42..90c90b7f4a2 100644
--- a/sound/soc/codecs/ak4642.c
+++ b/sound/soc/codecs/ak4642.c
@@ -72,6 +72,12 @@
#define AK4642_CACHEREGNUM 0x25
+/* PW_MGMT1*/
+#define PMVCM (1 << 6) /* VCOM Power Management */
+#define PMMIN (1 << 5) /* MIN Input Power Management */
+#define PMDAC (1 << 2) /* DAC Power Management */
+#define PMADL (1 << 0) /* MIC Amp Lch and ADC Lch Power Management */
+
/* PW_MGMT2 */
#define HPMTN (1 << 6)
#define PMHPL (1 << 5)
@@ -83,6 +89,23 @@
#define PMHP_MASK (PMHPL | PMHPR)
#define PMHP PMHP_MASK
+/* PW_MGMT3 */
+#define PMADR (1 << 0) /* MIC L / ADC R Power Management */
+
+/* SG_SL1 */
+#define MINS (1 << 6) /* Switch from MIN to Speaker */
+#define DACL (1 << 4) /* Switch from DAC to Stereo or Receiver */
+#define PMMP (1 << 2) /* MPWR pin Power Management */
+#define MGAIN0 (1 << 0) /* MIC amp gain*/
+
+/* TIMER */
+#define ZTM(param) ((param & 0x3) << 4) /* ALC Zoro Crossing TimeOut */
+#define WTM(param) (((param & 0x4) << 4) | ((param & 0x3) << 2))
+
+/* ALC_CTL1 */
+#define ALC (1 << 5) /* ALC Enable */
+#define LMTH0 (1 << 0) /* ALC Limiter / Recovery Level */
+
/* MD_CTL1 */
#define PLL3 (1 << 7)
#define PLL2 (1 << 6)
@@ -100,6 +123,11 @@
#define FS3 (1 << 5)
#define FS_MASK (FS0 | FS1 | FS2 | FS3)
+/* MD_CTL3 */
+#define BST1 (1 << 3)
+
+/* MD_CTL4 */
+#define DACH (1 << 0)
/*
* Playback Volume (table 39)
@@ -216,11 +244,12 @@ static int ak4642_dai_startup(struct snd_pcm_substream *substream,
* This operation came from example code of
* "ASAHI KASEI AK4642" (japanese) manual p97.
*/
- ak4642_write(codec, 0x0f, 0x09);
- ak4642_write(codec, 0x0e, 0x19);
- ak4642_write(codec, 0x09, 0x91);
- ak4642_write(codec, 0x0c, 0x91);
- ak4642_write(codec, 0x00, 0x64);
+ snd_soc_update_bits(codec, MD_CTL4, DACH, DACH);
+ snd_soc_update_bits(codec, MD_CTL3, BST1, BST1);
+ ak4642_write(codec, L_IVC, 0x91); /* volume */
+ ak4642_write(codec, R_IVC, 0x91); /* volume */
+ snd_soc_update_bits(codec, PW_MGMT1, PMVCM | PMMIN | PMDAC,
+ PMVCM | PMMIN | PMDAC);
snd_soc_update_bits(codec, PW_MGMT2, PMHP_MASK, PMHP);
snd_soc_update_bits(codec, PW_MGMT2, HPMTN, HPMTN);
} else {
@@ -237,13 +266,12 @@ static int ak4642_dai_startup(struct snd_pcm_substream *substream,
* This operation came from example code of
* "ASAHI KASEI AK4642" (japanese) manual p94.
*/
- ak4642_write(codec, 0x02, 0x05);
- ak4642_write(codec, 0x06, 0x3c);
- ak4642_write(codec, 0x08, 0xe1);
- ak4642_write(codec, 0x0b, 0x00);
- ak4642_write(codec, 0x07, 0x21);
- ak4642_write(codec, 0x00, 0x41);
- ak4642_write(codec, 0x10, 0x01);
+ ak4642_write(codec, SG_SL1, PMMP | MGAIN0);
+ ak4642_write(codec, TIMER, ZTM(0x3) | WTM(0x3));
+ ak4642_write(codec, ALC_CTL1, ALC | LMTH0);
+ snd_soc_update_bits(codec, PW_MGMT1, PMVCM | PMADL,
+ PMVCM | PMADL);
+ snd_soc_update_bits(codec, PW_MGMT3, PMADR, PMADR);
}
return 0;
@@ -259,14 +287,14 @@ static void ak4642_dai_shutdown(struct snd_pcm_substream *substream,
/* stop headphone output */
snd_soc_update_bits(codec, PW_MGMT2, HPMTN, 0);
snd_soc_update_bits(codec, PW_MGMT2, PMHP_MASK, 0);
- ak4642_write(codec, 0x00, 0x40);
- ak4642_write(codec, 0x0e, 0x11);
- ak4642_write(codec, 0x0f, 0x08);
+ snd_soc_update_bits(codec, PW_MGMT1, PMMIN | PMDAC, 0);
+ snd_soc_update_bits(codec, MD_CTL3, BST1, 0);
+ snd_soc_update_bits(codec, MD_CTL4, DACH, 0);
} else {
/* stop stereo input */
- ak4642_write(codec, 0x00, 0x40);
- ak4642_write(codec, 0x10, 0x00);
- ak4642_write(codec, 0x07, 0x01);
+ snd_soc_update_bits(codec, PW_MGMT1, PMADL, 0);
+ snd_soc_update_bits(codec, PW_MGMT3, PMADR, 0);
+ snd_soc_update_bits(codec, ALC_CTL1, ALC, 0);
}
}
diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c
index 5fd8e0d0dab..6d4bdc609ac 100644
--- a/sound/soc/codecs/cs4270.c
+++ b/sound/soc/codecs/cs4270.c
@@ -518,7 +518,7 @@ static struct snd_soc_dai_ops cs4270_dai_ops = {
.digital_mute = cs4270_dai_mute,
};
-struct snd_soc_dai_driver cs4270_dai = {
+static struct snd_soc_dai_driver cs4270_dai = {
.name = "cs4270-hifi",
.playback = {
.stream_name = "Playback",
diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c
new file mode 100644
index 00000000000..bc22ee93a75
--- /dev/null
+++ b/sound/soc/codecs/max98088.c
@@ -0,0 +1,2097 @@
+/*
+ * max98088.c -- MAX98088 ALSA SoC Audio driver
+ *
+ * Copyright 2010 Maxim Integrated Products
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <linux/slab.h>
+#include <asm/div64.h>
+#include <sound/max98088.h>
+#include "max98088.h"
+
+struct max98088_cdata {
+ unsigned int rate;
+ unsigned int fmt;
+ int eq_sel;
+};
+
+struct max98088_priv {
+ u8 reg_cache[M98088_REG_CNT];
+ void *control_data;
+ struct max98088_pdata *pdata;
+ unsigned int sysclk;
+ struct max98088_cdata dai[2];
+ int eq_textcnt;
+ const char **eq_texts;
+ struct soc_enum eq_enum;
+ u8 ina_state;
+ u8 inb_state;
+ unsigned int ex_mode;
+ unsigned int digmic;
+ unsigned int mic1pre;
+ unsigned int mic2pre;
+ unsigned int extmic_mode;
+};
+
+static const u8 max98088_reg[M98088_REG_CNT] = {
+ 0x00, /* 00 IRQ status */
+ 0x00, /* 01 MIC status */
+ 0x00, /* 02 jack status */
+ 0x00, /* 03 battery voltage */
+ 0x00, /* 04 */
+ 0x00, /* 05 */
+ 0x00, /* 06 */
+ 0x00, /* 07 */
+ 0x00, /* 08 */
+ 0x00, /* 09 */
+ 0x00, /* 0A */
+ 0x00, /* 0B */
+ 0x00, /* 0C */
+ 0x00, /* 0D */
+ 0x00, /* 0E */
+ 0x00, /* 0F interrupt enable */
+
+ 0x00, /* 10 master clock */
+ 0x00, /* 11 DAI1 clock mode */
+ 0x00, /* 12 DAI1 clock control */
+ 0x00, /* 13 DAI1 clock control */
+ 0x00, /* 14 DAI1 format */
+ 0x00, /* 15 DAI1 clock */
+ 0x00, /* 16 DAI1 config */
+ 0x00, /* 17 DAI1 TDM */
+ 0x00, /* 18 DAI1 filters */
+ 0x00, /* 19 DAI2 clock mode */
+ 0x00, /* 1A DAI2 clock control */
+ 0x00, /* 1B DAI2 clock control */
+ 0x00, /* 1C DAI2 format */
+ 0x00, /* 1D DAI2 clock */
+ 0x00, /* 1E DAI2 config */
+ 0x00, /* 1F DAI2 TDM */
+
+ 0x00, /* 20 DAI2 filters */
+ 0x00, /* 21 data config */
+ 0x00, /* 22 DAC mixer */
+ 0x00, /* 23 left ADC mixer */
+ 0x00, /* 24 right ADC mixer */
+ 0x00, /* 25 left HP mixer */
+ 0x00, /* 26 right HP mixer */
+ 0x00, /* 27 HP control */
+ 0x00, /* 28 left REC mixer */
+ 0x00, /* 29 right REC mixer */
+ 0x00, /* 2A REC control */
+ 0x00, /* 2B left SPK mixer */
+ 0x00, /* 2C right SPK mixer */
+ 0x00, /* 2D SPK control */
+ 0x00, /* 2E sidetone */
+ 0x00, /* 2F DAI1 playback level */
+
+ 0x00, /* 30 DAI1 playback level */
+ 0x00, /* 31 DAI2 playback level */
+ 0x00, /* 32 DAI2 playbakc level */
+ 0x00, /* 33 left ADC level */
+ 0x00, /* 34 right ADC level */
+ 0x00, /* 35 MIC1 level */
+ 0x00, /* 36 MIC2 level */
+ 0x00, /* 37 INA level */
+ 0x00, /* 38 INB level */
+ 0x00, /* 39 left HP volume */
+ 0x00, /* 3A right HP volume */
+ 0x00, /* 3B left REC volume */
+ 0x00, /* 3C right REC volume */
+ 0x00, /* 3D left SPK volume */
+ 0x00, /* 3E right SPK volume */
+ 0x00, /* 3F MIC config */
+
+ 0x00, /* 40 MIC threshold */
+ 0x00, /* 41 excursion limiter filter */
+ 0x00, /* 42 excursion limiter threshold */
+ 0x00, /* 43 ALC */
+ 0x00, /* 44 power limiter threshold */
+ 0x00, /* 45 power limiter config */
+ 0x00, /* 46 distortion limiter config */
+ 0x00, /* 47 audio input */
+ 0x00, /* 48 microphone */
+ 0x00, /* 49 level control */
+ 0x00, /* 4A bypass switches */
+ 0x00, /* 4B jack detect */
+ 0x00, /* 4C input enable */
+ 0x00, /* 4D output enable */
+ 0xF0, /* 4E bias control */
+ 0x00, /* 4F DAC power */
+
+ 0x0F, /* 50 DAC power */
+ 0x00, /* 51 system */
+ 0x00, /* 52 DAI1 EQ1 */
+ 0x00, /* 53 DAI1 EQ1 */
+ 0x00, /* 54 DAI1 EQ1 */
+ 0x00, /* 55 DAI1 EQ1 */
+ 0x00, /* 56 DAI1 EQ1 */
+ 0x00, /* 57 DAI1 EQ1 */
+ 0x00, /* 58 DAI1 EQ1 */
+ 0x00, /* 59 DAI1 EQ1 */
+ 0x00, /* 5A DAI1 EQ1 */
+ 0x00, /* 5B DAI1 EQ1 */
+ 0x00, /* 5C DAI1 EQ2 */
+ 0x00, /* 5D DAI1 EQ2 */
+ 0x00, /* 5E DAI1 EQ2 */
+ 0x00, /* 5F DAI1 EQ2 */
+
+ 0x00, /* 60 DAI1 EQ2 */
+ 0x00, /* 61 DAI1 EQ2 */
+ 0x00, /* 62 DAI1 EQ2 */
+ 0x00, /* 63 DAI1 EQ2 */
+ 0x00, /* 64 DAI1 EQ2 */
+ 0x00, /* 65 DAI1 EQ2 */
+ 0x00, /* 66 DAI1 EQ3 */
+ 0x00, /* 67 DAI1 EQ3 */
+ 0x00, /* 68 DAI1 EQ3 */
+ 0x00, /* 69 DAI1 EQ3 */
+ 0x00, /* 6A DAI1 EQ3 */
+ 0x00, /* 6B DAI1 EQ3 */
+ 0x00, /* 6C DAI1 EQ3 */
+ 0x00, /* 6D DAI1 EQ3 */
+ 0x00, /* 6E DAI1 EQ3 */
+ 0x00, /* 6F DAI1 EQ3 */
+
+ 0x00, /* 70 DAI1 EQ4 */
+ 0x00, /* 71 DAI1 EQ4 */
+ 0x00, /* 72 DAI1 EQ4 */
+ 0x00, /* 73 DAI1 EQ4 */
+ 0x00, /* 74 DAI1 EQ4 */
+ 0x00, /* 75 DAI1 EQ4 */
+ 0x00, /* 76 DAI1 EQ4 */
+ 0x00, /* 77 DAI1 EQ4 */
+ 0x00, /* 78 DAI1 EQ4 */
+ 0x00, /* 79 DAI1 EQ4 */
+ 0x00, /* 7A DAI1 EQ5 */
+ 0x00, /* 7B DAI1 EQ5 */
+ 0x00, /* 7C DAI1 EQ5 */
+ 0x00, /* 7D DAI1 EQ5 */
+ 0x00, /* 7E DAI1 EQ5 */
+ 0x00, /* 7F DAI1 EQ5 */
+
+ 0x00, /* 80 DAI1 EQ5 */
+ 0x00, /* 81 DAI1 EQ5 */
+ 0x00, /* 82 DAI1 EQ5 */
+ 0x00, /* 83 DAI1 EQ5 */
+ 0x00, /* 84 DAI2 EQ1 */
+ 0x00, /* 85 DAI2 EQ1 */
+ 0x00, /* 86 DAI2 EQ1 */
+ 0x00, /* 87 DAI2 EQ1 */
+ 0x00, /* 88 DAI2 EQ1 */
+ 0x00, /* 89 DAI2 EQ1 */
+ 0x00, /* 8A DAI2 EQ1 */
+ 0x00, /* 8B DAI2 EQ1 */
+ 0x00, /* 8C DAI2 EQ1 */
+ 0x00, /* 8D DAI2 EQ1 */
+ 0x00, /* 8E DAI2 EQ2 */
+ 0x00, /* 8F DAI2 EQ2 */
+
+ 0x00, /* 90 DAI2 EQ2 */
+ 0x00, /* 91 DAI2 EQ2 */
+ 0x00, /* 92 DAI2 EQ2 */
+ 0x00, /* 93 DAI2 EQ2 */
+ 0x00, /* 94 DAI2 EQ2 */
+ 0x00, /* 95 DAI2 EQ2 */
+ 0x00, /* 96 DAI2 EQ2 */
+ 0x00, /* 97 DAI2 EQ2 */
+ 0x00, /* 98 DAI2 EQ3 */
+ 0x00, /* 99 DAI2 EQ3 */
+ 0x00, /* 9A DAI2 EQ3 */
+ 0x00, /* 9B DAI2 EQ3 */
+ 0x00, /* 9C DAI2 EQ3 */
+ 0x00, /* 9D DAI2 EQ3 */
+ 0x00, /* 9E DAI2 EQ3 */
+ 0x00, /* 9F DAI2 EQ3 */
+
+ 0x00, /* A0 DAI2 EQ3 */
+ 0x00, /* A1 DAI2 EQ3 */
+ 0x00, /* A2 DAI2 EQ4 */
+ 0x00, /* A3 DAI2 EQ4 */
+ 0x00, /* A4 DAI2 EQ4 */
+ 0x00, /* A5 DAI2 EQ4 */
+ 0x00, /* A6 DAI2 EQ4 */
+ 0x00, /* A7 DAI2 EQ4 */
+ 0x00, /* A8 DAI2 EQ4 */
+ 0x00, /* A9 DAI2 EQ4 */
+ 0x00, /* AA DAI2 EQ4 */
+ 0x00, /* AB DAI2 EQ4 */
+ 0x00, /* AC DAI2 EQ5 */
+ 0x00, /* AD DAI2 EQ5 */
+ 0x00, /* AE DAI2 EQ5 */
+ 0x00, /* AF DAI2 EQ5 */
+
+ 0x00, /* B0 DAI2 EQ5 */
+ 0x00, /* B1 DAI2 EQ5 */
+ 0x00, /* B2 DAI2 EQ5 */
+ 0x00, /* B3 DAI2 EQ5 */
+ 0x00, /* B4 DAI2 EQ5 */
+ 0x00, /* B5 DAI2 EQ5 */
+ 0x00, /* B6 DAI1 biquad */
+ 0x00, /* B7 DAI1 biquad */
+ 0x00, /* B8 DAI1 biquad */
+ 0x00, /* B9 DAI1 biquad */
+ 0x00, /* BA DAI1 biquad */
+ 0x00, /* BB DAI1 biquad */
+ 0x00, /* BC DAI1 biquad */
+ 0x00, /* BD DAI1 biquad */
+ 0x00, /* BE DAI1 biquad */
+ 0x00, /* BF DAI1 biquad */
+
+ 0x00, /* C0 DAI2 biquad */
+ 0x00, /* C1 DAI2 biquad */
+ 0x00, /* C2 DAI2 biquad */
+ 0x00, /* C3 DAI2 biquad */
+ 0x00, /* C4 DAI2 biquad */
+ 0x00, /* C5 DAI2 biquad */
+ 0x00, /* C6 DAI2 biquad */
+ 0x00, /* C7 DAI2 biquad */
+ 0x00, /* C8 DAI2 biquad */
+ 0x00, /* C9 DAI2 biquad */
+ 0x00, /* CA */
+ 0x00, /* CB */
+ 0x00, /* CC */
+ 0x00, /* CD */
+ 0x00, /* CE */
+ 0x00, /* CF */
+
+ 0x00, /* D0 */
+ 0x00, /* D1 */
+ 0x00, /* D2 */
+ 0x00, /* D3 */
+ 0x00, /* D4 */
+ 0x00, /* D5 */
+ 0x00, /* D6 */
+ 0x00, /* D7 */
+ 0x00, /* D8 */
+ 0x00, /* D9 */
+ 0x00, /* DA */
+ 0x70, /* DB */
+ 0x00, /* DC */
+ 0x00, /* DD */
+ 0x00, /* DE */
+ 0x00, /* DF */
+
+ 0x00, /* E0 */
+ 0x00, /* E1 */
+ 0x00, /* E2 */
+ 0x00, /* E3 */
+ 0x00, /* E4 */
+ 0x00, /* E5 */
+ 0x00, /* E6 */
+ 0x00, /* E7 */
+ 0x00, /* E8 */
+ 0x00, /* E9 */
+ 0x00, /* EA */
+ 0x00, /* EB */
+ 0x00, /* EC */
+ 0x00, /* ED */
+ 0x00, /* EE */
+ 0x00, /* EF */
+
+ 0x00, /* F0 */
+ 0x00, /* F1 */
+ 0x00, /* F2 */
+ 0x00, /* F3 */
+ 0x00, /* F4 */
+ 0x00, /* F5 */
+ 0x00, /* F6 */
+ 0x00, /* F7 */
+ 0x00, /* F8 */
+ 0x00, /* F9 */
+ 0x00, /* FA */
+ 0x00, /* FB */
+ 0x00, /* FC */
+ 0x00, /* FD */
+ 0x00, /* FE */
+ 0x00, /* FF */
+};
+
+static struct {
+ int readable;
+ int writable;
+ int vol;
+} max98088_access[M98088_REG_CNT] = {
+ { 0xFF, 0xFF, 1 }, /* 00 IRQ status */
+ { 0xFF, 0x00, 1 }, /* 01 MIC status */
+ { 0xFF, 0x00, 1 }, /* 02 jack status */
+ { 0x1F, 0x1F, 1 }, /* 03 battery voltage */
+ { 0xFF, 0xFF, 0 }, /* 04 */
+ { 0xFF, 0xFF, 0 }, /* 05 */
+ { 0xFF, 0xFF, 0 }, /* 06 */
+ { 0xFF, 0xFF, 0 }, /* 07 */
+ { 0xFF, 0xFF, 0 }, /* 08 */
+ { 0xFF, 0xFF, 0 }, /* 09 */
+ { 0xFF, 0xFF, 0 }, /* 0A */
+ { 0xFF, 0xFF, 0 }, /* 0B */
+ { 0xFF, 0xFF, 0 }, /* 0C */
+ { 0xFF, 0xFF, 0 }, /* 0D */
+ { 0xFF, 0xFF, 0 }, /* 0E */
+ { 0xFF, 0xFF, 0 }, /* 0F interrupt enable */
+
+ { 0xFF, 0xFF, 0 }, /* 10 master clock */
+ { 0xFF, 0xFF, 0 }, /* 11 DAI1 clock mode */
+ { 0xFF, 0xFF, 0 }, /* 12 DAI1 clock control */
+ { 0xFF, 0xFF, 0 }, /* 13 DAI1 clock control */
+ { 0xFF, 0xFF, 0 }, /* 14 DAI1 format */
+ { 0xFF, 0xFF, 0 }, /* 15 DAI1 clock */
+ { 0xFF, 0xFF, 0 }, /* 16 DAI1 config */
+ { 0xFF, 0xFF, 0 }, /* 17 DAI1 TDM */
+ { 0xFF, 0xFF, 0 }, /* 18 DAI1 filters */
+ { 0xFF, 0xFF, 0 }, /* 19 DAI2 clock mode */
+ { 0xFF, 0xFF, 0 }, /* 1A DAI2 clock control */
+ { 0xFF, 0xFF, 0 }, /* 1B DAI2 clock control */
+ { 0xFF, 0xFF, 0 }, /* 1C DAI2 format */
+ { 0xFF, 0xFF, 0 }, /* 1D DAI2 clock */
+ { 0xFF, 0xFF, 0 }, /* 1E DAI2 config */
+ { 0xFF, 0xFF, 0 }, /* 1F DAI2 TDM */
+
+ { 0xFF, 0xFF, 0 }, /* 20 DAI2 filters */
+ { 0xFF, 0xFF, 0 }, /* 21 data config */
+ { 0xFF, 0xFF, 0 }, /* 22 DAC mixer */
+ { 0xFF, 0xFF, 0 }, /* 23 left ADC mixer */
+ { 0xFF, 0xFF, 0 }, /* 24 right ADC mixer */
+ { 0xFF, 0xFF, 0 }, /* 25 left HP mixer */
+ { 0xFF, 0xFF, 0 }, /* 26 right HP mixer */
+ { 0xFF, 0xFF, 0 }, /* 27 HP control */
+ { 0xFF, 0xFF, 0 }, /* 28 left REC mixer */
+ { 0xFF, 0xFF, 0 }, /* 29 right REC mixer */
+ { 0xFF, 0xFF, 0 }, /* 2A REC control */
+ { 0xFF, 0xFF, 0 }, /* 2B left SPK mixer */
+ { 0xFF, 0xFF, 0 }, /* 2C right SPK mixer */
+ { 0xFF, 0xFF, 0 }, /* 2D SPK control */
+ { 0xFF, 0xFF, 0 }, /* 2E sidetone */
+ { 0xFF, 0xFF, 0 }, /* 2F DAI1 playback level */
+
+ { 0xFF, 0xFF, 0 }, /* 30 DAI1 playback level */
+ { 0xFF, 0xFF, 0 }, /* 31 DAI2 playback level */
+ { 0xFF, 0xFF, 0 }, /* 32 DAI2 playbakc level */
+ { 0xFF, 0xFF, 0 }, /* 33 left ADC level */
+ { 0xFF, 0xFF, 0 }, /* 34 right ADC level */
+ { 0xFF, 0xFF, 0 }, /* 35 MIC1 level */
+ { 0xFF, 0xFF, 0 }, /* 36 MIC2 level */
+ { 0xFF, 0xFF, 0 }, /* 37 INA level */
+ { 0xFF, 0xFF, 0 }, /* 38 INB level */
+ { 0xFF, 0xFF, 0 }, /* 39 left HP volume */
+ { 0xFF, 0xFF, 0 }, /* 3A right HP volume */
+ { 0xFF, 0xFF, 0 }, /* 3B left REC volume */
+ { 0xFF, 0xFF, 0 }, /* 3C right REC volume */
+ { 0xFF, 0xFF, 0 }, /* 3D left SPK volume */
+ { 0xFF, 0xFF, 0 }, /* 3E right SPK volume */
+ { 0xFF, 0xFF, 0 }, /* 3F MIC config */
+
+ { 0xFF, 0xFF, 0 }, /* 40 MIC threshold */
+ { 0xFF, 0xFF, 0 }, /* 41 excursion limiter filter */
+ { 0xFF, 0xFF, 0 }, /* 42 excursion limiter threshold */
+ { 0xFF, 0xFF, 0 }, /* 43 ALC */
+ { 0xFF, 0xFF, 0 }, /* 44 power limiter threshold */
+ { 0xFF, 0xFF, 0 }, /* 45 power limiter config */
+ { 0xFF, 0xFF, 0 }, /* 46 distortion limiter config */
+ { 0xFF, 0xFF, 0 }, /* 47 audio input */
+ { 0xFF, 0xFF, 0 }, /* 48 microphone */
+ { 0xFF, 0xFF, 0 }, /* 49 level control */
+ { 0xFF, 0xFF, 0 }, /* 4A bypass switches */
+ { 0xFF, 0xFF, 0 }, /* 4B jack detect */
+ { 0xFF, 0xFF, 0 }, /* 4C input enable */
+ { 0xFF, 0xFF, 0 }, /* 4D output enable */
+ { 0xFF, 0xFF, 0 }, /* 4E bias control */
+ { 0xFF, 0xFF, 0 }, /* 4F DAC power */
+
+ { 0xFF, 0xFF, 0 }, /* 50 DAC power */
+ { 0xFF, 0xFF, 0 }, /* 51 system */
+ { 0xFF, 0xFF, 0 }, /* 52 DAI1 EQ1 */
+ { 0xFF, 0xFF, 0 }, /* 53 DAI1 EQ1 */
+ { 0xFF, 0xFF, 0 }, /* 54 DAI1 EQ1 */
+ { 0xFF, 0xFF, 0 }, /* 55 DAI1 EQ1 */
+ { 0xFF, 0xFF, 0 }, /* 56 DAI1 EQ1 */
+ { 0xFF, 0xFF, 0 }, /* 57 DAI1 EQ1 */
+ { 0xFF, 0xFF, 0 }, /* 58 DAI1 EQ1 */
+ { 0xFF, 0xFF, 0 }, /* 59 DAI1 EQ1 */
+ { 0xFF, 0xFF, 0 }, /* 5A DAI1 EQ1 */
+ { 0xFF, 0xFF, 0 }, /* 5B DAI1 EQ1 */
+ { 0xFF, 0xFF, 0 }, /* 5C DAI1 EQ2 */
+ { 0xFF, 0xFF, 0 }, /* 5D DAI1 EQ2 */
+ { 0xFF, 0xFF, 0 }, /* 5E DAI1 EQ2 */
+ { 0xFF, 0xFF, 0 }, /* 5F DAI1 EQ2 */
+
+ { 0xFF, 0xFF, 0 }, /* 60 DAI1 EQ2 */
+ { 0xFF, 0xFF, 0 }, /* 61 DAI1 EQ2 */
+ { 0xFF, 0xFF, 0 }, /* 62 DAI1 EQ2 */
+ { 0xFF, 0xFF, 0 }, /* 63 DAI1 EQ2 */
+ { 0xFF, 0xFF, 0 }, /* 64 DAI1 EQ2 */
+ { 0xFF, 0xFF, 0 }, /* 65 DAI1 EQ2 */
+ { 0xFF, 0xFF, 0 }, /* 66 DAI1 EQ3 */
+ { 0xFF, 0xFF, 0 }, /* 67 DAI1 EQ3 */
+ { 0xFF, 0xFF, 0 }, /* 68 DAI1 EQ3 */
+ { 0xFF, 0xFF, 0 }, /* 69 DAI1 EQ3 */
+ { 0xFF, 0xFF, 0 }, /* 6A DAI1 EQ3 */
+ { 0xFF, 0xFF, 0 }, /* 6B DAI1 EQ3 */
+ { 0xFF, 0xFF, 0 }, /* 6C DAI1 EQ3 */
+ { 0xFF, 0xFF, 0 }, /* 6D DAI1 EQ3 */
+ { 0xFF, 0xFF, 0 }, /* 6E DAI1 EQ3 */
+ { 0xFF, 0xFF, 0 }, /* 6F DAI1 EQ3 */
+
+ { 0xFF, 0xFF, 0 }, /* 70 DAI1 EQ4 */
+ { 0xFF, 0xFF, 0 }, /* 71 DAI1 EQ4 */
+ { 0xFF, 0xFF, 0 }, /* 72 DAI1 EQ4 */
+ { 0xFF, 0xFF, 0 }, /* 73 DAI1 EQ4 */
+ { 0xFF, 0xFF, 0 }, /* 74 DAI1 EQ4 */
+ { 0xFF, 0xFF, 0 }, /* 75 DAI1 EQ4 */
+ { 0xFF, 0xFF, 0 }, /* 76 DAI1 EQ4 */
+ { 0xFF, 0xFF, 0 }, /* 77 DAI1 EQ4 */
+ { 0xFF, 0xFF, 0 }, /* 78 DAI1 EQ4 */
+ { 0xFF, 0xFF, 0 }, /* 79 DAI1 EQ4 */
+ { 0xFF, 0xFF, 0 }, /* 7A DAI1 EQ5 */
+ { 0xFF, 0xFF, 0 }, /* 7B DAI1 EQ5 */
+ { 0xFF, 0xFF, 0 }, /* 7C DAI1 EQ5 */
+ { 0xFF, 0xFF, 0 }, /* 7D DAI1 EQ5 */
+ { 0xFF, 0xFF, 0 }, /* 7E DAI1 EQ5 */
+ { 0xFF, 0xFF, 0 }, /* 7F DAI1 EQ5 */
+
+ { 0xFF, 0xFF, 0 }, /* 80 DAI1 EQ5 */
+ { 0xFF, 0xFF, 0 }, /* 81 DAI1 EQ5 */
+ { 0xFF, 0xFF, 0 }, /* 82 DAI1 EQ5 */
+ { 0xFF, 0xFF, 0 }, /* 83 DAI1 EQ5 */
+ { 0xFF, 0xFF, 0 }, /* 84 DAI2 EQ1 */
+ { 0xFF, 0xFF, 0 }, /* 85 DAI2 EQ1 */
+ { 0xFF, 0xFF, 0 }, /* 86 DAI2 EQ1 */
+ { 0xFF, 0xFF, 0 }, /* 87 DAI2 EQ1 */
+ { 0xFF, 0xFF, 0 }, /* 88 DAI2 EQ1 */
+ { 0xFF, 0xFF, 0 }, /* 89 DAI2 EQ1 */
+ { 0xFF, 0xFF, 0 }, /* 8A DAI2 EQ1 */
+ { 0xFF, 0xFF, 0 }, /* 8B DAI2 EQ1 */
+ { 0xFF, 0xFF, 0 }, /* 8C DAI2 EQ1 */
+ { 0xFF, 0xFF, 0 }, /* 8D DAI2 EQ1 */
+ { 0xFF, 0xFF, 0 }, /* 8E DAI2 EQ2 */
+ { 0xFF, 0xFF, 0 }, /* 8F DAI2 EQ2 */
+
+ { 0xFF, 0xFF, 0 }, /* 90 DAI2 EQ2 */
+ { 0xFF, 0xFF, 0 }, /* 91 DAI2 EQ2 */
+ { 0xFF, 0xFF, 0 }, /* 92 DAI2 EQ2 */
+ { 0xFF, 0xFF, 0 }, /* 93 DAI2 EQ2 */
+ { 0xFF, 0xFF, 0 }, /* 94 DAI2 EQ2 */
+ { 0xFF, 0xFF, 0 }, /* 95 DAI2 EQ2 */
+ { 0xFF, 0xFF, 0 }, /* 96 DAI2 EQ2 */
+ { 0xFF, 0xFF, 0 }, /* 97 DAI2 EQ2 */
+ { 0xFF, 0xFF, 0 }, /* 98 DAI2 EQ3 */
+ { 0xFF, 0xFF, 0 }, /* 99 DAI2 EQ3 */
+ { 0xFF, 0xFF, 0 }, /* 9A DAI2 EQ3 */
+ { 0xFF, 0xFF, 0 }, /* 9B DAI2 EQ3 */
+ { 0xFF, 0xFF, 0 }, /* 9C DAI2 EQ3 */
+ { 0xFF, 0xFF, 0 }, /* 9D DAI2 EQ3 */
+ { 0xFF, 0xFF, 0 }, /* 9E DAI2 EQ3 */
+ { 0xFF, 0xFF, 0 }, /* 9F DAI2 EQ3 */
+
+ { 0xFF, 0xFF, 0 }, /* A0 DAI2 EQ3 */
+ { 0xFF, 0xFF, 0 }, /* A1 DAI2 EQ3 */
+ { 0xFF, 0xFF, 0 }, /* A2 DAI2 EQ4 */
+ { 0xFF, 0xFF, 0 }, /* A3 DAI2 EQ4 */
+ { 0xFF, 0xFF, 0 }, /* A4 DAI2 EQ4 */
+ { 0xFF, 0xFF, 0 }, /* A5 DAI2 EQ4 */
+ { 0xFF, 0xFF, 0 }, /* A6 DAI2 EQ4 */
+ { 0xFF, 0xFF, 0 }, /* A7 DAI2 EQ4 */
+ { 0xFF, 0xFF, 0 }, /* A8 DAI2 EQ4 */
+ { 0xFF, 0xFF, 0 }, /* A9 DAI2 EQ4 */
+ { 0xFF, 0xFF, 0 }, /* AA DAI2 EQ4 */
+ { 0xFF, 0xFF, 0 }, /* AB DAI2 EQ4 */
+ { 0xFF, 0xFF, 0 }, /* AC DAI2 EQ5 */
+ { 0xFF, 0xFF, 0 }, /* AD DAI2 EQ5 */
+ { 0xFF, 0xFF, 0 }, /* AE DAI2 EQ5 */
+ { 0xFF, 0xFF, 0 }, /* AF DAI2 EQ5 */
+
+ { 0xFF, 0xFF, 0 }, /* B0 DAI2 EQ5 */
+ { 0xFF, 0xFF, 0 }, /* B1 DAI2 EQ5 */
+ { 0xFF, 0xFF, 0 }, /* B2 DAI2 EQ5 */
+ { 0xFF, 0xFF, 0 }, /* B3 DAI2 EQ5 */
+ { 0xFF, 0xFF, 0 }, /* B4 DAI2 EQ5 */
+ { 0xFF, 0xFF, 0 }, /* B5 DAI2 EQ5 */
+ { 0xFF, 0xFF, 0 }, /* B6 DAI1 biquad */
+ { 0xFF, 0xFF, 0 }, /* B7 DAI1 biquad */
+ { 0xFF, 0xFF, 0 }, /* B8 DAI1 biquad */
+ { 0xFF, 0xFF, 0 }, /* B9 DAI1 biquad */
+ { 0xFF, 0xFF, 0 }, /* BA DAI1 biquad */
+ { 0xFF, 0xFF, 0 }, /* BB DAI1 biquad */
+ { 0xFF, 0xFF, 0 }, /* BC DAI1 biquad */
+ { 0xFF, 0xFF, 0 }, /* BD DAI1 biquad */
+ { 0xFF, 0xFF, 0 }, /* BE DAI1 biquad */
+ { 0xFF, 0xFF, 0 }, /* BF DAI1 biquad */
+
+ { 0xFF, 0xFF, 0 }, /* C0 DAI2 biquad */
+ { 0xFF, 0xFF, 0 }, /* C1 DAI2 biquad */
+ { 0xFF, 0xFF, 0 }, /* C2 DAI2 biquad */
+ { 0xFF, 0xFF, 0 }, /* C3 DAI2 biquad */
+ { 0xFF, 0xFF, 0 }, /* C4 DAI2 biquad */
+ { 0xFF, 0xFF, 0 }, /* C5 DAI2 biquad */
+ { 0xFF, 0xFF, 0 }, /* C6 DAI2 biquad */
+ { 0xFF, 0xFF, 0 }, /* C7 DAI2 biquad */
+ { 0xFF, 0xFF, 0 }, /* C8 DAI2 biquad */
+ { 0xFF, 0xFF, 0 }, /* C9 DAI2 biquad */
+ { 0x00, 0x00, 0 }, /* CA */
+ { 0x00, 0x00, 0 }, /* CB */
+ { 0x00, 0x00, 0 }, /* CC */
+ { 0x00, 0x00, 0 }, /* CD */
+ { 0x00, 0x00, 0 }, /* CE */
+ { 0x00, 0x00, 0 }, /* CF */
+
+ { 0x00, 0x00, 0 }, /* D0 */
+ { 0x00, 0x00, 0 }, /* D1 */
+ { 0x00, 0x00, 0 }, /* D2 */
+ { 0x00, 0x00, 0 }, /* D3 */
+ { 0x00, 0x00, 0 }, /* D4 */
+ { 0x00, 0x00, 0 }, /* D5 */
+ { 0x00, 0x00, 0 }, /* D6 */
+ { 0x00, 0x00, 0 }, /* D7 */
+ { 0x00, 0x00, 0 }, /* D8 */
+ { 0x00, 0x00, 0 }, /* D9 */
+ { 0x00, 0x00, 0 }, /* DA */
+ { 0x00, 0x00, 0 }, /* DB */
+ { 0x00, 0x00, 0 }, /* DC */
+ { 0x00, 0x00, 0 }, /* DD */
+ { 0x00, 0x00, 0 }, /* DE */
+ { 0x00, 0x00, 0 }, /* DF */
+
+ { 0x00, 0x00, 0 }, /* E0 */
+ { 0x00, 0x00, 0 }, /* E1 */
+ { 0x00, 0x00, 0 }, /* E2 */
+ { 0x00, 0x00, 0 }, /* E3 */
+ { 0x00, 0x00, 0 }, /* E4 */
+ { 0x00, 0x00, 0 }, /* E5 */
+ { 0x00, 0x00, 0 }, /* E6 */
+ { 0x00, 0x00, 0 }, /* E7 */
+ { 0x00, 0x00, 0 }, /* E8 */
+ { 0x00, 0x00, 0 }, /* E9 */
+ { 0x00, 0x00, 0 }, /* EA */
+ { 0x00, 0x00, 0 }, /* EB */
+ { 0x00, 0x00, 0 }, /* EC */
+ { 0x00, 0x00, 0 }, /* ED */
+ { 0x00, 0x00, 0 }, /* EE */
+ { 0x00, 0x00, 0 }, /* EF */
+
+ { 0x00, 0x00, 0 }, /* F0 */
+ { 0x00, 0x00, 0 }, /* F1 */
+ { 0x00, 0x00, 0 }, /* F2 */
+ { 0x00, 0x00, 0 }, /* F3 */
+ { 0x00, 0x00, 0 }, /* F4 */
+ { 0x00, 0x00, 0 }, /* F5 */
+ { 0x00, 0x00, 0 }, /* F6 */
+ { 0x00, 0x00, 0 }, /* F7 */
+ { 0x00, 0x00, 0 }, /* F8 */
+ { 0x00, 0x00, 0 }, /* F9 */
+ { 0x00, 0x00, 0 }, /* FA */
+ { 0x00, 0x00, 0 }, /* FB */
+ { 0x00, 0x00, 0 }, /* FC */
+ { 0x00, 0x00, 0 }, /* FD */
+ { 0x00, 0x00, 0 }, /* FE */
+ { 0xFF, 0x00, 1 }, /* FF */
+};
+
+static int max98088_volatile_register(unsigned int reg)
+{
+ return max98088_access[reg].vol;
+}
+
+
+/*
+ * Load equalizer DSP coefficient configurations registers
+ */
+static void m98088_eq_band(struct snd_soc_codec *codec, unsigned int dai,
+ unsigned int band, u16 *coefs)
+{
+ unsigned int eq_reg;
+ unsigned int i;
+
+ BUG_ON(band > 4);
+ BUG_ON(dai > 1);
+
+ /* Load the base register address */
+ eq_reg = dai ? M98088_REG_84_DAI2_EQ_BASE : M98088_REG_52_DAI1_EQ_BASE;
+
+ /* Add the band address offset, note adjustment for word address */
+ eq_reg += band * (M98088_COEFS_PER_BAND << 1);
+
+ /* Step through the registers and coefs */
+ for (i = 0; i < M98088_COEFS_PER_BAND; i++) {
+ snd_soc_write(codec, eq_reg++, M98088_BYTE1(coefs[i]));
+ snd_soc_write(codec, eq_reg++, M98088_BYTE0(coefs[i]));
+ }
+}
+
+/*
+ * Excursion limiter modes
+ */
+static const char *max98088_exmode_texts[] = {
+ "Off", "100Hz", "400Hz", "600Hz", "800Hz", "1000Hz", "200-400Hz",
+ "400-600Hz", "400-800Hz",
+};
+
+static const unsigned int max98088_exmode_values[] = {
+ 0x00, 0x43, 0x10, 0x20, 0x30, 0x40, 0x11, 0x22, 0x32
+};
+
+static const struct soc_enum max98088_exmode_enum =
+ SOC_VALUE_ENUM_SINGLE(M98088_REG_41_SPKDHP, 0, 127,
+ ARRAY_SIZE(max98088_exmode_texts),
+ max98088_exmode_texts,
+ max98088_exmode_values);
+static const struct snd_kcontrol_new max98088_exmode_controls =
+ SOC_DAPM_VALUE_ENUM("Route", max98088_exmode_enum);
+
+static const char *max98088_ex_thresh[] = { /* volts PP */
+ "0.6", "1.2", "1.8", "2.4", "3.0", "3.6", "4.2", "4.8"};
+static const struct soc_enum max98088_ex_thresh_enum[] = {
+ SOC_ENUM_SINGLE(M98088_REG_42_SPKDHP_THRESH, 0, 8,
+ max98088_ex_thresh),
+};
+
+static const char *max98088_fltr_mode[] = {"Voice", "Music" };
+static const struct soc_enum max98088_filter_mode_enum[] = {
+ SOC_ENUM_SINGLE(M98088_REG_18_DAI1_FILTERS, 7, 2, max98088_fltr_mode),
+};
+
+static const char *max98088_extmic_text[] = { "None", "MIC1", "MIC2" };
+
+static const struct soc_enum max98088_extmic_enum =
+ SOC_ENUM_SINGLE(M98088_REG_48_CFG_MIC, 0, 3, max98088_extmic_text);
+
+static const struct snd_kcontrol_new max98088_extmic_mux =
+ SOC_DAPM_ENUM("External MIC Mux", max98088_extmic_enum);
+
+static const char *max98088_dai1_fltr[] = {
+ "Off", "fc=258/fs=16k", "fc=500/fs=16k",
+ "fc=258/fs=8k", "fc=500/fs=8k", "fc=200"};
+static const struct soc_enum max98088_dai1_dac_filter_enum[] = {
+ SOC_ENUM_SINGLE(M98088_REG_18_DAI1_FILTERS, 0, 6, max98088_dai1_fltr),
+};
+static const struct soc_enum max98088_dai1_adc_filter_enum[] = {
+ SOC_ENUM_SINGLE(M98088_REG_18_DAI1_FILTERS, 4, 6, max98088_dai1_fltr),
+};
+
+static int max98088_mic1pre_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ unsigned int sel = ucontrol->value.integer.value[0];
+
+ max98088->mic1pre = sel;
+ snd_soc_update_bits(codec, M98088_REG_35_LVL_MIC1, M98088_MICPRE_MASK,
+ (1+sel)<<M98088_MICPRE_SHIFT);
+
+ return 0;
+}
+
+static int max98088_mic1pre_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = max98088->mic1pre;
+ return 0;
+}
+
+static int max98088_mic2pre_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ unsigned int sel = ucontrol->value.integer.value[0];
+
+ max98088->mic2pre = sel;
+ snd_soc_update_bits(codec, M98088_REG_36_LVL_MIC2, M98088_MICPRE_MASK,
+ (1+sel)<<M98088_MICPRE_SHIFT);
+
+ return 0;
+}
+
+static int max98088_mic2pre_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = max98088->mic2pre;
+ return 0;
+}
+
+static const unsigned int max98088_micboost_tlv[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0, 1, TLV_DB_SCALE_ITEM(0, 2000, 0),
+ 2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0),
+};
+
+static const struct snd_kcontrol_new max98088_snd_controls[] = {
+
+ SOC_DOUBLE_R("Headphone Volume", M98088_REG_39_LVL_HP_L,
+ M98088_REG_3A_LVL_HP_R, 0, 31, 0),
+ SOC_DOUBLE_R("Speaker Volume", M98088_REG_3D_LVL_SPK_L,
+ M98088_REG_3E_LVL_SPK_R, 0, 31, 0),
+ SOC_DOUBLE_R("Receiver Volume", M98088_REG_3B_LVL_REC_L,
+ M98088_REG_3C_LVL_REC_R, 0, 31, 0),
+
+ SOC_DOUBLE_R("Headphone Switch", M98088_REG_39_LVL_HP_L,
+ M98088_REG_3A_LVL_HP_R, 7, 1, 1),
+ SOC_DOUBLE_R("Speaker Switch", M98088_REG_3D_LVL_SPK_L,
+ M98088_REG_3E_LVL_SPK_R, 7, 1, 1),
+ SOC_DOUBLE_R("Receiver Switch", M98088_REG_3B_LVL_REC_L,
+ M98088_REG_3C_LVL_REC_R, 7, 1, 1),
+
+ SOC_SINGLE("MIC1 Volume", M98088_REG_35_LVL_MIC1, 0, 31, 1),
+ SOC_SINGLE("MIC2 Volume", M98088_REG_36_LVL_MIC2, 0, 31, 1),
+
+ SOC_SINGLE_EXT_TLV("MIC1 Boost Volume",
+ M98088_REG_35_LVL_MIC1, 5, 2, 0,
+ max98088_mic1pre_get, max98088_mic1pre_set,
+ max98088_micboost_tlv),
+ SOC_SINGLE_EXT_TLV("MIC2 Boost Volume",
+ M98088_REG_36_LVL_MIC2, 5, 2, 0,
+ max98088_mic2pre_get, max98088_mic2pre_set,
+ max98088_micboost_tlv),
+
+ SOC_SINGLE("INA Volume", M98088_REG_37_LVL_INA, 0, 7, 1),
+ SOC_SINGLE("INB Volume", M98088_REG_38_LVL_INB, 0, 7, 1),
+
+ SOC_SINGLE("ADCL Volume", M98088_REG_33_LVL_ADC_L, 0, 15, 0),
+ SOC_SINGLE("ADCR Volume", M98088_REG_34_LVL_ADC_R, 0, 15, 0),
+
+ SOC_SINGLE("ADCL Boost Volume", M98088_REG_33_LVL_ADC_L, 4, 3, 0),
+ SOC_SINGLE("ADCR Boost Volume", M98088_REG_34_LVL_ADC_R, 4, 3, 0),
+
+ SOC_SINGLE("EQ1 Switch", M98088_REG_49_CFG_LEVEL, 0, 1, 0),
+ SOC_SINGLE("EQ2 Switch", M98088_REG_49_CFG_LEVEL, 1, 1, 0),
+
+ SOC_ENUM("EX Limiter Threshold", max98088_ex_thresh_enum),
+
+ SOC_ENUM("DAI1 Filter Mode", max98088_filter_mode_enum),
+ SOC_ENUM("DAI1 DAC Filter", max98088_dai1_dac_filter_enum),
+ SOC_ENUM("DAI1 ADC Filter", max98088_dai1_adc_filter_enum),
+ SOC_SINGLE("DAI2 DC Block Switch", M98088_REG_20_DAI2_FILTERS,
+ 0, 1, 0),
+
+ SOC_SINGLE("ALC Switch", M98088_REG_43_SPKALC_COMP, 7, 1, 0),
+ SOC_SINGLE("ALC Threshold", M98088_REG_43_SPKALC_COMP, 0, 7, 0),
+ SOC_SINGLE("ALC Multiband", M98088_REG_43_SPKALC_COMP, 3, 1, 0),
+ SOC_SINGLE("ALC Release Time", M98088_REG_43_SPKALC_COMP, 4, 7, 0),
+
+ SOC_SINGLE("PWR Limiter Threshold", M98088_REG_44_PWRLMT_CFG,
+ 4, 15, 0),
+ SOC_SINGLE("PWR Limiter Weight", M98088_REG_44_PWRLMT_CFG, 0, 7, 0),
+ SOC_SINGLE("PWR Limiter Time1", M98088_REG_45_PWRLMT_TIME, 0, 15, 0),
+ SOC_SINGLE("PWR Limiter Time2", M98088_REG_45_PWRLMT_TIME, 4, 15, 0),
+
+ SOC_SINGLE("THD Limiter Threshold", M98088_REG_46_THDLMT_CFG, 4, 15, 0),
+ SOC_SINGLE("THD Limiter Time", M98088_REG_46_THDLMT_CFG, 0, 7, 0),
+};
+
+/* Left speaker mixer switch */
+static const struct snd_kcontrol_new max98088_left_speaker_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 5, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 6, 1, 0),
+ SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 1, 1, 0),
+ SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 2, 1, 0),
+ SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 3, 1, 0),
+ SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 4, 1, 0),
+};
+
+/* Right speaker mixer switch */
+static const struct snd_kcontrol_new max98088_right_speaker_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 0, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 5, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 6, 1, 0),
+ SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 1, 1, 0),
+ SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 2, 1, 0),
+ SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 3, 1, 0),
+ SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 4, 1, 0),
+};
+
+/* Left headphone mixer switch */
+static const struct snd_kcontrol_new max98088_left_hp_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_25_MIX_HP_LEFT, 5, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_25_MIX_HP_LEFT, 6, 1, 0),
+ SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_25_MIX_HP_LEFT, 1, 1, 0),
+ SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_25_MIX_HP_LEFT, 2, 1, 0),
+ SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_25_MIX_HP_LEFT, 3, 1, 0),
+ SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_25_MIX_HP_LEFT, 4, 1, 0),
+};
+
+/* Right headphone mixer switch */
+static const struct snd_kcontrol_new max98088_right_hp_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_26_MIX_HP_RIGHT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_26_MIX_HP_RIGHT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_26_MIX_HP_RIGHT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_26_MIX_HP_RIGHT, 0, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_26_MIX_HP_RIGHT, 5, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_26_MIX_HP_RIGHT, 6, 1, 0),
+ SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_26_MIX_HP_RIGHT, 1, 1, 0),
+ SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_26_MIX_HP_RIGHT, 2, 1, 0),
+ SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_26_MIX_HP_RIGHT, 3, 1, 0),
+ SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_26_MIX_HP_RIGHT, 4, 1, 0),
+};
+
+/* Left earpiece/receiver mixer switch */
+static const struct snd_kcontrol_new max98088_left_rec_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_28_MIX_REC_LEFT, 5, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_28_MIX_REC_LEFT, 6, 1, 0),
+ SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_28_MIX_REC_LEFT, 1, 1, 0),
+ SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_28_MIX_REC_LEFT, 2, 1, 0),
+ SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_28_MIX_REC_LEFT, 3, 1, 0),
+ SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_28_MIX_REC_LEFT, 4, 1, 0),
+};
+
+/* Right earpiece/receiver mixer switch */
+static const struct snd_kcontrol_new max98088_right_rec_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_29_MIX_REC_RIGHT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_29_MIX_REC_RIGHT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_29_MIX_REC_RIGHT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_29_MIX_REC_RIGHT, 0, 1, 0),
+ SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_29_MIX_REC_RIGHT, 5, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_29_MIX_REC_RIGHT, 6, 1, 0),
+ SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_29_MIX_REC_RIGHT, 1, 1, 0),
+ SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_29_MIX_REC_RIGHT, 2, 1, 0),
+ SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_29_MIX_REC_RIGHT, 3, 1, 0),
+ SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_29_MIX_REC_RIGHT, 4, 1, 0),
+};
+
+/* Left ADC mixer switch */
+static const struct snd_kcontrol_new max98088_left_ADC_mixer_controls[] = {
+ SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_23_MIX_ADC_LEFT, 7, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_23_MIX_ADC_LEFT, 6, 1, 0),
+ SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_23_MIX_ADC_LEFT, 3, 1, 0),
+ SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_23_MIX_ADC_LEFT, 2, 1, 0),
+ SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_23_MIX_ADC_LEFT, 1, 1, 0),
+ SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_23_MIX_ADC_LEFT, 0, 1, 0),
+};
+
+/* Right ADC mixer switch */
+static const struct snd_kcontrol_new max98088_right_ADC_mixer_controls[] = {
+ SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_24_MIX_ADC_RIGHT, 7, 1, 0),
+ SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_24_MIX_ADC_RIGHT, 6, 1, 0),
+ SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_24_MIX_ADC_RIGHT, 3, 1, 0),
+ SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_24_MIX_ADC_RIGHT, 2, 1, 0),
+ SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_24_MIX_ADC_RIGHT, 1, 1, 0),
+ SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_24_MIX_ADC_RIGHT, 0, 1, 0),
+};
+
+static int max98088_mic_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (w->reg == M98088_REG_35_LVL_MIC1) {
+ snd_soc_update_bits(codec, w->reg, M98088_MICPRE_MASK,
+ (1+max98088->mic1pre)<<M98088_MICPRE_SHIFT);
+ } else {
+ snd_soc_update_bits(codec, w->reg, M98088_MICPRE_MASK,
+ (1+max98088->mic2pre)<<M98088_MICPRE_SHIFT);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec, w->reg, M98088_MICPRE_MASK, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * The line inputs are 2-channel stereo inputs with the left
+ * and right channels sharing a common PGA power control signal.
+ */
+static int max98088_line_pga(struct snd_soc_dapm_widget *w,
+ int event, int line, u8 channel)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ u8 *state;
+
+ BUG_ON(!((channel == 1) || (channel == 2)));
+
+ switch (line) {
+ case LINE_INA:
+ state = &max98088->ina_state;
+ break;
+ case LINE_INB:
+ state = &max98088->inb_state;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ *state |= channel;
+ snd_soc_update_bits(codec, w->reg,
+ (1 << w->shift), (1 << w->shift));
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ *state &= ~channel;
+ if (*state == 0) {
+ snd_soc_update_bits(codec, w->reg,
+ (1 << w->shift), 0);
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int max98088_pga_ina1_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ return max98088_line_pga(w, event, LINE_INA, 1);
+}
+
+static int max98088_pga_ina2_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ return max98088_line_pga(w, event, LINE_INA, 2);
+}
+
+static int max98088_pga_inb1_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ return max98088_line_pga(w, event, LINE_INB, 1);
+}
+
+static int max98088_pga_inb2_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ return max98088_line_pga(w, event, LINE_INB, 2);
+}
+
+static const struct snd_soc_dapm_widget max98088_dapm_widgets[] = {
+
+ SND_SOC_DAPM_ADC("ADCL", "HiFi Capture", M98088_REG_4C_PWR_EN_IN, 1, 0),
+ SND_SOC_DAPM_ADC("ADCR", "HiFi Capture", M98088_REG_4C_PWR_EN_IN, 0, 0),
+
+ SND_SOC_DAPM_DAC("DACL1", "HiFi Playback",
+ M98088_REG_4D_PWR_EN_OUT, 1, 0),
+ SND_SOC_DAPM_DAC("DACR1", "HiFi Playback",
+ M98088_REG_4D_PWR_EN_OUT, 0, 0),
+ SND_SOC_DAPM_DAC("DACL2", "Aux Playback",
+ M98088_REG_4D_PWR_EN_OUT, 1, 0),
+ SND_SOC_DAPM_DAC("DACR2", "Aux Playback",
+ M98088_REG_4D_PWR_EN_OUT, 0, 0),
+
+ SND_SOC_DAPM_PGA("HP Left Out", M98088_REG_4D_PWR_EN_OUT,
+ 7, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HP Right Out", M98088_REG_4D_PWR_EN_OUT,
+ 6, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("SPK Left Out", M98088_REG_4D_PWR_EN_OUT,
+ 5, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("SPK Right Out", M98088_REG_4D_PWR_EN_OUT,
+ 4, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("REC Left Out", M98088_REG_4D_PWR_EN_OUT,
+ 3, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("REC Right Out", M98088_REG_4D_PWR_EN_OUT,
+ 2, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("External MIC", SND_SOC_NOPM, 0, 0,
+ &max98088_extmic_mux),
+
+ SND_SOC_DAPM_MIXER("Left HP Mixer", SND_SOC_NOPM, 0, 0,
+ &max98088_left_hp_mixer_controls[0],
+ ARRAY_SIZE(max98088_left_hp_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Right HP Mixer", SND_SOC_NOPM, 0, 0,
+ &max98088_right_hp_mixer_controls[0],
+ ARRAY_SIZE(max98088_right_hp_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Left SPK Mixer", SND_SOC_NOPM, 0, 0,
+ &max98088_left_speaker_mixer_controls[0],
+ ARRAY_SIZE(max98088_left_speaker_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Right SPK Mixer", SND_SOC_NOPM, 0, 0,
+ &max98088_right_speaker_mixer_controls[0],
+ ARRAY_SIZE(max98088_right_speaker_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Left REC Mixer", SND_SOC_NOPM, 0, 0,
+ &max98088_left_rec_mixer_controls[0],
+ ARRAY_SIZE(max98088_left_rec_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Right REC Mixer", SND_SOC_NOPM, 0, 0,
+ &max98088_right_rec_mixer_controls[0],
+ ARRAY_SIZE(max98088_right_rec_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Left ADC Mixer", SND_SOC_NOPM, 0, 0,
+ &max98088_left_ADC_mixer_controls[0],
+ ARRAY_SIZE(max98088_left_ADC_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("Right ADC Mixer", SND_SOC_NOPM, 0, 0,
+ &max98088_right_ADC_mixer_controls[0],
+ ARRAY_SIZE(max98088_right_ADC_mixer_controls)),
+
+ SND_SOC_DAPM_PGA_E("MIC1 Input", M98088_REG_35_LVL_MIC1,
+ 5, 0, NULL, 0, max98088_mic_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_PGA_E("MIC2 Input", M98088_REG_36_LVL_MIC2,
+ 5, 0, NULL, 0, max98088_mic_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_PGA_E("INA1 Input", M98088_REG_4C_PWR_EN_IN,
+ 7, 0, NULL, 0, max98088_pga_ina1_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_PGA_E("INA2 Input", M98088_REG_4C_PWR_EN_IN,
+ 7, 0, NULL, 0, max98088_pga_ina2_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_PGA_E("INB1 Input", M98088_REG_4C_PWR_EN_IN,
+ 6, 0, NULL, 0, max98088_pga_inb1_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_PGA_E("INB2 Input", M98088_REG_4C_PWR_EN_IN,
+ 6, 0, NULL, 0, max98088_pga_inb2_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MICBIAS("MICBIAS", M98088_REG_4C_PWR_EN_IN, 3, 0),
+
+ SND_SOC_DAPM_MUX("EX Limiter Mode", SND_SOC_NOPM, 0, 0,
+ &max98088_exmode_controls),
+
+ SND_SOC_DAPM_OUTPUT("HPL"),
+ SND_SOC_DAPM_OUTPUT("HPR"),
+ SND_SOC_DAPM_OUTPUT("SPKL"),
+ SND_SOC_DAPM_OUTPUT("SPKR"),
+ SND_SOC_DAPM_OUTPUT("RECL"),
+ SND_SOC_DAPM_OUTPUT("RECR"),
+
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("INA1"),
+ SND_SOC_DAPM_INPUT("INA2"),
+ SND_SOC_DAPM_INPUT("INB1"),
+ SND_SOC_DAPM_INPUT("INB2"),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ /* Left headphone output mixer */
+ {"Left HP Mixer", "Left DAC1 Switch", "DACL1"},
+ {"Left HP Mixer", "Left DAC2 Switch", "DACL2"},
+ {"Left HP Mixer", "Right DAC1 Switch", "DACR1"},
+ {"Left HP Mixer", "Right DAC2 Switch", "DACR2"},
+ {"Left HP Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Left HP Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Left HP Mixer", "INA1 Switch", "INA1 Input"},
+ {"Left HP Mixer", "INA2 Switch", "INA2 Input"},
+ {"Left HP Mixer", "INB1 Switch", "INB1 Input"},
+ {"Left HP Mixer", "INB2 Switch", "INB2 Input"},
+
+ /* Right headphone output mixer */
+ {"Right HP Mixer", "Left DAC1 Switch", "DACL1"},
+ {"Right HP Mixer", "Left DAC2 Switch", "DACL2" },
+ {"Right HP Mixer", "Right DAC1 Switch", "DACR1"},
+ {"Right HP Mixer", "Right DAC2 Switch", "DACR2"},
+ {"Right HP Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Right HP Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Right HP Mixer", "INA1 Switch", "INA1 Input"},
+ {"Right HP Mixer", "INA2 Switch", "INA2 Input"},
+ {"Right HP Mixer", "INB1 Switch", "INB1 Input"},
+ {"Right HP Mixer", "INB2 Switch", "INB2 Input"},
+
+ /* Left speaker output mixer */
+ {"Left SPK Mixer", "Left DAC1 Switch", "DACL1"},
+ {"Left SPK Mixer", "Left DAC2 Switch", "DACL2"},
+ {"Left SPK Mixer", "Right DAC1 Switch", "DACR1"},
+ {"Left SPK Mixer", "Right DAC2 Switch", "DACR2"},
+ {"Left SPK Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Left SPK Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Left SPK Mixer", "INA1 Switch", "INA1 Input"},
+ {"Left SPK Mixer", "INA2 Switch", "INA2 Input"},
+ {"Left SPK Mixer", "INB1 Switch", "INB1 Input"},
+ {"Left SPK Mixer", "INB2 Switch", "INB2 Input"},
+
+ /* Right speaker output mixer */
+ {"Right SPK Mixer", "Left DAC1 Switch", "DACL1"},
+ {"Right SPK Mixer", "Left DAC2 Switch", "DACL2"},
+ {"Right SPK Mixer", "Right DAC1 Switch", "DACR1"},
+ {"Right SPK Mixer", "Right DAC2 Switch", "DACR2"},
+ {"Right SPK Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Right SPK Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Right SPK Mixer", "INA1 Switch", "INA1 Input"},
+ {"Right SPK Mixer", "INA2 Switch", "INA2 Input"},
+ {"Right SPK Mixer", "INB1 Switch", "INB1 Input"},
+ {"Right SPK Mixer", "INB2 Switch", "INB2 Input"},
+
+ /* Earpiece/Receiver output mixer */
+ {"Left REC Mixer", "Left DAC1 Switch", "DACL1"},
+ {"Left REC Mixer", "Left DAC2 Switch", "DACL2"},
+ {"Left REC Mixer", "Right DAC1 Switch", "DACR1"},
+ {"Left REC Mixer", "Right DAC2 Switch", "DACR2"},
+ {"Left REC Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Left REC Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Left REC Mixer", "INA1 Switch", "INA1 Input"},
+ {"Left REC Mixer", "INA2 Switch", "INA2 Input"},
+ {"Left REC Mixer", "INB1 Switch", "INB1 Input"},
+ {"Left REC Mixer", "INB2 Switch", "INB2 Input"},
+
+ /* Earpiece/Receiver output mixer */
+ {"Right REC Mixer", "Left DAC1 Switch", "DACL1"},
+ {"Right REC Mixer", "Left DAC2 Switch", "DACL2"},
+ {"Right REC Mixer", "Right DAC1 Switch", "DACR1"},
+ {"Right REC Mixer", "Right DAC2 Switch", "DACR2"},
+ {"Right REC Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Right REC Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Right REC Mixer", "INA1 Switch", "INA1 Input"},
+ {"Right REC Mixer", "INA2 Switch", "INA2 Input"},
+ {"Right REC Mixer", "INB1 Switch", "INB1 Input"},
+ {"Right REC Mixer", "INB2 Switch", "INB2 Input"},
+
+ {"HP Left Out", NULL, "Left HP Mixer"},
+ {"HP Right Out", NULL, "Right HP Mixer"},
+ {"SPK Left Out", NULL, "Left SPK Mixer"},
+ {"SPK Right Out", NULL, "Right SPK Mixer"},
+ {"REC Left Out", NULL, "Left REC Mixer"},
+ {"REC Right Out", NULL, "Right REC Mixer"},
+
+ {"HPL", NULL, "HP Left Out"},
+ {"HPR", NULL, "HP Right Out"},
+ {"SPKL", NULL, "SPK Left Out"},
+ {"SPKR", NULL, "SPK Right Out"},
+ {"RECL", NULL, "REC Left Out"},
+ {"RECR", NULL, "REC Right Out"},
+
+ /* Left ADC input mixer */
+ {"Left ADC Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Left ADC Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Left ADC Mixer", "INA1 Switch", "INA1 Input"},
+ {"Left ADC Mixer", "INA2 Switch", "INA2 Input"},
+ {"Left ADC Mixer", "INB1 Switch", "INB1 Input"},
+ {"Left ADC Mixer", "INB2 Switch", "INB2 Input"},
+
+ /* Right ADC input mixer */
+ {"Right ADC Mixer", "MIC1 Switch", "MIC1 Input"},
+ {"Right ADC Mixer", "MIC2 Switch", "MIC2 Input"},
+ {"Right ADC Mixer", "INA1 Switch", "INA1 Input"},
+ {"Right ADC Mixer", "INA2 Switch", "INA2 Input"},
+ {"Right ADC Mixer", "INB1 Switch", "INB1 Input"},
+ {"Right ADC Mixer", "INB2 Switch", "INB2 Input"},
+
+ /* Inputs */
+ {"ADCL", NULL, "Left ADC Mixer"},
+ {"ADCR", NULL, "Right ADC Mixer"},
+ {"INA1 Input", NULL, "INA1"},
+ {"INA2 Input", NULL, "INA2"},
+ {"INB1 Input", NULL, "INB1"},
+ {"INB2 Input", NULL, "INB2"},
+ {"MIC1 Input", NULL, "MIC1"},
+ {"MIC2 Input", NULL, "MIC2"},
+};
+
+static int max98088_add_widgets(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_new_controls(codec, max98088_dapm_widgets,
+ ARRAY_SIZE(max98088_dapm_widgets));
+
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+ snd_soc_add_controls(codec, max98088_snd_controls,
+ ARRAY_SIZE(max98088_snd_controls));
+
+ snd_soc_dapm_new_widgets(codec);
+ return 0;
+}
+
+/* codec mclk clock divider coefficients */
+static const struct {
+ u32 rate;
+ u8 sr;
+} rate_table[] = {
+ {8000, 0x10},
+ {11025, 0x20},
+ {16000, 0x30},
+ {22050, 0x40},
+ {24000, 0x50},
+ {32000, 0x60},
+ {44100, 0x70},
+ {48000, 0x80},
+ {88200, 0x90},
+ {96000, 0xA0},
+};
+
+static inline int rate_value(int rate, u8 *value)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(rate_table); i++) {
+ if (rate_table[i].rate >= rate) {
+ *value = rate_table[i].sr;
+ return 0;
+ }
+ }
+ *value = rate_table[0].sr;
+ return -EINVAL;
+}
+
+static int max98088_dai1_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct max98088_cdata *cdata;
+ unsigned long long ni;
+ unsigned int rate;
+ u8 regval;
+
+ cdata = &max98088->dai[0];
+
+ rate = params_rate(params);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+ M98088_DAI_WS, 0);
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+ M98088_DAI_WS, M98088_DAI_WS);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN, 0);
+
+ if (rate_value(rate, &regval))
+ return -EINVAL;
+
+ snd_soc_update_bits(codec, M98088_REG_11_DAI1_CLKMODE,
+ M98088_CLKMODE_MASK, regval);
+ cdata->rate = rate;
+
+ /* Configure NI when operating as master */
+ if (snd_soc_read(codec, M98088_REG_14_DAI1_FORMAT)
+ & M98088_DAI_MAS) {
+ if (max98088->sysclk == 0) {
+ dev_err(codec->dev, "Invalid system clock frequency\n");
+ return -EINVAL;
+ }
+ ni = 65536ULL * (rate < 50000 ? 96ULL : 48ULL)
+ * (unsigned long long int)rate;
+ do_div(ni, (unsigned long long int)max98088->sysclk);
+ snd_soc_write(codec, M98088_REG_12_DAI1_CLKCFG_HI,
+ (ni >> 8) & 0x7F);
+ snd_soc_write(codec, M98088_REG_13_DAI1_CLKCFG_LO,
+ ni & 0xFF);
+ }
+
+ /* Update sample rate mode */
+ if (rate < 50000)
+ snd_soc_update_bits(codec, M98088_REG_18_DAI1_FILTERS,
+ M98088_DAI_DHF, 0);
+ else
+ snd_soc_update_bits(codec, M98088_REG_18_DAI1_FILTERS,
+ M98088_DAI_DHF, M98088_DAI_DHF);
+
+ snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN,
+ M98088_SHDNRUN);
+
+ return 0;
+}
+
+static int max98088_dai2_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct max98088_cdata *cdata;
+ unsigned long long ni;
+ unsigned int rate;
+ u8 regval;
+
+ cdata = &max98088->dai[1];
+
+ rate = params_rate(params);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+ M98088_DAI_WS, 0);
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+ M98088_DAI_WS, M98088_DAI_WS);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN, 0);
+
+ if (rate_value(rate, &regval))
+ return -EINVAL;
+
+ snd_soc_update_bits(codec, M98088_REG_19_DAI2_CLKMODE,
+ M98088_CLKMODE_MASK, regval);
+ cdata->rate = rate;
+
+ /* Configure NI when operating as master */
+ if (snd_soc_read(codec, M98088_REG_1C_DAI2_FORMAT)
+ & M98088_DAI_MAS) {
+ if (max98088->sysclk == 0) {
+ dev_err(codec->dev, "Invalid system clock frequency\n");
+ return -EINVAL;
+ }
+ ni = 65536ULL * (rate < 50000 ? 96ULL : 48ULL)
+ * (unsigned long long int)rate;
+ do_div(ni, (unsigned long long int)max98088->sysclk);
+ snd_soc_write(codec, M98088_REG_1A_DAI2_CLKCFG_HI,
+ (ni >> 8) & 0x7F);
+ snd_soc_write(codec, M98088_REG_1B_DAI2_CLKCFG_LO,
+ ni & 0xFF);
+ }
+
+ /* Update sample rate mode */
+ if (rate < 50000)
+ snd_soc_update_bits(codec, M98088_REG_20_DAI2_FILTERS,
+ M98088_DAI_DHF, 0);
+ else
+ snd_soc_update_bits(codec, M98088_REG_20_DAI2_FILTERS,
+ M98088_DAI_DHF, M98088_DAI_DHF);
+
+ snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN,
+ M98088_SHDNRUN);
+
+ return 0;
+}
+
+static int max98088_dai_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+
+ /* Requested clock frequency is already setup */
+ if (freq == max98088->sysclk)
+ return 0;
+
+ max98088->sysclk = freq; /* remember current sysclk */
+
+ /* Setup clocks for slave mode, and using the PLL
+ * PSCLK = 0x01 (when master clk is 10MHz to 20MHz)
+ * 0x02 (when master clk is 20MHz to 30MHz)..
+ */
+ if ((freq >= 10000000) && (freq < 20000000)) {
+ snd_soc_write(codec, M98088_REG_10_SYS_CLK, 0x10);
+ } else if ((freq >= 20000000) && (freq < 30000000)) {
+ snd_soc_write(codec, M98088_REG_10_SYS_CLK, 0x20);
+ } else {
+ dev_err(codec->dev, "Invalid master clock frequency\n");
+ return -EINVAL;
+ }
+
+ if (snd_soc_read(codec, M98088_REG_51_PWR_SYS) & M98088_SHDNRUN) {
+ snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS,
+ M98088_SHDNRUN, 0);
+ snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS,
+ M98088_SHDNRUN, M98088_SHDNRUN);
+ }
+
+ dev_dbg(dai->dev, "Clock source is %d at %uHz\n", clk_id, freq);
+
+ max98088->sysclk = freq;
+ return 0;
+}
+
+static int max98088_dai1_set_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct max98088_cdata *cdata;
+ u8 reg15val;
+ u8 reg14val = 0;
+
+ cdata = &max98088->dai[0];
+
+ if (fmt != cdata->fmt) {
+ cdata->fmt = fmt;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ /* Slave mode PLL */
+ snd_soc_write(codec, M98088_REG_12_DAI1_CLKCFG_HI,
+ 0x80);
+ snd_soc_write(codec, M98088_REG_13_DAI1_CLKCFG_LO,
+ 0x00);
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ /* Set to master mode */
+ reg14val |= M98088_DAI_MAS;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ case SND_SOC_DAIFMT_CBM_CFS:
+ default:
+ dev_err(codec->dev, "Clock mode unsupported");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ reg14val |= M98088_DAI_DLY;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ reg14val |= M98088_DAI_WCI;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ reg14val |= M98088_DAI_BCI;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ reg14val |= M98088_DAI_BCI|M98088_DAI_WCI;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+ M98088_DAI_MAS | M98088_DAI_DLY | M98088_DAI_BCI |
+ M98088_DAI_WCI, reg14val);
+
+ reg15val = M98088_DAI_BSEL64;
+ if (max98088->digmic)
+ reg15val |= M98088_DAI_OSR64;
+ snd_soc_write(codec, M98088_REG_15_DAI1_CLOCK, reg15val);
+ }
+
+ return 0;
+}
+
+static int max98088_dai2_set_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct max98088_cdata *cdata;
+ u8 reg1Cval = 0;
+
+ cdata = &max98088->dai[1];
+
+ if (fmt != cdata->fmt) {
+ cdata->fmt = fmt;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ /* Slave mode PLL */
+ snd_soc_write(codec, M98088_REG_1A_DAI2_CLKCFG_HI,
+ 0x80);
+ snd_soc_write(codec, M98088_REG_1B_DAI2_CLKCFG_LO,
+ 0x00);
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ /* Set to master mode */
+ reg1Cval |= M98088_DAI_MAS;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ case SND_SOC_DAIFMT_CBM_CFS:
+ default:
+ dev_err(codec->dev, "Clock mode unsupported");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ reg1Cval |= M98088_DAI_DLY;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ reg1Cval |= M98088_DAI_WCI;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ reg1Cval |= M98088_DAI_BCI;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ reg1Cval |= M98088_DAI_BCI|M98088_DAI_WCI;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+ M98088_DAI_MAS | M98088_DAI_DLY | M98088_DAI_BCI |
+ M98088_DAI_WCI, reg1Cval);
+
+ snd_soc_write(codec, M98088_REG_1D_DAI2_CLOCK,
+ M98088_DAI_BSEL64);
+ }
+
+ return 0;
+}
+
+static void max98088_sync_cache(struct snd_soc_codec *codec)
+{
+ struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ int i;
+
+ if (!codec->cache_sync)
+ return;
+
+ codec->cache_only = 0;
+
+ /* write back cached values if they're writeable and
+ * different from the hardware default.
+ */
+ for (i = 1; i < ARRAY_SIZE(max98088->reg_cache); i++) {
+ if (!max98088_access[i].writable)
+ continue;
+
+ if (max98088->reg_cache[i] == max98088_reg[i])
+ continue;
+
+ snd_soc_write(codec, i, max98088->reg_cache[i]);
+ }
+
+ codec->cache_sync = 0;
+}
+
+static int max98088_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->bias_level == SND_SOC_BIAS_OFF)
+ max98088_sync_cache(codec);
+
+ snd_soc_update_bits(codec, M98088_REG_4C_PWR_EN_IN,
+ M98088_MBEN, M98088_MBEN);
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ snd_soc_update_bits(codec, M98088_REG_4C_PWR_EN_IN,
+ M98088_MBEN, 0);
+ codec->cache_sync = 1;
+ break;
+ }
+ codec->bias_level = level;
+ return 0;
+}
+
+#define MAX98088_RATES SNDRV_PCM_RATE_8000_96000
+#define MAX98088_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops max98088_dai1_ops = {
+ .set_sysclk = max98088_dai_set_sysclk,
+ .set_fmt = max98088_dai1_set_fmt,
+ .hw_params = max98088_dai1_hw_params,
+};
+
+static struct snd_soc_dai_ops max98088_dai2_ops = {
+ .set_sysclk = max98088_dai_set_sysclk,
+ .set_fmt = max98088_dai2_set_fmt,
+ .hw_params = max98088_dai2_hw_params,
+};
+
+static struct snd_soc_dai_driver max98088_dai[] = {
+{
+ .name = "HiFi",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98088_RATES,
+ .formats = MAX98088_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98088_RATES,
+ .formats = MAX98088_FORMATS,
+ },
+ .ops = &max98088_dai1_ops,
+},
+{
+ .name = "Aux",
+ .playback = {
+ .stream_name = "Aux Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98088_RATES,
+ .formats = MAX98088_FORMATS,
+ },
+ .ops = &max98088_dai2_ops,
+}
+};
+
+static int max98088_get_channel(const char *name)
+{
+ if (strcmp(name, "EQ1 Mode") == 0)
+ return 0;
+ if (strcmp(name, "EQ2 Mode") == 0)
+ return 1;
+ return -EINVAL;
+}
+
+static void max98088_setup_eq1(struct snd_soc_codec *codec)
+{
+ struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct max98088_pdata *pdata = max98088->pdata;
+ struct max98088_eq_cfg *coef_set;
+ int best, best_val, save, i, sel, fs;
+ struct max98088_cdata *cdata;
+
+ cdata = &max98088->dai[0];
+
+ if (!pdata || !max98088->eq_textcnt)
+ return;
+
+ /* Find the selected configuration with nearest sample rate */
+ fs = cdata->rate;
+ sel = cdata->eq_sel;
+
+ best = 0;
+ best_val = INT_MAX;
+ for (i = 0; i < pdata->eq_cfgcnt; i++) {
+ if (strcmp(pdata->eq_cfg[i].name, max98088->eq_texts[sel]) == 0 &&
+ abs(pdata->eq_cfg[i].rate - fs) < best_val) {
+ best = i;
+ best_val = abs(pdata->eq_cfg[i].rate - fs);
+ }
+ }
+
+ dev_dbg(codec->dev, "Selected %s/%dHz for %dHz sample rate\n",
+ pdata->eq_cfg[best].name,
+ pdata->eq_cfg[best].rate, fs);
+
+ /* Disable EQ while configuring, and save current on/off state */
+ save = snd_soc_read(codec, M98088_REG_49_CFG_LEVEL);
+ snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ1EN, 0);
+
+ coef_set = &pdata->eq_cfg[sel];
+
+ m98088_eq_band(codec, 0, 0, coef_set->band1);
+ m98088_eq_band(codec, 0, 1, coef_set->band2);
+ m98088_eq_band(codec, 0, 2, coef_set->band3);
+ m98088_eq_band(codec, 0, 3, coef_set->band4);
+ m98088_eq_band(codec, 0, 4, coef_set->band5);
+
+ /* Restore the original on/off state */
+ snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ1EN, save);
+}
+
+static void max98088_setup_eq2(struct snd_soc_codec *codec)
+{
+ struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct max98088_pdata *pdata = max98088->pdata;
+ struct max98088_eq_cfg *coef_set;
+ int best, best_val, save, i, sel, fs;
+ struct max98088_cdata *cdata;
+
+ cdata = &max98088->dai[1];
+
+ if (!pdata || !max98088->eq_textcnt)
+ return;
+
+ /* Find the selected configuration with nearest sample rate */
+ fs = cdata->rate;
+
+ sel = cdata->eq_sel;
+ best = 0;
+ best_val = INT_MAX;
+ for (i = 0; i < pdata->eq_cfgcnt; i++) {
+ if (strcmp(pdata->eq_cfg[i].name, max98088->eq_texts[sel]) == 0 &&
+ abs(pdata->eq_cfg[i].rate - fs) < best_val) {
+ best = i;
+ best_val = abs(pdata->eq_cfg[i].rate - fs);
+ }
+ }
+
+ dev_dbg(codec->dev, "Selected %s/%dHz for %dHz sample rate\n",
+ pdata->eq_cfg[best].name,
+ pdata->eq_cfg[best].rate, fs);
+
+ /* Disable EQ while configuring, and save current on/off state */
+ save = snd_soc_read(codec, M98088_REG_49_CFG_LEVEL);
+ snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ2EN, 0);
+
+ coef_set = &pdata->eq_cfg[sel];
+
+ m98088_eq_band(codec, 1, 0, coef_set->band1);
+ m98088_eq_band(codec, 1, 1, coef_set->band2);
+ m98088_eq_band(codec, 1, 2, coef_set->band3);
+ m98088_eq_band(codec, 1, 3, coef_set->band4);
+ m98088_eq_band(codec, 1, 4, coef_set->band5);
+
+ /* Restore the original on/off state */
+ snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ2EN,
+ save);
+}
+
+static int max98088_put_eq_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct max98088_pdata *pdata = max98088->pdata;
+ int channel = max98088_get_channel(kcontrol->id.name);
+ struct max98088_cdata *cdata;
+ int sel = ucontrol->value.integer.value[0];
+
+ cdata = &max98088->dai[channel];
+
+ if (sel >= pdata->eq_cfgcnt)
+ return -EINVAL;
+
+ cdata->eq_sel = sel;
+
+ switch (channel) {
+ case 0:
+ max98088_setup_eq1(codec);
+ break;
+ case 1:
+ max98088_setup_eq2(codec);
+ break;
+ }
+
+ return 0;
+}
+
+static int max98088_get_eq_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ int channel = max98088_get_channel(kcontrol->id.name);
+ struct max98088_cdata *cdata;
+
+ cdata = &max98088->dai[channel];
+ ucontrol->value.enumerated.item[0] = cdata->eq_sel;
+ return 0;
+}
+
+static void max98088_handle_eq_pdata(struct snd_soc_codec *codec)
+{
+ struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct max98088_pdata *pdata = max98088->pdata;
+ struct max98088_eq_cfg *cfg;
+ unsigned int cfgcnt;
+ int i, j;
+ const char **t;
+ int ret;
+
+ struct snd_kcontrol_new controls[] = {
+ SOC_ENUM_EXT("EQ1 Mode",
+ max98088->eq_enum,
+ max98088_get_eq_enum,
+ max98088_put_eq_enum),
+ SOC_ENUM_EXT("EQ2 Mode",
+ max98088->eq_enum,
+ max98088_get_eq_enum,
+ max98088_put_eq_enum),
+ };
+
+ cfg = pdata->eq_cfg;
+ cfgcnt = pdata->eq_cfgcnt;
+
+ /* Setup an array of texts for the equalizer enum.
+ * This is based on Mark Brown's equalizer driver code.
+ */
+ max98088->eq_textcnt = 0;
+ max98088->eq_texts = NULL;
+ for (i = 0; i < cfgcnt; i++) {
+ for (j = 0; j < max98088->eq_textcnt; j++) {
+ if (strcmp(cfg[i].name, max98088->eq_texts[j]) == 0)
+ break;
+ }
+
+ if (j != max98088->eq_textcnt)
+ continue;
+
+ /* Expand the array */
+ t = krealloc(max98088->eq_texts,
+ sizeof(char *) * (max98088->eq_textcnt + 1),
+ GFP_KERNEL);
+ if (t == NULL)
+ continue;
+
+ /* Store the new entry */
+ t[max98088->eq_textcnt] = cfg[i].name;
+ max98088->eq_textcnt++;
+ max98088->eq_texts = t;
+ }
+
+ /* Now point the soc_enum to .texts array items */
+ max98088->eq_enum.texts = max98088->eq_texts;
+ max98088->eq_enum.max = max98088->eq_textcnt;
+
+ ret = snd_soc_add_controls(codec, controls, ARRAY_SIZE(controls));
+ if (ret != 0)
+ dev_err(codec->dev, "Failed to add EQ control: %d\n", ret);
+}
+
+static void max98088_handle_pdata(struct snd_soc_codec *codec)
+{
+ struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct max98088_pdata *pdata = max98088->pdata;
+ u8 regval = 0;
+
+ if (!pdata) {
+ dev_dbg(codec->dev, "No platform data\n");
+ return;
+ }
+
+ /* Configure mic for analog/digital mic mode */
+ if (pdata->digmic_left_mode)
+ regval |= M98088_DIGMIC_L;
+
+ if (pdata->digmic_right_mode)
+ regval |= M98088_DIGMIC_R;
+
+ max98088->digmic = (regval ? 1 : 0);
+
+ snd_soc_write(codec, M98088_REG_48_CFG_MIC, regval);
+
+ /* Configure receiver output */
+ regval = ((pdata->receiver_mode) ? M98088_REC_LINEMODE : 0);
+ snd_soc_update_bits(codec, M98088_REG_2A_MIC_REC_CNTL,
+ M98088_REC_LINEMODE_MASK, regval);
+
+ /* Configure equalizers */
+ if (pdata->eq_cfgcnt)
+ max98088_handle_eq_pdata(codec);
+}
+
+#ifdef CONFIG_PM
+static int max98088_suspend(struct snd_soc_codec *codec, pm_message_t state)
+{
+ max98088_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ return 0;
+}
+
+static int max98088_resume(struct snd_soc_codec *codec)
+{
+ max98088_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ return 0;
+}
+#else
+#define max98088_suspend NULL
+#define max98088_resume NULL
+#endif
+
+static int max98088_probe(struct snd_soc_codec *codec)
+{
+ struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct max98088_cdata *cdata;
+ int ret = 0;
+
+ codec->cache_sync = 1;
+ memcpy(codec->reg_cache, max98088_reg, sizeof(max98088_reg));
+
+ ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ return ret;
+ }
+
+ /* initalize private data */
+
+ max98088->sysclk = (unsigned)-1;
+ max98088->eq_textcnt = 0;
+
+ cdata = &max98088->dai[0];
+ cdata->rate = (unsigned)-1;
+ cdata->fmt = (unsigned)-1;
+ cdata->eq_sel = 0;
+
+ cdata = &max98088->dai[1];
+ cdata->rate = (unsigned)-1;
+ cdata->fmt = (unsigned)-1;
+ cdata->eq_sel = 0;
+
+ max98088->ina_state = 0;
+ max98088->inb_state = 0;
+ max98088->ex_mode = 0;
+ max98088->digmic = 0;
+ max98088->mic1pre = 0;
+ max98088->mic2pre = 0;
+
+ ret = snd_soc_read(codec, M98088_REG_FF_REV_ID);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to read device revision: %d\n",
+ ret);
+ goto err_access;
+ }
+ dev_info(codec->dev, "revision %c\n", ret + 'A');
+
+ snd_soc_write(codec, M98088_REG_51_PWR_SYS, M98088_PWRSV);
+
+ /* initialize registers cache to hardware default */
+ max98088_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ snd_soc_write(codec, M98088_REG_0F_IRQ_ENABLE, 0x00);
+
+ snd_soc_write(codec, M98088_REG_22_MIX_DAC,
+ M98088_DAI1L_TO_DACL|M98088_DAI2L_TO_DACL|
+ M98088_DAI1R_TO_DACR|M98088_DAI2R_TO_DACR);
+
+ snd_soc_write(codec, M98088_REG_4E_BIAS_CNTL, 0xF0);
+ snd_soc_write(codec, M98088_REG_50_DAC_BIAS2, 0x0F);
+
+ snd_soc_write(codec, M98088_REG_16_DAI1_IOCFG,
+ M98088_S1NORMAL|M98088_SDATA);
+
+ snd_soc_write(codec, M98088_REG_1E_DAI2_IOCFG,
+ M98088_S2NORMAL|M98088_SDATA);
+
+ max98088_handle_pdata(codec);
+
+ max98088_add_widgets(codec);
+
+err_access:
+ return ret;
+}
+
+static int max98088_remove(struct snd_soc_codec *codec)
+{
+ max98088_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_max98088 = {
+ .probe = max98088_probe,
+ .remove = max98088_remove,
+ .suspend = max98088_suspend,
+ .resume = max98088_resume,
+ .set_bias_level = max98088_set_bias_level,
+ .reg_cache_size = ARRAY_SIZE(max98088_reg),
+ .reg_word_size = sizeof(u8),
+ .reg_cache_default = max98088_reg,
+ .volatile_register = max98088_volatile_register,
+};
+
+static int max98088_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct max98088_priv *max98088;
+ int ret;
+
+ max98088 = kzalloc(sizeof(struct max98088_priv), GFP_KERNEL);
+ if (max98088 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, max98088);
+ max98088->control_data = i2c;
+ max98088->pdata = i2c->dev.platform_data;
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_max98088, &max98088_dai[0], 2);
+ if (ret < 0)
+ kfree(max98088);
+ return ret;
+}
+
+static int __devexit max98088_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ kfree(i2c_get_clientdata(client));
+ return 0;
+}
+
+static const struct i2c_device_id max98088_i2c_id[] = {
+ { "max98088", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max98088_i2c_id);
+
+static struct i2c_driver max98088_i2c_driver = {
+ .driver = {
+ .name = "max98088",
+ .owner = THIS_MODULE,
+ },
+ .probe = max98088_i2c_probe,
+ .remove = __devexit_p(max98088_i2c_remove),
+ .id_table = max98088_i2c_id,
+};
+
+static int __init max98088_init(void)
+{
+ int ret;
+
+ ret = i2c_add_driver(&max98088_i2c_driver);
+ if (ret)
+ pr_err("Failed to register max98088 I2C driver: %d\n", ret);
+
+ return ret;
+}
+module_init(max98088_init);
+
+static void __exit max98088_exit(void)
+{
+ i2c_del_driver(&max98088_i2c_driver);
+}
+module_exit(max98088_exit);
+
+MODULE_DESCRIPTION("ALSA SoC MAX98088 driver");
+MODULE_AUTHOR("Peter Hsiang, Jesse Marroquin");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98088.h b/sound/soc/codecs/max98088.h
new file mode 100644
index 00000000000..56554c797fe
--- /dev/null
+++ b/sound/soc/codecs/max98088.h
@@ -0,0 +1,193 @@
+/*
+ * max98088.h -- MAX98088 ALSA SoC Audio driver
+ *
+ * Copyright 2010 Maxim Integrated Products
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _MAX98088_H
+#define _MAX98088_H
+
+/*
+ * MAX98088 Registers Definition
+ */
+#define M98088_REG_00_IRQ_STATUS 0x00
+#define M98088_REG_01_MIC_STATUS 0x01
+#define M98088_REG_02_JACK_STAUS 0x02
+#define M98088_REG_03_BATTERY_VOLTAGE 0x03
+#define M98088_REG_0F_IRQ_ENABLE 0x0F
+#define M98088_REG_10_SYS_CLK 0x10
+#define M98088_REG_11_DAI1_CLKMODE 0x11
+#define M98088_REG_12_DAI1_CLKCFG_HI 0x12
+#define M98088_REG_13_DAI1_CLKCFG_LO 0x13
+#define M98088_REG_14_DAI1_FORMAT 0x14
+#define M98088_REG_15_DAI1_CLOCK 0x15
+#define M98088_REG_16_DAI1_IOCFG 0x16
+#define M98088_REG_17_DAI1_TDM 0x17
+#define M98088_REG_18_DAI1_FILTERS 0x18
+#define M98088_REG_19_DAI2_CLKMODE 0x19
+#define M98088_REG_1A_DAI2_CLKCFG_HI 0x1A
+#define M98088_REG_1B_DAI2_CLKCFG_LO 0x1B
+#define M98088_REG_1C_DAI2_FORMAT 0x1C
+#define M98088_REG_1D_DAI2_CLOCK 0x1D
+#define M98088_REG_1E_DAI2_IOCFG 0x1E
+#define M98088_REG_1F_DAI2_TDM 0x1F
+#define M98088_REG_20_DAI2_FILTERS 0x20
+#define M98088_REG_21_SRC 0x21
+#define M98088_REG_22_MIX_DAC 0x22
+#define M98088_REG_23_MIX_ADC_LEFT 0x23
+#define M98088_REG_24_MIX_ADC_RIGHT 0x24
+#define M98088_REG_25_MIX_HP_LEFT 0x25
+#define M98088_REG_26_MIX_HP_RIGHT 0x26
+#define M98088_REG_27_MIX_HP_CNTL 0x27
+#define M98088_REG_28_MIX_REC_LEFT 0x28
+#define M98088_REG_29_MIX_REC_RIGHT 0x29
+#define M98088_REG_2A_MIC_REC_CNTL 0x2A
+#define M98088_REG_2B_MIX_SPK_LEFT 0x2B
+#define M98088_REG_2C_MIX_SPK_RIGHT 0x2C
+#define M98088_REG_2D_MIX_SPK_CNTL 0x2D
+#define M98088_REG_2E_LVL_SIDETONE 0x2E
+#define M98088_REG_2F_LVL_DAI1_PLAY 0x2F
+#define M98088_REG_30_LVL_DAI1_PLAY_EQ 0x30
+#define M98088_REG_31_LVL_DAI2_PLAY 0x31
+#define M98088_REG_32_LVL_DAI2_PLAY_EQ 0x32
+#define M98088_REG_33_LVL_ADC_L 0x33
+#define M98088_REG_34_LVL_ADC_R 0x34
+#define M98088_REG_35_LVL_MIC1 0x35
+#define M98088_REG_36_LVL_MIC2 0x36
+#define M98088_REG_37_LVL_INA 0x37
+#define M98088_REG_38_LVL_INB 0x38
+#define M98088_REG_39_LVL_HP_L 0x39
+#define M98088_REG_3A_LVL_HP_R 0x3A
+#define M98088_REG_3B_LVL_REC_L 0x3B
+#define M98088_REG_3C_LVL_REC_R 0x3C
+#define M98088_REG_3D_LVL_SPK_L 0x3D
+#define M98088_REG_3E_LVL_SPK_R 0x3E
+#define M98088_REG_3F_MICAGC_CFG 0x3F
+#define M98088_REG_40_MICAGC_THRESH 0x40
+#define M98088_REG_41_SPKDHP 0x41
+#define M98088_REG_42_SPKDHP_THRESH 0x42
+#define M98088_REG_43_SPKALC_COMP 0x43
+#define M98088_REG_44_PWRLMT_CFG 0x44
+#define M98088_REG_45_PWRLMT_TIME 0x45
+#define M98088_REG_46_THDLMT_CFG 0x46
+#define M98088_REG_47_CFG_AUDIO_IN 0x47
+#define M98088_REG_48_CFG_MIC 0x48
+#define M98088_REG_49_CFG_LEVEL 0x49
+#define M98088_REG_4A_CFG_BYPASS 0x4A
+#define M98088_REG_4B_CFG_JACKDET 0x4B
+#define M98088_REG_4C_PWR_EN_IN 0x4C
+#define M98088_REG_4D_PWR_EN_OUT 0x4D
+#define M98088_REG_4E_BIAS_CNTL 0x4E
+#define M98088_REG_4F_DAC_BIAS1 0x4F
+#define M98088_REG_50_DAC_BIAS2 0x50
+#define M98088_REG_51_PWR_SYS 0x51
+#define M98088_REG_52_DAI1_EQ_BASE 0x52
+#define M98088_REG_84_DAI2_EQ_BASE 0x84
+#define M98088_REG_B6_DAI1_BIQUAD_BASE 0xB6
+#define M98088_REG_C0_DAI2_BIQUAD_BASE 0xC0
+#define M98088_REG_FF_REV_ID 0xFF
+
+#define M98088_REG_CNT (0xFF+1)
+
+/* MAX98088 Registers Bit Fields */
+
+/* M98088_REG_11_DAI1_CLKMODE, M98088_REG_19_DAI2_CLKMODE */
+ #define M98088_CLKMODE_MASK 0xFF
+
+/* M98088_REG_14_DAI1_FORMAT, M98088_REG_1C_DAI2_FORMAT */
+ #define M98088_DAI_MAS (1<<7)
+ #define M98088_DAI_WCI (1<<6)
+ #define M98088_DAI_BCI (1<<5)
+ #define M98088_DAI_DLY (1<<4)
+ #define M98088_DAI_TDM (1<<2)
+ #define M98088_DAI_FSW (1<<1)
+ #define M98088_DAI_WS (1<<0)
+
+/* M98088_REG_15_DAI1_CLOCK, M98088_REG_1D_DAI2_CLOCK */
+ #define M98088_DAI_BSEL64 (1<<0)
+ #define M98088_DAI_OSR64 (1<<6)
+
+/* M98088_REG_16_DAI1_IOCFG, M98088_REG_1E_DAI2_IOCFG */
+ #define M98088_S1NORMAL (1<<6)
+ #define M98088_S2NORMAL (2<<6)
+ #define M98088_SDATA (3<<0)
+
+/* M98088_REG_18_DAI1_FILTERS, M98088_REG_20_DAI2_FILTERS */
+ #define M98088_DAI_DHF (1<<3)
+
+/* M98088_REG_22_MIX_DAC */
+ #define M98088_DAI1L_TO_DACL (1<<7)
+ #define M98088_DAI1R_TO_DACL (1<<6)
+ #define M98088_DAI2L_TO_DACL (1<<5)
+ #define M98088_DAI2R_TO_DACL (1<<4)
+ #define M98088_DAI1L_TO_DACR (1<<3)
+ #define M98088_DAI1R_TO_DACR (1<<2)
+ #define M98088_DAI2L_TO_DACR (1<<1)
+ #define M98088_DAI2R_TO_DACR (1<<0)
+
+/* M98088_REG_2A_MIC_REC_CNTL */
+ #define M98088_REC_LINEMODE (1<<7)
+ #define M98088_REC_LINEMODE_MASK (1<<7)
+
+/* M98088_REG_35_LVL_MIC1, M98088_REG_36_LVL_MIC2 */
+ #define M98088_MICPRE_MASK (3<<5)
+ #define M98088_MICPRE_SHIFT 5
+
+/* M98088_REG_3A_LVL_HP_R */
+ #define M98088_HP_MUTE (1<<7)
+
+/* M98088_REG_3C_LVL_REC_R */
+ #define M98088_REC_MUTE (1<<7)
+
+/* M98088_REG_3E_LVL_SPK_R */
+ #define M98088_SP_MUTE (1<<7)
+
+/* M98088_REG_48_CFG_MIC */
+ #define M98088_EXTMIC_MASK (3<<0)
+ #define M98088_DIGMIC_L (1<<5)
+ #define M98088_DIGMIC_R (1<<4)
+
+/* M98088_REG_49_CFG_LEVEL */
+ #define M98088_VSEN (1<<6)
+ #define M98088_ZDEN (1<<5)
+ #define M98088_EQ2EN (1<<1)
+ #define M98088_EQ1EN (1<<0)
+
+/* M98088_REG_4C_PWR_EN_IN */
+ #define M98088_INAEN (1<<7)
+ #define M98088_INBEN (1<<6)
+ #define M98088_MBEN (1<<3)
+ #define M98088_ADLEN (1<<1)
+ #define M98088_ADREN (1<<0)
+
+/* M98088_REG_4D_PWR_EN_OUT */
+ #define M98088_HPLEN (1<<7)
+ #define M98088_HPREN (1<<6)
+ #define M98088_HPEN ((1<<7)|(1<<6))
+ #define M98088_SPLEN (1<<5)
+ #define M98088_SPREN (1<<4)
+ #define M98088_RECEN (1<<3)
+ #define M98088_DALEN (1<<1)
+ #define M98088_DAREN (1<<0)
+
+/* M98088_REG_51_PWR_SYS */
+ #define M98088_SHDNRUN (1<<7)
+ #define M98088_PERFMODE (1<<3)
+ #define M98088_HPPLYBACK (1<<2)
+ #define M98088_PWRSV8K (1<<1)
+ #define M98088_PWRSV (1<<0)
+
+/* Line inputs */
+#define LINE_INA 0
+#define LINE_INB 1
+
+#define M98088_COEFS_PER_BAND 5
+
+#define M98088_BYTE1(w) ((w >> 8) & 0xff)
+#define M98088_BYTE0(w) (w & 0xff)
+
+#endif
diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c
index ad5987e6c9c..a2e0ed59b37 100644
--- a/sound/soc/codecs/wm8580.c
+++ b/sound/soc/codecs/wm8580.c
@@ -548,11 +548,9 @@ static int wm8580_set_paif_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
- struct wm8580_priv *wm8580 = snd_soc_codec_get_drvdata(codec);
unsigned int aifa;
unsigned int aifb;
int can_invert_lrclk;
- int sysclk;
aifa = snd_soc_read(codec, WM8580_PAIF1 + codec_dai->driver->id);
aifb = snd_soc_read(codec, WM8580_PAIF3 + codec_dai->driver->id);
@@ -621,8 +619,6 @@ static int wm8580_set_paif_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- sysclk = wm8580->sysclk[codec_dai->driver->id];
-
snd_soc_write(codec, WM8580_PAIF1 + codec_dai->driver->id, aifa);
snd_soc_write(codec, WM8580_PAIF3 + codec_dai->driver->id, aifb);
diff --git a/sound/soc/codecs/wm8804.c b/sound/soc/codecs/wm8804.c
index 642b07ceddf..4599e8e95aa 100644
--- a/sound/soc/codecs/wm8804.c
+++ b/sound/soc/codecs/wm8804.c
@@ -720,8 +720,8 @@ static int __devinit wm8804_spi_probe(struct spi_device *spi)
int ret;
wm8804 = kzalloc(sizeof *wm8804, GFP_KERNEL);
- if (IS_ERR(wm8804))
- return PTR_ERR(wm8804);
+ if (!wm8804)
+ return -ENOMEM;
wm8804->control_type = SND_SOC_SPI;
spi_set_drvdata(spi, wm8804);
@@ -758,8 +758,8 @@ static __devinit int wm8804_i2c_probe(struct i2c_client *i2c,
int ret;
wm8804 = kzalloc(sizeof *wm8804, GFP_KERNEL);
- if (IS_ERR(wm8804))
- return PTR_ERR(wm8804);
+ if (!wm8804)
+ return -ENOMEM;
wm8804->control_type = SND_SOC_I2C;
i2c_set_clientdata(i2c, wm8804);
diff --git a/sound/soc/codecs/wm8962-tables.c b/sound/soc/codecs/wm8962-tables.c
deleted file mode 100644
index 6fa98ea0ae4..00000000000
--- a/sound/soc/codecs/wm8962-tables.c
+++ /dev/null
@@ -1,42299 +0,0 @@
-/*
- * wm8962-tables.c -- WM8962 ASoC driver
- *
- * Copyright 2010 Wolfson Microelectronics, plc
- *
- * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include "wm8962.h"
-
-const u16 wm8962_reg[WM8962_MAX_REGISTER + 1] = {
- 0x009F, /* R0 - Left Input volume */
- 0x049F, /* R1 - Right Input volume */
- 0x0000, /* R2 - HPOUTL volume */
- 0x0000, /* R3 - HPOUTR volume */
- 0x0020, /* R4 - Clocking1 */
- 0x0018, /* R5 - ADC & DAC Control 1 */
- 0x2008, /* R6 - ADC & DAC Control 2 */
- 0x000A, /* R7 - Audio Interface 0 */
- 0x01E4, /* R8 - Clocking2 */
- 0x0300, /* R9 - Audio Interface 1 */
- 0x00C0, /* R10 - Left DAC volume */
- 0x00C0, /* R11 - Right DAC volume */
- 0x0000, /* R12 */
- 0x0000, /* R13 */
- 0x0040, /* R14 - Audio Interface 2 */
- 0x6243, /* R15 - Software Reset */
- 0x0000, /* R16 */
- 0x007B, /* R17 - ALC1 */
- 0x0000, /* R18 - ALC2 */
- 0x1C32, /* R19 - ALC3 */
- 0x3200, /* R20 - Noise Gate */
- 0x00C0, /* R21 - Left ADC volume */
- 0x00C0, /* R22 - Right ADC volume */
- 0x0160, /* R23 - Additional control(1) */
- 0x0000, /* R24 - Additional control(2) */
- 0x0000, /* R25 - Pwr Mgmt (1) */
- 0x0000, /* R26 - Pwr Mgmt (2) */
- 0x0010, /* R27 - Additional Control (3) */
- 0x0000, /* R28 - Anti-pop */
- 0x0000, /* R29 */
- 0x005E, /* R30 - Clocking 3 */
- 0x0000, /* R31 - Input mixer control (1) */
- 0x0145, /* R32 - Left input mixer volume */
- 0x0145, /* R33 - Right input mixer volume */
- 0x0009, /* R34 - Input mixer control (2) */
- 0x0003, /* R35 - Input bias control */
- 0x0000, /* R36 */
- 0x0008, /* R37 - Left input PGA control */
- 0x0008, /* R38 - Right input PGA control */
- 0x0000, /* R39 */
- 0x0000, /* R40 - SPKOUTL volume */
- 0x0000, /* R41 - SPKOUTR volume */
- 0x0000, /* R42 */
- 0x0000, /* R43 */
- 0x0000, /* R44 */
- 0x0000, /* R45 */
- 0x0000, /* R46 */
- 0x0000, /* R47 - Thermal Shutdown Status */
- 0x8027, /* R48 - Additional Control (4) */
- 0x0010, /* R49 - Class D Control 1 */
- 0x0000, /* R50 */
- 0x0003, /* R51 - Class D Control 2 */
- 0x0000, /* R52 */
- 0x0000, /* R53 */
- 0x0000, /* R54 */
- 0x0000, /* R55 */
- 0x0506, /* R56 - Clocking 4 */
- 0x0000, /* R57 - DAC DSP Mixing (1) */
- 0x0000, /* R58 - DAC DSP Mixing (2) */
- 0x0000, /* R59 */
- 0x0300, /* R60 - DC Servo 0 */
- 0x0300, /* R61 - DC Servo 1 */
- 0x0000, /* R62 */
- 0x0000, /* R63 */
- 0x0810, /* R64 - DC Servo 4 */
- 0x0000, /* R65 */
- 0x0000, /* R66 - DC Servo 6 */
- 0x0000, /* R67 */
- 0x001B, /* R68 - Analogue PGA Bias */
- 0x0000, /* R69 - Analogue HP 0 */
- 0x0000, /* R70 */
- 0x01FB, /* R71 - Analogue HP 2 */
- 0x0000, /* R72 - Charge Pump 1 */
- 0x0000, /* R73 */
- 0x0000, /* R74 */
- 0x0000, /* R75 */
- 0x0000, /* R76 */
- 0x0000, /* R77 */
- 0x0000, /* R78 */
- 0x0000, /* R79 */
- 0x0000, /* R80 */
- 0x0000, /* R81 */
- 0x0004, /* R82 - Charge Pump B */
- 0x0000, /* R83 */
- 0x0000, /* R84 */
- 0x0000, /* R85 */
- 0x0000, /* R86 */
- 0x0000, /* R87 - Write Sequencer Control 1 */
- 0x0000, /* R88 */
- 0x0000, /* R89 */
- 0x0000, /* R90 - Write Sequencer Control 2 */
- 0x0000, /* R91 */
- 0x0000, /* R92 */
- 0x0000, /* R93 - Write Sequencer Control 3 */
- 0x0000, /* R94 - Control Interface */
- 0x0000, /* R95 */
- 0x0000, /* R96 */
- 0x0000, /* R97 */
- 0x0000, /* R98 */
- 0x0000, /* R99 - Mixer Enables */
- 0x0000, /* R100 - Headphone Mixer (1) */
- 0x0000, /* R101 - Headphone Mixer (2) */
- 0x013F, /* R102 - Headphone Mixer (3) */
- 0x013F, /* R103 - Headphone Mixer (4) */
- 0x0000, /* R104 */
- 0x0000, /* R105 - Speaker Mixer (1) */
- 0x0000, /* R106 - Speaker Mixer (2) */
- 0x013F, /* R107 - Speaker Mixer (3) */
- 0x013F, /* R108 - Speaker Mixer (4) */
- 0x0003, /* R109 - Speaker Mixer (5) */
- 0x0002, /* R110 - Beep Generator (1) */
- 0x0000, /* R111 */
- 0x0000, /* R112 */
- 0x0000, /* R113 */
- 0x0000, /* R114 */
- 0x0006, /* R115 - Oscillator Trim (3) */
- 0x0026, /* R116 - Oscillator Trim (4) */
- 0x0000, /* R117 */
- 0x0000, /* R118 */
- 0x0000, /* R119 - Oscillator Trim (7) */
- 0x0000, /* R120 */
- 0x0000, /* R121 */
- 0x0000, /* R122 */
- 0x0000, /* R123 */
- 0x0011, /* R124 - Analogue Clocking1 */
- 0x004B, /* R125 - Analogue Clocking2 */
- 0x000D, /* R126 - Analogue Clocking3 */
- 0x0000, /* R127 - PLL Software Reset */
- 0x0000, /* R128 */
- 0x0000, /* R129 - PLL2 */
- 0x0000, /* R130 */
- 0x0000, /* R131 - PLL 4 */
- 0x0000, /* R132 */
- 0x0000, /* R133 */
- 0x0000, /* R134 */
- 0x0000, /* R135 */
- 0x0067, /* R136 - PLL 9 */
- 0x001C, /* R137 - PLL 10 */
- 0x0071, /* R138 - PLL 11 */
- 0x00C7, /* R139 - PLL 12 */
- 0x0067, /* R140 - PLL 13 */
- 0x0048, /* R141 - PLL 14 */
- 0x0022, /* R142 - PLL 15 */
- 0x0097, /* R143 - PLL 16 */
- 0x0000, /* R144 */
- 0x0000, /* R145 */
- 0x0000, /* R146 */
- 0x0000, /* R147 */
- 0x0000, /* R148 */
- 0x0000, /* R149 */
- 0x0000, /* R150 */
- 0x0000, /* R151 */
- 0x0000, /* R152 */
- 0x0000, /* R153 */
- 0x0000, /* R154 */
- 0x000C, /* R155 - FLL Control (1) */
- 0x0039, /* R156 - FLL Control (2) */
- 0x0180, /* R157 - FLL Control (3) */
- 0x0000, /* R158 */
- 0x0032, /* R159 - FLL Control (5) */
- 0x0018, /* R160 - FLL Control (6) */
- 0x007D, /* R161 - FLL Control (7) */
- 0x0008, /* R162 - FLL Control (8) */
- 0x0000, /* R163 */
- 0x0000, /* R164 */
- 0x0000, /* R165 */
- 0x0000, /* R166 */
- 0x0000, /* R167 */
- 0x0000, /* R168 */
- 0x0000, /* R169 */
- 0x0000, /* R170 */
- 0x0000, /* R171 */
- 0x0000, /* R172 */
- 0x0000, /* R173 */
- 0x0000, /* R174 */
- 0x0000, /* R175 */
- 0x0000, /* R176 */
- 0x0000, /* R177 */
- 0x0000, /* R178 */
- 0x0000, /* R179 */
- 0x0000, /* R180 */
- 0x0000, /* R181 */
- 0x0000, /* R182 */
- 0x0000, /* R183 */
- 0x0000, /* R184 */
- 0x0000, /* R185 */
- 0x0000, /* R186 */
- 0x0000, /* R187 */
- 0x0000, /* R188 */
- 0x0000, /* R189 */
- 0x0000, /* R190 */
- 0x0000, /* R191 */
- 0x0000, /* R192 */
- 0x0000, /* R193 */
- 0x0000, /* R194 */
- 0x0000, /* R195 */
- 0x0000, /* R196 */
- 0x0000, /* R197 */
- 0x0000, /* R198 */
- 0x0000, /* R199 */
- 0x0000, /* R200 */
- 0x0000, /* R201 */
- 0x0000, /* R202 */
- 0x0000, /* R203 */
- 0x0000, /* R204 */
- 0x0000, /* R205 */
- 0x0000, /* R206 */
- 0x0000, /* R207 */
- 0x0000, /* R208 */
- 0x0000, /* R209 */
- 0x0000, /* R210 */
- 0x0000, /* R211 */
- 0x0000, /* R212 */
- 0x0000, /* R213 */
- 0x0000, /* R214 */
- 0x0000, /* R215 */
- 0x0000, /* R216 */
- 0x0000, /* R217 */
- 0x0000, /* R218 */
- 0x0000, /* R219 */
- 0x0000, /* R220 */
- 0x0000, /* R221 */
- 0x0000, /* R222 */
- 0x0000, /* R223 */
- 0x0000, /* R224 */
- 0x0000, /* R225 */
- 0x0000, /* R226 */
- 0x0000, /* R227 */
- 0x0000, /* R228 */
- 0x0000, /* R229 */
- 0x0000, /* R230 */
- 0x0000, /* R231 */
- 0x0000, /* R232 */
- 0x0000, /* R233 */
- 0x0000, /* R234 */
- 0x0000, /* R235 */
- 0x0000, /* R236 */
- 0x0000, /* R237 */
- 0x0000, /* R238 */
- 0x0000, /* R239 */
- 0x0000, /* R240 */
- 0x0000, /* R241 */
- 0x0000, /* R242 */
- 0x0000, /* R243 */
- 0x0000, /* R244 */
- 0x0000, /* R245 */
- 0x0000, /* R246 */
- 0x0000, /* R247 */
- 0x0000, /* R248 */
- 0x0000, /* R249 */
- 0x0000, /* R250 */
- 0x0000, /* R251 */
- 0x0005, /* R252 - General test 1 */
- 0x0000, /* R253 */
- 0x0000, /* R254 */
- 0x0000, /* R255 */
- 0x0000, /* R256 - DF1 */
- 0x0000, /* R257 - DF2 */
- 0x0000, /* R258 - DF3 */
- 0x0000, /* R259 - DF4 */
- 0x0000, /* R260 - DF5 */
- 0x0000, /* R261 - DF6 */
- 0x0000, /* R262 - DF7 */
- 0x0000, /* R263 */
- 0x0000, /* R264 - LHPF1 */
- 0x0000, /* R265 - LHPF2 */
- 0x0000, /* R266 */
- 0x0000, /* R267 */
- 0x0000, /* R268 - THREED1 */
- 0x0000, /* R269 - THREED2 */
- 0x0000, /* R270 - THREED3 */
- 0x0000, /* R271 - THREED4 */
- 0x0000, /* R272 */
- 0x0000, /* R273 */
- 0x0000, /* R274 */
- 0x0000, /* R275 */
- 0x000C, /* R276 - DRC 1 */
- 0x0925, /* R277 - DRC 2 */
- 0x0000, /* R278 - DRC 3 */
- 0x0000, /* R279 - DRC 4 */
- 0x0000, /* R280 - DRC 5 */
- 0x0000, /* R281 */
- 0x0000, /* R282 */
- 0x0000, /* R283 */
- 0x0000, /* R284 */
- 0x0000, /* R285 - Tloopback */
- 0x0000, /* R286 */
- 0x0000, /* R287 */
- 0x0000, /* R288 */
- 0x0000, /* R289 */
- 0x0000, /* R290 */
- 0x0000, /* R291 */
- 0x0000, /* R292 */
- 0x0000, /* R293 */
- 0x0000, /* R294 */
- 0x0000, /* R295 */
- 0x0000, /* R296 */
- 0x0000, /* R297 */
- 0x0000, /* R298 */
- 0x0000, /* R299 */
- 0x0000, /* R300 */
- 0x0000, /* R301 */
- 0x0000, /* R302 */
- 0x0000, /* R303 */
- 0x0000, /* R304 */
- 0x0000, /* R305 */
- 0x0000, /* R306 */
- 0x0000, /* R307 */
- 0x0000, /* R308 */
- 0x0000, /* R309 */
- 0x0000, /* R310 */
- 0x0000, /* R311 */
- 0x0000, /* R312 */
- 0x0000, /* R313 */
- 0x0000, /* R314 */
- 0x0000, /* R315 */
- 0x0000, /* R316 */
- 0x0000, /* R317 */
- 0x0000, /* R318 */
- 0x0000, /* R319 */
- 0x0000, /* R320 */
- 0x0000, /* R321 */
- 0x0000, /* R322 */
- 0x0000, /* R323 */
- 0x0000, /* R324 */
- 0x0000, /* R325 */
- 0x0000, /* R326 */
- 0x0000, /* R327 */
- 0x0000, /* R328 */
- 0x0000, /* R329 */
- 0x0000, /* R330 */
- 0x0000, /* R331 */
- 0x0000, /* R332 */
- 0x0000, /* R333 */
- 0x0000, /* R334 */
- 0x0004, /* R335 - EQ1 */
- 0x6318, /* R336 - EQ2 */
- 0x6300, /* R337 - EQ3 */
- 0x0FCA, /* R338 - EQ4 */
- 0x0400, /* R339 - EQ5 */
- 0x00D8, /* R340 - EQ6 */
- 0x1EB5, /* R341 - EQ7 */
- 0xF145, /* R342 - EQ8 */
- 0x0B75, /* R343 - EQ9 */
- 0x01C5, /* R344 - EQ10 */
- 0x1C58, /* R345 - EQ11 */
- 0xF373, /* R346 - EQ12 */
- 0x0A54, /* R347 - EQ13 */
- 0x0558, /* R348 - EQ14 */
- 0x168E, /* R349 - EQ15 */
- 0xF829, /* R350 - EQ16 */
- 0x07AD, /* R351 - EQ17 */
- 0x1103, /* R352 - EQ18 */
- 0x0564, /* R353 - EQ19 */
- 0x0559, /* R354 - EQ20 */
- 0x4000, /* R355 - EQ21 */
- 0x6318, /* R356 - EQ22 */
- 0x6300, /* R357 - EQ23 */
- 0x0FCA, /* R358 - EQ24 */
- 0x0400, /* R359 - EQ25 */
- 0x00D8, /* R360 - EQ26 */
- 0x1EB5, /* R361 - EQ27 */
- 0xF145, /* R362 - EQ28 */
- 0x0B75, /* R363 - EQ29 */
- 0x01C5, /* R364 - EQ30 */
- 0x1C58, /* R365 - EQ31 */
- 0xF373, /* R366 - EQ32 */
- 0x0A54, /* R367 - EQ33 */
- 0x0558, /* R368 - EQ34 */
- 0x168E, /* R369 - EQ35 */
- 0xF829, /* R370 - EQ36 */
- 0x07AD, /* R371 - EQ37 */
- 0x1103, /* R372 - EQ38 */
- 0x0564, /* R373 - EQ39 */
- 0x0559, /* R374 - EQ40 */
- 0x4000, /* R375 - EQ41 */
- 0x0000, /* R376 */
- 0x0000, /* R377 */
- 0x0000, /* R378 */
- 0x0000, /* R379 */
- 0x0000, /* R380 */
- 0x0000, /* R381 */
- 0x0000, /* R382 */
- 0x0000, /* R383 */
- 0x0000, /* R384 */
- 0x0000, /* R385 */
- 0x0000, /* R386 */
- 0x0000, /* R387 */
- 0x0000, /* R388 */
- 0x0000, /* R389 */
- 0x0000, /* R390 */
- 0x0000, /* R391 */
- 0x0000, /* R392 */
- 0x0000, /* R393 */
- 0x0000, /* R394 */
- 0x0000, /* R395 */
- 0x0000, /* R396 */
- 0x0000, /* R397 */
- 0x0000, /* R398 */
- 0x0000, /* R399 */
- 0x0000, /* R400 */
- 0x0000, /* R401 */
- 0x0000, /* R402 */
- 0x0000, /* R403 */
- 0x0000, /* R404 */
- 0x0000, /* R405 */
- 0x0000, /* R406 */
- 0x0000, /* R407 */
- 0x0000, /* R408 */
- 0x0000, /* R409 */
- 0x0000, /* R410 */
- 0x0000, /* R411 */
- 0x0000, /* R412 */
- 0x0000, /* R413 */
- 0x0000, /* R414 */
- 0x0000, /* R415 */
- 0x0000, /* R416 */
- 0x0000, /* R417 */
- 0x0000, /* R418 */
- 0x0000, /* R419 */
- 0x0000, /* R420 */
- 0x0000, /* R421 */
- 0x0000, /* R422 */
- 0x0000, /* R423 */
- 0x0000, /* R424 */
- 0x0000, /* R425 */
- 0x0000, /* R426 */
- 0x0000, /* R427 */
- 0x0000, /* R428 */
- 0x0000, /* R429 */
- 0x0000, /* R430 */
- 0x0000, /* R431 */
- 0x0000, /* R432 */
- 0x0000, /* R433 */
- 0x0000, /* R434 */
- 0x0000, /* R435 */
- 0x0000, /* R436 */
- 0x0000, /* R437 */
- 0x0000, /* R438 */
- 0x0000, /* R439 */
- 0x0000, /* R440 */
- 0x0000, /* R441 */
- 0x0000, /* R442 */
- 0x0000, /* R443 */
- 0x0000, /* R444 */
- 0x0000, /* R445 */
- 0x0000, /* R446 */
- 0x0000, /* R447 */
- 0x0000, /* R448 */
- 0x0000, /* R449 */
- 0x0000, /* R450 */
- 0x0000, /* R451 */
- 0x0000, /* R452 */
- 0x0000, /* R453 */
- 0x0000, /* R454 */
- 0x0000, /* R455 */
- 0x0000, /* R456 */
- 0x0000, /* R457 */
- 0x0000, /* R458 */
- 0x0000, /* R459 */
- 0x0000, /* R460 */
- 0x0000, /* R461 */
- 0x0000, /* R462 */
- 0x0000, /* R463 */
- 0x0000, /* R464 */
- 0x0000, /* R465 */
- 0x0000, /* R466 */
- 0x0000, /* R467 */
- 0x0000, /* R468 */
- 0x0000, /* R469 */
- 0x0000, /* R470 */
- 0x0000, /* R471 */
- 0x0000, /* R472 */
- 0x0000, /* R473 */
- 0x0000, /* R474 */
- 0x0000, /* R475 */
- 0x0000, /* R476 */
- 0x0000, /* R477 */
- 0x0000, /* R478 */
- 0x0000, /* R479 */
- 0x0000, /* R480 */
- 0x0000, /* R481 */
- 0x0000, /* R482 */
- 0x0000, /* R483 */
- 0x0000, /* R484 */
- 0x0000, /* R485 */
- 0x0000, /* R486 */
- 0x0000, /* R487 */
- 0x0000, /* R488 */
- 0x0000, /* R489 */
- 0x0000, /* R490 */
- 0x0000, /* R491 */
- 0x0000, /* R492 */
- 0x0000, /* R493 */
- 0x0000, /* R494 */
- 0x0000, /* R495 */
- 0x0000, /* R496 */
- 0x0000, /* R497 */
- 0x0000, /* R498 */
- 0x0000, /* R499 */
- 0x0000, /* R500 */
- 0x0000, /* R501 */
- 0x0000, /* R502 */
- 0x0000, /* R503 */
- 0x0000, /* R504 */
- 0x0000, /* R505 */
- 0x0000, /* R506 */
- 0x0000, /* R507 */
- 0x0000, /* R508 */
- 0x0000, /* R509 */
- 0x0000, /* R510 */
- 0x0000, /* R511 */
- 0x0000, /* R512 */
- 0x0000, /* R513 - GPIO 2 */
- 0x0000, /* R514 - GPIO 3 */
- 0x0000, /* R515 */
- 0x8100, /* R516 - GPIO 5 */
- 0x8100, /* R517 - GPIO 6 */
- 0x0000, /* R518 */
- 0x0000, /* R519 */
- 0x0000, /* R520 */
- 0x0000, /* R521 */
- 0x0000, /* R522 */
- 0x0000, /* R523 */
- 0x0000, /* R524 */
- 0x0000, /* R525 */
- 0x0000, /* R526 */
- 0x0000, /* R527 */
- 0x0000, /* R528 */
- 0x0000, /* R529 */
- 0x0000, /* R530 */
- 0x0000, /* R531 */
- 0x0000, /* R532 */
- 0x0000, /* R533 */
- 0x0000, /* R534 */
- 0x0000, /* R535 */
- 0x0000, /* R536 */
- 0x0000, /* R537 */
- 0x0000, /* R538 */
- 0x0000, /* R539 */
- 0x0000, /* R540 */
- 0x0000, /* R541 */
- 0x0000, /* R542 */
- 0x0000, /* R543 */
- 0x0000, /* R544 */
- 0x0000, /* R545 */
- 0x0000, /* R546 */
- 0x0000, /* R547 */
- 0x0000, /* R548 */
- 0x0000, /* R549 */
- 0x0000, /* R550 */
- 0x0000, /* R551 */
- 0x0000, /* R552 */
- 0x0000, /* R553 */
- 0x0000, /* R554 */
- 0x0000, /* R555 */
- 0x0000, /* R556 */
- 0x0000, /* R557 */
- 0x0000, /* R558 */
- 0x0000, /* R559 */
- 0x0000, /* R560 - Interrupt Status 1 */
- 0x0000, /* R561 - Interrupt Status 2 */
- 0x0000, /* R562 */
- 0x0000, /* R563 */
- 0x0000, /* R564 */
- 0x0000, /* R565 */
- 0x0000, /* R566 */
- 0x0000, /* R567 */
- 0x0030, /* R568 - Interrupt Status 1 Mask */
- 0xFFED, /* R569 - Interrupt Status 2 Mask */
- 0x0000, /* R570 */
- 0x0000, /* R571 */
- 0x0000, /* R572 */
- 0x0000, /* R573 */
- 0x0000, /* R574 */
- 0x0000, /* R575 */
- 0x0000, /* R576 - Interrupt Control */
- 0x0000, /* R577 */
- 0x0000, /* R578 */
- 0x0000, /* R579 */
- 0x0000, /* R580 */
- 0x0000, /* R581 */
- 0x0000, /* R582 */
- 0x0000, /* R583 */
- 0x002D, /* R584 - IRQ Debounce */
- 0x0000, /* R585 */
- 0x0000, /* R586 - MICINT Source Pol */
- 0x0000, /* R587 */
- 0x0000, /* R588 */
- 0x0000, /* R589 */
- 0x0000, /* R590 */
- 0x0000, /* R591 */
- 0x0000, /* R592 */
- 0x0000, /* R593 */
- 0x0000, /* R594 */
- 0x0000, /* R595 */
- 0x0000, /* R596 */
- 0x0000, /* R597 */
- 0x0000, /* R598 */
- 0x0000, /* R599 */
- 0x0000, /* R600 */
- 0x0000, /* R601 */
- 0x0000, /* R602 */
- 0x0000, /* R603 */
- 0x0000, /* R604 */
- 0x0000, /* R605 */
- 0x0000, /* R606 */
- 0x0000, /* R607 */
- 0x0000, /* R608 */
- 0x0000, /* R609 */
- 0x0000, /* R610 */
- 0x0000, /* R611 */
- 0x0000, /* R612 */
- 0x0000, /* R613 */
- 0x0000, /* R614 */
- 0x0000, /* R615 */
- 0x0000, /* R616 */
- 0x0000, /* R617 */
- 0x0000, /* R618 */
- 0x0000, /* R619 */
- 0x0000, /* R620 */
- 0x0000, /* R621 */
- 0x0000, /* R622 */
- 0x0000, /* R623 */
- 0x0000, /* R624 */
- 0x0000, /* R625 */
- 0x0000, /* R626 */
- 0x0000, /* R627 */
- 0x0000, /* R628 */
- 0x0000, /* R629 */
- 0x0000, /* R630 */
- 0x0000, /* R631 */
- 0x0000, /* R632 */
- 0x0000, /* R633 */
- 0x0000, /* R634 */
- 0x0000, /* R635 */
- 0x0000, /* R636 */
- 0x0000, /* R637 */
- 0x0000, /* R638 */
- 0x0000, /* R639 */
- 0x0000, /* R640 */
- 0x0000, /* R641 */
- 0x0000, /* R642 */
- 0x0000, /* R643 */
- 0x0000, /* R644 */
- 0x0000, /* R645 */
- 0x0000, /* R646 */
- 0x0000, /* R647 */
- 0x0000, /* R648 */
- 0x0000, /* R649 */
- 0x0000, /* R650 */
- 0x0000, /* R651 */
- 0x0000, /* R652 */
- 0x0000, /* R653 */
- 0x0000, /* R654 */
- 0x0000, /* R655 */
- 0x0000, /* R656 */
- 0x0000, /* R657 */
- 0x0000, /* R658 */
- 0x0000, /* R659 */
- 0x0000, /* R660 */
- 0x0000, /* R661 */
- 0x0000, /* R662 */
- 0x0000, /* R663 */
- 0x0000, /* R664 */
- 0x0000, /* R665 */
- 0x0000, /* R666 */
- 0x0000, /* R667 */
- 0x0000, /* R668 */
- 0x0000, /* R669 */
- 0x0000, /* R670 */
- 0x0000, /* R671 */
- 0x0000, /* R672 */
- 0x0000, /* R673 */
- 0x0000, /* R674 */
- 0x0000, /* R675 */
- 0x0000, /* R676 */
- 0x0000, /* R677 */
- 0x0000, /* R678 */
- 0x0000, /* R679 */
- 0x0000, /* R680 */
- 0x0000, /* R681 */
- 0x0000, /* R682 */
- 0x0000, /* R683 */
- 0x0000, /* R684 */
- 0x0000, /* R685 */
- 0x0000, /* R686 */
- 0x0000, /* R687 */
- 0x0000, /* R688 */
- 0x0000, /* R689 */
- 0x0000, /* R690 */
- 0x0000, /* R691 */
- 0x0000, /* R692 */
- 0x0000, /* R693 */
- 0x0000, /* R694 */
- 0x0000, /* R695 */
- 0x0000, /* R696 */
- 0x0000, /* R697 */
- 0x0000, /* R698 */
- 0x0000, /* R699 */
- 0x0000, /* R700 */
- 0x0000, /* R701 */
- 0x0000, /* R702 */
- 0x0000, /* R703 */
- 0x0000, /* R704 */
- 0x0000, /* R705 */
- 0x0000, /* R706 */
- 0x0000, /* R707 */
- 0x0000, /* R708 */
- 0x0000, /* R709 */
- 0x0000, /* R710 */
- 0x0000, /* R711 */
- 0x0000, /* R712 */
- 0x0000, /* R713 */
- 0x0000,