版权声明:本文为博主原创文章,未经博主允许不得转载。 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