[RK3399]tc358743芯片的HDMI IN播放视频有电流声

一、前言

产品开发中发现HDMI IN输入音频的时候有电流声,分析得知hal层hdmiin的音频采样率和驱动指定采样率的不同。

二、代码路径

Android\kernel\drivers\media\i2c\tc35874x.c
Android\hardware\rockchip\audio\tinyalsa_hal\audio_hw.c
Android\kernel\drivers\media\v4l2-core\v4l2-dev.c

三、代码分析

audio_hw.c

static int get_hdmiin_audio_rate(struct audio_device *adev)
{
    int rate = 44100;
    char value[PROPERTY_VALUE_MAX] = "";
    property_get("vendor.hdmiin.audiorate", value, STR_44_1KHZ);

    if ( 0 == strncmp(value, STR_32KHZ, strlen(STR_32KHZ)) ){
        rate = 32000;
    }else if( 0 == strncmp(value, STR_44_1KHZ, strlen(STR_44_1KHZ)) ){
        rate = 44100;
    }else if( 0 == strncmp(value, STR_48KHZ, strlen(STR_48KHZ)) ){
        rate = 48000;
    } else {
        rate = atoi(value);
        if (rate <= 0)
            rate = 44100;
    }

    // if hdmiin connect to codec, use 44100 sample rate
    if (adev->dev_in[SND_IN_SOUND_CARD_HDMI].card
            == adev->dev_out[SND_OUT_SOUND_CARD_SPEAKER].card)
        rate = 44100;
        
    ALOGD("%s rate =%d",__FUNCTION__,rate);//打印出来的rate是48000
    
    return rate;
}

打印出来的rate是48000,并且挂示波器量的也是48000

tc35874x.c

static int get_audio_sampling_rate(struct v4l2_subdev *sd)
{
	static const int code_to_rate[] = {
		44100, 0, 48000, 32000, 22050, 384000, 24000, 352800,
		88200, 768000, 96000, 705600, 176400, 0, 192000, 0
	};

	/* Register FS_SET is not cleared when the cable is disconnected */
	if (no_signal(sd))
		return 0;
	 
	 //printk("i2c_rd8(sd, FS_SET) & MASK_FS = %d*******=%d\n",i2c_rd8(sd, FS_SET) & MASK_FS,code_to_rate[i2c_rd8(sd, FS_SET) & MASK_FS]);
	return code_to_rate[0];
}

可以看到返回的是44100,和48000明显不一致,这是导致电流声的主要原因。思路是做一个节点让hal层读出准确的值。

四、代码操作

tc35874x.c

#define FS_SET                                0x8621
#define MASK_FS                               0x0f
static int get_audio_sampling_rate(struct v4l2_subdev *sd)
{
	static const int code_to_rate[] = {
		44100, 0, 48000, 32000, 22050, 384000, 24000, 352800,
		88200, 768000, 96000, 705600, 176400, 0, 192000, 0
	};

	/* Register FS_SET is not cleared when the cable is disconnected */
	if (no_signal(sd))
		return 0;
	 
	 //printk("i2c_rd8(sd, FS_SET) & MASK_FS = %d*******=%d\n",i2c_rd8(sd, FS_SET) & MASK_FS,code_to_rate[i2c_rd8(sd, FS_SET) & MASK_FS]);
+	 return code_to_rate[i2c_rd8(sd, FS_SET) & MASK_FS];
-	return code_to_rate[0];
}

返回一个读寄存器的值,读出正确采样率的值,然后再添加如下, 将get_audio_sampling_rate读出来的值放入节点

+struct class *videodev_hdmirx_class(void);
+static ssize_t audio_rate_show(struct device *dev,
+                              struct device_attribute *attr, char *buf)
+{
+       struct tc35874x_state *state = dev_get_drvdata(dev);
+
+       return snprintf(buf, PAGE_SIZE, "%d", get_audio_sampling_rate(&state->sd));
+}
+
+static ssize_t audio_present_show(struct device *dev,
+                                 struct device_attribute *attr, char *buf)
+{
+       struct tc35874x_state *state = dev_get_drvdata(dev);
+
+       return snprintf(buf, PAGE_SIZE, "%d",
+                       tx_5v_power_present(&state->sd) ? 1 : 0);
+}
+
+static DEVICE_ATTR_RO(audio_rate);
+static DEVICE_ATTR_RO(audio_present);

+static struct attribute *tc35874x_attrs[] = {
+       &dev_attr_audio_rate.attr,
+       &dev_attr_audio_present.attr,
+       NULL
+};
+ATTRIBUTE_GROUPS(tc35874x);
......
+struct class *videodev_hdmirx_class(void);//记得在v4l2-dev.c处理
......
static int tc35874x_probe...
...
+       state->classdev = device_create_with_groups(videodev_hdmirx_class(),
+                                                       dev, MKDEV(0, 0),
+                                                       state,
+                                                       tc35874x_groups,
+                                                       "tc35874x");
+       if (IS_ERR(state->classdev)) {
+               return -ENODEV;
+       }

 v4l2-dev.c

+struct class *videodev_hdmirx_class(void)
+{
+	return &hdmirx_class;
+}
+EXPORT_SYMBOL(videodev_hdmirx_class);

然后在/sys/class/hdmirx/rk628/audio_rate节点中就可以读到相应的值,接下来是在hal层处理

audio_hw.c

#define MAX_BUFFER_SIZE 1024
+const char *ratePath = "/sys/class/hdmirx/rk628/audio_rate";
char buffer[MAX_BUFFER_SIZE];
.....
static int get_hdmiin_audio_rate(struct audio_device *adev)
{
    int rate = 44100;
    char value[PROPERTY_VALUE_MAX] = "";
    property_get("vendor.hdmiin.audiorate", value, STR_44_1KHZ);

    if ( 0 == strncmp(value, STR_32KHZ, strlen(STR_32KHZ)) ){
        rate = 32000;
    }else if( 0 == strncmp(value, STR_44_1KHZ, strlen(STR_44_1KHZ)) ){
        rate = 44100;
    }else if( 0 == strncmp(value, STR_48KHZ, strlen(STR_48KHZ)) ){
        rate = 48000;
    } else {
        rate = atoi(value);
        if (rate <= 0)
            rate = 44100;
    }

    // if hdmiin connect to codec, use 44100 sample rate
    if (adev->dev_in[SND_IN_SOUND_CARD_HDMI].card
            == adev->dev_out[SND_OUT_SOUND_CARD_SPEAKER].card)
        rate = 44100;
        
+    rate = readFromFile(ratePath);//强行将读取到的值返回
    
    //ALOGD("%s rate =%d",__FUNCTION__,rate);//打开打印获取
    
    return rate;
}
......
//readFromFile函数的实现
+int readFromFile(const char* filePath) {
+	  
+	    char buffer[32] = {0};
+   FILE *file = fopen(filePath, "r");//打开路径节点
+    if (file == NULL) {
+        perror("Error opening file");
+        return -1;  
+    }

+    int bytesRead = fread(buffer, 32, 1, file);//读出数据
    
+    //ALOGD("htc readFromFile = %s",buffer);//打印buffer
    
+    if (bytesRead < 0) {
+        perror("Error reading file");
+        fclose(file);//关闭
+        return -1;  
    
+  }

+   //buffer[bytesRead] = '\0';

+    fclose(file);//关闭
+    return atoi(buffer);//转成整型
+}

这样就解决了HDMI音频电流声的问题,hal层和驱动层指定的采样率就一致了。

五、总结

audio_hw.c这个是非常重要的文件,可以多研究,很多音频的问题都在这里调整,还要音频策略的那个java文件进行调整