Yuhang Zheng

wm8960芯片驱动打印寄存器方法

N 人看过

目前在调试wm8960芯片的时候遇到一个问题,需要能够打印wm8960芯片上的寄存器的值。

但是通过wm8960的手册我们知道,它的寄存器地址是7为,数据是9位,不是标准的i2c协议的情况,只支持了i2c来写寄存器,没有实现i2c读寄存器。

image-20220908105702728

但是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

img

以上便是解决此问题的全过程,其实解决类似问题的思路无非就是找到我们需要的函数,然后再一层一层的去找该如何去拿到我们想要的参数。下面是完整的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;