diff options
-rw-r--r-- | Documentation/sound/alsa/ALSA-Configuration.txt | 4 | ||||
-rw-r--r-- | sound/pci/hda/patch_conexant.c | 297 |
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 | ||
225 | static 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 | |||
239 | static 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 | |||
249 | static 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 | |||
220 | static int conexant_build_pcms(struct hda_codec *codec) | 260 | static 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 */ | ||
1470 | static hda_nid_t cxt5051_dac_nids[1] = { 0x10 }; | ||
1471 | static 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 | |||
1476 | static struct hda_channel_mode cxt5051_modes[1] = { | ||
1477 | { 2, NULL }, | ||
1478 | }; | ||
1479 | |||
1480 | static 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 */ | ||
1490 | static 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 */ | ||
1502 | static 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 */ | ||
1515 | static 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 */ | ||
1540 | static 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 */ | ||
1551 | static 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 | |||
1567 | static 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 | |||
1587 | static 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 | |||
1605 | static 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 */ | ||
1636 | static 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 | |||
1648 | enum { | ||
1649 | CXT5051_LAPTOP, /* Laptops w/ EAPD support */ | ||
1650 | CXT5051_HP, /* no docking */ | ||
1651 | CXT5051_MODELS | ||
1652 | }; | ||
1653 | |||
1654 | static const char *cxt5051_models[CXT5051_MODELS] = { | ||
1655 | [CXT5051_LAPTOP] = "laptop", | ||
1656 | [CXT5051_HP] = "hp", | ||
1657 | }; | ||
1658 | |||
1659 | static 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 | |||
1666 | static 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 | |||
1424 | struct hda_codec_preset snd_hda_preset_conexant[] = { | 1717 | struct 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 | }; |