Wm8962 sound

Hello, after the TX2 is powered on, my audio occasionally generates an alarm of “wm8962 8-001a: DC servo timed out”, so the audio cannot be played. Is there any way to solve it?

Please check if can gain some ideas from NVIDIA Jetson Linux Driver Package Software Features : Audio Setup and Development | NVIDIA Docs

Hello, the inspection found that it is a problem with the wm8962 chip itself. At first, I set CONFIG_SND_SOC_WM8962=y in .config, compiled the wm8962 driver into the kernel, and programmed it into TX2; but now I modified the wm8962.c file, set CONFIG_SND_SOC_WM8962=m, and generated snd-soc-wm8962.ko . If the snd-soc-wm8962.ko file is ported to TX2 under the premise that the kernel itself supports 8962 drivers, will the modified wm8962.c take effect?

Are you still facing this problem? Or it is resovled?

If you are building the driver as module (CONFIG_SND_SOC_WM8962=m), make sure after build you push this driver module to target and make sure it gets loaded. From functionality point of view it does not matter whether you build it as part of kernel image or a separate module.

Once your device boots you can “lsmod” to see list of the modules loaded. You can put a info print as well in probe() function to confirm if is able to load fine.

Thanks.

Yes, I am trying to force the 7th and 8th bits of the register WM8962_DC_SERVO_6 in the static int hp_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) function in the wm8962.c file to be 1, but it has not been implemented, can you help

I set is:

static int hp_event(struct snd_soc_dapm_widget *w,
		    struct snd_kcontrol *kcontrol, int event)
{
	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
	int timeout;
	int reg;
	//(0x0100 | 0x0080)
	int expected = (WM8962_DCS_STARTUP_DONE_HP1L |
			WM8962_DCS_STARTUP_DONE_HP1R);
	dev_err(codec->dev, "############ expected=%d\n", expected);

	switch (event) {
	case SND_SOC_DAPM_POST_PMU:
		//(codec, 0x45, 0x0010 | 0x0001, 0x0010 | 0x0001)
		snd_soc_update_bits(codec, WM8962_ANALOGUE_HP_0,
				    WM8962_HP1L_ENA | WM8962_HP1R_ENA,
				    WM8962_HP1L_ENA | WM8962_HP1R_ENA);
		udelay(20);

		// (codec, 0x45, 0x0020 | 0x0002, 0x0020 | 0x0002)
		snd_soc_update_bits(codec, WM8962_ANALOGUE_HP_0,
				    WM8962_HP1L_ENA_DLY | WM8962_HP1R_ENA_DLY,
				    WM8962_HP1L_ENA_DLY | WM8962_HP1R_ENA_DLY);

		/* Start the DC servo */
		//(codec, 0x3D, 0x0080|0x0008|0x0040|0x0004, )
		snd_soc_update_bits(codec, WM8962_DC_SERVO_1,
				    WM8962_HP1L_DCS_ENA | WM8962_HP1R_DCS_ENA |
				    WM8962_HP1L_DCS_STARTUP |
				    WM8962_HP1R_DCS_STARTUP,
				    WM8962_HP1L_DCS_ENA | WM8962_HP1R_DCS_ENA |
				    WM8962_HP1L_DCS_STARTUP |
				    WM8962_HP1R_DCS_STARTUP);

		/* Wait for it to complete, should be well under 100ms */
		timeout = 0;

		do {
			msleep(1);
			
			reg = snd_soc_write(codec, WM8962_DC_SERVO_6, expected);
			dev_err(codec->dev, "*********snd_soc_write=%d\n", reg);
		
			reg = snd_soc_update_bits(codec, WM8962_DC_SERVO_6, WM8962_DCS_STARTUP_DONE_HP1L | WM8962_DCS_STARTUP_DONE_HP1R,
				       		WM8962_DCS_STARTUP_DONE_HP1L | WM8962_DCS_STARTUP_DONE_HP1R);
			dev_err(codec->dev, "*********snd_soc_update_bits=%d\n", reg);

			reg = snd_soc_read(codec, WM8962_DC_SERVO_6);
			dev_err(codec->dev, "*********timeout=%d, reg=%d\n", timeout, reg);
			snd_soc_write(codec, WM8962_DC_SERVO_6, expected);
			if (reg < 0) {
				dev_err(codec->dev,
					"Failed to read DCS status: %d\n",
					reg);
				continue;
			}
			dev_dbg(codec->dev, "DCS status: %x\n", reg);
		} while (++timeout < 200 && (reg & expected) != expected);


		if ((reg & expected) != expected)
		{
			dev_err(codec->dev, "DC servo timed out\n");
			dev_err(codec->dev, "********** reg=%d,   expected=%d,   reg & expected=%d\n", reg, expected, reg & expected);
		}
		else
			dev_dbg(codec->dev, "DC servo complete after %dms\n",
				timeout);

		//(codec, 0x45, 0x0040 | 0x0004, 0x0040 | 0x0004)
		snd_soc_update_bits(codec, WM8962_ANALOGUE_HP_0,
				    WM8962_HP1L_ENA_OUTP |
				    WM8962_HP1R_ENA_OUTP,
				    WM8962_HP1L_ENA_OUTP |
				    WM8962_HP1R_ENA_OUTP);
		udelay(20);

		//(codec, 0x45, 0x0080 | 0x0008, 0x0080 | 0x0008) 
		snd_soc_update_bits(codec, WM8962_ANALOGUE_HP_0,
				    WM8962_HP1L_RMV_SHORT |
				    WM8962_HP1R_RMV_SHORT,
				    WM8962_HP1L_RMV_SHORT |
				    WM8962_HP1R_RMV_SHORT);
		break;

	case SND_SOC_DAPM_PRE_PMD:
		snd_soc_update_bits(codec, WM8962_ANALOGUE_HP_0,
				    WM8962_HP1L_RMV_SHORT |
				    WM8962_HP1R_RMV_SHORT, 0);

		udelay(20);

		snd_soc_update_bits(codec, WM8962_DC_SERVO_1,
				    WM8962_HP1L_DCS_ENA | WM8962_HP1R_DCS_ENA |
				    WM8962_HP1L_DCS_STARTUP |
				    WM8962_HP1R_DCS_STARTUP,
				    0);

		snd_soc_update_bits(codec, WM8962_ANALOGUE_HP_0,
				    WM8962_HP1L_ENA | WM8962_HP1R_ENA |
				    WM8962_HP1L_ENA_DLY | WM8962_HP1R_ENA_DLY |
				    WM8962_HP1L_ENA_OUTP |
				    WM8962_HP1R_ENA_OUTP, 0);
				    
		break;

	default:
		WARN(1, "Invalid event %d\n", event);
		return -EINVAL;
	
	}

	return 0;
}


You need to check this with the Codec vendor, who is the owner of this driver.

Build it both as part of the kernel image and as part of a separate module (and when loaded into the TX2). If so, which case works.

Hi Yolomei

It does not make sense to build it as part of kernel image and then also build it as module and load. Use either ‘y’ or ‘m’. Don’t compile kernel multiple times with different option and mix the configurations.

To answer you question, if it is built as part of kernel image then it will be probed as part of kernel boot. Then if you try to load it as module, it has no effect since the driver is already probed as part of kernel image and module loading may report some errors.

Thanks.

ok, got it, thank you!!