目前在调试wm8960芯片的时候遇到一个问题,需要能够打印wm8960芯片上的寄存器的值。
但是通过wm8960的手册我们知道,它的寄存器地址是7为,数据是9位,不是标准的i2c协议的情况,只支持了i2c来写寄存器,没有实现i2c读寄存器。
但是wm8960驱动在写寄存器的时候,会把写的寄存器的值记录下来,通过看其他的驱动,我们最终确定可以使用以下函数来读取寄存器的值
在文件./sound/soc/soc-io.c中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 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);
可以看到该函数需要传入的参数如下:
1 2 3 4 5 6 7 8 9 int snd_soc_component_read (struct snd_soc_component *component, unsigned int reg, unsigned int *val) 返回值:
同时,我们也想到了一种操作的方法,可以将wm8960驱动编译成内核模块,通过insmod的时候使用module_param把要读的寄存器的地址传参进去,然后在执行cat /sys/bus/platform/drivers/imx-wm8960/micphone的时候将读到的寄存器的值打印出来
为了实现上面的这个想法,我们要添加的代码如下:
在sound/soc/fsl/imx-wm8960.c文件内的引用头文件的下面增加
1 2 3 static int a;module_param(a, int , S_IRUSR);
然后我们要实现的函数要放在下面函数中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 static ssize_t micphone_show (struct device_driver *dev, char *buf) { struct imx_priv *priv = &card_priv; int mic_status; 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文件中有:
1 2 3 4 5 6 7 8 9 10 11 struct snd_soc_dai { ... struct snd_soc_component *component ; ... };
原来component在struct snd_soc_dai中,那现在的问题就是找到struct snd_soc_dai这个结构体了,我们在sound/soc/fsl/imx-wm8960.c文件中看到了获得的方法:
1 2 3 4 5 6 7 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文件中,我们看到了获得的方法
1 2 3 4 5 6 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文件中,我们在文件的开头可以看到
1 2 3 4 5 6 7 8 9 10 11 12 13 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函数中我们可以看到
1 2 3 4 5 6 7 8 9 10 11 12 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;
1 static struct imx_wm8960_data *tttest ;
然后再probe函数中拿到struct imx_wm8960_data *data
1 2 3 4 5 6 7 8 9 10 11 12 13 14 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函数改动如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 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寄存器为例:
1 2 3 4 5 echo 7 > /proc/sys/kernel/printk insmod snd-soc-imx-wm8960.ko a=0x07 cat /sys/bus/platform/drivers/imx-wm8960/micphone
以上便是解决此问题的全过程,其实解决类似问题的思路无非就是找到我们需要的函数,然后再一层一层的去找该如何去拿到我们想要的参数。下面是完整的patch
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 @@ -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", @@ -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 @@ -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;