aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2008-01-25 05:35:17 -0500
committerJaroslav Kysela <perex@perex.cz>2008-01-31 11:30:16 -0500
commit461e2c78b153e38f284d09721c50c0cd3c47e073 (patch)
treefb82d58738534d1736f3a0f64fe49eb25fcd3bdb
parentc626026dd72ec8363aaa862178adeacfa7ac09c5 (diff)
[ALSA] hda-codec - Add Conexant 5051 codec support
Added the support for Conexant 5051 audio codec. Right now there are two preset models, laptop and hp. The whole patch is based on the information from the base patch by Linuxant. Signed-off-by: Takashi Iwai <tiwai@suse.de> Signed-off-by: Jaroslav Kysela <perex@perex.cz>
-rw-r--r--Documentation/sound/alsa/ALSA-Configuration.txt4
-rw-r--r--sound/pci/hda/patch_conexant.c297
2 files changed, 300 insertions, 1 deletions
diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt
index 67256bd431d6..73f5012326ba 100644
--- a/Documentation/sound/alsa/ALSA-Configuration.txt
+++ b/Documentation/sound/alsa/ALSA-Configuration.txt
@@ -961,6 +961,10 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
961 can be adjusted. Appearing only when compiled with 961 can be adjusted. Appearing only when compiled with
962 $CONFIG_SND_DEBUG=y 962 $CONFIG_SND_DEBUG=y
963 963
964 Conexant 5051
965 laptop Basic Laptop config (default)
966 hp HP Spartan laptop
967
964 STAC9200 968 STAC9200
965 ref Reference board 969 ref Reference board
966 dell-d21 Dell (unknown) 970 dell-d21 Dell (unknown)
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 1ed0f0757e01..5f6de340b237 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -64,6 +64,11 @@ struct conexant_spec {
64 hda_nid_t *adc_nids; 64 hda_nid_t *adc_nids;
65 hda_nid_t dig_in_nid; /* digital-in NID; optional */ 65 hda_nid_t dig_in_nid; /* digital-in NID; optional */
66 66
67 unsigned int cur_adc_idx;
68 hda_nid_t cur_adc;
69 unsigned int cur_adc_stream_tag;
70 unsigned int cur_adc_format;
71
67 /* capture source */ 72 /* capture source */
68 const struct hda_input_mux *input_mux; 73 const struct hda_input_mux *input_mux;
69 hda_nid_t *capsrc_nids; 74 hda_nid_t *capsrc_nids;
@@ -217,6 +222,41 @@ static struct hda_pcm_stream conexant_pcm_digital_capture = {
217 /* NID is set in alc_build_pcms */ 222 /* NID is set in alc_build_pcms */
218}; 223};
219 224
225static int cx5051_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
226 struct hda_codec *codec,
227 unsigned int stream_tag,
228 unsigned int format,
229 struct snd_pcm_substream *substream)
230{
231 struct conexant_spec *spec = codec->spec;
232 spec->cur_adc = spec->adc_nids[spec->cur_adc_idx];
233 spec->cur_adc_stream_tag = stream_tag;
234 spec->cur_adc_format = format;
235 snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
236 return 0;
237}
238
239static int cx5051_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
240 struct hda_codec *codec,
241 struct snd_pcm_substream *substream)
242{
243 struct conexant_spec *spec = codec->spec;
244 snd_hda_codec_setup_stream(codec, spec->cur_adc, 0, 0, 0);
245 spec->cur_adc = 0;
246 return 0;
247}
248
249static struct hda_pcm_stream cx5051_pcm_analog_capture = {
250 .substreams = 1,
251 .channels_min = 2,
252 .channels_max = 2,
253 .nid = 0, /* fill later */
254 .ops = {
255 .prepare = cx5051_capture_pcm_prepare,
256 .cleanup = cx5051_capture_pcm_cleanup
257 },
258};
259
220static int conexant_build_pcms(struct hda_codec *codec) 260static int conexant_build_pcms(struct hda_codec *codec)
221{ 261{
222 struct conexant_spec *spec = codec->spec; 262 struct conexant_spec *spec = codec->spec;
@@ -231,7 +271,12 @@ static int conexant_build_pcms(struct hda_codec *codec)
231 spec->multiout.max_channels; 271 spec->multiout.max_channels;
232 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 272 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
233 spec->multiout.dac_nids[0]; 273 spec->multiout.dac_nids[0];
234 info->stream[SNDRV_PCM_STREAM_CAPTURE] = conexant_pcm_analog_capture; 274 if (codec->vendor_id == 0x14f15051)
275 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
276 cx5051_pcm_analog_capture;
277 else
278 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
279 conexant_pcm_analog_capture;
235 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adc_nids; 280 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adc_nids;
236 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; 281 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
237 282
@@ -1421,10 +1466,260 @@ static int patch_cxt5047(struct hda_codec *codec)
1421 return 0; 1466 return 0;
1422} 1467}
1423 1468
1469/* Conexant 5051 specific */
1470static hda_nid_t cxt5051_dac_nids[1] = { 0x10 };
1471static hda_nid_t cxt5051_adc_nids[2] = { 0x14, 0x15 };
1472#define CXT5051_SPDIF_OUT 0x1C
1473#define CXT5051_PORTB_EVENT 0x38
1474#define CXT5051_PORTC_EVENT 0x39
1475
1476static struct hda_channel_mode cxt5051_modes[1] = {
1477 { 2, NULL },
1478};
1479
1480static void cxt5051_update_speaker(struct hda_codec *codec)
1481{
1482 struct conexant_spec *spec = codec->spec;
1483 unsigned int pinctl;
1484 pinctl = (!spec->hp_present && spec->cur_eapd) ? PIN_OUT : 0;
1485 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
1486 pinctl);
1487}
1488
1489/* turn on/off EAPD (+ mute HP) as a master switch */
1490static int cxt5051_hp_master_sw_put(struct snd_kcontrol *kcontrol,
1491 struct snd_ctl_elem_value *ucontrol)
1492{
1493 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1494
1495 if (!cxt_eapd_put(kcontrol, ucontrol))
1496 return 0;
1497 cxt5051_update_speaker(codec);
1498 return 1;
1499}
1500
1501/* toggle input of built-in and mic jack appropriately */
1502static void cxt5051_portb_automic(struct hda_codec *codec)
1503{
1504 unsigned int present;
1505
1506 present = snd_hda_codec_read(codec, 0x17, 0,
1507 AC_VERB_GET_PIN_SENSE, 0) &
1508 AC_PINSENSE_PRESENCE;
1509 snd_hda_codec_write(codec, 0x14, 0,
1510 AC_VERB_SET_CONNECT_SEL,
1511 present ? 0x01 : 0x00);
1512}
1513
1514/* switch the current ADC according to the jack state */
1515static void cxt5051_portc_automic(struct hda_codec *codec)
1516{
1517 struct conexant_spec *spec = codec->spec;
1518 unsigned int present;
1519 hda_nid_t new_adc;
1520
1521 present = snd_hda_codec_read(codec, 0x18, 0,
1522 AC_VERB_GET_PIN_SENSE, 0) &
1523 AC_PINSENSE_PRESENCE;
1524 if (present)
1525 spec->cur_adc_idx = 1;
1526 else
1527 spec->cur_adc_idx = 0;
1528 new_adc = spec->adc_nids[spec->cur_adc_idx];
1529 if (spec->cur_adc && spec->cur_adc != new_adc) {
1530 /* stream is running, let's swap the current ADC */
1531 snd_hda_codec_setup_stream(codec, spec->cur_adc, 0, 0, 0);
1532 spec->cur_adc = new_adc;
1533 snd_hda_codec_setup_stream(codec, new_adc,
1534 spec->cur_adc_stream_tag, 0,
1535 spec->cur_adc_format);
1536 }
1537}
1538
1539/* mute internal speaker if HP is plugged */
1540static void cxt5051_hp_automute(struct hda_codec *codec)
1541{
1542 struct conexant_spec *spec = codec->spec;
1543
1544 spec->hp_present = snd_hda_codec_read(codec, 0x16, 0,
1545 AC_VERB_GET_PIN_SENSE, 0) &
1546 AC_PINSENSE_PRESENCE;
1547 cxt5051_update_speaker(codec);
1548}
1549
1550/* unsolicited event for HP jack sensing */
1551static void cxt5051_hp_unsol_event(struct hda_codec *codec,
1552 unsigned int res)
1553{
1554 switch (res >> 26) {
1555 case CONEXANT_HP_EVENT:
1556 cxt5051_hp_automute(codec);
1557 break;
1558 case CXT5051_PORTB_EVENT:
1559 cxt5051_portb_automic(codec);
1560 break;
1561 case CXT5051_PORTC_EVENT:
1562 cxt5051_portc_automic(codec);
1563 break;
1564 }
1565}
1566
1567static struct snd_kcontrol_new cxt5051_mixers[] = {
1568 HDA_CODEC_VOLUME("Internal Mic Volume", 0x14, 0x00, HDA_INPUT),
1569 HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT),
1570 HDA_CODEC_VOLUME("External Mic Volume", 0x14, 0x01, HDA_INPUT),
1571 HDA_CODEC_MUTE("External Mic Switch", 0x14, 0x01, HDA_INPUT),
1572 HDA_CODEC_VOLUME("Docking Mic Volume", 0x15, 0x00, HDA_INPUT),
1573 HDA_CODEC_MUTE("Docking Mic Switch", 0x15, 0x00, HDA_INPUT),
1574 HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT),
1575 {
1576 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1577 .name = "Master Playback Switch",
1578 .info = cxt_eapd_info,
1579 .get = cxt_eapd_get,
1580 .put = cxt5051_hp_master_sw_put,
1581 .private_value = 0x1a,
1582 },
1583
1584 {}
1585};
1586
1587static struct snd_kcontrol_new cxt5051_hp_mixers[] = {
1588 HDA_CODEC_VOLUME("Internal Mic Volume", 0x14, 0x00, HDA_INPUT),
1589 HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT),
1590 HDA_CODEC_VOLUME("External Mic Volume", 0x15, 0x00, HDA_INPUT),
1591 HDA_CODEC_MUTE("External Mic Switch", 0x15, 0x00, HDA_INPUT),
1592 HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT),
1593 {
1594 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1595 .name = "Master Playback Switch",
1596 .info = cxt_eapd_info,
1597 .get = cxt_eapd_get,
1598 .put = cxt5051_hp_master_sw_put,
1599 .private_value = 0x1a,
1600 },
1601
1602 {}
1603};
1604
1605static struct hda_verb cxt5051_init_verbs[] = {
1606 /* Line in, Mic */
1607 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
1608 {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
1609 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
1610 {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
1611 {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
1612 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
1613 /* SPK */
1614 {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
1615 {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00},
1616 /* HP, Amp */
1617 {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
1618 {0x16, AC_VERB_SET_CONNECT_SEL, 0x00},
1619 /* DAC1 */
1620 {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
1621 /* Record selector: Int mic */
1622 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44},
1623 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44},
1624 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44},
1625 /* SPDIF route: PCM */
1626 {0x1c, AC_VERB_SET_CONNECT_SEL, 0x0},
1627 /* EAPD */
1628 {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
1629 {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT},
1630 {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTB_EVENT},
1631 {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTC_EVENT},
1632 { } /* end */
1633};
1634
1635/* initialize jack-sensing, too */
1636static int cxt5051_init(struct hda_codec *codec)
1637{
1638 conexant_init(codec);
1639 if (codec->patch_ops.unsol_event) {
1640 cxt5051_hp_automute(codec);
1641 cxt5051_portb_automic(codec);
1642 cxt5051_portc_automic(codec);
1643 }
1644 return 0;
1645}
1646
1647
1648enum {
1649 CXT5051_LAPTOP, /* Laptops w/ EAPD support */
1650 CXT5051_HP, /* no docking */
1651 CXT5051_MODELS
1652};
1653
1654static const char *cxt5051_models[CXT5051_MODELS] = {
1655 [CXT5051_LAPTOP] = "laptop",
1656 [CXT5051_HP] = "hp",
1657};
1658
1659static struct snd_pci_quirk cxt5051_cfg_tbl[] = {
1660 SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board",
1661 CXT5051_LAPTOP),
1662 SND_PCI_QUIRK(0x14f1, 0x5051, "HP Spartan 1.1", CXT5051_HP),
1663 {}
1664};
1665
1666static int patch_cxt5051(struct hda_codec *codec)
1667{
1668 struct conexant_spec *spec;
1669 int board_config;
1670
1671 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
1672 if (!spec)
1673 return -ENOMEM;
1674 mutex_init(&spec->amp_mutex);
1675 codec->spec = spec;
1676
1677 codec->patch_ops = conexant_patch_ops;
1678 codec->patch_ops.init = cxt5051_init;
1679
1680 spec->multiout.max_channels = 2;
1681 spec->multiout.num_dacs = ARRAY_SIZE(cxt5051_dac_nids);
1682 spec->multiout.dac_nids = cxt5051_dac_nids;
1683 spec->multiout.dig_out_nid = CXT5051_SPDIF_OUT;
1684 spec->num_adc_nids = 1; /* not 2; via auto-mic switch */
1685 spec->adc_nids = cxt5051_adc_nids;
1686 spec->num_mixers = 1;
1687 spec->mixers[0] = cxt5051_mixers;
1688 spec->num_init_verbs = 1;
1689 spec->init_verbs[0] = cxt5051_init_verbs;
1690 spec->spdif_route = 0;
1691 spec->num_channel_mode = ARRAY_SIZE(cxt5051_modes);
1692 spec->channel_mode = cxt5051_modes;
1693 spec->cur_adc = 0;
1694 spec->cur_adc_idx = 0;
1695
1696 board_config = snd_hda_check_board_config(codec, CXT5051_MODELS,
1697 cxt5051_models,
1698 cxt5051_cfg_tbl);
1699 switch (board_config) {
1700 case CXT5051_HP:
1701 codec->patch_ops.unsol_event = cxt5051_hp_unsol_event;
1702 spec->mixers[0] = cxt5051_hp_mixers;
1703 break;
1704 default:
1705 case CXT5051_LAPTOP:
1706 codec->patch_ops.unsol_event = cxt5051_hp_unsol_event;
1707 break;
1708 }
1709
1710 return 0;
1711}
1712
1713
1714/*
1715 */
1716
1424struct hda_codec_preset snd_hda_preset_conexant[] = { 1717struct hda_codec_preset snd_hda_preset_conexant[] = {
1425 { .id = 0x14f15045, .name = "CX20549 (Venice)", 1718 { .id = 0x14f15045, .name = "CX20549 (Venice)",
1426 .patch = patch_cxt5045 }, 1719 .patch = patch_cxt5045 },
1427 { .id = 0x14f15047, .name = "CX20551 (Waikiki)", 1720 { .id = 0x14f15047, .name = "CX20551 (Waikiki)",
1428 .patch = patch_cxt5047 }, 1721 .patch = patch_cxt5047 },
1722 { .id = 0x14f15051, .name = "CX20561 (Hermosa)",
1723 .patch = patch_cxt5051 },
1429 {} /* terminator */ 1724 {} /* terminator */
1430}; 1725};