龙讯桥接芯片lt9611 hdmi out audio调试

前言:
在nxp-imx8mm相关项目中用,使用龙讯的桥接芯片lt9611。在调试过程中,发现,audio挑显示器,在一些显示器上没有声音,主控的i2s clk,data信号都是输出到lt9611上的。于是在排查了一些必要条件后,把audio部分的问题归结到了lt9611 mcu的i2s接口上,即、lt9611 i2s 相关寄存器上。
1.原理图:
在这里插入图片描述

i2s:mclk,sclk,ws , data用示波器量都是有信号的。
2.官方提供的单片机mcu参考代码
audio部分:可以看到整个代码中,只有在函数入口初始化了lt9611的i2s相关寄存器。由于lt9611的datasheet没有找到,不知具体的寄存器代表什么意思,有点可惜,但是很确定的是,对i2s寄存器配置,打开了i2s通道。所以重点就是下面的i2s寄存器配置。

void LT9611_Audio_Init(void) //sujin
{
	#if 1
	if(lt9611.audio_out==audio_i2s)
	{
		printf("\r\nAudio inut = I2S 2ch");
	
		HDMI_WriteI2C_Byte(0xff,0x84);
		HDMI_WriteI2C_Byte(0x06,0x08);
		HDMI_WriteI2C_Byte(0x07,0x10);

        //48K sampling frequency	
		HDMI_WriteI2C_Byte(0x0f,0x2b); //0x2b: 48K, 0xab:96K
		HDMI_WriteI2C_Byte(0x34,0xd4); //CTS_N 20180823 0xd5: sclk = 32fs, 0xd4: sclk = 64fs
      
		HDMI_WriteI2C_Byte(0x35,0x00); // N value = 6144
		HDMI_WriteI2C_Byte(0x36,0x18); 
		HDMI_WriteI2C_Byte(0x37,0x00); 
		
        //96K sampling frequency	
		//HDMI_WriteI2C_Byte(0x0f,0xab); //0x2b: 48K, 0xab:96K
		//HDMI_WriteI2C_Byte(0x34,0xd4); //CTS_N 20180823 0xd5: sclk = 32fs, 0xd4: sclk = 64fs
      
		//HDMI_WriteI2C_Byte(0x35,0x00); // N value = 12288
		//HDMI_WriteI2C_Byte(0x36,0x30);
		//HDMI_WriteI2C_Byte(0x37,0x00); 


        //44.1K sampling frequency
/*
		HDMI_WriteI2C_Byte(0x0f,0x0b); //0x2b: 48K, 0xab:96K
		HDMI_WriteI2C_Byte(0x34,0xd4); //CTS_N 20180823 0xd5: sclk = 32fs, 0xd4: sclk = 64fs
      
		HDMI_WriteI2C_Byte(0x35,0x00); // N value = 6272
		HDMI_WriteI2C_Byte(0x36,0x18); 
		HDMI_WriteI2C_Byte(0x37,0x80); 
*/
	}
	
	if(lt9611.audio_out==audio_spdif)
	{
		printf("\r\nAudio inut = SPDIF");

		HDMI_WriteI2C_Byte(0xff,0x84);
		HDMI_WriteI2C_Byte(0x06,0x0c);
		HDMI_WriteI2C_Byte(0x07,0x10);	

		HDMI_WriteI2C_Byte(0x34,0xd4); //CTS_N
	}
	#endif
}

