Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6: (365 commits)
  ALSA: hda - Disable sticky PCM stream assignment for AD codecs
  ALSA: usb - Creative USB X-Fi volume knob support
  ALSA: ca0106: Use card specific dac id for mute controls.
  ALSA: ca0106: Allow different sound cards to use different SPI channel mappings.
  ALSA: ca0106: Create a nice spot for mapping channels to dacs.
  ALSA: ca0106: Move enabling of front dac out of hardcoded setup sequence.
  ALSA: ca0106: Pull out dac powering routine into separate function.
  ALSA: ca0106 - add Sound Blaster 5.1vx info.
  ASoC: tlv320dac33: Use usleep_range for delays
  ALSA: usb-audio: add Novation Launchpad support
  ALSA: hda - Add workarounds for CT-IBG controllers
  ALSA: hda - Fix wrong TLV mute bit for STAC/IDT codecs
  ASoC: tpa6130a2: Error handling for broken chip
  ASoC: max98088: Staticise m98088_eq_band
  ASoC: soc-core: Fix codec->name memory leak
  ALSA: hda - Apply ideapad quirk to Acer laptops with Cxt5066
  ALSA: hda - Add some workarounds for Creative IBG
  ALSA: hda - Fix wrong SPDIF NID assignment for CA0110
  ALSA: hda - Fix codec rename rules for ALC662-compatible codecs
  ALSA: hda - Add alc_init_jacks() call to other codecs
  ...
This commit is contained in:
Linus Torvalds 2010-10-25 08:32:05 -07:00
commit 33081adf8b
479 changed files with 37901 additions and 19477 deletions

View file

@ -604,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;

View file

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

View file

@ -364,8 +364,7 @@ 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,

View file

@ -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 */

View file

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

View file

@ -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

View file

@ -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

1258
sound/drivers/aloop.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -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) {

View file

@ -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++) {

View file

@ -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

View file

@ -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/

View file

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

View file

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

10
sound/isa/galaxy/Makefile Normal file
View file

@ -0,0 +1,10 @@
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <[email protected]>
#
snd-azt1605-objs := azt1605.o
snd-azt2316-objs := azt2316.o
obj-$(CONFIG_SND_AZT1605) += snd-azt1605.o
obj-$(CONFIG_SND_AZT2316) += snd-azt2316.o

View file

@ -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"

111
sound/isa/galaxy/azt2316.c Normal file
View file

@ -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"

652
sound/isa/galaxy/galaxy.c Normal file
View file

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

View file

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

View file

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

View file

@ -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 <[email protected]>");
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)

View file

@ -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

View file

@ -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

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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, [email protected]");
MODULE_DESCRIPTION("SH DAC sound driver");
MODULE_LICENSE("GPL");

View file

@ -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;
}
@ -399,7 +400,7 @@ static long sound_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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.

View file

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

View file

@ -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

View file

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

View file

@ -676,27 +676,64 @@ 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)
{
@ -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)

View file

@ -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;
}

View file

@ -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

View file

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

View file

@ -1216,6 +1216,7 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
struct hda_codec *c;
struct hda_cvt_setup *p;
unsigned int oldval, newval;
int type;
int i;
if (!nid)
@ -1254,10 +1255,12 @@ 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 */
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)
if (!p->active && p->stream_tag == stream_tag &&
get_wcaps_type(get_wcaps(codec, p->nid)) == type)
p->dirty = 1;
}
}
@ -1281,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) {
@ -1831,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))
@ -1841,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))
@ -2228,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;
}
@ -4372,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
*
@ -4385,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.
*/
@ -4398,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));
@ -4468,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:
@ -4539,6 +4558,8 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
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 */
@ -4549,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
@ -4602,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
*/
@ -4621,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);
@ -4636,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

View file

@ -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
*/

View file

@ -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,6 @@ 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 */
@ -645,4 +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);

View file

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

View file

@ -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);
@ -305,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 */
@ -433,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 */
@ -458,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 */
};
@ -473,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",
};
@ -563,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);
@ -1136,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);
}
@ -1309,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);
@ -1647,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);
@ -1689,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);
}
@ -1852,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;
@ -2313,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
@ -2334,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;
}
@ -2735,25 +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 },
@ -2794,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,

View file

@ -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

View file

@ -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;
int i, err, type, type_idx;
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);
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;
}
@ -4422,6 +4428,7 @@ static int patch_ad1884a(struct hda_codec *codec)
}
codec->no_trigger_sense = 1;
codec->no_sticky_stream = 1;
return 0;
}
@ -4761,6 +4768,7 @@ static int patch_ad1882(struct hda_codec *codec)
}
codec->no_trigger_sense = 1;
codec->no_sticky_stream = 1;
return 0;
}

View file

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

View file

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

View file

@ -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)
@ -1130,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",
@ -1137,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 */
};
@ -1147,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 },
@ -1176,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,
};
@ -1208,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 */

View file

@ -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,9 +117,12 @@ 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;
@ -3065,7 +3075,7 @@ 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 */
@ -3076,25 +3086,26 @@ 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, 0x02d8, "Dell Vostro", CXT5066_DELL_VOSTO),
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),
@ -3196,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;
@ -3253,6 +3264,604 @@ static int patch_cxt5066(struct hda_codec *codec)
return 0;
}
/*
* 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;
}
/*
*/
@ -3271,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 */
};
@ -3281,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");

View file

@ -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);
}
}
@ -791,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;
@ -922,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)

View file

@ -1,220 +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 struct hda_pcm_stream intel_hdmi_pcm_playback = {
.substreams = 1,
.channels_min = 2,
.ops = {
.open = hdmi_pcm_open,
.prepare = intel_hdmi_playback_pcm_prepare,
},
};
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)

View file

@ -1,608 +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_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
/*
* 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_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,
},
};
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)

File diff suppressed because it is too large Load diff

View file

@ -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;
@ -2779,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;
@ -2791,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);
}
@ -2823,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;
return 0;
}
/* check whether the mic-input can be used as line-out */
static hda_nid_t check_mic_out_switch(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
unsigned int def_conf, pincap;
unsigned int mic_pin;
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];
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) {
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;
}
if (mic_pin == AUTO_PIN_MIC)
mic_pin = AUTO_PIN_FRONT_MIC;
else
break;
}
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, hda_nid_t *dac)
{
struct sigmatel_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
unsigned int def_conf, pincap;
int i;
*dac = 0;
if (cfg->line_out_type != AUTO_PIN_LINE_OUT)
return 0;
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 (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) {
*dac = get_unassigned_dac(codec, nid);
if (*dac)
return nid;
}
}
}
return 0;
}
@ -3004,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",
@ -3204,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;
@ -3256,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);
@ -3386,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;
}
@ -3417,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];
@ -3434,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;
@ -3448,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];
@ -3482,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++;
}
}
@ -3511,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;
@ -3542,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)
@ -3576,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;
}
@ -3606,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],
@ -3623,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;
@ -4305,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);
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 < 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);
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],
@ -4383,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;
@ -4688,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;
@ -4695,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)
@ -4937,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;
}
@ -5313,11 +5337,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;
@ -5363,9 +5392,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;
}
@ -5424,36 +5457,6 @@ again:
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)
{
@ -5592,7 +5595,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;
@ -5624,7 +5627,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;
@ -5638,7 +5641,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;
@ -6320,6 +6323,8 @@ static struct hda_codec_preset snd_hda_preset_sigmatel[] = {
{ .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 */
};

View file

@ -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;
static hda_nid_t pin_idxs[] = { 0x2b, 0x2a, 0x29, 0, 0, 0xff };
int err;
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++;
}
/* build volume/mute control of loopback */
err = via_new_analog_input(spec, "Stereo Mixer", 5, 0x21);
err = vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs,
ARRAY_SIZE(pin_idxs));
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++;
/* build volume/mute control of loopback */
err = via_new_analog_input(spec, "Stereo Mixer", 0, 5, 0x21);
if (err < 0)
return err;
/* 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;

View file

@ -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 */
};

View file

@ -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[];

View file

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

View file

@ -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++) {

View file

@ -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,

View file

@ -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,

View file

@ -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;
}
}
@ -613,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);

View file

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

View file

@ -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,
};

View file

@ -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

View file

@ -25,9 +25,9 @@
#include "xonar.h"
MODULE_AUTHOR("Clemens Ladisch <[email protected]>");
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 },
{ }

View file

@ -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,

View file

@ -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;
}

View file

