Message ID | 20210304190241.5363-4-vitalyr@opensource.cirrus.com (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
Series | ALSA: hda/cirrus: Add support for CS8409 HDA bridge and CS42L42 companion codec | expand |
Hi Vitaly, Thank you for the patch! Perhaps something to improve: [auto build test WARNING on sound/for-next] [also build test WARNING on v5.12-rc1 next-20210304] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch] url: https://github.com/0day-ci/linux/commits/Vitaly-Rodionov/ALSA-hda-cirrus-Add-support-for-CS8409-HDA-bridge-and-CS42L42-companion-codec/20210305-030714 base: https://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git for-next config: riscv-randconfig-r025-20210304 (attached as .config) compiler: clang version 13.0.0 (https://github.com/llvm/llvm-project eec7f8f7b1226be422a76542cb403d02538f453a) reproduce (this is a W=1 build): wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # install riscv cross compiling tool for clang build # apt-get install binutils-riscv64-linux-gnu # https://github.com/0day-ci/linux/commit/9d4c2aa0fd6872aa8b866929c1537ce2905a6dba git remote add linux-review https://github.com/0day-ci/linux git fetch --no-tags linux-review Vitaly-Rodionov/ALSA-hda-cirrus-Add-support-for-CS8409-HDA-bridge-and-CS42L42-companion-codec/20210305-030714 git checkout 9d4c2aa0fd6872aa8b866929c1537ce2905a6dba # save the attached .config to linux build tree COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross ARCH=riscv If you fix the issue, kindly add following tag as appropriate Reported-by: kernel test robot <lkp@intel.com> All warnings (new ones prefixed by >>): sound/pci/hda/patch_cirrus.c:1529:37: warning: variable 'retval' is uninitialized when used here [-Wuninitialized] __func__, i2c_address, i2c_reg, retval); ^~~~~~ sound/pci/hda/hda_local.h:723:39: note: expanded from macro 'codec_err' dev_err(hda_codec_dev(codec), fmt, ##args) ^~~~ include/linux/dev_printk.h:112:32: note: expanded from macro 'dev_err' _dev_err(dev, dev_fmt(fmt), ##__VA_ARGS__) ^~~~~~~~~~~ sound/pci/hda/patch_cirrus.c:1519:21: note: initialize the variable 'retval' to silence this warning unsigned int retval; ^ = 0 >> sound/pci/hda/patch_cirrus.c:1686:6: warning: no previous prototype for function 'cs8409_jack_unsol_event' [-Wmissing-prototypes] void cs8409_jack_unsol_event(struct hda_codec *codec, unsigned int res) ^ sound/pci/hda/patch_cirrus.c:1686:1: note: declare 'static' if the function is not intended to be used outside of this translation unit void cs8409_jack_unsol_event(struct hda_codec *codec, unsigned int res) ^ static 2 warnings generated. vim +/cs8409_jack_unsol_event +1686 sound/pci/hda/patch_cirrus.c 1511 1512 /* CS8409 slave i2cRead */ 1513 static unsigned int cs8409_i2c_read(struct hda_codec *codec, 1514 unsigned int i2c_address, 1515 unsigned int i2c_reg, 1516 unsigned int paged) 1517 { 1518 unsigned int i2c_reg_data; > 1519 unsigned int retval; 1520 1521 cs8409_enable_i2c_clock(codec, 1); 1522 cs_vendor_coef_set(codec, CIR_I2C_ADDR, i2c_address); 1523 1524 if (paged) { 1525 cs_vendor_coef_set(codec, CIR_I2C_QWRITE, i2c_reg >> 8); 1526 if (cs8409_i2c_wait_complete(codec) == -1) { 1527 codec_err(codec, 1528 "%s() Paged Transaction Failed 0x%02x : 0x%04x = 0x%02x\n", 1529 __func__, i2c_address, i2c_reg, retval); 1530 } 1531 } 1532 1533 i2c_reg_data = (i2c_reg << 8) & 0x0ffff; 1534 cs_vendor_coef_set(codec, CIR_I2C_QREAD, i2c_reg_data); 1535 if (cs8409_i2c_wait_complete(codec) == -1) { 1536 codec_err(codec, "%s() Transaction Failed 0x%02x : 0x%04x = 0x%02x\n", 1537 __func__, i2c_address, i2c_reg, retval); 1538 } 1539 1540 /* Register in bits 15-8 and the data in 7-0 */ 1541 retval = cs_vendor_coef_get(codec, CIR_I2C_QREAD); 1542 retval &= 0x0ff; 1543 1544 cs8409_enable_i2c_clock(codec, 0); 1545 1546 return retval; 1547 } 1548 1549 /* CS8409 slave i2cWrite */ 1550 static unsigned int cs8409_i2c_write(struct hda_codec *codec, 1551 unsigned int i2c_address, unsigned int i2c_reg, 1552 unsigned int i2c_data, 1553 unsigned int paged) 1554 { 1555 unsigned int retval = 0; 1556 unsigned int i2c_reg_data = 0; 1557 1558 cs8409_enable_i2c_clock(codec, 1); 1559 cs_vendor_coef_set(codec, CIR_I2C_ADDR, i2c_address); 1560 1561 if (paged) { 1562 cs_vendor_coef_set(codec, CIR_I2C_QWRITE, i2c_reg >> 8); 1563 if (cs8409_i2c_wait_complete(codec) == -1) { 1564 codec_err(codec, 1565 "%s() Paged Transaction Failed 0x%02x : 0x%04x = 0x%02x\n", 1566 __func__, i2c_address, i2c_reg, retval); 1567 } 1568 } 1569 1570 i2c_reg_data = ((i2c_reg << 8) & 0x0ff00) | (i2c_data & 0x0ff); 1571 cs_vendor_coef_set(codec, CIR_I2C_QWRITE, i2c_reg_data); 1572 1573 if (cs8409_i2c_wait_complete(codec) == -1) { 1574 codec_err(codec, "%s() Transaction Failed 0x%02x : 0x%04x = 0x%02x\n", 1575 __func__, i2c_address, i2c_reg, retval); 1576 } 1577 1578 cs8409_enable_i2c_clock(codec, 0); 1579 1580 return retval; 1581 } 1582 1583 /* Assert/release RTS# line to CS42L42 */ 1584 static void cs8409_cs42l42_reset(struct hda_codec *codec) 1585 { 1586 struct cs_spec *spec = codec->spec; 1587 1588 /* Assert RTS# line */ 1589 snd_hda_codec_write(codec, 1590 codec->core.afg, 0, AC_VERB_SET_GPIO_DATA, 0); 1591 /* wait ~10ms */ 1592 usleep_range(10000, 15000); 1593 /* Release RTS# line */ 1594 snd_hda_codec_write(codec, 1595 codec->core.afg, 0, AC_VERB_SET_GPIO_DATA, GPIO5_INT); 1596 /* wait ~10ms */ 1597 usleep_range(10000, 15000); 1598 1599 mutex_lock(&spec->cs8409_i2c_mux); 1600 1601 /* Clear interrupts, by reading interrupt status registers */ 1602 cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1308, 1); 1603 cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1309, 1); 1604 cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x130A, 1); 1605 cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x130F, 1); 1606 1607 mutex_unlock(&spec->cs8409_i2c_mux); 1608 1609 } 1610 1611 /* Configure CS42L42 slave codec for jack autodetect */ 1612 static int cs8409_cs42l42_enable_jack_detect(struct hda_codec *codec) 1613 { 1614 struct cs_spec *spec = codec->spec; 1615 1616 mutex_lock(&spec->cs8409_i2c_mux); 1617 1618 /* Set TIP_SENSE_EN for analog front-end of tip sense. */ 1619 cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b70, 0x0020, 1); 1620 /* Clear WAKE# */ 1621 cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b71, 0x0001, 1); 1622 /* Wait ~2.5ms */ 1623 usleep_range(2500, 3000); 1624 /* Set mode WAKE# output follows the combination logic directly */ 1625 cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b71, 0x0020, 1); 1626 /* Clear interrupts status */ 1627 cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x130f, 1); 1628 cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1b7b, 1); 1629 /* Enable interrupt */ 1630 cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1320, 0x03, 1); 1631 cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b79, 0x00, 1); 1632 1633 mutex_unlock(&spec->cs8409_i2c_mux); 1634 1635 return 0; 1636 } 1637 1638 /* Enable and run CS42L42 slave codec jack auto detect */ 1639 static void cs8409_cs42l42_run_jack_detect(struct hda_codec *codec) 1640 { 1641 struct cs_spec *spec = codec->spec; 1642 1643 mutex_lock(&spec->cs8409_i2c_mux); 1644 1645 /* Clear interrupts */ 1646 cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1308, 1); 1647 cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1b77, 1); 1648 1649 cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1102, 0x87, 1); 1650 cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1f06, 0x86, 1); 1651 cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b74, 0x07, 1); 1652 cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x131b, 0x01, 1); 1653 cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1120, 0x80, 1); 1654 /* Wait ~110ms*/ 1655 usleep_range(110000, 200000); 1656 cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x111f, 0x77, 1); 1657 cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1120, 0xc0, 1); 1658 /* Wait ~10ms */ 1659 usleep_range(10000, 25000); 1660 1661 mutex_unlock(&spec->cs8409_i2c_mux); 1662 1663 } 1664 1665 static void cs8409_cs42l42_reg_setup(struct hda_codec *codec) 1666 { 1667 const struct cs8409_i2c_param *seq = cs42l42_init_reg_seq; 1668 struct cs_spec *spec = codec->spec; 1669 1670 mutex_lock(&spec->cs8409_i2c_mux); 1671 1672 for (; seq->addr; seq++) 1673 cs8409_i2c_write(codec, CS42L42_I2C_ADDR, seq->addr, seq->reg, 1); 1674 1675 mutex_unlock(&spec->cs8409_i2c_mux); 1676 1677 } 1678 1679 /* 1680 * In the case of CS8409 we do not have unsolicited events from NID's 0x24 1681 * and 0x34 where hs mic and hp are connected. Companion codec CS42L42 will 1682 * generate interrupt via gpio 4 to notify jack events. We have to overwrite 1683 * generic snd_hda_jack_unsol_event(), read CS42L42 jack detect status registers 1684 * and then notify status via generic snd_hda_jack_unsol_event() call. 1685 */ > 1686 void cs8409_jack_unsol_event(struct hda_codec *codec, unsigned int res) 1687 { 1688 struct cs_spec *spec = codec->spec; 1689 int status_changed = 0; 1690 unsigned int reg_cdc_status = 0; 1691 unsigned int reg_hs_status = 0; 1692 unsigned int reg_ts_status = 0; 1693 int type = 0; 1694 struct hda_jack_tbl *jk; 1695 1696 /* jack_unsol_event() will be called every time gpio line changing state. 1697 * In this case gpio4 line goes up as a result of reading interrupt status 1698 * registers in previous cs8409_jack_unsol_event() call. 1699 * We don't need to handle this event, ignoring... 1700 */ 1701 if ((res & (1 << 4))) 1702 return; 1703 1704 mutex_lock(&spec->cs8409_i2c_mux); 1705 1706 /* Read jack detect status registers */ 1707 reg_cdc_status = cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1308, 1); 1708 reg_hs_status = cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1124, 1); 1709 reg_ts_status = cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x130f, 1); 1710 1711 /* Clear interrupts */ 1712 cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1b7b, 1); 1713 cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1308, 1); 1714 cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x130f, 1); 1715 1716 mutex_unlock(&spec->cs8409_i2c_mux); 1717 1718 /* HSDET_AUTO_DONE */ 1719 if (reg_cdc_status & CS42L42_HSDET_AUTO_DONE) { 1720 1721 type = ((reg_hs_status & CS42L42_HSTYPE_MASK) + 1); 1722 /* CS42L42 reports optical jack as type 4 1723 * We don't handle optical jack 1724 */ 1725 if (type != 4) { 1726 if (!spec->cs42l42_hp_jack_in) { 1727 status_changed = 1; 1728 spec->cs42l42_hp_jack_in = 1; 1729 } 1730 /* type = 3 has no mic */ 1731 if ((!spec->cs42l42_mic_jack_in) && (type != 3)) { 1732 status_changed = 1; 1733 spec->cs42l42_mic_jack_in = 1; 1734 } 1735 } 1736 1737 } else { 1738 /* TIP_SENSE INSERT/REMOVE */ 1739 switch (reg_ts_status) { 1740 case CS42L42_JACK_INSERTED: 1741 cs8409_cs42l42_run_jack_detect(codec); 1742 break; 1743 1744 case CS42L42_JACK_REMOVED: 1745 if (spec->cs42l42_hp_jack_in || spec->cs42l42_mic_jack_in) { 1746 status_changed = 1; 1747 spec->cs42l42_hp_jack_in = 0; 1748 spec->cs42l42_mic_jack_in = 0; 1749 } 1750 break; 1751 1752 default: 1753 /* jack in transition */ 1754 status_changed = 0; 1755 break; 1756 } 1757 } 1758 1759 if (status_changed) { 1760 1761 snd_hda_set_pin_ctl(codec, CS8409_CS42L42_SPK_PIN_NID, 1762 (spec->cs42l42_hp_jack_in)?0 : PIN_OUT); 1763 1764 /* Report jack*/ 1765 jk = snd_hda_jack_tbl_get_mst(codec, CS8409_CS42L42_HP_PIN_NID, 0); 1766 if (jk) { 1767 snd_hda_jack_unsol_event(codec, 1768 (jk->tag << AC_UNSOL_RES_TAG_SHIFT) & AC_UNSOL_RES_TAG); 1769 } 1770 /* Report jack*/ 1771 jk = snd_hda_jack_tbl_get_mst(codec, CS8409_CS42L42_AMIC_PIN_NID, 0); 1772 if (jk) { 1773 snd_hda_jack_unsol_event(codec, 1774 (jk->tag << AC_UNSOL_RES_TAG_SHIFT) & AC_UNSOL_RES_TAG); 1775 } 1776 } 1777 } 1778 --- 0-DAY CI Kernel Test Service, Intel Corporation https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
Hi Vitaly, Thank you for the patch! Perhaps something to improve: [auto build test WARNING on sound/for-next] [also build test WARNING on v5.12-rc1 next-20210304] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch] url: https://github.com/0day-ci/linux/commits/Vitaly-Rodionov/ALSA-hda-cirrus-Add-support-for-CS8409-HDA-bridge-and-CS42L42-companion-codec/20210305-030714 base: https://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git for-next config: alpha-randconfig-r035-20210305 (attached as .config) compiler: alpha-linux-gcc (GCC) 9.3.0 reproduce (this is a W=1 build): wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # https://github.com/0day-ci/linux/commit/9d4c2aa0fd6872aa8b866929c1537ce2905a6dba git remote add linux-review https://github.com/0day-ci/linux git fetch --no-tags linux-review Vitaly-Rodionov/ALSA-hda-cirrus-Add-support-for-CS8409-HDA-bridge-and-CS42L42-companion-codec/20210305-030714 git checkout 9d4c2aa0fd6872aa8b866929c1537ce2905a6dba # save the attached .config to linux build tree COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross ARCH=alpha If you fix the issue, kindly add following tag as appropriate Reported-by: kernel test robot <lkp@intel.com> All warnings (new ones prefixed by >>): >> sound/pci/hda/patch_cirrus.c:1686:6: warning: no previous prototype for 'cs8409_jack_unsol_event' [-Wmissing-prototypes] 1686 | void cs8409_jack_unsol_event(struct hda_codec *codec, unsigned int res) | ^~~~~~~~~~~~~~~~~~~~~~~ vim +/cs8409_jack_unsol_event +1686 sound/pci/hda/patch_cirrus.c 1678 1679 /* 1680 * In the case of CS8409 we do not have unsolicited events from NID's 0x24 1681 * and 0x34 where hs mic and hp are connected. Companion codec CS42L42 will 1682 * generate interrupt via gpio 4 to notify jack events. We have to overwrite 1683 * generic snd_hda_jack_unsol_event(), read CS42L42 jack detect status registers 1684 * and then notify status via generic snd_hda_jack_unsol_event() call. 1685 */ > 1686 void cs8409_jack_unsol_event(struct hda_codec *codec, unsigned int res) 1687 { 1688 struct cs_spec *spec = codec->spec; 1689 int status_changed = 0; 1690 unsigned int reg_cdc_status = 0; 1691 unsigned int reg_hs_status = 0; 1692 unsigned int reg_ts_status = 0; 1693 int type = 0; 1694 struct hda_jack_tbl *jk; 1695 1696 /* jack_unsol_event() will be called every time gpio line changing state. 1697 * In this case gpio4 line goes up as a result of reading interrupt status 1698 * registers in previous cs8409_jack_unsol_event() call. 1699 * We don't need to handle this event, ignoring... 1700 */ 1701 if ((res & (1 << 4))) 1702 return; 1703 1704 mutex_lock(&spec->cs8409_i2c_mux); 1705 1706 /* Read jack detect status registers */ 1707 reg_cdc_status = cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1308, 1); 1708 reg_hs_status = cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1124, 1); 1709 reg_ts_status = cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x130f, 1); 1710 1711 /* Clear interrupts */ 1712 cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1b7b, 1); 1713 cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1308, 1); 1714 cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x130f, 1); 1715 1716 mutex_unlock(&spec->cs8409_i2c_mux); 1717 1718 /* HSDET_AUTO_DONE */ 1719 if (reg_cdc_status & CS42L42_HSDET_AUTO_DONE) { 1720 1721 type = ((reg_hs_status & CS42L42_HSTYPE_MASK) + 1); 1722 /* CS42L42 reports optical jack as type 4 1723 * We don't handle optical jack 1724 */ 1725 if (type != 4) { 1726 if (!spec->cs42l42_hp_jack_in) { 1727 status_changed = 1; 1728 spec->cs42l42_hp_jack_in = 1; 1729 } 1730 /* type = 3 has no mic */ 1731 if ((!spec->cs42l42_mic_jack_in) && (type != 3)) { 1732 status_changed = 1; 1733 spec->cs42l42_mic_jack_in = 1; 1734 } 1735 } 1736 1737 } else { 1738 /* TIP_SENSE INSERT/REMOVE */ 1739 switch (reg_ts_status) { 1740 case CS42L42_JACK_INSERTED: 1741 cs8409_cs42l42_run_jack_detect(codec); 1742 break; 1743 1744 case CS42L42_JACK_REMOVED: 1745 if (spec->cs42l42_hp_jack_in || spec->cs42l42_mic_jack_in) { 1746 status_changed = 1; 1747 spec->cs42l42_hp_jack_in = 0; 1748 spec->cs42l42_mic_jack_in = 0; 1749 } 1750 break; 1751 1752 default: 1753 /* jack in transition */ 1754 status_changed = 0; 1755 break; 1756 } 1757 } 1758 1759 if (status_changed) { 1760 1761 snd_hda_set_pin_ctl(codec, CS8409_CS42L42_SPK_PIN_NID, 1762 (spec->cs42l42_hp_jack_in)?0 : PIN_OUT); 1763 1764 /* Report jack*/ 1765 jk = snd_hda_jack_tbl_get_mst(codec, CS8409_CS42L42_HP_PIN_NID, 0); 1766 if (jk) { 1767 snd_hda_jack_unsol_event(codec, 1768 (jk->tag << AC_UNSOL_RES_TAG_SHIFT) & AC_UNSOL_RES_TAG); 1769 } 1770 /* Report jack*/ 1771 jk = snd_hda_jack_tbl_get_mst(codec, CS8409_CS42L42_AMIC_PIN_NID, 0); 1772 if (jk) { 1773 snd_hda_jack_unsol_event(codec, 1774 (jk->tag << AC_UNSOL_RES_TAG_SHIFT) & AC_UNSOL_RES_TAG); 1775 } 1776 } 1777 } 1778 --- 0-DAY CI Kernel Test Service, Intel Corporation https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index c95588681d53..0b8980240176 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -9,6 +9,7 @@ #include <linux/slab.h> #include <linux/module.h> #include <sound/core.h> +#include <linux/mutex.h> #include <linux/pci.h> #include <sound/tlv.h> #include <sound/hda_codec.h> @@ -38,6 +39,15 @@ struct cs_spec { /* for MBP SPDIF control */ int (*spdif_sw_put)(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); + + unsigned int cs42l42_hp_jack_in:1; + unsigned int cs42l42_mic_jack_in:1; + + struct mutex cs8409_i2c_mux; + + /* verb exec op override */ + int (*exec_verb)(struct hdac_device *dev, unsigned int cmd, + unsigned int flags, unsigned int *res); }; /* available models with CS420x */ @@ -1229,6 +1239,13 @@ static int patch_cs4213(struct hda_codec *codec) #define CS8409_CS42L42_SPK_PIN_NID 0x2c #define CS8409_CS42L42_AMIC_PIN_NID 0x34 #define CS8409_CS42L42_DMIC_PIN_NID 0x44 +#define CS8409_CS42L42_DMIC_ADC_PIN_NID 0x22 + +#define CS42L42_HSDET_AUTO_DONE 0x02 +#define CS42L42_HSTYPE_MASK 0x03 + +#define CS42L42_JACK_INSERTED 0x0C +#define CS42L42_JACK_REMOVED 0x00 #define GPIO3_INT (1 << 3) #define GPIO4_INT (1 << 4) @@ -1429,6 +1446,7 @@ static const struct cs8409_i2c_param cs42l42_init_reg_seq[] = { { 0x1C03, 0xC0 }, { 0x1105, 0x00 }, { 0x1112, 0xC0 }, + { 0x1101, 0x02 }, {} /* Terminator */ }; @@ -1565,6 +1583,8 @@ static unsigned int cs8409_i2c_write(struct hda_codec *codec, /* Assert/release RTS# line to CS42L42 */ static void cs8409_cs42l42_reset(struct hda_codec *codec) { + struct cs_spec *spec = codec->spec; + /* Assert RTS# line */ snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DATA, 0); @@ -1576,21 +1596,184 @@ static void cs8409_cs42l42_reset(struct hda_codec *codec) /* wait ~10ms */ usleep_range(10000, 15000); - /* Clear interrupts status */ + mutex_lock(&spec->cs8409_i2c_mux); + + /* Clear interrupts, by reading interrupt status registers */ cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1308, 1); cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1309, 1); cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x130A, 1); cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x130F, 1); + mutex_unlock(&spec->cs8409_i2c_mux); + +} + +/* Configure CS42L42 slave codec for jack autodetect */ +static int cs8409_cs42l42_enable_jack_detect(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + + mutex_lock(&spec->cs8409_i2c_mux); + + /* Set TIP_SENSE_EN for analog front-end of tip sense. */ + cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b70, 0x0020, 1); + /* Clear WAKE# */ + cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b71, 0x0001, 1); + /* Wait ~2.5ms */ + usleep_range(2500, 3000); + /* Set mode WAKE# output follows the combination logic directly */ + cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b71, 0x0020, 1); + /* Clear interrupts status */ + cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x130f, 1); + cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1b7b, 1); + /* Enable interrupt */ + cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1320, 0x03, 1); + cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b79, 0x00, 1); + + mutex_unlock(&spec->cs8409_i2c_mux); + + return 0; +} + +/* Enable and run CS42L42 slave codec jack auto detect */ +static void cs8409_cs42l42_run_jack_detect(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + + mutex_lock(&spec->cs8409_i2c_mux); + + /* Clear interrupts */ + cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1308, 1); + cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1b77, 1); + + cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1102, 0x87, 1); + cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1f06, 0x86, 1); + cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1b74, 0x07, 1); + cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x131b, 0x01, 1); + cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1120, 0x80, 1); + /* Wait ~110ms*/ + usleep_range(110000, 200000); + cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x111f, 0x77, 1); + cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1120, 0xc0, 1); + /* Wait ~10ms */ + usleep_range(10000, 25000); + + mutex_unlock(&spec->cs8409_i2c_mux); + } static void cs8409_cs42l42_reg_setup(struct hda_codec *codec) { const struct cs8409_i2c_param *seq = cs42l42_init_reg_seq; + struct cs_spec *spec = codec->spec; + + mutex_lock(&spec->cs8409_i2c_mux); for (; seq->addr; seq++) cs8409_i2c_write(codec, CS42L42_I2C_ADDR, seq->addr, seq->reg, 1); + mutex_unlock(&spec->cs8409_i2c_mux); + +} + +/* + * In the case of CS8409 we do not have unsolicited events from NID's 0x24 + * and 0x34 where hs mic and hp are connected. Companion codec CS42L42 will + * generate interrupt via gpio 4 to notify jack events. We have to overwrite + * generic snd_hda_jack_unsol_event(), read CS42L42 jack detect status registers + * and then notify status via generic snd_hda_jack_unsol_event() call. + */ +void cs8409_jack_unsol_event(struct hda_codec *codec, unsigned int res) +{ + struct cs_spec *spec = codec->spec; + int status_changed = 0; + unsigned int reg_cdc_status = 0; + unsigned int reg_hs_status = 0; + unsigned int reg_ts_status = 0; + int type = 0; + struct hda_jack_tbl *jk; + + /* jack_unsol_event() will be called every time gpio line changing state. + * In this case gpio4 line goes up as a result of reading interrupt status + * registers in previous cs8409_jack_unsol_event() call. + * We don't need to handle this event, ignoring... + */ + if ((res & (1 << 4))) + return; + + mutex_lock(&spec->cs8409_i2c_mux); + + /* Read jack detect status registers */ + reg_cdc_status = cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1308, 1); + reg_hs_status = cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1124, 1); + reg_ts_status = cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x130f, 1); + + /* Clear interrupts */ + cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1b7b, 1); + cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x1308, 1); + cs8409_i2c_read(codec, CS42L42_I2C_ADDR, 0x130f, 1); + + mutex_unlock(&spec->cs8409_i2c_mux); + + /* HSDET_AUTO_DONE */ + if (reg_cdc_status & CS42L42_HSDET_AUTO_DONE) { + + type = ((reg_hs_status & CS42L42_HSTYPE_MASK) + 1); + /* CS42L42 reports optical jack as type 4 + * We don't handle optical jack + */ + if (type != 4) { + if (!spec->cs42l42_hp_jack_in) { + status_changed = 1; + spec->cs42l42_hp_jack_in = 1; + } + /* type = 3 has no mic */ + if ((!spec->cs42l42_mic_jack_in) && (type != 3)) { + status_changed = 1; + spec->cs42l42_mic_jack_in = 1; + } + } + + } else { + /* TIP_SENSE INSERT/REMOVE */ + switch (reg_ts_status) { + case CS42L42_JACK_INSERTED: + cs8409_cs42l42_run_jack_detect(codec); + break; + + case CS42L42_JACK_REMOVED: + if (spec->cs42l42_hp_jack_in || spec->cs42l42_mic_jack_in) { + status_changed = 1; + spec->cs42l42_hp_jack_in = 0; + spec->cs42l42_mic_jack_in = 0; + } + break; + + default: + /* jack in transition */ + status_changed = 0; + break; + } + } + + if (status_changed) { + + snd_hda_set_pin_ctl(codec, CS8409_CS42L42_SPK_PIN_NID, + (spec->cs42l42_hp_jack_in)?0 : PIN_OUT); + + /* Report jack*/ + jk = snd_hda_jack_tbl_get_mst(codec, CS8409_CS42L42_HP_PIN_NID, 0); + if (jk) { + snd_hda_jack_unsol_event(codec, + (jk->tag << AC_UNSOL_RES_TAG_SHIFT) & AC_UNSOL_RES_TAG); + } + /* Report jack*/ + jk = snd_hda_jack_tbl_get_mst(codec, CS8409_CS42L42_AMIC_PIN_NID, 0); + if (jk) { + snd_hda_jack_unsol_event(codec, + (jk->tag << AC_UNSOL_RES_TAG_SHIFT) & AC_UNSOL_RES_TAG); + } + } } static int cs8409_cs42l42_build_controls(struct hda_codec *codec) @@ -1603,6 +1786,13 @@ static int cs8409_cs42l42_build_controls(struct hda_codec *codec) snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_BUILD); + /* Run jack auto detect first time on boot + * after controls have been added, to check if jack has + * been already plugged in + */ + cs8409_cs42l42_run_jack_detect(codec); + usleep_range(100000, 150000); + return 0; } @@ -1610,21 +1800,72 @@ static int cs8409_cs42l42_build_controls(struct hda_codec *codec) /* Manage PDREF, when transition to D3hot */ static int cs8409_suspend(struct hda_codec *codec) { + struct cs_spec *spec = codec->spec; + + mutex_lock(&spec->cs8409_i2c_mux); + /* Power down CS42L42 ASP/EQ/MIX/HP */ + cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x1101, 0xfe, 1); + mutex_unlock(&spec->cs8409_i2c_mux); + snd_hda_shutup_pins(codec); + return 0; } #endif +static void cs8409_cs42l42_cap_sync_hook(struct hda_codec *codec, + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct cs_spec *spec = codec->spec; + unsigned int curval, expval; + /* CS8409 DMIC Pin only allows the setting of the Stream Parameters in + * Power State D0. When a headset is unplugged, and the path is switched to + * the DMIC, the Stream is restarted with the new ADC, but this is done in + * Power State D3. Restart the Stream now DMIC is in D0. + */ + if (spec->gen.cur_adc == CS8409_CS42L42_DMIC_ADC_PIN_NID) { + curval = snd_hda_codec_read(codec, spec->gen.cur_adc, + 0, AC_VERB_GET_CONV, 0); + expval = (spec->gen.cur_adc_stream_tag << 4) | 0; + if (curval != expval) { + codec_dbg(codec, "%s Restarting Stream after DMIC switch\n", __func__); + __snd_hda_codec_cleanup_stream(codec, spec->gen.cur_adc, 1); + snd_hda_codec_setup_stream(codec, spec->gen.cur_adc, + spec->gen.cur_adc_stream_tag, 0, + spec->gen.cur_adc_format); + } + } +} + +/* Enable/Disable Unsolicited Response for gpio(s) 3,4 */ +static void cs8409_enable_ur(struct hda_codec *codec, int flag) +{ + /* GPIO4 INT# and GPIO3 WAKE# */ + snd_hda_codec_write(codec, codec->core.afg, + 0, AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, + flag?(GPIO3_INT | GPIO4_INT) : 0); + + snd_hda_codec_write(codec, codec->core.afg, + 0, AC_VERB_SET_UNSOLICITED_ENABLE, + flag?AC_UNSOL_ENABLED : 0); + +} + /* Vendor specific HW configuration * PLL, ASP, I2C, SPI, GPIOs, DMIC etc... */ static int cs8409_cs42l42_hw_init(struct hda_codec *codec) { const struct cs8409_cir_param *seq = cs8409_cs42l42_hw_cfg; + struct cs_spec *spec = codec->spec; for (; seq->nid; seq++) cs_vendor_coef_set(codec, seq->cir, seq->coeff); + /* Disable Unsolicited Response during boot */ + cs8409_enable_ur(codec, 0); + /* Reset CS42L42 */ cs8409_cs42l42_reset(codec); @@ -1634,11 +1875,18 @@ static int cs8409_cs42l42_hw_init(struct hda_codec *codec) if (codec->fixup_id == CS8409_WARLOCK || codec->fixup_id == CS8409_CYBORG) { /* FULL_SCALE_VOL = 0 for Warlock / Cyborg */ + mutex_lock(&spec->cs8409_i2c_mux); cs8409_i2c_write(codec, CS42L42_I2C_ADDR, 0x2001, 0x01, 1); + mutex_unlock(&spec->cs8409_i2c_mux); /* DMIC1_MO=00b, DMIC1/2_SR=1 */ cs_vendor_coef_set(codec, 0x09, 0x0003); } + cs8409_cs42l42_enable_jack_detect(codec); + + /* Enable Unsolicited Response */ + cs8409_enable_ur(codec, 1); + return 1; } @@ -1668,6 +1916,8 @@ static int cs8409_cs42l42_init(struct hda_codec *codec) cs8409_cs42l42_hw_init(codec); + cs8409_cs42l42_run_jack_detect(codec); + usleep_range(100000, 150000); } return ret; @@ -1678,7 +1928,7 @@ static const struct hda_codec_ops cs8409_cs42l42_patch_ops = { .build_pcms = snd_hda_gen_build_pcms, .init = cs8409_cs42l42_init, .free = cs_free, - .unsol_event = snd_hda_jack_unsol_event, + .unsol_event = cs8409_jack_unsol_event, #ifdef CONFIG_PM .suspend = cs8409_suspend, #endif @@ -1738,6 +1988,45 @@ static int cs8409_cs42l42_fixup(struct hda_codec *codec) return err; } +static int cs8409_cs42l42_exec_verb(struct hdac_device *dev, + unsigned int cmd, unsigned int flags, unsigned int *res) +{ + struct hda_codec *codec = container_of(dev, struct hda_codec, core); + struct cs_spec *spec = codec->spec; + + unsigned int nid = 0; + unsigned int verb = 0; + + nid = ((cmd >> 20) & 0x07f); + verb = ((cmd >> 8) & 0x0fff); + + /* CS8409 pins have no AC_PINSENSE_PRESENCE + * capabilities. We have to intercept 2 calls for pins 0x24 and 0x34 + * and return correct pin sense values for read_pin_sense() call from + * hda_jack based on CS42L42 jack detect status. + */ + switch (nid) { + case CS8409_CS42L42_HP_PIN_NID: + if (verb == AC_VERB_GET_PIN_SENSE) { + *res = (spec->cs42l42_hp_jack_in) ? AC_PINSENSE_PRESENCE : 0; + return 0; + } + break; + + case CS8409_CS42L42_AMIC_PIN_NID: + if (verb == AC_VERB_GET_PIN_SENSE) { + *res = (spec->cs42l42_mic_jack_in) ? AC_PINSENSE_PRESENCE : 0; + return 0; + } + break; + + default: + break; + } + + return spec->exec_verb(dev, cmd, flags, res); +} + static int patch_cs8409(struct hda_codec *codec) { struct cs_spec *spec; @@ -1763,8 +2052,16 @@ static int patch_cs8409(struct hda_codec *codec) snd_hda_add_verbs(codec, cs8409_cs42l42_add_verbs); + /* verb exec op override */ + spec->exec_verb = codec->core.exec_verb; + codec->core.exec_verb = cs8409_cs42l42_exec_verb; + + mutex_init(&spec->cs8409_i2c_mux); + codec->patch_ops = cs8409_cs42l42_patch_ops; + spec->gen.cap_sync_hook = cs8409_cs42l42_cap_sync_hook; + spec->gen.suppress_auto_mute = 1; spec->gen.no_primary_hp = 1; /* GPIO 5 out, 3,4 in */ @@ -1772,6 +2069,9 @@ static int patch_cs8409(struct hda_codec *codec) spec->gpio_data = 0; spec->gpio_mask = 0x03f; + spec->cs42l42_hp_jack_in = 0; + spec->cs42l42_mic_jack_in = 0; + err = cs8409_cs42l42_fixup(codec); if (err > 0)
In the case of CS8409 we do not have unsol events from NID's 0x24 and 0x34 where hs mic and hp are connected. Companion codec CS42L42 will generate interrupt via gpio 4 to notify jack events. We have to overwrite standard snd_hda_jack_unsol_event(), read CS42L42 jack detect status registers and then notify status via generic snd_hda_jack_unsol_event() call. Tested on DELL Inspiron-3500, DELL Inspiron-3501, DELL Inspiron-3505. Signed-off-by: Vitaly Rodionov <vitalyr@opensource.cirrus.com> --- sound/pci/hda/patch_cirrus.c | 304 ++++++++++++++++++++++++++++++++++- 1 file changed, 302 insertions(+), 2 deletions(-)