void LT9611_Init(void)
{
  u32 cnt = 0;
	//RESET_LT9611();
	LT9611_Chip_ID();
	LT9611_System_Init(); 
	
	//LT9611_RST_PD_Init(); 
	LT9611_MIPI_Input_Analog(); 
	LT9611_MIPI_Input_Digtal();

	Timer0_Delay1ms(1000);
	LT9611_Video_Check();
	
	LT9611_PLL(video);
	LT9611_MIPI_Pcr(video); //pcr setup

	LT9611_Audio_Init();
	LT9611_CSC();
	LT9611_HDCP_Init();
	LT9611_HDMI_TX_Digital(video);
	LT9611_HDMI_TX_Phy();

	LT9611_IRQ_Init();;
	//LT9611_Read_EDID();

//	LT9611_HDMI_CEC_ON(1);
//	lt9611_cec_msg_set_logical_address();
  lt9611_cec_msg_init(&lt9611_cec_msg);
	
	LT9611_Enable_Interrupts(HPD_INTERRUPT_ENABLE, 1);
  LT9611_Enable_Interrupts(VID_CHG_INTERRUPT_ENABLE, 0);
	LT9611_Enable_Interrupts(CEC_INTERRUPT_ENABLE, 1);
	
	LT9611_Frequency_Meter_Byte_Clk();
	LT9611_Dphy_debug();
	LT9611_Htotal_Sysclk();
	LT9611_Pcr_MK_Print();

	printf("\r\n==========================LT9611 Initial End===============================");
	Timer0_Delay1ms(200); //HPD have debounce, wait HPD irq.
	//while(1);
	LT9611_HDP_Interrupt_Handle();
  //lt9611_cec_msg_write_demo();
  
	while(1)
	{
	  if(irq_task_flag)
	  {
		  //print("\r\nirq task...");
		  LT9611_IRQ_Task();
		  irq_task_flag = 0;
		  set_EPI;
	  }

//    lt9611_cec_msg_mainloop(&lt9611_cec_msg);
    }
}


3.linux lt9611 驱动代码
驱动在probe后,上层调用的时候,先init部分寄存器,在hw有设置了相关的寄存器,并且对比官方参考代码,audio部分设置的寄存器还是有很大差别的。


static int rk_lt9611_audio_init(struct lt9611 *lt)
{
    int ret = 0;
        printk(KERN_ERR"\r\nAudio input = I2S 2ch");
        ret = lt9611_write_byte(lt, BANK_SELECT_ADDR, SYSTEM_CTRL0_BLOCK);
        if (ret) {
                DRM_ERROR("Failed to select SYSTEM_CTRL0_BLOCK, ret=%d\n", ret);
                return ret;
        }
//      HDMI_WriteI2C_Byte(0xff,0x82);
        ret = lt9611_write_byte(lt, RGO_HDMITX_CTRL0_OFFSET, 0x8e); //0x8e: HDMI, 0x0e:DVI
        if (ret) {
                DRM_ERROR("Failed to set RGO_HDMITX_CTRL0_OFFSET, ret=%d\n", ret);
                return ret;
        }
//      HDMI_WriteI2C_Byte(0xd6,0x8c);
        ret = lt9611_write_byte(lt, RGO_HDMITX_CTRL1_OFFSET, 0x04);
        if (ret) {
                DRM_ERROR("Failed to set RGO_HDMITX_CTRL1_OFFSET, ret=%d\n", ret);
                return ret;
        }
//      HDMI_WriteI2C_Byte(0xd7,0x04); //sync polarity
        ret = lt9611_write_byte(lt, BANK_SELECT_ADDR, SYSTEM_CTRL1_BLOCK);
        if (ret) {
                DRM_ERROR("Failed to select SYSTEM_CTRL1_BLOCK, ret=%d\n", ret);
                return ret;
        }
//      HDMI_WriteI2C_Byte(0xff,0x84);
        ret = lt9611_write_byte(lt, RGO_AUDIO_CTRL0_OFFSET, 0x08);
        if (ret) {
                DRM_ERROR("Failed to set RGO_AUDIO_CTRL0_OFFSET, ret=%d\n", ret);
                return ret;
        }
//      HDMI_WriteI2C_Byte(0x06,0x08);
        ret = lt9611_write_byte(lt, RGO_AUDIO_CTRL1_OFFSET, 0x10);
        if (ret) {
                DRM_ERROR("Failed to set RGO_AUDIO_CTRL1_OFFSET, ret=%d\n", ret);
                return ret;
        }
//      HDMI_WriteI2C_Byte(0x07,0x10);

        ret = lt9611_write_byte(lt, RGO_840F_OFFSET_NA, 0x2b); //20190425
        if (ret) {
                DRM_ERROR("Failed to set RGO_840F_OFFSET_NA, ret=%d\n", ret);
                return ret;
        }
//      HDMI_WriteI2C_Byte(0x0f,0x2b); //0x29: 48K, 20bit

        ret = lt9611_write_byte(lt, RGO_CST_CTRL1_OFFSET, 0xd4);
        if (ret) {
                DRM_ERROR("Failed to set RGO_CST_CTRL1_OFFSET, ret=%d\n", ret);
                return ret;
        }
//      HDMI_WriteI2C_Byte(0x34,0xd4); //CTS_N
}


