wm8960芯片驱动打印寄存器方法
目前在调试wm8960芯片的时候遇到一个问题,需要能够打印wm8960芯片上的寄存器的值。
但是通过wm8960的手册我们知道,它的寄存器地址是7为,数据是9位,不是标准的i2c协议的情况,只支持了i2c来写寄存器,没有实现i2c读寄存器。
但是wm8960驱动在写寄存器的时候,会把写的寄存器的值记录下来,通过看其他的驱动,我们最终确定可以使用以下函数来读取寄存器的值
在文件./sound/soc/soc-io.c中
/**
* snd_soc_component_read() - Read register value
* @component: Component to read from
* @reg: Register to read
* @val: Pointer to where the read value is stored
*
* Return: 0 on success, a negative error code otherwise.
*/
int snd_soc_component_read(struct snd_soc_component *component,
unsigned int reg, unsigned int *val)
{
int ret;
if (component->regmap)
ret = regmap_read(component->regmap, reg, val);
else if (component->driver->read) {
*val = component->driver->read(component, reg);
ret = 0;
}
else
ret = -EIO;
return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_component_read);
可以看到该函数需要传入的参数如下:
int snd_soc_component_read(struct snd_soc_component *component,
unsigned int reg, unsigned int *val)
//component:一个struct snd_soc_component的结构体指针
//reg:要读取的寄存器地址
//val:读取后的值要存储的地址
返回值:
//0:读取成功
//其他:读取失败
同时,我们也想到了一种操作的方法,可以将wm8960驱动编译成内核模块,通过insmod的时候使用module_param把要读的寄存器的地址传参进去,然后在执行cat /sys/bus/platform/drivers/imx-wm8960/micphone的时候将读到的寄存器的值打印出来
为了实现上面的这个想法,我们要添加的代码如下:
在sound/soc/fsl/imx-wm8960.c文件内的引用头文件的下面增加
static int a;
module_param(a, int, S_IRUSR);
//其中a就是我们传入的要读的寄存器的地址
然后我们要实现的函数要放在下面函数中
static ssize_t micphone_show(struct device_driver *dev, char *buf)
{
struct imx_priv *priv = &card_priv;
int mic_status;
/* Check if headphone is plugged in */
mic_status = gpio_get_value(imx_mic_jack_gpio.gpio);
if (mic_status != priv->mic_active_low)
strcpy(buf, "Mic Jack\n");
else
strcpy(buf, "Main MIC\n");
return strlen(buf);
}
准备工作完成了,接下来就要好好想想怎么去使用snd_soc_component_read这个函数了,最重要的是如何获得这个函数中的第一个参数component。
首先要看struct snd_soc_component这个结构体在哪里,经过搜索,在./include/sound/soc-dai.h文件中有:
/*
* Digital Audio Interface runtime data.
*
* Holds runtime data for a DAI.
*/
struct snd_soc_dai {
...
/* parent platform/codec */
struct snd_soc_component *component;
...
};
原来component在struct snd_soc_dai中,那现在的问题就是找到struct snd_soc_dai这个结构体了,我们在sound/soc/fsl/imx-wm8960.c文件中看到了获得的方法:
static int imx_hifi_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
...
}
所以现在的问题又变成了如何获得struct snd_soc_pcm_runtime *rtd,同样是在sound/soc/fsl/imx-wm8960.c文件中,我们看到了获得的方法
static int imx_wm8960_late_probe(struct snd_soc_card *card)
{
struct snd_soc_pcm_runtime *rtd = list_first_entry(
&card->rtd_list, struct snd_soc_pcm_runtime, list);
...
}
所以现在的问题又变成了如何获得struct snd_soc_card *card,同样是在sound/soc/fsl/imx-wm8960.c文件中,我们在文件的开头可以看到
struct imx_wm8960_data {
struct snd_soc_card card;
struct clk *codec_clk;
unsigned int clk_frequency;
bool is_codec_master;
bool is_codec_rpmsg;
bool is_stream_in_use[2];
bool is_stream_opened[2];
struct regmap *gpr;
unsigned int hp_det[2];
u32 asrc_rate;
u32 asrc_format;
};
struct snd_soc_card *card在struct imx_wm8960_data里,所以现在的问题又变成了如何获得struct imx_wm8960_data
在sound/soc/fsl/imx-wm8960.c文件的probe函数中我们可以看到
static int imx_wm8960_probe(struct platform_device *pdev)
{
...
struct imx_wm8960_data *data;
...
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (!data) {
ret = -ENOMEM;
goto fail;
}
...
}
这里就已经获得了struct imx_wm8960_data *data,那我们直接拿来用就可以了
首先定义一个全局变量tttest;
static struct imx_wm8960_data *tttest;
然后再probe函数中拿到struct imx_wm8960_data *data
static int imx_wm8960_probe(struct platform_device *pdev)
{
...
struct imx_wm8960_data *data;
...
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (!data) {
ret = -ENOMEM;
goto fail;
}
tttest = data;
...
}
然后在micphone_show函数中一步步拿到我们最终想要的struct snd_soc_component,最后再传入snd_soc_component_read函数中即可
micphone_show函数改动如下
static ssize_t micphone_show(struct device_driver *dev, char *buf)
{
+ int ret;
+ unsigned int val;
struct imx_priv *priv = &card_priv;
int mic_status;
+ struct snd_soc_pcm_runtime *rtd = list_first_entry(
+ &tttest->card.rtd_list, struct snd_soc_pcm_runtime, list);
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+
+ ret = snd_soc_component_read(codec_dai->component,a, &val);
+
+ printk("----%03x\n",val);
+
/* Check if headphone is plugged in */
mic_status = gpio_get_value(imx_mic_jack_gpio.gpio);
最后编译内核以及模块,在linux命令行操作即可,以读0x07寄存器为例:
echo 7 > /proc/sys/kernel/printk
insmod snd-soc-imx-wm8960.ko a=0x07
cat /sys/bus/platform/drivers/imx-wm8960/micphone
以上便是解决此问题的全过程,其实解决类似问题的思路无非就是找到我们需要的函数,然后再一层一层的去找该如何去拿到我们想要的参数。下面是完整的patch
diff --git a/arch/arm64/boot/dts/freescale/OK1028A-C.dts b/arch/arm64/boot/dts/freescale/OK1028A-C.dts
index c45185663..78510a442 100644
--- a/arch/arm64/boot/dts/freescale/OK1028A-C.dts
+++ b/arch/arm64/boot/dts/freescale/OK1028A-C.dts
@@ -82,6 +82,7 @@
audio-codec = <&wm8960>;
codec-master;
hp-det = <3 0>;
+ mic-det-gpios = <&gpio3 0 0>;
audio-routing =
"Headphone Jack", "HP_L",
"Headphone Jack", "HP_R",
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index 630a39ae8..cb7acb9a9 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -105,7 +105,7 @@ obj-$(CONFIG_SND_SOC_IMX_ES8328) += snd-soc-imx-es8328.o
obj-$(CONFIG_SND_SOC_IMX_CS42888) += snd-soc-imx-cs42888.o
obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o
obj-${CONFIG_SND_SOC_IMX_WM8958} += snd-soc-imx-wm8958.o
-obj-$(CONFIG_SND_SOC_IMX_WM8960) += snd-soc-imx-wm8960.o
+obj-m += snd-soc-imx-wm8960.o
obj-$(CONFIG_SND_SOC_IMX_WM8524) += snd-soc-imx-wm8524.o
obj-$(CONFIG_SND_SOC_IMX_WM8962) += snd-soc-imx-wm8962.o
obj-$(CONFIG_SND_SOC_IMX_SII902X) += snd-soc-imx-sii902x.o
diff --git a/sound/soc/fsl/imx-wm8960.c b/sound/soc/fsl/imx-wm8960.c
index 9005b5e36..95365c035 100644
--- a/sound/soc/fsl/imx-wm8960.c
+++ b/sound/soc/fsl/imx-wm8960.c
@@ -26,6 +26,9 @@
#include "../codecs/wm8960.h"
#include "fsl_sai.h"
+static int a;
+module_param(a, int, S_IRUSR);
+
struct imx_wm8960_data {
struct snd_soc_card card;
struct clk *codec_clk;
@@ -48,6 +51,8 @@ struct imx_priv {
struct platform_device *asrc_pdev;
};
+static struct imx_wm8960_data *tttest;
+
static struct imx_priv card_priv;
static struct snd_soc_jack imx_hp_jack;
@@ -165,9 +170,19 @@ static ssize_t headphone_show(struct device_driver *dev, char *buf)
static ssize_t micphone_show(struct device_driver *dev, char *buf)
{
+ int ret;
+ unsigned int val;
struct imx_priv *priv = &card_priv;
int mic_status;
+ struct snd_soc_pcm_runtime *rtd = list_first_entry(
+ &tttest->card.rtd_list, struct snd_soc_pcm_runtime, list);
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+
+ ret = snd_soc_component_read(codec_dai->component,a, &val);
+
+ printk("----%03x\n",val);
+
/* Check if headphone is plugged in */
mic_status = gpio_get_value(imx_mic_jack_gpio.gpio);
@@ -470,6 +488,8 @@ static int imx_wm8960_probe(struct platform_device *pdev)
goto fail;
}
+ tttest = data;
+
if (of_property_read_bool(pdev->dev.of_node, "codec-rpmsg"))
data->is_codec_rpmsg = true;