Well, it was quit interesting quest.
I found that I couldn't hear anything because of some issue in driver /sound/soc/codecs/uda134x.c. I dug in to the kernel (2.6.31 from buserror repo), traced hardware calls and found that on UDA134X driver loading during the kernel boot value 0x80 is written to the STATUS register (address 0x16) of the UDA1341 IC and according to the datasheet it means that ADC and DAC are both switched off. It happens bias level setting in /sound/soc/soc-dapm.c file in function dapm_power_widgets:
Code: Select all
/* If we just powered the last thing off drop to standby bias */
if (codec->bias_level == SND_SOC_BIAS_PREPARE && !sys_power) {
ret = snd_soc_dapm_set_bias_level(socdev,
SND_SOC_BIAS_STANDBY);
sys_power value is set in the same function:
Code: Select all
/* Check which widgets we need to power and store them in
* lists indicating if they should be powered up or down.
*/
list_for_each_entry(w, &codec->dapm_widgets, list) {
switch (w->id) {
case snd_soc_dapm_pre:
list_add_tail(&codec->down_list, &w->power_list);
break;
case snd_soc_dapm_post:
list_add_tail(&codec->up_list, &w->power_list);
break;
default:
if (!w->power_check)
continue;
power = w->power_check(w);
if (power)
sys_power = 1;
As you can see to set sys_power to 1 we need to add widget to codec->dapm_widgets and initialize its power_check pointer. As a temporary hack I added dummy widget to the codec in /sound/soc/codecs/uda134x.c (id = snd_soc_dapm_vmid because function that adds new widget doesn't initialize power_check pointer for this id):
Code: Select all
int uda134x_soc_dummy_power_check(struct snd_soc_dapm_widget *w)
{
return 1;
}
static const struct snd_soc_dapm_widget uda134x_dummy_dapm_widget = {
.id = snd_soc_dapm_vmid,
.power_check = uda134x_soc_dummy_power_check
};
And in uda134x_soc_probe function before snd_soc_init_card I added:
Code: Select all
snd_soc_dapm_new_controls(codec, &uda134x_dummy_dapm_widget, 1);
snd_soc_dapm_new_widgets(codec);
This resolved the sound issue. Is it bug in driver? And if so is it a proper workaround to solve this issue?
P.S. There is also bug in /sound/soc/codecs/uda134x.c file in function uda134x_mute:
Shoul be:
Code: Select all
uda134x_write(codec, UDA134X_DATA010, mute_reg);
instead of
Code: Select all
uda134x_write(codec, UDA134X_DATA010, mute_reg & ~(1<<2));
My diff for uda134x.c:
Code: Select all
===========================================================
--- a/sound/soc/codecs/uda134x.c 2009-10-07 19:48:17.000000000 +0300
+++ b/sound/soc/codecs/uda134x.c 2009-10-15 22:24:00.000000000 +0300
@@ -163,7 +163,7 @@
else
mute_reg &= ~(1<<2);
- uda134x_write(codec, UDA134X_DATA010, mute_reg & ~(1<<2));
+ uda134x_write(codec, UDA134X_DATA010, mute_reg);
return 0;
}
@@ -419,6 +419,16 @@
SOC_SINGLE("DC Filter Enable Switch", UDA134X_STATUS0, 0, 1, 0),
};
+int uda134x_soc_dummy_power_check(struct snd_soc_dapm_widget *w)
+{
+ return 1;
+}
+
+static const struct snd_soc_dapm_widget uda134x_dummy_dapm_widget = {
+ .id = snd_soc_dapm_vmid,
+ .power_check = uda134x_soc_dummy_power_check
+};
+
static const struct snd_kcontrol_new uda1340_snd_controls[] = {
SOC_SINGLE("Master Playback Volume", UDA134X_DATA000, 0, 0x3F, 1),
@@ -562,6 +572,9 @@
goto pcm_err;
}
+ snd_soc_dapm_new_controls(codec, &uda134x_dummy_dapm_widget, 1);
+ snd_soc_dapm_new_widgets(codec);
+
ret = snd_soc_init_card(socdev);
if (ret < 0) {
printk(KERN_ERR "UDA134X: failed to register card\n");
===========================================================