static int lt9611_hdmi_hw_params(struct device *dev, void *data,
                                 struct hdmi_codec_daifmt *fmt,
                                 struct hdmi_codec_params *hparms)
{
        struct lt9611 *lt9611 = data;
        
        pr_info("%s:start hparms->sample_rate =%d\n", __FUNCTION__,hparms->sample_rate);
        if (hparms->sample_rate == 48000 || hparms->sample_rate == 44100)
                regmap_write(lt9611->regmap, 0x840f, 0x2b);
        else if (hparms->sample_rate == 96000)
                regmap_write(lt9611->regmap, 0x840f, 0xab);
        else
                return -EINVAL;

        regmap_write(lt9611->regmap, 0x8435, 0x00);
        regmap_write(lt9611->regmap, 0x8436, 0x18);
        regmap_write(lt9611->regmap, 0x8437, 0x00);
        
        return 0;
}

static int lt9611_audio_startup(struct device *dev, void *data)
{
        struct lt9611 *lt9611 = data;

        pr_info("%s:start \n", __FUNCTION__);
        regmap_write(lt9611->regmap, 0x82d6, 0x8c);
        regmap_write(lt9611->regmap, 0x82d7, 0x04);

        regmap_write(lt9611->regmap, 0x8406, 0x08);
        regmap_write(lt9611->regmap, 0x8407, 0x10);

        regmap_write(lt9611->regmap, 0x8434, 0xd5);
        return 0;
}


static void lt9611_audio_shutdown(struct device *dev, void *data)
{
        struct lt9611 *lt9611 = data;

        pr_info("%s:start \n", __FUNCTION__);
        regmap_write(lt9611->regmap, 0x8406, 0x00);
        regmap_write(lt9611->regmap, 0x8407, 0x00);
}

static int lt9611_hdmi_i2s_get_dai_id(struct snd_soc_component *component,
                                      struct device_node *endpoint)
{
        struct of_endpoint of_ep;
        int ret;

        pr_info("%s:start \n", __FUNCTION__);
        ret = of_graph_parse_endpoint(endpoint, &of_ep);
        if (ret < 0)
                return ret;

        /*
         * HDMI sound should be located as reg = <2>
         * Then, it is sound port 0
         */
        if (of_ep.port == 2)
                return 0;

        return -EINVAL;
}

static const struct hdmi_codec_ops lt9611_codec_ops = {
        .hw_params      = lt9611_hdmi_hw_params,
        .audio_shutdown = lt9611_audio_shutdown,
        .audio_startup  = lt9611_audio_startup,
        .get_dai_id     = lt9611_hdmi_i2s_get_dai_id,
};

static struct hdmi_codec_pdata codec_data = {
        .ops = &lt9611_codec_ops,
        .max_i2s_channels = 2,
        .i2s = 1,
};

static int lt9611_audio_init(struct device *dev, struct lt9611 *lt9611)
{
        rk_lt9611_audio_init(lt9611);
        codec_data.data = lt9611;
        lt9611->audio_pdev =
                platform_device_register_data(dev, HDMI_CODEC_DRV_NAME,
                                              1,
                                              &codec_data, sizeof(codec_data));

        return PTR_ERR_OR_ZERO(lt9611->audio_pdev);
}

static void lt9611_audio_exit(struct lt9611 *lt9611)
{
        if (lt9611->audio_pdev) {
                platform_device_unregister(lt9611->audio_pdev);
                lt9611->audio_pdev = NULL;
        }
}

static int lt9611_probe(struct i2c_client *client,
         const struct i2c_device_id *id)
{
...

        return lt9611_audio_init(&client->dev, pdata);

err_sysfs_init:
        disable_irq(pdata->irq);
        free_irq(pdata->irq, pdata);
err_i2c_prog:
        lt9611_gpio_configure(pdata, false);
err_dt_supply:
        lt9611_put_dt_supply(&client->dev, pdata);
err_dt_parse:
        devm_kfree(&client->dev, pdata);
        return ret;
}

