[alsa-devel] Support two wm8962 codec in i.mx6 board.
Hello everyone,
I'm new to ALSA.
I have a question here. Now there is a wm8962 on the i.mx6 board, running well, and we want to add an extra wm8962 to the board, connected to i.mx6 on another SSI port through a different i.mx6's AUDMUX port. The two wm8962 will work independently completely, at the same time possiblly.
Before sending this mail to the list, I've already googled some materials about my requirements. I've found there is a structure named "struct snd_soc_aux_dev". For my applicantion, if I want to support another wm8962, could I can use this structure for my intention please? But in this link http://www.spinics.net/lists/alsa-devel/msg40127.html , it's said that
This makes possible to register auxiliary dailess codecs in a machine driver. Term dailess is used here for amplifiers and codecs without DAI or DAI being unused.
then, does it mean that I couldn't use this structure for my application?
Or, I should declare another structure "struct snd_soc_card" for the second wm8962, just as the now-working wm8962? or other ways?
In fact, I'm a little confused if I could declare two structure snd_soc_card at the same? If it's OK, what should I do next to meet my requirement?
Could anyone give me any guidance on this? Any links, and materials will also be much appreciated.
Thank you in advance.
-------------------
Best Regards, Shawn
Hi Shawn,
On Mon, Mar 16, 2015 at 03:30:49PM +0800, Shawn Embedded wrote:
Now there is a wm8962 on the i.mx6 board, running well, and we
want to add an extra wm8962 to the board, connected to i.mx6 on another SSI port through a different i.mx6's AUDMUX port. The two wm8962 will work independently completely, at the same time possiblly.
I'm not sure if your requirement is to create another dai-link. But have you tried to simply add a new device node for the new dai-link?
Supposing two WM8962 chips on one board, there should be two WM8962 nodes hanging to the I2C node with different I2C slave addresses.
So you may need two different dai-link nodes for each of the WM8962 i2c nodes as well. And, of course, you need to specify different SSIs and different AUDMUX ports for two dai-link nodes.
Then you will get two sound card in the system.
Nicolin
Hello Nicolin,
On Tue, Mar 17, 2015 at 8:55 AM, Nicolin Chen nicoleotsuka@gmail.com wrote:
Hi Shawn,
On Mon, Mar 16, 2015 at 03:30:49PM +0800, Shawn Embedded wrote:
Now there is a wm8962 on the i.mx6 board, running well, and we
want to add an extra wm8962 to the board, connected to i.mx6 on another SSI port through a different i.mx6's AUDMUX port. The two wm8962 will work independently completely, at the same time possiblly.
I'm not sure if your requirement is to create another dai-link. But have you tried to simply add a new device node for the new dai-link?
Actually I donot know much about dai-link. I just got started recently to working on this requirement. You mean to create the same platform device as the now-working one in board-xxx.c. Sure I can do that. Since the two wm8962 codecs are on different I2C buses, so I just need to update the corresponding i2c's i2c_board_info structure to support the second one as a I2C device?
Supposing two WM8962 chips on one board, there should be two WM8962 nodes hanging to the I2C node with different I2C slave addresses.
Yes, but the I2C slave addresses should be the same, IIRC, only on different I2C buses. This should be OK for I2C architecture. What do you mean by "two wm8962 nodes" ?
In my experience, the corresponding files are board-xxx.c, imx-wm8962.c, wm8962.c.
I looked at the wm8962.c, which is the codec driver file, very independent. There comes the wm8962_i2c_probe() and wm8962_probe(). In wm8962_i2c_probe(), snd_soc_register_codec() is called, so a new codec structure will be created when there comes another i2c device named "wm8962". Seems no need to change anything in wm8962.c, if the external hardware signals of the second wm8962 are connected in the same way as the first now-working one, right?
Do you think any changes should be made to imx-wm8962.c which is the machine driver file for wm8962 on imx6?
For
static struct platform_device *imx_snd_device;
and
static struct snd_soc_dai_link imx_dai[] = { { .name = "HiFi", .stream_name = "HiFi", .codec_dai_name = "wm8962", .codec_name = "wm8962.0-001a", .cpu_dai_name = "imx-ssi.1", .platform_name = "imx-pcm-audio.1", .init = imx_wm8962_init, .ops = &imx_hifi_ops, }, };
and
static struct snd_soc_card snd_soc_card_imx = { .name = "wm8962-audio", .dai_link = imx_dai, .num_links = ARRAY_SIZE(imx_dai), };
in imx-wm8962.c, should I copy them to create a new copy of them respectively? Or no need to do this?
Or any changes you think I should make?
In board-xxx.c, do I just need to copy declarations related to wm8962? Is that enough?
So you may need two different dai-link nodes for each of the WM8962 i2c nodes as well. And, of course, you need to specify different SSIs and different AUDMUX ports for two dai-link nodes.
Again, could you tell me how to make two different dai-link nodes? Sorry for knowing not much about this.
Then you will get two sound card in the system.
Nicolin
Thank you.
-------------------- Best Regards, Shawn
On Tue, Mar 17, 2015 at 09:51:22AM +0800, Shawn Embedded wrote:
Actually I donot know much about dai-link. I just got started recently to working on this requirement. You mean to create the same platform device as the now-working one in board-xxx.c. Sure I can do that.
Are you using old kernel version that's not included Device Tree yet?
If so, yes, you need to add a similar platform device as the original one but you need to assign a different AUDMUX port.
Since the two wm8962 codecs are on different I2C buses, so I just need to update the corresponding i2c's i2c_board_info structure to support the second one as a I2C device?
Exactly.
Supposing two WM8962 chips on one board, there should be two WM8962 nodes hanging to the I2C node with different I2C slave addresses.
Yes, but the I2C slave addresses should be the same, IIRC, only on different I2C buses.
No problem for this. You can use same slave address but connecting to different I2C buses. As long as you system probes two different CODECs, that's fine.
What do you mean by "two wm8962 nodes" ?
If you are not using code from Linux Mainline, just forget about it.
In my experience, the corresponding files are board-xxx.c, imx-wm8962.c, wm8962.c.
I looked at the wm8962.c, which is the codec driver file, very independent. There comes the wm8962_i2c_probe() and wm8962_probe(). In wm8962_i2c_probe(), snd_soc_register_codec() is called, so a new codec structure will be created when there comes another i2c device named "wm8962". Seems no need to change anything in wm8962.c, if the external hardware signals of the second wm8962 are connected in the same way as the first now-working one, right?
No, you don't need to. Just register it in the board level driver.
Do you think any changes should be made to imx-wm8962.c which is the machine driver file for wm8962 on imx6?
Ideally, You only need to append a new platform device in the board level driver, same as you do for WM8962 CODECs.
static struct snd_soc_dai_link imx_dai[] = { { .name = "HiFi", .stream_name = "HiFi", .codec_dai_name = "wm8962", .codec_name = "wm8962.0-001a", .cpu_dai_name = "imx-ssi.1", .platform_name = "imx-pcm-audio.1", .init = imx_wm8962_init, .ops = &imx_hifi_ops, }, };
Okay...this one looks like you are using an old version BSP, other than the code from Linux Mainline.
in imx-wm8962.c, should I copy them to create a new copy of them respectively? Or no need to do this?
In this case, yes, because the structure has hard code. Otherwise, you new dai-link will not be successfully connected.
In board-xxx.c, do I just need to copy declarations related to wm8962? Is that enough?
I think so.
Again, could you tell me how to make two different dai-link nodes? Sorry for knowing not much about this.
That's for Device Tree which you are not using.
Nicolin
Hello Nicolin,
On Wed, Mar 18, 2015 at 2:00 AM, Nicolin Chen nicoleotsuka@gmail.com wrote:
On Tue, Mar 17, 2015 at 09:51:22AM +0800, Shawn Embedded wrote:
Actually I donot know much about dai-link. I just got started recently to working on this requirement. You mean to create the same platform device as the now-working one in board-xxx.c. Sure I can do that.
Are you using old kernel version that's not included Device Tree yet?
If so, yes, you need to add a similar platform device as the original one but you need to assign a different AUDMUX port.
Yes, it's and old version, without support of device tree. For AUDMUX configuration, is the code below enough, snippet 1 and 2?
1. In mach-mx6/board-xxx.c:
static struct mxc_audio_platform_data wm8962_data = { .ssi_num = MY_SSI_NUM, .src_port = MY_SRC_PORT, .ext_port = 6, .hp_gpio = -1, /*MX6S_GPT4_HEADPHONE_DET, */ .hp_active_low = 1, .mic_gpio = -1, /*MX6S_GPT4_MICROPHONE_DET,*/ .mic_active_low = 1, .init = mxc_wm8962_init, .clock_enable = wm8962_clk_enable, };
And here I have another question about clock source sharing between the two codecs. The two wm8962 codecs use the same clock source from imx6, in my case, clko, as the code mentioned below,
static int wm8962_clk_enable(int enable) { if (enable) clk_enable(clko); else clk_disable(clko);
return 0; }
static int mxc_wm8962_init(void) { int rate;
clko = clk_get(NULL, "clko_clk");
if (IS_ERR(clko)) { pr_err("can't get CLKO clock.\n"); return PTR_ERR(clko); }
/* both audio codec and comera use CLKO clk*/ rate = clk_round_rate(clko, 24000000); clk_set_rate(clko, rate);
wm8962_data.sysclk = rate;
return 0; }
Since the two codecs share the same clock source, clko, and we donot know when mxc_wm8962_init() and wm8962_clk_enable() will be called by using the function pointer exactly, do you think it's necessary to make NULL to init, make wm8962_clk_enable_do_nothing to clock_enable, and manually calling mxc_wm8962_init() and wm8962_clk_enable() in the xxx_board_init() function instead?
2. In imx-wm8962.c:
static int __devinit imx_wm8962_probe(struct platform_device *pdev) { .... imx_audmux_config(src, ext); // should keep the same, not need to change, right? .... }
Since the two wm8962 codecs are on different I2C buses, so I just need to update the corresponding i2c's i2c_board_info structure to support the second one as a I2C device?
Exactly.
Supposing two WM8962 chips on one board, there should be two WM8962 nodes hanging to the I2C node with different I2C slave addresses.
Yes, but the I2C slave addresses should be the same, IIRC, only on different I2C buses.
No problem for this. You can use same slave address but connecting to different I2C buses. As long as you system probes two different CODECs, that's fine.
What do you mean by "two wm8962 nodes" ?
If you are not using code from Linux Mainline, just forget about it.
Ok.
In my experience, the corresponding files are board-xxx.c, imx-wm8962.c, wm8962.c.
I looked at the wm8962.c, which is the codec driver file, very independent. There comes the wm8962_i2c_probe() and wm8962_probe(). In wm8962_i2c_probe(), snd_soc_register_codec() is called, so a new codec structure will be created when there comes another i2c device named "wm8962". Seems no need to change anything in wm8962.c, if the external hardware signals of the second wm8962 are connected in the same way as the first now-working one, right?
No, you don't need to. Just register it in the board level driver.
Do you think any changes should be made to imx-wm8962.c which is the machine driver file for wm8962 on imx6?
Ideally, You only need to append a new platform device in the board level driver, same as you do for WM8962 CODECs.
static struct snd_soc_dai_link imx_dai[] = { { .name = "HiFi", .stream_name = "HiFi", .codec_dai_name = "wm8962", .codec_name = "wm8962.0-001a", .cpu_dai_name = "imx-ssi.1", .platform_name = "imx-pcm-audio.1", .init = imx_wm8962_init, .ops = &imx_hifi_ops, }, };
Okay...this one looks like you are using an old version BSP, other than the code from Linux Mainline.
Yes, it's Linux-Kernel 3.0.x from Freescale.
in imx-wm8962.c, should I copy them to create a new copy of them respectively? Or no need to do this?
In this case, yes, because the structure has hard code. Otherwise, you new dai-link will not be successfully connected.
It seems I just need to copy some global variables, and change all the functions that reference the global variables just changed.
But there is a question here. How should I track the card_priv and its copy for the two codecs repectively. They are used heavily. In order to make the functions run without problems, seems necessary to differentiate the function call is for which codec of the two, right? The same situation may exist for other just copied global variables in imx-wm8962.c.
In board-xxx.c, do I just need to copy declarations related to wm8962? Is that enough?
I think so.
Again, could you tell me how to make two different dai-link nodes? Sorry for knowing not much about this.
That's for Device Tree which you are not using.
Nicolin
Thank you for your patience.
Best Regards, Shawn
On Wed, Mar 18, 2015 at 09:12:19AM +0800, Shawn Embedded wrote:
Yes, it's and old version, without support of device tree. For AUDMUX configuration, is the code below enough, snippet 1 and 2?
I think so.
And here I have another question about clock source sharing
between the two codecs. The two wm8962 codecs use the same clock source from imx6, in my case, clko, as the code mentioned below,
Since the two codecs share the same clock source, clko, and we donot know when mxc_wm8962_init() and wm8962_clk_enable() will be called by using the function pointer exactly, do you think it's necessary to make NULL to init, make wm8962_clk_enable_do_nothing to clock_enable, and manually calling mxc_wm8962_init() and wm8962_clk_enable() in the xxx_board_init() function instead?
I don't think you need to change this part.
- In imx-wm8962.c:
static int __devinit imx_wm8962_probe(struct platform_device *pdev) { .... imx_audmux_config(src, ext); // should keep the same, not need to change, right? .... }
No, you don't need to.
But there is a question here. How should I track the card_priv and its copy for the two codecs repectively. They are used heavily. In order to make the functions run without problems, seems necessary to differentiate the function call is for which codec of the two, right? The same situation may exist for other just copied global variables in imx-wm8962.c.
Each dai-link will create an individual sound card during system boot-up. Use 'aplay -l', you will see they are different devices.
Hi Nicolin,
On Wed, Mar 18, 2015 at 9:33 AM, Nicolin Chen nicoleotsuka@gmail.com wrote:
On Wed, Mar 18, 2015 at 09:12:19AM +0800, Shawn Embedded wrote:
Yes, it's and old version, without support of device tree. For AUDMUX configuration, is the code below enough, snippet 1 and 2?
I think so.
And here I have another question about clock source sharing
between the two codecs. The two wm8962 codecs use the same clock source from imx6, in my case, clko, as the code mentioned below,
Since the two codecs share the same clock source, clko, and we donot know when mxc_wm8962_init() and wm8962_clk_enable() will be called by using the function pointer exactly, do you think it's necessary to make NULL to init, make wm8962_clk_enable_do_nothing to clock_enable, and manually calling mxc_wm8962_init() and wm8962_clk_enable() in the xxx_board_init() function instead?
I don't think you need to change this part.
If donot change it, what will happen to the working codec if the clock_enable is called caused by stopping the other codec? the working codec will not be affected?
- In imx-wm8962.c:
static int __devinit imx_wm8962_probe(struct platform_device *pdev) { .... imx_audmux_config(src, ext); // should keep the same, not need to change, right? .... }
No, you don't need to.
But there is a question here. How should I track the card_priv and its copy for the two codecs repectively. They are used heavily. In order to make the functions run without problems, seems necessary to differentiate the function call is for which codec of the two, right? The same situation may exist for other just copied global variables in imx-wm8962.c.
Each dai-link will create an individual sound card during system boot-up. Use 'aplay -l', you will see they are different devices.
I know what you mean. But let me paste a snippet of code here, for two codecs on board, do'not you think it's necessary to setup 2 structure imx_priv to track the two codecs respectively?
In imx-wm8962.c
static struct imx_priv card_priv;
static void imx_hifi_shutdown(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *codec_dai = rtd->codec_dai; struct imx_priv *priv = &card_priv; struct mxc_audio_platform_data *plat = priv->pdev->dev.platform_data;
if (!codec_dai->active) plat->clock_enable(0);
return; }
/* * This function will register the snd_soc_pcm_link drivers. */ static int __devinit imx_wm8962_probe(struct platform_device *pdev) {
struct mxc_audio_platform_data *plat = pdev->dev.platform_data; struct imx_priv *priv = &card_priv; int ret = 0;
priv->pdev = pdev;
imx_audmux_config(plat->src_port, plat->ext_port);
if (plat->init && plat->init()) { ret = -EINVAL; return ret; }
priv->sysclk = plat->sysclk;
priv->sdev.name = "h2w"; ret = switch_dev_register(&priv->sdev); if (ret < 0) { ret = -EINVAL; return ret; }
if (plat->hp_gpio != -1) { priv->hp_status = gpio_get_value(plat->hp_gpio);
if (priv->hp_status != plat->hp_active_low) switch_set_state(&priv->sdev, 2); else switch_set_state(&priv->sdev, 0); } priv->first_stream = NULL; priv->second_stream = NULL;
return ret; }
Thank you.
Best Regards, Shawn
On Wed, Mar 18, 2015 at 12:48:24PM +0800, Shawn Embedded wrote:
Since the two codecs share the same clock source, clko, and we donot know when mxc_wm8962_init() and wm8962_clk_enable() will be called by using the function pointer exactly, do you think it's necessary to make NULL to init, make wm8962_clk_enable_do_nothing to clock_enable, and manually calling mxc_wm8962_init() and wm8962_clk_enable() in the xxx_board_init() function instead?
I don't think you need to change this part.
If donot change it, what will happen to the working codec if the clock_enable is called caused by stopping the other codec? the working codec will not be affected?
Before stopping, the other one has been opened once. So the reference count is 2 when the second clock_enable(); the clock_disable() that you worry about only decreases the count.
But let me paste a snippet of code here, for two codecs on board, do'not you think it's necessary to setup 2 structure imx_priv to track the two codecs respectively?
You can try whatever you think it's right. A better solution should be to modify the driver to get rid of that hard code. So there's no need to add any of structure in the imx-wm8962.c but only to register an extra platform driver in board level file.
On Wed, Mar 18, 2015 at 12:58 PM, Nicolin Chen nicoleotsuka@gmail.com wrote:
On Wed, Mar 18, 2015 at 12:48:24PM +0800, Shawn Embedded wrote:
Since the two codecs share the same clock source, clko, and we donot know when mxc_wm8962_init() and wm8962_clk_enable() will be called by using the function pointer exactly, do you think it's necessary to make NULL to init, make wm8962_clk_enable_do_nothing to clock_enable, and manually calling mxc_wm8962_init() and wm8962_clk_enable() in the xxx_board_init() function instead?
I don't think you need to change this part.
If donot change it, what will happen to the working codec if the clock_enable is called caused by stopping the other codec? the working codec will not be affected?
Before stopping, the other one has been opened once. So the reference count is 2 when the second clock_enable(); the clock_disable() that you worry about only decreases the count.
yes, right.
But let me paste a snippet of code here, for two codecs on board, do'not you think it's necessary to setup 2 structure imx_priv to track the two codecs respectively?
You can try whatever you think it's right. A better solution should be to modify the driver to get rid of that hard code. So there's no need to add any of structure in the imx-wm8962.c but only to register an extra platform driver in board level file.
Ok, I understand. Maybe I'll patch it later. Thanks a lot for your big help.
Best Regards, Shawn
participants (2)
-
Nicolin Chen
-
Shawn Embedded