diff mbox series

[v2,3/4] ALSA: hda/cirrus: Add jack detect interrupt support from CS42L42 companion codec.

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

Commit Message

Vitaly Rodionov March 4, 2021, 7:02 p.m. UTC
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(-)

Comments

kernel test robot March 4, 2021, 11:13 p.m. UTC | #1
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
kernel test robot March 5, 2021, 3:43 a.m. UTC | #2
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 mbox series

Patch

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)