4.调试

所以调试过程中,按照官方提示的代码,在linux 驱动中设置了相同的寄存器,并把init,hw相关设置寄存器代码注掉,如 代码1 所示。在系统启动的时候,lt9611_power_on ->lt9611_init_setup会init i2s寄存器,用aplay 播放音频文件,有声音,再次的播放没有声音了。用i2c调试工具,对比了前后的寄存器配置,有差别。

差别产生的原因:系统启动,lt9611_power_on ->lt9611_init_setup 只设置了一次i2s的寄存器,然后在shutdown时,又设置了i2s寄存器,关闭了i2s通道。

所以i2s init 初始化这部分寄存器设置,需要在上层执行aplay的时候,让它每次都设置进去,打开i2s通道。更改代码2 ,这样上层调用的时候,就会每次init一下i2s寄存器,打开i2s,声音就出来了,并且在之前没有声音的显示器上,声音也是正常的。
代码1

static struct lt9611_reg_cfg lt9611_init_setup[] = {
        /* LT9611_System_Init */
        {0xFF, 0x81, 0},
        {0x01, 0x18, 0}, /* sel xtal clock */

        /* timer for frequency meter */
        {0xff, 0x82, 0},
        {0x1b, 0x69, 0}, /*timer 2*/
        {0x1c, 0x78, 0},
        {0xcb, 0x69, 0}, /*timer 1 */
        {0xcc, 0x78, 0},

        /* irq init */
        {0xff, 0x82, 0},
        {0x51, 0x01, 0},
        {0x58, 0x0a, 0}, /* hpd irq */
        {0x59, 0x80, 0}, /* hpd debounce width */
        {0x9e, 0xf7, 0}, /* video check irq */

        /* power consumption for work */
        {0xff, 0x80, 0},
        {0x04, 0xf0, 0},
        {0x06, 0xf0, 0},
        {0x0a, 0x80, 0},
        {0x0b, 0x40, 0},
        {0x0d, 0xef, 0},
        {0x11, 0xfa, 0},

        //+ i2s_init
        {0xff, 0x84 ,0},
        {0x06, 0x08 ,0},
        {0x07, 0x10 ,0},
        {0x0f, 0x2b ,0},
        {0x34, 0xd4 ,0},
        {0x35, 0x00 ,0},
        {0x36, 0x18 ,0},
        {0x37, 0x00 ,0},
};//static int lt9611_power_on(struct lt9611 *pdata, bool on) probe 调到 

static int lt9611_hdmi_hw_params(struct device *dev, void *data,
                                 struct hdmi_codec_daifmt *fmt,
                                 struct hdmi_codec_params *hparms)
{
        struct lt9611 *lt9611 = data;

#if 0
        pr_info("%s:start hparms->sample_rate =%d\n", __FUNCTION__,hparms->sample_rate);
        if (hparms->sample_rate == 48000 || hparms->sample_rate == 44100)
                regmap_write(lt9611->regmap, 0x840f, 0x2b);
        else if (hparms->sample_rate == 96000)
                regmap_write(lt9611->regmap, 0x840f, 0xab);
        else
                return -EINVAL;

        regmap_write(lt9611->regmap, 0x8435, 0x00);
        regmap_write(lt9611->regmap, 0x8436, 0x18);
        regmap_write(lt9611->regmap, 0x8437, 0x00);
#endif
        return 0;
}

static int lt9611_audio_startup(struct device *dev, void *data)
{
        struct lt9611 *lt9611 = data;

        pr_info("%s:start \n", __FUNCTION__);
#if 0
        regmap_write(lt9611->regmap, 0x82d6, 0x8c);
        regmap_write(lt9611->regmap, 0x82d7, 0x04);

        regmap_write(lt9611->regmap, 0x8406, 0x08);
        regmap_write(lt9611->regmap, 0x8407, 0x10);

        regmap_write(lt9611->regmap, 0x8434, 0xd5);
#endif
        return 0;
}

