[RK3399][Android7.1] Audio中的MCLK时钟小结

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/kris_fei/article/details/83310535

Platform: RK3399
OS: Android 7.1
Kernel: v4.4.83

引用:

I2S有3个主要信号:
1、串行时钟SCLK,也叫位时钟BCLK,即对应数字音频的每一位数据,SCLK有1个脉冲。SCLK的频率=2×采样频率×采样位数。
2、帧时钟LRCK,用于切换左右声道的数据。LRCK为“0”表示正在传输的是左声道的数据,为“1”则表示正在传输的是右声道的数据。LRCK的频率等于采样频率。
3、串行数据SDATA,就是用二进制补码表示的音频数据。
有时为了使系统间能够更好地同步,还需要另外传输一个信号MCLK,称为主时钟,也叫系统时钟(Sys
Clock),是采样频率的256倍或384倍。


  • Power Domain:

audio codec的clk被关联到power domains中管理。
rk3399.dtsi:

i2s0: i2s@ff880000 {
	//......
	clock-names = "i2s_clk", "i2s_hclk";
	clocks = <&cru SCLK_I2S0_8CH>, <&cru HCLK_I2S0_8CH>;
	power-domains = <&power RK3399_PD_SDIOAUDIO>;
};

Power Domain对于RK3399_PD_SDIOAUDIO的定义:

pmu: power-management@ff310000 {
	compatible = "rockchip,rk3399-pmu", "syscon", "simple-mfd";
	reg = <0x0 0xff310000 0x0 0x1000>;
	pd_sdioaudio@RK3399_PD_SDIOAUDIO {
		reg = <RK3399_PD_SDIOAUDIO>;
		clocks = <&cru HCLK_SDIO>;
		pm_qos = <&qos_sdioaudio>;
	};
}

power domain的添加(包括RK3399_PD_SDIOAUDIO)是在pm_domain.c中

static int rockchip_pm_add_subdomain(struct rockchip_pmu *pmu,
				     struct device_node *parent)
{
	error = rockchip_pm_add_one_domain(pmu, np);
	if (error) {
		dev_err(pmu->dev, "failed to handle node %s: %d\n",
			np->name, error);
		goto err_out;
	}
}

  • MCLK定义:

clk-rk3399.c

static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = {
	GATE(SCLK_I2S0_8CH, "clk_i2s0", "clk_i2s0_mux", CLK_SET_RATE_PARENT,
			RK3399_CLKGATE_CON(8), 5, GFLAGS),
}

  • MCLK和PM关联:

当I2S模块初始化的时候会把包括mclk的clk都注册到power domain中去:

rockchip_i2s_driver_init -> rockchip_i2s.c //module_platform_driver(rockchip_i2s_driver);扩展开来得到
  platform_drv_probe -> platform.c	//驱动probe
	dev_pm_domain_attach -> common.c
	  genpd_dev_pm_attach -> domain.c
		pm_genpd_add_device -> pm_domain.h
		  __pm_genpd_add_device ->
			genpd->attach_dev ->
			  rockchip_pd_attach_dev -> pm_domains.c
				pm_clk_add_clk ->
				  __pm_clk_add ->
					list_add_tail //把当前clk节点添加到psd->clock_list

rockchip_pd_attach_dev():

static int rockchip_pd_attach_dev(struct generic_pm_domain *genpd,
				  struct device *dev)
{
	......
	while ((clk = of_clk_get(dev->of_node, i++)) && !IS_ERR(clk)) {
		//可以把Log打印出来,和pd_sdioaudio关联,打印出来的两个clk和dtsi中的i2s0节点配置的一致。
		//[    1.399115] rockchip-i2s ff880000.i2s: attaching to power domain 'pd_sdioaudio'
		//[    1.399144] rockchip-i2s ff880000.i2s: adding clock 'clk_i2s0' to list of PM clocks
		//[    1.399180] rockchip-i2s ff880000.i2s: adding clock 'hclk_i2s0' to list of PM clocks
		dev_dbg(dev, "adding clock '%pC' to list of PM clocks\n", clk);
		error = pm_clk_add_clk(dev, clk);
	}
	......
}
  • MCLK设置:

rockchip_i2s.c

static int rockchip_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id,
				   unsigned int freq, int dir)
{
	ret = clk_set_rate(i2s->mclk, freq);
}

  • MCLK使能:

当打开audio设备的时候,会调用open接口。

soc_pcm_open -> soc-pcm.c
  pm_runtime_get_sync -> 增加引用计数
    __pm_runtime_resume -> 
	  rpm_resume -> 
	    rpm_callback -> 
	      __rpm_callback -> 
	        cb(dev); ->
	          pm_genpd_runtime_resume ->	//这里分两条路子,都会使能一遍clk
	            genpd_start_dev -> 
	              GENPD_DEV_CALLBACK ->
	                __routine(dev);	//传进来的名字是start, 在pm_genpd_init()赋值
					  pm_clk_resume ->
					    __pm_clk_enable -> 
						  clk_enable 
			    genpd_restore_dev -> //路线2
			      GENPD_DEV_CALLBACK -> //和上面一样,也是在pm_genpd_init()赋值restore_state
			        pm_genpd_default_restore_state ->
			          cb(dev) ->
			            i2s_runtime_resume ->
			              clk_prepare_enable
			    

pm_clk_resume():

int pm_clk_resume(struct device *dev)
{
	//从前面添加到psd->clock_list的列表中取出对应的元素
	list_for_each_entry(ce, &psd->clock_list, node)
		__pm_clk_enable(dev, ce);
}

  • MCLK查看:

在这里插入图片描述


  • 参考:

clk prepare和enable的问题
runtime电源管理模式(内核文档runtime_pm.txt有详细描述)
I2S和PCM

猜你喜欢

转载自blog.csdn.net/kris_fei/article/details/83310535