@ -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
@ -56,6 +65,7 @@ struct xonar_wm87x6 {
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,
@ -97,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,
@ -107,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)
@ -141,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));
@ -170,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;
@ -178,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");
}
@ -208,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,
@ -323,12 +377,27 @@ static void update_wm87x6_mute(struct oxygen *chip)
(chip->dac_mute ? WM8766_DMUTE_MASK : 0));
}
static void update_wm8766_center_lfe_mix(struct oxygen *chip, bool mixed)
{
struct xonar_wm87x6 *data = chip->model_data;
unsigned int reg;
/*
* 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)
{
u16 bits;
bits = oxygen_read16(chip, OXYGEN_GPIO_DATA);
snd_printk(KERN_INFO "HP detect: %d\n", !!(bits & GPIO_DS_HP_DETECT));
xonar_ds_handle_hp_jack(chip);
}
static int wm8776_bit_switch_get(struct snd_kcontrol *ctl,
@ -896,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",
@ -956,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;
@ -999,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,
@ -1013,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),

View file

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

View file

@ -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;
@ -5156,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);
@ -5182,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;

View file

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

View file

@ -179,7 +179,7 @@ static int atmel_pcm_hw_params(struct snd_pcm_substream *substream,
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
runtime->dma_bytes = params_buffer_bytes(params);
prtd->params = snd_soc_dai_get_dma_data(rtd->dai->cpu_dai, substream);
prtd->params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
prtd->params->dma_intr_handler = atmel_pcm_dma_irq;
prtd->dma_buffer = runtime->dma_addr;
@ -374,14 +374,14 @@ static int atmel_pcm_new(struct snd_card *card,
if (!card->dev->coherent_dma_mask)
card->dev->coherent_dma_mask = 0xffffffff;
if (dai->playback.channels_min) {
if (dai->driver->playback.channels_min) {
ret = atmel_pcm_preallocate_dma_buffer(pcm,
SNDRV_PCM_STREAM_PLAYBACK);
if (ret)
goto out;
}
if (dai->capture.channels_min) {
if (dai->driver->capture.channels_min) {
pr_debug("at32-pcm:"
"Allocating PCM capture DMA buffer\n");
ret = atmel_pcm_preallocate_dma_buffer(pcm,
@ -414,12 +414,9 @@ static void atmel_pcm_free_dma_buffers(struct snd_pcm *pcm)
}
#ifdef CONFIG_PM
static int atmel_pcm_suspend(struct snd_soc_dai_link *dai_link)
static int atmel_pcm_suspend(struct snd_soc_dai *dai)
{
struct snd_pcm *pcm = dai_link->pcm;
struct snd_pcm_str *stream = &pcm->streams[0];
struct snd_pcm_substream *substream = stream->substream;
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_pcm_runtime *runtime = dai->runtime;
struct atmel_runtime_data *prtd;
struct atmel_pcm_dma_params *params;
@ -441,12 +438,9 @@ static int atmel_pcm_suspend(struct snd_soc_dai_link *dai_link)
return 0;
}
static int atmel_pcm_resume(struct snd_soc_dai_link *dai_link)
static int atmel_pcm_resume(struct snd_soc_dai *dai)
{
struct snd_pcm *pcm = dai_link->pcm;
struct snd_pcm_str *stream = &pcm->streams[0];
struct snd_pcm_substream *substream = stream->substream;
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_pcm_runtime *runtime = dai->runtime;
struct atmel_runtime_data *prtd;
struct atmel_pcm_dma_params *params;
@ -470,27 +464,46 @@ static int atmel_pcm_resume(struct snd_soc_dai_link *dai_link)
#define atmel_pcm_resume NULL
#endif
struct snd_soc_platform atmel_soc_platform = {
.name = "atmel-audio",
.pcm_ops = &atmel_pcm_ops,
static struct snd_soc_platform_driver atmel_soc_platform = {
.ops = &atmel_pcm_ops,
.pcm_new = atmel_pcm_new,
.pcm_free = atmel_pcm_free_dma_buffers,
.suspend = atmel_pcm_suspend,
.resume = atmel_pcm_resume,
};
EXPORT_SYMBOL_GPL(atmel_soc_platform);
static int __init atmel_pcm_modinit(void)
static int __devinit atmel_soc_platform_probe(struct platform_device *pdev)
{
return snd_soc_register_platform(&atmel_soc_platform);
return snd_soc_register_platform(&pdev->dev, &atmel_soc_platform);
}
module_init(atmel_pcm_modinit);
static void __exit atmel_pcm_modexit(void)
static int __devexit atmel_soc_platform_remove(struct platform_device *pdev)
{
snd_soc_unregister_platform(&atmel_soc_platform);
snd_soc_unregister_platform(&pdev->dev);
return 0;
}
module_exit(atmel_pcm_modexit);
static struct platform_driver atmel_pcm_driver = {
.driver = {
.name = "atmel-pcm-audio",
.owner = THIS_MODULE,
},
.probe = atmel_soc_platform_probe,
.remove = __devexit_p(atmel_soc_platform_remove),
};
static int __init snd_atmel_pcm_init(void)
{
return platform_driver_register(&atmel_pcm_driver);
}
module_init(snd_atmel_pcm_init);
static void __exit snd_atmel_pcm_exit(void)
{
platform_driver_unregister(&atmel_pcm_driver);
}
module_exit(snd_atmel_pcm_exit);
MODULE_AUTHOR("Sedji Gaouaou <[email protected]>");
MODULE_DESCRIPTION("Atmel PCM module");

View file

@ -74,9 +74,6 @@ struct atmel_pcm_dma_params {
void (*dma_intr_handler)(u32, struct snd_pcm_substream *);
};
extern struct snd_soc_platform atmel_soc_platform;
/*
* SSC register access (since ssc_writel() / ssc_readl() require literal name)
*/

View file

@ -205,8 +205,7 @@ static irqreturn_t atmel_ssc_interrupt(int irq, void *dev_id)
static int atmel_ssc_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
struct atmel_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
struct atmel_ssc_info *ssc_p = &ssc_info[dai->id];
int dir_mask;
pr_debug("atmel_ssc_startup: SSC_SR=0x%u\n",
@ -235,8 +234,7 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream,
static void atmel_ssc_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
struct atmel_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
struct atmel_ssc_info *ssc_p = &ssc_info[dai->id];
struct atmel_pcm_dma_params *dma_params;
int dir, dir_mask;
@ -338,7 +336,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
int id = rtd->dai->cpu_dai->id;
int id = dai->id;
struct atmel_ssc_info *ssc_p = &ssc_info[id];
struct atmel_pcm_dma_params *dma_params;
int dir, channels, bits;
@ -368,7 +366,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
* function. It should not be used for other purposes
* as it is common to all substreams.
*/
snd_soc_dai_set_dma_data(rtd->dai->cpu_dai, substream, dma_params);
snd_soc_dai_set_dma_data(rtd->cpu_dai, substream, dma_params);
channels = params_channels(params);
@ -605,8 +603,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
static int atmel_ssc_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
struct atmel_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
struct atmel_ssc_info *ssc_p = &ssc_info[dai->id];
struct atmel_pcm_dma_params *dma_params;
int dir;
@ -690,6 +687,32 @@ static int atmel_ssc_resume(struct snd_soc_dai *cpu_dai)
# define atmel_ssc_resume NULL
#endif /* CONFIG_PM */
static int atmel_ssc_probe(struct snd_soc_dai *dai)
{
struct atmel_ssc_info *ssc_p = &ssc_info[dai->id];
int ret = 0;
snd_soc_dai_set_drvdata(dai, ssc_p);
/*
* Request SSC device
*/
ssc_p->ssc = ssc_request(dai->id);
if (IS_ERR(ssc_p->ssc)) {
printk(KERN_ERR "ASoC: Failed to request SSC %d\n", dai->id);
ret = PTR_ERR(ssc_p->ssc);
}
return ret;
}
static int atmel_ssc_remove(struct snd_soc_dai *dai)
{
struct atmel_ssc_info *ssc_p = snd_soc_dai_get_drvdata(dai);
ssc_free(ssc_p->ssc);
return 0;
}
#define ATMEL_SSC_RATES (SNDRV_PCM_RATE_8000_96000)
@ -705,9 +728,11 @@ static struct snd_soc_dai_ops atmel_ssc_dai_ops = {
.set_clkdiv = atmel_ssc_set_dai_clkdiv,
};
struct snd_soc_dai atmel_ssc_dai[NUM_SSC_DEVICES] = {
{ .name = "atmel-ssc0",
.id = 0,
static struct snd_soc_dai_driver atmel_ssc_dai[NUM_SSC_DEVICES] = {
{
.name = "atmel-ssc-dai.0",
.probe = atmel_ssc_probe,
.remove = atmel_ssc_remove,
.suspend = atmel_ssc_suspend,
.resume = atmel_ssc_resume,
.playback = {
@ -721,11 +746,12 @@ struct snd_soc_dai atmel_ssc_dai[NUM_SSC_DEVICES] = {
.rates = ATMEL_SSC_RATES,
.formats = ATMEL_SSC_FORMATS,},
.ops = &atmel_ssc_dai_ops,
.private_data = &ssc_info[0],
},
#if NUM_SSC_DEVICES == 3
{ .name = "atmel-ssc1",
.id = 1,
{
.name = "atmel-ssc-dai.1",
.probe = atmel_ssc_probe,
.remove = atmel_ssc_remove,
.suspend = atmel_ssc_suspend,
.resume = atmel_ssc_resume,
.playback = {
@ -739,10 +765,11 @@ struct snd_soc_dai atmel_ssc_dai[NUM_SSC_DEVICES] = {
.rates = ATMEL_SSC_RATES,
.formats = ATMEL_SSC_FORMATS,},
.ops = &atmel_ssc_dai_ops,
.private_data = &ssc_info[1],
},
{ .name = "atmel-ssc2",
.id = 2,
{
.name = "atmel-ssc-dai.2",
.probe = atmel_ssc_probe,
.remove = atmel_ssc_remove,
.suspend = atmel_ssc_suspend,
.resume = atmel_ssc_resume,
.playback = {
@ -756,23 +783,94 @@ struct snd_soc_dai atmel_ssc_dai[NUM_SSC_DEVICES] = {
.rates = ATMEL_SSC_RATES,
.formats = ATMEL_SSC_FORMATS,},
.ops = &atmel_ssc_dai_ops,
.private_data = &ssc_info[2],
},
#endif
};
EXPORT_SYMBOL_GPL(atmel_ssc_dai);
static int __init atmel_ssc_modinit(void)
static __devinit int asoc_ssc_probe(struct platform_device *pdev)
{
return snd_soc_register_dais(atmel_ssc_dai, ARRAY_SIZE(atmel_ssc_dai));
BUG_ON(pdev->id < 0);
BUG_ON(pdev->id >= ARRAY_SIZE(atmel_ssc_dai));
return snd_soc_register_dai(&pdev->dev, &atmel_ssc_dai[pdev->id]);
}
module_init(atmel_ssc_modinit);
static void __exit atmel_ssc_modexit(void)
static int __devexit asoc_ssc_remove(struct platform_device *pdev)
{
snd_soc_unregister_dais(atmel_ssc_dai, ARRAY_SIZE(atmel_ssc_dai));
snd_soc_unregister_dai(&pdev->dev);
return 0;
}
module_exit(atmel_ssc_modexit);
static struct platform_driver asoc_ssc_driver = {
.driver = {
.name = "atmel-ssc-dai",
.owner = THIS_MODULE,
},
.probe = asoc_ssc_probe,
.remove = __devexit_p(asoc_ssc_remove),
};
/**
* atmel_ssc_set_audio - Allocate the specified SSC for audio use.
*/
int atmel_ssc_set_audio(int ssc_id)
{
struct ssc_device *ssc;
static struct platform_device *dma_pdev;
struct platform_device *ssc_pdev;
int ret;
if (ssc_id < 0 || ssc_id >= ARRAY_SIZE(atmel_ssc_dai))
return -EINVAL;
/* Allocate a dummy device for DMA if we don't have one already */
if (!dma_pdev) {
dma_pdev = platform_device_alloc("atmel-pcm-audio", -1);
if (!dma_pdev)
return -ENOMEM;
ret = platform_device_add(dma_pdev);
if (ret < 0) {
platform_device_put(dma_pdev);
dma_pdev = NULL;
return ret;
}
}
ssc_pdev = platform_device_alloc("atmel-ssc-dai", ssc_id);
if (!ssc_pdev) {
ssc_free(ssc);
return -ENOMEM;
}
/* If we can grab the SSC briefly to parent the DAI device off it */
ssc = ssc_request(ssc_id);
if (IS_ERR(ssc))
pr_warn("Unable to parent ASoC SSC DAI on SSC: %ld\n",
PTR_ERR(ssc));
else
ssc_pdev->dev.parent = &(ssc->pdev->dev);
ssc_free(ssc);
ret = platform_device_add(ssc_pdev);
if (ret < 0)
platform_device_put(ssc_pdev);
return ret;
}
EXPORT_SYMBOL_GPL(atmel_ssc_set_audio);
static int __init snd_atmel_ssc_init(void)
{
return platform_driver_register(&asoc_ssc_driver);
}
module_init(snd_atmel_ssc_init);
static void __exit snd_atmel_ssc_exit(void)
{
platform_driver_unregister(&asoc_ssc_driver);
}
module_exit(snd_atmel_ssc_exit);
/* Module information */
MODULE_AUTHOR("Sedji Gaouaou, [email protected], www.atmel.com");

View file

@ -116,6 +116,7 @@ struct atmel_ssc_info {
struct atmel_pcm_dma_params *dma_params[2];
struct atmel_ssc_state ssc_state;
};
extern struct snd_soc_dai atmel_ssc_dai[];
int atmel_ssc_set_audio(int ssc);
#endif /* _AT91_SSC_DAI_H */

View file

@ -83,7 +83,7 @@ static struct ssc_clock_data playpaq_wm8510_calc_ssc_clock(
struct snd_pcm_hw_params *params,
struct snd_soc_dai *cpu_dai)
{
struct at32_ssc_info *ssc_p = cpu_dai->private_data;
struct at32_ssc_info *ssc_p = snd_soc_dai_get_drvdata(cpu_dai);
struct ssc_device *ssc = ssc_p->ssc;
struct ssc_clock_data cd;
unsigned int rate, width_bits, channels;
@ -131,9 +131,9 @@ static int playpaq_wm8510_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
struct at32_ssc_info *ssc_p = cpu_dai->private_data;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct at32_ssc_info *ssc_p = snd_soc_dai_get_drvdata(cpu_dai);
struct ssc_device *ssc = ssc_p->ssc;
unsigned int pll_out = 0, bclk = 0, mclk_div = 0;
int ret;
@ -315,8 +315,9 @@ static const struct snd_soc_dapm_route intercon[] = {
static int playpaq_wm8510_init(struct snd_soc_codec *codec)
static int playpaq_wm8510_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_codec *codec = rtd->codec;
int i;
/*
@ -342,7 +343,7 @@ static int playpaq_wm8510_init(struct snd_soc_codec *codec)
/* Make CSB show PLL rate */
snd_soc_dai_set_clkdiv(codec->dai, WM8510_OPCLKDIV,
snd_soc_dai_set_clkdiv(rtd->codec_dai, WM8510_OPCLKDIV,
WM8510_OPCLKDIV_1 | 4);
return 0;
@ -353,8 +354,10 @@ static int playpaq_wm8510_init(struct snd_soc_codec *codec)
static struct snd_soc_dai_link playpaq_wm8510_dai = {
.name = "WM8510",
.stream_name = "WM8510 PCM",
.cpu_dai = &at32_ssc_dai[0],
.codec_dai = &wm8510_dai,
.cpu_dai_name= "atmel-ssc-dai.0",
.platform_name = "atmel-pcm-audio",
.codec_name = "wm8510-codec.0-0x1a",
.codec_dai_name = "wm8510-hifi",
.init = playpaq_wm8510_init,
.ops = &playpaq_wm8510_ops,
};
@ -363,46 +366,16 @@ static struct snd_soc_dai_link playpaq_wm8510_dai = {
static struct snd_soc_card snd_soc_playpaq = {
.name = "LRS_PlayPaq_WM8510",
.platform = &at32_soc_platform,
.dai_link = &playpaq_wm8510_dai,
.num_links = 1,
};
static struct wm8510_setup_data playpaq_wm8510_setup = {
.i2c_bus = 0,
.i2c_address = 0x1a,
};
static struct snd_soc_device playpaq_wm8510_snd_devdata = {
.card = &snd_soc_playpaq,
.codec_dev = &soc_codec_dev_wm8510,
.codec_data = &playpaq_wm8510_setup,
};
static struct platform_device *playpaq_snd_device;
static int __init playpaq_asoc_init(void)
{
int ret = 0;
struct at32_ssc_info *ssc_p = playpaq_wm8510_dai.cpu_dai->private_data;
struct ssc_device *ssc = NULL;
/*
* Request SSC device
*/
ssc = ssc_request(0);
if (IS_ERR(ssc)) {
ret = PTR_ERR(ssc);
goto err_ssc;
}
ssc_p->ssc = ssc;
/*
* Configure MCLK for WM8510
@ -439,8 +412,7 @@ static int __init playpaq_asoc_init(void)
goto err_device_alloc;
}
platform_set_drvdata(playpaq_snd_device, &playpaq_wm8510_snd_devdata);
playpaq_wm8510_snd_devdata.dev = &playpaq_snd_device->dev;
platform_set_drvdata(playpaq_snd_device, &snd_soc_playpaq);
ret = platform_device_add(playpaq_snd_device);
if (ret) {
@ -468,25 +440,12 @@ err_pll0:
clk_put(_gclk0);
_gclk0 = NULL;
}
err_gclk0:
ssc_free(ssc);
err_ssc:
return ret;
}
static void __exit playpaq_asoc_exit(void)
{
struct at32_ssc_info *ssc_p = playpaq_wm8510_dai.cpu_dai->private_data;
struct ssc_device *ssc;
if (ssc_p != NULL) {
ssc = ssc_p->ssc;
if (ssc != NULL)
ssc_free(ssc);
ssc_p->ssc = NULL;
}
if (_gclk0 != NULL) {
clk_put(_gclk0);
_gclk0 = NULL;

View file

@ -69,8 +69,8 @@ static int at91sam9g20ek_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
int ret;
/* set codec DAI configuration */
@ -136,16 +136,17 @@ static const struct snd_soc_dapm_route intercon[] = {
/*
* Logic for a wm8731 as connected on a at91sam9g20ek board.
*/
static int at91sam9g20ek_wm8731_init(struct snd_soc_codec *codec)
static int at91sam9g20ek_wm8731_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_dai *codec_dai = &codec->dai[0];
struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
int ret;
printk(KERN_DEBUG
"at91sam9g20ek_wm8731 "
": at91sam9g20ek_wm8731_init() called\n");
ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK,
ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_XTAL,
MCLK_RATE, SND_SOC_CLOCK_IN);
if (ret < 0) {
printk(KERN_ERR "Failed to set WM8731 SYSCLK: %d\n", ret);
@ -179,37 +180,37 @@ static int at91sam9g20ek_wm8731_init(struct snd_soc_codec *codec)
static struct snd_soc_dai_link at91sam9g20ek_dai = {
.name = "WM8731",
.stream_name = "WM8731 PCM",
.cpu_dai = &atmel_ssc_dai[0],
.codec_dai = &wm8731_dai,
.cpu_dai_name = "atmel-ssc-dai.0",
.codec_dai_name = "wm8731-hifi",
.init = at91sam9g20ek_wm8731_init,
.platform_name = "atmel-pcm-audio",
.codec_name = "wm8731-codec.0-001b",
.ops = &at91sam9g20ek_ops,
};
static struct snd_soc_card snd_soc_at91sam9g20ek = {
.name = "AT91SAMG20-EK",
.platform = &atmel_soc_platform,
.dai_link = &at91sam9g20ek_dai,
.num_links = 1,
.set_bias_level = at91sam9g20ek_set_bias_level,
};
static struct snd_soc_device at91sam9g20ek_snd_devdata = {
.card = &snd_soc_at91sam9g20ek,
.codec_dev = &soc_codec_dev_wm8731,
};
static struct platform_device *at91sam9g20ek_snd_device;
static int __init at91sam9g20ek_init(void)
{
struct atmel_ssc_info *ssc_p = at91sam9g20ek_dai.cpu_dai->private_data;
struct ssc_device *ssc = NULL;
struct clk *pllb;
int ret;
if (!(machine_is_at91sam9g20ek() || machine_is_at91sam9g20ek_2mmc()))
return -ENODEV;
ret = atmel_ssc_set_audio(0);
if (ret != 0) {
pr_err("Failed to set SSC 0 for audio: %d\n", ret);
return ret;
}
/*
* Codec MCLK is supplied by PCK0 - set it up.
*/
@ -235,18 +236,6 @@ static int __init at91sam9g20ek_init(void)
clk_set_rate(mclk, MCLK_RATE);
/*
* Request SSC device
*/
ssc = ssc_request(0);
if (IS_ERR(ssc)) {
printk(KERN_ERR "ASoC: Failed to request SSC 0\n");
ret = PTR_ERR(ssc);
ssc = NULL;
goto err_ssc;
}
ssc_p->ssc = ssc;
at91sam9g20ek_snd_device = platform_device_alloc("soc-audio", -1);
if (!at91sam9g20ek_snd_device) {
printk(KERN_ERR "ASoC: Platform device allocation failed\n");
@ -254,8 +243,7 @@ static int __init at91sam9g20ek_init(void)
}
platform_set_drvdata(at91sam9g20ek_snd_device,
&at91sam9g20ek_snd_devdata);
at91sam9g20ek_snd_devdata.dev = &at91sam9g20ek_snd_device->dev;
&snd_soc_at91sam9g20ek);
ret = platform_device_add(at91sam9g20ek_snd_device);
if (ret) {
@ -265,9 +253,6 @@ static int __init at91sam9g20ek_init(void)
return ret;
err_ssc:
ssc_free(ssc);
ssc_p->ssc = NULL;
err_mclk:
clk_put(mclk);
mclk = NULL;
@ -277,16 +262,6 @@ err:
static void __exit at91sam9g20ek_exit(void)
{
struct atmel_ssc_info *ssc_p = at91sam9g20ek_dai.cpu_dai->private_data;
struct ssc_device *ssc;
if (ssc_p != NULL) {
ssc = ssc_p->ssc;
if (ssc != NULL)
ssc_free(ssc);
ssc_p->ssc = NULL;
}
platform_device_unregister(at91sam9g20ek_snd_device);
at91sam9g20ek_snd_device = NULL;
clk_put(mclk);

View file

@ -46,8 +46,8 @@ static int afeb9260_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
int err;
/* Set codec DAI configuration */
@ -102,8 +102,9 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"MICIN", NULL, "Mic Jack"},
};
static int afeb9260_tlv320aic23_init(struct snd_soc_codec *codec)
static int afeb9260_tlv320aic23_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_codec *codec = rtd->codec;
/* Add afeb9260 specific widgets */
snd_soc_dapm_new_controls(codec, tlv320aic23_dapm_widgets,
@ -125,8 +126,10 @@ static int afeb9260_tlv320aic23_init(struct snd_soc_codec *codec)
static struct snd_soc_dai_link afeb9260_dai = {
.name = "TLV320AIC23",
.stream_name = "AIC23",
.cpu_dai = &atmel_ssc_dai[0],
.codec_dai = &tlv320aic23_dai,
.cpu_dai_name = "atmel-ssc-dai.0",
.codec_dai_name = "tlv320aic23-hifi",
.platform_name = "atmel_pcm-audio",
.codec_name = "tlv320aic23-codec.0-0x1a",
.init = afeb9260_tlv320aic23_init,
.ops = &afeb9260_ops,
};
@ -134,37 +137,20 @@ static struct snd_soc_dai_link afeb9260_dai = {
/* Audio machine driver */
static struct snd_soc_card snd_soc_machine_afeb9260 = {
.name = "AFEB9260",
.platform = &atmel_soc_platform,
.dai_link = &afeb9260_dai,
.num_links = 1,
};
/* Audio subsystem */
static struct snd_soc_device afeb9260_snd_devdata = {
.card = &snd_soc_machine_afeb9260,
.codec_dev = &soc_codec_dev_tlv320aic23,
};
static struct platform_device *afeb9260_snd_device;
static int __init afeb9260_soc_init(void)
{
int err;
struct device *dev;
struct atmel_ssc_info *ssc_p = afeb9260_dai.cpu_dai->private_data;
struct ssc_device *ssc = NULL;
if (!(machine_is_afeb9260()))
return -ENODEV;
ssc = ssc_request(0);
if (IS_ERR(ssc)) {
printk(KERN_ERR "ASoC: Failed to request SSC 0\n");
err = PTR_ERR(ssc);
ssc = NULL;
goto err_ssc;
}
ssc_p->ssc = ssc;
afeb9260_snd_device = platform_device_alloc("soc-audio", -1);
if (!afeb9260_snd_device) {
@ -172,8 +158,7 @@ static int __init afeb9260_soc_init(void)
return -ENOMEM;
}
platform_set_drvdata(afeb9260_snd_device, &afeb9260_snd_devdata);
afeb9260_snd_devdata.dev = &afeb9260_snd_device->dev;
platform_set_drvdata(afeb9260_snd_device, &snd_soc_machine_afeb9260);
err = platform_device_add(afeb9260_snd_device);
if (err)
goto err1;
@ -184,9 +169,7 @@ static int __init afeb9260_soc_init(void)
err1:
platform_device_del(afeb9260_snd_device);
platform_device_put(afeb9260_snd_device);
err_ssc:
return err;
}
static void __exit afeb9260_soc_exit(void)

View file

@ -19,7 +19,6 @@
#include <asm/mach-au1x00/au1xxx_dbdma.h>
#include <asm/mach-db1x00/bcsr.h>
#include "../codecs/ac97.h"
#include "../codecs/wm8731.h"
#include "psc.h"
@ -28,20 +27,16 @@
static struct snd_soc_dai_link db1200_ac97_dai = {
.name = "AC97",
.stream_name = "AC97 HiFi",
.cpu_dai = &au1xpsc_ac97_dai,
.codec_dai = &ac97_dai,
.codec_dai_name = "ac97-hifi",
.cpu_dai_name = "au1xpsc_ac97.1",
.platform_name = "au1xpsc-pcm.1",
.codec_name = "ac97-codec.1",
};
static struct snd_soc_card db1200_ac97_machine = {
.name = "DB1200_AC97",
.dai_link = &db1200_ac97_dai,
.num_links = 1,
.platform = &au1xpsc_soc_platform,
};
static struct snd_soc_device db1200_ac97_devdata = {
.card = &db1200_ac97_machine,
.codec_dev = &soc_codec_dev_ac97,
};
/*------------------------- I2S PART ---------------------------*/
@ -49,12 +44,12 @@ static struct snd_soc_device db1200_ac97_devdata = {
static int db1200_i2s_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
int ret;
/* WM8731 has its own 12MHz crystal */
snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK,
snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_XTAL,
12000000, SND_SOC_CLOCK_IN);
/* codec is bitclock and lrclk master */
@ -80,8 +75,10 @@ static struct snd_soc_ops db1200_i2s_wm8731_ops = {
static struct snd_soc_dai_link db1200_i2s_dai = {
.name = "WM8731",
.stream_name = "WM8731 PCM",
.cpu_dai = &au1xpsc_i2s_dai,
.codec_dai = &wm8731_dai,
.codec_dai_name = "wm8731-hifi",
.cpu_dai_name = "au1xpsc_i2s.1",
.platform_name = "au1xpsc-pcm.1",
.codec_name = "wm8731-codec.0-001b",
.ops = &db1200_i2s_wm8731_ops,
};
@ -89,12 +86,6 @@ static struct snd_soc_card db1200_i2s_machine = {
.name = "DB1200_I2S",
.dai_link = &db1200_i2s_dai,
.num_links = 1,
.platform = &au1xpsc_soc_platform,
};
static struct snd_soc_device db1200_i2s_devdata = {
.card = &db1200_i2s_machine,
.codec_dev = &soc_codec_dev_wm8731,
};
/*------------------------- COMMON PART ---------------------------*/
@ -106,18 +97,16 @@ static int __init db1200_audio_load(void)
int ret;
ret = -ENOMEM;
db1200_asoc_dev = platform_device_alloc("soc-audio", -1);
db1200_asoc_dev = platform_device_alloc("soc-audio", 1); /* PSC1 */
if (!db1200_asoc_dev)
goto out;
/* DB1200 board setup set PSC1MUX to preferred audio device */
if (bcsr_read(BCSR_RESETS) & BCSR_RESETS_PSC1MUX)
platform_set_drvdata(db1200_asoc_dev, &db1200_i2s_devdata);
platform_set_drvdata(db1200_asoc_dev, &db1200_i2s_machine);
else
platform_set_drvdata(db1200_asoc_dev, &db1200_ac97_devdata);
platform_set_drvdata(db1200_asoc_dev, &db1200_ac97_machine);
db1200_ac97_devdata.dev = &db1200_asoc_dev->dev;
db1200_i2s_devdata.dev = &db1200_asoc_dev->dev;
ret = platform_device_add(db1200_asoc_dev);
if (ret) {

View file

@ -10,9 +10,6 @@
*
* DMA glue for Au1x-PSC audio.
*
* NOTE: all of these drivers can only work with a SINGLE instance
* of a PSC. Multiple independent audio devices are impossible
* with ASoC v1.
*/
@ -61,9 +58,6 @@ struct au1xpsc_audio_dmadata {
int msbits;
};
/* instance data. There can be only one, MacLeod!!!! */
static struct au1xpsc_audio_dmadata *au1xpsc_audio_pcmdma[2];
/*
* These settings are somewhat okay, at least on my machine audio plays
* almost skip-free. Especially the 64kB buffer seems to help a LOT.
@ -199,6 +193,14 @@ out:
return 0;
}
static inline struct au1xpsc_audio_dmadata *to_dmadata(struct snd_pcm_substream *ss)
{
struct snd_soc_pcm_runtime *rtd = ss->private_data;
struct au1xpsc_audio_dmadata *pcd =
snd_soc_platform_get_drvdata(rtd->platform);
return &pcd[SUBSTREAM_TYPE(ss)];
}
static int au1xpsc_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
@ -211,7 +213,7 @@ static int au1xpsc_pcm_hw_params(struct snd_pcm_substream *substream,
goto out;
stype = SUBSTREAM_TYPE(substream);
pcd = au1xpsc_audio_pcmdma[stype];
pcd = to_dmadata(substream);
DBG("runtime->dma_area = 0x%08lx dma_addr_t = 0x%08lx dma_size = %d "
"runtime->min_align %d\n",
@ -249,8 +251,7 @@ static int au1xpsc_pcm_hw_free(struct snd_pcm_substream *substream)
static int au1xpsc_pcm_prepare(struct snd_pcm_substream *substream)
{
struct au1xpsc_audio_dmadata *pcd =
au1xpsc_audio_pcmdma[SUBSTREAM_TYPE(substream)];
struct au1xpsc_audio_dmadata *pcd = to_dmadata(substream);
au1xxx_dbdma_reset(pcd->ddma_chan);
@ -267,7 +268,7 @@ static int au1xpsc_pcm_prepare(struct snd_pcm_substream *substream)
static int au1xpsc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
u32 c = au1xpsc_audio_pcmdma[SUBSTREAM_TYPE(substream)]->ddma_chan;
u32 c = to_dmadata(substream)->ddma_chan;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@ -287,8 +288,7 @@ static int au1xpsc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
static snd_pcm_uframes_t
au1xpsc_pcm_pointer(struct snd_pcm_substream *substream)
{
return bytes_to_frames(substream->runtime,
au1xpsc_audio_pcmdma[SUBSTREAM_TYPE(substream)]->pos);
return bytes_to_frames(substream->runtime, to_dmadata(substream)->pos);
}
static int au1xpsc_pcm_open(struct snd_pcm_substream *substream)
@ -299,7 +299,7 @@ static int au1xpsc_pcm_open(struct snd_pcm_substream *substream)
static int au1xpsc_pcm_close(struct snd_pcm_substream *substream)
{
au1x_pcm_dbdma_free(au1xpsc_audio_pcmdma[SUBSTREAM_TYPE(substream)]);
au1x_pcm_dbdma_free(to_dmadata(substream));
return 0;
}
@ -329,42 +329,21 @@ static int au1xpsc_pcm_new(struct snd_card *card,
return 0;
}
static int au1xpsc_pcm_probe(struct platform_device *pdev)
{
if (!au1xpsc_audio_pcmdma[PCM_TX] || !au1xpsc_audio_pcmdma[PCM_RX])
return -ENODEV;
return 0;
}
static int au1xpsc_pcm_remove(struct platform_device *pdev)
{
return 0;
}
/* au1xpsc audio platform */
struct snd_soc_platform au1xpsc_soc_platform = {
.name = "au1xpsc-pcm-dbdma",
.probe = au1xpsc_pcm_probe,
.remove = au1xpsc_pcm_remove,
.pcm_ops = &au1xpsc_pcm_ops,
struct snd_soc_platform_driver au1xpsc_soc_platform = {
.ops = &au1xpsc_pcm_ops,
.pcm_new = au1xpsc_pcm_new,
.pcm_free = au1xpsc_pcm_free_dma_buffers,
};
EXPORT_SYMBOL_GPL(au1xpsc_soc_platform);
static int __devinit au1xpsc_pcm_drvprobe(struct platform_device *pdev)
{
struct au1xpsc_audio_dmadata *dmadata;
struct resource *r;
int ret;
if (au1xpsc_audio_pcmdma[PCM_TX] || au1xpsc_audio_pcmdma[PCM_RX])
return -EBUSY;
/* TX DMA */
au1xpsc_audio_pcmdma[PCM_TX]
= kzalloc(sizeof(struct au1xpsc_audio_dmadata), GFP_KERNEL);
if (!au1xpsc_audio_pcmdma[PCM_TX])
dmadata = kzalloc(2 * sizeof(struct au1xpsc_audio_dmadata), GFP_KERNEL);
if (!dmadata)
return -ENOMEM;
r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
@ -372,47 +351,33 @@ static int __devinit au1xpsc_pcm_drvprobe(struct platform_device *pdev)
ret = -ENODEV;
goto out1;
}
(au1xpsc_audio_pcmdma[PCM_TX])->ddma_id = r->start;
dmadata[PCM_TX].ddma_id = r->start;
/* RX DMA */
au1xpsc_audio_pcmdma[PCM_RX]
= kzalloc(sizeof(struct au1xpsc_audio_dmadata), GFP_KERNEL);
if (!au1xpsc_audio_pcmdma[PCM_RX])
return -ENOMEM;
r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
if (!r) {
ret = -ENODEV;
goto out2;
goto out1;
}
(au1xpsc_audio_pcmdma[PCM_RX])->ddma_id = r->start;
dmadata[PCM_RX].ddma_id = r->start;
ret = snd_soc_register_platform(&au1xpsc_soc_platform);
platform_set_drvdata(pdev, dmadata);
ret = snd_soc_register_platform(&pdev->dev, &au1xpsc_soc_platform);
if (!ret)
return ret;
out2:
kfree(au1xpsc_audio_pcmdma[PCM_RX]);
au1xpsc_audio_pcmdma[PCM_RX] = NULL;
out1:
kfree(au1xpsc_audio_pcmdma[PCM_TX]);
au1xpsc_audio_pcmdma[PCM_TX] = NULL;
kfree(dmadata);
return ret;
}
static int __devexit au1xpsc_pcm_drvremove(struct platform_device *pdev)
{
int i;
struct au1xpsc_audio_dmadata *dmadata = platform_get_drvdata(pdev);
snd_soc_unregister_platform(&au1xpsc_soc_platform);
for (i = 0; i < 2; i++) {
if (au1xpsc_audio_pcmdma[i]) {
au1x_pcm_dbdma_free(au1xpsc_audio_pcmdma[i]);
kfree(au1xpsc_audio_pcmdma[i]);
au1xpsc_audio_pcmdma[i] = NULL;
}
}
snd_soc_unregister_platform(&pdev->dev);
kfree(dmadata);
return 0;
}
@ -428,8 +393,6 @@ static struct platform_driver au1xpsc_pcm_driver = {
static int __init au1xpsc_audio_dbdma_load(void)
{
au1xpsc_audio_pcmdma[PCM_TX] = NULL;
au1xpsc_audio_pcmdma[PCM_RX] = NULL;
return platform_driver_register(&au1xpsc_pcm_driver);
}
@ -467,7 +430,7 @@ struct platform_device *au1xpsc_pcm_add(struct platform_device *pdev)
res[1].start = res[1].end = id[1];
res[0].flags = res[1].flags = IORESOURCE_DMA;
pd = platform_device_alloc("au1xpsc-pcm", -1);
pd = platform_device_alloc("au1xpsc-pcm", pdev->id);
if (!pd)
goto out;

View file

@ -10,9 +10,6 @@
*
* Au1xxx-PSC AC97 glue.
*
* NOTE: all of these drivers can only work with a SINGLE instance
* of a PSC. Multiple independent audio devices are impossible
* with ASoC v1.
*/
#include <linux/init.h>
@ -56,12 +53,29 @@
/* instance data. There can be only one, MacLeod!!!! */
static struct au1xpsc_audio_data *au1xpsc_ac97_workdata;
#if 0
/* this could theoretically work, but ac97->bus->card->private_data can be NULL
* when snd_ac97_mixer() is called; I don't know if the rest further down the
* chain are always valid either.
*/
static inline struct au1xpsc_audio_data *ac97_to_pscdata(struct snd_ac97 *x)
{
struct snd_soc_card *c = x->bus->card->private_data;
return snd_soc_dai_get_drvdata(c->rtd->cpu_dai);
}
#else
#define ac97_to_pscdata(x) au1xpsc_ac97_workdata
#endif
/* AC97 controller reads codec register */
static unsigned short au1xpsc_ac97_read(struct snd_ac97 *ac97,
unsigned short reg)
{
/* FIXME */
struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
struct au1xpsc_audio_data *pscdata = ac97_to_pscdata(ac97);
unsigned short retry, tmo;
unsigned long data;
@ -102,8 +116,7 @@ static unsigned short au1xpsc_ac97_read(struct snd_ac97 *ac97,
static void au1xpsc_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
unsigned short val)
{
/* FIXME */
struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
struct au1xpsc_audio_data *pscdata = ac97_to_pscdata(ac97);
unsigned int tmo, retry;
au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
@ -134,8 +147,7 @@ static void au1xpsc_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
/* AC97 controller asserts a warm reset */
static void au1xpsc_ac97_warm_reset(struct snd_ac97 *ac97)
{
/* FIXME */
struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
struct au1xpsc_audio_data *pscdata = ac97_to_pscdata(ac97);
au_writel(PSC_AC97RST_SNC, AC97_RST(pscdata));
au_sync();
@ -146,8 +158,7 @@ static void au1xpsc_ac97_warm_reset(struct snd_ac97 *ac97)
static void au1xpsc_ac97_cold_reset(struct snd_ac97 *ac97)
{
/* FIXME */
struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
struct au1xpsc_audio_data *pscdata = ac97_to_pscdata(ac97);
int i;
/* disable PSC during cold reset */
@ -202,8 +213,7 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
/* FIXME */
struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(dai);
unsigned long r, ro, stat;
int chans, t, stype = SUBSTREAM_TYPE(substream);
@ -283,8 +293,7 @@ out:
static int au1xpsc_ac97_trigger(struct snd_pcm_substream *substream,
int cmd, struct snd_soc_dai *dai)
{
/* FIXME */
struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(dai);
int ret, stype = SUBSTREAM_TYPE(substream);
ret = 0;
@ -315,27 +324,19 @@ static int au1xpsc_ac97_trigger(struct snd_pcm_substream *substream,
return ret;
}
static int au1xpsc_ac97_probe(struct platform_device *pdev,
struct snd_soc_dai *dai)
static int au1xpsc_ac97_probe(struct snd_soc_dai *dai)
{
return au1xpsc_ac97_workdata ? 0 : -ENODEV;
}
static void au1xpsc_ac97_remove(struct platform_device *pdev,
struct snd_soc_dai *dai)
{
}
static struct snd_soc_dai_ops au1xpsc_ac97_dai_ops = {
.trigger = au1xpsc_ac97_trigger,
.hw_params = au1xpsc_ac97_hw_params,
};
struct snd_soc_dai au1xpsc_ac97_dai = {
.name = "au1xpsc_ac97",
static const struct snd_soc_dai_driver au1xpsc_ac97_dai_template = {
.ac97_control = 1,
.probe = au1xpsc_ac97_probe,
.remove = au1xpsc_ac97_remove,
.playback = {
.rates = AC97_RATES,
.formats = AC97_FMTS,
@ -350,7 +351,6 @@ struct snd_soc_dai au1xpsc_ac97_dai = {
},
.ops = &au1xpsc_ac97_dai_ops,
};
EXPORT_SYMBOL_GPL(au1xpsc_ac97_dai);
static int __devinit au1xpsc_ac97_drvprobe(struct platform_device *pdev)
{
@ -359,9 +359,6 @@ static int __devinit au1xpsc_ac97_drvprobe(struct platform_device *pdev)
unsigned long sel;
struct au1xpsc_audio_data *wd;
if (au1xpsc_ac97_workdata)
return -EBUSY;
wd = kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL);
if (!wd)
return -ENOMEM;
@ -395,18 +392,24 @@ static int __devinit au1xpsc_ac97_drvprobe(struct platform_device *pdev)
au_writel(PSC_SEL_PS_AC97MODE | sel, PSC_SEL(wd));
au_sync();
ret = snd_soc_register_dai(&au1xpsc_ac97_dai);
/* name the DAI like this device instance ("au1xpsc-ac97.PSCINDEX") */
memcpy(&wd->dai_drv, &au1xpsc_ac97_dai_template,
sizeof(struct snd_soc_dai_driver));
wd->dai_drv.name = dev_name(&pdev->dev);
platform_set_drvdata(pdev, wd);
ret = snd_soc_register_dai(&pdev->dev, &wd->dai_drv);
if (ret)
goto out1;
wd->dmapd = au1xpsc_pcm_add(pdev);
if (wd->dmapd) {
platform_set_drvdata(pdev, wd);
au1xpsc_ac97_workdata = wd; /* MDEV */
au1xpsc_ac97_workdata = wd;
return 0;
}
snd_soc_unregister_dai(&au1xpsc_ac97_dai);
snd_soc_unregister_dai(&pdev->dev);
out1:
release_mem_region(r->start, resource_size(r));
out0:
@ -422,7 +425,7 @@ static int __devexit au1xpsc_ac97_drvremove(struct platform_device *pdev)
if (wd->dmapd)
au1xpsc_pcm_destroy(wd->dmapd);
snd_soc_unregister_dai(&au1xpsc_ac97_dai);
snd_soc_unregister_dai(&pdev->dev);
/* disable PSC completely */
au_writel(0, AC97_CFG(wd));

View file

@ -10,9 +10,6 @@
*
* Au1xxx-PSC I2S glue.
*
* NOTE: all of these drivers can only work with a SINGLE instance
* of a PSC. Multiple independent audio devices are impossible
* with ASoC v1.
* NOTE: so far only PSC slave mode (bit- and frameclock) is supported.
*/
@ -54,13 +51,10 @@
((stype) == PCM_TX ? PSC_I2SPCR_TC : PSC_I2SPCR_RC)
/* instance data. There can be only one, MacLeod!!!! */
static struct au1xpsc_audio_data *au1xpsc_i2s_workdata;
static int au1xpsc_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
unsigned int fmt)
{
struct au1xpsc_audio_data *pscdata = au1xpsc_i2s_workdata;
struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(cpu_dai);
unsigned long ct;
int ret;
@ -120,7 +114,7 @@ static int au1xpsc_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct au1xpsc_audio_data *pscdata = au1xpsc_i2s_workdata;
struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(dai);
int cfgbits;
unsigned long stat;
@ -245,7 +239,7 @@ static int au1xpsc_i2s_stop(struct au1xpsc_audio_data *pscdata, int stype)
static int au1xpsc_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
struct au1xpsc_audio_data *pscdata = au1xpsc_i2s_workdata;
struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(dai);
int ret, stype = SUBSTREAM_TYPE(substream);
switch (cmd) {
@ -263,27 +257,13 @@ static int au1xpsc_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
return ret;
}
static int au1xpsc_i2s_probe(struct platform_device *pdev,
struct snd_soc_dai *dai)
{
return au1xpsc_i2s_workdata ? 0 : -ENODEV;
}
static void au1xpsc_i2s_remove(struct platform_device *pdev,
struct snd_soc_dai *dai)
{
}
static struct snd_soc_dai_ops au1xpsc_i2s_dai_ops = {
.trigger = au1xpsc_i2s_trigger,
.hw_params = au1xpsc_i2s_hw_params,
.set_fmt = au1xpsc_i2s_set_fmt,
};
struct snd_soc_dai au1xpsc_i2s_dai = {
.name = "au1xpsc_i2s",
.probe = au1xpsc_i2s_probe,
.remove = au1xpsc_i2s_remove,
static const struct snd_soc_dai_driver au1xpsc_i2s_dai_template = {
.playback = {
.rates = AU1XPSC_I2S_RATES,
.formats = AU1XPSC_I2S_FMTS,
@ -298,7 +278,6 @@ struct snd_soc_dai au1xpsc_i2s_dai = {
},
.ops = &au1xpsc_i2s_dai_ops,
};
EXPORT_SYMBOL(au1xpsc_i2s_dai);
static int __devinit au1xpsc_i2s_drvprobe(struct platform_device *pdev)
{
@ -307,9 +286,6 @@ static int __devinit au1xpsc_i2s_drvprobe(struct platform_device *pdev)
int ret;
struct au1xpsc_audio_data *wd;
if (au1xpsc_i2s_workdata)
return -EBUSY;
wd = kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL);
if (!wd)
return -ENOMEM;
@ -346,19 +322,23 @@ static int __devinit au1xpsc_i2s_drvprobe(struct platform_device *pdev)
* time out.
*/
ret = snd_soc_register_dai(&au1xpsc_i2s_dai);
/* name the DAI like this device instance ("au1xpsc-i2s.PSCINDEX") */
memcpy(&wd->dai_drv, &au1xpsc_i2s_dai_template,
sizeof(struct snd_soc_dai_driver));
wd->dai_drv.name = dev_name(&pdev->dev);
platform_set_drvdata(pdev, wd);
ret = snd_soc_register_dai(&pdev->dev, &wd->dai_drv);
if (ret)
goto out1;
/* finally add the DMA device for this PSC */
wd->dmapd = au1xpsc_pcm_add(pdev);
if (wd->dmapd) {
platform_set_drvdata(pdev, wd);
au1xpsc_i2s_workdata = wd;
if (wd->dmapd)
return 0;
}
snd_soc_unregister_dai(&au1xpsc_i2s_dai);
snd_soc_unregister_dai(&pdev->dev);
out1:
release_mem_region(r->start, resource_size(r));
out0:
@ -374,7 +354,7 @@ static int __devexit au1xpsc_i2s_drvremove(struct platform_device *pdev)
if (wd->dmapd)
au1xpsc_pcm_destroy(wd->dmapd);
snd_soc_unregister_dai(&au1xpsc_i2s_dai);
snd_soc_unregister_dai(&pdev->dev);
au_writel(0, I2S_CFG(wd));
au_sync();
@ -385,8 +365,6 @@ static int __devexit au1xpsc_i2s_drvremove(struct platform_device *pdev)
release_mem_region(r->start, resource_size(r));
kfree(wd);
au1xpsc_i2s_workdata = NULL; /* MDEV */
return 0;
}
@ -446,7 +424,6 @@ static struct platform_driver au1xpsc_i2s_driver = {
static int __init au1xpsc_i2s_load(void)
{
au1xpsc_i2s_workdata = NULL;
return platform_driver_register(&au1xpsc_i2s_driver);
}

View file

@ -8,19 +8,11 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* NOTE: all of these drivers can only work with a SINGLE instance
* of a PSC. Multiple independent audio devices are impossible
* with ASoC v1.
*/
#ifndef _AU1X_PCM_H
#define _AU1X_PCM_H
extern struct snd_soc_dai au1xpsc_ac97_dai;
extern struct snd_soc_dai au1xpsc_i2s_dai;
extern struct snd_soc_platform au1xpsc_soc_platform;
extern struct snd_ac97_bus_ops soc_ac97_ops;
/* DBDMA helpers */
extern struct platform_device *au1xpsc_pcm_add(struct platform_device *pdev);
extern void au1xpsc_pcm_destroy(struct platform_device *dmapd);
@ -31,6 +23,8 @@ struct au1xpsc_audio_data {
unsigned long cfg;
unsigned long rate;
struct snd_soc_dai_driver dai_drv;
unsigned long pm[2];
struct mutex lock;
struct platform_device *dmapd;

View file

@ -422,14 +422,14 @@ int bf5xx_pcm_ac97_new(struct snd_card *card, struct snd_soc_dai *dai,
if (!card->dev->coherent_dma_mask)
card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
if (dai->playback.channels_min) {
if (dai->driver->playback.channels_min) {
ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
SNDRV_PCM_STREAM_PLAYBACK);
if (ret)
goto out;
}
if (dai->capture.channels_min) {
if (dai->driver->capture.channels_min) {
ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
SNDRV_PCM_STREAM_CAPTURE);
if (ret)
@ -439,25 +439,44 @@ int bf5xx_pcm_ac97_new(struct snd_card *card, struct snd_soc_dai *dai,
return ret;
}
struct snd_soc_platform bf5xx_ac97_soc_platform = {
.name = "bf5xx-audio",
.pcm_ops = &bf5xx_pcm_ac97_ops,
static struct snd_soc_platform_driver bf5xx_ac97_soc_platform = {
.ops = &bf5xx_pcm_ac97_ops,
.pcm_new = bf5xx_pcm_ac97_new,
.pcm_free = bf5xx_pcm_free_dma_buffers,
};
EXPORT_SYMBOL_GPL(bf5xx_ac97_soc_platform);
static int __init bfin_ac97_init(void)
static int __devinit bf5xx_soc_platform_probe(struct platform_device *pdev)
{
return snd_soc_register_platform(&bf5xx_ac97_soc_platform);
return snd_soc_register_platform(&pdev->dev, &bf5xx_ac97_soc_platform);
}
module_init(bfin_ac97_init);
static void __exit bfin_ac97_exit(void)
static int __devexit bf5xx_soc_platform_remove(struct platform_device *pdev)
{
snd_soc_unregister_platform(&bf5xx_ac97_soc_platform);
snd_soc_unregister_platform(&pdev->dev);
return 0;
}
module_exit(bfin_ac97_exit);
static struct platform_driver bf5xx_pcm_driver = {
.driver = {
.name = "bf5xx-pcm-audio",
.owner = THIS_MODULE,
},
.probe = bf5xx_soc_platform_probe,
.remove = __devexit_p(bf5xx_soc_platform_remove),
};
static int __init snd_bf5xx_pcm_init(void)
{
return platform_driver_register(&bf5xx_pcm_driver);
}
module_init(snd_bf5xx_pcm_init);
static void __exit snd_bf5xx_pcm_exit(void)
{
platform_driver_unregister(&bf5xx_pcm_driver);
}
module_exit(snd_bf5xx_pcm_exit);
MODULE_AUTHOR("Cliff Cai");
MODULE_DESCRIPTION("ADI Blackfin AC97 PCM DMA module");

View file

@ -23,7 +23,4 @@ struct bf5xx_gpio {
u32 frm;
};
/* platform data */
extern struct snd_soc_platform bf5xx_ac97_soc_platform;
#endif

View file

@ -255,7 +255,7 @@ EXPORT_SYMBOL_GPL(soc_ac97_ops);
#ifdef CONFIG_PM
static int bf5xx_ac97_suspend(struct snd_soc_dai *dai)
{
struct sport_device *sport = dai->private_data;
struct sport_device *sport = snd_soc_dai_get_drvdata(dai);
pr_debug("%s : sport %d\n", __func__, dai->id);
if (!dai->active)
@ -270,7 +270,7 @@ static int bf5xx_ac97_suspend(struct snd_soc_dai *dai)
static int bf5xx_ac97_resume(struct snd_soc_dai *dai)
{
int ret;
struct sport_device *sport = dai->private_data;
struct sport_device *sport = snd_soc_dai_get_drvdata(dai);
pr_debug("%s : sport %d\n", __func__, dai->id);
if (!dai->active)
@ -306,8 +306,7 @@ static int bf5xx_ac97_resume(struct snd_soc_dai *dai)
#define bf5xx_ac97_resume NULL
#endif
static int bf5xx_ac97_probe(struct platform_device *pdev,
struct snd_soc_dai *dai)
static int bf5xx_ac97_probe(struct snd_soc_dai *dai)
{
int ret = 0;
cmd_count = (int *)get_zeroed_page(GFP_KERNEL);
@ -379,8 +378,7 @@ peripheral_err:
return ret;
}
static void bf5xx_ac97_remove(struct platform_device *pdev,
struct snd_soc_dai *dai)
static int bf5xx_ac97_remove(struct snd_soc_dai *dai)
{
free_page((unsigned long)cmd_count);
cmd_count = NULL;
@ -388,11 +386,10 @@ static void bf5xx_ac97_remove(struct platform_device *pdev,
#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
#endif
return 0;
}
struct snd_soc_dai bfin_ac97_dai = {
.name = "bf5xx-ac97",
.id = 0,
struct snd_soc_dai_driver bfin_ac97_dai = {
.ac97_control = 1,
.probe = bf5xx_ac97_probe,
.remove = bf5xx_ac97_remove,
@ -417,18 +414,40 @@ struct snd_soc_dai bfin_ac97_dai = {
};
EXPORT_SYMBOL_GPL(bfin_ac97_dai);
static __devinit int asoc_bfin_ac97_probe(struct platform_device *pdev)
{
return snd_soc_register_dai(&pdev->dev, &bfin_ac97_dai);
}
static int __devexit asoc_bfin_ac97_remove(struct platform_device *pdev)
{
snd_soc_unregister_dai(&pdev->dev);
return 0;
}
static struct platform_driver asoc_bfin_ac97_driver = {
.driver = {
.name = "bfin-ac97",
.owner = THIS_MODULE,
},
.probe = asoc_bfin_ac97_probe,
.remove = __devexit_p(asoc_bfin_ac97_remove),
};
static int __init bfin_ac97_init(void)
{
return snd_soc_register_dai(&bfin_ac97_dai);
return platform_driver_register(&asoc_bfin_ac97_driver);
}
module_init(bfin_ac97_init);
static void __exit bfin_ac97_exit(void)
{
snd_soc_unregister_dai(&bfin_ac97_dai);
platform_driver_unregister(&asoc_bfin_ac97_driver);
}
module_exit(bfin_ac97_exit);
MODULE_AUTHOR("Roy Huang");
MODULE_DESCRIPTION("AC97 driver for ADI Blackfin");
MODULE_LICENSE("GPL");

View file

@ -50,8 +50,6 @@ struct ac97_frame {
#define TAG_PCM_SR 0x0080
#define TAG_PCM_LFE 0x0040
extern struct snd_soc_dai bfin_ac97_dai;
void bf5xx_pcm_to_ac97(struct ac97_frame *dst, const __u16 *src, \
size_t count, unsigned int chan_mask);

View file

@ -40,9 +40,9 @@ static struct snd_soc_card bf5xx_ad1836;
static int bf5xx_ad1836_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
cpu_dai->private_data = sport_handle;
snd_soc_dai_set_drvdata(cpu_dai, sport_handle);
return 0;
}
@ -50,8 +50,8 @@ static int bf5xx_ad1836_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
unsigned int channel_map[] = {0, 4, 1, 5, 2, 6, 3, 7};
int ret = 0;
/* set cpu DAI configuration */
@ -83,23 +83,19 @@ static struct snd_soc_ops bf5xx_ad1836_ops = {
static struct snd_soc_dai_link bf5xx_ad1836_dai = {
.name = "ad1836",
.stream_name = "AD1836",
.cpu_dai = &bf5xx_tdm_dai,
.codec_dai = &ad1836_dai,
.cpu_dai_name = "bf5xx-tdm",
.codec_dai_name = "ad1836-hifi",
.platform_name = "bf5xx-tdm-pcm-audio",
.codec_name = "ad1836-codec.0",
.ops = &bf5xx_ad1836_ops,
};
static struct snd_soc_card bf5xx_ad1836 = {
.name = "bf5xx_ad1836",
.platform = &bf5xx_tdm_soc_platform,
.dai_link = &bf5xx_ad1836_dai,
.num_links = 1,
};
static struct snd_soc_device bf5xx_ad1836_snd_devdata = {
.card = &bf5xx_ad1836,
.codec_dev = &soc_codec_dev_ad1836,
};
static struct platform_device *bfxx_ad1836_snd_device;
static int __init bf5xx_ad1836_init(void)
@ -110,8 +106,7 @@ static int __init bf5xx_ad1836_init(void)
if (!bfxx_ad1836_snd_device)
return -ENOMEM;
platform_set_drvdata(bfxx_ad1836_snd_device, &bf5xx_ad1836_snd_devdata);
bf5xx_ad1836_snd_devdata.dev = &bfxx_ad1836_snd_device->dev;
platform_set_drvdata(bfxx_ad1836_snd_device, &bf5xx_ad1836);
ret = platform_device_add(bfxx_ad1836_snd_device);
if (ret)

View file

@ -49,9 +49,9 @@ static struct snd_soc_card bf5xx_ad193x;
static int bf5xx_ad193x_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
cpu_dai->private_data = sport_handle;
snd_soc_dai_set_drvdata(cpu_dai, sport_handle);
return 0;
}
@ -59,8 +59,8 @@ static int bf5xx_ad193x_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
unsigned int channel_map[] = {0, 1, 2, 3, 4, 5, 6, 7};
int ret = 0;
/* set cpu DAI configuration */
@ -97,23 +97,19 @@ static struct snd_soc_ops bf5xx_ad193x_ops = {
static struct snd_soc_dai_link bf5xx_ad193x_dai = {
.name = "ad193x",
.stream_name = "AD193X",
.cpu_dai = &bf5xx_tdm_dai,
.codec_dai = &ad193x_dai,
.cpu_dai_name = "bf5xx-tdm",
.codec_dai_name ="ad193x-hifi",
.platform_name = "bf5xx-tdm-pcm-audio",
.codec_name = "ad193x-codec.5",
.ops = &bf5xx_ad193x_ops,
};
static struct snd_soc_card bf5xx_ad193x = {
.name = "bf5xx_ad193x",
.platform = &bf5xx_tdm_soc_platform,
.dai_link = &bf5xx_ad193x_dai,
.num_links = 1,
};
static struct snd_soc_device bf5xx_ad193x_snd_devdata = {
.card = &bf5xx_ad193x,
.codec_dev = &soc_codec_dev_ad193x,
};
static struct platform_device *bfxx_ad193x_snd_device;
static int __init bf5xx_ad193x_init(void)
@ -124,8 +120,7 @@ static int __init bf5xx_ad193x_init(void)
if (!bfxx_ad193x_snd_device)
return -ENOMEM;
platform_set_drvdata(bfxx_ad193x_snd_device, &bf5xx_ad193x_snd_devdata);
bf5xx_ad193x_snd_devdata.dev = &bfxx_ad193x_snd_device->dev;
platform_set_drvdata(bfxx_ad193x_snd_device, &bf5xx_ad193x);
ret = platform_device_add(bfxx_ad193x_snd_device);
if (ret)

View file

@ -56,10 +56,10 @@ static struct snd_soc_card bf5xx_board;
static int bf5xx_board_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
pr_debug("%s enter\n", __func__);
cpu_dai->private_data = sport_handle;
snd_soc_dai_set_drvdata(cpu_dai, sport_handle);
return 0;
}
@ -70,23 +70,19 @@ static struct snd_soc_ops bf5xx_board_ops = {
static struct snd_soc_dai_link bf5xx_board_dai = {
.name = "AC97",
.stream_name = "AC97 HiFi",
.cpu_dai = &bfin_ac97_dai,
.codec_dai = &ad1980_dai,
.cpu_dai_name = "bfin-ac97",
.codec_dai_name = "ad1980-hifi",
.platform_name = "bfin-pcm-audio",
.codec_name = "ad1980-codec",
.ops = &bf5xx_board_ops,
};
static struct snd_soc_card bf5xx_board = {
.name = "bf5xx-board",
.platform = &bf5xx_ac97_soc_platform,
.dai_link = &bf5xx_board_dai,
.num_links = 1,
};
static struct snd_soc_device bf5xx_board_snd_devdata = {
.card = &bf5xx_board,
.codec_dev = &soc_codec_dev_ad1980,
};
static struct platform_device *bf5xx_board_snd_device;
static int __init bf5xx_board_init(void)
@ -97,8 +93,7 @@ static int __init bf5xx_board_init(void)
if (!bf5xx_board_snd_device)
return -ENOMEM;
platform_set_drvdata(bf5xx_board_snd_device, &bf5xx_board_snd_devdata);
bf5xx_board_snd_devdata.dev = &bf5xx_board_snd_device->dev;
platform_set_drvdata(bf5xx_board_snd_device, &bf5xx_board);
ret = platform_device_add(bf5xx_board_snd_device);
if (ret)

View file

@ -47,7 +47,6 @@
#include "../codecs/ad73311.h"
#include "bf5xx-sport.h"
#include "bf5xx-i2s-pcm.h"
#include "bf5xx-i2s.h"
#if CONFIG_SND_BF5XX_SPORT_NUM == 0
#define bfin_write_SPORT_TCR1 bfin_write_SPORT0_TCR1
@ -150,10 +149,10 @@ static int bf5xx_probe(struct platform_device *pdev)
static int bf5xx_ad73311_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
pr_debug("%s enter\n", __func__);
cpu_dai->private_data = sport_handle;
snd_soc_dai_set_drvdata(cpu_dai, sport_handle);
return 0;
}
@ -161,7 +160,7 @@ static int bf5xx_ad73311_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
int ret = 0;
pr_debug("%s rate %d format %x\n", __func__, params_rate(params),
@ -185,24 +184,20 @@ static struct snd_soc_ops bf5xx_ad73311_ops = {
static struct snd_soc_dai_link bf5xx_ad73311_dai = {
.name = "ad73311",
.stream_name = "AD73311",
.cpu_dai = &bf5xx_i2s_dai,
.codec_dai = &ad73311_dai,
.cpu_dai_name = "bf5xx-i2s",
.codec_dai_name = "ad73311-hifi",
.platform_name = "bfin-pcm-audio",
.codec_name = "ad73311-codec",
.ops = &bf5xx_ad73311_ops,
};
static struct snd_soc_card bf5xx_ad73311 = {
.name = "bf5xx_ad73311",
.platform = &bf5xx_i2s_soc_platform,
.probe = bf5xx_probe,
.dai_link = &bf5xx_ad73311_dai,
.num_links = 1,
};
static struct snd_soc_device bf5xx_ad73311_snd_devdata = {
.card = &bf5xx_ad73311,
.codec_dev = &soc_codec_dev_ad73311,
};
static struct platform_device *bf5xx_ad73311_snd_device;
static int __init bf5xx_ad73311_init(void)
@ -214,8 +209,7 @@ static int __init bf5xx_ad73311_init(void)
if (!bf5xx_ad73311_snd_device)
return -ENOMEM;
platform_set_drvdata(bf5xx_ad73311_snd_device, &bf5xx_ad73311_snd_devdata);
bf5xx_ad73311_snd_devdata.dev = &bf5xx_ad73311_snd_device->dev;
platform_set_drvdata(bf5xx_ad73311_snd_device, &bf5xx_ad73311);
ret = platform_device_add(bf5xx_ad73311_snd_device);
if (ret)

View file

@ -40,7 +40,6 @@
#include <asm/dma.h>
#include "bf5xx-i2s-pcm.h"
#include "bf5xx-i2s.h"
#include "bf5xx-sport.h"
static void bf5xx_dma_irq(void *data)
@ -257,14 +256,14 @@ int bf5xx_pcm_i2s_new(struct snd_card *card, struct snd_soc_dai *dai,
if (!card->dev->coherent_dma_mask)
card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
if (dai->playback.channels_min) {
if (dai->driver->playback.channels_min) {
ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
SNDRV_PCM_STREAM_PLAYBACK);
if (ret)
goto out;
}
if (dai->capture.channels_min) {
if (dai->driver->capture.channels_min) {
ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
SNDRV_PCM_STREAM_CAPTURE);
if (ret)
@ -274,25 +273,44 @@ int bf5xx_pcm_i2s_new(struct snd_card *card, struct snd_soc_dai *dai,
return ret;
}
struct snd_soc_platform bf5xx_i2s_soc_platform = {
.name = "bf5xx-audio",
.pcm_ops = &bf5xx_pcm_i2s_ops,
static struct snd_soc_platform_driver bf5xx_i2s_soc_platform = {
.ops = &bf5xx_pcm_i2s_ops,
.pcm_new = bf5xx_pcm_i2s_new,
.pcm_free = bf5xx_pcm_free_dma_buffers,
};
EXPORT_SYMBOL_GPL(bf5xx_i2s_soc_platform);
static int __init bfin_i2s_init(void)
static int __devinit bfin_i2s_soc_platform_probe(struct platform_device *pdev)
{
return snd_soc_register_platform(&bf5xx_i2s_soc_platform);
return snd_soc_register_platform(&pdev->dev, &bf5xx_i2s_soc_platform);
}
module_init(bfin_i2s_init);
static void __exit bfin_i2s_exit(void)
static int __devexit bfin_i2s_soc_platform_remove(struct platform_device *pdev)
{
snd_soc_unregister_platform(&bf5xx_i2s_soc_platform);
snd_soc_unregister_platform(&pdev->dev);
return 0;
}
module_exit(bfin_i2s_exit);
static struct platform_driver bfin_i2s_pcm_driver = {
.driver = {
.name = "bfin-pcm-audio",
.owner = THIS_MODULE,
},
.probe = bfin_i2s_soc_platform_probe,
.remove = __devexit_p(bfin_i2s_soc_platform_remove),
};
static int __init snd_bfin_i2s_pcm_init(void)
{
return platform_driver_register(&bfin_i2s_pcm_driver);
}
module_init(snd_bfin_i2s_pcm_init);
static void __exit snd_bfin_i2s_pcm_exit(void)
{
platform_driver_unregister(&bfin_i2s_pcm_driver);
}
module_exit(snd_bfin_i2s_pcm_exit);
MODULE_AUTHOR("Cliff Cai");
MODULE_DESCRIPTION("ADI Blackfin I2S PCM DMA module");

View file

@ -23,7 +23,4 @@ struct bf5xx_gpio {
u32 frm;
};
/* platform data */
extern struct snd_soc_platform bf5xx_i2s_soc_platform;
#endif

View file

@ -42,7 +42,6 @@
#include <linux/gpio.h>
#include "bf5xx-sport.h"
#include "bf5xx-i2s.h"
struct bf5xx_i2s_port {
u16 tcr1;
@ -195,8 +194,7 @@ static void bf5xx_i2s_shutdown(struct snd_pcm_substream *substream,
bf5xx_i2s.configured = 0;
}
static int bf5xx_i2s_probe(struct platform_device *pdev,
struct snd_soc_dai *dai)
static int bf5xx_i2s_probe(struct snd_soc_dai *dai)
{
pr_debug("%s enter\n", __func__);
if (peripheral_request_list(&sport_req[sport_num][0], "soc-audio")) {
@ -215,11 +213,11 @@ static int bf5xx_i2s_probe(struct platform_device *pdev,
return 0;
}
static void bf5xx_i2s_remove(struct platform_device *pdev,
struct snd_soc_dai *dai)
static int bf5xx_i2s_remove(struct snd_soc_dai *dai)
{
pr_debug("%s enter\n", __func__);
peripheral_free_list(&sport_req[sport_num][0]);
return 0;
}
#ifdef CONFIG_PM
@ -228,9 +226,9 @@ static int bf5xx_i2s_suspend(struct snd_soc_dai *dai)
pr_debug("%s : sport %d\n", __func__, dai->id);
if (dai->capture.active)
if (dai->capture_active)
sport_rx_stop(sport_handle);
if (dai->playback.active)
if (dai->playback_active)
sport_tx_stop(sport_handle);
return 0;
}
@ -277,9 +275,7 @@ static struct snd_soc_dai_ops bf5xx_i2s_dai_ops = {
.set_fmt = bf5xx_i2s_set_dai_fmt,
};
struct snd_soc_dai bf5xx_i2s_dai = {
.name = "bf5xx-i2s",
.id = 0,
static struct snd_soc_dai_driver bf5xx_i2s_dai = {
.probe = bf5xx_i2s_probe,
.remove = bf5xx_i2s_remove,
.suspend = bf5xx_i2s_suspend,
@ -296,18 +292,39 @@ struct snd_soc_dai bf5xx_i2s_dai = {
.formats = BF5XX_I2S_FORMATS,},
.ops = &bf5xx_i2s_dai_ops,
};
EXPORT_SYMBOL_GPL(bf5xx_i2s_dai);
static int bfin_i2s_drv_probe(struct platform_device *pdev)
{
return snd_soc_register_dai(&pdev->dev, &bf5xx_i2s_dai);
}
static int __devexit bfin_i2s_drv_remove(struct platform_device *pdev)
{
snd_soc_unregister_dai(&pdev->dev);
return 0;
}
static struct platform_driver bfin_i2s_driver = {
.probe = bfin_i2s_drv_probe,
.remove = __devexit_p(bfin_i2s_drv_remove),
.driver = {
.name = "bf5xx-i2s",
.owner = THIS_MODULE,
},
};
static int __init bfin_i2s_init(void)
{
return snd_soc_register_dai(&bf5xx_i2s_dai);
return platform_driver_register(&bfin_i2s_driver);
}
module_init(bfin_i2s_init);
static void __exit bfin_i2s_exit(void)
{
snd_soc_unregister_dai(&bf5xx_i2s_dai);
platform_driver_unregister(&bfin_i2s_driver);
}
module_init(bfin_i2s_init);
module_exit(bfin_i2s_exit);
/* Module information */

View file

@ -1,14 +0,0 @@
/*
* sound/soc/blackfin/bf5xx-i2s.h
*
* 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 _BF5XX_I2S_H
#define _BF5XX_I2S_H
extern struct snd_soc_dai bf5xx_i2s_dai;
#endif

View file

@ -42,17 +42,16 @@
#include "../codecs/ssm2602.h"
#include "bf5xx-sport.h"
#include "bf5xx-i2s-pcm.h"
#include "bf5xx-i2s.h"
static struct snd_soc_card bf5xx_ssm2602;
static int bf5xx_ssm2602_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
pr_debug("%s enter\n", __func__);
cpu_dai->private_data = sport_handle;
snd_soc_dai_set_drvdata(cpu_dai, sport_handle);
return 0;
}
@ -60,8 +59,8 @@ static int bf5xx_ssm2602_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
unsigned int clk = 0;
int ret = 0;
@ -118,36 +117,19 @@ static struct snd_soc_ops bf5xx_ssm2602_ops = {
static struct snd_soc_dai_link bf5xx_ssm2602_dai = {
.name = "ssm2602",
.stream_name = "SSM2602",
.cpu_dai = &bf5xx_i2s_dai,
.codec_dai = &ssm2602_dai,
.cpu_dai_name = "bf5xx-i2s",
.codec_dai_name = "ssm2602-hifi",
.platform_name = "bf5xx-pcm-audio",
.codec_name = "ssm2602-codec.0-0x1b",
.ops = &bf5xx_ssm2602_ops,
};
/*
* SSM2602 2 wire address is determined by CSB
* state during powerup.
* low = 0x1a
* high = 0x1b
*/
static struct ssm2602_setup_data bf5xx_ssm2602_setup = {
.i2c_bus = 0,
.i2c_address = 0x1b,
};
static struct snd_soc_card bf5xx_ssm2602 = {
.name = "bf5xx_ssm2602",
.platform = &bf5xx_i2s_soc_platform,
.dai_link = &bf5xx_ssm2602_dai,
.num_links = 1,
};
static struct snd_soc_device bf5xx_ssm2602_snd_devdata = {
.card = &bf5xx_ssm2602,
.codec_dev = &soc_codec_dev_ssm2602,
.codec_data = &bf5xx_ssm2602_setup,
};
static struct platform_device *bf5xx_ssm2602_snd_device;
static int __init bf5xx_ssm2602_init(void)
@ -159,9 +141,7 @@ static int __init bf5xx_ssm2602_init(void)
if (!bf5xx_ssm2602_snd_device)
return -ENOMEM;
platform_set_drvdata(bf5xx_ssm2602_snd_device,
&bf5xx_ssm2602_snd_devdata);
bf5xx_ssm2602_snd_devdata.dev = &bf5xx_ssm2602_snd_device->dev;
platform_set_drvdata(bf5xx_ssm2602_snd_device, &bf5xx_ssm2602);
ret = platform_device_add(bf5xx_ssm2602_snd_device);
if (ret)

View file

@ -290,14 +290,14 @@ static int bf5xx_pcm_tdm_new(struct snd_card *card, struct snd_soc_dai *dai,
if (!card->dev->coherent_dma_mask)
card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
if (dai->playback.channels_min) {
if (dai->driver->playback.channels_min) {
ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
SNDRV_PCM_STREAM_PLAYBACK);
if (ret)
goto out;
}
if (dai->capture.channels_min) {
if (dai->driver->capture.channels_min) {
ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
SNDRV_PCM_STREAM_CAPTURE);
if (ret)
@ -307,25 +307,44 @@ out:
return ret;
}
struct snd_soc_platform bf5xx_tdm_soc_platform = {
.name = "bf5xx-audio",
.pcm_ops = &bf5xx_pcm_tdm_ops,
static struct snd_soc_platform_driver bf5xx_tdm_soc_platform = {
.ops = &bf5xx_pcm_tdm_ops,
.pcm_new = bf5xx_pcm_tdm_new,
.pcm_free = bf5xx_pcm_free_dma_buffers,
};
EXPORT_SYMBOL_GPL(bf5xx_tdm_soc_platform);
static int __init bfin_pcm_tdm_init(void)
static int __devinit bf5xx_soc_platform_probe(struct platform_device *pdev)
{
return snd_soc_register_platform(&bf5xx_tdm_soc_platform);
return snd_soc_register_platform(&pdev->dev, &bf5xx_tdm_soc_platform);
}
module_init(bfin_pcm_tdm_init);
static void __exit bfin_pcm_tdm_exit(void)
static int __devexit bf5xx_soc_platform_remove(struct platform_device *pdev)
{
snd_soc_unregister_platform(&bf5xx_tdm_soc_platform);
snd_soc_unregister_platform(&pdev->dev);
return 0;
}
module_exit(bfin_pcm_tdm_exit);
static struct platform_driver bfin_tdm_driver = {
.driver = {
.name = "bf5xx-tdm-pcm-audio",
.owner = THIS_MODULE,
},
.probe = bf5xx_soc_platform_probe,
.remove = __devexit_p(bf5xx_soc_platform_remove),
};
static int __init snd_bfin_tdm_init(void)
{
return platform_driver_register(&bfin_tdm_driver);
}
module_init(snd_bfin_tdm_init);
static void __exit snd_bfin_tdm_exit(void)
{
platform_driver_unregister(&bfin_tdm_driver);
}
module_exit(snd_bfin_tdm_exit);
MODULE_AUTHOR("Barry Song");
MODULE_DESCRIPTION("ADI Blackfin TDM PCM DMA module");

View file

@ -15,7 +15,4 @@ struct bf5xx_pcm_dma_params {
char *name; /* stream identifier */
};
/* platform data */
extern struct snd_soc_platform bf5xx_tdm_soc_platform;
#endif

View file

@ -214,9 +214,9 @@ static int bf5xx_tdm_suspend(struct snd_soc_dai *dai)
if (!dai->active)
return 0;
if (dai->capture.active)
if (dai->capture_active)
sport_rx_stop(sport);
if (dai->playback.active)
if (dai->playback_active)
sport_tx_stop(sport);
return 0;
}
@ -224,7 +224,7 @@ static int bf5xx_tdm_suspend(struct snd_soc_dai *dai)
static int bf5xx_tdm_resume(struct snd_soc_dai *dai)
{
int ret;
struct sport_device *sport = dai->private_data;
struct sport_device *sport = snd_soc_dai_get_drvdata(dai);
if (!dai->active)
return 0;
@ -262,9 +262,7 @@ static struct snd_soc_dai_ops bf5xx_tdm_dai_ops = {
.set_channel_map = bf5xx_tdm_set_channel_map,
};
struct snd_soc_dai bf5xx_tdm_dai = {
.name = "bf5xx-tdm",
.id = 0,
static struct snd_soc_dai_driver bf5xx_tdm_dai = {
.suspend = bf5xx_tdm_suspend,
.resume = bf5xx_tdm_resume,
.playback = {
@ -279,7 +277,6 @@ struct snd_soc_dai bf5xx_tdm_dai = {
.formats = SNDRV_PCM_FMTBIT_S32_LE,},
.ops = &bf5xx_tdm_dai_ops,
};
EXPORT_SYMBOL_GPL(bf5xx_tdm_dai);
static int __devinit bfin_tdm_probe(struct platform_device *pdev)
{
@ -320,7 +317,7 @@ static int __devinit bfin_tdm_probe(struct platform_device *pdev)
goto sport_config_err;
}
ret = snd_soc_register_dai(&bf5xx_tdm_dai);
ret = snd_soc_register_dai(&pdev->dev, &bf5xx_tdm_dai);
if (ret) {
pr_err("Failed to register DAI: %d\n", ret);
goto sport_config_err;
@ -337,7 +334,7 @@ sport_config_err:
static int __devexit bfin_tdm_remove(struct platform_device *pdev)
{
peripheral_free_list(&sport_req[sport_num][0]);
snd_soc_unregister_dai(&bf5xx_tdm_dai);
snd_soc_unregister_dai(&pdev->dev);
return 0;
}

Some files were not shown because too many files have changed in this diff Show more