static void lt9611_audio_shutdown(struct device *dev, void *data)
{
        struct lt9611 *lt9611 = data;

        pr_info("%s:start \n", __FUNCTION__);
        regmap_write(lt9611->regmap, 0x8406, 0x00);
        regmap_write(lt9611->regmap, 0x8407, 0x00);
}

static int lt9611_hdmi_i2s_get_dai_id(struct snd_soc_component *component,
                                      struct device_node *endpoint)
{
        struct of_endpoint of_ep;
        int ret;

        pr_info("%s:start \n", __FUNCTION__);
        ret = of_graph_parse_endpoint(endpoint, &of_ep);
        if (ret < 0)
                return ret;

        /*
         * HDMI sound should be located as reg = <2>
         * Then, it is sound port 0
         */
        if (of_ep.port == 2)
                return 0;

        return -EINVAL;
}

static const struct hdmi_codec_ops lt9611_codec_ops = {
        .hw_params      = lt9611_hdmi_hw_params,
        .audio_shutdown = lt9611_audio_shutdown,
        .audio_startup  = lt9611_audio_startup,
        .get_dai_id     = lt9611_hdmi_i2s_get_dai_id,
};

static struct hdmi_codec_pdata codec_data = {
        .ops = &lt9611_codec_ops,
        .max_i2s_channels = 2,
        .i2s = 1,
};

static int lt9611_audio_init(struct device *dev, struct lt9611 *lt9611)
{
#if 0
        rk_lt9611_audio_init(lt9611);
#endif
        codec_data.data = lt9611;
        lt9611->audio_pdev =
                platform_device_register_data(dev, HDMI_CODEC_DRV_NAME,
                                              1,
                                              &codec_data, sizeof(codec_data));

        return PTR_ERR_OR_ZERO(lt9611->audio_pdev);
}


代码2

diff --git a/drivers/gpu/drm/bridge/lt9611.c b/drivers/gpu/drm/bridge/lt9611.c
index 5e3023dddabf..460892f4bd1b 100755
--- a/drivers/gpu/drm/bridge/lt9611.c
+++ b/drivers/gpu/drm/bridge/lt9611.c
@@ -212,6 +212,27 @@ static struct lt9611_reg_cfg lt9611_init_setup[] = {
        {0x0b, 0x40, 0},
        {0x0d, 0xef, 0},
        {0x11, 0xfa, 0},
+
+       //i2s_init
+       {0xff, 0x84 ,0},
+       {0x06, 0x08 ,0},
+       {0x07, 0x10 ,0},
+       {0x0f, 0x2b ,0},
+       {0x34, 0xd4 ,0},
+       {0x35, 0x00 ,0},
+       {0x36, 0x18 ,0},
+       {0x37, 0x00 ,0},
+};
+
+static struct lt9611_reg_cfg i2s_init_set[]={
+       //i2s_init
+       {0xff, 0x84 ,0},
+       {0x06, 0x08 ,0},
+       {0x07, 0x10 ,0},
+       {0x0f, 0x2b ,0},
+       {0x34, 0xd4 ,0},
+       {0x35, 0x00 ,0},
+       {0x36, 0x18 ,0},
 };
 
 struct lt9611_timing_info {
@@ -931,7 +952,6 @@ static int lt9611_pcr_setup(struct lt9611 *pdata,
 static int rk_lt9611_audio_init(struct lt9611 *lt)
 {
     int ret = 0;
-
        printk(KERN_ERR"\r\nAudio input = I2S 2ch");
        ret = lt9611_write_byte(lt, BANK_SELECT_ADDR, SYSTEM_CTRL0_BLOCK);
        if (ret) {
@@ -3175,6 +3195,7 @@ static int lt9611_hdmi_hw_params(struct device *dev, void *data,
 {
        struct lt9611 *lt9611 = data;
 
+#if 0
        pr_info("%s:start hparms->sample_rate =%d\n", __FUNCTION__,hparms->sample_rate);
        if (hparms->sample_rate == 48000 || hparms->sample_rate == 44100)
                regmap_write(lt9611->regmap, 0x840f, 0x2b);
@@ -3186,7 +3207,7 @@ static int lt9611_hdmi_hw_params(struct device *dev, void *data,
        regmap_write(lt9611->regmap, 0x8435, 0x00);
        regmap_write(lt9611->regmap, 0x8436, 0x18);
        regmap_write(lt9611->regmap, 0x8437, 0x00);
-
+#endif
        return 0;
 }
 
@@ -3195,6 +3216,7 @@ static int lt9611_audio_startup(struct device *dev, void *data)
        struct lt9611 *lt9611 = data;
 
        pr_info("%s:start \n", __FUNCTION__);
+#if 0
        regmap_write(lt9611->regmap, 0x82d6, 0x8c);
        regmap_write(lt9611->regmap, 0x82d7, 0x04);
 
@@ -3202,7 +3224,9 @@ static int lt9611_audio_startup(struct device *dev, void *data)
        regmap_write(lt9611->regmap, 0x8407, 0x10);
 
        regmap_write(lt9611->regmap, 0x8434, 0xd5);
-
+#endif
+       //printk("i2s_init \r\n");
+       lt9611_write_array(lt9611, i2s_init_set,sizeof(i2s_init_set));//上层每次都会调到
        return 0;
 }
 
@@ -3251,7 +3275,9 @@ static struct hdmi_codec_pdata codec_data = {
 
 static int lt9611_audio_init(struct device *dev, struct lt9611 *lt9611)
 {
+#if 0
        rk_lt9611_audio_init(lt9611);
+#endif
        codec_data.data = lt9611;
        lt9611->audio_pdev =
                platform_device_register_data(dev, HDMI_CODEC_DRV_NAME,

### 回答1: 龙讯LT8911EXB是一款基于MTK平台的无线网络芯片,它可以实现无线网络通信功能。为了使LT8911EXB能够正常工作,需要安装对应的驱动程序。 MTK平台龙讯LT8911EXB驱动是由联发科技(MTK)公司开发的一款软件,用于支持LT8911EXB芯片在操作系统中的正常运行。驱动程序可以提供必要的接口和指令,使操作系统能够识别和控制LT8911EXB芯片。这样,用户可以通过操作系统来配置和管理LT8911EXB,实现无线网络通信功能。 用户在安装MTK平台龙讯LT8911EXB驱动时,需要根据自己使用的操作系统版本选择相应的驱动程序。通常,驱动程序会提供一个安装包,用户只需双击运行安装包,按照提示完成安装即可。 安装驱动程序后,用户需要重启计算机,以使驱动程序生效。然后,用户可以打开操作系统的网络设置界面,在可用的网络连接选项中寻找和选择LT8911EXB网络。用户可以根据需要进行网络的连接、配置和管理。 总之,MTK平台龙讯LT8911EXB驱动是一款重要的软件,可以使LT8911EXB芯片在操作系统中正常工作。用户安装驱动后,可以通过操作系统使用和管理LT8911EXB芯片,实现无线网络通信功能。 ### 回答2: 龙讯LT8911EXB驱动是为MTK平台开发的一种驱动程序。MTK平台是现代移动设备如手机、平板电脑等使用的一种芯片平台,而龙讯LT8911EXB是一种在MTK平台上使用的无线通信芯片。该驱动程序可以使得移动设备和LT8911EXB芯片之间能够正常通信和交互。 这个驱动程序的作用主要有以下几个方面: 1. 设备识别和连接:该驱动程序能够帮助操作系统识别龙讯LT8911EXB芯片,将其作为一个无线通信设备进行识别,并在系统中建立与之的连接; 2. 驱动控制和管理:通过该驱动程序,用户可以对LT8911EXB芯片进行各种配置和设置,如频率、功率、通信方式等,以及监控和管理芯片的状态和运行情况; 3. 数据传输和处理:该驱动程序可以帮助实现LT8911EXB芯片与移动设备之间的数据传输,包括无线通信信号的接收和发送等功能; 4. 兼容性和稳定性:驱动程序的开发旨在提供兼容性和稳定性,以确保LT8911EXB芯片能够在MTK平台上正常运行,并保证数据的可靠传输。 总之,MTK平台的龙讯LT8911EXB驱动是一种重要的软件,能够使得移动设备与LT8911EXB芯片之间能够实现正常的无线通信和数据传输。这种驱动程序的开发目的是提供兼容性、稳定性和可靠性,以确保设备的正常运行。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值