sound fixes for 3.14-rc1
The big chunks here are the updates for oxygen driver for Xonar DG devices, which were slipped from the previous pull request. They are device-specific and thus not too dangerous. Other than that, all patches are small bug fixes, mainly for Samsung build fixes, a few HD-audio enhancements, and other misc ASoC fixes. (And this time ASoC merge is less than Octopus, lucky seven :) -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iQIcBAABAgAGBQJS69k+AAoJEGwxgFQ9KSmk6pMQAIbo4mBrMzdkt7elEErpdpDj 4P11/owXL+/gD+fTVXyTubvalXN9MATUWnHbLqi9qTYWx7u/RjnP32PQ0aMSyq1r PJq99Or+H+H2ui1dTsgIg2A8pYvbeuzU+Y7UcTusy9IolZNvKTNHVJRL79QQzNrp JTX220K4wLAjrl96fOtJ2gpABSej2kLdJ8Tj+YE7eMzySw+nS18nwoca2l3GPcR4 qAyDQ0owgOWmBBaExSAgC9ck0l6NVSAba9r1ub156Szf0htmv5+pHqv3JJjKVTc/ NmD/XVZxT7jEucPb6tAvmSfAqQBm97mX3nXVKQd3+QlH3pRI0pifS4ZOgRqJUZr/ XZnM5lB17kd7XCBqnik+Nk9EmBRgF5ASRatVE+Mg469i9GBzqQOv0NTEsqL+Vhmb 6tng5hBOV8o3v4/Z3uPVMtTEP+UYj+d8YseleNV5OI7dfjqQPZHWTeCllKl42Rra Ui5P1V1i1KD2jgiqbLR4XYmkMbXY31o5I6BvKb1iJ2jVn0igtrFpcD28Z+ufENLk /618pjpJH9WswwzYTAB1lijbM2Wuh7KF/UwsA02OyT4glwVYkMjEYQybpeQQ31sA vNiOLiy0epAw/8FBvg2nVl8n5EdFCiLlczqFhcabBEvO/RAqg4w9ipUBlR6OMo0+ JQTwuvLLjyimHkQdUcmO =QB98 -----END PGP SIGNATURE----- Merge tag 'sound-fix-3.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound Pull sound fixes from Takashi Iwai: "The big chunks here are the updates for oxygen driver for Xonar DG devices, which were slipped from the previous pull request. They are device-specific and thus not too dangerous. Other than that, all patches are small bug fixes, mainly for Samsung build fixes, a few HD-audio enhancements, and other misc ASoC fixes. (And this time ASoC merge is less than Octopus, lucky seven :)" * tag 'sound-fix-3.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (42 commits) ALSA: hda/hdmi - allow PIN_OUT to be dynamically enabled ALSA: hda - add headset mic detect quirks for another Dell laptop ALSA: oxygen: Xonar DG(X): cleanup and minor changes ALSA: oxygen: Xonar DG(X): modify high-pass filter control ALSA: oxygen: Xonar DG(X): modify input select functions ALSA: oxygen: Xonar DG(X): modify capture volume functions ALSA: oxygen: Xonar DG(X): use headphone volume control ALSA: oxygen: Xonar DG(X): modify playback output select ALSA: oxygen: Xonar DG(X): capture from I2S channel 1, not 2 ALSA: oxygen: Xonar DG(X): move the mixer code into another file ALSA: oxygen: modify CS4245 register dumping function ALSA: oxygen: modify adjust_dg_dac_routing function ALSA: oxygen: Xonar DG(X): modify DAC/ADC parameters function ALSA: oxygen: Xonar DG(X): modify initialization functions ALSA: oxygen: Xonar DG(X): add new CS4245 SPI functions ALSA: oxygen: additional definitions for the Xonar DG/DGX card ALSA: oxygen: change description of the xonar_dg.c file ALSA: oxygen: export oxygen_update_dac_routing symbol ALSA: oxygen: add mute mask for the OXYGEN_PLAY_ROUTING register ALSA: oxygen: modify the SPI writing function ...
This commit is contained in:
Коммит
14864a52cd
|
@ -43,7 +43,7 @@ Example:
|
|||
sound {
|
||||
compatible = "simple-audio-card";
|
||||
simple-audio-card,format = "left_j";
|
||||
simple-audio-routing =
|
||||
simple-audio-card,routing =
|
||||
"MIC_IN", "Mic Jack",
|
||||
"Headphone Jack", "HP_OUT",
|
||||
"Ext Spk", "LINE_OUT";
|
||||
|
|
|
@ -131,6 +131,31 @@ static inline int init_info_for_card(struct snd_card *card)
|
|||
#define init_info_for_card(card)
|
||||
#endif
|
||||
|
||||
static int check_empty_slot(struct module *module, int slot)
|
||||
{
|
||||
return !slots[slot] || !*slots[slot];
|
||||
}
|
||||
|
||||
/* return an empty slot number (>= 0) found in the given bitmask @mask.
|
||||
* @mask == -1 == 0xffffffff means: take any free slot up to 32
|
||||
* when no slot is available, return the original @mask as is.
|
||||
*/
|
||||
static int get_slot_from_bitmask(int mask, int (*check)(struct module *, int),
|
||||
struct module *module)
|
||||
{
|
||||
int slot;
|
||||
|
||||
for (slot = 0; slot < SNDRV_CARDS; slot++) {
|
||||
if (slot < 32 && !(mask & (1U << slot)))
|
||||
continue;
|
||||
if (!test_bit(slot, snd_cards_lock)) {
|
||||
if (check(module, slot))
|
||||
return slot; /* found */
|
||||
}
|
||||
}
|
||||
return mask; /* unchanged */
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_card_create - create and initialize a soundcard structure
|
||||
* @idx: card index (address) [0 ... (SNDRV_CARDS-1)]
|
||||
|
@ -152,7 +177,7 @@ int snd_card_create(int idx, const char *xid,
|
|||
struct snd_card **card_ret)
|
||||
{
|
||||
struct snd_card *card;
|
||||
int err, idx2;
|
||||
int err;
|
||||
|
||||
if (snd_BUG_ON(!card_ret))
|
||||
return -EINVAL;
|
||||
|
@ -167,32 +192,10 @@ int snd_card_create(int idx, const char *xid,
|
|||
strlcpy(card->id, xid, sizeof(card->id));
|
||||
err = 0;
|
||||
mutex_lock(&snd_card_mutex);
|
||||
if (idx < 0) {
|
||||
for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++) {
|
||||
/* idx == -1 == 0xffff means: take any free slot */
|
||||
if (idx2 < sizeof(int) && !(idx & (1U << idx2)))
|
||||
continue;
|
||||
if (!test_bit(idx2, snd_cards_lock)) {
|
||||
if (module_slot_match(module, idx2)) {
|
||||
idx = idx2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (idx < 0) {
|
||||
for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++) {
|
||||
/* idx == -1 == 0xffff means: take any free slot */
|
||||
if (idx2 < sizeof(int) && !(idx & (1U << idx2)))
|
||||
continue;
|
||||
if (!test_bit(idx2, snd_cards_lock)) {
|
||||
if (!slots[idx2] || !*slots[idx2]) {
|
||||
idx = idx2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (idx < 0) /* first check the matching module-name slot */
|
||||
idx = get_slot_from_bitmask(idx, module_slot_match, module);
|
||||
if (idx < 0) /* if not matched, assign an empty slot */
|
||||
idx = get_slot_from_bitmask(idx, check_empty_slot, module);
|
||||
if (idx < 0)
|
||||
err = -ENODEV;
|
||||
else if (idx < snd_ecards_limit) {
|
||||
|
|
|
@ -369,6 +369,7 @@ static void free_module_desc(struct dsp_module_desc *module)
|
|||
kfree(module->segments[i].data);
|
||||
kfree(module->segments);
|
||||
}
|
||||
kfree(module);
|
||||
}
|
||||
|
||||
/* firmware binary format:
|
||||
|
|
|
@ -361,6 +361,7 @@ struct hda_codec {
|
|||
unsigned int epss:1; /* supporting EPSS? */
|
||||
unsigned int cached_write:1; /* write only to caches */
|
||||
unsigned int dp_mst:1; /* support DP1.2 Multi-stream transport */
|
||||
unsigned int dump_coef:1; /* dump processing coefs in codec proc file */
|
||||
#ifdef CONFIG_PM
|
||||
unsigned int power_on :1; /* current (global) power-state */
|
||||
unsigned int d3_stop_clk:1; /* support D3 operation without BCLK */
|
||||
|
|
|
@ -24,9 +24,14 @@
|
|||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/core.h>
|
||||
#include <linux/module.h>
|
||||
#include "hda_codec.h"
|
||||
#include "hda_local.h"
|
||||
|
||||
static int dump_coef = -1;
|
||||
module_param(dump_coef, int, 0644);
|
||||
MODULE_PARM_DESC(dump_coef, "Dump processing coefficients in codec proc file (-1=auto, 0=disable, 1=enable)");
|
||||
|
||||
static char *bits_names(unsigned int bits, char *names[], int size)
|
||||
{
|
||||
int i, n;
|
||||
|
@ -488,14 +493,39 @@ static void print_unsol_cap(struct snd_info_buffer *buffer,
|
|||
(unsol & AC_UNSOL_ENABLED) ? 1 : 0);
|
||||
}
|
||||
|
||||
static inline bool can_dump_coef(struct hda_codec *codec)
|
||||
{
|
||||
switch (dump_coef) {
|
||||
case 0: return false;
|
||||
case 1: return true;
|
||||
default: return codec->dump_coef;
|
||||
}
|
||||
}
|
||||
|
||||
static void print_proc_caps(struct snd_info_buffer *buffer,
|
||||
struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
unsigned int i, ncoeff, oldindex;
|
||||
unsigned int proc_caps = snd_hda_param_read(codec, nid,
|
||||
AC_PAR_PROC_CAP);
|
||||
ncoeff = (proc_caps & AC_PCAP_NUM_COEF) >> AC_PCAP_NUM_COEF_SHIFT;
|
||||
snd_iprintf(buffer, " Processing caps: benign=%d, ncoeff=%d\n",
|
||||
proc_caps & AC_PCAP_BENIGN,
|
||||
(proc_caps & AC_PCAP_NUM_COEF) >> AC_PCAP_NUM_COEF_SHIFT);
|
||||
proc_caps & AC_PCAP_BENIGN, ncoeff);
|
||||
|
||||
if (!can_dump_coef(codec))
|
||||
return;
|
||||
|
||||
/* Note: This is racy - another process could run in parallel and change
|
||||
the coef index too. */
|
||||
oldindex = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_COEF_INDEX, 0);
|
||||
for (i = 0; i < ncoeff; i++) {
|
||||
unsigned int val;
|
||||
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX, i);
|
||||
val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PROC_COEF,
|
||||
0);
|
||||
snd_iprintf(buffer, " Coeff 0x%02x: 0x%04x\n", i, val);
|
||||
}
|
||||
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX, oldindex);
|
||||
}
|
||||
|
||||
static void print_conn_list(struct snd_info_buffer *buffer,
|
||||
|
|
|
@ -132,6 +132,9 @@ struct hdmi_spec {
|
|||
|
||||
struct hdmi_eld temp_eld;
|
||||
struct hdmi_ops ops;
|
||||
|
||||
bool dyn_pin_out;
|
||||
|
||||
/*
|
||||
* Non-generic VIA/NVIDIA specific
|
||||
*/
|
||||
|
@ -500,15 +503,25 @@ static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t pin_nid,
|
|||
|
||||
static void hdmi_init_pin(struct hda_codec *codec, hda_nid_t pin_nid)
|
||||
{
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
int pin_out;
|
||||
|
||||
/* Unmute */
|
||||
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);
|
||||
/* Enable pin out: some machines with GM965 gets broken output when
|
||||
* the pin is disabled or changed while using with HDMI
|
||||
*/
|
||||
|
||||
if (spec->dyn_pin_out)
|
||||
/* Disable pin out until stream is active */
|
||||
pin_out = 0;
|
||||
else
|
||||
/* Enable pin out: some machines with GM965 gets broken output
|
||||
* when the pin is disabled or changed while using with HDMI
|
||||
*/
|
||||
pin_out = PIN_OUT;
|
||||
|
||||
snd_hda_codec_write(codec, pin_nid, 0,
|
||||
AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
|
||||
AC_VERB_SET_PIN_WIDGET_CONTROL, pin_out);
|
||||
}
|
||||
|
||||
static int hdmi_get_channel_count(struct hda_codec *codec, hda_nid_t cvt_nid)
|
||||
|
@ -1735,6 +1748,7 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
|
|||
struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
|
||||
hda_nid_t pin_nid = per_pin->pin_nid;
|
||||
bool non_pcm;
|
||||
int pinctl;
|
||||
|
||||
non_pcm = check_non_pcm_per_cvt(codec, cvt_nid);
|
||||
mutex_lock(&per_pin->lock);
|
||||
|
@ -1744,6 +1758,14 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
|
|||
hdmi_setup_audio_infoframe(codec, per_pin, non_pcm);
|
||||
mutex_unlock(&per_pin->lock);
|
||||
|
||||
if (spec->dyn_pin_out) {
|
||||
pinctl = snd_hda_codec_read(codec, pin_nid, 0,
|
||||
AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
|
||||
snd_hda_codec_write(codec, pin_nid, 0,
|
||||
AC_VERB_SET_PIN_WIDGET_CONTROL,
|
||||
pinctl | PIN_OUT);
|
||||
}
|
||||
|
||||
return spec->ops.setup_stream(codec, cvt_nid, pin_nid, stream_tag, format);
|
||||
}
|
||||
|
||||
|
@ -1763,6 +1785,7 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo,
|
|||
int cvt_idx, pin_idx;
|
||||
struct hdmi_spec_per_cvt *per_cvt;
|
||||
struct hdmi_spec_per_pin *per_pin;
|
||||
int pinctl;
|
||||
|
||||
if (hinfo->nid) {
|
||||
cvt_idx = cvt_nid_to_cvt_index(spec, hinfo->nid);
|
||||
|
@ -1779,6 +1802,14 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo,
|
|||
return -EINVAL;
|
||||
per_pin = get_pin(spec, pin_idx);
|
||||
|
||||
if (spec->dyn_pin_out) {
|
||||
pinctl = snd_hda_codec_read(codec, per_pin->pin_nid, 0,
|
||||
AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
|
||||
snd_hda_codec_write(codec, per_pin->pin_nid, 0,
|
||||
AC_VERB_SET_PIN_WIDGET_CONTROL,
|
||||
pinctl & ~PIN_OUT);
|
||||
}
|
||||
|
||||
snd_hda_spdif_ctls_unassign(codec, pin_idx);
|
||||
|
||||
mutex_lock(&per_pin->lock);
|
||||
|
@ -2840,6 +2871,7 @@ static int patch_nvhdmi(struct hda_codec *codec)
|
|||
return err;
|
||||
|
||||
spec = codec->spec;
|
||||
spec->dyn_pin_out = true;
|
||||
|
||||
spec->ops.chmap_cea_alloc_validate_get_type =
|
||||
nvhdmi_chmap_cea_alloc_validate_get_type;
|
||||
|
|
|
@ -1819,6 +1819,7 @@ enum {
|
|||
ALC889_FIXUP_DAC_ROUTE,
|
||||
ALC889_FIXUP_MBP_VREF,
|
||||
ALC889_FIXUP_IMAC91_VREF,
|
||||
ALC889_FIXUP_MBA11_VREF,
|
||||
ALC889_FIXUP_MBA21_VREF,
|
||||
ALC882_FIXUP_INV_DMIC,
|
||||
ALC882_FIXUP_NO_PRIMARY_HP,
|
||||
|
@ -1949,6 +1950,16 @@ static void alc889_fixup_imac91_vref(struct hda_codec *codec,
|
|||
alc889_fixup_mac_pins(codec, nids, ARRAY_SIZE(nids));
|
||||
}
|
||||
|
||||
/* Set VREF on speaker pins on mba11 */
|
||||
static void alc889_fixup_mba11_vref(struct hda_codec *codec,
|
||||
const struct hda_fixup *fix, int action)
|
||||
{
|
||||
static hda_nid_t nids[1] = { 0x18 };
|
||||
|
||||
if (action == HDA_FIXUP_ACT_INIT)
|
||||
alc889_fixup_mac_pins(codec, nids, ARRAY_SIZE(nids));
|
||||
}
|
||||
|
||||
/* Set VREF on speaker pins on mba21 */
|
||||
static void alc889_fixup_mba21_vref(struct hda_codec *codec,
|
||||
const struct hda_fixup *fix, int action)
|
||||
|
@ -2167,6 +2178,12 @@ static const struct hda_fixup alc882_fixups[] = {
|
|||
.chained = true,
|
||||
.chain_id = ALC882_FIXUP_GPIO1,
|
||||
},
|
||||
[ALC889_FIXUP_MBA11_VREF] = {
|
||||
.type = HDA_FIXUP_FUNC,
|
||||
.v.func = alc889_fixup_mba11_vref,
|
||||
.chained = true,
|
||||
.chain_id = ALC889_FIXUP_MBP_VREF,
|
||||
},
|
||||
[ALC889_FIXUP_MBA21_VREF] = {
|
||||
.type = HDA_FIXUP_FUNC,
|
||||
.v.func = alc889_fixup_mba21_vref,
|
||||
|
@ -2242,7 +2259,7 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = {
|
|||
SND_PCI_QUIRK(0x106b, 0x2c00, "MacbookPro rev3", ALC889_FIXUP_MBP_VREF),
|
||||
SND_PCI_QUIRK(0x106b, 0x3000, "iMac", ALC889_FIXUP_MBP_VREF),
|
||||
SND_PCI_QUIRK(0x106b, 0x3200, "iMac 7,1 Aluminum", ALC882_FIXUP_EAPD),
|
||||
SND_PCI_QUIRK(0x106b, 0x3400, "MacBookAir 1,1", ALC889_FIXUP_MBP_VREF),
|
||||
SND_PCI_QUIRK(0x106b, 0x3400, "MacBookAir 1,1", ALC889_FIXUP_MBA11_VREF),
|
||||
SND_PCI_QUIRK(0x106b, 0x3500, "MacBookAir 2,1", ALC889_FIXUP_MBA21_VREF),
|
||||
SND_PCI_QUIRK(0x106b, 0x3600, "Macbook 3,1", ALC889_FIXUP_MBP_VREF),
|
||||
SND_PCI_QUIRK(0x106b, 0x3800, "MacbookPro 4,1", ALC889_FIXUP_MBP_VREF),
|
||||
|
@ -3833,6 +3850,7 @@ enum {
|
|||
ALC269_FIXUP_ACER_AC700,
|
||||
ALC269_FIXUP_LIMIT_INT_MIC_BOOST,
|
||||
ALC269VB_FIXUP_ASUS_ZENBOOK,
|
||||
ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A,
|
||||
ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED,
|
||||
ALC269VB_FIXUP_ORDISSIMO_EVE2,
|
||||
ALC283_FIXUP_CHROME_BOOK,
|
||||
|
@ -4126,6 +4144,17 @@ static const struct hda_fixup alc269_fixups[] = {
|
|||
.chained = true,
|
||||
.chain_id = ALC269VB_FIXUP_DMIC,
|
||||
},
|
||||
[ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A] = {
|
||||
.type = HDA_FIXUP_VERBS,
|
||||
.v.verbs = (const struct hda_verb[]) {
|
||||
/* class-D output amp +5dB */
|
||||
{ 0x20, AC_VERB_SET_COEF_INDEX, 0x12 },
|
||||
{ 0x20, AC_VERB_SET_PROC_COEF, 0x2800 },
|
||||
{}
|
||||
},
|
||||
.chained = true,
|
||||
.chain_id = ALC269VB_FIXUP_ASUS_ZENBOOK,
|
||||
},
|
||||
[ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED] = {
|
||||
.type = HDA_FIXUP_FUNC,
|
||||
.v.func = alc269_fixup_limit_int_mic_boost,
|
||||
|
@ -4265,6 +4294,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
|
|||
SND_PCI_QUIRK(0x1028, 0x063e, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
|
||||
SND_PCI_QUIRK(0x1028, 0x063f, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
|
||||
SND_PCI_QUIRK(0x1028, 0x0640, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
|
||||
SND_PCI_QUIRK(0x1028, 0x064d, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
|
||||
SND_PCI_QUIRK(0x1028, 0x0651, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
|
||||
SND_PCI_QUIRK(0x1028, 0x0652, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
|
||||
SND_PCI_QUIRK(0x1028, 0x0653, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
|
||||
|
@ -4282,7 +4312,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
|
|||
SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
|
||||
SND_PCI_QUIRK(0x1043, 0x115d, "Asus 1015E", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
|
||||
SND_PCI_QUIRK(0x1043, 0x1427, "Asus Zenbook UX31E", ALC269VB_FIXUP_ASUS_ZENBOOK),
|
||||
SND_PCI_QUIRK(0x1043, 0x1517, "Asus Zenbook UX31A", ALC269VB_FIXUP_ASUS_ZENBOOK),
|
||||
SND_PCI_QUIRK(0x1043, 0x1517, "Asus Zenbook UX31A", ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A),
|
||||
SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_FIXUP_STEREO_DMIC),
|
||||
SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW),
|
||||
SND_PCI_QUIRK(0x1043, 0x1b13, "Asus U41SV", ALC269_FIXUP_INV_DMIC),
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
snd-oxygen-lib-objs := oxygen_io.o oxygen_lib.o oxygen_mixer.o oxygen_pcm.o
|
||||
snd-oxygen-objs := oxygen.o xonar_dg.o
|
||||
snd-oxygen-objs := oxygen.o xonar_dg_mixer.o xonar_dg.o
|
||||
snd-virtuoso-objs := virtuoso.o xonar_lib.o \
|
||||
xonar_pcm179x.o xonar_cs43xx.o xonar_wm87x6.o xonar_hdmi.o
|
||||
|
||||
|
|
|
@ -102,6 +102,9 @@
|
|||
#define CS4245_ADC_OVFL 0x02
|
||||
#define CS4245_ADC_UNDRFL 0x01
|
||||
|
||||
#define CS4245_SPI_ADDRESS_S (0x9e << 16)
|
||||
#define CS4245_SPI_WRITE_S (0 << 16)
|
||||
|
||||
#define CS4245_SPI_ADDRESS (0x9e << 16)
|
||||
#define CS4245_SPI_WRITE (0 << 16)
|
||||
#define CS4245_SPI_ADDRESS 0x9e
|
||||
#define CS4245_SPI_WRITE 0
|
||||
#define CS4245_SPI_READ 1
|
||||
|
|
|
@ -198,7 +198,7 @@ void oxygen_write_ac97(struct oxygen *chip, unsigned int codec,
|
|||
void oxygen_write_ac97_masked(struct oxygen *chip, unsigned int codec,
|
||||
unsigned int index, u16 data, u16 mask);
|
||||
|
||||
void oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data);
|
||||
int oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data);
|
||||
void oxygen_write_i2c(struct oxygen *chip, u8 device, u8 map, u8 data);
|
||||
|
||||
void oxygen_reset_uart(struct oxygen *chip);
|
||||
|
|
|
@ -194,23 +194,36 @@ void oxygen_write_ac97_masked(struct oxygen *chip, unsigned int codec,
|
|||
}
|
||||
EXPORT_SYMBOL(oxygen_write_ac97_masked);
|
||||
|
||||
void oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data)
|
||||
static int oxygen_wait_spi(struct oxygen *chip)
|
||||
{
|
||||
unsigned int count;
|
||||
|
||||
/* should not need more than 30.72 us (24 * 1.28 us) */
|
||||
count = 10;
|
||||
while ((oxygen_read8(chip, OXYGEN_SPI_CONTROL) & OXYGEN_SPI_BUSY)
|
||||
&& count > 0) {
|
||||
/*
|
||||
* Higher timeout to be sure: 200 us;
|
||||
* actual transaction should not need more than 40 us.
|
||||
*/
|
||||
for (count = 50; count > 0; count--) {
|
||||
udelay(4);
|
||||
--count;
|
||||
if ((oxygen_read8(chip, OXYGEN_SPI_CONTROL) &
|
||||
OXYGEN_SPI_BUSY) == 0)
|
||||
return 0;
|
||||
}
|
||||
snd_printk(KERN_ERR "oxygen: SPI wait timeout\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
int oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data)
|
||||
{
|
||||
/*
|
||||
* We need to wait AFTER initiating the SPI transaction,
|
||||
* otherwise read operations will not work.
|
||||
*/
|
||||
oxygen_write8(chip, OXYGEN_SPI_DATA1, data);
|
||||
oxygen_write8(chip, OXYGEN_SPI_DATA2, data >> 8);
|
||||
if (control & OXYGEN_SPI_DATA_LENGTH_3)
|
||||
oxygen_write8(chip, OXYGEN_SPI_DATA3, data >> 16);
|
||||
oxygen_write8(chip, OXYGEN_SPI_CONTROL, control);
|
||||
return oxygen_wait_spi(chip);
|
||||
}
|
||||
EXPORT_SYMBOL(oxygen_write_spi);
|
||||
|
||||
|
|
|
@ -190,6 +190,7 @@ void oxygen_update_dac_routing(struct oxygen *chip)
|
|||
if (chip->model.update_center_lfe_mix)
|
||||
chip->model.update_center_lfe_mix(chip, chip->dac_routing > 2);
|
||||
}
|
||||
EXPORT_SYMBOL(oxygen_update_dac_routing);
|
||||
|
||||
static int upmix_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
|
||||
{
|
||||
|
|
|
@ -318,6 +318,7 @@
|
|||
#define OXYGEN_PLAY_MUTE23 0x0002
|
||||
#define OXYGEN_PLAY_MUTE45 0x0004
|
||||
#define OXYGEN_PLAY_MUTE67 0x0008
|
||||
#define OXYGEN_PLAY_MUTE_MASK 0x000f
|
||||
#define OXYGEN_PLAY_MULTICH_MASK 0x0010
|
||||
#define OXYGEN_PLAY_MULTICH_I2S_DAC 0x0000
|
||||
#define OXYGEN_PLAY_MULTICH_AC97 0x0010
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* card driver for the Xonar DG/DGX
|
||||
*
|
||||
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
|
||||
*
|
||||
* Copyright (c) Roman Volkov <v1ron@mail.ru>
|
||||
*
|
||||
* This driver is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2.
|
||||
|
@ -20,27 +20,35 @@
|
|||
* Xonar DG/DGX
|
||||
* ------------
|
||||
*
|
||||
* CS4245 and CS4361 both will mute all outputs if any clock ratio
|
||||
* is invalid.
|
||||
*
|
||||
* CMI8788:
|
||||
*
|
||||
* SPI 0 -> CS4245
|
||||
*
|
||||
* Playback:
|
||||
* I²S 1 -> CS4245
|
||||
* I²S 2 -> CS4361 (center/LFE)
|
||||
* I²S 3 -> CS4361 (surround)
|
||||
* I²S 4 -> CS4361 (front)
|
||||
* Capture:
|
||||
* I²S ADC 1 <- CS4245
|
||||
*
|
||||
* GPIO 3 <- ?
|
||||
* GPIO 4 <- headphone detect
|
||||
* GPIO 5 -> route input jack to line-in (0) or mic-in (1)
|
||||
* GPIO 6 -> route input jack to line-in (0) or mic-in (1)
|
||||
* GPIO 7 -> enable rear headphone amp
|
||||
* GPIO 5 -> enable ADC analog circuit for the left channel
|
||||
* GPIO 6 -> enable ADC analog circuit for the right channel
|
||||
* GPIO 7 -> switch green rear output jack between CS4245 and and the first
|
||||
* channel of CS4361 (mechanical relay)
|
||||
* GPIO 8 -> enable output to speakers
|
||||
*
|
||||
* CS4245:
|
||||
*
|
||||
* input 0 <- mic
|
||||
* input 1 <- aux
|
||||
* input 2 <- front mic
|
||||
* input 4 <- line/mic
|
||||
* input 4 <- line
|
||||
* DAC out -> headphones
|
||||
* aux out -> front panel headphones
|
||||
*/
|
||||
|
@ -56,553 +64,214 @@
|
|||
#include "xonar_dg.h"
|
||||
#include "cs4245.h"
|
||||
|
||||
#define GPIO_MAGIC 0x0008
|
||||
#define GPIO_HP_DETECT 0x0010
|
||||
#define GPIO_INPUT_ROUTE 0x0060
|
||||
#define GPIO_HP_REAR 0x0080
|
||||
#define GPIO_OUTPUT_ENABLE 0x0100
|
||||
|
||||
struct dg {
|
||||
unsigned int output_sel;
|
||||
s8 input_vol[4][2];
|
||||
unsigned int input_sel;
|
||||
u8 hp_vol_att;
|
||||
u8 cs4245_regs[0x11];
|
||||
};
|
||||
|
||||
static void cs4245_write(struct oxygen *chip, unsigned int reg, u8 value)
|
||||
int cs4245_write_spi(struct oxygen *chip, u8 reg)
|
||||
{
|
||||
struct dg *data = chip->model_data;
|
||||
unsigned int packet;
|
||||
|
||||
oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
|
||||
OXYGEN_SPI_DATA_LENGTH_3 |
|
||||
OXYGEN_SPI_CLOCK_1280 |
|
||||
(0 << OXYGEN_SPI_CODEC_SHIFT) |
|
||||
OXYGEN_SPI_CEN_LATCH_CLOCK_HI,
|
||||
CS4245_SPI_ADDRESS |
|
||||
CS4245_SPI_WRITE |
|
||||
(reg << 8) | value);
|
||||
data->cs4245_regs[reg] = value;
|
||||
packet = reg << 8;
|
||||
packet |= (CS4245_SPI_ADDRESS | CS4245_SPI_WRITE) << 16;
|
||||
packet |= data->cs4245_shadow[reg];
|
||||
|
||||
return oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
|
||||
OXYGEN_SPI_DATA_LENGTH_3 |
|
||||
OXYGEN_SPI_CLOCK_1280 |
|
||||
(0 << OXYGEN_SPI_CODEC_SHIFT) |
|
||||
OXYGEN_SPI_CEN_LATCH_CLOCK_HI,
|
||||
packet);
|
||||
}
|
||||
|
||||
static void cs4245_write_cached(struct oxygen *chip, unsigned int reg, u8 value)
|
||||
int cs4245_read_spi(struct oxygen *chip, u8 addr)
|
||||
{
|
||||
struct dg *data = chip->model_data;
|
||||
int ret;
|
||||
|
||||
if (value != data->cs4245_regs[reg])
|
||||
cs4245_write(chip, reg, value);
|
||||
ret = oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
|
||||
OXYGEN_SPI_DATA_LENGTH_2 |
|
||||
OXYGEN_SPI_CEN_LATCH_CLOCK_HI |
|
||||
OXYGEN_SPI_CLOCK_1280 | (0 << OXYGEN_SPI_CODEC_SHIFT),
|
||||
((CS4245_SPI_ADDRESS | CS4245_SPI_WRITE) << 8) | addr);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
|
||||
OXYGEN_SPI_DATA_LENGTH_2 |
|
||||
OXYGEN_SPI_CEN_LATCH_CLOCK_HI |
|
||||
OXYGEN_SPI_CLOCK_1280 | (0 << OXYGEN_SPI_CODEC_SHIFT),
|
||||
(CS4245_SPI_ADDRESS | CS4245_SPI_READ) << 8);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
data->cs4245_shadow[addr] = oxygen_read8(chip, OXYGEN_SPI_DATA1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cs4245_registers_init(struct oxygen *chip)
|
||||
int cs4245_shadow_control(struct oxygen *chip, enum cs4245_shadow_operation op)
|
||||
{
|
||||
struct dg *data = chip->model_data;
|
||||
unsigned char addr;
|
||||
int ret;
|
||||
|
||||
cs4245_write(chip, CS4245_POWER_CTRL, CS4245_PDN);
|
||||
cs4245_write(chip, CS4245_DAC_CTRL_1,
|
||||
data->cs4245_regs[CS4245_DAC_CTRL_1]);
|
||||
cs4245_write(chip, CS4245_ADC_CTRL,
|
||||
data->cs4245_regs[CS4245_ADC_CTRL]);
|
||||
cs4245_write(chip, CS4245_SIGNAL_SEL,
|
||||
data->cs4245_regs[CS4245_SIGNAL_SEL]);
|
||||
cs4245_write(chip, CS4245_PGA_B_CTRL,
|
||||
data->cs4245_regs[CS4245_PGA_B_CTRL]);
|
||||
cs4245_write(chip, CS4245_PGA_A_CTRL,
|
||||
data->cs4245_regs[CS4245_PGA_A_CTRL]);
|
||||
cs4245_write(chip, CS4245_ANALOG_IN,
|
||||
data->cs4245_regs[CS4245_ANALOG_IN]);
|
||||
cs4245_write(chip, CS4245_DAC_A_CTRL,
|
||||
data->cs4245_regs[CS4245_DAC_A_CTRL]);
|
||||
cs4245_write(chip, CS4245_DAC_B_CTRL,
|
||||
data->cs4245_regs[CS4245_DAC_B_CTRL]);
|
||||
cs4245_write(chip, CS4245_DAC_CTRL_2,
|
||||
CS4245_DAC_SOFT | CS4245_DAC_ZERO | CS4245_INVERT_DAC);
|
||||
cs4245_write(chip, CS4245_INT_MASK, 0);
|
||||
cs4245_write(chip, CS4245_POWER_CTRL, 0);
|
||||
for (addr = 1; addr < ARRAY_SIZE(data->cs4245_shadow); addr++) {
|
||||
ret = (op == CS4245_SAVE_TO_SHADOW ?
|
||||
cs4245_read_spi(chip, addr) :
|
||||
cs4245_write_spi(chip, addr));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cs4245_init(struct oxygen *chip)
|
||||
{
|
||||
struct dg *data = chip->model_data;
|
||||
|
||||
data->cs4245_regs[CS4245_DAC_CTRL_1] =
|
||||
/* save the initial state: codec version, registers */
|
||||
cs4245_shadow_control(chip, CS4245_SAVE_TO_SHADOW);
|
||||
|
||||
/*
|
||||
* Power up the CODEC internals, enable soft ramp & zero cross, work in
|
||||
* async. mode, enable aux output from DAC. Invert DAC output as in the
|
||||
* Windows driver.
|
||||
*/
|
||||
data->cs4245_shadow[CS4245_POWER_CTRL] = 0;
|
||||
data->cs4245_shadow[CS4245_SIGNAL_SEL] =
|
||||
CS4245_A_OUT_SEL_DAC | CS4245_ASYNCH;
|
||||
data->cs4245_shadow[CS4245_DAC_CTRL_1] =
|
||||
CS4245_DAC_FM_SINGLE | CS4245_DAC_DIF_LJUST;
|
||||
data->cs4245_regs[CS4245_ADC_CTRL] =
|
||||
data->cs4245_shadow[CS4245_DAC_CTRL_2] =
|
||||
CS4245_DAC_SOFT | CS4245_DAC_ZERO | CS4245_INVERT_DAC;
|
||||
data->cs4245_shadow[CS4245_ADC_CTRL] =
|
||||
CS4245_ADC_FM_SINGLE | CS4245_ADC_DIF_LJUST;
|
||||
data->cs4245_regs[CS4245_SIGNAL_SEL] =
|
||||
CS4245_A_OUT_SEL_HIZ | CS4245_ASYNCH;
|
||||
data->cs4245_regs[CS4245_PGA_B_CTRL] = 0;
|
||||
data->cs4245_regs[CS4245_PGA_A_CTRL] = 0;
|
||||
data->cs4245_regs[CS4245_ANALOG_IN] =
|
||||
CS4245_PGA_SOFT | CS4245_PGA_ZERO | CS4245_SEL_INPUT_4;
|
||||
data->cs4245_regs[CS4245_DAC_A_CTRL] = 0;
|
||||
data->cs4245_regs[CS4245_DAC_B_CTRL] = 0;
|
||||
cs4245_registers_init(chip);
|
||||
data->cs4245_shadow[CS4245_ANALOG_IN] =
|
||||
CS4245_PGA_SOFT | CS4245_PGA_ZERO;
|
||||
data->cs4245_shadow[CS4245_PGA_B_CTRL] = 0;
|
||||
data->cs4245_shadow[CS4245_PGA_A_CTRL] = 0;
|
||||
data->cs4245_shadow[CS4245_DAC_A_CTRL] = 8;
|
||||
data->cs4245_shadow[CS4245_DAC_B_CTRL] = 8;
|
||||
|
||||
cs4245_shadow_control(chip, CS4245_LOAD_FROM_SHADOW);
|
||||
snd_component_add(chip->card, "CS4245");
|
||||
}
|
||||
|
||||
static void dg_output_enable(struct oxygen *chip)
|
||||
{
|
||||
msleep(2500);
|
||||
oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
|
||||
}
|
||||
|
||||
static void dg_init(struct oxygen *chip)
|
||||
void dg_init(struct oxygen *chip)
|
||||
{
|
||||
struct dg *data = chip->model_data;
|
||||
|
||||
data->output_sel = 0;
|
||||
data->input_sel = 3;
|
||||
data->hp_vol_att = 2 * 16;
|
||||
data->output_sel = PLAYBACK_DST_HP_FP;
|
||||
data->input_sel = CAPTURE_SRC_MIC;
|
||||
|
||||
cs4245_init(chip);
|
||||
|
||||
oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL,
|
||||
GPIO_MAGIC | GPIO_HP_DETECT);
|
||||
oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
|
||||
GPIO_INPUT_ROUTE | GPIO_HP_REAR | GPIO_OUTPUT_ENABLE);
|
||||
oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA,
|
||||
GPIO_INPUT_ROUTE | GPIO_HP_REAR);
|
||||
dg_output_enable(chip);
|
||||
oxygen_write16(chip, OXYGEN_GPIO_CONTROL,
|
||||
GPIO_OUTPUT_ENABLE | GPIO_HP_REAR | GPIO_INPUT_ROUTE);
|
||||
/* anti-pop delay, wait some time before enabling the output */
|
||||
msleep(2500);
|
||||
oxygen_write16(chip, OXYGEN_GPIO_DATA,
|
||||
GPIO_OUTPUT_ENABLE | GPIO_INPUT_ROUTE);
|
||||
}
|
||||
|
||||
static void dg_cleanup(struct oxygen *chip)
|
||||
void dg_cleanup(struct oxygen *chip)
|
||||
{
|
||||
oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
|
||||
}
|
||||
|
||||
static void dg_suspend(struct oxygen *chip)
|
||||
void dg_suspend(struct oxygen *chip)
|
||||
{
|
||||
dg_cleanup(chip);
|
||||
}
|
||||
|
||||
static void dg_resume(struct oxygen *chip)
|
||||
void dg_resume(struct oxygen *chip)
|
||||
{
|
||||
cs4245_registers_init(chip);
|
||||
dg_output_enable(chip);
|
||||
cs4245_shadow_control(chip, CS4245_LOAD_FROM_SHADOW);
|
||||
msleep(2500);
|
||||
oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
|
||||
}
|
||||
|
||||
static void set_cs4245_dac_params(struct oxygen *chip,
|
||||
void set_cs4245_dac_params(struct oxygen *chip,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct dg *data = chip->model_data;
|
||||
u8 value;
|
||||
unsigned char dac_ctrl;
|
||||
unsigned char mclk_freq;
|
||||
|
||||
value = data->cs4245_regs[CS4245_DAC_CTRL_1] & ~CS4245_DAC_FM_MASK;
|
||||
if (params_rate(params) <= 50000)
|
||||
value |= CS4245_DAC_FM_SINGLE;
|
||||
else if (params_rate(params) <= 100000)
|
||||
value |= CS4245_DAC_FM_DOUBLE;
|
||||
else
|
||||
value |= CS4245_DAC_FM_QUAD;
|
||||
cs4245_write_cached(chip, CS4245_DAC_CTRL_1, value);
|
||||
dac_ctrl = data->cs4245_shadow[CS4245_DAC_CTRL_1] & ~CS4245_DAC_FM_MASK;
|
||||
mclk_freq = data->cs4245_shadow[CS4245_MCLK_FREQ] & ~CS4245_MCLK1_MASK;
|
||||
if (params_rate(params) <= 50000) {
|
||||
dac_ctrl |= CS4245_DAC_FM_SINGLE;
|
||||
mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK1_SHIFT;
|
||||
} else if (params_rate(params) <= 100000) {
|
||||
dac_ctrl |= CS4245_DAC_FM_DOUBLE;
|
||||
mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK1_SHIFT;
|
||||
} else {
|
||||
dac_ctrl |= CS4245_DAC_FM_QUAD;
|
||||
mclk_freq |= CS4245_MCLK_2 << CS4245_MCLK1_SHIFT;
|
||||
}
|
||||
data->cs4245_shadow[CS4245_DAC_CTRL_1] = dac_ctrl;
|
||||
data->cs4245_shadow[CS4245_MCLK_FREQ] = mclk_freq;
|
||||
cs4245_write_spi(chip, CS4245_DAC_CTRL_1);
|
||||
cs4245_write_spi(chip, CS4245_MCLK_FREQ);
|
||||
}
|
||||
|
||||
static void set_cs4245_adc_params(struct oxygen *chip,
|
||||
void set_cs4245_adc_params(struct oxygen *chip,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct dg *data = chip->model_data;
|
||||
u8 value;
|
||||
unsigned char adc_ctrl;
|
||||
unsigned char mclk_freq;
|
||||
|
||||
value = data->cs4245_regs[CS4245_ADC_CTRL] & ~CS4245_ADC_FM_MASK;
|
||||
if (params_rate(params) <= 50000)
|
||||
value |= CS4245_ADC_FM_SINGLE;
|
||||
else if (params_rate(params) <= 100000)
|
||||
value |= CS4245_ADC_FM_DOUBLE;
|
||||
else
|
||||
value |= CS4245_ADC_FM_QUAD;
|
||||
cs4245_write_cached(chip, CS4245_ADC_CTRL, value);
|
||||
adc_ctrl = data->cs4245_shadow[CS4245_ADC_CTRL] & ~CS4245_ADC_FM_MASK;
|
||||
mclk_freq = data->cs4245_shadow[CS4245_MCLK_FREQ] & ~CS4245_MCLK2_MASK;
|
||||
if (params_rate(params) <= 50000) {
|
||||
adc_ctrl |= CS4245_ADC_FM_SINGLE;
|
||||
mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK2_SHIFT;
|
||||
} else if (params_rate(params) <= 100000) {
|
||||
adc_ctrl |= CS4245_ADC_FM_DOUBLE;
|
||||
mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK2_SHIFT;
|
||||
} else {
|
||||
adc_ctrl |= CS4245_ADC_FM_QUAD;
|
||||
mclk_freq |= CS4245_MCLK_2 << CS4245_MCLK2_SHIFT;
|
||||
}
|
||||
data->cs4245_shadow[CS4245_ADC_CTRL] = adc_ctrl;
|
||||
data->cs4245_shadow[CS4245_MCLK_FREQ] = mclk_freq;
|
||||
cs4245_write_spi(chip, CS4245_ADC_CTRL);
|
||||
cs4245_write_spi(chip, CS4245_MCLK_FREQ);
|
||||
}
|
||||
|
||||
static inline unsigned int shift_bits(unsigned int value,
|
||||
unsigned int shift_from,
|
||||
unsigned int shift_to,
|
||||
unsigned int mask)
|
||||
{
|
||||
if (shift_from < shift_to)
|
||||
return (value << (shift_to - shift_from)) & mask;
|
||||
else
|
||||
return (value >> (shift_from - shift_to)) & mask;
|
||||
}
|
||||
|
||||
static unsigned int adjust_dg_dac_routing(struct oxygen *chip,
|
||||
unsigned int adjust_dg_dac_routing(struct oxygen *chip,
|
||||
unsigned int play_routing)
|
||||
{
|
||||
return (play_routing & OXYGEN_PLAY_DAC0_SOURCE_MASK) |
|
||||
shift_bits(play_routing,
|
||||
OXYGEN_PLAY_DAC2_SOURCE_SHIFT,
|
||||
OXYGEN_PLAY_DAC1_SOURCE_SHIFT,
|
||||
OXYGEN_PLAY_DAC1_SOURCE_MASK) |
|
||||
shift_bits(play_routing,
|
||||
OXYGEN_PLAY_DAC1_SOURCE_SHIFT,
|
||||
OXYGEN_PLAY_DAC2_SOURCE_SHIFT,
|
||||
OXYGEN_PLAY_DAC2_SOURCE_MASK) |
|
||||
shift_bits(play_routing,
|
||||
OXYGEN_PLAY_DAC0_SOURCE_SHIFT,
|
||||
OXYGEN_PLAY_DAC3_SOURCE_SHIFT,
|
||||
OXYGEN_PLAY_DAC3_SOURCE_MASK);
|
||||
}
|
||||
|
||||
static int output_switch_info(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_info *info)
|
||||
{
|
||||
static const char *const names[3] = {
|
||||
"Speakers", "Headphones", "FP Headphones"
|
||||
};
|
||||
|
||||
return snd_ctl_enum_info(info, 1, 3, names);
|
||||
}
|
||||
|
||||
static int output_switch_get(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
struct dg *data = chip->model_data;
|
||||
unsigned int routing = 0;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
value->value.enumerated.item[0] = data->output_sel;
|
||||
mutex_unlock(&chip->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int output_switch_put(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
struct dg *data = chip->model_data;
|
||||
u8 reg;
|
||||
int changed;
|
||||
|
||||
if (value->value.enumerated.item[0] > 2)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
changed = value->value.enumerated.item[0] != data->output_sel;
|
||||
if (changed) {
|
||||
data->output_sel = value->value.enumerated.item[0];
|
||||
|
||||
reg = data->cs4245_regs[CS4245_SIGNAL_SEL] &
|
||||
~CS4245_A_OUT_SEL_MASK;
|
||||
reg |= data->output_sel == 2 ?
|
||||
CS4245_A_OUT_SEL_DAC : CS4245_A_OUT_SEL_HIZ;
|
||||
cs4245_write_cached(chip, CS4245_SIGNAL_SEL, reg);
|
||||
|
||||
cs4245_write_cached(chip, CS4245_DAC_A_CTRL,
|
||||
data->output_sel ? data->hp_vol_att : 0);
|
||||
cs4245_write_cached(chip, CS4245_DAC_B_CTRL,
|
||||
data->output_sel ? data->hp_vol_att : 0);
|
||||
|
||||
oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
|
||||
data->output_sel == 1 ? GPIO_HP_REAR : 0,
|
||||
GPIO_HP_REAR);
|
||||
switch (data->output_sel) {
|
||||
case PLAYBACK_DST_HP:
|
||||
case PLAYBACK_DST_HP_FP:
|
||||
oxygen_write8_masked(chip, OXYGEN_PLAY_ROUTING,
|
||||
OXYGEN_PLAY_MUTE23 | OXYGEN_PLAY_MUTE45 |
|
||||
OXYGEN_PLAY_MUTE67, OXYGEN_PLAY_MUTE_MASK);
|
||||
break;
|
||||
case PLAYBACK_DST_MULTICH:
|
||||
routing = (0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) |
|
||||
(2 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) |
|
||||
(1 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) |
|
||||
(0 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT);
|
||||
oxygen_write8_masked(chip, OXYGEN_PLAY_ROUTING,
|
||||
OXYGEN_PLAY_MUTE01, OXYGEN_PLAY_MUTE_MASK);
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&chip->mutex);
|
||||
return changed;
|
||||
return routing;
|
||||
}
|
||||
|
||||
static int hp_volume_offset_info(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_info *info)
|
||||
{
|
||||
static const char *const names[3] = {
|
||||
"< 64 ohms", "64-150 ohms", "150-300 ohms"
|
||||
};
|
||||
|
||||
return snd_ctl_enum_info(info, 1, 3, names);
|
||||
}
|
||||
|
||||
static int hp_volume_offset_get(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
struct dg *data = chip->model_data;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
if (data->hp_vol_att > 2 * 7)
|
||||
value->value.enumerated.item[0] = 0;
|
||||
else if (data->hp_vol_att > 0)
|
||||
value->value.enumerated.item[0] = 1;
|
||||
else
|
||||
value->value.enumerated.item[0] = 2;
|
||||
mutex_unlock(&chip->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hp_volume_offset_put(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
static const s8 atts[3] = { 2 * 16, 2 * 7, 0 };
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
struct dg *data = chip->model_data;
|
||||
s8 att;
|
||||
int changed;
|
||||
|
||||
if (value->value.enumerated.item[0] > 2)
|
||||
return -EINVAL;
|
||||
att = atts[value->value.enumerated.item[0]];
|
||||
mutex_lock(&chip->mutex);
|
||||
changed = att != data->hp_vol_att;
|
||||
if (changed) {
|
||||
data->hp_vol_att = att;
|
||||
if (data->output_sel) {
|
||||
cs4245_write_cached(chip, CS4245_DAC_A_CTRL, att);
|
||||
cs4245_write_cached(chip, CS4245_DAC_B_CTRL, att);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&chip->mutex);
|
||||
return changed;
|
||||
}
|
||||
|
||||
static int input_vol_info(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_info *info)
|
||||
{
|
||||
info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
info->count = 2;
|
||||
info->value.integer.min = 2 * -12;
|
||||
info->value.integer.max = 2 * 12;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int input_vol_get(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
struct dg *data = chip->model_data;
|
||||
unsigned int idx = ctl->private_value;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
value->value.integer.value[0] = data->input_vol[idx][0];
|
||||
value->value.integer.value[1] = data->input_vol[idx][1];
|
||||
mutex_unlock(&chip->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int input_vol_put(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
struct dg *data = chip->model_data;
|
||||
unsigned int idx = ctl->private_value;
|
||||
int changed = 0;
|
||||
|
||||
if (value->value.integer.value[0] < 2 * -12 ||
|
||||
value->value.integer.value[0] > 2 * 12 ||
|
||||
value->value.integer.value[1] < 2 * -12 ||
|
||||
value->value.integer.value[1] > 2 * 12)
|
||||
return -EINVAL;
|
||||
mutex_lock(&chip->mutex);
|
||||
changed = data->input_vol[idx][0] != value->value.integer.value[0] ||
|
||||
data->input_vol[idx][1] != value->value.integer.value[1];
|
||||
if (changed) {
|
||||
data->input_vol[idx][0] = value->value.integer.value[0];
|
||||
data->input_vol[idx][1] = value->value.integer.value[1];
|
||||
if (idx == data->input_sel) {
|
||||
cs4245_write_cached(chip, CS4245_PGA_A_CTRL,
|
||||
data->input_vol[idx][0]);
|
||||
cs4245_write_cached(chip, CS4245_PGA_B_CTRL,
|
||||
data->input_vol[idx][1]);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&chip->mutex);
|
||||
return changed;
|
||||
}
|
||||
|
||||
static DECLARE_TLV_DB_SCALE(cs4245_pga_db_scale, -1200, 50, 0);
|
||||
|
||||
static int input_sel_info(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_info *info)
|
||||
{
|
||||
static const char *const names[4] = {
|
||||
"Mic", "Aux", "Front Mic", "Line"
|
||||
};
|
||||
|
||||
return snd_ctl_enum_info(info, 1, 4, names);
|
||||
}
|
||||
|
||||
static int input_sel_get(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
struct dg *data = chip->model_data;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
value->value.enumerated.item[0] = data->input_sel;
|
||||
mutex_unlock(&chip->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int input_sel_put(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
static const u8 sel_values[4] = {
|
||||
CS4245_SEL_MIC,
|
||||
CS4245_SEL_INPUT_1,
|
||||
CS4245_SEL_INPUT_2,
|
||||
CS4245_SEL_INPUT_4
|
||||
};
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
struct dg *data = chip->model_data;
|
||||
int changed;
|
||||
|
||||
if (value->value.enumerated.item[0] > 3)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
changed = value->value.enumerated.item[0] != data->input_sel;
|
||||
if (changed) {
|
||||
data->input_sel = value->value.enumerated.item[0];
|
||||
|
||||
cs4245_write(chip, CS4245_ANALOG_IN,
|
||||
(data->cs4245_regs[CS4245_ANALOG_IN] &
|
||||
~CS4245_SEL_MASK) |
|
||||
sel_values[data->input_sel]);
|
||||
|
||||
cs4245_write_cached(chip, CS4245_PGA_A_CTRL,
|
||||
data->input_vol[data->input_sel][0]);
|
||||
cs4245_write_cached(chip, CS4245_PGA_B_CTRL,
|
||||
data->input_vol[data->input_sel][1]);
|
||||
|
||||
oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
|
||||
data->input_sel ? 0 : GPIO_INPUT_ROUTE,
|
||||
GPIO_INPUT_ROUTE);
|
||||
}
|
||||
mutex_unlock(&chip->mutex);
|
||||
return changed;
|
||||
}
|
||||
|
||||
static int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
|
||||
{
|
||||
static const char *const names[2] = { "Active", "Frozen" };
|
||||
|
||||
return snd_ctl_enum_info(info, 1, 2, names);
|
||||
}
|
||||
|
||||
static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
struct dg *data = chip->model_data;
|
||||
|
||||
value->value.enumerated.item[0] =
|
||||
!!(data->cs4245_regs[CS4245_ADC_CTRL] & CS4245_HPF_FREEZE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
struct dg *data = chip->model_data;
|
||||
u8 reg;
|
||||
int changed;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
reg = data->cs4245_regs[CS4245_ADC_CTRL] & ~CS4245_HPF_FREEZE;
|
||||
if (value->value.enumerated.item[0])
|
||||
reg |= CS4245_HPF_FREEZE;
|
||||
changed = reg != data->cs4245_regs[CS4245_ADC_CTRL];
|
||||
if (changed)
|
||||
cs4245_write(chip, CS4245_ADC_CTRL, reg);
|
||||
mutex_unlock(&chip->mutex);
|
||||
return changed;
|
||||
}
|
||||
|
||||
#define INPUT_VOLUME(xname, index) { \
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
|
||||
.name = xname, \
|
||||
.info = input_vol_info, \
|
||||
.get = input_vol_get, \
|
||||
.put = input_vol_put, \
|
||||
.tlv = { .p = cs4245_pga_db_scale }, \
|
||||
.private_value = index, \
|
||||
}
|
||||
static const struct snd_kcontrol_new dg_controls[] = {
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Analog Output Playback Enum",
|
||||
.info = output_switch_info,
|
||||
.get = output_switch_get,
|
||||
.put = output_switch_put,
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Headphones Impedance Playback Enum",
|
||||
.info = hp_volume_offset_info,
|
||||
.get = hp_volume_offset_get,
|
||||
.put = hp_volume_offset_put,
|
||||
},
|
||||
INPUT_VOLUME("Mic Capture Volume", 0),
|
||||
INPUT_VOLUME("Aux Capture Volume", 1),
|
||||
INPUT_VOLUME("Front Mic Capture Volume", 2),
|
||||
INPUT_VOLUME("Line Capture Volume", 3),
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Capture Source",
|
||||
.info = input_sel_info,
|
||||
.get = input_sel_get,
|
||||
.put = input_sel_put,
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "ADC High-pass Filter Capture Enum",
|
||||
.info = hpf_info,
|
||||
.get = hpf_get,
|
||||
.put = hpf_put,
|
||||
},
|
||||
};
|
||||
|
||||
static int dg_control_filter(struct snd_kcontrol_new *template)
|
||||
{
|
||||
if (!strncmp(template->name, "Master Playback ", 16))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dg_mixer_init(struct oxygen *chip)
|
||||
{
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(dg_controls); ++i) {
|
||||
err = snd_ctl_add(chip->card,
|
||||
snd_ctl_new1(&dg_controls[i], chip));
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dump_cs4245_registers(struct oxygen *chip,
|
||||
void dump_cs4245_registers(struct oxygen *chip,
|
||||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
struct dg *data = chip->model_data;
|
||||
unsigned int i;
|
||||
unsigned int addr;
|
||||
|
||||
snd_iprintf(buffer, "\nCS4245:");
|
||||
for (i = 1; i <= 0x10; ++i)
|
||||
snd_iprintf(buffer, " %02x", data->cs4245_regs[i]);
|
||||
cs4245_read_spi(chip, CS4245_INT_STATUS);
|
||||
for (addr = 1; addr < ARRAY_SIZE(data->cs4245_shadow); addr++)
|
||||
snd_iprintf(buffer, " %02x", data->cs4245_shadow[addr]);
|
||||
snd_iprintf(buffer, "\n");
|
||||
}
|
||||
|
||||
struct oxygen_model model_xonar_dg = {
|
||||
.longname = "C-Media Oxygen HD Audio",
|
||||
.chip = "CMI8786",
|
||||
.init = dg_init,
|
||||
.control_filter = dg_control_filter,
|
||||
.mixer_init = dg_mixer_init,
|
||||
.cleanup = dg_cleanup,
|
||||
.suspend = dg_suspend,
|
||||
.resume = dg_resume,
|
||||
.set_dac_params = set_cs4245_dac_params,
|
||||
.set_adc_params = set_cs4245_adc_params,
|
||||
.adjust_dac_routing = adjust_dg_dac_routing,
|
||||
.dump_registers = dump_cs4245_registers,
|
||||
.model_data_size = sizeof(struct dg),
|
||||
.device_config = PLAYBACK_0_TO_I2S |
|
||||
PLAYBACK_1_TO_SPDIF |
|
||||
CAPTURE_0_FROM_I2S_2 |
|
||||
CAPTURE_1_FROM_SPDIF,
|
||||
.dac_channels_pcm = 6,
|
||||
.dac_channels_mixer = 0,
|
||||
.function_flags = OXYGEN_FUNCTION_SPI,
|
||||
.dac_mclks = OXYGEN_MCLKS(256, 128, 128),
|
||||
.adc_mclks = OXYGEN_MCLKS(256, 128, 128),
|
||||
.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
|
||||
.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
|
||||
};
|
||||
|
|
|
@ -3,6 +3,54 @@
|
|||
|
||||
#include "oxygen.h"
|
||||
|
||||
#define GPIO_MAGIC 0x0008
|
||||
#define GPIO_HP_DETECT 0x0010
|
||||
#define GPIO_INPUT_ROUTE 0x0060
|
||||
#define GPIO_HP_REAR 0x0080
|
||||
#define GPIO_OUTPUT_ENABLE 0x0100
|
||||
|
||||
#define CAPTURE_SRC_MIC 0
|
||||
#define CAPTURE_SRC_FP_MIC 1
|
||||
#define CAPTURE_SRC_LINE 2
|
||||
#define CAPTURE_SRC_AUX 3
|
||||
|
||||
#define PLAYBACK_DST_HP 0
|
||||
#define PLAYBACK_DST_HP_FP 1
|
||||
#define PLAYBACK_DST_MULTICH 2
|
||||
|
||||
enum cs4245_shadow_operation {
|
||||
CS4245_SAVE_TO_SHADOW,
|
||||
CS4245_LOAD_FROM_SHADOW
|
||||
};
|
||||
|
||||
struct dg {
|
||||
/* shadow copy of the CS4245 register space */
|
||||
unsigned char cs4245_shadow[17];
|
||||
/* output select: headphone/speakers */
|
||||
unsigned char output_sel;
|
||||
/* volumes for all capture sources */
|
||||
char input_vol[4][2];
|
||||
/* input select: mic/fp mic/line/aux */
|
||||
unsigned char input_sel;
|
||||
};
|
||||
|
||||
/* Xonar DG control routines */
|
||||
int cs4245_write_spi(struct oxygen *chip, u8 reg);
|
||||
int cs4245_read_spi(struct oxygen *chip, u8 reg);
|
||||
int cs4245_shadow_control(struct oxygen *chip, enum cs4245_shadow_operation op);
|
||||
void dg_init(struct oxygen *chip);
|
||||
void set_cs4245_dac_params(struct oxygen *chip,
|
||||
struct snd_pcm_hw_params *params);
|
||||
void set_cs4245_adc_params(struct oxygen *chip,
|
||||
struct snd_pcm_hw_params *params);
|
||||
unsigned int adjust_dg_dac_routing(struct oxygen *chip,
|
||||
unsigned int play_routing);
|
||||
void dump_cs4245_registers(struct oxygen *chip,
|
||||
struct snd_info_buffer *buffer);
|
||||
void dg_suspend(struct oxygen *chip);
|
||||
void dg_resume(struct oxygen *chip);
|
||||
void dg_cleanup(struct oxygen *chip);
|
||||
|
||||
extern struct oxygen_model model_xonar_dg;
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,477 @@
|
|||
/*
|
||||
* Mixer controls for the Xonar DG/DGX
|
||||
*
|
||||
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
|
||||
* Copyright (c) Roman Volkov <v1ron@mail.ru>
|
||||
*
|
||||
* This driver is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2.
|
||||
*
|
||||
* 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 driver; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/pci.h>
|
||||
#include <linux/delay.h>
|
||||
#include <sound/control.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/info.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/tlv.h>
|
||||
#include "oxygen.h"
|
||||
#include "xonar_dg.h"
|
||||
#include "cs4245.h"
|
||||
|
||||
/* analog output select */
|
||||
|
||||
static int output_select_apply(struct oxygen *chip)
|
||||
{
|
||||
struct dg *data = chip->model_data;
|
||||
|
||||
data->cs4245_shadow[CS4245_SIGNAL_SEL] &= ~CS4245_A_OUT_SEL_MASK;
|
||||
if (data->output_sel == PLAYBACK_DST_HP) {
|
||||
/* mute FP (aux output) amplifier, switch rear jack to CS4245 */
|
||||
oxygen_set_bits8(chip, OXYGEN_GPIO_DATA, GPIO_HP_REAR);
|
||||
} else if (data->output_sel == PLAYBACK_DST_HP_FP) {
|
||||
/*
|
||||
* Unmute FP amplifier, switch rear jack to CS4361;
|
||||
* I2S channels 2,3,4 should be inactive.
|
||||
*/
|
||||
oxygen_clear_bits8(chip, OXYGEN_GPIO_DATA, GPIO_HP_REAR);
|
||||
data->cs4245_shadow[CS4245_SIGNAL_SEL] |= CS4245_A_OUT_SEL_DAC;
|
||||
} else {
|
||||
/*
|
||||
* 2.0, 4.0, 5.1: switch to CS4361, mute FP amp.,
|
||||
* and change playback routing.
|
||||
*/
|
||||
oxygen_clear_bits8(chip, OXYGEN_GPIO_DATA, GPIO_HP_REAR);
|
||||
}
|
||||
return cs4245_write_spi(chip, CS4245_SIGNAL_SEL);
|
||||
}
|
||||
|
||||
static int output_select_info(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_info *info)
|
||||
{
|
||||
static const char *const names[3] = {
|
||||
"Stereo Headphones",
|
||||
"Stereo Headphones FP",
|
||||
"Multichannel",
|
||||
};
|
||||
|
||||
return snd_ctl_enum_info(info, 1, 3, names);
|
||||
}
|
||||
|
||||
static int output_select_get(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
struct dg *data = chip->model_data;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
value->value.enumerated.item[0] = data->output_sel;
|
||||
mutex_unlock(&chip->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int output_select_put(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
struct dg *data = chip->model_data;
|
||||
unsigned int new = value->value.enumerated.item[0];
|
||||
int changed = 0;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
if (data->output_sel != new) {
|
||||
data->output_sel = new;
|
||||
ret = output_select_apply(chip);
|
||||
changed = ret >= 0 ? 1 : ret;
|
||||
oxygen_update_dac_routing(chip);
|
||||
}
|
||||
mutex_unlock(&chip->mutex);
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
/* CS4245 Headphone Channels A&B Volume Control */
|
||||
|
||||
static int hp_stereo_volume_info(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_info *info)
|
||||
{
|
||||
info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
info->count = 2;
|
||||
info->value.integer.min = 0;
|
||||
info->value.integer.max = 255;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hp_stereo_volume_get(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *val)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
struct dg *data = chip->model_data;
|
||||
unsigned int tmp;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
tmp = (~data->cs4245_shadow[CS4245_DAC_A_CTRL]) & 255;
|
||||
val->value.integer.value[0] = tmp;
|
||||
tmp = (~data->cs4245_shadow[CS4245_DAC_B_CTRL]) & 255;
|
||||
val->value.integer.value[1] = tmp;
|
||||
mutex_unlock(&chip->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hp_stereo_volume_put(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *val)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
struct dg *data = chip->model_data;
|
||||
int ret;
|
||||
int changed = 0;
|
||||
long new1 = val->value.integer.value[0];
|
||||
long new2 = val->value.integer.value[1];
|
||||
|
||||
if ((new1 > 255) || (new1 < 0) || (new2 > 255) || (new2 < 0))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
if ((data->cs4245_shadow[CS4245_DAC_A_CTRL] != ~new1) ||
|
||||
(data->cs4245_shadow[CS4245_DAC_B_CTRL] != ~new2)) {
|
||||
data->cs4245_shadow[CS4245_DAC_A_CTRL] = ~new1;
|
||||
data->cs4245_shadow[CS4245_DAC_B_CTRL] = ~new2;
|
||||
ret = cs4245_write_spi(chip, CS4245_DAC_A_CTRL);
|
||||
if (ret >= 0)
|
||||
ret = cs4245_write_spi(chip, CS4245_DAC_B_CTRL);
|
||||
changed = ret >= 0 ? 1 : ret;
|
||||
}
|
||||
mutex_unlock(&chip->mutex);
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
/* Headphone Mute */
|
||||
|
||||
static int hp_mute_get(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *val)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
struct dg *data = chip->model_data;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
val->value.integer.value[0] =
|
||||
!(data->cs4245_shadow[CS4245_DAC_CTRL_1] & CS4245_MUTE_DAC);
|
||||
mutex_unlock(&chip->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hp_mute_put(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *val)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
struct dg *data = chip->model_data;
|
||||
int ret;
|
||||
int changed;
|
||||
|
||||
if (val->value.integer.value[0] > 1)
|
||||
return -EINVAL;
|
||||
mutex_lock(&chip->mutex);
|
||||
data->cs4245_shadow[CS4245_DAC_CTRL_1] &= ~CS4245_MUTE_DAC;
|
||||
data->cs4245_shadow[CS4245_DAC_CTRL_1] |=
|
||||
(~val->value.integer.value[0] << 2) & CS4245_MUTE_DAC;
|
||||
ret = cs4245_write_spi(chip, CS4245_DAC_CTRL_1);
|
||||
changed = ret >= 0 ? 1 : ret;
|
||||
mutex_unlock(&chip->mutex);
|
||||
return changed;
|
||||
}
|
||||
|
||||
/* capture volume for all sources */
|
||||
|
||||
static int input_volume_apply(struct oxygen *chip, char left, char right)
|
||||
{
|
||||
struct dg *data = chip->model_data;
|
||||
int ret;
|
||||
|
||||
data->cs4245_shadow[CS4245_PGA_A_CTRL] = left;
|
||||
data->cs4245_shadow[CS4245_PGA_B_CTRL] = right;
|
||||
ret = cs4245_write_spi(chip, CS4245_PGA_A_CTRL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return cs4245_write_spi(chip, CS4245_PGA_B_CTRL);
|
||||
}
|
||||
|
||||
static int input_vol_info(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_info *info)
|
||||
{
|
||||
info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
info->count = 2;
|
||||
info->value.integer.min = 2 * -12;
|
||||
info->value.integer.max = 2 * 12;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int input_vol_get(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
struct dg *data = chip->model_data;
|
||||
unsigned int idx = ctl->private_value;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
value->value.integer.value[0] = data->input_vol[idx][0];
|
||||
value->value.integer.value[1] = data->input_vol[idx][1];
|
||||
mutex_unlock(&chip->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int input_vol_put(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
struct dg *data = chip->model_data;
|
||||
unsigned int idx = ctl->private_value;
|
||||
int changed = 0;
|
||||
int ret = 0;
|
||||
|
||||
if (value->value.integer.value[0] < 2 * -12 ||
|
||||
value->value.integer.value[0] > 2 * 12 ||
|
||||
value->value.integer.value[1] < 2 * -12 ||
|
||||
value->value.integer.value[1] > 2 * 12)
|
||||
return -EINVAL;
|
||||
mutex_lock(&chip->mutex);
|
||||
changed = data->input_vol[idx][0] != value->value.integer.value[0] ||
|
||||
data->input_vol[idx][1] != value->value.integer.value[1];
|
||||
if (changed) {
|
||||
data->input_vol[idx][0] = value->value.integer.value[0];
|
||||
data->input_vol[idx][1] = value->value.integer.value[1];
|
||||
if (idx == data->input_sel) {
|
||||
ret = input_volume_apply(chip,
|
||||
data->input_vol[idx][0],
|
||||
data->input_vol[idx][1]);
|
||||
}
|
||||
changed = ret >= 0 ? 1 : ret;
|
||||
}
|
||||
mutex_unlock(&chip->mutex);
|
||||
return changed;
|
||||
}
|
||||
|
||||
/* Capture Source */
|
||||
|
||||
static int input_source_apply(struct oxygen *chip)
|
||||
{
|
||||
struct dg *data = chip->model_data;
|
||||
|
||||
data->cs4245_shadow[CS4245_ANALOG_IN] &= ~CS4245_SEL_MASK;
|
||||
if (data->input_sel == CAPTURE_SRC_FP_MIC)
|
||||
data->cs4245_shadow[CS4245_ANALOG_IN] |= CS4245_SEL_INPUT_2;
|
||||
else if (data->input_sel == CAPTURE_SRC_LINE)
|
||||
data->cs4245_shadow[CS4245_ANALOG_IN] |= CS4245_SEL_INPUT_4;
|
||||
else if (data->input_sel != CAPTURE_SRC_MIC)
|
||||
data->cs4245_shadow[CS4245_ANALOG_IN] |= CS4245_SEL_INPUT_1;
|
||||
return cs4245_write_spi(chip, CS4245_ANALOG_IN);
|
||||
}
|
||||
|
||||
static int input_sel_info(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_info *info)
|
||||
{
|
||||
static const char *const names[4] = {
|
||||
"Mic", "Front Mic", "Line", "Aux"
|
||||
};
|
||||
|
||||
return snd_ctl_enum_info(info, 1, 4, names);
|
||||
}
|
||||
|
||||
static int input_sel_get(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
struct dg *data = chip->model_data;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
value->value.enumerated.item[0] = data->input_sel;
|
||||
mutex_unlock(&chip->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int input_sel_put(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
struct dg *data = chip->model_data;
|
||||
int changed;
|
||||
int ret;
|
||||
|
||||
if (value->value.enumerated.item[0] > 3)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
changed = value->value.enumerated.item[0] != data->input_sel;
|
||||
if (changed) {
|
||||
data->input_sel = value->value.enumerated.item[0];
|
||||
|
||||
ret = input_source_apply(chip);
|
||||
if (ret >= 0)
|
||||
ret = input_volume_apply(chip,
|
||||
data->input_vol[data->input_sel][0],
|
||||
data->input_vol[data->input_sel][1]);
|
||||
changed = ret >= 0 ? 1 : ret;
|
||||
}
|
||||
mutex_unlock(&chip->mutex);
|
||||
return changed;
|
||||
}
|
||||
|
||||
/* ADC high-pass filter */
|
||||
|
||||
static int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
|
||||
{
|
||||
static const char *const names[2] = { "Active", "Frozen" };
|
||||
|
||||
return snd_ctl_enum_info(info, 1, 2, names);
|
||||
}
|
||||
|
||||
static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
struct dg *data = chip->model_data;
|
||||
|
||||
value->value.enumerated.item[0] =
|
||||
!!(data->cs4245_shadow[CS4245_ADC_CTRL] & CS4245_HPF_FREEZE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
struct dg *data = chip->model_data;
|
||||
u8 reg;
|
||||
int changed;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
reg = data->cs4245_shadow[CS4245_ADC_CTRL] & ~CS4245_HPF_FREEZE;
|
||||
if (value->value.enumerated.item[0])
|
||||
reg |= CS4245_HPF_FREEZE;
|
||||
changed = reg != data->cs4245_shadow[CS4245_ADC_CTRL];
|
||||
if (changed) {
|
||||
data->cs4245_shadow[CS4245_ADC_CTRL] = reg;
|
||||
cs4245_write_spi(chip, CS4245_ADC_CTRL);
|
||||
}
|
||||
mutex_unlock(&chip->mutex);
|
||||
return changed;
|
||||
}
|
||||
|
||||
#define INPUT_VOLUME(xname, index) { \
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
|
||||
.name = xname, \
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
|
||||
SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
|
||||
.info = input_vol_info, \
|
||||
.get = input_vol_get, \
|
||||
.put = input_vol_put, \
|
||||
.tlv = { .p = pga_db_scale }, \
|
||||
.private_value = index, \
|
||||
}
|
||||
static const DECLARE_TLV_DB_MINMAX(hp_db_scale, -12550, 0);
|
||||
static const DECLARE_TLV_DB_MINMAX(pga_db_scale, -1200, 1200);
|
||||
static const struct snd_kcontrol_new dg_controls[] = {
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Analog Output Playback Enum",
|
||||
.info = output_select_info,
|
||||
.get = output_select_get,
|
||||
.put = output_select_put,
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Headphone Playback Volume",
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
|
||||
SNDRV_CTL_ELEM_ACCESS_TLV_READ,
|
||||
.info = hp_stereo_volume_info,
|
||||
.get = hp_stereo_volume_get,
|
||||
.put = hp_stereo_volume_put,
|
||||
.tlv = { .p = hp_db_scale, },
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Headphone Playback Switch",
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
|
||||
.info = snd_ctl_boolean_mono_info,
|
||||
.get = hp_mute_get,
|
||||
.put = hp_mute_put,
|
||||
},
|
||||
INPUT_VOLUME("Mic Capture Volume", CAPTURE_SRC_MIC),
|
||||
INPUT_VOLUME("Front Mic Capture Volume", CAPTURE_SRC_FP_MIC),
|
||||
INPUT_VOLUME("Line Capture Volume", CAPTURE_SRC_LINE),
|
||||
INPUT_VOLUME("Aux Capture Volume", CAPTURE_SRC_AUX),
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Capture Source",
|
||||
.info = input_sel_info,
|
||||
.get = input_sel_get,
|
||||
.put = input_sel_put,
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "ADC High-pass Filter Capture Enum",
|
||||
.info = hpf_info,
|
||||
.get = hpf_get,
|
||||
.put = hpf_put,
|
||||
},
|
||||
};
|
||||
|
||||
static int dg_control_filter(struct snd_kcontrol_new *template)
|
||||
{
|
||||
if (!strncmp(template->name, "Master Playback ", 16))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dg_mixer_init(struct oxygen *chip)
|
||||
{
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
output_select_apply(chip);
|
||||
input_source_apply(chip);
|
||||
oxygen_update_dac_routing(chip);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(dg_controls); ++i) {
|
||||
err = snd_ctl_add(chip->card,
|
||||
snd_ctl_new1(&dg_controls[i], chip));
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct oxygen_model model_xonar_dg = {
|
||||
.longname = "C-Media Oxygen HD Audio",
|
||||
.chip = "CMI8786",
|
||||
.init = dg_init,
|
||||
.control_filter = dg_control_filter,
|
||||
.mixer_init = dg_mixer_init,
|
||||
.cleanup = dg_cleanup,
|
||||
.suspend = dg_suspend,
|
||||
.resume = dg_resume,
|
||||
.set_dac_params = set_cs4245_dac_params,
|
||||
.set_adc_params = set_cs4245_adc_params,
|
||||
.adjust_dac_routing = adjust_dg_dac_routing,
|
||||
.dump_registers = dump_cs4245_registers,
|
||||
.model_data_size = sizeof(struct dg),
|
||||
.device_config = PLAYBACK_0_TO_I2S |
|
||||
PLAYBACK_1_TO_SPDIF |
|
||||
CAPTURE_0_FROM_I2S_1 |
|
||||
CAPTURE_1_FROM_SPDIF,
|
||||
.dac_channels_pcm = 6,
|
||||
.dac_channels_mixer = 0,
|
||||
.function_flags = OXYGEN_FUNCTION_SPI,
|
||||
.dac_mclks = OXYGEN_MCLKS(256, 128, 128),
|
||||
.adc_mclks = OXYGEN_MCLKS(256, 128, 128),
|
||||
.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
|
||||
.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
|
||||
};
|
|
@ -450,6 +450,17 @@ static int aic32x4_hw_params(struct snd_pcm_substream *substream,
|
|||
}
|
||||
snd_soc_write(codec, AIC32X4_IFACE1, data);
|
||||
|
||||
if (params_channels(params) == 1) {
|
||||
data = AIC32X4_RDAC2LCHN | AIC32X4_LDAC2LCHN;
|
||||
} else {
|
||||
if (aic32x4->swapdacs)
|
||||
data = AIC32X4_RDAC2LCHN | AIC32X4_LDAC2RCHN;
|
||||
else
|
||||
data = AIC32X4_LDAC2LCHN | AIC32X4_RDAC2RCHN;
|
||||
}
|
||||
snd_soc_update_bits(codec, AIC32X4_DACSETUP, AIC32X4_DAC_CHAN_MASK,
|
||||
data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -606,20 +617,15 @@ static int aic32x4_probe(struct snd_soc_codec *codec)
|
|||
}
|
||||
snd_soc_write(codec, AIC32X4_CMMODE, tmp_reg);
|
||||
|
||||
/* Do DACs need to be swapped? */
|
||||
if (aic32x4->swapdacs) {
|
||||
snd_soc_write(codec, AIC32X4_DACSETUP, AIC32X4_LDAC2RCHN | AIC32X4_RDAC2LCHN);
|
||||
} else {
|
||||
snd_soc_write(codec, AIC32X4_DACSETUP, AIC32X4_LDAC2LCHN | AIC32X4_RDAC2RCHN);
|
||||
}
|
||||
|
||||
/* Mic PGA routing */
|
||||
if (aic32x4->micpga_routing & AIC32X4_MICPGA_ROUTE_LMIC_IN2R_10K) {
|
||||
if (aic32x4->micpga_routing & AIC32X4_MICPGA_ROUTE_LMIC_IN2R_10K)
|
||||
snd_soc_write(codec, AIC32X4_LMICPGANIN, AIC32X4_LMICPGANIN_IN2R_10K);
|
||||
}
|
||||
if (aic32x4->micpga_routing & AIC32X4_MICPGA_ROUTE_RMIC_IN1L_10K) {
|
||||
else
|
||||
snd_soc_write(codec, AIC32X4_LMICPGANIN, AIC32X4_LMICPGANIN_CM1L_10K);
|
||||
if (aic32x4->micpga_routing & AIC32X4_MICPGA_ROUTE_RMIC_IN1L_10K)
|
||||
snd_soc_write(codec, AIC32X4_RMICPGANIN, AIC32X4_RMICPGANIN_IN1L_10K);
|
||||
}
|
||||
else
|
||||
snd_soc_write(codec, AIC32X4_RMICPGANIN, AIC32X4_RMICPGANIN_CM1R_10K);
|
||||
|
||||
aic32x4_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
|
||||
|
||||
|
|
|
@ -120,7 +120,9 @@
|
|||
#define AIC32X4_MICBIAS_2075V 0x60
|
||||
|
||||
#define AIC32X4_LMICPGANIN_IN2R_10K 0x10
|
||||
#define AIC32X4_LMICPGANIN_CM1L_10K 0x40
|
||||
#define AIC32X4_RMICPGANIN_IN1L_10K 0x10
|
||||
#define AIC32X4_RMICPGANIN_CM1R_10K 0x40
|
||||
|
||||
#define AIC32X4_LMICPGAVOL_NOGAIN 0x80
|
||||
#define AIC32X4_RMICPGAVOL_NOGAIN 0x80
|
||||
|
@ -138,6 +140,7 @@
|
|||
#define AIC32X4_LDAC2RCHN (0x02 << 4)
|
||||
#define AIC32X4_LDAC2LCHN (0x01 << 4)
|
||||
#define AIC32X4_RDAC2RCHN (0x01 << 2)
|
||||
#define AIC32X4_DAC_CHAN_MASK 0x3c
|
||||
|
||||
#define AIC32X4_SSTEP2WCLK 0x01
|
||||
#define AIC32X4_MUTEON 0x0C
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <linux/moduleparam.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/gcd.h>
|
||||
#include <linux/gpio.h>
|
||||
|
@ -2141,6 +2142,7 @@ int wm5100_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
|
|||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wm5100_detect);
|
||||
|
||||
static irqreturn_t wm5100_irq(int irq, void *data)
|
||||
{
|
||||
|
|
|
@ -81,6 +81,54 @@ static const struct reg_default wm5110_sysclk_revd_patch[] = {
|
|||
{ 0x3133, 0x1201 },
|
||||
{ 0x3183, 0x1501 },
|
||||
{ 0x31D3, 0x1401 },
|
||||
{ 0x0049, 0x01ea },
|
||||
{ 0x004a, 0x01f2 },
|
||||
{ 0x0057, 0x01e7 },
|
||||
{ 0x0058, 0x01fb },
|
||||
{ 0x33ce, 0xc4f5 },
|
||||
{ 0x33cf, 0x1361 },
|
||||
{ 0x33d0, 0x0402 },
|
||||
{ 0x33d1, 0x4700 },
|
||||
{ 0x33d2, 0x026d },
|
||||
{ 0x33d3, 0xff00 },
|
||||
{ 0x33d4, 0x026d },
|
||||
{ 0x33d5, 0x0101 },
|
||||
{ 0x33d6, 0xc4f5 },
|
||||
{ 0x33d7, 0x0361 },
|
||||
{ 0x33d8, 0x0402 },
|
||||
{ 0x33d9, 0x6701 },
|
||||
{ 0x33da, 0xc4f5 },
|
||||
{ 0x33db, 0x136f },
|
||||
{ 0x33dc, 0xc4f5 },
|
||||
{ 0x33dd, 0x134f },
|
||||
{ 0x33de, 0xc4f5 },
|
||||
{ 0x33df, 0x131f },
|
||||
{ 0x33e0, 0x026d },
|
||||
{ 0x33e1, 0x4f01 },
|
||||
{ 0x33e2, 0x026d },
|
||||
{ 0x33e3, 0xf100 },
|
||||
{ 0x33e4, 0x026d },
|
||||
{ 0x33e5, 0x0001 },
|
||||
{ 0x33e6, 0xc4f5 },
|
||||
{ 0x33e7, 0x0361 },
|
||||
{ 0x33e8, 0x0402 },
|
||||
{ 0x33e9, 0x6601 },
|
||||
{ 0x33ea, 0xc4f5 },
|
||||
{ 0x33eb, 0x136f },
|
||||
{ 0x33ec, 0xc4f5 },
|
||||
{ 0x33ed, 0x134f },
|
||||
{ 0x33ee, 0xc4f5 },
|
||||
{ 0x33ef, 0x131f },
|
||||
{ 0x33f0, 0x026d },
|
||||
{ 0x33f1, 0x4e01 },
|
||||
{ 0x33f2, 0x026d },
|
||||
{ 0x33f3, 0xf000 },
|
||||
{ 0x33f6, 0xc4f5 },
|
||||
{ 0x33f7, 0x1361 },
|
||||
{ 0x33f8, 0x0402 },
|
||||
{ 0x33f9, 0x4600 },
|
||||
{ 0x33fa, 0x026d },
|
||||
{ 0x33fb, 0xfe00 },
|
||||
};
|
||||
|
||||
static int wm5110_sysclk_ev(struct snd_soc_dapm_widget *w,
|
||||
|
|
|
@ -1278,18 +1278,13 @@ static int fsl_ssi_probe(struct platform_device *pdev)
|
|||
return -EINVAL;
|
||||
hw_type = (enum fsl_ssi_type) of_id->data;
|
||||
|
||||
/* We only support the SSI in "I2S Slave" mode */
|
||||
sprop = of_get_property(np, "fsl,mode", NULL);
|
||||
if (!sprop) {
|
||||
dev_err(&pdev->dev, "fsl,mode property is necessary\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!strcmp(sprop, "ac97-slave")) {
|
||||
if (!strcmp(sprop, "ac97-slave"))
|
||||
ac97 = true;
|
||||
} else if (strcmp(sprop, "i2s-slave")) {
|
||||
dev_notice(&pdev->dev, "mode %s is unsupported\n", sprop);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* The DAI name is the last part of the full name of the node. */
|
||||
p = strrchr(np->full_name, '/') + 1;
|
||||
|
@ -1407,7 +1402,7 @@ static int fsl_ssi_probe(struct platform_device *pdev)
|
|||
*/
|
||||
ssi_private->baudclk = devm_clk_get(&pdev->dev, "baud");
|
||||
if (IS_ERR(ssi_private->baudclk))
|
||||
dev_warn(&pdev->dev, "could not get baud clock: %ld\n",
|
||||
dev_dbg(&pdev->dev, "could not get baud clock: %ld\n",
|
||||
PTR_ERR(ssi_private->baudclk));
|
||||
else
|
||||
clk_prepare_enable(ssi_private->baudclk);
|
||||
|
|
|
@ -30,6 +30,7 @@ config SND_OMAP_SOC_RX51
|
|||
select SND_OMAP_SOC_MCBSP
|
||||
select SND_SOC_TLV320AIC3X
|
||||
select SND_SOC_TPA6130A2
|
||||
depends on GPIOLIB
|
||||
help
|
||||
Say Y if you want to add support for SoC audio on Nokia RX-51
|
||||
hardware. This is also known as Nokia N900 product.
|
||||
|
|
|
@ -19,7 +19,7 @@ config SND_S3C_DMA_LEGACY
|
|||
|
||||
config SND_S3C24XX_I2S
|
||||
tristate
|
||||
select S3C2410_DMA
|
||||
select S3C24XX_DMA
|
||||
|
||||
config SND_S3C_I2SV2_SOC
|
||||
tristate
|
||||
|
@ -210,7 +210,7 @@ config SND_SOC_TOBERMORY
|
|||
|
||||
config SND_SOC_BELLS
|
||||
tristate "Audio support for Wolfson Bells"
|
||||
depends on SND_SOC_SAMSUNG && MACH_WLF_CRAGG_6410
|
||||
depends on SND_SOC_SAMSUNG && MACH_WLF_CRAGG_6410 && MFD_ARIZONA
|
||||
select SND_SAMSUNG_I2S
|
||||
select SND_SOC_WM5102
|
||||
select SND_SOC_WM5110
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "regs-iis.h"
|
||||
#include <asm/mach-types.h>
|
||||
|
||||
#include <mach/gpio-samsung.h>
|
||||
#include "s3c24xx-i2s.h"
|
||||
|
||||
static unsigned int rates[] = {
|
||||
|
|
|
@ -22,8 +22,6 @@
|
|||
#include <sound/soc.h>
|
||||
#include <sound/pcm_params.h>
|
||||
|
||||
#include <mach/dma.h>
|
||||
|
||||
#include <linux/platform_data/asoc-s3c.h>
|
||||
|
||||
#include "dma.h"
|
||||
|
@ -1268,7 +1266,8 @@ static int samsung_i2s_probe(struct platform_device *pdev)
|
|||
|
||||
return 0;
|
||||
err:
|
||||
release_mem_region(regs_base, resource_size(res));
|
||||
if (res)
|
||||
release_mem_region(regs_base, resource_size(res));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include <mach/gpio-samsung.h>
|
||||
#include <asm/mach-types.h>
|
||||
#include "regs-iis.h"
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <sound/soc.h>
|
||||
#include <sound/jack.h>
|
||||
|
||||
#include <mach/gpio-samsung.h>
|
||||
#include "regs-iis.h"
|
||||
#include <asm/mach-types.h>
|
||||
|
||||
|
|
|
@ -729,7 +729,7 @@ int s3c_i2sv2_register_component(struct device *dev, int id,
|
|||
struct snd_soc_component_driver *cmp_drv,
|
||||
struct snd_soc_dai_driver *dai_drv)
|
||||
{
|
||||
struct snd_soc_dai_ops *ops = drv->ops;
|
||||
struct snd_soc_dai_ops *ops = dai_drv->ops;
|
||||
|
||||
ops->trigger = s3c2412_i2s_trigger;
|
||||
if (!ops->hw_params)
|
||||
|
@ -742,8 +742,8 @@ int s3c_i2sv2_register_component(struct device *dev, int id,
|
|||
if (!ops->delay)
|
||||
ops->delay = s3c2412_i2s_delay;
|
||||
|
||||
drv->suspend = s3c2412_i2s_suspend;
|
||||
drv->resume = s3c2412_i2s_resume;
|
||||
dai_drv->suspend = s3c2412_i2s_suspend;
|
||||
dai_drv->resume = s3c2412_i2s_resume;
|
||||
|
||||
return snd_soc_register_component(dev, cmp_drv, dai_drv, 1);
|
||||
}
|
||||
|
|
|
@ -26,6 +26,8 @@
|
|||
#include <sound/pcm_params.h>
|
||||
|
||||
#include <mach/dma.h>
|
||||
#include <mach/gpio-samsung.h>
|
||||
#include <plat/gpio-cfg.h>
|
||||
|
||||
#include "dma.h"
|
||||
#include "regs-i2s-v2.h"
|
||||
|
|
|
@ -24,6 +24,8 @@
|
|||
#include <sound/pcm_params.h>
|
||||
|
||||
#include <mach/dma.h>
|
||||
#include <mach/gpio-samsung.h>
|
||||
#include <plat/gpio-cfg.h>
|
||||
#include "regs-iis.h"
|
||||
|
||||
#include "dma.h"
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <sound/soc.h>
|
||||
#include <sound/jack.h>
|
||||
|
||||
#include <mach/gpio-samsung.h>
|
||||
#include <asm/mach-types.h>
|
||||
|
||||
#include "i2s.h"
|
||||
|
|
|
@ -152,13 +152,11 @@ static struct snd_soc_card smdk = {
|
|||
.num_links = ARRAY_SIZE(smdk_dai),
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id samsung_wm8994_of_match[] = {
|
||||
{ .compatible = "samsung,smdk-wm8994", .data = &smdk_board_data },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, samsung_wm8994_of_match);
|
||||
#endif /* CONFIG_OF */
|
||||
|
||||
static int smdk_audio_probe(struct platform_device *pdev)
|
||||
{
|
||||
|
@ -188,7 +186,7 @@ static int smdk_audio_probe(struct platform_device *pdev)
|
|||
smdk_dai[0].platform_of_node = smdk_dai[0].cpu_of_node;
|
||||
}
|
||||
|
||||
id = of_match_device(samsung_wm8994_of_match, &pdev->dev);
|
||||
id = of_match_device(of_match_ptr(samsung_wm8994_of_match), &pdev->dev);
|
||||
if (id)
|
||||
*board = *((struct smdk_wm8994_data *)id->data);
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче